go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/tsmon/registry/registry.go (about) 1 // Copyright 2017 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 registry holds a map of all metrics registered by the process. 16 // 17 // This map is global and it is populated during init() time, when individual 18 // metrics are defined. 19 package registry 20 21 import ( 22 "fmt" 23 "regexp" 24 "sync" 25 26 "go.chromium.org/luci/common/tsmon/monitor" 27 "go.chromium.org/luci/common/tsmon/types" 28 ) 29 30 var ( 31 registry = map[metricRegistryKey]types.Metric{} 32 lock = sync.RWMutex{} 33 metricNameRe = regexp.MustCompile("^(/[a-zA-Z0-9_-]+)+$") 34 metricFieldNameRe = regexp.MustCompile("^[A-Za-z_][A-Za-z0-9_]*$") 35 ) 36 37 type metricRegistryKey struct { 38 MetricName string 39 TargetType types.TargetType 40 } 41 42 // Add adds a metric to the metric registry. 43 // 44 // Panics if 45 // - the metric name is invalid. 46 // - a metric with the same name and target type is defined already. 47 // - a field name is invalid. 48 func Add(m types.Metric) { 49 key := metricRegistryKey{ 50 MetricName: m.Info().Name, 51 TargetType: m.Info().TargetType, 52 } 53 fields := m.Info().Fields 54 55 lock.Lock() 56 defer lock.Unlock() 57 58 switch _, exist := registry[key]; { 59 case key.MetricName == "": 60 panic(fmt.Errorf("empty metric name")) 61 case !metricNameRe.MatchString(monitor.MetricNamePrefix + key.MetricName): 62 panic(fmt.Errorf("invalid metric name %q: doesn't match %s", key.MetricName, metricNameRe)) 63 case exist: 64 panic(fmt.Errorf("duplicate metric name: metric %q with target %q is registered already", 65 key.MetricName, key.TargetType.Name)) 66 default: 67 for _, f := range fields { 68 if !metricFieldNameRe.MatchString(f.Name) { 69 panic(fmt.Errorf("invalid field name %q: doesn't match %s", 70 f.Name, metricFieldNameRe)) 71 } 72 } 73 } 74 registry[key] = m 75 } 76 77 // Iter calls a callback for each registered metric. 78 // 79 // Metrics are visited in no particular order. The callback must not modify 80 // the registry. 81 func Iter(cb func(m types.Metric)) { 82 lock.RLock() 83 defer lock.RUnlock() 84 for _, v := range registry { 85 cb(v) 86 } 87 }