-
Notifications
You must be signed in to change notification settings - Fork 4.2k
/
wrapped_metrics.go
156 lines (131 loc) · 4.91 KB
/
wrapped_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
package metricsutil
import (
"strings"
"sync/atomic"
"time"
"github.com/armon/go-metrics"
"github.com/hashicorp/vault/helper/namespace"
)
// ClusterMetricSink serves as a shim around go-metrics
// and inserts a "cluster" label.
//
// It also provides a mechanism to limit the cardinality of the labels on a gauge
// (at each reporting interval, which isn't sufficient if there is variability in which
// labels are the top N) and a backoff mechanism for gauge computation.
type ClusterMetricSink struct {
// ClusterName is either the cluster ID, or a name provided
// in the telemetry configuration stanza.
//
// Because it may be set after the Core is initialized, we need
// to protect against concurrent access.
ClusterName atomic.Value
MaxGaugeCardinality int
GaugeInterval time.Duration
// Sink is the go-metrics instance to send to.
Sink metrics.MetricSink
// Constants that are helpful for metrics within the metrics sink
TelemetryConsts TelemetryConstConfig
}
type TelemetryConstConfig struct {
LeaseMetricsEpsilon time.Duration
NumLeaseMetricsTimeBuckets int
LeaseMetricsNameSpaceLabels bool
}
type Metrics interface {
SetGaugeWithLabels(key []string, val float32, labels []Label)
IncrCounterWithLabels(key []string, val float32, labels []Label)
AddSampleWithLabels(key []string, val float32, labels []Label)
AddDurationWithLabels(key []string, d time.Duration, labels []Label)
MeasureSinceWithLabels(key []string, start time.Time, labels []Label)
}
var _ Metrics = &ClusterMetricSink{}
// SinkWrapper implements `metricsutil.Metrics` using an instance of
// armon/go-metrics `MetricSink` as the underlying implementation.
type SinkWrapper struct {
metrics.MetricSink
}
func (s SinkWrapper) AddDurationWithLabels(key []string, d time.Duration, labels []Label) {
val := float32(d) / float32(time.Millisecond)
s.MetricSink.AddSampleWithLabels(key, val, labels)
}
func (s SinkWrapper) MeasureSinceWithLabels(key []string, start time.Time, labels []Label) {
elapsed := time.Now().Sub(start)
val := float32(elapsed) / float32(time.Millisecond)
s.MetricSink.AddSampleWithLabels(key, val, labels)
}
var _ Metrics = SinkWrapper{}
// Convenience alias
type Label = metrics.Label
func (m *ClusterMetricSink) SetGauge(key []string, val float32) {
m.Sink.SetGaugeWithLabels(key, val, []Label{{"cluster", m.ClusterName.Load().(string)}})
}
func (m *ClusterMetricSink) SetGaugeWithLabels(key []string, val float32, labels []Label) {
m.Sink.SetGaugeWithLabels(key, val,
append(labels, Label{"cluster", m.ClusterName.Load().(string)}))
}
func (m *ClusterMetricSink) IncrCounterWithLabels(key []string, val float32, labels []Label) {
m.Sink.IncrCounterWithLabels(key, val,
append(labels, Label{"cluster", m.ClusterName.Load().(string)}))
}
func (m *ClusterMetricSink) AddSample(key []string, val float32) {
m.Sink.AddSampleWithLabels(key, val, []Label{{"cluster", m.ClusterName.Load().(string)}})
}
func (m *ClusterMetricSink) AddSampleWithLabels(key []string, val float32, labels []Label) {
m.Sink.AddSampleWithLabels(key, val,
append(labels, Label{"cluster", m.ClusterName.Load().(string)}))
}
func (m *ClusterMetricSink) AddDurationWithLabels(key []string, d time.Duration, labels []Label) {
val := float32(d) / float32(time.Millisecond)
m.AddSampleWithLabels(key, val, labels)
}
func (m *ClusterMetricSink) MeasureSinceWithLabels(key []string, start time.Time, labels []Label) {
elapsed := time.Now().Sub(start)
val := float32(elapsed) / float32(time.Millisecond)
m.AddSampleWithLabels(key, val, labels)
}
// BlackholeSink is a default suitable for use in unit tests.
func BlackholeSink() *ClusterMetricSink {
conf := metrics.DefaultConfig("")
conf.EnableRuntimeMetrics = false
sink, _ := metrics.New(conf, &metrics.BlackholeSink{})
cms := &ClusterMetricSink{
ClusterName: atomic.Value{},
Sink: sink,
}
cms.ClusterName.Store("")
return cms
}
func NewClusterMetricSink(clusterName string, sink metrics.MetricSink) *ClusterMetricSink {
cms := &ClusterMetricSink{
ClusterName: atomic.Value{},
Sink: sink,
TelemetryConsts: TelemetryConstConfig{},
}
cms.ClusterName.Store(clusterName)
return cms
}
// SetDefaultClusterName changes the cluster name from its default value,
// if it has not previously been configured.
func (m *ClusterMetricSink) SetDefaultClusterName(clusterName string) {
// This is not a true compare-and-swap, but it should be
// consistent enough for normal uses
if m.ClusterName.Load().(string) == "" {
m.ClusterName.Store(clusterName)
}
}
// NamespaceLabel creates a metrics label for the given
// Namespace: root is "root"; others are path with the
// final '/' removed.
func NamespaceLabel(ns *namespace.Namespace) metrics.Label {
switch {
case ns == nil:
return metrics.Label{"namespace", "root"}
case ns.ID == namespace.RootNamespaceID:
return metrics.Label{"namespace", "root"}
default:
return metrics.Label{
"namespace",
strings.Trim(ns.Path, "/"),
}
}
}