diff --git a/CHANGELOG.md b/CHANGELOG.md index e5ef17eb946..96efc69e083 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Add log support for the autoexport package. (#5733) - Add support for disabling the old runtime metrics using the `OTEL_GO_X_DEPRECATED_RUNTIME_METRICS=false` environment variable. (#5747) - Add support for signal-specific protocols environment variables (`OTEL_EXPORTER_OTLP_TRACES_PROTOCOL`, `OTEL_EXPORTER_OTLP_LOGS_PROTOCOL`, `OTEL_EXPORTER_OTLP_METRICS_PROTOCOL`) in `go.opentelemetry.io/contrib/exporters/autoexport`. (#5816) +- The `go.opentelemetry.io/contrib/processors/baggagecopy` module. + This module is a replacment of `go.opentelemetry.io/contrib/processors/baggage/baggagetrace`. (#5824) ### Fixed @@ -24,7 +26,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Use `c.FullPath()` method to set `http.route` attribute in `go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin`. (#5734) - The double setup in `go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/example` that caused duplicate traces. (#5564) - Out-of-bounds panic in case of invalid span ID in `go.opentelemetry.io/contrib/propagators/b3`. (#5754) -- Do not panic if a zero-value `SpanProcessor` is used from `go.opentelemetry.io/contrib/processors/baggage/baggagetrace`. (#5811) ### Deprecated @@ -36,6 +37,12 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm If you would like to become a Code Owner of this module and prevent it from being removed, see [#5552]. (#5646) - The `go.opentelemetry.io/contrib/samplers/aws/xray` package is deprecated. If you would like to become a Code Owner of this module and prevent it from being removed, see [#5554]. (#5647) +- The `go.opentelemetry.io/contrib/processors/baggage/baggagetrace` package is deprecated. + Use the added `go.opentelemetry.io/contrib/processors/baggagecopy` package instead. (#5824) + - Use `baggagecopy.NewSpanProcessor` as a replacement for `baggagetrace.New`. + - `NewSpanProcessor` accepts a `Fitler` function type that selects which baggage members are added to a span. + - `NewSpanProcessor` returns a `*baggagecopy.SpanProcessor` instead of a `trace.SpanProcessor` interface. + The returned type still implements the interface. [#5550]: https://github.com/open-telemetry/opentelemetry-go-contrib/issues/5550 [#5551]: https://github.com/open-telemetry/opentelemetry-go-contrib/issues/5551 @@ -47,10 +54,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Improve performance of `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc` with the usage of `WithAttributeSet()` instead of `WithAttribute()`. (#5664) - Improve performance of `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` with the usage of `WithAttributeSet()` instead of `WithAttribute()`. (#5664) - Update `go.opentelemetry.io/contrib/config` to latest released configuration schema which introduces breaking changes where `Attributes` is now a `map[string]interface{}`. (#5758) -- Rename `BaggageKeyPredicate` to `Filter` in `go.opentelemetry.io/contrib/processors/baggage/baggagetrace`. (#5809) -- Return `*SpanProcessor` from `"go.opentelemetry.io/contrib/processors/baggage/baggagetrace".New` instead of the `trace.SpanProcessor` interface. (#5810) -- The `Filter` in `go.opentelemetry.io/contrib/processors/baggage/baggagetrace` now accepts a `baggage.Member` as a parameter instead of a string. (#5813) -- Rename `AllowAllBaggageKeys` to `AllowAllMembers` in `go.opentelemetry.io/contrib/processors/baggage/baggagetrace`. (#5813) ## [1.27.0/0.52.0/0.21.0/0.7.0/0.2.0] - 2024-05-21 @@ -84,8 +87,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm This parameter is used as a replacement of `WithInstrumentationScope` to specify the name of the logger backing the returned `Handler`. (#5588) - Upgrade all dependencies of `go.opentelemetry.io/otel/semconv/v1.24.0` to `go.opentelemetry.io/otel/semconv/v1.25.0`. (#5605) -- Update the span processor in `go.opentelemetry.io/contrib/processors/baggage/baggagetrace` to require a baggage key predicate. (#5619) - ### Removed - The `WithInstrumentationScope` option function in `go.opentelemetry.io/contrib/bridges/otelslog` is removed. diff --git a/processors/baggage/baggagetrace/doc.go b/processors/baggage/baggagetrace/doc.go index c18ff060b25..96b8d8cc41c 100644 --- a/processors/baggage/baggagetrace/doc.go +++ b/processors/baggage/baggagetrace/doc.go @@ -1,27 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -// Package baggagetrace is an OpenTelemetry [Span Processor] that reads key/values -// stored in [Baggage] in the starting span's parent context and adds them as -// attributes to the span. +// Package baggagetrace implements a baggage span processor. // -// Keys and values added to Baggage will appear on all subsequent child spans for -// a trace within this service and will be propagated to external services via -// propagation headers. -// If the external services also have a Baggage span processor, the keys and -// values will appear in those child spans as well. -// -// Do not put sensitive information in Baggage. -// -// # Usage -// -// Add the span processor when configuring the tracer provider. -// -// The convenience function [AllowAllBaggageKeys] is provided to -// allow all baggage keys to be copied to the span. Alternatively, you can -// provide a custom baggage key predicate to select which baggage keys you want -// to copy. -// -// [Span Processor]: https://opentelemetry.io/docs/specs/otel/trace/sdk/#span-processor -// [Baggage]: https://opentelemetry.io/docs/specs/otel/api/baggage +// Deprecated: Use go.opentelemetry.io/contrib/processors/baggagecopy instead. package baggagetrace // import "go.opentelemetry.io/contrib/processors/baggage/baggagetrace" diff --git a/processors/baggage/baggagetrace/go.mod b/processors/baggage/baggagetrace/go.mod index 5b660a2200c..32a9f695892 100644 --- a/processors/baggage/baggagetrace/go.mod +++ b/processors/baggage/baggagetrace/go.mod @@ -1,9 +1,11 @@ +// Deprecated: Use go.opentelemetry.io/contrib/processors/baggagecopy instead. module go.opentelemetry.io/contrib/processors/baggage/baggagetrace go 1.21 require ( github.com/stretchr/testify v1.9.0 + go.opentelemetry.io/contrib/processors/baggagecopy v0.0.0-00010101000000-000000000000 go.opentelemetry.io/otel v1.27.0 go.opentelemetry.io/otel/sdk v1.27.0 ) @@ -18,3 +20,5 @@ require ( golang.org/x/sys v0.21.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace go.opentelemetry.io/contrib/processors/baggagecopy => ../../baggagecopy diff --git a/processors/baggage/baggagetrace/processor.go b/processors/baggage/baggagetrace/processor.go index 77dc5c4dad8..8eabac0c725 100644 --- a/processors/baggage/baggagetrace/processor.go +++ b/processors/baggage/baggagetrace/processor.go @@ -7,48 +7,29 @@ import ( "context" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/baggage" + otelbaggage "go.opentelemetry.io/otel/baggage" "go.opentelemetry.io/otel/sdk/trace" -) - -// Filter returns true if the baggage member should be added to a span. -type Filter func(member baggage.Member) bool -// AllowAllMembers allows all baggage members to be added to a span. -var AllowAllMembers Filter = func(baggage.Member) bool { return true } + "go.opentelemetry.io/contrib/processors/baggagecopy" +) -// SpanProcessor is a [trace.SpanProcessor] implementation that adds baggage -// members onto a span as attributes. -type SpanProcessor struct { - filter Filter -} +// SpanProcessor is a processing pipeline for spans in the trace signal. +type SpanProcessor struct{} var _ trace.SpanProcessor = (*SpanProcessor)(nil) -// New returns a new [SpanProcessor]. +// New returns a new SpanProcessor. // // The Baggage span processor duplicates onto a span the attributes found // in Baggage in the parent context at the moment the span is started. -// The passed filter determines which baggage members are added to the span. -// -// If filter is nil, all baggage members will be added. -func New(filter Filter) *SpanProcessor { - return &SpanProcessor{ - filter: filter, - } +func New() trace.SpanProcessor { + return baggagecopy.NewSpanProcessor(baggagecopy.AllowAllMembers) } // OnStart is called when a span is started and adds span attributes for baggage contents. func (processor SpanProcessor) OnStart(ctx context.Context, span trace.ReadWriteSpan) { - filter := processor.filter - if filter == nil { - filter = AllowAllMembers - } - - for _, member := range baggage.FromContext(ctx).Members() { - if filter(member) { - span.SetAttributes(attribute.String(member.Key(), member.Value())) - } + for _, entry := range otelbaggage.FromContext(ctx).Members() { + span.SetAttributes(attribute.String(entry.Key(), entry.Value())) } } diff --git a/processors/baggage/baggagetrace/processor_test.go b/processors/baggage/baggagetrace/processor_test.go index bd680c03842..3b50bcf3eac 100644 --- a/processors/baggage/baggagetrace/processor_test.go +++ b/processors/baggage/baggagetrace/processor_test.go @@ -5,17 +5,14 @@ package baggagetrace import ( "context" - "regexp" - "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/baggage" + otelbaggage "go.opentelemetry.io/otel/baggage" "go.opentelemetry.io/otel/sdk/trace" - "go.opentelemetry.io/otel/sdk/trace/tracetest" ) var _ trace.SpanExporter = &testExporter{} @@ -36,72 +33,19 @@ func NewTestExporter() *testExporter { return &testExporter{} } -func TestSpanProcessorAppendsAllBaggageAttributes(t *testing.T) { - b, _ := baggage.New() - b = addEntryToBaggage(t, b, "baggage.test", "baggage value") - ctx := baggage.ContextWithBaggage(context.Background(), b) - - // create trace provider with baggage processor and test exporter - exporter := NewTestExporter() - tp := trace.NewTracerProvider( - trace.WithSpanProcessor(New(AllowAllMembers)), - trace.WithSpanProcessor(trace.NewSimpleSpanProcessor(exporter)), - ) - - // create tracer and start/end span - tracer := tp.Tracer("test") - _, span := tracer.Start(ctx, "test") - span.End() - - require.Len(t, exporter.spans, 1) - require.Len(t, exporter.spans[0].Attributes(), 1) - - want := []attribute.KeyValue{attribute.String("baggage.test", "baggage value")} - require.Equal(t, want, exporter.spans[0].Attributes()) -} - -func TestSpanProcessorAppendsBaggageAttributesWithHaPrefixPredicate(t *testing.T) { - b, _ := baggage.New() - b = addEntryToBaggage(t, b, "baggage.test", "baggage value") - ctx := baggage.ContextWithBaggage(context.Background(), b) - - baggageKeyPredicate := func(m baggage.Member) bool { - return strings.HasPrefix(m.Key(), "baggage.") - } - - // create trace provider with baggage processor and test exporter - exporter := NewTestExporter() - tp := trace.NewTracerProvider( - trace.WithSpanProcessor(New(baggageKeyPredicate)), - trace.WithSpanProcessor(trace.NewSimpleSpanProcessor(exporter)), - ) - - // create tracer and start/end span - tracer := tp.Tracer("test") - _, span := tracer.Start(ctx, "test") - span.End() - - require.Len(t, exporter.spans, 1) - require.Len(t, exporter.spans[0].Attributes(), 1) - - want := []attribute.KeyValue{attribute.String("baggage.test", "baggage value")} - require.Equal(t, want, exporter.spans[0].Attributes()) -} - -func TestSpanProcessorAppendsBaggageAttributesWithRegexPredicate(t *testing.T) { - b, _ := baggage.New() - b = addEntryToBaggage(t, b, "baggage.test", "baggage value") - ctx := baggage.ContextWithBaggage(context.Background(), b) - - expr := regexp.MustCompile(`^baggage\..*`) - baggageKeyPredicate := func(m baggage.Member) bool { - return expr.MatchString(m.Key()) - } +func TestSpanProcessorAppendsBaggageAttributes(t *testing.T) { + suitcase, err := otelbaggage.New() + require.NoError(t, err) + packingCube, err := otelbaggage.NewMemberRaw("baggage.test", "baggage value") + require.NoError(t, err) + suitcase, err = suitcase.SetMember(packingCube) + require.NoError(t, err) + ctx := otelbaggage.ContextWithBaggage(context.Background(), suitcase) // create trace provider with baggage processor and test exporter exporter := NewTestExporter() tp := trace.NewTracerProvider( - trace.WithSpanProcessor(New(baggageKeyPredicate)), + trace.WithSpanProcessor(New()), trace.WithSpanProcessor(trace.NewSimpleSpanProcessor(exporter)), ) @@ -110,71 +54,9 @@ func TestSpanProcessorAppendsBaggageAttributesWithRegexPredicate(t *testing.T) { _, span := tracer.Start(ctx, "test") span.End() - require.Len(t, exporter.spans, 1) - require.Len(t, exporter.spans[0].Attributes(), 1) + assert.Len(t, exporter.spans, 1) + assert.Len(t, exporter.spans[0].Attributes(), 1) want := []attribute.KeyValue{attribute.String("baggage.test", "baggage value")} - require.Equal(t, want, exporter.spans[0].Attributes()) + assert.Equal(t, want, exporter.spans[0].Attributes()) } - -func TestOnlyAddsBaggageEntriesThatMatchPredicate(t *testing.T) { - b, _ := baggage.New() - b = addEntryToBaggage(t, b, "baggage.test", "baggage value") - b = addEntryToBaggage(t, b, "foo", "bar") - ctx := baggage.ContextWithBaggage(context.Background(), b) - - baggageKeyPredicate := func(m baggage.Member) bool { - return m.Key() == "baggage.test" - } - - // create trace provider with baggage processor and test exporter - exporter := NewTestExporter() - tp := trace.NewTracerProvider( - trace.WithSpanProcessor(New(baggageKeyPredicate)), - trace.WithSpanProcessor(trace.NewSimpleSpanProcessor(exporter)), - ) - - // create tracer and start/end span - tracer := tp.Tracer("test") - _, span := tracer.Start(ctx, "test") - span.End() - - require.Len(t, exporter.spans, 1) - require.Len(t, exporter.spans[0].Attributes(), 1) - - want := attribute.String("baggage.test", "baggage value") - require.Equal(t, want, exporter.spans[0].Attributes()[0]) -} - -func addEntryToBaggage(t *testing.T, b baggage.Baggage, key, value string) baggage.Baggage { - member, err := baggage.NewMemberRaw(key, value) - require.NoError(t, err) - b, err = b.SetMember(member) - require.NoError(t, err) - return b -} - -func TestZeroSpanProcessorNoPanic(t *testing.T) { - sp := new(SpanProcessor) - - m, err := baggage.NewMember("key", "val") - require.NoError(t, err) - b, err := baggage.New(m) - require.NoError(t, err) - - ctx := baggage.ContextWithBaggage(context.Background(), b) - roS := (tracetest.SpanStub{}).Snapshot() - rwS := rwSpan{} - assert.NotPanics(t, func() { - sp.OnStart(ctx, rwS) - sp.OnEnd(roS) - _ = sp.ForceFlush(ctx) - _ = sp.Shutdown(ctx) - }) -} - -type rwSpan struct { - trace.ReadWriteSpan -} - -func (s rwSpan) SetAttributes(kv ...attribute.KeyValue) {} diff --git a/processors/baggagecopy/doc.go b/processors/baggagecopy/doc.go new file mode 100644 index 00000000000..8548794410c --- /dev/null +++ b/processors/baggagecopy/doc.go @@ -0,0 +1,27 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package baggagecopy is an OpenTelemetry [Span Processor] that reads key/values +// stored in [Baggage] in the starting span's parent context and adds them as +// attributes to the span. +// +// Keys and values added to Baggage will appear on all subsequent child spans for +// a trace within this service and will be propagated to external services via +// propagation headers. +// If the external services also have a Baggage span processor, the keys and +// values will appear in those child spans as well. +// +// Do not put sensitive information in Baggage. +// +// # Usage +// +// Add the span processor when configuring the tracer provider. +// +// The convenience function [AllowAllBaggageKeys] is provided to +// allow all baggage keys to be copied to the span. Alternatively, you can +// provide a custom baggage key predicate to select which baggage keys you want +// to copy. +// +// [Span Processor]: https://opentelemetry.io/docs/specs/otel/trace/sdk/#span-processor +// [Baggage]: https://opentelemetry.io/docs/specs/otel/api/baggage +package baggagecopy // import "go.opentelemetry.io/contrib/processors/baggagecopy" diff --git a/processors/baggage/baggagetrace/example_test.go b/processors/baggagecopy/example_test.go similarity index 74% rename from processors/baggage/baggagetrace/example_test.go rename to processors/baggagecopy/example_test.go index a8641841667..a05d71f5be9 100644 --- a/processors/baggage/baggagetrace/example_test.go +++ b/processors/baggagecopy/example_test.go @@ -1,27 +1,27 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package baggagetrace_test +package baggagecopy_test import ( "regexp" "strings" - "go.opentelemetry.io/contrib/processors/baggage/baggagetrace" + "go.opentelemetry.io/contrib/processors/baggagecopy" "go.opentelemetry.io/otel/baggage" "go.opentelemetry.io/otel/sdk/trace" ) func ExampleNew_allKeys() { trace.NewTracerProvider( - trace.WithSpanProcessor(baggagetrace.New(baggagetrace.AllowAllMembers)), + trace.WithSpanProcessor(baggagecopy.NewSpanProcessor(baggagecopy.AllowAllMembers)), ) } func ExampleNew_keysWithPrefix() { trace.NewTracerProvider( trace.WithSpanProcessor( - baggagetrace.New( + baggagecopy.NewSpanProcessor( func(m baggage.Member) bool { return strings.HasPrefix(m.Key(), "my-key") }, @@ -34,7 +34,7 @@ func ExampleNew_keysMatchingRegex() { expr := regexp.MustCompile(`^key.+`) trace.NewTracerProvider( trace.WithSpanProcessor( - baggagetrace.New( + baggagecopy.NewSpanProcessor( func(m baggage.Member) bool { return expr.MatchString(m.Key()) }, diff --git a/processors/baggagecopy/go.mod b/processors/baggagecopy/go.mod new file mode 100644 index 00000000000..a592a67f4da --- /dev/null +++ b/processors/baggagecopy/go.mod @@ -0,0 +1,20 @@ +module go.opentelemetry.io/contrib/processors/baggagecopy + +go 1.21 + +require ( + github.com/stretchr/testify v1.9.0 + go.opentelemetry.io/otel v1.27.0 + go.opentelemetry.io/otel/sdk v1.27.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/otel/metric v1.27.0 // indirect + go.opentelemetry.io/otel/trace v1.27.0 // indirect + golang.org/x/sys v0.21.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/processors/baggagecopy/go.sum b/processors/baggagecopy/go.sum new file mode 100644 index 00000000000..2f8d594dba3 --- /dev/null +++ b/processors/baggagecopy/go.sum @@ -0,0 +1,27 @@ +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.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= +go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= +go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= +go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= +go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= +go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= +go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= +go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/processors/baggagecopy/processor.go b/processors/baggagecopy/processor.go new file mode 100644 index 00000000000..040483cad47 --- /dev/null +++ b/processors/baggagecopy/processor.go @@ -0,0 +1,63 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package baggagecopy // import "go.opentelemetry.io/contrib/processors/baggagecopy" + +import ( + "context" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/baggage" + "go.opentelemetry.io/otel/sdk/trace" +) + +// Filter returns true if the baggage member should be added to a span. +type Filter func(member baggage.Member) bool + +// AllowAllMembers allows all baggage members to be added to a span. +var AllowAllMembers Filter = func(baggage.Member) bool { return true } + +// SpanProcessor is a [trace.SpanProcessor] implementation that adds baggage +// members onto a span as attributes. +type SpanProcessor struct { + filter Filter +} + +var _ trace.SpanProcessor = (*SpanProcessor)(nil) + +// NewSpanProcessor returns a new [SpanProcessor]. +// +// The Baggage span processor duplicates onto a span the attributes found +// in Baggage in the parent context at the moment the span is started. +// The passed filter determines which baggage members are added to the span. +// +// If filter is nil, all baggage members will be added. +func NewSpanProcessor(filter Filter) *SpanProcessor { + return &SpanProcessor{ + filter: filter, + } +} + +// OnStart is called when a span is started and adds span attributes for baggage contents. +func (processor SpanProcessor) OnStart(ctx context.Context, span trace.ReadWriteSpan) { + filter := processor.filter + if filter == nil { + filter = AllowAllMembers + } + + for _, member := range baggage.FromContext(ctx).Members() { + if filter(member) { + span.SetAttributes(attribute.String(member.Key(), member.Value())) + } + } +} + +// OnEnd is called when span is finished and is a no-op for this processor. +func (processor SpanProcessor) OnEnd(s trace.ReadOnlySpan) {} + +// Shutdown is called when the SDK shuts down and is a no-op for this processor. +func (processor SpanProcessor) Shutdown(context.Context) error { return nil } + +// ForceFlush exports all ended spans to the configured Exporter that have not yet +// been exported and is a no-op for this processor. +func (processor SpanProcessor) ForceFlush(context.Context) error { return nil } diff --git a/processors/baggagecopy/processor_test.go b/processors/baggagecopy/processor_test.go new file mode 100644 index 00000000000..b6656ca4cd5 --- /dev/null +++ b/processors/baggagecopy/processor_test.go @@ -0,0 +1,180 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package baggagecopy + +import ( + "context" + "regexp" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/baggage" + "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/sdk/trace/tracetest" +) + +var _ trace.SpanExporter = &testExporter{} + +type testExporter struct { + spans []trace.ReadOnlySpan +} + +func (e *testExporter) Start(ctx context.Context) error { return nil } +func (e *testExporter) Shutdown(ctx context.Context) error { return nil } + +func (e *testExporter) ExportSpans(ctx context.Context, ss []trace.ReadOnlySpan) error { + e.spans = append(e.spans, ss...) + return nil +} + +func NewTestExporter() *testExporter { + return &testExporter{} +} + +func TestSpanProcessorAppendsAllBaggageAttributes(t *testing.T) { + b, _ := baggage.New() + b = addEntryToBaggage(t, b, "baggage.test", "baggage value") + ctx := baggage.ContextWithBaggage(context.Background(), b) + + // create trace provider with baggage processor and test exporter + exporter := NewTestExporter() + tp := trace.NewTracerProvider( + trace.WithSpanProcessor(NewSpanProcessor(AllowAllMembers)), + trace.WithSpanProcessor(trace.NewSimpleSpanProcessor(exporter)), + ) + + // create tracer and start/end span + tracer := tp.Tracer("test") + _, span := tracer.Start(ctx, "test") + span.End() + + require.Len(t, exporter.spans, 1) + require.Len(t, exporter.spans[0].Attributes(), 1) + + want := []attribute.KeyValue{attribute.String("baggage.test", "baggage value")} + require.Equal(t, want, exporter.spans[0].Attributes()) +} + +func TestSpanProcessorAppendsBaggageAttributesWithHaPrefixPredicate(t *testing.T) { + b, _ := baggage.New() + b = addEntryToBaggage(t, b, "baggage.test", "baggage value") + ctx := baggage.ContextWithBaggage(context.Background(), b) + + baggageKeyPredicate := func(m baggage.Member) bool { + return strings.HasPrefix(m.Key(), "baggage.") + } + + // create trace provider with baggage processor and test exporter + exporter := NewTestExporter() + tp := trace.NewTracerProvider( + trace.WithSpanProcessor(NewSpanProcessor(baggageKeyPredicate)), + trace.WithSpanProcessor(trace.NewSimpleSpanProcessor(exporter)), + ) + + // create tracer and start/end span + tracer := tp.Tracer("test") + _, span := tracer.Start(ctx, "test") + span.End() + + require.Len(t, exporter.spans, 1) + require.Len(t, exporter.spans[0].Attributes(), 1) + + want := []attribute.KeyValue{attribute.String("baggage.test", "baggage value")} + require.Equal(t, want, exporter.spans[0].Attributes()) +} + +func TestSpanProcessorAppendsBaggageAttributesWithRegexPredicate(t *testing.T) { + b, _ := baggage.New() + b = addEntryToBaggage(t, b, "baggage.test", "baggage value") + ctx := baggage.ContextWithBaggage(context.Background(), b) + + expr := regexp.MustCompile(`^baggage\..*`) + baggageKeyPredicate := func(m baggage.Member) bool { + return expr.MatchString(m.Key()) + } + + // create trace provider with baggage processor and test exporter + exporter := NewTestExporter() + tp := trace.NewTracerProvider( + trace.WithSpanProcessor(NewSpanProcessor(baggageKeyPredicate)), + trace.WithSpanProcessor(trace.NewSimpleSpanProcessor(exporter)), + ) + + // create tracer and start/end span + tracer := tp.Tracer("test") + _, span := tracer.Start(ctx, "test") + span.End() + + require.Len(t, exporter.spans, 1) + require.Len(t, exporter.spans[0].Attributes(), 1) + + want := []attribute.KeyValue{attribute.String("baggage.test", "baggage value")} + require.Equal(t, want, exporter.spans[0].Attributes()) +} + +func TestOnlyAddsBaggageEntriesThatMatchPredicate(t *testing.T) { + b, _ := baggage.New() + b = addEntryToBaggage(t, b, "baggage.test", "baggage value") + b = addEntryToBaggage(t, b, "foo", "bar") + ctx := baggage.ContextWithBaggage(context.Background(), b) + + baggageKeyPredicate := func(m baggage.Member) bool { + return m.Key() == "baggage.test" + } + + // create trace provider with baggage processor and test exporter + exporter := NewTestExporter() + tp := trace.NewTracerProvider( + trace.WithSpanProcessor(NewSpanProcessor(baggageKeyPredicate)), + trace.WithSpanProcessor(trace.NewSimpleSpanProcessor(exporter)), + ) + + // create tracer and start/end span + tracer := tp.Tracer("test") + _, span := tracer.Start(ctx, "test") + span.End() + + require.Len(t, exporter.spans, 1) + require.Len(t, exporter.spans[0].Attributes(), 1) + + want := attribute.String("baggage.test", "baggage value") + require.Equal(t, want, exporter.spans[0].Attributes()[0]) +} + +func addEntryToBaggage(t *testing.T, b baggage.Baggage, key, value string) baggage.Baggage { + member, err := baggage.NewMemberRaw(key, value) + require.NoError(t, err) + b, err = b.SetMember(member) + require.NoError(t, err) + return b +} + +func TestZeroSpanProcessorNoPanic(t *testing.T) { + sp := new(SpanProcessor) + + m, err := baggage.NewMember("key", "val") + require.NoError(t, err) + b, err := baggage.New(m) + require.NoError(t, err) + + ctx := baggage.ContextWithBaggage(context.Background(), b) + roS := (tracetest.SpanStub{}).Snapshot() + rwS := rwSpan{} + assert.NotPanics(t, func() { + sp.OnStart(ctx, rwS) + sp.OnEnd(roS) + _ = sp.ForceFlush(ctx) + _ = sp.Shutdown(ctx) + }) +} + +type rwSpan struct { + trace.ReadWriteSpan +} + +func (s rwSpan) SetAttributes(kv ...attribute.KeyValue) {} diff --git a/versions.yaml b/versions.yaml index 26e0311c742..b5a77a1e81c 100644 --- a/versions.yaml +++ b/versions.yaml @@ -82,6 +82,7 @@ module-sets: version: v0.0.1 modules: - go.opentelemetry.io/contrib/processors/baggage/baggagetrace + - go.opentelemetry.io/contrib/processors/baggagecopy experimental-detectors: version: v0.0.1 modules: