Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

- Add `error.type` attribute to `http.client.request.duration` for transport failures in `otelhttp`. (#8801)
- Add examples for prometheus compatibility document. (#8716)
- Add support for `cardinality_limits` in `PeriodicMetricReader` in `otelconf`. (#8885)

### Changed

Expand Down
56 changes: 56 additions & 0 deletions otelconf/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@
if r.Periodic.Timeout != nil {
opts = append(opts, sdkmetric.WithTimeout(time.Duration(*r.Periodic.Timeout)*time.Millisecond))
}

if r.Periodic.CardinalityLimits != nil {
opts = append(opts, sdkmetric.WithCardinalityLimitSelector(cardinalityLimitSelector(r.Periodic.CardinalityLimits)))
}
return periodicExporter(ctx, r.Periodic.Exporter, opts...)
}

Expand All @@ -91,6 +95,58 @@
return nil, newErrInvalid("no valid metric exporter")
}

// cardinalityLimitSelector returns a CardinalityLimitSelector for the given CardinalityLimits config.
func cardinalityLimitSelector(cl *CardinalityLimits) sdkmetric.CardinalityLimitSelector {
return func(ik sdkmetric.InstrumentKind) (int, bool) {
var limit *int
switch ik {
case sdkmetric.InstrumentKindCounter:
if cl.Counter != nil {
v := int(*cl.Counter)

Check failure on line 105 in otelconf/metric.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary conversion (unconvert)
limit = &v
}
case sdkmetric.InstrumentKindUpDownCounter:
if cl.UpDownCounter != nil {
v := int(*cl.UpDownCounter)

Check failure on line 110 in otelconf/metric.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary conversion (unconvert)
limit = &v
}
case sdkmetric.InstrumentKindHistogram:
if cl.Histogram != nil {
v := int(*cl.Histogram)

Check failure on line 115 in otelconf/metric.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary conversion (unconvert)
limit = &v
}
case sdkmetric.InstrumentKindObservableCounter:
if cl.ObservableCounter != nil {
v := int(*cl.ObservableCounter)

Check failure on line 120 in otelconf/metric.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary conversion (unconvert)
limit = &v
}
case sdkmetric.InstrumentKindObservableUpDownCounter:
if cl.ObservableUpDownCounter != nil {
v := int(*cl.ObservableUpDownCounter)

Check failure on line 125 in otelconf/metric.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary conversion (unconvert)
limit = &v
}
case sdkmetric.InstrumentKindObservableGauge:
if cl.ObservableGauge != nil {
v := int(*cl.ObservableGauge)

Check failure on line 130 in otelconf/metric.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary conversion (unconvert)
limit = &v
}
case sdkmetric.InstrumentKindGauge:
if cl.Gauge != nil {
v := int(*cl.Gauge)

Check failure on line 135 in otelconf/metric.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary conversion (unconvert)
limit = &v
}
}
if limit != nil {
return *limit, false
}
Comment thread
sonalgaud12 marked this conversation as resolved.
Outdated
if cl.Default != nil {
return int(*cl.Default), false

Check failure on line 143 in otelconf/metric.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary conversion (unconvert)
}
// fallback=true; defer to the SDK/provider global cardinality limit
return 0, true
}
}

func periodicExporter(ctx context.Context, exporter PushMetricExporter, opts ...sdkmetric.PeriodicReaderOption) (sdkmetric.Reader, error) {
exportersConfigured := 0
var exportFunc func() (sdkmetric.Reader, error)
Expand Down
136 changes: 136 additions & 0 deletions otelconf/metric_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,66 @@ func TestReader(t *testing.T) {
},
wantErrT: newErrInvalid("no valid metric exporter"),
},
{
name: "periodic/console-exporter-with-cardinality-limits",
reader: MetricReader{
Periodic: &PeriodicMetricReader{
CardinalityLimits: &CardinalityLimits{
Counter: ptr(100),
UpDownCounter: ptr(200),
Histogram: ptr(300),
ObservableCounter: ptr(400),
ObservableUpDownCounter: ptr(500),
ObservableGauge: ptr(600),
Gauge: ptr(700),
},
Exporter: PushMetricExporter{
Console: &ConsoleMetricExporter{},
},
},
},
wantReader: sdkmetric.NewPeriodicReader(
consoleExporter,
sdkmetric.WithCardinalityLimitSelector(func(ik sdkmetric.InstrumentKind) (int, bool) {
switch ik {
case sdkmetric.InstrumentKindCounter:
return 100, false
case sdkmetric.InstrumentKindUpDownCounter:
return 200, false
case sdkmetric.InstrumentKindHistogram:
return 300, false
case sdkmetric.InstrumentKindObservableCounter:
return 400, false
case sdkmetric.InstrumentKindObservableUpDownCounter:
return 500, false
case sdkmetric.InstrumentKindObservableGauge:
return 600, false
case sdkmetric.InstrumentKindGauge:
return 700, false
}
return 0, true
}),
),
},
{
name: "periodic/console-exporter-with-default-cardinality-limit",
reader: MetricReader{
Periodic: &PeriodicMetricReader{
CardinalityLimits: &CardinalityLimits{
Default: ptr(50),
},
Exporter: PushMetricExporter{
Console: &ConsoleMetricExporter{},
},
},
},
wantReader: sdkmetric.NewPeriodicReader(
consoleExporter,
sdkmetric.WithCardinalityLimitSelector(func(sdkmetric.InstrumentKind) (int, bool) {
return 50, false
}),
),
},
Comment thread
sonalgaud12 marked this conversation as resolved.
{
name: "periodic/console-exporter",
reader: MetricReader{
Expand Down Expand Up @@ -820,6 +880,82 @@ func TestReader(t *testing.T) {
}
}

func TestCardinalityLimitSelector(t *testing.T) {
allKinds := []sdkmetric.InstrumentKind{
sdkmetric.InstrumentKindCounter,
sdkmetric.InstrumentKindUpDownCounter,
sdkmetric.InstrumentKindHistogram,
sdkmetric.InstrumentKindObservableCounter,
sdkmetric.InstrumentKindObservableUpDownCounter,
sdkmetric.InstrumentKindObservableGauge,
sdkmetric.InstrumentKindGauge,
}

t.Run("per-kind limits", func(t *testing.T) {
cl := &CardinalityLimits{
Counter: ptr(100),
UpDownCounter: ptr(200),
Histogram: ptr(300),
ObservableCounter: ptr(400),
ObservableUpDownCounter: ptr(500),
ObservableGauge: ptr(600),
Gauge: ptr(700),
}
sel := cardinalityLimitSelector(cl)
expected := map[sdkmetric.InstrumentKind]int{
sdkmetric.InstrumentKindCounter: 100,
sdkmetric.InstrumentKindUpDownCounter: 200,
sdkmetric.InstrumentKindHistogram: 300,
sdkmetric.InstrumentKindObservableCounter: 400,
sdkmetric.InstrumentKindObservableUpDownCounter: 500,
sdkmetric.InstrumentKindObservableGauge: 600,
sdkmetric.InstrumentKindGauge: 700,
}
for _, ik := range allKinds {
limit, fallback := sel(ik)
assert.Equal(t, expected[ik], limit)
assert.False(t, fallback)
}
})

t.Run("default limit used when kind not set", func(t *testing.T) {
cl := &CardinalityLimits{
Default: ptr(50),
}
sel := cardinalityLimitSelector(cl)
for _, ik := range allKinds {
limit, fallback := sel(ik)
assert.Equal(t, 50, limit)
assert.False(t, fallback)
}
})

t.Run("per-kind overrides default", func(t *testing.T) {
cl := &CardinalityLimits{
Default: ptr(50),
Counter: ptr(100),
}
sel := cardinalityLimitSelector(cl)
limit, fallback := sel(sdkmetric.InstrumentKindCounter)
assert.Equal(t, 100, limit)
assert.False(t, fallback)

limit, fallback = sel(sdkmetric.InstrumentKindGauge)
assert.Equal(t, 50, limit)
assert.False(t, fallback)
})

t.Run("fallback to provider when no limit set", func(t *testing.T) {
cl := &CardinalityLimits{}
sel := cardinalityLimitSelector(cl)
for _, ik := range allKinds {
limit, fallback := sel(ik)
assert.Equal(t, 0, limit)
assert.True(t, fallback)
}
})
}

func TestView(t *testing.T) {
testCases := []struct {
name string
Expand Down
56 changes: 56 additions & 0 deletions otelconf/x/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@
if r.Periodic.Timeout != nil {
opts = append(opts, sdkmetric.WithTimeout(time.Duration(*r.Periodic.Timeout)*time.Millisecond))
}

if r.Periodic.CardinalityLimits != nil {
opts = append(opts, sdkmetric.WithCardinalityLimitSelector(cardinalityLimitSelector(r.Periodic.CardinalityLimits)))
}
return periodicExporter(ctx, r.Periodic.Exporter, opts...)
}

Expand All @@ -95,6 +99,58 @@
return nil, newErrInvalid("no valid metric reader")
}

// cardinalityLimitSelector returns a CardinalityLimitSelector for the given CardinalityLimits config.
func cardinalityLimitSelector(cl *CardinalityLimits) sdkmetric.CardinalityLimitSelector {
return func(ik sdkmetric.InstrumentKind) (int, bool) {
var limit *int
switch ik {
case sdkmetric.InstrumentKindCounter:
if cl.Counter != nil {
v := int(*cl.Counter)

Check failure on line 109 in otelconf/x/metric.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary conversion (unconvert)
limit = &v
}
case sdkmetric.InstrumentKindUpDownCounter:
if cl.UpDownCounter != nil {
v := int(*cl.UpDownCounter)

Check failure on line 114 in otelconf/x/metric.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary conversion (unconvert)
limit = &v
}
case sdkmetric.InstrumentKindHistogram:
if cl.Histogram != nil {
v := int(*cl.Histogram)
limit = &v
}
case sdkmetric.InstrumentKindObservableCounter:
if cl.ObservableCounter != nil {
v := int(*cl.ObservableCounter)
limit = &v
}
case sdkmetric.InstrumentKindObservableUpDownCounter:
if cl.ObservableUpDownCounter != nil {
v := int(*cl.ObservableUpDownCounter)
limit = &v
}
case sdkmetric.InstrumentKindObservableGauge:
if cl.ObservableGauge != nil {
v := int(*cl.ObservableGauge)
limit = &v
}
case sdkmetric.InstrumentKindGauge:
if cl.Gauge != nil {
v := int(*cl.Gauge)
limit = &v
}
}
if limit != nil {
return *limit, false
}
Comment thread
sonalgaud12 marked this conversation as resolved.
Outdated
if cl.Default != nil {
return int(*cl.Default), false
}
// fallback=true → defer to the SDK/provider global cardinality limit
return 0, true
}
}

func pullReader(ctx context.Context, exporter PullMetricExporter) (sdkmetric.Reader, error) {
if exporter.PrometheusDevelopment != nil {
return prometheusReader(ctx, exporter.PrometheusDevelopment)
Expand Down
Loading
Loading