diff --git a/CHANGELOG.md b/CHANGELOG.md index cde625eb23a..b3b0e344a51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +### Added + +- The `GetTextMapPropagator` function to `go.opentelemetry.io/contrib/propagators/autoprop`. + This function is used to return a composite `TextMapPropagator` from registered names (instead of having to specify with an environment variable). (#2593) + ### Changed - Upgraded all `semconv` package use to `v1.12.0`. (#2589) diff --git a/propagators/autoprop/example_test.go b/propagators/autoprop/example_test.go index f98ca9c022f..699372215d5 100644 --- a/propagators/autoprop/example_test.go +++ b/propagators/autoprop/example_test.go @@ -82,3 +82,31 @@ func ExampleRegisterTextMapPropagator() { fmt.Println(autoprop.NewTextMapPropagator().Fields()) // Output: [my-header-val] } + +func ExampleGetTextMapPropagator() { + prop, err := autoprop.TextMapPropagator("b3", "baggage") + if err != nil { + // Handle error appropriately. + panic(err) + } + + fields := prop.Fields() + sort.Strings(fields) + fmt.Println(fields) + // Output: [baggage x-b3-flags x-b3-sampled x-b3-spanid x-b3-traceid] +} + +func ExampleGetTextMapPropagator_custom() { + // To use your own or a 3rd-party exporter it needs to be registered prior + // to calling GetTextMapPropagator. + autoprop.RegisterTextMapPropagator("custom-get-prop", myTextMapPropagator{}) + + prop, err := autoprop.TextMapPropagator("custom-get-prop") + if err != nil { + // Handle error appropriately. + panic(err) + } + + fmt.Println(prop.Fields()) + // Output: [my-header-val] +} diff --git a/propagators/autoprop/propagator.go b/propagators/autoprop/propagator.go index 1fe17781227..29ac8580a64 100644 --- a/propagators/autoprop/propagator.go +++ b/propagators/autoprop/propagator.go @@ -16,7 +16,6 @@ package autoprop // import "go.opentelemetry.io/contrib/propagators/autoprop" import ( "errors" - "fmt" "os" "strings" @@ -87,43 +86,5 @@ func parseEnv() (propagation.TextMapPropagator, error) { if !defined { return nil, nil } - - const sep = "," - - var ( - props []propagation.TextMapPropagator - unknown []string - ) - - for _, pStr := range strings.Split(propStrs, sep) { - if pStr == none { - // If "none" is passed in combination with any other propagator, - // the result still needs to be a no-op propagator. Therefore, - // short-circuit here. - return propagation.NewCompositeTextMapPropagator(), nil - } - - p, ok := envRegistry.load(pStr) - if !ok { - unknown = append(unknown, pStr) - continue - } - props = append(props, p) - } - - var err error - if len(unknown) > 0 { - joined := strings.Join(unknown, sep) - err = fmt.Errorf("%w: %s", errUnknownPropagator, joined) - } - - switch len(props) { - case 0: - return nil, err - case 1: - // Do not return a composite of a single propagator. - return props[0], err - default: - return propagation.NewCompositeTextMapPropagator(props...), err - } + return TextMapPropagator(strings.Split(propStrs, ",")...) } diff --git a/propagators/autoprop/registry.go b/propagators/autoprop/registry.go index 499a7da4b75..a831773f3d0 100644 --- a/propagators/autoprop/registry.go +++ b/propagators/autoprop/registry.go @@ -17,6 +17,7 @@ package autoprop // import "go.opentelemetry.io/contrib/propagators/autoprop" import ( "errors" "fmt" + "strings" "sync" "go.opentelemetry.io/contrib/propagators/aws/xray" @@ -30,9 +31,9 @@ import ( // configured. const none = "none" -// envRegistry is the index of all supported environment variable -// values and their mapping to a TextMapPropagator. -var envRegistry = ®istry{ +// propagators is the registry of TextMapPropagators registered with this +// package. It includes all the OpenTelemetry defaults at startup. +var propagators = ®istry{ names: map[string]propagation.TextMapPropagator{ // W3C Trace Context. "tracecontext": propagation.TraceContext{}, @@ -102,7 +103,7 @@ func (r *registry) drop(key string) { // will panic if name has already been registered or is a default // (tracecontext, baggage, b3, b3multi, jaeger, xray, or ottrace). func RegisterTextMapPropagator(name string, p propagation.TextMapPropagator) { - if err := envRegistry.store(name, p); err != nil { + if err := propagators.store(name, p); err != nil { // envRegistry.store will return errDupReg if name is already // registered. Panic here so the user is made aware of the duplicate // registration, which could be done by malicious code trying to @@ -115,3 +116,54 @@ func RegisterTextMapPropagator(name string, p propagation.TextMapPropagator) { panic(err) } } + +// TextMapPropagator returns a TextMapPropagator composed from the +// passed names of registered TextMapPropagators. Each name must match an +// already registered TextMapPropagator (see the RegisterTextMapPropagator +// function for more information) or a default (tracecontext, baggage, b3, +// b3multi, jaeger, xray, or ottrace). +// +// If "none" is included in the arguments, or no names are provided, the +// returned TextMapPropagator will be a no-operation implementation. +// +// An error is returned for any un-registered names. The remaining, known, +// names will be used to compose a TextMapPropagator that is returned with the +// error. +func TextMapPropagator(names ...string) (propagation.TextMapPropagator, error) { + var ( + props []propagation.TextMapPropagator + unknown []string + ) + + for _, name := range names { + if name == none { + // If "none" is passed in combination with any other propagator, + // the result still needs to be a no-op propagator. Therefore, + // short-circuit here. + return propagation.NewCompositeTextMapPropagator(), nil + } + + p, ok := propagators.load(name) + if !ok { + unknown = append(unknown, name) + continue + } + props = append(props, p) + } + + var err error + if len(unknown) > 0 { + joined := strings.Join(unknown, ",") + err = fmt.Errorf("%w: %s", errUnknownPropagator, joined) + } + + switch len(props) { + case 0: + return nil, err + case 1: + // Do not return a composite of a single propagator. + return props[0], err + default: + return propagation.NewCompositeTextMapPropagator(props...), err + } +} diff --git a/propagators/autoprop/registry_test.go b/propagators/autoprop/registry_test.go index 7f284b6a27a..a08606244c5 100644 --- a/propagators/autoprop/registry_test.go +++ b/propagators/autoprop/registry_test.go @@ -68,9 +68,9 @@ func TestRegistryConcurrentSafe(t *testing.T) { func TestRegisterTextMapPropagator(t *testing.T) { const propName = "custom" RegisterTextMapPropagator(propName, noop) - t.Cleanup(func() { envRegistry.drop(propName) }) + t.Cleanup(func() { propagators.drop(propName) }) - v, ok := envRegistry.load(propName) + v, ok := propagators.load(propName) assert.True(t, ok, "missing propagator in envRegistry") assert.Equal(t, noop, v, "wrong propagator stored") } @@ -78,7 +78,7 @@ func TestRegisterTextMapPropagator(t *testing.T) { func TestDuplicateRegisterTextMapPropagatorPanics(t *testing.T) { const propName = "custom" RegisterTextMapPropagator(propName, noop) - t.Cleanup(func() { envRegistry.drop(propName) }) + t.Cleanup(func() { propagators.drop(propName) }) errString := fmt.Sprintf("%s: %q", errDupReg, propName) assert.PanicsWithError(t, errString, func() {