go.temporal.io/server@v1.23.0/common/metrics/metricstest/capture_handler.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package metricstest 26 27 import ( 28 "sync" 29 "time" 30 31 "go.temporal.io/server/common/log" 32 "go.temporal.io/server/common/metrics" 33 ) 34 35 // CapturedRecording is a single recording. Fields here should not be mutated. 36 type CapturedRecording struct { 37 Value any 38 Tags map[string]string 39 Unit metrics.MetricUnit 40 } 41 42 // Capture is a specific capture instance. 43 type Capture struct { 44 recordings map[string][]*CapturedRecording 45 recordingsLock sync.RWMutex 46 } 47 48 // Snapshot returns a copy of all metrics recorded, keyed by name. 49 func (c *Capture) Snapshot() map[string][]*CapturedRecording { 50 c.recordingsLock.RLock() 51 defer c.recordingsLock.RUnlock() 52 ret := make(map[string][]*CapturedRecording, len(c.recordings)) 53 for k, v := range c.recordings { 54 recs := make([]*CapturedRecording, len(v)) 55 copy(recs, v) 56 ret[k] = recs 57 } 58 return ret 59 } 60 61 func (c *Capture) record(name string, r *CapturedRecording) { 62 c.recordingsLock.Lock() 63 defer c.recordingsLock.Unlock() 64 c.recordings[name] = append(c.recordings[name], r) 65 } 66 67 // CaptureHandler is a [metrics.Handler] that captures each metric recording. 68 type CaptureHandler struct { 69 tags []metrics.Tag 70 captures map[*Capture]struct{} 71 capturesLock *sync.RWMutex 72 } 73 74 var _ metrics.Handler = (*CaptureHandler)(nil) 75 76 // NewCaptureHandler creates a new [metrics.Handler] that captures. 77 func NewCaptureHandler() *CaptureHandler { 78 return &CaptureHandler{ 79 captures: map[*Capture]struct{}{}, 80 capturesLock: &sync.RWMutex{}, 81 } 82 } 83 84 // StartCapture returns a started capture. StopCapture should be called on 85 // complete. 86 func (c *CaptureHandler) StartCapture() *Capture { 87 capture := &Capture{recordings: map[string][]*CapturedRecording{}} 88 c.capturesLock.Lock() 89 defer c.capturesLock.Unlock() 90 c.captures[capture] = struct{}{} 91 return capture 92 } 93 94 // StopCapture stops capturing metrics for the given capture instance. 95 func (c *CaptureHandler) StopCapture(capture *Capture) { 96 c.capturesLock.Lock() 97 defer c.capturesLock.Unlock() 98 delete(c.captures, capture) 99 } 100 101 // WithTags implements [metrics.Handler.WithTags]. 102 func (c *CaptureHandler) WithTags(tags ...metrics.Tag) metrics.Handler { 103 return &CaptureHandler{ 104 tags: append(append(make([]metrics.Tag, 0, len(c.tags)+len(tags)), c.tags...), tags...), 105 captures: c.captures, 106 capturesLock: c.capturesLock, 107 } 108 } 109 110 func (c *CaptureHandler) record(name string, v any, unit metrics.MetricUnit, tags ...metrics.Tag) { 111 rec := &CapturedRecording{Value: v, Tags: make(map[string]string, len(c.tags)+len(tags)), Unit: unit} 112 for _, tag := range c.tags { 113 rec.Tags[tag.Key()] = tag.Value() 114 } 115 for _, tag := range tags { 116 rec.Tags[tag.Key()] = tag.Value() 117 } 118 c.capturesLock.RLock() 119 defer c.capturesLock.RUnlock() 120 for c := range c.captures { 121 c.record(name, rec) 122 } 123 } 124 125 // Counter implements [metrics.Handler.Counter]. 126 func (c *CaptureHandler) Counter(name string) metrics.CounterIface { 127 return metrics.CounterFunc(func(v int64, tags ...metrics.Tag) { c.record(name, v, "", tags...) }) 128 } 129 130 // Gauge implements [metrics.Handler.Gauge]. 131 func (c *CaptureHandler) Gauge(name string) metrics.GaugeIface { 132 return metrics.GaugeFunc(func(v float64, tags ...metrics.Tag) { c.record(name, v, "", tags...) }) 133 } 134 135 // Timer implements [metrics.Handler.Timer]. 136 func (c *CaptureHandler) Timer(name string) metrics.TimerIface { 137 return metrics.TimerFunc(func(v time.Duration, tags ...metrics.Tag) { c.record(name, v, "", tags...) }) 138 } 139 140 // Histogram implements [metrics.Handler.Histogram]. 141 func (c *CaptureHandler) Histogram(name string, unit metrics.MetricUnit) metrics.HistogramIface { 142 return metrics.HistogramFunc(func(v int64, tags ...metrics.Tag) { c.record(name, v, unit, tags...) }) 143 } 144 145 // Stop implements [metrics.Handler.Stop]. 146 func (*CaptureHandler) Stop(log.Logger) {}