blob: b3bab64e15b3896ca24d875a79c6d8495d31c502 [file] [log] [blame]
Scott Bakered4efab2020-01-13 19:12:25 -08001package metrics
2
3import (
4 "fmt"
5 "reflect"
6 "strings"
7 "sync"
8)
9
10// DuplicateMetric is the error returned by Registry.Register when a metric
11// already exists. If you mean to Register that metric you must first
12// Unregister the existing metric.
13type DuplicateMetric string
14
15func (err DuplicateMetric) Error() string {
16 return fmt.Sprintf("duplicate metric: %s", string(err))
17}
18
19// A Registry holds references to a set of metrics by name and can iterate
20// over them, calling callback functions provided by the user.
21//
22// This is an interface so as to encourage other structs to implement
23// the Registry API as appropriate.
24type Registry interface {
25
26 // Call the given function for each registered metric.
27 Each(func(string, interface{}))
28
29 // Get the metric by the given name or nil if none is registered.
30 Get(string) interface{}
31
32 // GetAll metrics in the Registry.
33 GetAll() map[string]map[string]interface{}
34
35 // Gets an existing metric or registers the given one.
36 // The interface can be the metric to register if not found in registry,
37 // or a function returning the metric for lazy instantiation.
38 GetOrRegister(string, interface{}) interface{}
39
40 // Register the given metric under the given name.
41 Register(string, interface{}) error
42
43 // Run all registered healthchecks.
44 RunHealthchecks()
45
46 // Unregister the metric with the given name.
47 Unregister(string)
48
49 // Unregister all metrics. (Mostly for testing.)
50 UnregisterAll()
51}
52
53// The standard implementation of a Registry is a mutex-protected map
54// of names to metrics.
55type StandardRegistry struct {
56 metrics map[string]interface{}
57 mutex sync.RWMutex
58}
59
60// Create a new registry.
61func NewRegistry() Registry {
62 return &StandardRegistry{metrics: make(map[string]interface{})}
63}
64
65// Call the given function for each registered metric.
66func (r *StandardRegistry) Each(f func(string, interface{})) {
67 for name, i := range r.registered() {
68 f(name, i)
69 }
70}
71
72// Get the metric by the given name or nil if none is registered.
73func (r *StandardRegistry) Get(name string) interface{} {
74 r.mutex.RLock()
75 defer r.mutex.RUnlock()
76 return r.metrics[name]
77}
78
79// Gets an existing metric or creates and registers a new one. Threadsafe
80// alternative to calling Get and Register on failure.
81// The interface can be the metric to register if not found in registry,
82// or a function returning the metric for lazy instantiation.
83func (r *StandardRegistry) GetOrRegister(name string, i interface{}) interface{} {
84 // access the read lock first which should be re-entrant
85 r.mutex.RLock()
86 metric, ok := r.metrics[name]
87 r.mutex.RUnlock()
88 if ok {
89 return metric
90 }
91
92 // only take the write lock if we'll be modifying the metrics map
93 r.mutex.Lock()
94 defer r.mutex.Unlock()
95 if metric, ok := r.metrics[name]; ok {
96 return metric
97 }
98 if v := reflect.ValueOf(i); v.Kind() == reflect.Func {
99 i = v.Call(nil)[0].Interface()
100 }
101 r.register(name, i)
102 return i
103}
104
105// Register the given metric under the given name. Returns a DuplicateMetric
106// if a metric by the given name is already registered.
107func (r *StandardRegistry) Register(name string, i interface{}) error {
108 r.mutex.Lock()
109 defer r.mutex.Unlock()
110 return r.register(name, i)
111}
112
113// Run all registered healthchecks.
114func (r *StandardRegistry) RunHealthchecks() {
115 r.mutex.RLock()
116 defer r.mutex.RUnlock()
117 for _, i := range r.metrics {
118 if h, ok := i.(Healthcheck); ok {
119 h.Check()
120 }
121 }
122}
123
124// GetAll metrics in the Registry
125func (r *StandardRegistry) GetAll() map[string]map[string]interface{} {
126 data := make(map[string]map[string]interface{})
127 r.Each(func(name string, i interface{}) {
128 values := make(map[string]interface{})
129 switch metric := i.(type) {
130 case Counter:
131 values["count"] = metric.Count()
132 case Gauge:
133 values["value"] = metric.Value()
134 case GaugeFloat64:
135 values["value"] = metric.Value()
136 case Healthcheck:
137 values["error"] = nil
138 metric.Check()
139 if err := metric.Error(); nil != err {
140 values["error"] = metric.Error().Error()
141 }
142 case Histogram:
143 h := metric.Snapshot()
144 ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
145 values["count"] = h.Count()
146 values["min"] = h.Min()
147 values["max"] = h.Max()
148 values["mean"] = h.Mean()
149 values["stddev"] = h.StdDev()
150 values["median"] = ps[0]
151 values["75%"] = ps[1]
152 values["95%"] = ps[2]
153 values["99%"] = ps[3]
154 values["99.9%"] = ps[4]
155 case Meter:
156 m := metric.Snapshot()
157 values["count"] = m.Count()
158 values["1m.rate"] = m.Rate1()
159 values["5m.rate"] = m.Rate5()
160 values["15m.rate"] = m.Rate15()
161 values["mean.rate"] = m.RateMean()
162 case Timer:
163 t := metric.Snapshot()
164 ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
165 values["count"] = t.Count()
166 values["min"] = t.Min()
167 values["max"] = t.Max()
168 values["mean"] = t.Mean()
169 values["stddev"] = t.StdDev()
170 values["median"] = ps[0]
171 values["75%"] = ps[1]
172 values["95%"] = ps[2]
173 values["99%"] = ps[3]
174 values["99.9%"] = ps[4]
175 values["1m.rate"] = t.Rate1()
176 values["5m.rate"] = t.Rate5()
177 values["15m.rate"] = t.Rate15()
178 values["mean.rate"] = t.RateMean()
179 }
180 data[name] = values
181 })
182 return data
183}
184
185// Unregister the metric with the given name.
186func (r *StandardRegistry) Unregister(name string) {
187 r.mutex.Lock()
188 defer r.mutex.Unlock()
189 r.stop(name)
190 delete(r.metrics, name)
191}
192
193// Unregister all metrics. (Mostly for testing.)
194func (r *StandardRegistry) UnregisterAll() {
195 r.mutex.Lock()
196 defer r.mutex.Unlock()
197 for name, _ := range r.metrics {
198 r.stop(name)
199 delete(r.metrics, name)
200 }
201}
202
203func (r *StandardRegistry) register(name string, i interface{}) error {
204 if _, ok := r.metrics[name]; ok {
205 return DuplicateMetric(name)
206 }
207 switch i.(type) {
208 case Counter, Gauge, GaugeFloat64, Healthcheck, Histogram, Meter, Timer:
209 r.metrics[name] = i
210 }
211 return nil
212}
213
214func (r *StandardRegistry) registered() map[string]interface{} {
215 r.mutex.Lock()
216 defer r.mutex.Unlock()
217 metrics := make(map[string]interface{}, len(r.metrics))
218 for name, i := range r.metrics {
219 metrics[name] = i
220 }
221 return metrics
222}
223
224func (r *StandardRegistry) stop(name string) {
225 if i, ok := r.metrics[name]; ok {
226 if s, ok := i.(Stoppable); ok {
227 s.Stop()
228 }
229 }
230}
231
232// Stoppable defines the metrics which has to be stopped.
233type Stoppable interface {
234 Stop()
235}
236
237type PrefixedRegistry struct {
238 underlying Registry
239 prefix string
240}
241
242func NewPrefixedRegistry(prefix string) Registry {
243 return &PrefixedRegistry{
244 underlying: NewRegistry(),
245 prefix: prefix,
246 }
247}
248
249func NewPrefixedChildRegistry(parent Registry, prefix string) Registry {
250 return &PrefixedRegistry{
251 underlying: parent,
252 prefix: prefix,
253 }
254}
255
256// Call the given function for each registered metric.
257func (r *PrefixedRegistry) Each(fn func(string, interface{})) {
258 wrappedFn := func(prefix string) func(string, interface{}) {
259 return func(name string, iface interface{}) {
260 if strings.HasPrefix(name, prefix) {
261 fn(name, iface)
262 } else {
263 return
264 }
265 }
266 }
267
268 baseRegistry, prefix := findPrefix(r, "")
269 baseRegistry.Each(wrappedFn(prefix))
270}
271
272func findPrefix(registry Registry, prefix string) (Registry, string) {
273 switch r := registry.(type) {
274 case *PrefixedRegistry:
275 return findPrefix(r.underlying, r.prefix+prefix)
276 case *StandardRegistry:
277 return r, prefix
278 }
279 return nil, ""
280}
281
282// Get the metric by the given name or nil if none is registered.
283func (r *PrefixedRegistry) Get(name string) interface{} {
284 realName := r.prefix + name
285 return r.underlying.Get(realName)
286}
287
288// Gets an existing metric or registers the given one.
289// The interface can be the metric to register if not found in registry,
290// or a function returning the metric for lazy instantiation.
291func (r *PrefixedRegistry) GetOrRegister(name string, metric interface{}) interface{} {
292 realName := r.prefix + name
293 return r.underlying.GetOrRegister(realName, metric)
294}
295
296// Register the given metric under the given name. The name will be prefixed.
297func (r *PrefixedRegistry) Register(name string, metric interface{}) error {
298 realName := r.prefix + name
299 return r.underlying.Register(realName, metric)
300}
301
302// Run all registered healthchecks.
303func (r *PrefixedRegistry) RunHealthchecks() {
304 r.underlying.RunHealthchecks()
305}
306
307// GetAll metrics in the Registry
308func (r *PrefixedRegistry) GetAll() map[string]map[string]interface{} {
309 return r.underlying.GetAll()
310}
311
312// Unregister the metric with the given name. The name will be prefixed.
313func (r *PrefixedRegistry) Unregister(name string) {
314 realName := r.prefix + name
315 r.underlying.Unregister(realName)
316}
317
318// Unregister all metrics. (Mostly for testing.)
319func (r *PrefixedRegistry) UnregisterAll() {
320 r.underlying.UnregisterAll()
321}
322
323var DefaultRegistry Registry = NewRegistry()
324
325// Call the given function for each registered metric.
326func Each(f func(string, interface{})) {
327 DefaultRegistry.Each(f)
328}
329
330// Get the metric by the given name or nil if none is registered.
331func Get(name string) interface{} {
332 return DefaultRegistry.Get(name)
333}
334
335// Gets an existing metric or creates and registers a new one. Threadsafe
336// alternative to calling Get and Register on failure.
337func GetOrRegister(name string, i interface{}) interface{} {
338 return DefaultRegistry.GetOrRegister(name, i)
339}
340
341// Register the given metric under the given name. Returns a DuplicateMetric
342// if a metric by the given name is already registered.
343func Register(name string, i interface{}) error {
344 return DefaultRegistry.Register(name, i)
345}
346
347// Register the given metric under the given name. Panics if a metric by the
348// given name is already registered.
349func MustRegister(name string, i interface{}) {
350 if err := Register(name, i); err != nil {
351 panic(err)
352 }
353}
354
355// Run all registered healthchecks.
356func RunHealthchecks() {
357 DefaultRegistry.RunHealthchecks()
358}
359
360// Unregister the metric with the given name.
361func Unregister(name string) {
362 DefaultRegistry.Unregister(name)
363}