github.com/wfusion/gofusion@v1.1.14/common/infra/metrics/sink.go (about) 1 package metrics 2 3 import ( 4 "fmt" 5 "net/url" 6 7 "github.com/wfusion/gofusion/common/utils" 8 ) 9 10 // The MetricSink interface is used to transmit metrics information 11 // to an external system 12 type MetricSink interface { 13 // SetGauge A Gauge should retain the last value it is set to 14 SetGauge(key []string, val float32, opts ...utils.OptionExtender) 15 SetGaugeWithLabels(key []string, val float32, labels []Label, opts ...utils.OptionExtender) 16 17 // EmitKey Should emit a Key/Value pair for each call 18 EmitKey(key []string, val float32, opts ...utils.OptionExtender) 19 20 // IncrCounter should accumulate values 21 IncrCounter(key []string, val float32, opts ...utils.OptionExtender) 22 IncrCounterWithLabels(key []string, val float32, labels []Label, opts ...utils.OptionExtender) 23 24 // AddSample are for timing information, where quantiles are used 25 AddSample(key []string, val float32, opts ...utils.OptionExtender) 26 AddSampleWithLabels(key []string, val float32, labels []Label, opts ...utils.OptionExtender) 27 } 28 29 // PrecisionGaugeMetricSink interface is used to support 64 bit precisions for Sinks, if needed. 30 type PrecisionGaugeMetricSink interface { 31 SetPrecisionGauge(key []string, val float64, opts ...utils.OptionExtender) 32 SetPrecisionGaugeWithLabels(key []string, val float64, labels []Label, opts ...utils.OptionExtender) 33 } 34 35 // PrecisionSampleMetricSink interface is used to support 64 bit precisions for Sinks, if needed. 36 type PrecisionSampleMetricSink interface { 37 AddPrecisionSample(key []string, val float64, opts ...utils.OptionExtender) 38 AddPrecisionSampleWithLabels(key []string, val float64, labels []Label, opts ...utils.OptionExtender) 39 } 40 41 type ShutdownSink interface { 42 MetricSink 43 44 // Shutdown the metric sink, flush metrics to storage, and cleanup resources. 45 // Called immediately prior to application exit. Implementations must block 46 // until metrics are flushed to storage. 47 Shutdown() 48 } 49 50 // BlackholeSink is used to just blackhole messages 51 type BlackholeSink struct{} 52 53 func (*BlackholeSink) SetGauge(key []string, val float32, opts ...utils.OptionExtender) { 54 } 55 func (*BlackholeSink) SetGaugeWithLabels(key []string, val float32, labels []Label, opts ...utils.OptionExtender) { 56 } 57 func (*BlackholeSink) SetPrecisionGauge(key []string, val float64, opts ...utils.OptionExtender) { 58 } 59 func (*BlackholeSink) SetPrecisionGaugeWithLabels(key []string, val float64, labels []Label, 60 opts ...utils.OptionExtender) { 61 } 62 func (*BlackholeSink) EmitKey(key []string, val float32, opts ...utils.OptionExtender) { 63 } 64 func (*BlackholeSink) IncrCounter(key []string, val float32, opts ...utils.OptionExtender) { 65 } 66 func (*BlackholeSink) IncrCounterWithLabels(key []string, val float32, labels []Label, opts ...utils.OptionExtender) { 67 } 68 func (*BlackholeSink) AddSample(key []string, val float32, opts ...utils.OptionExtender) { 69 } 70 func (*BlackholeSink) AddSampleWithLabels(key []string, val float32, labels []Label, opts ...utils.OptionExtender) { 71 } 72 73 // FanoutSink is used to sink to fanout values to multiple sinks 74 type FanoutSink []MetricSink 75 76 func (fh FanoutSink) SetGauge(key []string, val float32, opts ...utils.OptionExtender) { 77 fh.SetGaugeWithLabels(key, val, nil) 78 } 79 80 func (fh FanoutSink) SetGaugeWithLabels(key []string, val float32, labels []Label, opts ...utils.OptionExtender) { 81 for _, s := range fh { 82 s.SetGaugeWithLabels(key, val, labels) 83 } 84 } 85 86 func (fh FanoutSink) SetPrecisionGauge(key []string, val float64, opts ...utils.OptionExtender) { 87 fh.SetPrecisionGaugeWithLabels(key, val, nil) 88 } 89 90 func (fh FanoutSink) SetPrecisionGaugeWithLabels(key []string, val float64, labels []Label, 91 opts ...utils.OptionExtender) { 92 for _, s := range fh { 93 // The Sink needs to implement PrecisionGaugeMetricSink, 94 // in case it doesn't, the metric value won't be set and ignored instead 95 if s64, ok := s.(PrecisionGaugeMetricSink); ok { 96 s64.SetPrecisionGaugeWithLabels(key, val, labels) 97 } 98 } 99 } 100 101 func (fh FanoutSink) EmitKey(key []string, val float32, opts ...utils.OptionExtender) { 102 for _, s := range fh { 103 s.EmitKey(key, val) 104 } 105 } 106 107 func (fh FanoutSink) IncrCounter(key []string, val float32, opts ...utils.OptionExtender) { 108 fh.IncrCounterWithLabels(key, val, nil) 109 } 110 111 func (fh FanoutSink) IncrCounterWithLabels(key []string, val float32, labels []Label, opts ...utils.OptionExtender) { 112 for _, s := range fh { 113 s.IncrCounterWithLabels(key, val, labels) 114 } 115 } 116 117 func (fh FanoutSink) AddSample(key []string, val float32, opts ...utils.OptionExtender) { 118 fh.AddSampleWithLabels(key, val, nil) 119 } 120 121 func (fh FanoutSink) AddSampleWithLabels(key []string, val float32, labels []Label, opts ...utils.OptionExtender) { 122 for _, s := range fh { 123 s.AddSampleWithLabels(key, val, labels) 124 } 125 } 126 127 func (fh FanoutSink) Shutdown() { 128 for _, s := range fh { 129 if ss, ok := s.(ShutdownSink); ok { 130 ss.Shutdown() 131 } 132 } 133 } 134 135 // sinkURLFactoryFunc is a generic interface around the *SinkFromURL() function provided 136 // by each sink type 137 type sinkURLFactoryFunc func(*url.URL) (MetricSink, error) 138 139 // sinkRegistry supports the generic NewMetricSink function by mapping URL 140 // schemes to metric sink factory functions 141 var sinkRegistry = map[string]sinkURLFactoryFunc{ 142 "statsd": NewStatsdSinkFromURL, 143 "statsite": NewStatsiteSinkFromURL, 144 "inmem": NewInmemSinkFromURL, 145 } 146 147 // NewMetricSinkFromURL allows a generic URL input to configure any of the 148 // supported sinks. The scheme of the URL identifies the type of the sink, the 149 // and query parameters are used to set options. 150 // 151 // "statsd://" - Initializes a StatsdSink. The host and port are passed through 152 // as the "addr" of the sink 153 // 154 // "statsite://" - Initializes a StatsiteSink. The host and port become the 155 // "addr" of the sink 156 // 157 // "inmem://" - Initializes an InmemSink. The host and port are ignored. The 158 // "interval" and "duration" query parameters must be specified with valid 159 // durations, see NewInmemSink for details. 160 func NewMetricSinkFromURL(urlStr string) (MetricSink, error) { 161 u, err := url.Parse(urlStr) 162 if err != nil { 163 return nil, err 164 } 165 166 sinkURLFactoryFunc := sinkRegistry[u.Scheme] 167 if sinkURLFactoryFunc == nil { 168 return nil, fmt.Errorf( 169 "cannot create metric sink, unrecognized sink name: %q", u.Scheme) 170 } 171 172 return sinkURLFactoryFunc(u) 173 } 174 175 type Option struct { 176 Buckets []float64 177 Precision bool 178 } 179 180 func Precision() utils.OptionFunc[Option] { 181 return func(o *Option) { 182 o.Precision = true 183 } 184 }