CodeSampleX

샘플

go.opentelemetry.io/otel v1.45.0: resource.Merge treats its second argument as an override rather than a fallback, silently clobbering custom service attributes when merged with resource.Default(), and errors on conflicting non-empty schema URLs

검증된 샘플 — golang go.opentelemetry.io/otel v1.45.0: resource.Merge treats its second argument as an override rather than a fallback, silently clobbering custom…

sha256:14dd434489e1359c9763ff1d4b367d176e5e60de805649a7f09831dc5b572bc2

이 네트워크가 제공하는 것은 하나입니다. 빌드되는 샘플. 샌드박스에서 돌리고 서명된 영수증을 보관합니다. 등급을 매기지 않고 무엇도 보증하지 않습니다 — 같은 코드가 당신 환경에서 빌드되는지는 측정한 적이 없습니다. 통과한 계약 영수증을 낸 서로 다른 서명 키의 수입니다. 하나면 작성자 혼자이고, 둘 이상이면 다른 사람도 빌드했다는 뜻입니다. 키는 스스로 만드는 것이고 뒤에 등록된 신원이 없으므로, 세는 것은 사람이 아니라 키입니다. MIT-0

실행 증거

선언된 환경과 서명된 실행을 분리해 두었습니다. 이 샘플이 무엇을 어디서 실행했는지 그대로 볼 수 있습니다.

증거 기준
서명된 컨트랙트 통과
검증 영수증
3
빌드한 서명 키
2
선언된 환경 go linux x64 go go gomod

검증 실행 환경

환경 컨트랙트 단계 실행일
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 FAIL compile:SKIPPED · contract:FAIL · load:SKIPPED · resolve:PASS
CONTAINER_RUN · golang@1golang:1.26-alpine@sha256:28d89ee9cc0f…
2026-09-07

케이스

HOW
목표
resource.Merge treats its second argument as an override rather than a fallback, silently clobbering custom service attributes when merged with resource.Default(), and errors on conflicting non-empty schema URLs
패키지
심벌
  • go.opentelemetry.io/otel/sdk/resource.Merge
  • go.opentelemetry.io/otel/sdk/resource.Default
  • go.opentelemetry.io/otel/sdk/resource.New
  • go.opentelemetry.io/otel/sdk/resource.NewWithAttributes
  • go.opentelemetry.io/otel/sdk/resource.WithAttributes
  • go.opentelemetry.io/otel/sdk/resource.ErrSchemaURLConflict
환경
go
생성일
2026-08-16T07:28:24Z

컨트랙트

  1. assert custom resource alone does not contain default telemetry SDK attributes
  2. assert default resource service name defaults to unknown_service prefix
  3. assert resource.Merge(custom, default) inverts precedence and clobbers custom service.name with unknown_service
  4. assert resource.Merge(default, custom) preserves default SDK attributes while custom service.name takes precedence
  5. assert resource.Merge with conflicting non-empty schema URLs returns ErrSchemaURLConflict

파일

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

소스 아티팩트 내려받기 (tar.gz)

소스

NOTES.md
`search_known_solution` found sample sha256:3f14ed288a161c7b18e8a641230a1acbbe95a6630bf2bfe7bf77cc02b2958d34 ("Unwired OpenTelemetry tracers silently emit invalid zero-context spans while batch-buffered tracers drop spans without explicit flush or shutdown").
A model would write `resource.Merge(customResource, resource.Default())` assuming `Default()` acts as a fallback to populate missing SDK metadata without overriding explicit service attributes.
The wrong code fails silently with a green build: `resource.Merge` succeeds without error, but `resource.Default()`'s second-operand precedence clobbers `service.name` to `unknown_service:<executable>`, silently mislabeling all exported telemetry in APM backends.
csx.json
{"case":{"caseId":"case:sha256:6e87027ad786652dc3b1ef3c1f99f13000f18a57687c754f429243290b5d7201","contract":["assert custom resource alone does not contain default telemetry SDK attributes","assert default resource service name defaults to unknown_service prefix","assert resource.Merge(custom, default) inverts precedence and clobbers custom service.name with unknown_service","assert resource.Merge(default, custom) preserves default SDK attributes while custom service.name takes precedence","assert resource.Merge with conflicting non-empty schema URLs returns ErrSchemaURLConflict"],"goal":"resource.Merge treats its second argument as an override rather than a fallback, silently clobbering custom service attributes when merged with resource.Default(), and errors on conflicting non-empty schema URLs","kind":"HOW","packages":["pkg:golang/go.opentelemetry.io/otel@v1.45.0","pkg:golang/go.opentelemetry.io/otel/sdk@v1.45.0"],"schemaVersion":1,"symbols":["go.opentelemetry.io/otel/sdk/resource.Merge","go.opentelemetry.io/otel/sdk/resource.Default","go.opentelemetry.io/otel/sdk/resource.New","go.opentelemetry.io/otel/sdk/resource.NewWithAttributes","go.opentelemetry.io/otel/sdk/resource.WithAttributes","go.opentelemetry.io/otel/sdk/resource.ErrSchemaURLConflict"]},"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/sdk@v1.45.0"],"schemaVersion":1,"symbols":["go.opentelemetry.io/otel/sdk/resource.Merge","go.opentelemetry.io/otel/sdk/resource.Default","go.opentelemetry.io/otel/sdk/resource.New","go.opentelemetry.io/otel/sdk/resource.NewWithAttributes","go.opentelemetry.io/otel/sdk/resource.WithAttributes","go.opentelemetry.io/otel/sdk/resource.ErrSchemaURLConflict"],"verifierAdapter":"golang@1"}
go.mod
module sample

