/
metrics.go
159 lines (130 loc) · 3.83 KB
/
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
package gocb
import (
"github.com/couchbase/gocbcore/v10"
"sync"
"time"
)
// Meter handles metrics information for SDK operations.
type Meter interface {
Counter(name string, tags map[string]string) (Counter, error)
ValueRecorder(name string, tags map[string]string) (ValueRecorder, error)
}
// Counter is used for incrementing a synchronous count metric.
type Counter interface {
IncrementBy(num uint64)
}
// ValueRecorder is used for grouping synchronous count metrics.
type ValueRecorder interface {
RecordValue(val uint64)
}
// NoopMeter is a Meter implementation which performs no metrics operations.
type NoopMeter struct {
}
var (
defaultNoopCounter = &noopCounter{}
defaultNoopValueRecorder = &noopValueRecorder{}
)
// Counter is used for incrementing a synchronous count metric.
func (nm *NoopMeter) Counter(name string, tags map[string]string) (Counter, error) {
return defaultNoopCounter, nil
}
// ValueRecorder is used for grouping synchronous count metrics.
func (nm *NoopMeter) ValueRecorder(name string, tags map[string]string) (ValueRecorder, error) {
return defaultNoopValueRecorder, nil
}
type noopCounter struct{}
func (bc *noopCounter) IncrementBy(num uint64) {
}
type noopValueRecorder struct{}
func (bc *noopValueRecorder) RecordValue(val uint64) {
}
// nolint: unused
type coreMeterWrapper struct {
meter Meter
}
// nolint: unused
func (meter *coreMeterWrapper) Counter(name string, tags map[string]string) (gocbcore.Counter, error) {
counter, err := meter.meter.Counter(name, tags)
if err != nil {
return nil, err
}
return &coreCounterWrapper{
counter: counter,
}, nil
}
// nolint: unused
func (meter *coreMeterWrapper) ValueRecorder(name string, tags map[string]string) (gocbcore.ValueRecorder, error) {
if name == "db.couchbase.requests" {
// gocbcore has its own requests metrics, we don't want to record those.
return &noopValueRecorder{}, nil
}
recorder, err := meter.meter.ValueRecorder(name, tags)
if err != nil {
return nil, err
}
return &coreValueRecorderWrapper{
valueRecorder: recorder,
}, nil
}
// nolint: unused
type coreCounterWrapper struct {
counter Counter
}
// nolint: unused
func (nm *coreCounterWrapper) IncrementBy(num uint64) {
nm.counter.IncrementBy(num)
}
// nolint: unused
type coreValueRecorderWrapper struct {
valueRecorder ValueRecorder
}
// nolint: unused
func (nm *coreValueRecorderWrapper) RecordValue(val uint64) {
nm.valueRecorder.RecordValue(val)
}
type meterWrapper struct {
attribsCache sync.Map
meter Meter
isNoopMeter bool
}
func newMeterWrapper(meter Meter) *meterWrapper {
_, ok := meter.(*NoopMeter)
return &meterWrapper{
meter: meter,
isNoopMeter: ok,
}
}
func (mw *meterWrapper) ValueRecorder(service, operation string) (ValueRecorder, error) {
if mw.isNoopMeter {
// If it's a noop meter then let's not pay the overhead of creating and caching attributes.
return defaultNoopValueRecorder, nil
}
key := service + "." + operation
attribs, ok := mw.attribsCache.Load(key)
if !ok {
// It doesn't really matter if we end up storing the attribs against the same key multiple times. We just need
// to have a read efficient cache that doesn't cause actual data races.
attribs = map[string]string{
meterAttribServiceKey: service,
meterAttribOperationKey: operation,
}
mw.attribsCache.Store(key, attribs)
}
recorder, err := mw.meter.ValueRecorder(meterNameCBOperations, attribs.(map[string]string))
if err != nil {
return nil, err
}
return recorder, nil
}
func (mw *meterWrapper) ValueRecord(service, operation string, start time.Time) {
recorder, err := mw.ValueRecorder(service, operation)
if err != nil {
logDebugf("Failed to create value recorder: %v", err)
return
}
duration := uint64(time.Since(start).Microseconds())
if duration == 0 {
duration = uint64(1 * time.Microsecond)
}
recorder.RecordValue(duration)
}