Skip to content

Commit 20f43a0

Browse files
committed
stats/otel: Add up down counter for A94
1 parent e60a04b commit 20f43a0

File tree

6 files changed

+150
-15
lines changed

6 files changed

+150
-15
lines changed

experimental/stats/metricregistry.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ const (
7575
MetricTypeIntHisto
7676
MetricTypeFloatHisto
7777
MetricTypeIntGauge
78+
MetricTypeIntUpDownCount
7879
)
7980

8081
// Int64CountHandle is a typed handle for a int count metric. This handle
@@ -93,6 +94,23 @@ func (h *Int64CountHandle) Record(recorder MetricsRecorder, incr int64, labels .
9394
recorder.RecordInt64Count(h, incr, labels...)
9495
}
9596

97+
// Int64UpDownCountHandle is a typed handle for an int up-down counter metric.
98+
// This handle is passed at the recording point in order to know which metric
99+
// to record on.
100+
type Int64UpDownCountHandle MetricDescriptor
101+
102+
// Descriptor returns the int64 up-down counter handle typecast to a pointer to a
103+
// MetricDescriptor.
104+
func (h *Int64UpDownCountHandle) Descriptor() *MetricDescriptor {
105+
return (*MetricDescriptor)(h)
106+
}
107+
108+
// Record records the int64 up-down counter value on the metrics recorder provided.
109+
// The value 'v' can be positive to increment or negative to decrement.
110+
func (h *Int64UpDownCountHandle) Record(recorder MetricsRecorder, v int64, labels ...string) {
111+
recorder.RecordInt64UpDownCount(h, v, labels...)
112+
}
113+
96114
// Float64CountHandle is a typed handle for a float count metric. This handle is
97115
// passed at the recording point in order to know which metric to record on.
98116
type Float64CountHandle MetricDescriptor
@@ -249,6 +267,21 @@ func RegisterInt64Gauge(descriptor MetricDescriptor) *Int64GaugeHandle {
249267
return (*Int64GaugeHandle)(descPtr)
250268
}
251269

270+
// RegisterInt64UpDownCounter registers the metric description onto the global registry.
271+
// It returns a typed handle to use for recording data.
272+
//
273+
// NOTE: this function must only be called during initialization time (i.e. in
274+
// an init() function), and is not thread-safe. If multiple metrics are
275+
// registered with the same name, this function will panic.
276+
func RegisterInt64UpDownCount(descriptor MetricDescriptor) *Int64UpDownCountHandle {
277+
registerMetric(descriptor.Name, descriptor.Default)
278+
// Set the specific metric type for the up-down counter
279+
descriptor.Type = MetricTypeIntUpDownCount
280+
descPtr := &descriptor
281+
metricsRegistry[descriptor.Name] = descPtr
282+
return (*Int64UpDownCountHandle)(descPtr)
283+
}
284+
252285
// snapshotMetricsRegistryForTesting snapshots the global data of the metrics
253286
// registry. Returns a cleanup function that sets the metrics registry to its
254287
// original state.

experimental/stats/metricregistry_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,14 @@ func (s) TestMetricRegistry(t *testing.T) {
109109
OptionalLabels: []string{"int gauge optional label"},
110110
Default: false,
111111
})
112+
intUpDownCountHandle1 := RegisterInt64UpDownCount(MetricDescriptor{
113+
Name: "simple up down counter",
114+
Description: "current number of emissions from tests",
115+
Unit: "int",
116+
Labels: []string{"int up down counter label"},
117+
OptionalLabels: []string{"int up down counter optional label"},
118+
Default: false,
119+
})
112120

113121
fmr := newFakeMetricsRecorder(t)
114122

@@ -120,6 +128,14 @@ func (s) TestMetricRegistry(t *testing.T) {
120128
t.Fatalf("fmr.intValues[intCountHandle1.MetricDescriptor] got %v, want: %v", got, 1)
121129
}
122130