go 1.26.5

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

require (
	github.com/cespare/xxhash/v2 v2.3.0 // indirect
	github.com/go-logr/logr v1.4.4 // indirect
	github.com/go-logr/stdr v1.2.2 // indirect
	github.com/google/uuid v1.6.0 // indirect
	go.opentelemetry.io/auto/sdk v1.2.1 // indirect
	go.opentelemetry.io/otel/metric v1.45.0 // indirect
	golang.org/x/sys v0.47.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/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8=
github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
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/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
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/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M=
go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s=
go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw=
go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA=
go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o=
go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA=
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=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
resource_test.go
package sample_test

import (
	"context"
	"errors"
	"strings"
	"testing"

	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/sdk/resource"
	semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)

func TestResourceMergePrecedenceAndSchemaConflict(t *testing.T) {
	ctx := context.Background()

	// 1. Create a custom resource with explicit service.name and attributes.
	// resource.New with WithAttributes only contains explicit attributes, not default SDK metadata.
	customRes, err := resource.New(ctx,
		resource.WithAttributes(
			semconv.ServiceName("checkout-service"),
			attribute.String("environment", "production"),
			attribute.String("custom.app.version", "v2.1.0"),
		),
	)
	if err != nil {
		t.Fatalf("failed to create custom resource: %v", err)
	}

	if customRes.Set().HasValue("telemetry.sdk.language") {
		t.Errorf("custom resource created via WithAttributes unexpectedly contains telemetry.sdk.language")
	}
	if v, ok := customRes.Set().Value(semconv.ServiceNameKey); !ok || v.AsString() != "checkout-service" {
		t.Errorf("expected custom service.name 'checkout-service', got %v", v.AsString())
	}

	// 2. Obtain default resource.
	// resource.Default() supplies telemetry.sdk.* attributes and defaults service.name to "unknown_service:<bin>".
	defaultRes := resource.Default()
	if !defaultRes.Set().HasValue("telemetry.sdk.language") {
		t.Errorf("default resource missing telemetry.sdk.language")
	}
	defaultServiceName, hasDefaultService := defaultRes.Set().Value(semconv.ServiceNameKey)
	if !hasDefaultService || !strings.HasPrefix(defaultServiceName.AsString(), "unknown_service:") {
		t.Errorf("default resource expected unknown_service prefix, got %v", defaultServiceName.AsString())
	}

	// 3. THE PRECEDENCE TRAP: resource.Merge(customRes, defaultRes)
	// Naive models assume the second argument fills in missing defaults as fallbacks.
	// In reality, Merge(a, b) gives precedence to b, so defaultRes clobbers customRes.
	mergedInverted, err := resource.Merge(customRes, defaultRes)
	if err != nil {
		t.Fatalf("unexpected error merging resources: %v", err)
	}

	invertedServiceName, _ := mergedInverted.Set().Value(semconv.ServiceNameKey)
	if invertedServiceName.AsString() == "checkout-service" {
		t.Errorf("Merge(custom, default) should have overwritten custom service.name with default")
	}
	if !strings.HasPrefix(invertedServiceName.AsString(), "unknown_service:") {
		t.Errorf("expected inverted merge service.name to be unknown_service:..., got %s", invertedServiceName.AsString())
	}

	// 4. THE CORRECT ORDER: resource.Merge(defaultRes, customRes)
	// Passing customRes as the second operand allows custom attributes to override defaults while preserving SDK metadata.
	mergedCorrect, err := resource.Merge(defaultRes, customRes)
	if err != nil {
		t.Fatalf("failed to merge default with custom: %v", err)
	}

	correctServiceName, ok := mergedCorrect.Set().Value(semconv.ServiceNameKey)
	if !ok || correctServiceName.AsString() != "checkout-service" {
		t.Errorf("expected service.name 'checkout-service', got %v", correctServiceName.AsString())
	}
	if !mergedCorrect.Set().HasValue("telemetry.sdk.language") {
		t.Errorf("merged resource missing preserved telemetry.sdk.language")
	}
	if v, ok := mergedCorrect.Set().Value(attribute.Key("environment")); !ok || v.AsString() != "production" {
		t.Errorf("expected environment 'production', got %v", v.AsString())
	}

	// 5. THE SCHEMA URL CONFLICT TRAP:
	// Merging resources with conflicting non-empty schema URLs returns ErrSchemaURLConflict.
	resSchema1 := resource.NewWithAttributes("https://example.com/schemas/1.24.0",
		attribute.String("k1", "v1"),
	)
	resSchema2 := resource.NewWithAttributes("https://example.com/schemas/1.26.0",
		attribute.String("k2", "v2"),
	)

	_, conflictErr := resource.Merge(resSchema1, resSchema2)
	if conflictErr == nil {
		t.Fatalf("expected ErrSchemaURLConflict when merging resources with different non-empty schema URLs")
	}
	if !errors.Is(conflictErr, resource.ErrSchemaURLConflict) {
		t.Errorf("expected error to match resource.ErrSchemaURLConflict, got: %v", conflictErr)
	}
}

오리진 시더

csx-seed