go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/cmd/statsd-to-tsmon/converter.go (about) 1 // Copyright 2020 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package main 16 17 import ( 18 "context" 19 "strconv" 20 21 "go.chromium.org/luci/common/errors" 22 "go.chromium.org/luci/common/tsmon/metric" 23 ) 24 25 var ( 26 // ErrSkipped is returned by ConvertMetric if the metric is not mentioned in 27 // the config and thus should not be reported to tsmon. 28 ErrSkipped = errors.New("the metric doesn't match the filter") 29 30 // ErrUnexpectedType is returned by ConvertMetric if statsd metric type 31 // doesn't match the metric type specified in the config. 32 ErrUnexpectedType = errors.New("the metric has unexpected type") 33 ) 34 35 // ConvertMetric uses the config as a template to construct a tsmon metric point 36 // from a statsd metric point. 37 func ConvertMetric(ctx context.Context, cfg *Config, m *StatsdMetric) error { 38 rule := cfg.FindMatchingRule(m.Name) 39 if rule == nil { 40 return ErrSkipped 41 } 42 43 // Now that we really know the value is needed, parse it. 44 val, err := strconv.ParseInt(string(m.Value), 10, 64) 45 if err != nil { 46 return ErrMalformedStatsdLine 47 } 48 49 // Assemble values of metric fields. Some of them come directly from the 50 // config rule, and some are picked from the parsed statsd metric name. 51 fields := make([]any, len(rule.Fields)) 52 for i, spec := range rule.Fields { 53 switch val := spec.(type) { 54 case string: 55 fields[i] = val 56 case NameComponentIndex: 57 fields[i] = string(m.Name[int(val)]) 58 default: 59 panic("impossible") 60 } 61 } 62 63 // Send the value to tsmon. 64 switch m.Type { 65 case StatsdMetricGauge: 66 // metric.Counter is a metric.Int too, but we want Int specifically. 67 if _, ok := rule.Metric.(metric.Counter); ok { 68 return ErrUnexpectedType 69 } 70 if tm, ok := rule.Metric.(metric.Int); ok { 71 tm.Set(ctx, val, fields...) 72 } else { 73 return ErrUnexpectedType 74 } 75 76 case StatsdMetricCounter: 77 if tm, ok := rule.Metric.(metric.Counter); ok { 78 tm.Add(ctx, val, fields...) 79 } else { 80 return ErrUnexpectedType 81 } 82 83 case StatsdMetricTimer: 84 if tm, ok := rule.Metric.(metric.CumulativeDistribution); ok { 85 tm.Add(ctx, float64(val), fields...) 86 } else { 87 return ErrUnexpectedType 88 } 89 90 default: 91 panic("impossible") 92 } 93 94 return nil 95 }