3
3
package goa
4
4
5
5
import (
6
+ "regexp"
7
+ "strings"
8
+ "sync/atomic"
6
9
"time"
7
10
8
11
"github.com/armon/go-metrics"
9
12
)
10
13
11
- // metriks is the local instance of metrics.Metrics
12
- var metriks * metrics.Metrics
14
+ const (
15
+ allMatcher string = "*/*"
16
+ allReplacement string = "all"
17
+ normalizedToken string = "_"
18
+ )
19
+
20
+ var (
21
+ // metriks atomic value storage
22
+ metriks atomic.Value
23
+
24
+ // invalidCharactersRE is the invert match of validCharactersRE
25
+ invalidCharactersRE = regexp .MustCompile (`[\*/]` )
26
+
27
+ // Taken from https://github.com/prometheus/client_golang/blob/66058aac3a83021948e5fb12f1f408ff556b9037/prometheus/desc.go
28
+ validCharactersRE = regexp .MustCompile (`^[a-zA-Z_][a-zA-Z0-9_:]*$` )
29
+ )
30
+
31
+ func init () {
32
+ m , err := metrics .New (metrics .DefaultConfig ("service" ), NewNoOpSink ())
33
+ if err != nil {
34
+ panic ("Unable to instantiate default metrics sink" )
35
+ }
36
+
37
+ SetMetrics (m )
38
+ }
39
+
40
+ // NewNoOpSink returns a NOOP sink.
41
+ func NewNoOpSink () metrics.MetricSink {
42
+ return & NoOpSink {}
43
+ }
44
+
45
+ // NoOpSink default NOOP metrics recorder
46
+ type NoOpSink struct {}
47
+
48
+ // SetGauge method
49
+ func (md * NoOpSink ) SetGauge (key []string , val float32 ) {}
50
+
51
+ // EmitKey method
52
+ func (md * NoOpSink ) EmitKey (key []string , val float32 ) {}
53
+
54
+ // IncrCounter method
55
+ func (md * NoOpSink ) IncrCounter (key []string , val float32 ) {}
56
+
57
+ // AddSample method
58
+ func (md * NoOpSink ) AddSample (key []string , val float32 ) {}
59
+
60
+ // MeasureSince method
61
+ func (md * NoOpSink ) MeasureSince (key []string , start time.Time ) {}
13
62
14
63
// NewMetrics initializes goa's metrics instance with the supplied
15
64
// configuration and metrics sink
65
+ // This method is deprecated and SetMetrics should be used instead.
16
66
func NewMetrics (conf * metrics.Config , sink metrics.MetricSink ) (err error ) {
17
- metriks , err = metrics .NewGlobal (conf , sink )
18
- return
67
+ m , err := metrics .NewGlobal (conf , sink )
68
+ SetMetrics (m )
69
+
70
+ return nil
71
+ }
72
+
73
+ // SetMetrics initializes goa's metrics instance with the supplied metrics adapter interface.
74
+ func SetMetrics (m * metrics.Metrics ) {
75
+ metriks .Store (m )
19
76
}
20
77
21
78
// AddSample adds a sample to an aggregated metric
22
79
// reporting count, min, max, mean, and std deviation
23
80
// Usage:
24
81
// AddSample([]string{"my","namespace","key"}, 15.0)
25
82
func AddSample (key []string , val float32 ) {
26
- if metriks != nil {
27
- metriks . AddSample ( key , val )
28
- }
83
+ normalizeKeys ( key )
84
+
85
+ metriks . Load ().( * metrics. Metrics ). AddSample ( key , val )
29
86
}
30
87
31
88
// EmitKey emits a key/value pair
32
89
// Usage:
33
90
// EmitKey([]string{"my","namespace","key"}, 15.0)
34
91
func EmitKey (key []string , val float32 ) {
35
- if metriks != nil {
36
- metriks . EmitKey ( key , val )
37
- }
92
+ normalizeKeys ( key )
93
+
94
+ metriks . Load ().( * metrics. Metrics ). EmitKey ( key , val )
38
95
}
39
96
40
97
// IncrCounter increments the counter named by `key`
41
98
// Usage:
42
99
// IncrCounter([]key{"my","namespace","counter"}, 1.0)
43
100
func IncrCounter (key []string , val float32 ) {
44
- if metriks != nil {
45
- metriks . IncrCounter ( key , val )
46
- }
101
+ normalizeKeys ( key )
102
+
103
+ metriks . Load ().( * metrics. Metrics ). IncrCounter ( key , val )
47
104
}
48
105
49
106
// MeasureSince creates a timing metric that records
@@ -53,16 +110,30 @@ func IncrCounter(key []string, val float32) {
53
110
// Frequently used in a defer:
54
111
// defer MeasureSince([]string{"my","namespace","action}, time.Now())
55
112
func MeasureSince (key []string , start time.Time ) {
56
- if metriks != nil {
57
- metriks . MeasureSince ( key , start )
58
- }
113
+ normalizeKeys ( key )
114
+
115
+ metriks . Load ().( * metrics. Metrics ). MeasureSince ( key , start )
59
116
}
60
117
61
118
// SetGauge sets the named gauge to the specified value
62
119
// Usage:
63
120
// SetGauge([]string{"my","namespace"}, 2.0)
64
121
func SetGauge (key []string , val float32 ) {
65
- if metriks != nil {
66
- metriks .SetGauge (key , val )
122
+ normalizeKeys (key )
123
+
124
+ metriks .Load ().(* metrics.Metrics ).SetGauge (key , val )
125
+ }
126
+
127
+ // This function is used to make metric names safe for all metric services. Specifically, prometheus does
128
+ // not support * or / in metric names.
129
+ func normalizeKeys (key []string ) {
130
+ for i , k := range key {
131
+ if ! validCharactersRE .MatchString (k ) {
132
+ // first replace */* with all
133
+ k = strings .Replace (k , allMatcher , allReplacement , - 1 )
134
+
135
+ // now replace all other invalid characters with a safe one.
136
+ key [i ] = invalidCharactersRE .ReplaceAllString (k , normalizedToken )
137
+ }
67
138
}
68
139
}
0 commit comments