Skip to content

Commit

Permalink
Add TextMapPropagator function to autoprop (#2593)
Browse files Browse the repository at this point in the history
* Rename envRegistry to propagators

* Add GetTextMapPropagator stub

* Implement the GetTextMapPropagator

Refactor the parseEnv function logic that already performed this
function into GetTextMapPropagator, and use that in parseEnv instead.

* Add example tests for the GetTextMapPropagator func

* Add changes to changelog

* Add PR number to changelog

* Rename GetTextMapPropagator to TextMapPropagator
  • Loading branch information
MrAlias committed Jul 25, 2022
1 parent 0be7156 commit 2116f96
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 47 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Expand Up @@ -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)
Expand Down
28 changes: 28 additions & 0 deletions propagators/autoprop/example_test.go
Expand Up @@ -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]
}
41 changes: 1 addition & 40 deletions propagators/autoprop/propagator.go
Expand Up @@ -16,7 +16,6 @@ package autoprop // import "go.opentelemetry.io/contrib/propagators/autoprop"

import (
"errors"
"fmt"
"os"
"strings"

Expand Down Expand Up @@ -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, ",")...)
}
60 changes: 56 additions & 4 deletions propagators/autoprop/registry.go
Expand Up @@ -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"
Expand All @@ -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 = &registry{
// propagators is the registry of TextMapPropagators registered with this
// package. It includes all the OpenTelemetry defaults at startup.
var propagators = &registry{
names: map[string]propagation.TextMapPropagator{
// W3C Trace Context.
"tracecontext": propagation.TraceContext{},
Expand Down Expand Up @@ -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
Expand All @@ -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
}
}
6 changes: 3 additions & 3 deletions propagators/autoprop/registry_test.go
Expand Up @@ -68,17 +68,17 @@ 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")
}

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() {
Expand Down

0 comments on commit 2116f96

Please sign in to comment.