You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2026-06-03 18:35:08 +02:00
48dd8b1a09
Fixes https://github.com/open-telemetry/opentelemetry-go/issues/7851 This adopts this proposal from @MrAlias: https://github.com/open-telemetry/opentelemetry-go/issues/7851#issuecomment-3837399805 This adds a Set function to attrOpt, and changes attrOpt functions to use a pointer receiver. Users can use this by checking if the attribute option implements the `Settable` interface in `metric/x`. There are no public API changes to the metric package, as this is still an experiment. See the benchmark change and the interface documentation for how it can be used to avoid calling metric.WithAttributeSet. I updated the benchmark in https://github.com/open-telemetry/opentelemetry-go/commit/448394b549375f4f6742201e199e0339d0b78524, but didn't commit it here to avoid a dependency between the SDK and the `metric/x` package. As expected, this removes one allocation from the Dynamic case! The results are: ``` │ main.txt │ resettable.txt │ │ sec/op │ sec/op vs base │ EndToEndCounterAdd/NoFilter/Attributes/1/Dynamic/WithAttributeSet-24 238.2n ± 5% 220.7n ± 9% -7.39% (p=0.002 n=6) EndToEndCounterAdd/NoFilter/Attributes/5/Dynamic/WithAttributeSet-24 611.1n ± 4% 620.3n ± 2% ~ (p=0.394 n=6) EndToEndCounterAdd/NoFilter/Attributes/10/Dynamic/WithAttributeSet-24 1.147µ ± 7% 1.171µ ± 5% ~ (p=0.258 n=6) EndToEndCounterAdd/Filtered/Attributes/1/Dynamic/WithAttributeSet-24 363.8n ± 6% 363.5n ± 6% ~ (p=1.000 n=6) EndToEndCounterAdd/Filtered/Attributes/5/Dynamic/WithAttributeSet-24 1.464µ ± 5% 1.475µ ± 10% ~ (p=0.727 n=6) EndToEndCounterAdd/Filtered/Attributes/10/Dynamic/WithAttributeSet-24 2.924µ ± 7% 2.589µ ± 6% -11.47% (p=0.002 n=6) │ main.txt │ resettable.txt │ │ B/op │ B/op vs base │ EndToEndCounterAdd/NoFilter/Attributes/1/Dynamic/WithAttributeSet-24 88.00 ± 0% 64.00 ± 0% -27.27% (p=0.002 n=6) EndToEndCounterAdd/NoFilter/Attributes/5/Dynamic/WithAttributeSet-24 344.0 ± 0% 321.0 ± 0% -6.69% (p=0.002 n=6) EndToEndCounterAdd/NoFilter/Attributes/10/Dynamic/WithAttributeSet-24 729.0 ± 0% 706.0 ± 0% -3.16% (p=0.002 n=6) EndToEndCounterAdd/Filtered/Attributes/1/Dynamic/WithAttributeSet-24 152.0 ± 0% 128.0 ± 0% -15.79% (p=0.002 n=6) EndToEndCounterAdd/Filtered/Attributes/5/Dynamic/WithAttributeSet-24 921.0 ± 0% 899.0 ± 0% -2.39% (p=0.002 n=6) EndToEndCounterAdd/Filtered/Attributes/10/Dynamic/WithAttributeSet-24 2.026Ki ± 0% 2.006Ki ± 0% -1.01% (p=0.002 n=6) │ main.txt │ resettable.txt │ │ allocs/op │ allocs/op vs base │ EndToEndCounterAdd/NoFilter/Attributes/1/Dynamic/WithAttributeSet-24 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.002 n=6) EndToEndCounterAdd/NoFilter/Attributes/5/Dynamic/WithAttributeSet-24 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.002 n=6) EndToEndCounterAdd/NoFilter/Attributes/10/Dynamic/WithAttributeSet-24 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.002 n=6) EndToEndCounterAdd/Filtered/Attributes/1/Dynamic/WithAttributeSet-24 3.000 ± 0% 2.000 ± 0% -33.33% (p=0.002 n=6) EndToEndCounterAdd/Filtered/Attributes/5/Dynamic/WithAttributeSet-24 4.000 ± 0% 3.000 ± 0% -25.00% (p=0.002 n=6) EndToEndCounterAdd/Filtered/Attributes/10/Dynamic/WithAttributeSet-24 4.000 ± 0% 3.000 ± 0% -25.00% (p=0.002 n=6) ``` --------- Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
162 lines
4.1 KiB
Go
162 lines
4.1 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package metric // import "go.opentelemetry.io/otel/metric"
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"go.opentelemetry.io/otel/attribute"
|
|
)
|
|
|
|
type attrConf interface {
|
|
Attributes() attribute.Set
|
|
}
|
|
|
|
func TestConfigAttrs(t *testing.T) {
|
|
t.Run("AddConfig", testConfAttr(func(mo ...MeasurementOption) attrConf {
|
|
opts := make([]AddOption, len(mo))
|
|
for i := range mo {
|
|
opts[i] = mo[i].(AddOption)
|
|
}
|
|
return NewAddConfig(opts)
|
|
}))
|
|
|
|
t.Run("RecordConfig", testConfAttr(func(mo ...MeasurementOption) attrConf {
|
|
opts := make([]RecordOption, len(mo))
|
|
for i := range mo {
|
|
opts[i] = mo[i].(RecordOption)
|
|
}
|
|
return NewRecordConfig(opts)
|
|
}))
|
|
|
|
t.Run("ObserveConfig", testConfAttr(func(mo ...MeasurementOption) attrConf {
|
|
opts := make([]ObserveOption, len(mo))
|
|
for i := range mo {
|
|
opts[i] = mo[i].(ObserveOption)
|
|
}
|
|
return NewObserveConfig(opts)
|
|
}))
|
|
}
|
|
|
|
func testConfAttr(newConf func(...MeasurementOption) attrConf) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
t.Run("ZeroConfigEmpty", func(t *testing.T) {
|
|
c := newConf()
|
|
assert.Equal(t, *attribute.EmptySet(), c.Attributes())
|
|
})
|
|
|
|
t.Run("EmptySet", func(t *testing.T) {
|
|
c := newConf(WithAttributeSet(*attribute.EmptySet()))
|
|
assert.Equal(t, *attribute.EmptySet(), c.Attributes())
|
|
})
|
|
|
|
aliceAttr := attribute.String("user", "Alice")
|
|
alice := attribute.NewSet(aliceAttr)
|
|
t.Run("SingleWithAttributeSet", func(t *testing.T) {
|
|
c := newConf(WithAttributeSet(alice))
|
|
assert.Equal(t, alice, c.Attributes())
|
|
})
|
|
|
|
t.Run("SingleWithAttributes", func(t *testing.T) {
|
|
c := newConf(WithAttributes(aliceAttr))
|
|
assert.Equal(t, alice, c.Attributes())
|
|
})
|
|
|
|
bobAttr := attribute.String("user", "Bob")
|
|
bob := attribute.NewSet(bobAttr)
|
|
t.Run("MultiWithAttributeSet", func(t *testing.T) {
|
|
c := newConf(WithAttributeSet(alice), WithAttributeSet(bob))
|
|
assert.Equal(t, bob, c.Attributes())
|
|
})
|
|
|
|
t.Run("MergedWithAttributes", func(t *testing.T) {
|
|
c := newConf(WithAttributes(aliceAttr, bobAttr))
|
|
assert.Equal(t, bob, c.Attributes())
|
|
})
|
|
|
|
t.Run("MultiWithAttributeSet", func(t *testing.T) {
|
|
c := newConf(WithAttributes(aliceAttr), WithAttributes(bobAttr))
|
|
assert.Equal(t, bob, c.Attributes())
|
|
})
|
|
|
|
t.Run("MergedEmpty", func(t *testing.T) {
|
|
c := newConf(WithAttributeSet(alice), WithAttributeSet(*attribute.EmptySet()))
|
|
assert.Equal(t, alice, c.Attributes())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestWithAttributesConcurrentSafe(*testing.T) {
|
|
attrs := []attribute.KeyValue{
|
|
attribute.String("user", "Alice"),
|
|
attribute.Bool("admin", true),
|
|
attribute.String("user", "Bob"),
|
|
}
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Go(func() {
|
|
opt := []AddOption{WithAttributes(attrs...)}
|
|
_ = NewAddConfig(opt)
|
|
})
|
|
wg.Go(func() {
|
|
opt := []AddOption{WithAttributes(attrs...)}
|
|
_ = NewAddConfig(opt)
|
|
})
|
|
|
|
wg.Go(func() {
|
|
opt := []RecordOption{WithAttributes(attrs...)}
|
|
_ = NewRecordConfig(opt)
|
|
})
|
|
wg.Go(func() {
|
|
opt := []RecordOption{WithAttributes(attrs...)}
|
|
_ = NewRecordConfig(opt)
|
|
})
|
|
|
|
wg.Go(func() {
|
|
opt := []ObserveOption{WithAttributes(attrs...)}
|
|
_ = NewObserveConfig(opt)
|
|
})
|
|
wg.Go(func() {
|
|
opt := []ObserveOption{WithAttributes(attrs...)}
|
|
_ = NewObserveConfig(opt)
|
|
})
|
|
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestSettableOptions(t *testing.T) {
|
|
type settable interface {
|
|
Set(attribute.Set)
|
|
}
|
|
|
|
aliceAttr := attribute.String("user", "Alice")
|
|
alice := attribute.NewSet(aliceAttr)
|
|
bobAttr := attribute.String("user", "Bob")
|
|
bob := attribute.NewSet(bobAttr)
|
|
|
|
t.Run("WithAttributeSet", func(t *testing.T) {
|
|
opt := WithAttributeSet(alice)
|
|
r, ok := opt.(settable)
|
|
require.True(t, ok, "WithAttributeSet option does not implement settable")
|
|
|
|
r.Set(bob)
|
|
c := NewAddConfig([]AddOption{opt.(AddOption)})
|
|
assert.Equal(t, bob, c.Attributes())
|
|
})
|
|
|
|
t.Run("WithAttributes", func(t *testing.T) {
|
|
opt := WithAttributes(aliceAttr)
|
|
r, ok := opt.(settable)
|
|
require.True(t, ok, "WithAttributes option does not implement settable")
|
|
|
|
r.Set(bob)
|
|
c := NewAddConfig([]AddOption{opt.(AddOption)})
|
|
assert.Equal(t, bob, c.Attributes())
|
|
})
|
|
}
|