From 54c74cd7f62fe301a187e39c2d0273157d455d9a Mon Sep 17 00:00:00 2001 From: Dave Protasowski Date: Tue, 30 Sep 2025 16:30:18 -0400 Subject: [PATCH 1/4] refactor out temporality selector funcs --- .../internal/oconf/envconfig.go | 29 ++--------------- .../internal/oconf/envconfig.go | 29 ++--------------- .../otlp/otlpmetric/oconf/envconfig.go.tmpl | 29 ++--------------- sdk/metric/reader.go | 32 ++++++++++++++++++- 4 files changed, 40 insertions(+), 79 deletions(-) diff --git a/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf/envconfig.go b/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf/envconfig.go index b54a173b6ea..7f947273b5e 100644 --- a/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf/envconfig.go +++ b/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf/envconfig.go @@ -18,7 +18,6 @@ import ( "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/envconfig" "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/sdk/metric" - "go.opentelemetry.io/otel/sdk/metric/metricdata" ) // DefaultEnvOptionsReader is the default environments reader. @@ -165,11 +164,11 @@ func withEnvTemporalityPreference(n string, fn func(metric.TemporalitySelector)) if s, ok := e.GetEnvValue(n); ok { switch strings.ToLower(s) { case "cumulative": - fn(cumulativeTemporality) + fn(metric.CumulativeTemporalitySelector) case "delta": - fn(deltaTemporality) + fn(metric.DeltaTemporalitySelector) case "lowmemory": - fn(lowMemory) + fn(metric.LowMemoryTemporalitySelector) default: global.Warn( "OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE is set to an invalid value, ignoring.", @@ -181,28 +180,6 @@ func withEnvTemporalityPreference(n string, fn func(metric.TemporalitySelector)) } } -func cumulativeTemporality(metric.InstrumentKind) metricdata.Temporality { - return metricdata.CumulativeTemporality -} - -func deltaTemporality(ik metric.InstrumentKind) metricdata.Temporality { - switch ik { - case metric.InstrumentKindCounter, metric.InstrumentKindHistogram, metric.InstrumentKindObservableCounter: - return metricdata.DeltaTemporality - default: - return metricdata.CumulativeTemporality - } -} - -func lowMemory(ik metric.InstrumentKind) metricdata.Temporality { - switch ik { - case metric.InstrumentKindCounter, metric.InstrumentKindHistogram: - return metricdata.DeltaTemporality - default: - return metricdata.CumulativeTemporality - } -} - func withEnvAggPreference(n string, fn func(metric.AggregationSelector)) func(e *envconfig.EnvOptionsReader) { return func(e *envconfig.EnvOptionsReader) { if s, ok := e.GetEnvValue(n); ok { diff --git a/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf/envconfig.go b/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf/envconfig.go index ef318ac676c..69224c97de8 100644 --- a/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf/envconfig.go +++ b/exporters/otlp/otlpmetric/otlpmetrichttp/internal/oconf/envconfig.go @@ -18,7 +18,6 @@ import ( "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/envconfig" "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/sdk/metric" - "go.opentelemetry.io/otel/sdk/metric/metricdata" ) // DefaultEnvOptionsReader is the default environments reader. @@ -165,11 +164,11 @@ func withEnvTemporalityPreference(n string, fn func(metric.TemporalitySelector)) if s, ok := e.GetEnvValue(n); ok { switch strings.ToLower(s) { case "cumulative": - fn(cumulativeTemporality) + fn(metric.CumulativeTemporalitySelector) case "delta": - fn(deltaTemporality) + fn(metric.DeltaTemporalitySelector) case "lowmemory": - fn(lowMemory) + fn(metric.LowMemoryTemporalitySelector) default: global.Warn( "OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE is set to an invalid value, ignoring.", @@ -181,28 +180,6 @@ func withEnvTemporalityPreference(n string, fn func(metric.TemporalitySelector)) } } -func cumulativeTemporality(metric.InstrumentKind) metricdata.Temporality { - return metricdata.CumulativeTemporality -} - -func deltaTemporality(ik metric.InstrumentKind) metricdata.Temporality { - switch ik { - case metric.InstrumentKindCounter, metric.InstrumentKindHistogram, metric.InstrumentKindObservableCounter: - return metricdata.DeltaTemporality - default: - return metricdata.CumulativeTemporality - } -} - -func lowMemory(ik metric.InstrumentKind) metricdata.Temporality { - switch ik { - case metric.InstrumentKindCounter, metric.InstrumentKindHistogram: - return metricdata.DeltaTemporality - default: - return metricdata.CumulativeTemporality - } -} - func withEnvAggPreference(n string, fn func(metric.AggregationSelector)) func(e *envconfig.EnvOptionsReader) { return func(e *envconfig.EnvOptionsReader) { if s, ok := e.GetEnvValue(n); ok { diff --git a/internal/shared/otlp/otlpmetric/oconf/envconfig.go.tmpl b/internal/shared/otlp/otlpmetric/oconf/envconfig.go.tmpl index 3c864710351..67155dc6be6 100644 --- a/internal/shared/otlp/otlpmetric/oconf/envconfig.go.tmpl +++ b/internal/shared/otlp/otlpmetric/oconf/envconfig.go.tmpl @@ -18,7 +18,6 @@ import ( "{{ .envconfigImportPath }}" "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/sdk/metric" - "go.opentelemetry.io/otel/sdk/metric/metricdata" ) // DefaultEnvOptionsReader is the default environments reader. @@ -165,11 +164,11 @@ func withEnvTemporalityPreference(n string, fn func(metric.TemporalitySelector)) if s, ok := e.GetEnvValue(n); ok { switch strings.ToLower(s) { case "cumulative": - fn(cumulativeTemporality) + fn(metric.CumulativeTemporalitySelector) case "delta": - fn(deltaTemporality) + fn(metric.DeltaTemporalitySelector) case "lowmemory": - fn(lowMemory) + fn(metric.LowMemoryTemporalitySelector) default: global.Warn( "OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE is set to an invalid value, ignoring.", @@ -181,28 +180,6 @@ func withEnvTemporalityPreference(n string, fn func(metric.TemporalitySelector)) } } -func cumulativeTemporality(metric.InstrumentKind) metricdata.Temporality { - return metricdata.CumulativeTemporality -} - -func deltaTemporality(ik metric.InstrumentKind) metricdata.Temporality { - switch ik { - case metric.InstrumentKindCounter, metric.InstrumentKindHistogram, metric.InstrumentKindObservableCounter: - return metricdata.DeltaTemporality - default: - return metricdata.CumulativeTemporality - } -} - -func lowMemory(ik metric.InstrumentKind) metricdata.Temporality { - switch ik { - case metric.InstrumentKindCounter, metric.InstrumentKindHistogram: - return metricdata.DeltaTemporality - default: - return metricdata.CumulativeTemporality - } -} - func withEnvAggPreference(n string, fn func(metric.AggregationSelector)) func(e *envconfig.EnvOptionsReader) { return func(e *envconfig.EnvOptionsReader) { if s, ok := e.GetEnvValue(n); ok { diff --git a/sdk/metric/reader.go b/sdk/metric/reader.go index 5c1cea8254e..7b205c736c2 100644 --- a/sdk/metric/reader.go +++ b/sdk/metric/reader.go @@ -127,10 +127,40 @@ type TemporalitySelector func(InstrumentKind) metricdata.Temporality // DefaultTemporalitySelector is the default TemporalitySelector used if // WithTemporalitySelector is not provided. CumulativeTemporality will be used // for all instrument kinds if this TemporalitySelector is used. -func DefaultTemporalitySelector(InstrumentKind) metricdata.Temporality { +func DefaultTemporalitySelector(k InstrumentKind) metricdata.Temporality { + return CumulativeTemporalitySelector(k) +} + +// CumulativeTemporalitySelector is the TemporalitySelector that uses +// a cumulative temporality for all instrument kinds. +func CumulativeTemporalitySelector(InstrumentKind) metricdata.Temporality { return metricdata.CumulativeTemporality } +// DeltaTemporalitySelector is the TemporalitySelector that uses +// a delta temporality for instrument kinds: counter, histogram, observable counter +// All other instruments use cumulative temporality. +func DeltaTemporalitySelector(k InstrumentKind) metricdata.Temporality { + switch k { + case InstrumentKindCounter, InstrumentKindHistogram, InstrumentKindObservableCounter: + return metricdata.DeltaTemporality + default: + return metricdata.CumulativeTemporality + } +} + +// LowMemoryTemporalitySelector is the TemporalitySelector that uses +// delta temporality for counters and histograms. All other instruments use +// cumulative temporality. +func LowMemoryTemporalitySelector(k InstrumentKind) metricdata.Temporality { + switch k { + case InstrumentKindCounter, InstrumentKindHistogram: + return metricdata.DeltaTemporality + default: + return metricdata.CumulativeTemporality + } +} + // AggregationSelector selects the aggregation and the parameters to use for // that aggregation based on the InstrumentKind. // From 2e254bb1ce46065026bcb76e3df49a7f47a4e0de Mon Sep 17 00:00:00 2001 From: Dave Protasowski Date: Wed, 1 Oct 2025 09:49:13 -0400 Subject: [PATCH 2/4] include unit tests --- sdk/metric/reader_test.go | 58 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/sdk/metric/reader_test.go b/sdk/metric/reader_test.go index b8c5a8a8d7f..eee7d6c4736 100644 --- a/sdk/metric/reader_test.go +++ b/sdk/metric/reader_test.go @@ -333,6 +333,64 @@ func TestDefaultTemporalitySelector(t *testing.T) { } } +func TestCumulativeTemporalitySelector(t *testing.T) { + var undefinedInstrument InstrumentKind + for _, ik := range []InstrumentKind{ + undefinedInstrument, + InstrumentKindCounter, + InstrumentKindUpDownCounter, + InstrumentKindHistogram, + InstrumentKindGauge, + InstrumentKindObservableCounter, + InstrumentKindObservableUpDownCounter, + InstrumentKindObservableGauge, + } { + assert.Equal(t, metricdata.CumulativeTemporality, CumulativeTemporalitySelector(ik)) + } +} + +func TestDeltaTemporalitySelector(t *testing.T) { + var undefinedInstrument InstrumentKind + for _, ik := range []InstrumentKind{ + InstrumentKindCounter, + InstrumentKindHistogram, + InstrumentKindObservableCounter, + } { + assert.Equal(t, metricdata.DeltaTemporality, DeltaTemporalitySelector(ik)) + } + + for _, ik := range []InstrumentKind{ + undefinedInstrument, + InstrumentKindGauge, + InstrumentKindObservableGauge, + InstrumentKindObservableUpDownCounter, + InstrumentKindUpDownCounter, + } { + assert.Equal(t, metricdata.CumulativeTemporality, DeltaTemporalitySelector(ik)) + } +} + +func TestLowMemoryTemporalitySelector(t *testing.T) { + var undefinedInstrument InstrumentKind + for _, ik := range []InstrumentKind{ + InstrumentKindCounter, + InstrumentKindHistogram, + } { + assert.Equal(t, metricdata.DeltaTemporality, LowMemoryTemporalitySelector(ik)) + } + + for _, ik := range []InstrumentKind{ + undefinedInstrument, + InstrumentKindGauge, + InstrumentKindObservableCounter, + InstrumentKindObservableGauge, + InstrumentKindObservableUpDownCounter, + InstrumentKindUpDownCounter, + } { + assert.Equal(t, metricdata.CumulativeTemporality, LowMemoryTemporalitySelector(ik)) + } +} + type notComparable [0]func() // nolint:unused // non-comparable type itself is used. type noCompareReader struct { From 6c3f0bbeb902f6b63ce99c3a2d86478145afc4d3 Mon Sep 17 00:00:00 2001 From: Dave Protasowski Date: Wed, 1 Oct 2025 09:53:04 -0400 Subject: [PATCH 3/4] include changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b02ad434482..c83aa5b6711 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added +- Add temporality selector functions `DeltaTemporalitySelector`, `CumulativeTemporalitySelector`, `LowMemoryTemporalitySelector`. - Add `WithInstrumentationAttributeSet` option to `go.opentelemetry.io/otel/log`, `go.opentelemetry.io/otel/metric`, and `go.opentelemetry.io/otel/trace` packages. This provides a concurrent-safe and performant alternative to `WithInstrumentationAttributes` by accepting a pre-constructed `attribute.Set`. (#7287) - Greatly reduce the cost of recording metrics in `go.opentelemetry.io/otel/sdk/metric` using hashing for map keys. (#7175) From 4b3bcd938eb7be505e0bab4f8fc619120053fb29 Mon Sep 17 00:00:00 2001 From: Dave Protasowski Date: Wed, 1 Oct 2025 10:11:45 -0400 Subject: [PATCH 4/4] Update CHANGELOG.md Co-authored-by: Damien Mathieu <42@dmathieu.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c83aa5b6711..e42f0eea459 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added -- Add temporality selector functions `DeltaTemporalitySelector`, `CumulativeTemporalitySelector`, `LowMemoryTemporalitySelector`. +- Add temporality selector functions `DeltaTemporalitySelector`, `CumulativeTemporalitySelector`, `LowMemoryTemporalitySelector` to `go.opentelemetry.io/otel/sdk/metric`. (#7434) - Add `WithInstrumentationAttributeSet` option to `go.opentelemetry.io/otel/log`, `go.opentelemetry.io/otel/metric`, and `go.opentelemetry.io/otel/trace` packages. This provides a concurrent-safe and performant alternative to `WithInstrumentationAttributes` by accepting a pre-constructed `attribute.Set`. (#7287) - Greatly reduce the cost of recording metrics in `go.opentelemetry.io/otel/sdk/metric` using hashing for map keys. (#7175)