github.com/facebookincubator/go-belt@v0.0.0-20230703220935-39cd348f1a38/tool/experimental/metrics/implementation/simplemetrics/metrics.go (about) 1 // Copyright 2022 Meta Platforms, Inc. and affiliates. 2 // 3 // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 // 5 // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 // 7 // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 // 9 // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 // 11 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 13 // Copyright (c) Facebook, Inc. and its affiliates. 14 // 15 // This source code is licensed under the MIT license found in the 16 // LICENSE file in the root directory of this source tree. 17 18 package simplemetrics 19 20 import ( 21 "fmt" 22 "sort" 23 "strings" 24 "sync" 25 26 "github.com/facebookincubator/go-belt/tool/experimental/metrics/types" 27 ) 28 29 var _ types.Metrics = &Metrics{} 30 31 type storage struct { 32 locker sync.RWMutex 33 intGaugeFamilies map[string]*intGaugeFamily 34 gaugeFamilies map[string]*gaugeFamily 35 countFamilies map[string]*countFamily 36 } 37 38 // Metrics is a naive implementation of Metrics 39 type Metrics struct { 40 *storage 41 fields *FieldsChain 42 } 43 44 // New returns an instance of Metrics. 45 func New() *Metrics { 46 return &Metrics{ 47 storage: &storage{ 48 intGaugeFamilies: make(map[string]*intGaugeFamily), 49 gaugeFamilies: make(map[string]*gaugeFamily), 50 countFamilies: make(map[string]*countFamily), 51 }, 52 } 53 } 54 55 func cleanStringForKey(in string) string { 56 return strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(in, "=", "=="), "_", "__"), ",", "_") 57 } 58 59 func fieldsToString(fields AbstractFields) string { 60 var fieldStrings []string 61 fields.ForEachField(func(f *Field) bool { 62 if !f.Properties.Has(types.FieldPropInclude) { 63 return true 64 } 65 key := cleanStringForKey(f.Key) 66 value := cleanStringForKey(fmt.Sprint(f.Value)) 67 fieldStrings = append(fieldStrings, fmt.Sprintf("%s=%s", key, value)) 68 return true 69 }) 70 sort.Slice(fieldStrings, func(i, j int) bool { 71 return fieldStrings[i] < fieldStrings[j] 72 }) 73 return strings.Join(fieldStrings, ",") 74 } 75 76 // Count implements metrics.Metrics. 77 func (metrics *Metrics) Count(key string) types.Count { 78 return metrics.CountFields(key, nil) 79 } 80 81 // CountFields implements metrics.Metrics. 82 func (metrics *Metrics) CountFields(key string, additionalFields AbstractFields) types.Count { 83 fields := metrics.fields.WithFields(additionalFields) 84 metrics.storage.locker.RLock() 85 family := metrics.countFamilies[key] 86 metrics.storage.locker.RUnlock() 87 if family != nil { 88 return family.get(fields) 89 } 90 91 metrics.storage.locker.Lock() 92 defer metrics.storage.locker.Unlock() 93 family = metrics.countFamilies[key] 94 if family != nil { 95 return family.get(fields) 96 } 97 98 family = &countFamily{ 99 Metrics: make(map[string]*Count), 100 } 101 metrics.countFamilies[key] = family 102 103 return family.get(fields) 104 } 105 106 // ForEachCount iterates through all Count metrics. Stops at first `false` returned by the callback function. 107 func (metrics *Metrics) ForEachCount(callback func(types.Count) bool) bool { 108 metrics.storage.locker.RLock() 109 defer metrics.storage.locker.RUnlock() 110 for _, family := range metrics.countFamilies { 111 for _, metric := range family.Metrics { 112 if !callback(metric) { 113 return false 114 } 115 } 116 } 117 return true 118 } 119 120 // Gauge implements metrics.Metrics. 121 func (metrics *Metrics) Gauge(key string) types.Gauge { 122 return metrics.GaugeFields(key, nil) 123 } 124 125 // GaugeFields implements metrics.Metrics. 126 func (metrics *Metrics) GaugeFields(key string, additionalFields AbstractFields) types.Gauge { 127 fields := metrics.fields.WithFields(additionalFields) 128 metrics.storage.locker.RLock() 129 family := metrics.gaugeFamilies[key] 130 metrics.storage.locker.RUnlock() 131 if family != nil { 132 return family.get(fields) 133 } 134 135 metrics.storage.locker.Lock() 136 defer metrics.storage.locker.Unlock() 137 family = metrics.gaugeFamilies[key] 138 if family != nil { 139 return family.get(fields) 140 } 141 142 family = &gaugeFamily{ 143 Metrics: make(map[string]*Gauge), 144 } 145 metrics.gaugeFamilies[key] = family 146 147 return family.get(fields) 148 } 149 150 // ForEachGauge iterates through all Gauge metrics. Stops at first `false` returned by the callback function. 151 func (metrics *Metrics) ForEachGauge(callback func(types.Gauge) bool) bool { 152 metrics.storage.locker.RLock() 153 defer metrics.storage.locker.RUnlock() 154 for _, family := range metrics.gaugeFamilies { 155 for _, metric := range family.Metrics { 156 if !callback(metric) { 157 return false 158 } 159 } 160 } 161 return true 162 } 163 164 // IntGauge implements metrics.Metrics. 165 func (metrics *Metrics) IntGauge(key string) types.IntGauge { 166 return metrics.IntGaugeFields(key, nil) 167 } 168 169 // IntGaugeFields implements metrics.Metrics. 170 func (metrics *Metrics) IntGaugeFields(key string, additionalFields AbstractFields) types.IntGauge { 171 fields := metrics.fields.WithFields(additionalFields) 172 metrics.storage.locker.RLock() 173 family := metrics.intGaugeFamilies[key] 174 metrics.storage.locker.RUnlock() 175 if family != nil { 176 return family.get(fields) 177 } 178 179 metrics.storage.locker.Lock() 180 defer metrics.storage.locker.Unlock() 181 family = metrics.intGaugeFamilies[key] 182 if family != nil { 183 return family.get(fields) 184 } 185 186 family = &intGaugeFamily{ 187 Metrics: make(map[string]*IntGauge), 188 } 189 metrics.intGaugeFamilies[key] = family 190 191 return family.get(fields) 192 } 193 194 // ForEachIntGauge iterates through all IntGauge metrics. Stops at first `false` returned by the callback function. 195 func (metrics *Metrics) ForEachIntGauge(callback func(types.IntGauge) bool) bool { 196 metrics.storage.locker.RLock() 197 defer metrics.storage.locker.RUnlock() 198 for _, family := range metrics.intGaugeFamilies { 199 for _, metric := range family.Metrics { 200 if !callback(metric) { 201 return false 202 } 203 } 204 } 205 return true 206 } 207 208 // WithContextFields implements metrics.Metrics. 209 func (metrics Metrics) WithContextFields(allFields *FieldsChain, newFieldsCount int) Tool { 210 metrics.fields = allFields 211 return &metrics 212 } 213 214 // WithTraceIDs implements metrics.Metrics. 215 func (metrics *Metrics) WithTraceIDs(traceIDs TraceIDs, newTraceIDsCount int) Tool { 216 // Should be ignored per metrics.Metrics interface description, so returning 217 // as is: 218 return metrics 219 } 220 221 // Flush implements metrics.Metrics (or more specifically belt.Tool). 222 func (*Metrics) Flush() {}