131+
intUpDownCountHandle1.Record(fmr, 2, []string{"some label value", "some optional label value"}...)
132+
// The Metric Descriptor in the handle should be able to identify the metric
133+
// information. This is the key passed to metrics recorder to identify
134+
// metric.
135+
if got := fmr.intValues[intUpDownCountHandle1.Descriptor()]; got != 2 {
136+
t.Fatalf("fmr.intValues[intUpDownCountHandle1.MetricDescriptor] got %v, want: %v", got, 2)
137+
}
138+
123139
floatCountHandle1.Record(fmr, 1.2, []string{"some label value", "some optional label value"}...)
124140
if got := fmr.floatValues[floatCountHandle1.Descriptor()]; got != 1.2 {
125141
t.Fatalf("fmr.floatValues[floatCountHandle1.MetricDescriptor] got %v, want: %v", got, 1.2)
@@ -141,6 +157,28 @@ func (s) TestMetricRegistry(t *testing.T) {
141157
}
142158
}
143159

160+
func TestUpDownCounts(t *testing.T) {
161+
cleanup := snapshotMetricsRegistryForTesting()
162+
defer cleanup()
163+
164+
intUpDownCountHandle1 := RegisterInt64UpDownCount(MetricDescriptor{
165+
Name: "simple up down counter",
166+
Description: "current number of emissions from tests",
167+
Unit: "int",
168+
Labels: []string{"int up down counter label"},
169+
OptionalLabels: []string{"int up down counter optional label"},
170+
Default: false,
171+
})
172+
173+
fmr := newFakeMetricsRecorder(t)
174+
intUpDownCountHandle1.Record(fmr, 2, []string{"up down value", "some optional label value"}...)
175+
intUpDownCountHandle1.Record(fmr, -1, []string{"up down value", "some optional label value"}...)
176+
177+
if got := fmr.intValues[intUpDownCountHandle1.Descriptor()]; got != 1 {
178+
t.Fatalf("fmr.intValues[intUpDownCountHandle1.MetricDescriptor] got %v, want: %v", got, 1)
179+
}
180+
}
181+
144182
// TestNumerousIntCounts tests numerous int count metrics registered onto the
145183
// metric registry. A component (simulated by test) should be able to record on
146184
// the different registered int count metrics.
@@ -265,3 +303,8 @@ func (r *fakeMetricsRecorder) RecordInt64Gauge(handle *Int64GaugeHandle, incr in
265303
verifyLabels(r.t, handle.Descriptor().Labels, handle.Descriptor().OptionalLabels, labels)
266304
r.intValues[handle.Descriptor()] += incr
267305
}
306+
307+
func (r *fakeMetricsRecorder) RecordInt64UpDownCount(handle *Int64UpDownCountHandle, incr int64, labels ...string) {
308+
verifyLabels(r.t, handle.Descriptor().Labels, handle.Descriptor().OptionalLabels, labels)
309+
r.intValues[handle.Descriptor()] += incr
310+
}

experimental/stats/metrics.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ type MetricsRecorder interface {
3838
// RecordInt64Gauge records the measurement alongside labels on the int
3939
// gauge associated with the provided handle.
4040
RecordInt64Gauge(handle *Int64GaugeHandle, incr int64, labels ...string)
41+
// RecordInt64UpDownCounter records the measurement alongside labels on the int
42+
// count associated with the provided handle.
43+
RecordInt64UpDownCount(handle *Int64UpDownCountHandle, incr int64, labels ...string)
4144
}
4245

4346
// Metrics is an experimental legacy alias of the now-stable stats.MetricSet.

internal/stats/metrics_recorder_list.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,16 @@ func (l *MetricsRecorderList) RecordInt64Count(handle *estats.Int64CountHandle,
6464
}
6565
}
6666

