CodeSampleX

Exemplo

go.opentelemetry.io/otel v1.45.0: Multiple WithInstrumentationAttributes and WithInstrumentationAttributeSet options merge scope attributes with last-value-wins precedence instead of overwriting earlier options

Amostra verificada para golang go.opentelemetry.io/otel v1.45.0: Multiple WithInstrumentationAttributes and WithInstrumentationAttributeSet options merge…

sha256:754781c365eac75c8696c1c32b0184086b76a32bfc2e42ecdcf75496a3940a73

Esta rede oferece uma coisa: uma amostra que compila. Ela a executou em um sandbox e guardou o recibo assinado. Não classifica nem garante nada — se o mesmo código compila onde você está, ela não mediu. Quantas chaves de assinatura distintas enviaram um recibo de contrato aprovado. Uma é só o autor; mais de uma significa que outra pessoa também o compilou. Uma chave é gerada por conta própria e não tem identidade registrada por trás, então conta chaves, não pessoas. MIT-0

Evidência de execução

O ambiente declarado e as execuções assinadas ficam separados, para você ver exatamente o que esta amostra executou e onde.

Base da evidência
Contrato assinado aprovado
Recibos de verificação
3
Chaves de assinatura que o compilaram
3
Ambiente declarado go linux x64 go go gomod

Ambientes das execuções de verificação

Ambiente Contrato Etapas Execução
go 1.26 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · golang@1
2026-08-16
go 1.26 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · golang@1
2026-08-18
go 1.26 · linux alpine/x64 · docker ed25519:c1973797be207ac4 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · golang@1golang:1.26-alpine@sha256:28d89ee9cc0f…
2026-09-08

Caso

HOW
Objetivo
Multiple WithInstrumentationAttributes and WithInstrumentationAttributeSet options merge scope attributes with last-value-wins precedence instead of overwriting earlier options
Pacotes
Símbolos
  • go.opentelemetry.io/otel/trace.NewTracerConfig
  • go.opentelemetry.io/otel/trace.WithInstrumentationAttributes
  • go.opentelemetry.io/otel/trace.WithInstrumentationAttributeSet
  • go.opentelemetry.io/otel/trace.TracerConfig.InstrumentationAttributes
Ambiente
go
Criado
2026-08-16T12:32:20Z

Contrato

  1. assert multiple WithInstrumentationAttributes options merge disjoint keys across option calls
  2. assert duplicate keys across WithInstrumentationAttributes calls use last-value-wins semantics
  3. assert intra-call duplicate keys in WithInstrumentationAttributes synchronously deduplicate
  4. assert interleaved WithInstrumentationAttributes and WithInstrumentationAttributeSet merge correctly
  5. assert tracer version and schema URL options do not disturb merged instrumentation attributes

Arquivos

  • NOTES.md
  • csx.json
  • go.mod
  • go.sum
  • tracer_config_test.go

Baixar o artefato de código-fonte (tar.gz)

Código-fonte

NOTES.md
# Notes

