You've already forked opentelemetry-go
							
							
				mirror of
				https://github.com/open-telemetry/opentelemetry-go.git
				synced 2025-10-31 00:07:40 +02:00 
			
		
		
		
	Change resource.New() to use functional options; add builtin attributes for (host.*, telemetry.sdk.*) (#1235)
* Add a resource.Configure() with functional options * Add a changelog * Add tests for builtin resources * Rename to WithoutBuiltin * Add new test; restore environment after tests * Apply feedback * Apply suggestions from code review ❤️ Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com> * Comment edits * Rename New, former method NewFromAttributes * NewFromAttributes->NewWithAttributes Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
This commit is contained in:
		| @@ -35,6 +35,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm | ||||
| - Move the `go.opentelemetry.io/otel/api/global` package to `go.opentelemetry.io/otel/global`. (#1262) | ||||
| - Rename correlation context header from `"otcorrelations"` to `"baggage"` to match the OpenTelemetry specification. (#1267) | ||||
| - Fix `Code.UnmarshalJSON` to work with valid json only. (#1276) | ||||
| - The `resource.New()` method changes signature to support builtin attributes and functional options, including `telemetry.sdk.*` and | ||||
|   `host.name` semantic conventions; the former method is renamed `resource.NewWithAttributes`. (#1235) | ||||
|  | ||||
| ### Removed | ||||
|  | ||||
|   | ||||
| @@ -41,6 +41,7 @@ import ( | ||||
| // Initializes an OTLP exporter, and configures the corresponding trace and | ||||
| // metric providers. | ||||
| func initProvider() func() { | ||||
| 	ctx := context.Background() | ||||
|  | ||||
| 	// If the OpenTelemetry Collector is running on a local cluster (minikube or | ||||
| 	// microk8s), it should be accessible through the NodePort service at the | ||||
| @@ -54,13 +55,18 @@ func initProvider() func() { | ||||
| 	) | ||||
| 	handleErr(err, "failed to create exporter") | ||||
|  | ||||
| 	res, err := resource.New(ctx, | ||||
| 		resource.WithAttributes( | ||||
| 			// the service name used to display traces in backends | ||||
| 			semconv.ServiceNameKey.String("test-service"), | ||||
| 		), | ||||
| 	) | ||||
| 	handleErr(err, "failed to create resource") | ||||
|  | ||||
| 	bsp := sdktrace.NewBatchSpanProcessor(exp) | ||||
| 	tracerProvider := sdktrace.NewTracerProvider( | ||||
| 		sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}), | ||||
| 		sdktrace.WithResource(resource.New( | ||||
| 			// the service name used to display traces in backends | ||||
| 			semconv.ServiceNameKey.String("test-service"), | ||||
| 		)), | ||||
| 		sdktrace.WithResource(res), | ||||
| 		sdktrace.WithSpanProcessor(bsp), | ||||
| 	) | ||||
|  | ||||
| @@ -80,7 +86,6 @@ func initProvider() func() { | ||||
| 	pusher.Start() | ||||
|  | ||||
| 	return func() { | ||||
| 		ctx := context.Background() | ||||
| 		handleErr(tracerProvider.Shutdown(ctx), "failed to shutdown provider") | ||||
| 		handleErr(exp.Shutdown(ctx), "failed to stop exporter") | ||||
| 		pusher.Stop() // pushes any last exports to the receiver | ||||
|   | ||||
| @@ -37,10 +37,20 @@ import ( | ||||
| // TODO: Address this issue. | ||||
|  | ||||
| func ExampleNewExportPipeline() { | ||||
| 	// Create a resource, with builtin attributes plus R=V. | ||||
| 	res, err := resource.New( | ||||
| 		context.Background(), | ||||
| 		resource.WithoutBuiltin(), // Test-only! | ||||
| 		resource.WithAttributes(label.String("R", "V")), | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| 	// Create a meter | ||||
| 	exporter, err := prometheus.NewExportPipeline( | ||||
| 		prometheus.Config{}, | ||||
| 		pull.WithResource(resource.New(label.String("R", "V"))), | ||||
| 		pull.WithResource(res), | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
|   | ||||
| @@ -39,7 +39,7 @@ func TestPrometheusExporter(t *testing.T) { | ||||
| 			DefaultHistogramBoundaries: []float64{-0.5, 1}, | ||||
| 		}, | ||||
| 		pull.WithCachePeriod(0), | ||||
| 		pull.WithResource(resource.New(label.String("R", "V"))), | ||||
| 		pull.WithResource(resource.NewWithAttributes(label.String("R", "V"))), | ||||
| 	) | ||||
| 	require.NoError(t, err) | ||||
|  | ||||
|   | ||||
| @@ -320,7 +320,7 @@ func TestRecordAggregatorIncompatibleErrors(t *testing.T) { | ||||
| 	makeMpb := func(kind aggregation.Kind, agg aggregation.Aggregation) (*metricpb.Metric, error) { | ||||
| 		desc := otel.NewDescriptor("things", otel.CounterInstrumentKind, otel.Int64NumberKind) | ||||
| 		labels := label.NewSet() | ||||
| 		res := resource.New() | ||||
| 		res := resource.Empty() | ||||
| 		test := &testAgg{ | ||||
| 			kind: kind, | ||||
| 			agg:  agg, | ||||
| @@ -357,7 +357,7 @@ func TestRecordAggregatorUnexpectedErrors(t *testing.T) { | ||||
| 	makeMpb := func(kind aggregation.Kind, agg aggregation.Aggregation) (*metricpb.Metric, error) { | ||||
| 		desc := otel.NewDescriptor("things", otel.CounterInstrumentKind, otel.Int64NumberKind) | ||||
| 		labels := label.NewSet() | ||||
| 		res := resource.New() | ||||
| 		res := resource.Empty() | ||||
| 		return Record(export.NewRecord(&desc, &labels, res, agg, intervalStart, intervalEnd)) | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -40,7 +40,7 @@ func TestEmptyResource(t *testing.T) { | ||||
| func TestResourceAttributes(t *testing.T) { | ||||
| 	attrs := []label.KeyValue{label.Int("one", 1), label.Int("two", 2)} | ||||
|  | ||||
| 	got := Resource(resource.New(attrs...)).GetAttributes() | ||||
| 	got := Resource(resource.NewWithAttributes(attrs...)).GetAttributes() | ||||
| 	if !assert.Len(t, attrs, 2) { | ||||
| 		return | ||||
| 	} | ||||
|   | ||||
| @@ -252,7 +252,7 @@ func TestSpanData(t *testing.T) { | ||||
| 		DroppedAttributeCount:    1, | ||||
| 		DroppedMessageEventCount: 2, | ||||
| 		DroppedLinkCount:         3, | ||||
| 		Resource:                 resource.New(label.String("rk1", "rv1"), label.Int64("rk2", 5)), | ||||
| 		Resource:                 resource.NewWithAttributes(label.String("rk1", "rv1"), label.Int64("rk2", 5)), | ||||
| 		InstrumentationLibrary: instrumentation.Library{ | ||||
| 			Name:    "go.opentelemetry.io/test/otel", | ||||
| 			Version: "v0.0.1", | ||||
|   | ||||
| @@ -94,13 +94,13 @@ func newExporterEndToEndTest(t *testing.T, additionalOpts []otlp.ExporterOption) | ||||
| 		), | ||||
| 	} | ||||
| 	tp1 := sdktrace.NewTracerProvider(append(pOpts, | ||||
| 		sdktrace.WithResource(resource.New( | ||||
| 		sdktrace.WithResource(resource.NewWithAttributes( | ||||
| 			label.String("rk1", "rv11)"), | ||||
| 			label.Int64("rk2", 5), | ||||
| 		)))...) | ||||
|  | ||||
| 	tp2 := sdktrace.NewTracerProvider(append(pOpts, | ||||
| 		sdktrace.WithResource(resource.New( | ||||
| 		sdktrace.WithResource(resource.NewWithAttributes( | ||||
| 			label.String("rk1", "rv12)"), | ||||
| 			label.Float32("rk3", 6.5), | ||||
| 		)))...) | ||||
|   | ||||
| @@ -104,8 +104,8 @@ var ( | ||||
| 	baseKeyValues = []label.KeyValue{label.String("host", "test.com")} | ||||
| 	cpuKey        = label.Key("CPU") | ||||
|  | ||||
| 	testInstA = resource.New(label.String("instance", "tester-a")) | ||||
| 	testInstB = resource.New(label.String("instance", "tester-b")) | ||||
| 	testInstA = resource.NewWithAttributes(label.String("instance", "tester-a")) | ||||
| 	testInstB = resource.NewWithAttributes(label.String("instance", "tester-b")) | ||||
|  | ||||
| 	testHistogramBoundaries = []float64{2.0, 4.0, 8.0} | ||||
|  | ||||
|   | ||||
| @@ -97,7 +97,7 @@ func TestExportSpans(t *testing.T) { | ||||
| 					}, | ||||
| 					StatusCode:    codes.Ok, | ||||
| 					StatusMessage: "Ok", | ||||
| 					Resource:      resource.New(label.String("instance", "tester-a")), | ||||
| 					Resource:      resource.NewWithAttributes(label.String("instance", "tester-a")), | ||||
| 					InstrumentationLibrary: instrumentation.Library{ | ||||
| 						Name:    "lib-a", | ||||
| 						Version: "v0.1.0", | ||||
| @@ -119,7 +119,7 @@ func TestExportSpans(t *testing.T) { | ||||
| 					}, | ||||
| 					StatusCode:    codes.Ok, | ||||
| 					StatusMessage: "Ok", | ||||
| 					Resource:      resource.New(label.String("instance", "tester-a")), | ||||
| 					Resource:      resource.NewWithAttributes(label.String("instance", "tester-a")), | ||||
| 					InstrumentationLibrary: instrumentation.Library{ | ||||
| 						Name:    "lib-b", | ||||
| 						Version: "v0.1.0", | ||||
| @@ -142,7 +142,7 @@ func TestExportSpans(t *testing.T) { | ||||
| 					}, | ||||
| 					StatusCode:    codes.Ok, | ||||
| 					StatusMessage: "Ok", | ||||
| 					Resource:      resource.New(label.String("instance", "tester-a")), | ||||
| 					Resource:      resource.NewWithAttributes(label.String("instance", "tester-a")), | ||||
| 					InstrumentationLibrary: instrumentation.Library{ | ||||
| 						Name:    "lib-a", | ||||
| 						Version: "v0.1.0", | ||||
| @@ -164,7 +164,7 @@ func TestExportSpans(t *testing.T) { | ||||
| 					}, | ||||
| 					StatusCode:    codes.Error, | ||||
| 					StatusMessage: "Unauthenticated", | ||||
| 					Resource:      resource.New(label.String("instance", "tester-b")), | ||||
| 					Resource:      resource.NewWithAttributes(label.String("instance", "tester-b")), | ||||
| 					InstrumentationLibrary: instrumentation.Library{ | ||||
| 						Name:    "lib-a", | ||||
| 						Version: "v1.1.0", | ||||
|   | ||||
| @@ -48,7 +48,7 @@ type testFixture struct { | ||||
| 	output   *bytes.Buffer | ||||
| } | ||||
|  | ||||
| var testResource = resource.New(label.String("R", "V")) | ||||
| var testResource = resource.NewWithAttributes(label.String("R", "V")) | ||||
|  | ||||
| func newFixture(t *testing.T, opts ...stdout.Option) testFixture { | ||||
| 	buf := &bytes.Buffer{} | ||||
| @@ -290,11 +290,11 @@ func TestStdoutResource(t *testing.T) { | ||||
| 	} | ||||
| 	testCases := []testCase{ | ||||
| 		newCase("R1=V1,R2=V2,A=B,C=D", | ||||
| 			resource.New(label.String("R1", "V1"), label.String("R2", "V2")), | ||||
| 			resource.NewWithAttributes(label.String("R1", "V1"), label.String("R2", "V2")), | ||||
| 			label.String("A", "B"), | ||||
| 			label.String("C", "D")), | ||||
| 		newCase("R1=V1,R2=V2", | ||||
| 			resource.New(label.String("R1", "V1"), label.String("R2", "V2")), | ||||
| 			resource.NewWithAttributes(label.String("R1", "V1"), label.String("R2", "V2")), | ||||
| 		), | ||||
| 		newCase("A=B,C=D", | ||||
| 			nil, | ||||
| @@ -304,7 +304,7 @@ func TestStdoutResource(t *testing.T) { | ||||
| 		// We explicitly do not de-duplicate between resources | ||||
| 		// and metric labels in this exporter. | ||||
| 		newCase("R1=V1,R2=V2,R1=V3,R2=V4", | ||||
| 			resource.New(label.String("R1", "V1"), label.String("R2", "V2")), | ||||
| 			resource.NewWithAttributes(label.String("R1", "V1"), label.String("R2", "V2")), | ||||
| 			label.String("R1", "V3"), | ||||
| 			label.String("R2", "V4")), | ||||
| 	} | ||||
|   | ||||
| @@ -44,7 +44,7 @@ func TestExporter_ExportSpan(t *testing.T) { | ||||
| 	spanID, _ := otel.SpanIDFromHex("0102030405060708") | ||||
| 	keyValue := "value" | ||||
| 	doubleValue := 123.456 | ||||
| 	resource := resource.New(label.String("rk1", "rv11")) | ||||
| 	resource := resource.NewWithAttributes(label.String("rk1", "rv11")) | ||||
|  | ||||
| 	testSpan := &export.SpanData{ | ||||
| 		SpanContext: otel.SpanContext{ | ||||
|   | ||||
| @@ -409,7 +409,7 @@ func Test_spanDataToThrift(t *testing.T) { | ||||
| 				StatusCode:    codes.Error, | ||||
| 				StatusMessage: statusMessage, | ||||
| 				SpanKind:      otel.SpanKindClient, | ||||
| 				Resource:      resource.New(label.String("rk1", rv1), label.Int64("rk2", rv2)), | ||||
| 				Resource:      resource.NewWithAttributes(label.String("rk1", rv1), label.Int64("rk2", rv2)), | ||||
| 				InstrumentationLibrary: instrumentation.Library{ | ||||
| 					Name:    instrLibName, | ||||
| 					Version: instrLibVersion, | ||||
|   | ||||
| @@ -42,7 +42,7 @@ func newFixture(b *testing.B) *benchFixture { | ||||
| 		AggregatorSelector: processortest.AggregatorSelector(), | ||||
| 	} | ||||
|  | ||||
| 	bf.accumulator = sdk.NewAccumulator(bf) | ||||
| 	bf.accumulator = sdk.NewAccumulator(bf, nil) | ||||
| 	bf.meter = otel.WrapMeterImpl(bf.accumulator, "benchmarks") | ||||
| 	return bf | ||||
| } | ||||
|   | ||||
| @@ -1,45 +0,0 @@ | ||||
| // Copyright The OpenTelemetry Authors | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package metric | ||||
|  | ||||
| import ( | ||||
| 	"go.opentelemetry.io/otel/sdk/resource" | ||||
| ) | ||||
|  | ||||
| // Config contains configuration for an SDK. | ||||
| type Config struct { | ||||
| 	// Resource describes all the metric records processed by the | ||||
| 	// Accumulator. | ||||
| 	Resource *resource.Resource | ||||
| } | ||||
|  | ||||
| // Option is the interface that applies the value to a configuration option. | ||||
| type Option interface { | ||||
| 	// Apply sets the Option value of a Config. | ||||
| 	Apply(*Config) | ||||
| } | ||||
|  | ||||
| // WithResource sets the Resource configuration option of a Config. | ||||
| func WithResource(res *resource.Resource) Option { | ||||
| 	return resourceOption{res} | ||||
| } | ||||
|  | ||||
| type resourceOption struct { | ||||
| 	*resource.Resource | ||||
| } | ||||
|  | ||||
| func (o resourceOption) Apply(config *Config) { | ||||
| 	config.Resource = o.Resource | ||||
| } | ||||
| @@ -60,7 +60,7 @@ func New(checkpointer export.Checkpointer, options ...Option) *Controller { | ||||
| 	} | ||||
| 	accum := sdk.NewAccumulator( | ||||
| 		checkpointer, | ||||
| 		sdk.WithResource(config.Resource), | ||||
| 		config.Resource, | ||||
| 	) | ||||
| 	return &Controller{ | ||||
| 		accumulator:  accum, | ||||
|   | ||||
| @@ -24,7 +24,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| func TestWithResource(t *testing.T) { | ||||
| 	r := resource.New(label.String("A", "a")) | ||||
| 	r := resource.NewWithAttributes(label.String("A", "a")) | ||||
|  | ||||
| 	c := &Config{} | ||||
| 	WithResource(r).Apply(c) | ||||
|   | ||||
| @@ -61,7 +61,7 @@ func New(checkpointer export.Checkpointer, exporter export.Exporter, opts ...Opt | ||||
|  | ||||
| 	impl := sdk.NewAccumulator( | ||||
| 		checkpointer, | ||||
| 		sdk.WithResource(c.Resource), | ||||
| 		c.Resource, | ||||
| 	) | ||||
| 	return &Controller{ | ||||
| 		provider:     registry.NewMeterProvider(impl), | ||||
|   | ||||
| @@ -36,7 +36,7 @@ import ( | ||||
| 	"go.opentelemetry.io/otel/sdk/resource" | ||||
| ) | ||||
|  | ||||
| var testResource = resource.New(label.String("R", "V")) | ||||
| var testResource = resource.NewWithAttributes(label.String("R", "V")) | ||||
|  | ||||
| type handler struct { | ||||
| 	sync.Mutex | ||||
|   | ||||
| @@ -34,7 +34,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| var Must = otel.Must | ||||
| var testResource = resource.New(label.String("R", "V")) | ||||
| var testResource = resource.NewWithAttributes(label.String("R", "V")) | ||||
|  | ||||
| type handler struct { | ||||
| 	sync.Mutex | ||||
| @@ -96,7 +96,7 @@ func newSDK(t *testing.T) (otel.Meter, *metricsdk.Accumulator, *correctnessProce | ||||
| 	} | ||||
| 	accum := metricsdk.NewAccumulator( | ||||
| 		processor, | ||||
| 		metricsdk.WithResource(testResource), | ||||
| 		testResource, | ||||
| 	) | ||||
| 	meter := otel.WrapMeterImpl(accum, "test") | ||||
| 	return meter, accum, processor | ||||
|   | ||||
| @@ -121,7 +121,7 @@ func testProcessor( | ||||
| 	// Note: this selector uses the instrument name to dictate | ||||
| 	// aggregation kind. | ||||
| 	selector := processorTest.AggregatorSelector() | ||||
| 	res := resource.New(label.String("R", "V")) | ||||
| 	res := resource.NewWithAttributes(label.String("R", "V")) | ||||
|  | ||||
| 	labs1 := []label.KeyValue{label.String("L1", "V")} | ||||
| 	labs2 := []label.KeyValue{label.String("L2", "V")} | ||||
| @@ -361,7 +361,7 @@ func TestBasicTimestamps(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestStatefulNoMemoryCumulative(t *testing.T) { | ||||
| 	res := resource.New(label.String("R", "V")) | ||||
| 	res := resource.NewWithAttributes(label.String("R", "V")) | ||||
| 	ekind := export.CumulativeExporter | ||||
|  | ||||
| 	desc := otel.NewDescriptor("inst.sum", otel.CounterInstrumentKind, otel.Int64NumberKind) | ||||
| @@ -395,7 +395,7 @@ func TestStatefulNoMemoryCumulative(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestStatefulNoMemoryDelta(t *testing.T) { | ||||
| 	res := resource.New(label.String("R", "V")) | ||||
| 	res := resource.NewWithAttributes(label.String("R", "V")) | ||||
| 	ekind := export.DeltaExporter | ||||
|  | ||||
| 	desc := otel.NewDescriptor("inst.sum", otel.SumObserverInstrumentKind, otel.Int64NumberKind) | ||||
| @@ -435,7 +435,7 @@ func TestMultiObserverSum(t *testing.T) { | ||||
| 		export.DeltaExporter, | ||||
| 	} { | ||||
|  | ||||
| 		res := resource.New(label.String("R", "V")) | ||||
| 		res := resource.NewWithAttributes(label.String("R", "V")) | ||||
| 		desc := otel.NewDescriptor("observe.sum", otel.SumObserverInstrumentKind, otel.Int64NumberKind) | ||||
| 		selector := processorTest.AggregatorSelector() | ||||
|  | ||||
|   | ||||
| @@ -32,9 +32,7 @@ func generateTestData(proc export.Processor) { | ||||
| 	ctx := context.Background() | ||||
| 	accum := metricsdk.NewAccumulator( | ||||
| 		proc, | ||||
| 		metricsdk.WithResource( | ||||
| 			resource.New(label.String("R", "V")), | ||||
| 		), | ||||
| 		resource.NewWithAttributes(label.String("R", "V")), | ||||
| 	) | ||||
| 	meter := otel.WrapMeterImpl(accum, "testing") | ||||
|  | ||||
|   | ||||
| @@ -75,9 +75,7 @@ func TestFilterProcessor(t *testing.T) { | ||||
| 	) | ||||
| 	accum := metricsdk.NewAccumulator( | ||||
| 		reducer.New(testFilter{}, processorTest.Checkpointer(testProc)), | ||||
| 		metricsdk.WithResource( | ||||
| 			resource.New(label.String("R", "V")), | ||||
| 		), | ||||
| 		resource.NewWithAttributes(label.String("R", "V")), | ||||
| 	) | ||||
| 	generateData(accum) | ||||
|  | ||||
| @@ -94,9 +92,7 @@ func TestFilterBasicProcessor(t *testing.T) { | ||||
| 	basicProc := basic.New(processorTest.AggregatorSelector(), export.CumulativeExporter) | ||||
| 	accum := metricsdk.NewAccumulator( | ||||
| 		reducer.New(testFilter{}, basicProc), | ||||
| 		metricsdk.WithResource( | ||||
| 			resource.New(label.String("R", "V")), | ||||
| 		), | ||||
| 		resource.NewWithAttributes(label.String("R", "V")), | ||||
| 	) | ||||
| 	exporter := processorTest.NewExporter(basicProc, label.DefaultEncoder()) | ||||
|  | ||||
|   | ||||
| @@ -305,16 +305,11 @@ func (s *syncInstrument) RecordOne(ctx context.Context, number api.Number, kvs [ | ||||
| // processor will call Collect() when it receives a request to scrape | ||||
| // current metric values.  A push-based processor should configure its | ||||
| // own periodic collection. | ||||
| func NewAccumulator(processor export.Processor, opts ...Option) *Accumulator { | ||||
| 	c := &Config{} | ||||
| 	for _, opt := range opts { | ||||
| 		opt.Apply(c) | ||||
| 	} | ||||
|  | ||||
| func NewAccumulator(processor export.Processor, resource *resource.Resource) *Accumulator { | ||||
| 	return &Accumulator{ | ||||
| 		processor:        processor, | ||||
| 		asyncInstruments: internal.NewAsyncInstrumentState(), | ||||
| 		resource:         c.Resource, | ||||
| 		resource:         resource, | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -292,7 +292,8 @@ func stressTest(t *testing.T, impl testImpl) { | ||||
| 		AggregatorSelector: processortest.AggregatorSelector(), | ||||
| 	} | ||||
| 	cc := concurrency() | ||||
| 	sdk := NewAccumulator(fixture) | ||||
|  | ||||
| 	sdk := NewAccumulator(fixture, nil) | ||||
| 	meter := otel.WrapMeterImpl(sdk, "stress_test") | ||||
| 	fixture.wg.Add(cc + 1) | ||||
|  | ||||
|   | ||||
| @@ -43,6 +43,9 @@ func Detect(ctx context.Context, detectors ...Detector) (*Resource, error) { | ||||
| 	var autoDetectedRes *Resource | ||||
| 	var errInfo []string | ||||
| 	for _, detector := range detectors { | ||||
| 		if detector == nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		res, err := detector.Detect(ctx) | ||||
| 		if err != nil { | ||||
| 			errInfo = append(errInfo, err.Error()) | ||||
|   | ||||
| @@ -47,7 +47,7 @@ func makeLabels(n int) (_, _ *resource.Resource) { | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
| 	return resource.New(l1...), resource.New(l2...) | ||||
| 	return resource.NewWithAttributes(l1...), resource.NewWithAttributes(l2...) | ||||
| } | ||||
|  | ||||
| func benchmarkMergeResource(b *testing.B, size int) { | ||||
|   | ||||
							
								
								
									
										81
									
								
								sdk/resource/builtin.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								sdk/resource/builtin.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| // Copyright The OpenTelemetry Authors | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package resource | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
|  | ||||
| 	"go.opentelemetry.io/otel/label" | ||||
| 	opentelemetry "go.opentelemetry.io/otel/sdk" | ||||
| 	"go.opentelemetry.io/otel/semconv" | ||||
| ) | ||||
|  | ||||
| type ( | ||||
| 	// TelemetrySDK is a Detector that provides information about | ||||
| 	// the OpenTelemetry SDK used.  This Detector is included as a | ||||
| 	// builtin. If these resource attributes are not wanted, use | ||||
| 	// the WithTelemetrySDK(nil) or WithoutBuiltin() options to | ||||
| 	// explicitly disable them. | ||||
| 	TelemetrySDK struct{} | ||||
|  | ||||
| 	// Host is a Detector that provides information about the host | ||||
| 	// being run on. This Detector is included as a builtin. If | ||||
| 	// these resource attributes are not wanted, use the | ||||
| 	// WithHost(nil) or WithoutBuiltin() options to explicitly | ||||
| 	// disable them. | ||||
| 	Host struct{} | ||||
|  | ||||
| 	stringDetector struct { | ||||
| 		K label.Key | ||||
| 		F func() (string, error) | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	_ Detector = TelemetrySDK{} | ||||
| 	_ Detector = Host{} | ||||
| 	_ Detector = stringDetector{} | ||||
| ) | ||||
|  | ||||
| // Detect returns a *Resource that describes the OpenTelemetry SDK used. | ||||
| func (TelemetrySDK) Detect(context.Context) (*Resource, error) { | ||||
| 	return NewWithAttributes( | ||||
| 		semconv.TelemetrySDKNameKey.String("opentelemetry-go"), | ||||
| 		semconv.TelemetrySDKLanguageKey.String("go"), | ||||
| 		semconv.TelemetrySDKVersionKey.String(opentelemetry.Version()), | ||||
| 	), nil | ||||
| } | ||||
|  | ||||
| // Detect returns a *Resource that describes the host being run on. | ||||
| func (Host) Detect(ctx context.Context) (*Resource, error) { | ||||
| 	return StringDetector(semconv.HostNameKey, os.Hostname).Detect(ctx) | ||||
| } | ||||
|  | ||||
| // StringDetector returns a Detector that will produce a *Resource | ||||
| // containing the string as a value corresponding to k. | ||||
| func StringDetector(k label.Key, f func() (string, error)) Detector { | ||||
| 	return stringDetector{K: k, F: f} | ||||
| } | ||||
|  | ||||
| // Detect implements Detector. | ||||
| func (sd stringDetector) Detect(ctx context.Context) (*Resource, error) { | ||||
| 	value, err := sd.F() | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("%s: %w", string(sd.K), err) | ||||
| 	} | ||||
| 	return NewWithAttributes(sd.K.String(value)), nil | ||||
| } | ||||
							
								
								
									
										59
									
								
								sdk/resource/builtin_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								sdk/resource/builtin_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| // Copyright The OpenTelemetry Authors | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package resource_test | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/require" | ||||
|  | ||||
| 	"go.opentelemetry.io/otel/label" | ||||
| 	"go.opentelemetry.io/otel/sdk/resource" | ||||
| ) | ||||
|  | ||||
| func TestBuiltinStringDetector(t *testing.T) { | ||||
| 	E := fmt.Errorf("no K") | ||||
| 	res, err := resource.StringDetector(label.Key("K"), func() (string, error) { | ||||
| 		return "", E | ||||
| 	}).Detect(context.Background()) | ||||
| 	require.True(t, errors.Is(err, E)) | ||||
| 	require.NotEqual(t, E, err) | ||||
| 	require.Nil(t, res) | ||||
| } | ||||
|  | ||||
| func TestBuiltinStringConfig(t *testing.T) { | ||||
| 	res, err := resource.New( | ||||
| 		context.Background(), | ||||
| 		resource.WithoutBuiltin(), | ||||
| 		resource.WithAttributes(label.String("A", "B")), | ||||
| 		resource.WithDetectors(resource.StringDetector(label.Key("K"), func() (string, error) { | ||||
| 			return "", fmt.Errorf("K-IS-MISSING") | ||||
| 		})), | ||||
| 	) | ||||
| 	require.Error(t, err) | ||||
| 	require.Contains(t, err.Error(), "K-IS-MISSING") | ||||
| 	require.NotNil(t, res) | ||||
|  | ||||
| 	m := map[string]string{} | ||||
| 	for _, kv := range res.Attributes() { | ||||
| 		m[string(kv.Key)] = kv.Value.Emit() | ||||
| 	} | ||||
| 	require.EqualValues(t, map[string]string{ | ||||
| 		"A": "B", | ||||
| 	}, m) | ||||
| } | ||||
							
								
								
									
										150
									
								
								sdk/resource/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								sdk/resource/config.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,150 @@ | ||||
| // Copyright The OpenTelemetry Authors | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package resource | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
|  | ||||
| 	"go.opentelemetry.io/otel/label" | ||||
| ) | ||||
|  | ||||
| // config contains configuration for Resource creation. | ||||
| type config struct { | ||||
| 	// detectors that will be evaluated. | ||||
| 	detectors []Detector | ||||
|  | ||||
| 	// telemetrySDK is used to specify non-default | ||||
| 	// `telemetry.sdk.*` attributes. | ||||
| 	telemetrySDK Detector | ||||
|  | ||||
| 	// HostResource is used to specify non-default `host.*` | ||||
| 	// attributes. | ||||
| 	host Detector | ||||
|  | ||||
| 	// FromEnv is used to specify non-default OTEL_RESOURCE_ATTRIBUTES | ||||
| 	// attributes. | ||||
| 	fromEnv Detector | ||||
| } | ||||
|  | ||||
| // Option is the interface that applies a configuration option. | ||||
| type Option interface { | ||||
| 	// Apply sets the Option value of a config. | ||||
| 	Apply(*config) | ||||
| } | ||||
|  | ||||
| // WithAttributes adds attributes to the configured Resource. | ||||
| func WithAttributes(attributes ...label.KeyValue) Option { | ||||
| 	return WithDetectors(detectAttributes{attributes}) | ||||
| } | ||||
|  | ||||
| type detectAttributes struct { | ||||
| 	attributes []label.KeyValue | ||||
| } | ||||
|  | ||||
| func (d detectAttributes) Detect(context.Context) (*Resource, error) { | ||||
| 	return NewWithAttributes(d.attributes...), nil | ||||
| } | ||||
|  | ||||
| // WithDetectors adds detectors to be evaluated for the configured resource. | ||||
| func WithDetectors(detectors ...Detector) Option { | ||||
| 	return detectorsOption{detectors} | ||||
| } | ||||
|  | ||||
| type detectorsOption struct { | ||||
| 	detectors []Detector | ||||
| } | ||||
|  | ||||
| // Apply implements Option. | ||||
| func (o detectorsOption) Apply(cfg *config) { | ||||
| 	cfg.detectors = append(cfg.detectors, o.detectors...) | ||||
| } | ||||
|  | ||||
| // WithTelemetrySDK overrides the builtin `telemetry.sdk.*` | ||||
| // attributes.  Use nil to disable these attributes entirely. | ||||
| func WithTelemetrySDK(d Detector) Option { | ||||
| 	return telemetrySDKOption{d} | ||||
| } | ||||
|  | ||||
| type telemetrySDKOption struct { | ||||
| 	Detector | ||||
| } | ||||
|  | ||||
| // Apply implements Option. | ||||
| func (o telemetrySDKOption) Apply(cfg *config) { | ||||
| 	cfg.telemetrySDK = o.Detector | ||||
| } | ||||
|  | ||||
| // WithHost overrides the builtin `host.*` attributes.  Use nil to | ||||
| // disable these attributes entirely. | ||||
| func WithHost(d Detector) Option { | ||||
| 	return hostOption{d} | ||||
| } | ||||
|  | ||||
| type hostOption struct { | ||||
| 	Detector | ||||
| } | ||||
|  | ||||
| // Apply implements Option. | ||||
| func (o hostOption) Apply(cfg *config) { | ||||
| 	cfg.host = o.Detector | ||||
| } | ||||
|  | ||||
| // WithFromEnv overrides the builtin detector for | ||||
| // OTEL_RESOURCE_ATTRIBUTES.  Use nil to disable environment checking. | ||||
| func WithFromEnv(d Detector) Option { | ||||
| 	return fromEnvOption{d} | ||||
| } | ||||
|  | ||||
| type fromEnvOption struct { | ||||
| 	Detector | ||||
| } | ||||
|  | ||||
| // Apply implements Option. | ||||
| func (o fromEnvOption) Apply(cfg *config) { | ||||
| 	cfg.fromEnv = o.Detector | ||||
| } | ||||
|  | ||||
| // WithoutBuiltin disables all the builtin detectors, including the | ||||
| // telemetry.sdk.*, host.*, and the environment detector. | ||||
| func WithoutBuiltin() Option { | ||||
| 	return noBuiltinOption{} | ||||
| } | ||||
|  | ||||
| type noBuiltinOption struct{} | ||||
|  | ||||
| // Apply implements Option. | ||||
| func (o noBuiltinOption) Apply(cfg *config) { | ||||
| 	cfg.host = nil | ||||
| 	cfg.telemetrySDK = nil | ||||
| 	cfg.fromEnv = nil | ||||
| } | ||||
|  | ||||
| // New returns a Resource combined from the provided attributes, | ||||
| // user-provided detectors and builtin detectors. | ||||
| func New(ctx context.Context, opts ...Option) (*Resource, error) { | ||||
| 	cfg := config{ | ||||
| 		telemetrySDK: TelemetrySDK{}, | ||||
| 		host:         Host{}, | ||||
| 		fromEnv:      FromEnv{}, | ||||
| 	} | ||||
| 	for _, opt := range opts { | ||||
| 		opt.Apply(&cfg) | ||||
| 	} | ||||
| 	detectors := append( | ||||
| 		[]Detector{cfg.telemetrySDK, cfg.host, cfg.fromEnv}, | ||||
| 		cfg.detectors..., | ||||
| 	) | ||||
| 	return Detect(ctx, detectors...) | ||||
| } | ||||
							
								
								
									
										139
									
								
								sdk/resource/config_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								sdk/resource/config_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,139 @@ | ||||
| // Copyright The OpenTelemetry Authors | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package resource_test | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/require" | ||||
|  | ||||
| 	ottest "go.opentelemetry.io/otel/internal/testing" | ||||
| 	"go.opentelemetry.io/otel/label" | ||||
| 	opentelemetry "go.opentelemetry.io/otel/sdk" | ||||
| 	"go.opentelemetry.io/otel/sdk/resource" | ||||
| ) | ||||
|  | ||||
| const envVar = "OTEL_RESOURCE_ATTRIBUTES" | ||||
|  | ||||
| func TestDefaultConfig(t *testing.T) { | ||||
| 	store, err := ottest.SetEnvVariables(map[string]string{ | ||||
| 		envVar: "", | ||||
| 	}) | ||||
| 	require.NoError(t, err) | ||||
| 	defer func() { require.NoError(t, store.Restore()) }() | ||||
|  | ||||
| 	ctx := context.Background() | ||||
| 	res, err := resource.New(ctx) | ||||
| 	require.NoError(t, err) | ||||
| 	require.EqualValues(t, map[string]string{ | ||||
| 		"host.name":              hostname(), | ||||
| 		"telemetry.sdk.name":     "opentelemetry-go", | ||||
| 		"telemetry.sdk.language": "go", | ||||
| 		"telemetry.sdk.version":  opentelemetry.Version(), | ||||
| 	}, toMap(res)) | ||||
| } | ||||
|  | ||||
| func TestDefaultConfigNoHost(t *testing.T) { | ||||
| 	store, err := ottest.SetEnvVariables(map[string]string{ | ||||
| 		envVar: "", | ||||
| 	}) | ||||
| 	require.NoError(t, err) | ||||
| 	defer func() { require.NoError(t, store.Restore()) }() | ||||
|  | ||||
| 	ctx := context.Background() | ||||
| 	res, err := resource.New(ctx, resource.WithHost(nil)) | ||||
| 	require.NoError(t, err) | ||||
| 	require.EqualValues(t, map[string]string{ | ||||
| 		"telemetry.sdk.name":     "opentelemetry-go", | ||||
| 		"telemetry.sdk.language": "go", | ||||
| 		"telemetry.sdk.version":  opentelemetry.Version(), | ||||
| 	}, toMap(res)) | ||||
| } | ||||
|  | ||||
| func TestDefaultConfigNoEnv(t *testing.T) { | ||||
| 	store, err := ottest.SetEnvVariables(map[string]string{ | ||||
| 		envVar: "from=here", | ||||
| 	}) | ||||
| 	require.NoError(t, err) | ||||
| 	defer func() { require.NoError(t, store.Restore()) }() | ||||
|  | ||||
| 	ctx := context.Background() | ||||
| 	res, err := resource.New(ctx, resource.WithFromEnv(nil)) | ||||
| 	require.NoError(t, err) | ||||
| 	require.EqualValues(t, map[string]string{ | ||||
| 		"host.name":              hostname(), | ||||
| 		"telemetry.sdk.name":     "opentelemetry-go", | ||||
| 		"telemetry.sdk.language": "go", | ||||
| 		"telemetry.sdk.version":  opentelemetry.Version(), | ||||
| 	}, toMap(res)) | ||||
| } | ||||
|  | ||||
| func TestDefaultConfigWithEnv(t *testing.T) { | ||||
| 	store, err := ottest.SetEnvVariables(map[string]string{ | ||||
| 		envVar: "key=value,other=attr", | ||||
| 	}) | ||||
| 	require.NoError(t, err) | ||||
| 	defer func() { require.NoError(t, store.Restore()) }() | ||||
|  | ||||
| 	ctx := context.Background() | ||||
| 	res, err := resource.New(ctx) | ||||
| 	require.NoError(t, err) | ||||
| 	require.EqualValues(t, map[string]string{ | ||||
| 		"key":                    "value", | ||||
| 		"other":                  "attr", | ||||
| 		"host.name":              hostname(), | ||||
| 		"telemetry.sdk.name":     "opentelemetry-go", | ||||
| 		"telemetry.sdk.language": "go", | ||||
| 		"telemetry.sdk.version":  opentelemetry.Version(), | ||||
| 	}, toMap(res)) | ||||
| } | ||||
|  | ||||
| func TestWithoutBuiltin(t *testing.T) { | ||||
| 	store, err := ottest.SetEnvVariables(map[string]string{ | ||||
| 		envVar: "key=value,other=attr", | ||||
| 	}) | ||||
| 	require.NoError(t, err) | ||||
| 	defer func() { require.NoError(t, store.Restore()) }() | ||||
|  | ||||
| 	ctx := context.Background() | ||||
| 	res, err := resource.New( | ||||
| 		ctx, | ||||
| 		resource.WithoutBuiltin(), | ||||
| 		resource.WithAttributes(label.String("hello", "collector")), | ||||
| 	) | ||||
| 	require.NoError(t, err) | ||||
| 	require.EqualValues(t, map[string]string{ | ||||
| 		"hello": "collector", | ||||
| 	}, toMap(res)) | ||||
| } | ||||
|  | ||||
| func toMap(res *resource.Resource) map[string]string { | ||||
| 	m := map[string]string{} | ||||
| 	for _, attr := range res.Attributes() { | ||||
| 		m[string(attr.Key)] = attr.Value.Emit() | ||||
| 	} | ||||
| 	return m | ||||
| } | ||||
|  | ||||
| func hostname() string { | ||||
| 	hn, err := os.Hostname() | ||||
| 	if err != nil { | ||||
| 		return fmt.Sprintf("hostname(%s)", err) | ||||
| 	} | ||||
| 	return hn | ||||
| } | ||||
| @@ -27,19 +27,22 @@ import ( | ||||
| const envVar = "OTEL_RESOURCE_ATTRIBUTES" | ||||
|  | ||||
| var ( | ||||
| 	//errMissingValue is returned when a resource value is missing. | ||||
| 	// errMissingValue is returned when a resource value is missing. | ||||
| 	errMissingValue = fmt.Errorf("%w: missing value", ErrPartialResource) | ||||
| ) | ||||
|  | ||||
| // FromEnv is a detector that implements the Detector and collects resources | ||||
| // from environment | ||||
| // FromEnv is a Detector that implements the Detector and collects | ||||
| // resources from environment.  This Detector is included as a | ||||
| // builtin.  If these resource attributes are not wanted, use the | ||||
| // WithFromEnv(nil) or WithoutBuiltin() options to explicitly disable | ||||
| // them. | ||||
| type FromEnv struct{} | ||||
|  | ||||
| // compile time assertion that FromEnv implements Detector interface | ||||
| var _ Detector = (*FromEnv)(nil) | ||||
| var _ Detector = FromEnv{} | ||||
|  | ||||
| // Detect collects resources from environment | ||||
| func (d *FromEnv) Detect(context.Context) (*Resource, error) { | ||||
| func (FromEnv) Detect(context.Context) (*Resource, error) { | ||||
| 	labels := strings.TrimSpace(os.Getenv(envVar)) | ||||
|  | ||||
| 	if labels == "" { | ||||
| @@ -65,5 +68,5 @@ func constructOTResources(s string) (*Resource, error) { | ||||
| 	if len(invalid) > 0 { | ||||
| 		err = fmt.Errorf("%w: %v", errMissingValue, invalid) | ||||
| 	} | ||||
| 	return New(labels...), err | ||||
| 	return NewWithAttributes(labels...), err | ||||
| } | ||||
|   | ||||
| @@ -17,32 +17,40 @@ package resource | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/require" | ||||
|  | ||||
| 	ottest "go.opentelemetry.io/otel/internal/testing" | ||||
| 	"go.opentelemetry.io/otel/label" | ||||
| ) | ||||
|  | ||||
| func TestDetectOnePair(t *testing.T) { | ||||
| 	os.Setenv(envVar, "key=value") | ||||
| 	store, err := ottest.SetEnvVariables(map[string]string{ | ||||
| 		envVar: "key=value", | ||||
| 	}) | ||||
| 	require.NoError(t, err) | ||||
| 	defer func() { require.NoError(t, store.Restore()) }() | ||||
|  | ||||
| 	detector := &FromEnv{} | ||||
| 	res, err := detector.Detect(context.Background()) | ||||
| 	require.NoError(t, err) | ||||
| 	assert.Equal(t, New(label.String("key", "value")), res) | ||||
| 	assert.Equal(t, NewWithAttributes(label.String("key", "value")), res) | ||||
| } | ||||
|  | ||||
| func TestDetectMultiPairs(t *testing.T) { | ||||
| 	os.Setenv("x", "1") | ||||
| 	os.Setenv(envVar, "key=value, k = v , a= x, a=z") | ||||
| 	store, err := ottest.SetEnvVariables(map[string]string{ | ||||
| 		"x":    "1", | ||||
| 		envVar: "key=value, k = v , a= x, a=z", | ||||
| 	}) | ||||
| 	require.NoError(t, err) | ||||
| 	defer func() { require.NoError(t, store.Restore()) }() | ||||
|  | ||||
| 	detector := &FromEnv{} | ||||
| 	res, err := detector.Detect(context.Background()) | ||||
| 	require.NoError(t, err) | ||||
| 	assert.Equal(t, res, New( | ||||
| 	assert.Equal(t, res, NewWithAttributes( | ||||
| 		label.String("key", "value"), | ||||
| 		label.String("k", "v"), | ||||
| 		label.String("a", "x"), | ||||
| @@ -51,7 +59,11 @@ func TestDetectMultiPairs(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestEmpty(t *testing.T) { | ||||
| 	os.Setenv(envVar, "   ") | ||||
| 	store, err := ottest.SetEnvVariables(map[string]string{ | ||||
| 		envVar: "   ", | ||||
| 	}) | ||||
| 	require.NoError(t, err) | ||||
| 	defer func() { require.NoError(t, store.Restore()) }() | ||||
|  | ||||
| 	detector := &FromEnv{} | ||||
| 	res, err := detector.Detect(context.Background()) | ||||
| @@ -60,13 +72,17 @@ func TestEmpty(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestMissingKeyError(t *testing.T) { | ||||
| 	os.Setenv(envVar, "key=value,key") | ||||
| 	store, err := ottest.SetEnvVariables(map[string]string{ | ||||
| 		envVar: "key=value,key", | ||||
| 	}) | ||||
| 	require.NoError(t, err) | ||||
| 	defer func() { require.NoError(t, store.Restore()) }() | ||||
|  | ||||
| 	detector := &FromEnv{} | ||||
| 	res, err := detector.Detect(context.Background()) | ||||
| 	assert.Error(t, err) | ||||
| 	assert.Equal(t, err, fmt.Errorf("%w: %v", errMissingValue, "[key]")) | ||||
| 	assert.Equal(t, res, New( | ||||
| 	assert.Equal(t, res, NewWithAttributes( | ||||
| 		label.String("key", "value"), | ||||
| 	)) | ||||
| } | ||||
|   | ||||
| @@ -31,10 +31,10 @@ type Resource struct { | ||||
|  | ||||
| var emptyResource Resource | ||||
|  | ||||
| // New creates a resource from a set of attributes.  If there are | ||||
| // NewWithAttributes creates a resource from a set of attributes.  If there are | ||||
| // duplicate keys present in the list of attributes, then the last | ||||
| // value found for the key is preserved. | ||||
| func New(kvs ...label.KeyValue) *Resource { | ||||
| func NewWithAttributes(kvs ...label.KeyValue) *Resource { | ||||
| 	return &Resource{ | ||||
| 		labels: label.NewSet(kvs...), | ||||
| 	} | ||||
| @@ -103,7 +103,7 @@ func Merge(a, b *Resource) *Resource { | ||||
| 	for mi.Next() { | ||||
| 		combine = append(combine, mi.Label()) | ||||
| 	} | ||||
| 	return New(combine...) | ||||
| 	return NewWithAttributes(combine...) | ||||
| } | ||||
|  | ||||
| // Empty returns an instance of Resource with no attributes.  It is | ||||
|   | ||||
| @@ -58,7 +58,7 @@ func TestNew(t *testing.T) { | ||||
| 	} | ||||
| 	for _, c := range cases { | ||||
| 		t.Run(fmt.Sprintf("case-%s", c.name), func(t *testing.T) { | ||||
| 			res := resource.New(c.in...) | ||||
| 			res := resource.NewWithAttributes(c.in...) | ||||
| 			if diff := cmp.Diff( | ||||
| 				res.Attributes(), | ||||
| 				c.want, | ||||
| @@ -77,61 +77,61 @@ func TestMerge(t *testing.T) { | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name: "Merge with no overlap, no nil", | ||||
| 			a:    resource.New(kv11, kv31), | ||||
| 			b:    resource.New(kv21, kv41), | ||||
| 			a:    resource.NewWithAttributes(kv11, kv31), | ||||
| 			b:    resource.NewWithAttributes(kv21, kv41), | ||||
| 			want: []label.KeyValue{kv11, kv21, kv31, kv41}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "Merge with no overlap, no nil, not interleaved", | ||||
| 			a:    resource.New(kv11, kv21), | ||||
| 			b:    resource.New(kv31, kv41), | ||||
| 			a:    resource.NewWithAttributes(kv11, kv21), | ||||
| 			b:    resource.NewWithAttributes(kv31, kv41), | ||||
| 			want: []label.KeyValue{kv11, kv21, kv31, kv41}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "Merge with common key order1", | ||||
| 			a:    resource.New(kv11), | ||||
| 			b:    resource.New(kv12, kv21), | ||||
| 			a:    resource.NewWithAttributes(kv11), | ||||
| 			b:    resource.NewWithAttributes(kv12, kv21), | ||||
| 			want: []label.KeyValue{kv11, kv21}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "Merge with common key order2", | ||||
| 			a:    resource.New(kv12, kv21), | ||||
| 			b:    resource.New(kv11), | ||||
| 			a:    resource.NewWithAttributes(kv12, kv21), | ||||
| 			b:    resource.NewWithAttributes(kv11), | ||||
| 			want: []label.KeyValue{kv12, kv21}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "Merge with common key order4", | ||||
| 			a:    resource.New(kv11, kv21, kv41), | ||||
| 			b:    resource.New(kv31, kv41), | ||||
| 			a:    resource.NewWithAttributes(kv11, kv21, kv41), | ||||
| 			b:    resource.NewWithAttributes(kv31, kv41), | ||||
| 			want: []label.KeyValue{kv11, kv21, kv31, kv41}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "Merge with no keys", | ||||
| 			a:    resource.New(), | ||||
| 			b:    resource.New(), | ||||
| 			a:    resource.NewWithAttributes(), | ||||
| 			b:    resource.NewWithAttributes(), | ||||
| 			want: nil, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "Merge with first resource no keys", | ||||
| 			a:    resource.New(), | ||||
| 			b:    resource.New(kv21), | ||||
| 			a:    resource.NewWithAttributes(), | ||||
| 			b:    resource.NewWithAttributes(kv21), | ||||
| 			want: []label.KeyValue{kv21}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "Merge with second resource no keys", | ||||
| 			a:    resource.New(kv11), | ||||
| 			b:    resource.New(), | ||||
| 			a:    resource.NewWithAttributes(kv11), | ||||
| 			b:    resource.NewWithAttributes(), | ||||
| 			want: []label.KeyValue{kv11}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "Merge with first resource nil", | ||||
| 			a:    nil, | ||||
| 			b:    resource.New(kv21), | ||||
| 			b:    resource.NewWithAttributes(kv21), | ||||
| 			want: []label.KeyValue{kv21}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "Merge with second resource nil", | ||||
| 			a:    resource.New(kv11), | ||||
| 			a:    resource.NewWithAttributes(kv11), | ||||
| 			b:    nil, | ||||
| 			want: []label.KeyValue{kv11}, | ||||
| 		}, | ||||
| @@ -207,14 +207,14 @@ func TestString(t *testing.T) { | ||||
| 			want: `A\=a\\\,B=b`, | ||||
| 		}, | ||||
| 	} { | ||||
| 		if got := resource.New(test.kvs...).String(); got != test.want { | ||||
| 		if got := resource.NewWithAttributes(test.kvs...).String(); got != test.want { | ||||
| 			t.Errorf("Resource(%v).String() = %q, want %q", test.kvs, got, test.want) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestMarshalJSON(t *testing.T) { | ||||
| 	r := resource.New(label.Int64("A", 1), label.String("C", "D")) | ||||
| 	r := resource.NewWithAttributes(label.Int64("A", 1), label.String("C", "D")) | ||||
| 	data, err := json.Marshal(r) | ||||
| 	require.NoError(t, err) | ||||
| 	require.Equal(t, | ||||
|   | ||||
| @@ -1169,7 +1169,7 @@ func TestWithResource(t *testing.T) { | ||||
| 	te := NewTestExporter() | ||||
| 	tp := NewTracerProvider(WithSyncer(te), | ||||
| 		WithConfig(Config{DefaultSampler: AlwaysSample()}), | ||||
| 		WithResource(resource.New(label.String("rk1", "rv1"), label.Int64("rk2", 5)))) | ||||
| 		WithResource(resource.NewWithAttributes(label.String("rk1", "rv1"), label.Int64("rk2", 5)))) | ||||
| 	span := startSpan(tp, "WithResource") | ||||
| 	span.SetAttributes(label.String("key1", "value1")) | ||||
| 	got, err := endSpan(te, span) | ||||
| @@ -1189,7 +1189,7 @@ func TestWithResource(t *testing.T) { | ||||
| 		}, | ||||
| 		SpanKind:               otel.SpanKindInternal, | ||||
| 		HasRemoteParent:        true, | ||||
| 		Resource:               resource.New(label.String("rk1", "rv1"), label.Int64("rk2", 5)), | ||||
| 		Resource:               resource.NewWithAttributes(label.String("rk1", "rv1"), label.Int64("rk2", 5)), | ||||
| 		InstrumentationLibrary: instrumentation.Library{Name: "WithResource"}, | ||||
| 	} | ||||
| 	if diff := cmpDiff(got, want); diff != "" { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user