Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

- Rename the `OTEL_GO_X_SELF_OBSERVABILITY` environment variable to `OTEL_GO_X_OBSERVABILITY` in `go.opentelemetry.io/otel/sdk/trace`, `go.opentelemetry.io/otel/sdk/log`, and `go.opentelemetry.io/otel/exporters/stdout/stdouttrace`. (#7302)
- Improve performance of histogram `Record` in `go.opentelemetry.io/otel/sdk/metric` when min and max are disabled using `NoMinMax`. (#7306)
- `WithInstrumentationAttributes` in `go.opentelemetry.io/otel/trace` synchronously de-duplicates the passed attributes instead of delegating it to the returned `TracerOption`. (#7266)
- `WithInstrumentationAttributes` in `go.opentelemetry.io/otel/meter` synchronously de-duplicates the passed attributes instead of delegating it to the returned `MeterOption`. (#7266)
- `WithInstrumentationAttributes` in `go.opentelemetry.io/otel/log` synchronously de-duplicates the passed attributes instead of delegating it to the returned `LoggerOption`. (#7266)

<!-- Released section -->
<!-- Don't change this section unless doing release -->
Expand Down
25 changes: 6 additions & 19 deletions log/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package log // import "go.opentelemetry.io/otel/log"

import (
"context"
"slices"

"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/log/embedded"
Expand Down Expand Up @@ -129,30 +130,16 @@ func mergeSets(a, b attribute.Set) attribute.Set {
// WithInstrumentationAttributes returns a [LoggerOption] that sets the
// instrumentation attributes of a [Logger].
//
// The passed attributes will be de-duplicated.
//
// Note that [WithInstrumentationAttributeSet] is recommended as
// it is more efficient and also allows safely reusing the passed argument.
// This is equivalent to calling WithInstrumentationAttributeSet with an
// [attribute.Set] created from a clone of the passed attributes.
// [WithInstrumentationAttributeSet] is recommended for more control.
//
// If multiple [WithInstrumentationAttributes] or [WithInstrumentationAttributeSet]
// options are passed, the attributes will be merged together in the order
// they are passed. Attributes with duplicate keys will use the last value passed.
func WithInstrumentationAttributes(attr ...attribute.KeyValue) LoggerOption {
if len(attr) == 0 {
return loggerOptionFunc(func(config LoggerConfig) LoggerConfig {
return config
})
}

return loggerOptionFunc(func(config LoggerConfig) LoggerConfig {
newAttrs := attribute.NewSet(attr...)
if config.attrs.Len() == 0 {
config.attrs = newAttrs
} else {
config.attrs = mergeSets(config.attrs, newAttrs)
}
return config
})
set := attribute.NewSet(slices.Clone(attr)...)
return WithInstrumentationAttributeSet(set)
}

// WithInstrumentationAttributeSet returns a [LoggerOption] that adds the
Expand Down
17 changes: 17 additions & 0 deletions log/logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ func TestNewLoggerConfig(t *testing.T) {
assert.Equal(t, attr, c.InstrumentationAttributes(), "instrumentation attributes")
}

func TestWithInstrumentationAttributesNotLazy(t *testing.T) {
attrs := []attribute.KeyValue{
attribute.String("service", "test"),
attribute.Int("three", 3),
}
want := attribute.NewSet(attrs...)

// WithInstrumentationAttributes is expected to immediately
// create an immutable set from the attributes, so later changes
// to attrs should not affect the config.
opt := log.WithInstrumentationAttributes(attrs...)
attrs[0] = attribute.String("service", "changed")

c := log.NewLoggerConfig(opt)
assert.Equal(t, want, c.InstrumentationAttributes(), "instrumentation attributes")
}

func TestWithInstrumentationAttributeSet(t *testing.T) {
attrs := attribute.NewSet(
attribute.String("service", "test"),
Expand Down
30 changes: 10 additions & 20 deletions metric/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@

package metric // import "go.opentelemetry.io/otel/metric"

import "go.opentelemetry.io/otel/attribute"
import (
"slices"

"go.opentelemetry.io/otel/attribute"
)

// MeterConfig contains options for Meters.
type MeterConfig struct {
Expand Down Expand Up @@ -64,30 +68,16 @@ func WithInstrumentationVersion(version string) MeterOption {

// WithInstrumentationAttributes adds the instrumentation attributes.
//
// The passed attributes will be de-duplicated.
//
// Note that [WithInstrumentationAttributeSet] is recommended as
// it is more efficient and also allows safely reusing the passed argument.
// This is equivalent to calling [WithInstrumentationAttributeSet] with an
// [attribute.Set] created from a clone of the passed attributes.
// [WithInstrumentationAttributeSet] is recommended for more control.
//
// If multiple [WithInstrumentationAttributes] or [WithInstrumentationAttributeSet]
// options are passed, the attributes will be merged together in the order
// they are passed. Attributes with duplicate keys will use the last value passed.
func WithInstrumentationAttributes(attr ...attribute.KeyValue) MeterOption {
if len(attr) == 0 {
return meterOptionFunc(func(config MeterConfig) MeterConfig {
return config
})
}

return meterOptionFunc(func(config MeterConfig) MeterConfig {
newAttrs := attribute.NewSet(attr...)
if config.attrs.Len() == 0 {
config.attrs = newAttrs
} else {
config.attrs = mergeSets(config.attrs, newAttrs)
}
return config
})
set := attribute.NewSet(slices.Clone(attr)...)
return WithInstrumentationAttributeSet(set)
}

// WithInstrumentationAttributeSet adds the instrumentation attributes.
Expand Down
17 changes: 17 additions & 0 deletions metric/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ func TestConfig(t *testing.T) {
assert.Equal(t, attr, c.InstrumentationAttributes(), "instrumentation attributes")
}

func TestWithInstrumentationAttributesNotLazy(t *testing.T) {
attrs := []attribute.KeyValue{
attribute.String("service", "test"),
attribute.Int("three", 3),
}
want := attribute.NewSet(attrs...)

// WithInstrumentationAttributes is expected to immediately
// create an immutable set from the attributes, so later changes
// to attrs should not affect the config.
opt := metric.WithInstrumentationAttributes(attrs...)
attrs[0] = attribute.String("service", "changed")

c := metric.NewMeterConfig(opt)
assert.Equal(t, want, c.InstrumentationAttributes(), "instrumentation attributes")
}

func TestWithInstrumentationAttributeSet(t *testing.T) {
attrs := attribute.NewSet(
attribute.String("service", "test"),
Expand Down
25 changes: 6 additions & 19 deletions trace/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package trace // import "go.opentelemetry.io/otel/trace"

import (
"slices"
"time"

"go.opentelemetry.io/otel/attribute"
Expand Down Expand Up @@ -318,30 +319,16 @@ func mergeSets(a, b attribute.Set) attribute.Set {

// WithInstrumentationAttributes adds the instrumentation attributes.
//
// The passed attributes will be de-duplicated.
//
// Note that [WithInstrumentationAttributeSet] is recommended as
// it is more efficient and also allows safely reusing the passed argument.
// This is equivalent to calling [WithInstrumentationAttributeSet] with an
// [attribute.Set] created from a clone of the passed attributes.
// [WithInstrumentationAttributeSet] is recommended for more control.
//
// If multiple [WithInstrumentationAttributes] or [WithInstrumentationAttributeSet]
// options are passed, the attributes will be merged together in the order
// they are passed. Attributes with duplicate keys will use the last value passed.
func WithInstrumentationAttributes(attr ...attribute.KeyValue) TracerOption {
if len(attr) == 0 {
return tracerOptionFunc(func(config TracerConfig) TracerConfig {
return config
})
}

return tracerOptionFunc(func(config TracerConfig) TracerConfig {
newAttrs := attribute.NewSet(attr...)
if config.attrs.Len() == 0 {
config.attrs = newAttrs
} else {
config.attrs = mergeSets(config.attrs, newAttrs)
}
return config
})
set := attribute.NewSet(slices.Clone(attr)...)
return WithInstrumentationAttributeSet(set)
}

// WithInstrumentationAttributeSet adds the instrumentation attributes.
Expand Down
17 changes: 17 additions & 0 deletions trace/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,23 @@ func TestTracerConfig(t *testing.T) {
assert.Equal(t, attrs, c.InstrumentationAttributes(), "instrumentation attributes")
}

func TestWithInstrumentationAttributesNotLazy(t *testing.T) {
attrs := []attribute.KeyValue{
attribute.String("service", "test"),
attribute.Int("three", 3),
}
want := attribute.NewSet(attrs...)

// WithInstrumentationAttributes is expected to immediately
// create an immutable set from the attributes, so later changes
// to attrs should not affect the config.
opt := WithInstrumentationAttributes(attrs...)
attrs[0] = attribute.String("service", "changed")

c := NewTracerConfig(opt)
assert.Equal(t, want, c.InstrumentationAttributes(), "instrumentation attributes")
}

func TestWithInstrumentationAttributeSet(t *testing.T) {
attrs := attribute.NewSet(
attribute.String("service", "test"),
Expand Down
Loading