github.com/shogo82148/goa-v1@v1.6.2/metrics.go (about) 1 //go:build !js 2 // +build !js 3 4 package goa 5 6 import ( 7 "regexp" 8 "strings" 9 "sync" 10 "time" 11 12 metrics "github.com/armon/go-metrics" 13 ) 14 15 const ( 16 allMatcher string = "*/*" 17 allReplacement string = "all" 18 normalizedToken string = "_" 19 ) 20 21 var ( 22 // metriks contains current collector 23 metriks Collector 24 25 // metriksMu is mutex for metriks variable 26 metriksMu sync.Mutex 27 28 // invalidCharactersRE is the invert match of validCharactersRE 29 invalidCharactersRE = regexp.MustCompile(`[\*/]`) 30 31 // Taken from https://github.com/prometheus/client_golang/blob/66058aac3a83021948e5fb12f1f408ff556b9037/prometheus/desc.go 32 validCharactersRE = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_:]*$`) 33 ) 34 35 // Collector is the interface used for collecting metrics. 36 type Collector interface { 37 AddSample(key []string, val float32) 38 EmitKey(key []string, val float32) 39 IncrCounter(key []string, val float32) 40 MeasureSince(key []string, start time.Time) 41 SetGauge(key []string, val float32) 42 } 43 44 func init() { 45 SetMetrics(NewNoOpCollector()) 46 } 47 48 // newNoOpCollector implements Collector, but provides no collection. 49 type noOpCollector struct{} 50 51 func (*noOpCollector) AddSample(key []string, val float32) {} 52 func (*noOpCollector) EmitKey(key []string, val float32) {} 53 func (*noOpCollector) IncrCounter(key []string, val float32) {} 54 func (*noOpCollector) MeasureSince(key []string, start time.Time) {} 55 func (*noOpCollector) SetGauge(key []string, val float32) {} 56 57 // NewNoOpCollector returns a Collector that does no collection. 58 func NewNoOpCollector() Collector { 59 return &noOpCollector{} 60 } 61 62 // NewNoOpSink returns a NOOP sink. 63 func NewNoOpSink() metrics.MetricSink { 64 return &NoOpSink{} 65 } 66 67 // NoOpSink default NOOP metrics recorder 68 type NoOpSink struct{} 69 70 // SetGauge implements metrics.MetricSin. 71 func (*NoOpSink) SetGauge(key []string, val float32) {} 72 73 // SetGaugeWithLabels implements metrics.MetricSin. 74 func (*NoOpSink) SetGaugeWithLabels(key []string, val float32, labels []metrics.Label) {} 75 76 // EmitKey implements metrics.MetricSin. 77 func (*NoOpSink) EmitKey(key []string, val float32) {} 78 79 // IncrCounter implements metrics.MetricSin. 80 func (*NoOpSink) IncrCounter(key []string, val float32) {} 81 82 // IncrCounterWithLabels implements metrics.MetricSin. 83 func (*NoOpSink) IncrCounterWithLabels(key []string, val float32, labels []metrics.Label) {} 84 85 // AddSample implements metrics.MetricSin. 86 func (*NoOpSink) AddSample(key []string, val float32) {} 87 88 // AddSampleWithLabels implements metrics.MetricSin. 89 func (*NoOpSink) AddSampleWithLabels(key []string, val float32, labels []metrics.Label) {} 90 91 // Shutdown implements metrics.MetricSin. 92 func (*NoOpSink) Shutdown() {} 93 94 // NewMetrics initializes goa's metrics instance with the supplied 95 // configuration and metrics sink 96 // This method is deprecated and SetMetrics should be used instead. 97 func NewMetrics(conf *metrics.Config, sink metrics.MetricSink) error { 98 m, err := metrics.NewGlobal(conf, sink) 99 if err != nil { 100 return err 101 } 102 SetMetrics(m) 103 104 return nil 105 } 106 107 // SetMetrics initializes goa's metrics instance with the supplied metrics adapter interface. 108 func SetMetrics(m Collector) { 109 metriksMu.Lock() 110 metriks = m 111 metriksMu.Unlock() 112 } 113 114 // GetMetrics returns goa's metrics collector adapter interface. 115 func GetMetrics() Collector { 116 metriksMu.Lock() 117 m := metriks 118 metriksMu.Unlock() 119 return m 120 } 121 122 // AddSample adds a sample to an aggregated metric 123 // reporting count, min, max, mean, and std deviation 124 // Usage: 125 // AddSample([]string{"my","namespace","key"}, 15.0) 126 func AddSample(key []string, val float32) { 127 normalizeKeys(key) 128 129 GetMetrics().AddSample(key, val) 130 } 131 132 // EmitKey emits a key/value pair 133 // Usage: 134 // EmitKey([]string{"my","namespace","key"}, 15.0) 135 func EmitKey(key []string, val float32) { 136 normalizeKeys(key) 137 138 GetMetrics().EmitKey(key, val) 139 } 140 141 // IncrCounter increments the counter named by `key` 142 // Usage: 143 // IncrCounter([]key{"my","namespace","counter"}, 1.0) 144 func IncrCounter(key []string, val float32) { 145 normalizeKeys(key) 146 147 GetMetrics().IncrCounter(key, val) 148 } 149 150 // MeasureSince creates a timing metric that records 151 // the duration of elapsed time since `start` 152 // Usage: 153 // MeasureSince([]string{"my","namespace","action}, time.Now()) 154 // Frequently used in a defer: 155 // defer MeasureSince([]string{"my","namespace","action}, time.Now()) 156 func MeasureSince(key []string, start time.Time) { 157 normalizeKeys(key) 158 159 GetMetrics().MeasureSince(key, start) 160 } 161 162 // SetGauge sets the named gauge to the specified value 163 // Usage: 164 // SetGauge([]string{"my","namespace"}, 2.0) 165 func SetGauge(key []string, val float32) { 166 normalizeKeys(key) 167 168 GetMetrics().SetGauge(key, val) 169 } 170 171 // This function is used to make metric names safe for all metric services. Specifically, prometheus does 172 // not support * or / in metric names. 173 func normalizeKeys(key []string) { 174 for i, k := range key { 175 if !validCharactersRE.MatchString(k) { 176 // first replace */* with all 177 k = strings.Replace(k, allMatcher, allReplacement, -1) 178 179 // now replace all other invalid characters with a safe one. 180 key[i] = invalidCharactersRE.ReplaceAllString(k, normalizedToken) 181 } 182 } 183 }