trpc.group/trpc-go/trpc-go@v1.0.3/metrics/metrics.go (about) 1 // 2 // 3 // Tencent is pleased to support the open source community by making tRPC available. 4 // 5 // Copyright (C) 2023 THL A29 Limited, a Tencent company. 6 // All rights reserved. 7 // 8 // If you have downloaded a copy of the tRPC source code from Tencent, 9 // please note that tRPC source code is licensed under the Apache 2.0 License, 10 // A copy of the Apache 2.0 License is included in this file. 11 // 12 // 13 14 // Package metrics defines some common metrics, such as Counter, IGauge, ITimer and IHistogram. 15 // The method MetricsSink is used to adapt to external monitor systems, such as monitors in our 16 // company or open source prometheus. 17 // 18 // For convenience, we provide two sorts of methods: 19 // 20 // 1. counter 21 // - reqNumCounter := metrics.Counter("req.num") 22 // reqNumCounter.Incr() 23 // - metrics.IncrCounter("req.num", 1) 24 // 25 // 2. gauge 26 // - cpuAvgLoad := metrics.Gauge("cpu.avgload") 27 // cpuAvgLoad.Set(0.75) 28 // - metrics.SetGauge("cpu.avgload", 0.75) 29 // 30 // 3. timer 31 // - timeCostTimer := metrics.Timer("req.proc.timecost") 32 // timeCostTimer.Record() 33 // - timeCostDuration := time.Millisecond * 2000 34 // metrics.RecordTimer("req.proc.timecost", timeCostDuration) 35 // 36 // 4. histogram 37 // - buckets := metrics.NewDurationBounds(time.Second, time.Second*2, time.Second*5), 38 // timeCostDist := metrics.Histogram("timecost.distribution", buckets) 39 // timeCostDist.AddSample(float64(time.Second*3)) 40 // - metrics.AddSample("timecost.distribution", buckets, float64(time.Second*3)) 41 package metrics 42 43 import ( 44 "fmt" 45 "sync" 46 "time" 47 ) 48 49 var ( 50 // metricsSinks emits same metrics information to multi external system at the same time. 51 metricsSinksMutex = sync.RWMutex{} 52 metricsSinks = map[string]Sink{} 53 54 countersMutex = sync.RWMutex{} 55 counters = map[string]ICounter{} 56 57 gaugesMutex = sync.RWMutex{} 58 gauges = map[string]IGauge{} 59 60 timersMutex = sync.RWMutex{} 61 timers = map[string]ITimer{} 62 63 histogramsMutex = sync.RWMutex{} 64 histograms = map[string]IHistogram{} 65 ) 66 67 // RegisterMetricsSink registers a Sink. 68 func RegisterMetricsSink(sink Sink) { 69 metricsSinksMutex.Lock() 70 metricsSinks[sink.Name()] = sink 71 metricsSinksMutex.Unlock() 72 if histSink, ok := sink.(HistogramSink); ok { 73 histogramsMutex.Lock() 74 for _, hist := range histograms { 75 if h, ok := hist.(*histogram); ok { 76 histSink.Register(h.Name, HistogramOption{BucketBounds: h.Spec}) 77 } 78 } 79 histogramsMutex.Unlock() 80 } 81 } 82 83 // GetMetricsSink gets a Sink by name 84 func GetMetricsSink(name string) (Sink, bool) { 85 metricsSinksMutex.RLock() 86 sink, ok := metricsSinks[name] 87 metricsSinksMutex.RUnlock() 88 return sink, ok 89 } 90 91 // Counter creates a named counter. 92 func Counter(name string) ICounter { 93 countersMutex.RLock() 94 c, ok := counters[name] 95 countersMutex.RUnlock() 96 if ok && c != nil { 97 return c 98 } 99 100 countersMutex.Lock() 101 c, ok = counters[name] 102 if ok && c != nil { 103 countersMutex.Unlock() 104 return c 105 } 106 c = &counter{name: name} 107 counters[name] = c 108 countersMutex.Unlock() 109 110 return c 111 } 112 113 // Gauge creates a named gauge. 114 func Gauge(name string) IGauge { 115 gaugesMutex.RLock() 116 c, ok := gauges[name] 117 gaugesMutex.RUnlock() 118 if ok && c != nil { 119 return c 120 } 121 122 gaugesMutex.Lock() 123 c, ok = gauges[name] 124 if ok && c != nil { 125 gaugesMutex.Unlock() 126 return c 127 } 128 c = &gauge{name: name} 129 gauges[name] = c 130 gaugesMutex.Unlock() 131 132 return c 133 } 134 135 // Timer creates a named timer. 136 func Timer(name string) ITimer { 137 timersMutex.RLock() 138 t, ok := timers[name] 139 timersMutex.RUnlock() 140 if ok && t != nil { 141 return t 142 } 143 144 timersMutex.Lock() 145 t, ok = timers[name] 146 if ok && t != nil { 147 timersMutex.Unlock() 148 return t 149 } 150 t = &timer{name: name, start: time.Now()} 151 timers[name] = t 152 timersMutex.Unlock() 153 154 return t 155 } 156 157 // NewTimer creates a named timer whose start is set to time.Now(). 158 func NewTimer(name string) ITimer { 159 t := Timer(name) 160 t.Reset() 161 return t 162 } 163 164 // Histogram creates a named histogram with buckets. 165 func Histogram(name string, buckets BucketBounds) IHistogram { 166 h, ok := GetHistogram(name) 167 if ok && h != nil { 168 return h 169 } 170 171 // histogramsMutex 的锁范围不应该包括 metricsSinksMutex 的锁。 172 histogramsMutex.Lock() 173 h, ok = histograms[name] 174 if ok && h != nil { 175 histogramsMutex.Unlock() 176 return h 177 } 178 h = newHistogram(name, buckets) 179 histograms[name] = h 180 histogramsMutex.Unlock() 181 182 metricsSinksMutex.Lock() 183 for _, sink := range metricsSinks { 184 if histSink, ok := sink.(HistogramSink); ok { 185 histSink.Register(name, HistogramOption{BucketBounds: buckets}) 186 } 187 } 188 metricsSinksMutex.Unlock() 189 190 return h 191 } 192 193 // GetHistogram gets the histogram by key. 194 func GetHistogram(key string) (v IHistogram, ok bool) { 195 histogramsMutex.RLock() 196 h, ok := histograms[key] 197 histogramsMutex.RUnlock() 198 if !ok { 199 return nil, false 200 } 201 202 hist, ok := h.(*histogram) 203 if !ok { 204 return nil, false 205 } 206 return hist, true 207 } 208 209 // IncrCounter increases counter key by value. Counters should accumulate values. 210 func IncrCounter(key string, value float64) { 211 Counter(key).IncrBy(value) 212 } 213 214 // SetGauge sets gauge key to value. An IGauge retains the last set value. 215 func SetGauge(key string, value float64) { 216 Gauge(key).Set(value) 217 } 218 219 // RecordTimer records timer named key with duration. 220 func RecordTimer(key string, duration time.Duration) { 221 Timer(key).RecordDuration(duration) 222 } 223 224 // AddSample adds one sample key with value. 225 func AddSample(key string, buckets BucketBounds, value float64) { 226 h := Histogram(key, buckets) 227 h.AddSample(value) 228 } 229 230 // Report reports a multi-dimension record. 231 func Report(rec Record, opts ...Option) (err error) { 232 var errs []error 233 for _, sink := range metricsSinks { 234 err = sink.Report(rec, opts...) 235 if err != nil { 236 errs = append(errs, fmt.Errorf("sink-%s error: %v", sink.Name(), err)) 237 } 238 } 239 240 if len(errs) == 0 { 241 return nil 242 } 243 return fmt.Errorf("metrics sink error: %v", errs) 244 }