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