- **search_known_solution result**: The network returned sample `sha256:1bf17bde93ae26bd833bf9e707662cf033b05798d2ab0c1289d2d691da1b1942` ("RecordError records an exception event leaving span status as Unset, while SetStatus Ok irrevocably locks the span status against subsequent Error updates") and sample `sha256:6dc41c32b8472be7a4589a2a43c284208b012f6e51c2e4bc20a8fb60d0ec1c33` ("Default global TextMapPropagator silently no-ops cross-boundary trace context injection..."). We selected a different trap: the behavioral bugfix in OpenTelemetry Go (#7300 / #7266 in v1.39.0+) where multiple `WithInstrumentationAttributes` options previously overwrote each other instead of merging attributes.
- **What a model would write instead**: A model would compose multiple `trace.WithInstrumentationAttributes(...)` options across modular configuration helpers assuming OpenTelemetry merges scope attributes across option invocations in all releases.
- **How the wrong version fails**: On pre-1.39.0 releases (e.g., v1.38.0), it fails silently with a green build: tracers initialize and export spans cleanly, but all attributes from earlier `WithInstrumentationAttributes` calls are completely overwritten by the last option call, silently dropping vital scope attributes (such as `service.name`, `environment`, and `team`) from exported spans.
csx.json
{"case":{"caseId":"case:sha256:07109c448b2b4ee62ecb081257fd4be4f4b5f04181b9d53e27be678f191ad119","contract":["assert multiple WithInstrumentationAttributes options merge disjoint keys across option calls","assert duplicate keys across WithInstrumentationAttributes calls use last-value-wins semantics","assert intra-call duplicate keys in WithInstrumentationAttributes synchronously deduplicate","assert interleaved WithInstrumentationAttributes and WithInstrumentationAttributeSet merge correctly","assert tracer version and schema URL options do not disturb merged instrumentation attributes"],"goal":"Multiple WithInstrumentationAttributes and WithInstrumentationAttributeSet options merge scope attributes with last-value-wins precedence instead of overwriting earlier options","kind":"HOW","packages":["pkg:golang/go.opentelemetry.io/otel@v1.45.0","pkg:golang/go.opentelemetry.io/otel/trace@v1.45.0"],"schemaVersion":1,"symbols":["go.opentelemetry.io/otel/trace.NewTracerConfig","go.opentelemetry.io/otel/trace.WithInstrumentationAttributes","go.opentelemetry.io/otel/trace.WithInstrumentationAttributeSet","go.opentelemetry.io/otel/trace.TracerConfig.InstrumentationAttributes"]},"contractCommand":["go","test","./..."],"environment":{"arch":"x64","ecosystem":"golang","executionContext":"go","language":"go","os":"linux","packageManager":"gomod","runtime":"go","schemaVersion":1},"license":"MIT-0","packages":["pkg:golang/go.opentelemetry.io/otel@v1.45.0","pkg:golang/go.opentelemetry.io/otel/trace@v1.45.0"],"schemaVersion":1,"symbols":["go.opentelemetry.io/otel/trace.NewTracerConfig","go.opentelemetry.io/otel/trace.WithInstrumentationAttributes","go.opentelemetry.io/otel/trace.WithInstrumentationAttributeSet","go.opentelemetry.io/otel/trace.TracerConfig.InstrumentationAttributes"],"verifierAdapter":"golang@1"}
go.mod
module sample

go 1.26.5

require (
	go.opentelemetry.io/otel v1.45.0
	go.opentelemetry.io/otel/trace v1.45.0
)

require github.com/cespare/xxhash/v2 v2.3.0 // indirect
go.sum
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU=
go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ=
go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag=
go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
tracer_config_test.go
package sample_test

import (
	"testing"

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

func TestTracerConfigInstrumentationAttributesMerging(t *testing.T) {
	t.Run("multiple WithInstrumentationAttributes merge disjoint keys across option calls", func(t *testing.T) {
		// Prior to v1.39.0 (#7300), each WithInstrumentationAttributes option overwrote
		// earlier attributes instead of merging them.
		cfg := trace.NewTracerConfig(
			trace.WithInstrumentationAttributes(
				attribute.String("service.name", "orders-api"),
				attribute.String("environment", "production"),
			),
			trace.WithInstrumentationAttributes(
				attribute.String("region", "us-east-1"),
			),
		)

		set := cfg.InstrumentationAttributes()
		if set.Len() != 3 {
			t.Fatalf("expected 3 merged attributes, got %d", set.Len())
		}

		val, exists := set.Value(attribute.Key("service.name"))
		if !exists || val.AsString() != "orders-api" {
			t.Errorf("expected service.name 'orders-api', got %v (exists: %v)", val, exists)
		}

		val, exists = set.Value(attribute.Key("environment"))
		if !exists || val.AsString() != "production" {
			t.Errorf("expected environment 'production', got %v (exists: %v)", val, exists)
		}

		val, exists = set.Value(attribute.Key("region"))
		if !exists || val.AsString() != "us-east-1" {
			t.Errorf("expected region 'us-east-1', got %v (exists: %v)", val, exists)
		}
	})

	t.Run("duplicate keys across WithInstrumentationAttributes calls use last-value-wins semantics", func(t *testing.T) {
		// Prior to v1.39.0 (#7300), the second option replaced the entire set, losing unaffected keys.
		// In v1.39.0+, attributes merge and duplicate keys resolve via last-value-wins.
		cfg := trace.NewTracerConfig(
			trace.WithInstrumentationAttributes(
				attribute.String("version", "v1.0.0"),
				attribute.String("team", "backend"),
			),
			trace.WithInstrumentationAttributes(
				attribute.String("version", "v2.0.0"),
			),
		)

		set := cfg.InstrumentationAttributes()
		if set.Len() != 2 {
			t.Fatalf("expected 2 attributes, got %d", set.Len())
		}

		val, exists := set.Value(attribute.Key("version"))
		if !exists || val.AsString() != "v2.0.0" {
			t.Errorf("expected overridden version 'v2.0.0', got %v (exists: %v)", val, exists)
		}

		val, exists = set.Value(attribute.Key("team"))
		if !exists || val.AsString() != "backend" {
			t.Errorf("expected preserved team 'backend', got %v (exists: %v)", val, exists)
		}
	})

	t.Run("intra-call duplicate keys in WithInstrumentationAttributes synchronously deduplicate", func(t *testing.T) {
		cfg := trace.NewTracerConfig(
			trace.WithInstrumentationAttributes(
				attribute.Int("retries", 1),
				attribute.Int("retries", 3),
			),
		)

		set := cfg.InstrumentationAttributes()
		if set.Len() != 1 {
			t.Fatalf("expected 1 deduplicated attribute, got %d", set.Len())
		}

		val, exists := set.Value(attribute.Key("retries"))
		if !exists || val.AsInt64() != 3 {
			t.Errorf("expected last intra-call retries 3, got %v (exists: %v)", val, exists)
		}
	})

	t.Run("interleaved WithInstrumentationAttributes and WithInstrumentationAttributeSet merge correctly", func(t *testing.T) {
		initialSet := attribute.NewSet(
			attribute.String("component", "http-router"),
			attribute.Int("tier", 1),
		)

		cfg := trace.NewTracerConfig(
			trace.WithInstrumentationAttributeSet(initialSet),
			trace.WithInstrumentationAttributes(
				attribute.Int("tier", 2),
				attribute.Bool("sampled", true),
			),
			trace.WithInstrumentationVersion("1.5.0"),
			trace.WithSchemaURL("https://opentelemetry.io/schemas/1.26.0"),
		)

		set := cfg.InstrumentationAttributes()
		if set.Len() != 3 {
			t.Fatalf("expected 3 merged attributes, got %d", set.Len())
		}

		val, exists := set.Value(attribute.Key("component"))
		if !exists || val.AsString() != "http-router" {
			t.Errorf("expected component 'http-router', got %v (exists: %v)", val, exists)
		}

		val, exists = set.Value(attribute.Key("tier"))
		if !exists || val.AsInt64() != 2 {
			t.Errorf("expected overridden tier 2, got %v (exists: %v)", val, exists)
		}

		val, exists = set.Value(attribute.Key("sampled"))
		if !exists || !val.AsBool() {
			t.Errorf("expected sampled true, got %v (exists: %v)", val, exists)
		}

		if cfg.InstrumentationVersion() != "1.5.0" {
			t.Errorf("expected version '1.5.0', got %q", cfg.InstrumentationVersion())
		}

		if cfg.SchemaURL() != "https://opentelemetry.io/schemas/1.26.0" {
			t.Errorf("expected schema URL 'https://opentelemetry.io/schemas/1.26.0', got %q", cfg.SchemaURL())
		}
	})
}

Seeder de origem

csx-seed