forked from vitessio/vitess
-
Notifications
You must be signed in to change notification settings - Fork 0
/
export.go
384 lines (327 loc) · 8.86 KB
/
export.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
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
// Copyright 2012, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package stats is a wrapper for expvar. It addtionally
// exports new types that can be used to track performance.
// It also provides a callback hook that allows a program
// to export the variables using methods other than /debug/vars.
// All variables support a String function that
// is expected to return a JSON representation
// of the variable.
// Any function named Add will add the specified
// number to the variable.
// Any function named Counts returns a map of counts
// that can be used by Rates to track rates over time.
package stats
import (
"bytes"
"expvar"
"flag"
"fmt"
"strconv"
"sync"
"time"
log "github.com/golang/glog"
"github.com/youtube/vitess/go/sync2"
)
var emitStats = flag.Bool("emit_stats", false, "true iff we should emit stats to push-based monitoring/stats backends")
var statsEmitPeriod = flag.Duration("stats_emit_period", time.Duration(60*time.Second), "Interval between emitting stats to all registered backends")
var statsBackend = flag.String("stats_backend", "influxdb", "The name of the registered push-based monitoring/stats backend to use")
// NewVarHook is the type of a hook to export variables in a different way
type NewVarHook func(name string, v expvar.Var)
type varGroup struct {
sync.Mutex
vars map[string]expvar.Var
newVarHook NewVarHook
}
func (vg *varGroup) register(nvh NewVarHook) {
vg.Lock()
defer vg.Unlock()
if vg.newVarHook != nil {
panic("You've already registered a function")
}
if nvh == nil {
panic("nil not allowed")
}
vg.newVarHook = nvh
// Call hook on existing vars because some might have been
// created before the call to register
for k, v := range vg.vars {
nvh(k, v)
}
vg.vars = nil
}
func (vg *varGroup) publish(name string, v expvar.Var) {
vg.Lock()
defer vg.Unlock()
expvar.Publish(name, v)
if vg.newVarHook != nil {
vg.newVarHook(name, v)
} else {
vg.vars[name] = v
}
}
var defaultVarGroup = varGroup{vars: make(map[string]expvar.Var)}
// Register allows you to register a callback function
// that will be called whenever a new stats variable gets
// created. This can be used to build alternate methods
// of exporting stats variables.
func Register(nvh NewVarHook) {
defaultVarGroup.register(nvh)
}
// Publish is expvar.Publish+hook
func Publish(name string, v expvar.Var) {
defaultVarGroup.publish(name, v)
}
// PushBackend is an interface for any stats/metrics backend that requires data
// to be pushed to it. It's used to support push-based metrics backends, as expvar
// by default only supports pull-based ones.
type PushBackend interface {
// PushAll pushes all stats from expvar to the backend
PushAll() error
}
var pushBackends = make(map[string]PushBackend)
var once sync.Once
// RegisterPushBackend allows modules to register PushBackend implementations.
// Should be called on init().
func RegisterPushBackend(name string, backend PushBackend) {
if _, ok := pushBackends[name]; ok {
log.Fatalf("PushBackend %s already exists; can't register the same name multiple times", name)
}
pushBackends[name] = backend
if *emitStats {
// Start a single goroutine to emit stats periodically
once.Do(func() {
go emitToBackend(statsEmitPeriod)
})
}
}
// emitToBackend does a periodic emit to the selected PushBackend. If a push fails,
// it will be logged as a warning (but things will otherwise proceed as normal).
func emitToBackend(emitPeriod *time.Duration) {
ticker := time.NewTicker(*emitPeriod)
defer ticker.Stop()
for _ = range ticker.C {
backend, ok := pushBackends[*statsBackend]
if !ok {
log.Errorf("No PushBackend registered with name %s", *statsBackend)
return
}
err := backend.PushAll()
if err != nil {
// TODO(aaijazi): This might cause log spam...
log.Warningf("Pushing stats to backend %v failed: %v", *statsBackend, err)
}
}
}
// Float is expvar.Float+Get+hook
type Float struct {
mu sync.Mutex
f float64
}
// NewFloat creates a new Float and exports it.
func NewFloat(name string) *Float {
v := new(Float)
Publish(name, v)
return v
}
// Add adds the provided value to the Float
func (v *Float) Add(delta float64) {
v.mu.Lock()
v.f += delta
v.mu.Unlock()
}
// Set sets the value
func (v *Float) Set(value float64) {
v.mu.Lock()
v.f = value
v.mu.Unlock()
}
// Get returns the value
func (v *Float) Get() float64 {
v.mu.Lock()
f := v.f
v.mu.Unlock()
return f
}
// String is the implementation of expvar.var
func (v *Float) String() string {
return strconv.FormatFloat(v.Get(), 'g', -1, 64)
}
// FloatFunc converts a function that returns
// a float64 as an expvar.
type FloatFunc func() float64
// String is the implementation of expvar.var
func (f FloatFunc) String() string {
return strconv.FormatFloat(f(), 'g', -1, 64)
}
// Int is expvar.Int+Get+hook
type Int struct {
i sync2.AtomicInt64
}
// NewInt returns a new Int
func NewInt(name string) *Int {
v := new(Int)
if name != "" {
Publish(name, v)
}
return v
}
// Add adds the provided value to the Int
func (v *Int) Add(delta int64) {
v.i.Add(delta)
}
// Set sets the value
func (v *Int) Set(value int64) {
v.i.Set(value)
}
// Get returns the value
func (v *Int) Get() int64 {
return v.i.Get()
}
// String is the implementation of expvar.var
func (v *Int) String() string {
return strconv.FormatInt(v.i.Get(), 10)
}
// Duration exports a time.Duration
type Duration struct {
i sync2.AtomicDuration
}
// NewDuration returns a new Duration
func NewDuration(name string) *Duration {
v := new(Duration)
Publish(name, v)
return v
}
// Add adds the provided value to the Duration
func (v *Duration) Add(delta time.Duration) {
v.i.Add(delta)
}
// Set sets the value
func (v *Duration) Set(value time.Duration) {
v.i.Set(value)
}
// Get returns the value
func (v *Duration) Get() time.Duration {
return v.i.Get()
}
// String is the implementation of expvar.var
func (v *Duration) String() string {
return strconv.FormatInt(int64(v.i.Get()), 10)
}
// IntFunc converts a function that returns
// an int64 as an expvar.
type IntFunc func() int64
// String is the implementation of expvar.var
func (f IntFunc) String() string {
return strconv.FormatInt(f(), 10)
}
// DurationFunc converts a function that returns
// an time.Duration as an expvar.
type DurationFunc func() time.Duration
// String is the implementation of expvar.var
func (f DurationFunc) String() string {
return strconv.FormatInt(int64(f()), 10)
}
// String is expvar.String+Get+hook
type String struct {
mu sync.Mutex
s string
}
// NewString returns a new String
func NewString(name string) *String {
v := new(String)
Publish(name, v)
return v
}
// Set sets the value
func (v *String) Set(value string) {
v.mu.Lock()
v.s = value
v.mu.Unlock()
}
// Get returns the value
func (v *String) Get() string {
v.mu.Lock()
s := v.s
v.mu.Unlock()
return s
}
// String is the implementation of expvar.var
func (v *String) String() string {
return strconv.Quote(v.Get())
}
// StringFunc converts a function that returns
// an string as an expvar.
type StringFunc func() string
// String is the implementation of expvar.var
func (f StringFunc) String() string {
return strconv.Quote(f())
}
// JSONFunc is the public type for a single function that returns json directly.
type JSONFunc func() string
// String is the implementation of expvar.var
func (f JSONFunc) String() string {
return f()
}
// PublishJSONFunc publishes any function that returns
// a JSON string as a variable. The string is sent to
// expvar as is.
func PublishJSONFunc(name string, f func() string) {
Publish(name, JSONFunc(f))
}
// StringMap is a map of string -> string
type StringMap struct {
mu sync.Mutex
values map[string]string
}
// NewStringMap returns a new StringMap
func NewStringMap(name string) *StringMap {
v := &StringMap{values: make(map[string]string)}
Publish(name, v)
return v
}
// Set will set a value (existing or not)
func (v *StringMap) Set(name, value string) {
v.mu.Lock()
v.values[name] = value
v.mu.Unlock()
}
// Get will return the value, or "" f not set.
func (v *StringMap) Get(name string) string {
v.mu.Lock()
s := v.values[name]
v.mu.Unlock()
return s
}
// String is the implementation of expvar.Var
func (v *StringMap) String() string {
v.mu.Lock()
defer v.mu.Unlock()
return stringMapToString(v.values)
}
// StringMapFunc is the function equivalent of StringMap
type StringMapFunc func() map[string]string
// String is used by expvar.
func (f StringMapFunc) String() string {
m := f()
if m == nil {
return "{}"
}
return stringMapToString(m)
}
func stringMapToString(m map[string]string) string {
b := bytes.NewBuffer(make([]byte, 0, 4096))
fmt.Fprintf(b, "{")
firstValue := true
for k, v := range m {
if firstValue {
firstValue = false
} else {
fmt.Fprintf(b, ", ")
}
fmt.Fprintf(b, "\"%v\": %v", k, strconv.Quote(v))
}
fmt.Fprintf(b, "}")
return b.String()
}