diff --git a/CHANGELOG.md b/CHANGELOG.md index 9877807a3c3..287a498320a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - The `Extrema` in `go.opentelemetry.io/otel/sdk/metric/metricdata` is redefined with a generic argument of `[N int64 | float64]`. (#3870) - Move No-Op implementation from `go.opentelemetry.io/otel/metric` into its own package `go.opentelemetry.io/otel/metric/noop`. (#3941) - `metric.NewNoopMeterProvider` is replaced with `noop.NewMeterProvider` +- The measurement methods for all instruments in `go.opentelemetry.io/otel/metric/instrument` accept a `"go.opentelemetry.io/otel/attribute".Set` instead of the variadic `"go.opentelemetry.io/otel/attribute".KeyValue`. (#3947) +- The `Observer` methods in `go.opentelemetry.io/otel/metric` accept a `"go.opentelemetry.io/otel/attribute".Set` instead of the variadic `"go.opentelemetry.io/otel/attribute".KeyValue`. (#3947) ### Fixed diff --git a/example/prometheus/main.go b/example/prometheus/main.go index 652140d9442..ae7bf560b16 100644 --- a/example/prometheus/main.go +++ b/example/prometheus/main.go @@ -50,17 +50,17 @@ func main() { // Start the prometheus HTTP server and pass the exporter Collector to it go serveMetrics() - attrs := []attribute.KeyValue{ + attrs := attribute.NewSet( attribute.Key("A").String("B"), attribute.Key("C").String("D"), - } + ) // This is the equivalent of prometheus.NewCounterVec counter, err := meter.Float64Counter("foo", instrument.WithDescription("a simple counter")) if err != nil { log.Fatal(err) } - counter.Add(ctx, 5, attrs...) + counter.AddWithAttributes(ctx, 5, attrs) gauge, err := meter.Float64ObservableGauge("bar", instrument.WithDescription("a fun little gauge")) if err != nil { @@ -68,7 +68,7 @@ func main() { } _, err = meter.RegisterCallback(func(_ context.Context, o api.Observer) error { n := -10. + rng.Float64()*(90.) // [-10, 100) - o.ObserveFloat64(gauge, n, attrs...) + o.ObserveFloat64WithAttributes(gauge, n, attrs) return nil }, gauge) if err != nil { @@ -80,10 +80,10 @@ func main() { if err != nil { log.Fatal(err) } - histogram.Record(ctx, 23, attrs...) - histogram.Record(ctx, 7, attrs...) - histogram.Record(ctx, 101, attrs...) - histogram.Record(ctx, 105, attrs...) + histogram.RecordWithAttributes(ctx, 23, attrs) + histogram.RecordWithAttributes(ctx, 7, attrs) + histogram.RecordWithAttributes(ctx, 101, attrs) + histogram.RecordWithAttributes(ctx, 105, attrs) ctx, _ = signal.NotifyContext(ctx, os.Interrupt) <-ctx.Done() diff --git a/example/view/main.go b/example/view/main.go index b609e48737d..91fabeca042 100644 --- a/example/view/main.go +++ b/example/view/main.go @@ -64,25 +64,25 @@ func main() { // Start the prometheus HTTP server and pass the exporter Collector to it go serveMetrics() - attrs := []attribute.KeyValue{ + attrs := attribute.NewSet( attribute.Key("A").String("B"), attribute.Key("C").String("D"), - } + ) counter, err := meter.Float64Counter("foo", instrument.WithDescription("a simple counter")) if err != nil { log.Fatal(err) } - counter.Add(ctx, 5, attrs...) + counter.AddWithAttributes(ctx, 5, attrs) histogram, err := meter.Float64Histogram("custom_histogram", instrument.WithDescription("a histogram with custom buckets and rename")) if err != nil { log.Fatal(err) } - histogram.Record(ctx, 136, attrs...) - histogram.Record(ctx, 64, attrs...) - histogram.Record(ctx, 701, attrs...) - histogram.Record(ctx, 830, attrs...) + histogram.RecordWithAttributes(ctx, 136, attrs) + histogram.RecordWithAttributes(ctx, 64, attrs) + histogram.RecordWithAttributes(ctx, 701, attrs) + histogram.RecordWithAttributes(ctx, 830, attrs) ctx, _ = signal.NotifyContext(ctx, os.Interrupt) <-ctx.Done() diff --git a/exporters/prometheus/exporter_test.go b/exporters/prometheus/exporter_test.go index 164dcbd0956..b38515c6130 100644 --- a/exporters/prometheus/exporter_test.go +++ b/exporters/prometheus/exporter_test.go @@ -46,67 +46,67 @@ func TestPrometheusExporter(t *testing.T) { name: "counter", expectedFile: "testdata/counter.txt", recordMetrics: func(ctx context.Context, meter otelmetric.Meter) { - attrs := []attribute.KeyValue{ + attrs := attribute.NewSet( attribute.Key("A").String("B"), attribute.Key("C").String("D"), attribute.Key("E").Bool(true), attribute.Key("F").Int(42), - } + ) counter, err := meter.Float64Counter( "foo", instrument.WithDescription("a simple counter"), instrument.WithUnit("ms"), ) require.NoError(t, err) - counter.Add(ctx, 5, attrs...) - counter.Add(ctx, 10.3, attrs...) - counter.Add(ctx, 9, attrs...) + counter.AddWithAttributes(ctx, 5, attrs) + counter.AddWithAttributes(ctx, 10.3, attrs) + counter.AddWithAttributes(ctx, 9, attrs) - attrs2 := []attribute.KeyValue{ + attrs2 := attribute.NewSet( attribute.Key("A").String("D"), attribute.Key("C").String("B"), attribute.Key("E").Bool(true), attribute.Key("F").Int(42), - } - counter.Add(ctx, 5, attrs2...) + ) + counter.AddWithAttributes(ctx, 5, attrs2) }, }, { name: "gauge", expectedFile: "testdata/gauge.txt", recordMetrics: func(ctx context.Context, meter otelmetric.Meter) { - attrs := []attribute.KeyValue{ + attrs := attribute.NewSet( attribute.Key("A").String("B"), attribute.Key("C").String("D"), - } + ) gauge, err := meter.Float64UpDownCounter( "bar", instrument.WithDescription("a fun little gauge"), instrument.WithUnit("1"), ) require.NoError(t, err) - gauge.Add(ctx, 1.0, attrs...) - gauge.Add(ctx, -.25, attrs...) + gauge.AddWithAttributes(ctx, 1.0, attrs) + gauge.AddWithAttributes(ctx, -.25, attrs) }, }, { name: "histogram", expectedFile: "testdata/histogram.txt", recordMetrics: func(ctx context.Context, meter otelmetric.Meter) { - attrs := []attribute.KeyValue{ + attrs := attribute.NewSet( attribute.Key("A").String("B"), attribute.Key("C").String("D"), - } + ) histogram, err := meter.Float64Histogram( "histogram_baz", instrument.WithDescription("a very nice histogram"), instrument.WithUnit("By"), ) require.NoError(t, err) - histogram.Record(ctx, 23, attrs...) - histogram.Record(ctx, 7, attrs...) - histogram.Record(ctx, 101, attrs...) - histogram.Record(ctx, 105, attrs...) + histogram.RecordWithAttributes(ctx, 23, attrs) + histogram.RecordWithAttributes(ctx, 7, attrs) + histogram.RecordWithAttributes(ctx, 101, attrs) + histogram.RecordWithAttributes(ctx, 105, attrs) }, }, { @@ -114,7 +114,7 @@ func TestPrometheusExporter(t *testing.T) { expectedFile: "testdata/sanitized_labels.txt", options: []Option{WithoutUnits()}, recordMetrics: func(ctx context.Context, meter otelmetric.Meter) { - attrs := []attribute.KeyValue{ + attrs := attribute.NewSet( // exact match, value should be overwritten attribute.Key("A.B").String("X"), attribute.Key("A.B").String("Q"), @@ -122,7 +122,7 @@ func TestPrometheusExporter(t *testing.T) { // unintended match due to sanitization, values should be concatenated attribute.Key("C.D").String("Y"), attribute.Key("C/D").String("Z"), - } + ) counter, err := meter.Float64Counter( "foo", instrument.WithDescription("a sanitary counter"), @@ -130,37 +130,37 @@ func TestPrometheusExporter(t *testing.T) { instrument.WithUnit("By"), ) require.NoError(t, err) - counter.Add(ctx, 5, attrs...) - counter.Add(ctx, 10.3, attrs...) - counter.Add(ctx, 9, attrs...) + counter.AddWithAttributes(ctx, 5, attrs) + counter.AddWithAttributes(ctx, 10.3, attrs) + counter.AddWithAttributes(ctx, 9, attrs) }, }, { name: "invalid instruments are renamed", expectedFile: "testdata/sanitized_names.txt", recordMetrics: func(ctx context.Context, meter otelmetric.Meter) { - attrs := []attribute.KeyValue{ + attrs := attribute.NewSet( attribute.Key("A").String("B"), attribute.Key("C").String("D"), - } + ) // Valid. gauge, err := meter.Float64UpDownCounter("bar", instrument.WithDescription("a fun little gauge")) require.NoError(t, err) - gauge.Add(ctx, 100, attrs...) - gauge.Add(ctx, -25, attrs...) + gauge.AddWithAttributes(ctx, 100, attrs) + gauge.AddWithAttributes(ctx, -25, attrs) // Invalid, will be renamed. gauge, err = meter.Float64UpDownCounter("invalid.gauge.name", instrument.WithDescription("a gauge with an invalid name")) require.NoError(t, err) - gauge.Add(ctx, 100, attrs...) + gauge.AddWithAttributes(ctx, 100, attrs) counter, err := meter.Float64Counter("0invalid.counter.name", instrument.WithDescription("a counter with an invalid name")) require.NoError(t, err) - counter.Add(ctx, 100, attrs...) + counter.AddWithAttributes(ctx, 100, attrs) histogram, err := meter.Float64Histogram("invalid.hist.name", instrument.WithDescription("a histogram with an invalid name")) require.NoError(t, err) - histogram.Record(ctx, 23, attrs...) + histogram.RecordWithAttributes(ctx, 23, attrs) }, }, { @@ -168,17 +168,17 @@ func TestPrometheusExporter(t *testing.T) { emptyResource: true, expectedFile: "testdata/empty_resource.txt", recordMetrics: func(ctx context.Context, meter otelmetric.Meter) { - attrs := []attribute.KeyValue{ + attrs := attribute.NewSet( attribute.Key("A").String("B"), attribute.Key("C").String("D"), attribute.Key("E").Bool(true), attribute.Key("F").Int(42), - } + ) counter, err := meter.Float64Counter("foo", instrument.WithDescription("a simple counter")) require.NoError(t, err) - counter.Add(ctx, 5, attrs...) - counter.Add(ctx, 10.3, attrs...) - counter.Add(ctx, 9, attrs...) + counter.AddWithAttributes(ctx, 5, attrs) + counter.AddWithAttributes(ctx, 10.3, attrs) + counter.AddWithAttributes(ctx, 9, attrs) }, }, { @@ -189,17 +189,17 @@ func TestPrometheusExporter(t *testing.T) { }, expectedFile: "testdata/custom_resource.txt", recordMetrics: func(ctx context.Context, meter otelmetric.Meter) { - attrs := []attribute.KeyValue{ + attrs := attribute.NewSet( attribute.Key("A").String("B"), attribute.Key("C").String("D"), attribute.Key("E").Bool(true), attribute.Key("F").Int(42), - } + ) counter, err := meter.Float64Counter("foo", instrument.WithDescription("a simple counter")) require.NoError(t, err) - counter.Add(ctx, 5, attrs...) - counter.Add(ctx, 10.3, attrs...) - counter.Add(ctx, 9, attrs...) + counter.AddWithAttributes(ctx, 5, attrs) + counter.AddWithAttributes(ctx, 10.3, attrs) + counter.AddWithAttributes(ctx, 9, attrs) }, }, { @@ -207,17 +207,17 @@ func TestPrometheusExporter(t *testing.T) { options: []Option{WithoutTargetInfo()}, expectedFile: "testdata/without_target_info.txt", recordMetrics: func(ctx context.Context, meter otelmetric.Meter) { - attrs := []attribute.KeyValue{ + attrs := attribute.NewSet( attribute.Key("A").String("B"), attribute.Key("C").String("D"), attribute.Key("E").Bool(true), attribute.Key("F").Int(42), - } + ) counter, err := meter.Float64Counter("foo", instrument.WithDescription("a simple counter")) require.NoError(t, err) - counter.Add(ctx, 5, attrs...) - counter.Add(ctx, 10.3, attrs...) - counter.Add(ctx, 9, attrs...) + counter.AddWithAttributes(ctx, 5, attrs) + counter.AddWithAttributes(ctx, 10.3, attrs) + counter.AddWithAttributes(ctx, 9, attrs) }, }, { @@ -225,18 +225,18 @@ func TestPrometheusExporter(t *testing.T) { options: []Option{WithoutScopeInfo()}, expectedFile: "testdata/without_scope_info.txt", recordMetrics: func(ctx context.Context, meter otelmetric.Meter) { - attrs := []attribute.KeyValue{ + attrs := attribute.NewSet( attribute.Key("A").String("B"), attribute.Key("C").String("D"), - } + ) gauge, err := meter.Int64UpDownCounter( "bar", instrument.WithDescription("a fun little gauge"), instrument.WithUnit("1"), ) require.NoError(t, err) - gauge.Add(ctx, 2, attrs...) - gauge.Add(ctx, -1, attrs...) + gauge.AddWithAttributes(ctx, 2, attrs) + gauge.AddWithAttributes(ctx, -1, attrs) }, }, { @@ -244,18 +244,18 @@ func TestPrometheusExporter(t *testing.T) { options: []Option{WithoutScopeInfo(), WithoutTargetInfo()}, expectedFile: "testdata/without_scope_and_target_info.txt", recordMetrics: func(ctx context.Context, meter otelmetric.Meter) { - attrs := []attribute.KeyValue{ + attrs := attribute.NewSet( attribute.Key("A").String("B"), attribute.Key("C").String("D"), - } + ) counter, err := meter.Int64Counter( "bar", instrument.WithDescription("a fun little counter"), instrument.WithUnit("By"), ) require.NoError(t, err) - counter.Add(ctx, 2, attrs...) - counter.Add(ctx, 1, attrs...) + counter.AddWithAttributes(ctx, 2, attrs) + counter.AddWithAttributes(ctx, 1, attrs) }, }, } @@ -368,7 +368,7 @@ func TestMultiScopes(t *testing.T) { instrument.WithUnit("ms"), instrument.WithDescription("meter foo counter")) assert.NoError(t, err) - fooCounter.Add(ctx, 100, attribute.String("type", "foo")) + fooCounter.AddWithAttributes(ctx, 100, attribute.NewSet(attribute.String("type", "foo"))) barCounter, err := provider.Meter("meterbar", otelmetric.WithInstrumentationVersion("v0.1.0")). Int64Counter( @@ -376,7 +376,7 @@ func TestMultiScopes(t *testing.T) { instrument.WithUnit("ms"), instrument.WithDescription("meter bar counter")) assert.NoError(t, err) - barCounter.Add(ctx, 200, attribute.String("type", "bar")) + barCounter.AddWithAttributes(ctx, 200, attribute.NewSet(attribute.String("type", "bar"))) file, err := os.Open("testdata/multi_scopes.txt") require.NoError(t, err) @@ -401,13 +401,13 @@ func TestDuplicateMetrics(t *testing.T) { instrument.WithUnit("By"), instrument.WithDescription("meter counter foo")) assert.NoError(t, err) - fooA.Add(ctx, 100, attribute.String("A", "B")) + fooA.AddWithAttributes(ctx, 100, attribute.NewSet(attribute.String("A", "B"))) fooB, err := meterB.Int64Counter("foo", instrument.WithUnit("By"), instrument.WithDescription("meter counter foo")) assert.NoError(t, err) - fooB.Add(ctx, 100, attribute.String("A", "B")) + fooB.AddWithAttributes(ctx, 100, attribute.NewSet(attribute.String("A", "B"))) }, possibleExpectedFiles: []string{"testdata/no_conflict_two_counters.txt"}, }, @@ -418,13 +418,13 @@ func TestDuplicateMetrics(t *testing.T) { instrument.WithUnit("By"), instrument.WithDescription("meter gauge foo")) assert.NoError(t, err) - fooA.Add(ctx, 100, attribute.String("A", "B")) + fooA.AddWithAttributes(ctx, 100, attribute.NewSet(attribute.String("A", "B"))) fooB, err := meterB.Int64UpDownCounter("foo", instrument.WithUnit("By"), instrument.WithDescription("meter gauge foo")) assert.NoError(t, err) - fooB.Add(ctx, 100, attribute.String("A", "B")) + fooB.AddWithAttributes(ctx, 100, attribute.NewSet(attribute.String("A", "B"))) }, possibleExpectedFiles: []string{"testdata/no_conflict_two_updowncounters.txt"}, }, @@ -435,13 +435,13 @@ func TestDuplicateMetrics(t *testing.T) { instrument.WithUnit("By"), instrument.WithDescription("meter histogram foo")) assert.NoError(t, err) - fooA.Record(ctx, 100, attribute.String("A", "B")) + fooA.RecordWithAttributes(ctx, 100, attribute.NewSet(attribute.String("A", "B"))) fooB, err := meterB.Int64Histogram("foo", instrument.WithUnit("By"), instrument.WithDescription("meter histogram foo")) assert.NoError(t, err) - fooB.Record(ctx, 100, attribute.String("A", "B")) + fooB.RecordWithAttributes(ctx, 100, attribute.NewSet(attribute.String("A", "B"))) }, possibleExpectedFiles: []string{"testdata/no_conflict_two_histograms.txt"}, }, @@ -452,13 +452,13 @@ func TestDuplicateMetrics(t *testing.T) { instrument.WithUnit("By"), instrument.WithDescription("meter a bar")) assert.NoError(t, err) - barA.Add(ctx, 100, attribute.String("type", "bar")) + barA.AddWithAttributes(ctx, 100, attribute.NewSet(attribute.String("type", "bar"))) barB, err := meterB.Int64Counter("bar", instrument.WithUnit("By"), instrument.WithDescription("meter b bar")) assert.NoError(t, err) - barB.Add(ctx, 100, attribute.String("type", "bar")) + barB.AddWithAttributes(ctx, 100, attribute.NewSet(attribute.String("type", "bar"))) }, possibleExpectedFiles: []string{ "testdata/conflict_help_two_counters_1.txt", @@ -472,13 +472,13 @@ func TestDuplicateMetrics(t *testing.T) { instrument.WithUnit("By"), instrument.WithDescription("meter a bar")) assert.NoError(t, err) - barA.Add(ctx, 100, attribute.String("type", "bar")) + barA.AddWithAttributes(ctx, 100, attribute.NewSet(attribute.String("type", "bar"))) barB, err := meterB.Int64UpDownCounter("bar", instrument.WithUnit("By"), instrument.WithDescription("meter b bar")) assert.NoError(t, err) - barB.Add(ctx, 100, attribute.String("type", "bar")) + barB.AddWithAttributes(ctx, 100, attribute.NewSet(attribute.String("type", "bar"))) }, possibleExpectedFiles: []string{ "testdata/conflict_help_two_updowncounters_1.txt", @@ -492,13 +492,13 @@ func TestDuplicateMetrics(t *testing.T) { instrument.WithUnit("By"), instrument.WithDescription("meter a bar")) assert.NoError(t, err) - barA.Record(ctx, 100, attribute.String("A", "B")) + barA.RecordWithAttributes(ctx, 100, attribute.NewSet(attribute.String("A", "B"))) barB, err := meterB.Int64Histogram("bar", instrument.WithUnit("By"), instrument.WithDescription("meter b bar")) assert.NoError(t, err) - barB.Record(ctx, 100, attribute.String("A", "B")) + barB.RecordWithAttributes(ctx, 100, attribute.NewSet(attribute.String("A", "B"))) }, possibleExpectedFiles: []string{ "testdata/conflict_help_two_histograms_1.txt", @@ -512,13 +512,13 @@ func TestDuplicateMetrics(t *testing.T) { instrument.WithUnit("By"), instrument.WithDescription("meter bar")) assert.NoError(t, err) - bazA.Add(ctx, 100, attribute.String("type", "bar")) + bazA.AddWithAttributes(ctx, 100, attribute.NewSet(attribute.String("type", "bar"))) bazB, err := meterB.Int64Counter("bar", instrument.WithUnit("ms"), instrument.WithDescription("meter bar")) assert.NoError(t, err) - bazB.Add(ctx, 100, attribute.String("type", "bar")) + bazB.AddWithAttributes(ctx, 100, attribute.NewSet(attribute.String("type", "bar"))) }, options: []Option{WithoutUnits()}, possibleExpectedFiles: []string{"testdata/conflict_unit_two_counters.txt"}, @@ -530,13 +530,13 @@ func TestDuplicateMetrics(t *testing.T) { instrument.WithUnit("By"), instrument.WithDescription("meter gauge bar")) assert.NoError(t, err) - barA.Add(ctx, 100, attribute.String("type", "bar")) + barA.AddWithAttributes(ctx, 100, attribute.NewSet(attribute.String("type", "bar"))) barB, err := meterB.Int64UpDownCounter("bar", instrument.WithUnit("ms"), instrument.WithDescription("meter gauge bar")) assert.NoError(t, err) - barB.Add(ctx, 100, attribute.String("type", "bar")) + barB.AddWithAttributes(ctx, 100, attribute.NewSet(attribute.String("type", "bar"))) }, options: []Option{WithoutUnits()}, possibleExpectedFiles: []string{"testdata/conflict_unit_two_updowncounters.txt"}, @@ -548,13 +548,13 @@ func TestDuplicateMetrics(t *testing.T) { instrument.WithUnit("By"), instrument.WithDescription("meter histogram bar")) assert.NoError(t, err) - barA.Record(ctx, 100, attribute.String("A", "B")) + barA.RecordWithAttributes(ctx, 100, attribute.NewSet(attribute.String("A", "B"))) barB, err := meterB.Int64Histogram("bar", instrument.WithUnit("ms"), instrument.WithDescription("meter histogram bar")) assert.NoError(t, err) - barB.Record(ctx, 100, attribute.String("A", "B")) + barB.RecordWithAttributes(ctx, 100, attribute.NewSet(attribute.String("A", "B"))) }, options: []Option{WithoutUnits()}, possibleExpectedFiles: []string{"testdata/conflict_unit_two_histograms.txt"}, @@ -566,13 +566,13 @@ func TestDuplicateMetrics(t *testing.T) { instrument.WithUnit("By"), instrument.WithDescription("meter foo")) assert.NoError(t, err) - counter.Add(ctx, 100, attribute.String("type", "foo")) + counter.AddWithAttributes(ctx, 100, attribute.NewSet(attribute.String("type", "foo"))) gauge, err := meterA.Int64UpDownCounter("foo_total", instrument.WithUnit("By"), instrument.WithDescription("meter foo")) assert.NoError(t, err) - gauge.Add(ctx, 200, attribute.String("type", "foo")) + gauge.AddWithAttributes(ctx, 200, attribute.NewSet(attribute.String("type", "foo"))) }, options: []Option{WithoutUnits()}, possibleExpectedFiles: []string{ @@ -587,13 +587,13 @@ func TestDuplicateMetrics(t *testing.T) { instrument.WithUnit("By"), instrument.WithDescription("meter gauge foo")) assert.NoError(t, err) - fooA.Add(ctx, 100, attribute.String("A", "B")) + fooA.AddWithAttributes(ctx, 100, attribute.NewSet(attribute.String("A", "B"))) fooHistogramA, err := meterA.Int64Histogram("foo", instrument.WithUnit("By"), instrument.WithDescription("meter histogram foo")) assert.NoError(t, err) - fooHistogramA.Record(ctx, 100, attribute.String("A", "B")) + fooHistogramA.RecordWithAttributes(ctx, 100, attribute.NewSet(attribute.String("A", "B"))) }, possibleExpectedFiles: []string{ "testdata/conflict_type_histogram_and_updowncounter_1.txt", diff --git a/internal/global/instruments.go b/internal/global/instruments.go index ef1ed650b0d..52febd961ed 100644 --- a/internal/global/instruments.go +++ b/internal/global/instruments.go @@ -215,9 +215,13 @@ func (i *sfCounter) setDelegate(m metric.Meter) { i.delegate.Store(ctr) } -func (i *sfCounter) Add(ctx context.Context, incr float64, attrs ...attribute.KeyValue) { +func (i *sfCounter) Add(ctx context.Context, incr float64) { + i.AddWithAttributes(ctx, incr, *attribute.EmptySet()) +} + +func (i *sfCounter) AddWithAttributes(ctx context.Context, incr float64, attrs attribute.Set) { if ctr := i.delegate.Load(); ctr != nil { - ctr.(instrument.Float64Counter).Add(ctx, incr, attrs...) + ctr.(instrument.Float64Counter).AddWithAttributes(ctx, incr, attrs) } } @@ -239,9 +243,13 @@ func (i *sfUpDownCounter) setDelegate(m metric.Meter) { i.delegate.Store(ctr) } -func (i *sfUpDownCounter) Add(ctx context.Context, incr float64, attrs ...attribute.KeyValue) { +func (i *sfUpDownCounter) Add(ctx context.Context, incr float64) { + i.AddWithAttributes(ctx, incr, *attribute.EmptySet()) +} + +func (i *sfUpDownCounter) AddWithAttributes(ctx context.Context, incr float64, attrs attribute.Set) { if ctr := i.delegate.Load(); ctr != nil { - ctr.(instrument.Float64UpDownCounter).Add(ctx, incr, attrs...) + ctr.(instrument.Float64UpDownCounter).AddWithAttributes(ctx, incr, attrs) } } @@ -263,9 +271,13 @@ func (i *sfHistogram) setDelegate(m metric.Meter) { i.delegate.Store(ctr) } -func (i *sfHistogram) Record(ctx context.Context, x float64, attrs ...attribute.KeyValue) { +func (i *sfHistogram) Record(ctx context.Context, x float64) { + i.RecordWithAttributes(ctx, x, *attribute.EmptySet()) +} + +func (i *sfHistogram) RecordWithAttributes(ctx context.Context, x float64, attrs attribute.Set) { if ctr := i.delegate.Load(); ctr != nil { - ctr.(instrument.Float64Histogram).Record(ctx, x, attrs...) + ctr.(instrument.Float64Histogram).RecordWithAttributes(ctx, x, attrs) } } @@ -287,9 +299,13 @@ func (i *siCounter) setDelegate(m metric.Meter) { i.delegate.Store(ctr) } -func (i *siCounter) Add(ctx context.Context, x int64, attrs ...attribute.KeyValue) { +func (i *siCounter) Add(ctx context.Context, x int64) { + i.AddWithAttributes(ctx, x, *attribute.EmptySet()) +} + +func (i *siCounter) AddWithAttributes(ctx context.Context, x int64, attrs attribute.Set) { if ctr := i.delegate.Load(); ctr != nil { - ctr.(instrument.Int64Counter).Add(ctx, x, attrs...) + ctr.(instrument.Int64Counter).AddWithAttributes(ctx, x, attrs) } } @@ -311,9 +327,13 @@ func (i *siUpDownCounter) setDelegate(m metric.Meter) { i.delegate.Store(ctr) } -func (i *siUpDownCounter) Add(ctx context.Context, x int64, attrs ...attribute.KeyValue) { +func (i *siUpDownCounter) Add(ctx context.Context, x int64) { + i.AddWithAttributes(ctx, x, *attribute.EmptySet()) +} + +func (i *siUpDownCounter) AddWithAttributes(ctx context.Context, x int64, attrs attribute.Set) { if ctr := i.delegate.Load(); ctr != nil { - ctr.(instrument.Int64UpDownCounter).Add(ctx, x, attrs...) + ctr.(instrument.Int64UpDownCounter).AddWithAttributes(ctx, x, attrs) } } @@ -335,8 +355,12 @@ func (i *siHistogram) setDelegate(m metric.Meter) { i.delegate.Store(ctr) } -func (i *siHistogram) Record(ctx context.Context, x int64, attrs ...attribute.KeyValue) { +func (i *siHistogram) Record(ctx context.Context, x int64) { + i.RecordWithAttributes(ctx, x, *attribute.EmptySet()) +} + +func (i *siHistogram) RecordWithAttributes(ctx context.Context, x int64, attrs attribute.Set) { if ctr := i.delegate.Load(); ctr != nil { - ctr.(instrument.Int64Histogram).Record(ctx, x, attrs...) + ctr.(instrument.Int64Histogram).RecordWithAttributes(ctx, x, attrs) } } diff --git a/internal/global/instruments_test.go b/internal/global/instruments_test.go index 66fe8499cf0..64595fe425f 100644 --- a/internal/global/instruments_test.go +++ b/internal/global/instruments_test.go @@ -24,11 +24,17 @@ import ( "go.opentelemetry.io/otel/metric/noop" ) -func testFloat64Race(interact func(context.Context, float64, ...attribute.KeyValue), setDelegate func(metric.Meter)) { +func testFloat64Race(interact func(context.Context, float64), setDelegate func(metric.Meter)) { + testFloat64RaceWithAttributes(func(ctx context.Context, f float64, s attribute.Set) { + interact(ctx, f) + }, setDelegate) +} + +func testFloat64RaceWithAttributes(interact func(context.Context, float64, attribute.Set), setDelegate func(metric.Meter)) { finish := make(chan struct{}) go func() { for { - interact(context.Background(), 1) + interact(context.Background(), 1, *attribute.EmptySet()) select { case <-finish: return @@ -41,11 +47,17 @@ func testFloat64Race(interact func(context.Context, float64, ...attribute.KeyVal close(finish) } -func testInt64Race(interact func(context.Context, int64, ...attribute.KeyValue), setDelegate func(metric.Meter)) { +func testInt64Race(interact func(context.Context, int64), setDelegate func(metric.Meter)) { + testInt64RaceWithAttributes(func(ctx context.Context, i int64, s attribute.Set) { + interact(ctx, i) + }, setDelegate) +} + +func testInt64RaceWithAttributes(interact func(context.Context, int64, attribute.Set), setDelegate func(metric.Meter)) { finish := make(chan struct{}) go func() { for { - interact(context.Background(), 1) + interact(context.Background(), 1, *attribute.EmptySet()) select { case <-finish: return @@ -63,19 +75,19 @@ func TestAsyncInstrumentSetDelegateRace(t *testing.T) { t.Run("Float64", func(t *testing.T) { t.Run("Counter", func(t *testing.T) { delegate := &afCounter{} - f := func(context.Context, float64, ...attribute.KeyValue) { _ = delegate.Unwrap() } + f := func(context.Context, float64) { _ = delegate.Unwrap() } testFloat64Race(f, delegate.setDelegate) }) t.Run("UpDownCounter", func(t *testing.T) { delegate := &afUpDownCounter{} - f := func(context.Context, float64, ...attribute.KeyValue) { _ = delegate.Unwrap() } + f := func(context.Context, float64) { _ = delegate.Unwrap() } testFloat64Race(f, delegate.setDelegate) }) t.Run("Gauge", func(t *testing.T) { delegate := &afGauge{} - f := func(context.Context, float64, ...attribute.KeyValue) { _ = delegate.Unwrap() } + f := func(context.Context, float64) { _ = delegate.Unwrap() } testFloat64Race(f, delegate.setDelegate) }) }) @@ -85,19 +97,19 @@ func TestAsyncInstrumentSetDelegateRace(t *testing.T) { t.Run("Int64", func(t *testing.T) { t.Run("Counter", func(t *testing.T) { delegate := &aiCounter{} - f := func(context.Context, int64, ...attribute.KeyValue) { _ = delegate.Unwrap() } + f := func(context.Context, int64) { _ = delegate.Unwrap() } testInt64Race(f, delegate.setDelegate) }) t.Run("UpDownCounter", func(t *testing.T) { delegate := &aiUpDownCounter{} - f := func(context.Context, int64, ...attribute.KeyValue) { _ = delegate.Unwrap() } + f := func(context.Context, int64) { _ = delegate.Unwrap() } testInt64Race(f, delegate.setDelegate) }) t.Run("Gauge", func(t *testing.T) { delegate := &aiGauge{} - f := func(context.Context, int64, ...attribute.KeyValue) { _ = delegate.Unwrap() } + f := func(context.Context, int64) { _ = delegate.Unwrap() } testInt64Race(f, delegate.setDelegate) }) }) @@ -109,16 +121,19 @@ func TestSyncInstrumentSetDelegateRace(t *testing.T) { t.Run("Counter", func(t *testing.T) { delegate := &sfCounter{} testFloat64Race(delegate.Add, delegate.setDelegate) + testFloat64RaceWithAttributes(delegate.AddWithAttributes, delegate.setDelegate) }) t.Run("UpDownCounter", func(t *testing.T) { delegate := &sfUpDownCounter{} testFloat64Race(delegate.Add, delegate.setDelegate) + testFloat64RaceWithAttributes(delegate.AddWithAttributes, delegate.setDelegate) }) t.Run("Histogram", func(t *testing.T) { delegate := &sfHistogram{} testFloat64Race(delegate.Record, delegate.setDelegate) + testFloat64RaceWithAttributes(delegate.RecordWithAttributes, delegate.setDelegate) }) }) @@ -128,16 +143,19 @@ func TestSyncInstrumentSetDelegateRace(t *testing.T) { t.Run("Counter", func(t *testing.T) { delegate := &siCounter{} testInt64Race(delegate.Add, delegate.setDelegate) + testInt64RaceWithAttributes(delegate.AddWithAttributes, delegate.setDelegate) }) t.Run("UpDownCounter", func(t *testing.T) { delegate := &siUpDownCounter{} testInt64Race(delegate.Add, delegate.setDelegate) + testInt64RaceWithAttributes(delegate.AddWithAttributes, delegate.setDelegate) }) t.Run("Histogram", func(t *testing.T) { delegate := &siHistogram{} testInt64Race(delegate.Record, delegate.setDelegate) + testInt64RaceWithAttributes(delegate.RecordWithAttributes, delegate.setDelegate) }) }) } @@ -151,10 +169,16 @@ type testCountingFloatInstrument struct { func (i *testCountingFloatInstrument) observe() { i.count++ } -func (i *testCountingFloatInstrument) Add(context.Context, float64, ...attribute.KeyValue) { +func (i *testCountingFloatInstrument) Add(context.Context, float64) { + i.count++ +} +func (i *testCountingFloatInstrument) AddWithAttributes(context.Context, float64, attribute.Set) { i.count++ } -func (i *testCountingFloatInstrument) Record(context.Context, float64, ...attribute.KeyValue) { +func (i *testCountingFloatInstrument) Record(context.Context, float64) { + i.count++ +} +func (i *testCountingFloatInstrument) RecordWithAttributes(context.Context, float64, attribute.Set) { i.count++ } @@ -167,9 +191,15 @@ type testCountingIntInstrument struct { func (i *testCountingIntInstrument) observe() { i.count++ } -func (i *testCountingIntInstrument) Add(context.Context, int64, ...attribute.KeyValue) { +func (i *testCountingIntInstrument) Add(context.Context, int64) { + i.count++ +} +func (i *testCountingIntInstrument) AddWithAttributes(context.Context, int64, attribute.Set) { + i.count++ +} +func (i *testCountingIntInstrument) Record(context.Context, int64) { i.count++ } -func (i *testCountingIntInstrument) Record(context.Context, int64, ...attribute.KeyValue) { +func (i *testCountingIntInstrument) RecordWithAttributes(context.Context, int64, attribute.Set) { i.count++ } diff --git a/internal/global/meter_types_test.go b/internal/global/meter_types_test.go index 038083f274b..1ab9e1b762e 100644 --- a/internal/global/meter_types_test.go +++ b/internal/global/meter_types_test.go @@ -148,14 +148,22 @@ type observationRecorder struct { ctx context.Context } -func (o observationRecorder) ObserveFloat64(i instrument.Float64Observable, value float64, attr ...attribute.KeyValue) { +func (o observationRecorder) ObserveFloat64(i instrument.Float64Observable, value float64) { + o.ObserveFloat64WithAttributes(i, value, *attribute.EmptySet()) +} + +func (o observationRecorder) ObserveFloat64WithAttributes(i instrument.Float64Observable, value float64, attr attribute.Set) { iImpl, ok := i.(*testCountingFloatInstrument) if ok { iImpl.observe() } } -func (o observationRecorder) ObserveInt64(i instrument.Int64Observable, value int64, attr ...attribute.KeyValue) { +func (o observationRecorder) ObserveInt64(i instrument.Int64Observable, value int64) { + o.ObserveInt64WithAttributes(i, value, *attribute.EmptySet()) +} + +func (o observationRecorder) ObserveInt64WithAttributes(i instrument.Int64Observable, value int64, attr attribute.Set) { iImpl, ok := i.(*testCountingIntInstrument) if ok { iImpl.observe() diff --git a/metric/example_test.go b/metric/example_test.go index c909410e9e4..44460c9bba7 100644 --- a/metric/example_test.go +++ b/metric/example_test.go @@ -63,7 +63,8 @@ func ExampleMeter_asynchronous_single() { // // For demonstration purpose, a static value is used here. usage := 75000 - obsrv.Observe(int64(usage), attribute.Int("disk.id", 3)) + attrs := attribute.NewSet(attribute.Int("disk.id", 3)) + obsrv.ObserveWithAttributes(int64(usage), attrs) return nil }), ) diff --git a/metric/instrument/asyncfloat64.go b/metric/instrument/asyncfloat64.go index cf0008e61f3..ac7623a8e5d 100644 --- a/metric/instrument/asyncfloat64.go +++ b/metric/instrument/asyncfloat64.go @@ -180,8 +180,11 @@ type Float64ObservableGaugeOption interface { // // Warning: methods may be added to this interface in minor releases. type Float64Observer interface { - // Observe records the float64 value with attributes. - Observe(value float64, attributes ...attribute.KeyValue) + // Observe records the float64 value. + Observe(value float64) + + // ObserveWithAttributes records the float64 value with attributes. + ObserveWithAttributes(value float64, attributes attribute.Set) } // Float64Callback is a function registered with a Meter that makes diff --git a/metric/instrument/asyncfloat64_test.go b/metric/instrument/asyncfloat64_test.go index 0613fb863bd..58fa2dad36b 100644 --- a/metric/instrument/asyncfloat64_test.go +++ b/metric/instrument/asyncfloat64_test.go @@ -87,6 +87,10 @@ type float64Observer struct { got float64 } -func (o *float64Observer) Observe(v float64, _ ...attribute.KeyValue) { +func (o *float64Observer) Observe(v float64) { + o.ObserveWithAttributes(v, *attribute.EmptySet()) +} + +func (o *float64Observer) ObserveWithAttributes(v float64, _ attribute.Set) { o.got = v } diff --git a/metric/instrument/asyncint64.go b/metric/instrument/asyncint64.go index 752dcea0a1e..7750821ad20 100644 --- a/metric/instrument/asyncint64.go +++ b/metric/instrument/asyncint64.go @@ -179,8 +179,11 @@ type Int64ObservableGaugeOption interface { // // Warning: methods may be added to this interface in minor releases. type Int64Observer interface { - // Observe records the int64 value with attributes. - Observe(value int64, attributes ...attribute.KeyValue) + // Observe records the int64 value. + Observe(value int64) + + // ObserveWithAttributes records the int64 value with attributes. + ObserveWithAttributes(value int64, attributes attribute.Set) } // Int64Callback is a function registered with a Meter that makes observations diff --git a/metric/instrument/asyncint64_test.go b/metric/instrument/asyncint64_test.go index b8e8cba5894..7667af7f56e 100644 --- a/metric/instrument/asyncint64_test.go +++ b/metric/instrument/asyncint64_test.go @@ -87,6 +87,10 @@ type int64Observer struct { got int64 } -func (o *int64Observer) Observe(v int64, _ ...attribute.KeyValue) { +func (o *int64Observer) Observe(v int64) { + o.ObserveWithAttributes(v, *attribute.EmptySet()) +} + +func (o *int64Observer) ObserveWithAttributes(v int64, _ attribute.Set) { o.got = v } diff --git a/metric/instrument/syncfloat64.go b/metric/instrument/syncfloat64.go index 2d55384ab26..79caea0ff11 100644 --- a/metric/instrument/syncfloat64.go +++ b/metric/instrument/syncfloat64.go @@ -24,8 +24,11 @@ import ( // // Warning: methods may be added to this interface in minor releases. type Float64Counter interface { - // Add records a change to the counter. - Add(ctx context.Context, incr float64, attrs ...attribute.KeyValue) + // Add records the incr change to the counter. + Add(ctx context.Context, incr float64) + + // AddWithAttributes records the incr change to the counter with attrs. + AddWithAttributes(ctx context.Context, incr float64, attrs attribute.Set) } // Float64CounterConfig contains options for synchronous counter instruments that @@ -66,8 +69,11 @@ type Float64CounterOption interface { // // Warning: methods may be added to this interface in minor releases. type Float64UpDownCounter interface { - // Add records a change to the counter. - Add(ctx context.Context, incr float64, attrs ...attribute.KeyValue) + // Add records the incr change to the counter. + Add(ctx context.Context, incr float64) + + // AddWithAttributes records the incr change to the counter with attrs. + AddWithAttributes(ctx context.Context, incr float64, attrs attribute.Set) } // Float64UpDownCounterConfig contains options for synchronous counter @@ -109,8 +115,11 @@ type Float64UpDownCounterOption interface { // // Warning: methods may be added to this interface in minor releases. type Float64Histogram interface { - // Record adds an additional value to the distribution. - Record(ctx context.Context, incr float64, attrs ...attribute.KeyValue) + // Record records val for the histogram. + Record(ctx context.Context, val float64) + + // RecordWithAttributes records val with attrs for the histogram. + RecordWithAttributes(ctx context.Context, val float64, attrs attribute.Set) } // Float64HistogramConfig contains options for synchronous counter instruments diff --git a/metric/instrument/syncint64.go b/metric/instrument/syncint64.go index 9b6b3917ffc..42299ac1b0e 100644 --- a/metric/instrument/syncint64.go +++ b/metric/instrument/syncint64.go @@ -24,8 +24,11 @@ import ( // // Warning: methods may be added to this interface in minor releases. type Int64Counter interface { - // Add records a change to the counter. - Add(ctx context.Context, incr int64, attrs ...attribute.KeyValue) + // Add records the incr change to the counter. + Add(ctx context.Context, incr int64) + + // AddWithAttributes records the incr change to the counter with attrs. + AddWithAttributes(ctx context.Context, incr int64, attrs attribute.Set) } // Int64CounterConfig contains options for synchronous counter instruments that @@ -66,8 +69,11 @@ type Int64CounterOption interface { // // Warning: methods may be added to this interface in minor releases. type Int64UpDownCounter interface { - // Add records a change to the counter. - Add(ctx context.Context, incr int64, attrs ...attribute.KeyValue) + // Add records the incr change to the counter. + Add(ctx context.Context, incr int64) + + // AddWithAttributes records the incr change to the counter with attrs. + AddWithAttributes(ctx context.Context, incr int64, attrs attribute.Set) } // Int64UpDownCounterConfig contains options for synchronous counter @@ -109,8 +115,11 @@ type Int64UpDownCounterOption interface { // // Warning: methods may be added to this interface in minor releases. type Int64Histogram interface { - // Record adds an additional value to the distribution. - Record(ctx context.Context, incr int64, attrs ...attribute.KeyValue) + // Record records val for the histogram. + Record(ctx context.Context, val int64) + + // RecordWithAttributes records val with attrs for the histogram. + RecordWithAttributes(ctx context.Context, val int64, attrs attribute.Set) } // Int64HistogramConfig contains options for synchronous counter instruments diff --git a/metric/meter.go b/metric/meter.go index 1cb556b0534..2fb5add0103 100644 --- a/metric/meter.go +++ b/metric/meter.go @@ -126,10 +126,17 @@ type Callback func(context.Context, Observer) error // Observer records measurements for multiple instruments in a Callback. type Observer interface { - // ObserveFloat64 records the float64 value with attributes for obsrv. - ObserveFloat64(obsrv instrument.Float64Observable, value float64, attributes ...attribute.KeyValue) - // ObserveInt64 records the int64 value with attributes for obsrv. - ObserveInt64(obsrv instrument.Int64Observable, value int64, attributes ...attribute.KeyValue) + // ObserveFloat64 records the float64 value for obsrv. + ObserveFloat64(obsrv instrument.Float64Observable, value float64) + // ObserveFloat64WithAttributes records the float64 value with attributes + // for obsrv. + ObserveFloat64WithAttributes(obsrv instrument.Float64Observable, value float64, attributes attribute.Set) + + // ObserveInt64 records the int64 value for obsrv. + ObserveInt64(obsrv instrument.Int64Observable, value int64) + + // ObserveInt64WithAttributes records the int64 value with attributes for obsrv. + ObserveInt64WithAttributes(obsrv instrument.Int64Observable, value int64, attributes attribute.Set) } // Registration is an token representing the unique registration of a callback diff --git a/metric/noop/noop.go b/metric/noop/noop.go index 0b2905da629..0172ac772c9 100644 --- a/metric/noop/noop.go +++ b/metric/noop/noop.go @@ -152,11 +152,19 @@ func (Meter) RegisterCallback(metric.Callback, ...instrument.Observable) (metric type Observer struct{} // ObserveFloat64 performs no operation. -func (Observer) ObserveFloat64(instrument.Float64Observable, float64, ...attribute.KeyValue) { +func (Observer) ObserveFloat64(instrument.Float64Observable, float64) { +} + +// ObserveFloat64WithAttributes performs no operation. +func (Observer) ObserveFloat64WithAttributes(instrument.Float64Observable, float64, attribute.Set) { } // ObserveInt64 performs no operation. -func (Observer) ObserveInt64(instrument.Int64Observable, int64, ...attribute.KeyValue) { +func (Observer) ObserveInt64(instrument.Int64Observable, int64) { +} + +// ObserveInt64WithAttributes performs no operation. +func (Observer) ObserveInt64WithAttributes(instrument.Int64Observable, int64, attribute.Set) { } // Registration is the registration of a Callback with a No-Op Meter. @@ -172,42 +180,60 @@ func (Registration) Unregister() error { return nil } type Int64Counter struct{} // Add performs no operation. -func (Int64Counter) Add(context.Context, int64, ...attribute.KeyValue) {} +func (Int64Counter) Add(context.Context, int64) {} + +// Add performs no operation. +func (Int64Counter) AddWithAttributes(context.Context, int64, attribute.Set) {} // Float64Counter is an OpenTelemetry Counter used to record float64 // measurements. It produces no telemetry. type Float64Counter struct{} // Add performs no operation. -func (Float64Counter) Add(context.Context, float64, ...attribute.KeyValue) {} +func (Float64Counter) Add(context.Context, float64) {} + +// AddWithAttributes performs no operation. +func (Float64Counter) AddWithAttributes(context.Context, float64, attribute.Set) {} // Int64UpDownCounter is an OpenTelemetry UpDownCounter used to record int64 // measurements. It produces no telemetry. type Int64UpDownCounter struct{} // Add performs no operation. -func (Int64UpDownCounter) Add(context.Context, int64, ...attribute.KeyValue) {} +func (Int64UpDownCounter) Add(context.Context, int64) {} + +// AddWithAttributes performs no operation. +func (Int64UpDownCounter) AddWithAttributes(context.Context, int64, attribute.Set) {} // Float64UpDownCounter is an OpenTelemetry UpDownCounter used to record // float64 measurements. It produces no telemetry. type Float64UpDownCounter struct{} // Add performs no operation. -func (Float64UpDownCounter) Add(context.Context, float64, ...attribute.KeyValue) {} +func (Float64UpDownCounter) Add(context.Context, float64) {} + +// AddWithAttributes performs no operation. +func (Float64UpDownCounter) AddWithAttributes(context.Context, float64, attribute.Set) {} // Int64Histogram is an OpenTelemetry Histogram used to record int64 // measurements. It produces no telemetry. type Int64Histogram struct{} // Record performs no operation. -func (Int64Histogram) Record(context.Context, int64, ...attribute.KeyValue) {} +func (Int64Histogram) Record(context.Context, int64) {} + +// RecordWithAttributes performs no operation. +func (Int64Histogram) RecordWithAttributes(context.Context, int64, attribute.Set) {} // Float64Histogram is an OpenTelemetry Histogram used to record float64 // measurements. It produces no telemetry. type Float64Histogram struct{} // Record performs no operation. -func (Float64Histogram) Record(context.Context, float64, ...attribute.KeyValue) {} +func (Float64Histogram) Record(context.Context, float64) {} + +// RecordWithAttributes performs no operation. +func (Float64Histogram) RecordWithAttributes(context.Context, float64, attribute.Set) {} // Int64ObservableCounter is an OpenTelemetry ObservableCounter used to record // int64 measurements. It produces no telemetry. @@ -237,11 +263,17 @@ type Float64ObservableUpDownCounter struct{ instrument.Float64Observable } type Int64Observer struct{} // Observe performs no operation. -func (Int64Observer) Observe(int64, ...attribute.KeyValue) {} +func (Int64Observer) Observe(int64) {} + +// ObserveWithAttributes performs no operation. +func (Int64Observer) ObserveWithAttributes(int64, attribute.Set) {} // Float64Observer is a recorder of float64 measurements that performs no // operation. type Float64Observer struct{} // Observe performs no operation. -func (Float64Observer) Observe(float64, ...attribute.KeyValue) {} +func (Float64Observer) Observe(float64) {} + +// Observe performs no operation. +func (Float64Observer) ObserveWithAttributes(float64, attribute.Set) {} diff --git a/sdk/metric/benchmark_test.go b/sdk/metric/benchmark_test.go index 9a30cd1749d..efdf0c24aa7 100644 --- a/sdk/metric/benchmark_test.go +++ b/sdk/metric/benchmark_test.go @@ -43,18 +43,23 @@ func BenchmarkCounterAddNoAttrs(b *testing.B) { } func BenchmarkCounterAddOneAttr(b *testing.B) { + s := attribute.NewSet(attribute.String("K", "V")) ctx, _, cntr := benchCounter(b) for i := 0; i < b.N; i++ { - cntr.Add(ctx, 1, attribute.String("K", "V")) + cntr.AddWithAttributes(ctx, 1, s) } } func BenchmarkCounterAddOneInvalidAttr(b *testing.B) { + s := attribute.NewSet( + attribute.String("", "V"), + attribute.String("K", "V"), + ) ctx, _, cntr := benchCounter(b) for i := 0; i < b.N; i++ { - cntr.Add(ctx, 1, attribute.String("", "V"), attribute.String("K", "V")) + cntr.AddWithAttributes(ctx, 1, s) } } @@ -62,7 +67,8 @@ func BenchmarkCounterAddSingleUseAttrs(b *testing.B) { ctx, _, cntr := benchCounter(b) for i := 0; i < b.N; i++ { - cntr.Add(ctx, 1, attribute.Int("K", i)) + s := attribute.NewSet(attribute.Int("K", i)) + cntr.AddWithAttributes(ctx, 1, s) } } @@ -70,7 +76,11 @@ func BenchmarkCounterAddSingleUseInvalidAttrs(b *testing.B) { ctx, _, cntr := benchCounter(b) for i := 0; i < b.N; i++ { - cntr.Add(ctx, 1, attribute.Int("", i), attribute.Int("K", i)) + s := attribute.NewSet( + attribute.Int("", i), + attribute.Int("K", i), + ) + cntr.AddWithAttributes(ctx, 1, s) } } @@ -83,15 +93,20 @@ func BenchmarkCounterAddSingleUseFilteredAttrs(b *testing.B) { )) for i := 0; i < b.N; i++ { - cntr.Add(ctx, 1, attribute.Int("L", i), attribute.Int("K", i)) + s := attribute.NewSet( + attribute.Int("L", i), + attribute.Int("K", i), + ) + cntr.AddWithAttributes(ctx, 1, s) } } func BenchmarkCounterCollectOneAttr(b *testing.B) { + s := attribute.NewSet(attribute.Int("K", 1)) ctx, rdr, cntr := benchCounter(b) for i := 0; i < b.N; i++ { - cntr.Add(ctx, 1, attribute.Int("K", 1)) + cntr.AddWithAttributes(ctx, 1, s) _ = rdr.Collect(ctx, nil) } @@ -102,7 +117,8 @@ func BenchmarkCounterCollectTenAttrs(b *testing.B) { for i := 0; i < b.N; i++ { for j := 0; j < 10; j++ { - cntr.Add(ctx, 1, attribute.Int("K", j)) + s := attribute.NewSet(attribute.Int("K", j)) + cntr.AddWithAttributes(ctx, 1, s) } _ = rdr.Collect(ctx, nil) } diff --git a/sdk/metric/instrument.go b/sdk/metric/instrument.go index 3ae2f2d7b6c..edec9b2489f 100644 --- a/sdk/metric/instrument.go +++ b/sdk/metric/instrument.go @@ -180,24 +180,31 @@ var _ instrument.Int64Counter = (*instrumentImpl[int64])(nil) var _ instrument.Int64UpDownCounter = (*instrumentImpl[int64])(nil) var _ instrument.Int64Histogram = (*instrumentImpl[int64])(nil) -func (i *instrumentImpl[N]) Add(ctx context.Context, val N, attrs ...attribute.KeyValue) { +func (i *instrumentImpl[N]) Add(ctx context.Context, val N) { + i.aggregate(ctx, val, *attribute.EmptySet()) +} + +func (i *instrumentImpl[N]) AddWithAttributes(ctx context.Context, val N, attrs attribute.Set) { i.aggregate(ctx, val, attrs) } -func (i *instrumentImpl[N]) Record(ctx context.Context, val N, attrs ...attribute.KeyValue) { +func (i *instrumentImpl[N]) Record(ctx context.Context, val N) { + i.aggregate(ctx, val, *attribute.EmptySet()) +} + +func (i *instrumentImpl[N]) RecordWithAttributes(ctx context.Context, val N, attrs attribute.Set) { i.aggregate(ctx, val, attrs) } -func (i *instrumentImpl[N]) aggregate(ctx context.Context, val N, attrs []attribute.KeyValue) { +func (i *instrumentImpl[N]) aggregate(ctx context.Context, val N, attrs attribute.Set) { if err := ctx.Err(); err != nil { return } // Do not use single attribute.Sortable and attribute.NewSetWithSortable, // this method needs to be concurrent safe. Let the sync.Pool in the // attribute package handle allocations of the Sortable. - s := attribute.NewSet(attrs...) for _, agg := range i.aggregators { - agg.Aggregate(val, s) + agg.Aggregate(val, attrs) } } @@ -261,13 +268,12 @@ func newObservable[N int64 | float64](scope instrumentation.Scope, kind Instrume } // observe records the val for the set of attrs. -func (o *observable[N]) observe(val N, attrs []attribute.KeyValue) { +func (o *observable[N]) observe(val N, attrs attribute.Set) { // Do not use single attribute.Sortable and attribute.NewSetWithSortable, // this method needs to be concurrent safe. Let the sync.Pool in the // attribute package handle allocations of the Sortable. - s := attribute.NewSet(attrs...) for _, agg := range o.aggregators { - agg.Aggregate(val, s) + agg.Aggregate(val, attrs) } } diff --git a/sdk/metric/instrument_test.go b/sdk/metric/instrument_test.go index 4315effbfd0..c02eb081ebd 100644 --- a/sdk/metric/instrument_test.go +++ b/sdk/metric/instrument_test.go @@ -23,12 +23,12 @@ import ( ) func BenchmarkInstrument(b *testing.B) { - attr := func(id int) []attribute.KeyValue { - return []attribute.KeyValue{ + attr := func(id int) attribute.Set { + return attribute.NewSet( attribute.String("user", "Alice"), attribute.Bool("admin", true), attribute.Int("id", id), - } + ) } b.Run("instrumentImpl/aggregate", func(b *testing.B) { diff --git a/sdk/metric/internal/aggregator_example_test.go b/sdk/metric/internal/aggregator_example_test.go index fdaf6833677..f254716ebf6 100644 --- a/sdk/metric/internal/aggregator_example_test.go +++ b/sdk/metric/internal/aggregator_example_test.go @@ -89,8 +89,10 @@ type inst struct { aggregateFunc func(int64, attribute.Set) } -func (inst) Add(context.Context, int64, ...attribute.KeyValue) {} -func (inst) Record(context.Context, int64, ...attribute.KeyValue) {} +func (inst) Add(context.Context, int64) {} +func (inst) AddWithAttributes(context.Context, int64, attribute.Set) {} +func (inst) Record(context.Context, int64) {} +func (inst) RecordWithAttributes(context.Context, int64, attribute.Set) {} func Example() { m := meter{} diff --git a/sdk/metric/meter.go b/sdk/metric/meter.go index 6af74fadeaa..b12d41c5da2 100644 --- a/sdk/metric/meter.go +++ b/sdk/metric/meter.go @@ -292,7 +292,11 @@ var ( errUnregObserver = errors.New("observable instrument not registered for callback") ) -func (r observer) ObserveFloat64(o instrument.Float64Observable, v float64, a ...attribute.KeyValue) { +func (r observer) ObserveFloat64(o instrument.Float64Observable, v float64) { + r.ObserveFloat64WithAttributes(o, v, *attribute.EmptySet()) +} + +func (r observer) ObserveFloat64WithAttributes(o instrument.Float64Observable, v float64, a attribute.Set) { var oImpl float64Observable switch conv := o.(type) { case float64Observable: @@ -324,7 +328,11 @@ func (r observer) ObserveFloat64(o instrument.Float64Observable, v float64, a .. oImpl.observe(v, a) } -func (r observer) ObserveInt64(o instrument.Int64Observable, v int64, a ...attribute.KeyValue) { +func (r observer) ObserveInt64(o instrument.Int64Observable, v int64) { + r.ObserveInt64WithAttributes(o, v, *attribute.EmptySet()) +} + +func (r observer) ObserveInt64WithAttributes(o instrument.Int64Observable, v int64, a attribute.Set) { var oImpl int64Observable switch conv := o.(type) { case int64Observable: @@ -417,7 +425,11 @@ type int64Observer struct { int64Observable } -func (o int64Observer) Observe(val int64, attrs ...attribute.KeyValue) { +func (o int64Observer) Observe(val int64) { + o.observe(val, *attribute.EmptySet()) +} + +func (o int64Observer) ObserveWithAttributes(val int64, attrs attribute.Set) { o.observe(val, attrs) } @@ -448,6 +460,10 @@ type float64Observer struct { float64Observable } -func (o float64Observer) Observe(val float64, attrs ...attribute.KeyValue) { +func (o float64Observer) Observe(val float64) { + o.observe(val, *attribute.EmptySet()) +} + +func (o float64Observer) ObserveWithAttributes(val float64, attrs attribute.Set) { o.observe(val, attrs) } diff --git a/sdk/metric/meter_test.go b/sdk/metric/meter_test.go index ef8c579d534..7deb969e52f 100644 --- a/sdk/metric/meter_test.go +++ b/sdk/metric/meter_test.go @@ -168,7 +168,7 @@ func TestCallbackUnregisterConcurrency(t *testing.T) { // Instruments should produce correct ResourceMetrics. func TestMeterCreatesInstruments(t *testing.T) { - attrs := []attribute.KeyValue{attribute.String("name", "alice")} + attrs := attribute.NewSet(attribute.String("name", "alice")) testCases := []struct { name string fn func(*testing.T, metric.Meter) @@ -178,7 +178,7 @@ func TestMeterCreatesInstruments(t *testing.T) { name: "ObservableInt64Count", fn: func(t *testing.T, m metric.Meter) { cback := func(_ context.Context, o instrument.Int64Observer) error { - o.Observe(4, attrs...) + o.ObserveWithAttributes(4, attrs) return nil } ctr, err := m.Int64ObservableCounter("aint", instrument.WithInt64Callback(cback)) @@ -195,7 +195,7 @@ func TestMeterCreatesInstruments(t *testing.T) { Temporality: metricdata.CumulativeTemporality, IsMonotonic: true, DataPoints: []metricdata.DataPoint[int64]{ - {Attributes: attribute.NewSet(attrs...), Value: 4}, + {Attributes: attrs, Value: 4}, {Value: 3}, }, }, @@ -205,7 +205,7 @@ func TestMeterCreatesInstruments(t *testing.T) { name: "ObservableInt64UpDownCount", fn: func(t *testing.T, m metric.Meter) { cback := func(_ context.Context, o instrument.Int64Observer) error { - o.Observe(4, attrs...) + o.ObserveWithAttributes(4, attrs) return nil } ctr, err := m.Int64ObservableUpDownCounter("aint", instrument.WithInt64Callback(cback)) @@ -222,7 +222,7 @@ func TestMeterCreatesInstruments(t *testing.T) { Temporality: metricdata.CumulativeTemporality, IsMonotonic: false, DataPoints: []metricdata.DataPoint[int64]{ - {Attributes: attribute.NewSet(attrs...), Value: 4}, + {Attributes: attrs, Value: 4}, {Value: 11}, }, }, @@ -232,7 +232,7 @@ func TestMeterCreatesInstruments(t *testing.T) { name: "ObservableInt64Gauge", fn: func(t *testing.T, m metric.Meter) { cback := func(_ context.Context, o instrument.Int64Observer) error { - o.Observe(4, attrs...) + o.ObserveWithAttributes(4, attrs) return nil } gauge, err := m.Int64ObservableGauge("agauge", instrument.WithInt64Callback(cback)) @@ -247,7 +247,7 @@ func TestMeterCreatesInstruments(t *testing.T) { Name: "agauge", Data: metricdata.Gauge[int64]{ DataPoints: []metricdata.DataPoint[int64]{ - {Attributes: attribute.NewSet(attrs...), Value: 4}, + {Attributes: attrs, Value: 4}, {Value: 11}, }, }, @@ -257,7 +257,7 @@ func TestMeterCreatesInstruments(t *testing.T) { name: "ObservableFloat64Count", fn: func(t *testing.T, m metric.Meter) { cback := func(_ context.Context, o instrument.Float64Observer) error { - o.Observe(4, attrs...) + o.ObserveWithAttributes(4, attrs) return nil } ctr, err := m.Float64ObservableCounter("afloat", instrument.WithFloat64Callback(cback)) @@ -274,7 +274,7 @@ func TestMeterCreatesInstruments(t *testing.T) { Temporality: metricdata.CumulativeTemporality, IsMonotonic: true, DataPoints: []metricdata.DataPoint[float64]{ - {Attributes: attribute.NewSet(attrs...), Value: 4}, + {Attributes: attrs, Value: 4}, {Value: 3}, }, }, @@ -284,7 +284,7 @@ func TestMeterCreatesInstruments(t *testing.T) { name: "ObservableFloat64UpDownCount", fn: func(t *testing.T, m metric.Meter) { cback := func(_ context.Context, o instrument.Float64Observer) error { - o.Observe(4, attrs...) + o.ObserveWithAttributes(4, attrs) return nil } ctr, err := m.Float64ObservableUpDownCounter("afloat", instrument.WithFloat64Callback(cback)) @@ -301,7 +301,7 @@ func TestMeterCreatesInstruments(t *testing.T) { Temporality: metricdata.CumulativeTemporality, IsMonotonic: false, DataPoints: []metricdata.DataPoint[float64]{ - {Attributes: attribute.NewSet(attrs...), Value: 4}, + {Attributes: attrs, Value: 4}, {Value: 11}, }, }, @@ -311,7 +311,7 @@ func TestMeterCreatesInstruments(t *testing.T) { name: "ObservableFloat64Gauge", fn: func(t *testing.T, m metric.Meter) { cback := func(_ context.Context, o instrument.Float64Observer) error { - o.Observe(4, attrs...) + o.ObserveWithAttributes(4, attrs) return nil } gauge, err := m.Float64ObservableGauge("agauge", instrument.WithFloat64Callback(cback)) @@ -326,7 +326,7 @@ func TestMeterCreatesInstruments(t *testing.T) { Name: "agauge", Data: metricdata.Gauge[float64]{ DataPoints: []metricdata.DataPoint[float64]{ - {Attributes: attribute.NewSet(attrs...), Value: 4}, + {Attributes: attrs, Value: 4}, {Value: 11}, }, }, @@ -897,9 +897,12 @@ func testAttributeFilter(temporality metricdata.Temporality) func(*testing.T) { return err } _, err = mtr.RegisterCallback(func(_ context.Context, o metric.Observer) error { - o.ObserveFloat64(ctr, 1.0, attribute.String("foo", "bar"), attribute.Int("version", 1)) - o.ObserveFloat64(ctr, 2.0, attribute.String("foo", "bar")) - o.ObserveFloat64(ctr, 1.0, attribute.String("foo", "bar"), attribute.Int("version", 2)) + s := attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 1)) + o.ObserveFloat64WithAttributes(ctr, 1.0, s) + s = attribute.NewSet(attribute.String("foo", "bar")) + o.ObserveFloat64WithAttributes(ctr, 2.0, s) + s = attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 2)) + o.ObserveFloat64WithAttributes(ctr, 1.0, s) return nil }, ctr) return err @@ -926,9 +929,12 @@ func testAttributeFilter(temporality metricdata.Temporality) func(*testing.T) { return err } _, err = mtr.RegisterCallback(func(_ context.Context, o metric.Observer) error { - o.ObserveFloat64(ctr, 1.0, attribute.String("foo", "bar"), attribute.Int("version", 1)) - o.ObserveFloat64(ctr, 2.0, attribute.String("foo", "bar")) - o.ObserveFloat64(ctr, 1.0, attribute.String("foo", "bar"), attribute.Int("version", 2)) + s := attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 1)) + o.ObserveFloat64WithAttributes(ctr, 1.0, s) + s = attribute.NewSet(attribute.String("foo", "bar")) + o.ObserveFloat64WithAttributes(ctr, 2.0, s) + s = attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 2)) + o.ObserveFloat64WithAttributes(ctr, 1.0, s) return nil }, ctr) return err @@ -955,8 +961,10 @@ func testAttributeFilter(temporality metricdata.Temporality) func(*testing.T) { return err } _, err = mtr.RegisterCallback(func(_ context.Context, o metric.Observer) error { - o.ObserveFloat64(ctr, 1.0, attribute.String("foo", "bar"), attribute.Int("version", 1)) - o.ObserveFloat64(ctr, 2.0, attribute.String("foo", "bar"), attribute.Int("version", 2)) + s := attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 1)) + o.ObserveFloat64WithAttributes(ctr, 1.0, s) + s = attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 2)) + o.ObserveFloat64WithAttributes(ctr, 2.0, s) return nil }, ctr) return err @@ -981,9 +989,12 @@ func testAttributeFilter(temporality metricdata.Temporality) func(*testing.T) { return err } _, err = mtr.RegisterCallback(func(_ context.Context, o metric.Observer) error { - o.ObserveInt64(ctr, 10, attribute.String("foo", "bar"), attribute.Int("version", 1)) - o.ObserveInt64(ctr, 20, attribute.String("foo", "bar")) - o.ObserveInt64(ctr, 10, attribute.String("foo", "bar"), attribute.Int("version", 2)) + s := attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 1)) + o.ObserveInt64WithAttributes(ctr, 10, s) + s = attribute.NewSet(attribute.String("foo", "bar")) + o.ObserveInt64WithAttributes(ctr, 20, s) + s = attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 2)) + o.ObserveInt64WithAttributes(ctr, 10, s) return nil }, ctr) return err @@ -1010,9 +1021,12 @@ func testAttributeFilter(temporality metricdata.Temporality) func(*testing.T) { return err } _, err = mtr.RegisterCallback(func(_ context.Context, o metric.Observer) error { - o.ObserveInt64(ctr, 10, attribute.String("foo", "bar"), attribute.Int("version", 1)) - o.ObserveInt64(ctr, 20, attribute.String("foo", "bar")) - o.ObserveInt64(ctr, 10, attribute.String("foo", "bar"), attribute.Int("version", 2)) + s := attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 1)) + o.ObserveInt64WithAttributes(ctr, 10, s) + s = attribute.NewSet(attribute.String("foo", "bar")) + o.ObserveInt64WithAttributes(ctr, 20, s) + s = attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 2)) + o.ObserveInt64WithAttributes(ctr, 10, s) return nil }, ctr) return err @@ -1039,8 +1053,10 @@ func testAttributeFilter(temporality metricdata.Temporality) func(*testing.T) { return err } _, err = mtr.RegisterCallback(func(_ context.Context, o metric.Observer) error { - o.ObserveInt64(ctr, 10, attribute.String("foo", "bar"), attribute.Int("version", 1)) - o.ObserveInt64(ctr, 20, attribute.String("foo", "bar"), attribute.Int("version", 2)) + s := attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 1)) + o.ObserveInt64WithAttributes(ctr, 10, s) + s = attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 2)) + o.ObserveInt64WithAttributes(ctr, 20, s) return nil }, ctr) return err @@ -1065,8 +1081,10 @@ func testAttributeFilter(temporality metricdata.Temporality) func(*testing.T) { return err } - ctr.Add(context.Background(), 1.0, attribute.String("foo", "bar"), attribute.Int("version", 1)) - ctr.Add(context.Background(), 2.0, attribute.String("foo", "bar"), attribute.Int("version", 2)) + s := attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 1)) + ctr.AddWithAttributes(context.Background(), 1.0, s) + s = attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 2)) + ctr.AddWithAttributes(context.Background(), 2.0, s) return nil }, wantMetric: metricdata.Metrics{ @@ -1091,8 +1109,10 @@ func testAttributeFilter(temporality metricdata.Temporality) func(*testing.T) { return err } - ctr.Add(context.Background(), 1.0, attribute.String("foo", "bar"), attribute.Int("version", 1)) - ctr.Add(context.Background(), 2.0, attribute.String("foo", "bar"), attribute.Int("version", 2)) + s := attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 1)) + ctr.AddWithAttributes(context.Background(), 1.0, s) + s = attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 2)) + ctr.AddWithAttributes(context.Background(), 2.0, s) return nil }, wantMetric: metricdata.Metrics{ @@ -1117,8 +1137,10 @@ func testAttributeFilter(temporality metricdata.Temporality) func(*testing.T) { return err } - ctr.Record(context.Background(), 1.0, attribute.String("foo", "bar"), attribute.Int("version", 1)) - ctr.Record(context.Background(), 2.0, attribute.String("foo", "bar"), attribute.Int("version", 2)) + s := attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 1)) + ctr.RecordWithAttributes(context.Background(), 1.0, s) + s = attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 2)) + ctr.RecordWithAttributes(context.Background(), 2.0, s) return nil }, wantMetric: metricdata.Metrics{ @@ -1147,8 +1169,10 @@ func testAttributeFilter(temporality metricdata.Temporality) func(*testing.T) { return err } - ctr.Add(context.Background(), 10, attribute.String("foo", "bar"), attribute.Int("version", 1)) - ctr.Add(context.Background(), 20, attribute.String("foo", "bar"), attribute.Int("version", 2)) + s := attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 1)) + ctr.AddWithAttributes(context.Background(), 10, s) + s = attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 2)) + ctr.AddWithAttributes(context.Background(), 20, s) return nil }, wantMetric: metricdata.Metrics{ @@ -1173,8 +1197,10 @@ func testAttributeFilter(temporality metricdata.Temporality) func(*testing.T) { return err } - ctr.Add(context.Background(), 10, attribute.String("foo", "bar"), attribute.Int("version", 1)) - ctr.Add(context.Background(), 20, attribute.String("foo", "bar"), attribute.Int("version", 2)) + s := attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 1)) + ctr.AddWithAttributes(context.Background(), 10, s) + s = attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 2)) + ctr.AddWithAttributes(context.Background(), 20, s) return nil }, wantMetric: metricdata.Metrics{ @@ -1199,8 +1225,10 @@ func testAttributeFilter(temporality metricdata.Temporality) func(*testing.T) { return err } - ctr.Record(context.Background(), 1, attribute.String("foo", "bar"), attribute.Int("version", 1)) - ctr.Record(context.Background(), 2, attribute.String("foo", "bar"), attribute.Int("version", 2)) + s := attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 1)) + ctr.RecordWithAttributes(context.Background(), 1, s) + s = attribute.NewSet(attribute.String("foo", "bar"), attribute.Int("version", 2)) + ctr.RecordWithAttributes(context.Background(), 2, s) return nil }, wantMetric: metricdata.Metrics{ @@ -1295,7 +1323,7 @@ func TestObservableExample(t *testing.T) { _, err := meter.Int64ObservableCounter(instName, instrument.WithInt64Callback( func(_ context.Context, o instrument.Int64Observer) error { for attrSet, val := range observations { - o.Observe(val, attrSet.ToSlice()...) + o.ObserveWithAttributes(val, attrSet) } return nil },