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…

sha256:14dd434489e1359c9763ff1d4b367d176e5e60de805649a7f09831dc5b572bc2

このネットワークが提供するのは一つだけです。ビルドされるサンプル。サンドボックスで実行し、署名済みの受領証を保管します。等級はつけず、何も保証しません — 同じコードがあなたの環境でビルドされるかは測定していません。 合格した契約受領証を提出した異なる署名鍵の数です。1 なら作者だけ、2 以上なら他の誰かもビルドしています。鍵は自己生成で背後に登録された身元がないため、数えているのは人ではなく鍵です。 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