forked from elastic/beats
-
Notifications
You must be signed in to change notification settings - Fork 4
/
go-metrics.go
229 lines (191 loc) · 6.18 KB
/
go-metrics.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
package adapter
import (
"fmt"
"reflect"
"sync"
"github.com/elastic/beats/libbeat/logp"
"github.com/elastic/beats/libbeat/monitoring"
metrics "github.com/rcrowley/go-metrics"
)
// implement adapter for adding go-metrics based counters
// to monitoring
// GoMetricsRegistry wraps a monitoring.Registry for filtering and registering
// go-metrics based metrics with the monitoring package. GoMetricsRegistry implements
// the go-metrics.Registry interface.
//
// Note: with the go-metrics using `interface{}`, there is no guarantee
// a variable satisfying any of go-metrics interfaces is returned.
// It's recommended to not mix go-metrics with other metrics types
// in the same namespace.
type GoMetricsRegistry struct {
mutex sync.Mutex
reg *monitoring.Registry
filters *metricFilters
shadow metrics.Registry // store non-accepted metrics
}
// GetGoMetrics wraps an existing monitoring.Registry with `name` into a
// GoMetricsRegistry for using the registry with go-metrics.Registry.
// If the monitoring.Registry does not exist yet, a new one will be generated.
//
// Note: with users of go-metrics potentially removing any metric at runtime,
// it's recommended to have the underlying registry being generated with
// `monitoring.IgnorePublishExpvar`.
func GetGoMetrics(parent *monitoring.Registry, name string, filters ...MetricFilter) *GoMetricsRegistry {
v := parent.Get(name)
if v == nil {
return NewGoMetrics(parent, name, filters...)
}
reg := v.(*monitoring.Registry)
return &GoMetricsRegistry{
reg: reg,
shadow: metrics.NewRegistry(),
filters: makeFilters(filters...),
}
}
// NewGoMetrics creates and registers a new GoMetricsRegistry with the parent
// registry.
func NewGoMetrics(parent *monitoring.Registry, name string, filters ...MetricFilter) *GoMetricsRegistry {
return &GoMetricsRegistry{
reg: parent.NewRegistry(name, monitoring.IgnorePublishExpvar),
shadow: metrics.NewRegistry(),
filters: makeFilters(filters...),
}
}
// Each only iterates the shadowed metrics, not registered to the monitoring package,
// as those metrics are owned by monitoring.Registry only.
func (r *GoMetricsRegistry) Each(f func(string, interface{})) {
r.shadow.Each(f)
}
func (r *GoMetricsRegistry) find(name string) interface{} {
st := r.findState(name)
if st.action == actIgnore {
return nil
}
return r.reg.Get(st.name)
}
// Get retrieves a registered metric by name. If the name is unknown, Get returns nil.
//
// Note: with the return values being `interface{}`, there is no guarantee
// a variable satisfying any of go-metrics interfaces is returned.
// It's recommended to not mix go-metrics with other metrics types in one
// namespace.
func (r *GoMetricsRegistry) Get(name string) interface{} {
r.mutex.Lock()
defer r.mutex.Unlock()
return r.get(name)
}
func (r *GoMetricsRegistry) get(name string) interface{} {
m := r.find(name)
if m == nil {
return r.shadow.Get(name)
}
if w, ok := m.(goMetricsWrapper); ok {
return w.wrapped()
}
return m
}
// GetOrRegister retries an existing metric via `Get` or registers a new one
// if the metric is unknown. For lazy instantiation metric can be a function.
func (r *GoMetricsRegistry) GetOrRegister(name string, metric interface{}) interface{} {
r.mutex.Lock()
defer r.mutex.Unlock()
v := r.get(name)
if v != nil {
return v
}
return r.doRegister(name, metric)
}
// Register adds a new metric.
// An error is returned if the metric is already known.
func (r *GoMetricsRegistry) Register(name string, metric interface{}) error {
r.mutex.Lock()
defer r.mutex.Unlock()
if r.get(name) != nil {
return fmt.Errorf("metric '%v' already registered", name)
}
r.doRegister(name, metric)
return nil
}
func (r *GoMetricsRegistry) doRegister(name string, metric interface{}) interface{} {
if v := reflect.ValueOf(metric); v.Kind() == reflect.Func {
metric = v.Call(nil)[0].Interface()
}
st := r.addState(name, metric)
if st.action == actIgnore {
return r.shadow.GetOrRegister(name, st.metric)
}
if st.action == actAccept {
w, ok := goMetricsWrap(st.metric)
if ok {
r.reg.Add(st.name, w, st.mode)
}
}
return st.metric
}
// RunHealthchecks is a noop, required to satisfy the metrics.Registry interface.
func (r *GoMetricsRegistry) RunHealthchecks() {}
// Unregister removes a metric.
func (r *GoMetricsRegistry) Unregister(name string) {
r.mutex.Lock()
defer r.mutex.Unlock()
st := r.rmState(name)
r.reg.Remove(st.name)
r.shadow.Unregister(name)
}
// UnregisterAll calls `Clear` on the underlying monitoring.Registry
func (r *GoMetricsRegistry) UnregisterAll() {
r.mutex.Lock()
defer r.mutex.Unlock()
r.shadow.UnregisterAll()
err := r.reg.Clear()
if err != nil {
logp.Err("Failed to clear registry: %v", err)
}
}
func (r *GoMetricsRegistry) findState(name string) state {
return r.stateWith(kndFind, name, nil)
}
func (r *GoMetricsRegistry) addState(name string, metric interface{}) state {
return r.stateWith(kndAdd, name, metric)
}
func (r *GoMetricsRegistry) rmState(name string) state {
return r.stateWith(kndRemove, name, nil)
}
func (r *GoMetricsRegistry) stateWith(k kind, name string, metric interface{}) state {
return r.filters.apply(state{
kind: k,
action: actIgnore,
reg: r.reg,
name: name,
mode: monitoring.Full,
metric: metric,
})
}
// GoMetricsRegistry MetricFilter used to convert all metrics not being
// accepted by the filters to be replace with a Noop-metric.
// This can be used to disable metrics in go-metrics users lazily generating
// metrics via GetOrRegister.
var GoMetricsNilify = withVarFilter(func(st state) state {
if st.action != actIgnore {
return st
}
switch st.metric.(type) {
case *metrics.StandardCounter:
st.metric = metrics.NilCounter{}
case *metrics.StandardEWMA:
st.metric = metrics.NilEWMA{}
case *metrics.StandardGauge:
st.metric = metrics.NilGauge{}
case *metrics.StandardGaugeFloat64:
st.metric = metrics.NilGaugeFloat64{}
case *metrics.StandardHealthcheck:
st.metric = metrics.NilHealthcheck{}
case *metrics.StandardHistogram:
st.metric = metrics.NilHistogram{}
case *metrics.StandardMeter:
st.metric = metrics.NilMeter{}
case *metrics.StandardTimer:
st.metric = metrics.NilTimer{}
}
return st
})