67+
// RecordInt64UpDownCount records the measurement alongside labels on the int
68+
// count associated with the provided handle.
69+
func (l *MetricsRecorderList) RecordInt64UpDownCount(handle *estats.Int64UpDownCountHandle, incr int64, labels ...string) {
70+
verifyLabels(handle.Descriptor(), labels...)
71+
72+
for _, metricRecorder := range l.metricsRecorders {
73+
metricRecorder.RecordInt64UpDownCount(handle, incr, labels...)
74+
}
75+
}
76+
6777
// RecordFloat64Count records the measurement alongside labels on the float
6878
// count associated with the provided handle.
6979
func (l *MetricsRecorderList) RecordFloat64Count(handle *estats.Float64CountHandle, incr float64, labels ...string) {

internal/testutils/stats/test_metrics_recorder.go

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@ import (
3535
// have taken place. It also persists metrics data keyed on the metrics
3636
// descriptor.
3737
type TestMetricsRecorder struct {
38-
intCountCh *testutils.Channel
39-
floatCountCh *testutils.Channel
40-
intHistoCh *testutils.Channel
41-
floatHistoCh *testutils.Channel
42-
intGaugeCh *testutils.Channel
38+
intCountCh *testutils.Channel
39+
floatCountCh *testutils.Channel
40+
intHistoCh *testutils.Channel
41+
floatHistoCh *testutils.Channel
42+
intGaugeCh *testutils.Channel
43+
intUpDownCountCh *testutils.Channel
4344

4445
// mu protects data.
4546
mu sync.Mutex
@@ -50,11 +51,12 @@ type TestMetricsRecorder struct {
5051
// NewTestMetricsRecorder returns a new TestMetricsRecorder.
5152
func NewTestMetricsRecorder() *TestMetricsRecorder {
5253
return &TestMetricsRecorder{
53-
intCountCh: testutils.NewChannelWithSize(10),
54-
floatCountCh: testutils.NewChannelWithSize(10),
55-
intHistoCh: testutils.NewChannelWithSize(10),
56-
floatHistoCh: testutils.NewChannelWithSize(10),
57-
intGaugeCh: testutils.NewChannelWithSize(10),
54+
intCountCh: testutils.NewChannelWithSize(10),
55+
floatCountCh: testutils.NewChannelWithSize(10),
56+
intHistoCh: testutils.NewChannelWithSize(10),
57+
floatHistoCh: testutils.NewChannelWithSize(10),
58+
intGaugeCh: testutils.NewChannelWithSize(10),
59+
intUpDownCountCh: testutils.NewChannelWithSize(10),
5860

5961
data: make(map[string]float64),
6062
}
@@ -135,6 +137,22 @@ func (r *TestMetricsRecorder) RecordInt64Count(handle *estats.Int64CountHandle,
135137
r.data[handle.Name] = float64(incr)
136138
}
137139

140+
// RecordInt64UpDownCount sends the metrics data to the intUpDownCountCh channel and updates
141+
// the internal data map with the recorded value.
142+
func (r *TestMetricsRecorder) RecordInt64UpDownCount(handle *estats.Int64UpDownCountHandle, incr int64, labels ...string) {
143+
r.intUpDownCountCh.ReceiveOrFail()
144+
r.intUpDownCountCh.Send(MetricsData{
145+
Handle: handle.Descriptor(),
146+
IntIncr: incr,
147+
LabelKeys: append(handle.Labels, handle.OptionalLabels...),
148+
LabelVals: labels,
149+
})
150+
151+
r.mu.Lock()
152+
defer r.mu.Unlock()
153+
r.data[handle.Name] = float64(incr)
154+
}
155+
138156
// WaitForFloat64Count waits for a float count metric to be recorded and
139157
// verifies that the recorded metrics data matches the expected metricsDataWant.
140158
// Returns an error if failed to wait or received wrong data.
@@ -294,3 +312,7 @@ func (r *NoopMetricsRecorder) RecordFloat64Histo(*estats.Float64HistoHandle, flo
294312

295313
// RecordInt64Gauge is a noop implementation of RecordInt64Gauge.
296314
func (r *NoopMetricsRecorder) RecordInt64Gauge(*estats.Int64GaugeHandle, int64, ...string) {}
315+
316+
// RecordInt64UpDownCount is a noop implementation of RecordInt64UpDownCount.
317+
func (r *NoopMetricsRecorder) RecordInt64UpDownCount(*estats.Int64UpDownCountHandle, int64, ...string) {
318+
}

stats/opentelemetry/opentelemetry.go

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,18 @@ func createInt64Counter(setOfMetrics map[string]bool, metricName string, meter o
280280
return ret
281281
}
282282

283+
func createInt64UpDownCounter(setOfMetrics map[string]bool, metricName string, meter otelmetric.Meter, options ...otelmetric.Int64UpDownCounterOption) otelmetric.Int64UpDownCounter {
284+
if _, ok := setOfMetrics[metricName]; !ok {
285+
return noop.Int64UpDownCounter{}
286+
}
287+
ret, err := meter.Int64UpDownCounter(string(metricName), options...)
288+
if err != nil {
289+
logger.Errorf("failed to register metric \"%v\", will not record: %v", metricName, err)
290+
return noop.Int64UpDownCounter{}
291+
}
292+
return ret
293+
}
294+
283295
func createFloat64Counter(setOfMetrics map[string]bool, metricName string, meter otelmetric.Meter, options ...otelmetric.Float64CounterOption) otelmetric.Float64Counter {
284296
if _, ok := setOfMetrics[metricName]; !ok {
285297
return noop.Float64Counter{}
@@ -350,11 +362,12 @@ func optionFromLabels(labelKeys []string, optionalLabelKeys []string, optionalLa
350362
// registryMetrics implements MetricsRecorder for the client and server stats
351363
// handlers.
352364
type registryMetrics struct {
353-
intCounts map[*estats.MetricDescriptor]otelmetric.Int64Counter
354-
floatCounts map[*estats.MetricDescriptor]otelmetric.Float64Counter
355-
intHistos map[*estats.MetricDescriptor]otelmetric.Int64Histogram
356-
floatHistos map[*estats.MetricDescriptor]otelmetric.Float64Histogram
357-
intGauges map[*estats.MetricDescriptor]otelmetric.Int64Gauge
365+
intCounts map[*estats.MetricDescriptor]otelmetric.Int64Counter
366+
floatCounts map[*estats.MetricDescriptor]otelmetric.Float64Counter
367+
intHistos map[*estats.MetricDescriptor]otelmetric.Int64Histogram
368+
floatHistos map[*estats.MetricDescriptor]otelmetric.Float64Histogram
369+
intGauges map[*estats.MetricDescriptor]otelmetric.Int64Gauge
370+
intUpDownCounts map[*estats.MetricDescriptor]otelmetric.Int64UpDownCounter
358371

359372
optionalLabels []string
360373
}
@@ -365,6 +378,7 @@ func (rm *registryMetrics) registerMetrics(metrics *stats.MetricSet, meter otelm
365378
rm.intHistos = make(map[*estats.MetricDescriptor]otelmetric.Int64Histogram)
366379
rm.floatHistos = make(map[*estats.MetricDescriptor]otelmetric.Float64Histogram)
367380
rm.intGauges = make(map[*estats.MetricDescriptor]otelmetric.Int64Gauge)
381+
rm.intUpDownCounts = make(map[*estats.MetricDescriptor]otelmetric.Int64UpDownCounter)
368382

369383
for metric := range metrics.Metrics() {
370384
desc := estats.DescriptorForMetric(metric)
@@ -385,6 +399,8 @@ func (rm *registryMetrics) registerMetrics(metrics *stats.MetricSet, meter otelm
385399
rm.floatHistos[desc] = createFloat64Histogram(metrics.Metrics(), desc.Name, meter, otelmetric.WithUnit(desc.Unit), otelmetric.WithDescription(desc.Description), otelmetric.WithExplicitBucketBoundaries(desc.Bounds...))
386400
case estats.MetricTypeIntGauge:
387401
rm.intGauges[desc] = createInt64Gauge(metrics.Metrics(), desc.Name, meter, otelmetric.WithUnit(desc.Unit), otelmetric.WithDescription(desc.Description))
402+
case estats.MetricTypeIntUpDownCount:
403+
rm.intUpDownCounts[desc] = createInt64UpDownCounter(metrics.Metrics(), desc.Name, meter, otelmetric.WithUnit(desc.Unit), otelmetric.WithDescription(desc.Description))
388404
}
389405
}
390406
}
@@ -397,6 +413,14 @@ func (rm *registryMetrics) RecordInt64Count(handle *estats.Int64CountHandle, inc
397413
}
398414
}
399415

416+
func (rm *registryMetrics) RecordInt64UpDownCount(handle *estats.Int64UpDownCountHandle, incr int64, labels ...string) {
417+
desc := handle.Descriptor()
418+
if ic, ok := rm.intUpDownCounts[desc]; ok {
419+
ao := optionFromLabels(desc.Labels, desc.OptionalLabels, rm.optionalLabels, labels...)
420+
ic.Add(context.TODO(), incr, ao)
421+
}
422+
}
423+
400424
func (rm *registryMetrics) RecordFloat64Count(handle *estats.Float64CountHandle, incr float64, labels ...string) {
401425
desc := handle.Descriptor()
402426
if fc, ok := rm.floatCounts[desc]; ok {

0 commit comments

Comments
 (0)