Skip to content

Commit

Permalink
Add autoprop package to provide configuration of propagators with use…
Browse files Browse the repository at this point in the history
…ful defaults and envar support (#2258)

* Add autoprop module

* Add example tests

* Add unit tests for registry.go

* Add unit test for NewTextMapPropagator

* Add NewTextMapPropagator unit tests

* Fix RegisterTextMapPropagator func name

* Fix lint

* Go import lint fix

* Add dependabot entry

* Add change to the changelog

* Add autoprop to versions

* Add setenv test func to support Go 1.16

* Fix spelling error in comment

* Rename registry index field to names

* Remove interface assertion

* Fix lint

* Update RegisterTextMapPropagator docs

* Remove compile time assertion and test of impl

* Upgrade to Go 1.17 as min supported version

* Use T.Setenv instead of own setenv func

* Panic on duplicate prop register

* Check errs in registry_test
  • Loading branch information
MrAlias committed Jun 2, 2022
1 parent b7910af commit 5205111
Show file tree
Hide file tree
Showing 11 changed files with 589 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .github/dependabot.yml
Expand Up @@ -532,6 +532,15 @@ updates:
schedule:
interval: weekly
day: sunday
- package-ecosystem: gomod
directory: /propagators/autoprop
labels:
- dependencies
- go
- Skip Changelog
schedule:
interval: weekly
day: sunday
- package-ecosystem: gomod
directory: /propagators/aws
labels:
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -8,6 +8,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased]

### Added

- The `go.opentelemetry.io/contrib/propagators/autoprop` package to provide configuration of propagators with useful defaults and envar support. (#2258)

## [1.7.0/0.32.0] - 2022-04-28

### Added
Expand Down
23 changes: 23 additions & 0 deletions propagators/autoprop/doc.go
@@ -0,0 +1,23 @@
// 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 autoprop provides an OpenTelemetry TextMapPropagator creation
// function. The OpenTelemetry specification states that the default
// TextMapPropagator needs to be a no-operation implementation. The
// opentelemetry-go project adheres to this requirement. However, for systems
// that perform propagation this default is not ideal. This package provides a
// TextMapPropagator with useful defaults (a combined TraceContext and Baggage
// TextMapPropagator), and supports environment overrides using the
// OTEL_PROPAGATORS environment variable.
package autoprop // import "go.opentelemetry.io/contrib/propagators/autoprop"
84 changes: 84 additions & 0 deletions propagators/autoprop/example_test.go
@@ -0,0 +1,84 @@
// 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 autoprop_test

import (
"fmt"
"os"
"sort"

"go.opentelemetry.io/contrib/propagators/autoprop"
"go.opentelemetry.io/contrib/propagators/b3"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
)

func ExampleNewTextMapPropagator() {
// NewTextMapPropagator returns a TraceContext and Baggage propagator by
// default. The response of this function can be directly registered with
// the go.opentelemetry.io/otel package.
otel.SetTextMapPropagator(autoprop.NewTextMapPropagator())

fields := otel.GetTextMapPropagator().Fields()
sort.Strings(fields)
fmt.Println(fields)
// Output: [baggage traceparent tracestate]
}

func ExampleNewTextMapPropagator_arguments() {
// NewTextMapPropagator behaves the same as the
// NewCompositeTextMapPropagator function in the
// go.opentelemetry.io/otel/propagation package when TextMapPropagator are
// passed as arguments.
fields := autoprop.NewTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
b3.New(),
).Fields()
sort.Strings(fields)
fmt.Println(fields)
// Output: [baggage traceparent tracestate x-b3-flags x-b3-sampled x-b3-spanid x-b3-traceid]
}

func ExampleNewTextMapPropagator_environment() {
// Propagators set for the OTEL_PROPAGATORS environment variable take
// precedence and will override any arguments passed to
// NewTextMapPropagator.
_ = os.Setenv("OTEL_PROPAGATORS", "b3,baggage")

// Returns only a B3 and Baggage TextMapPropagator (i.e. does not include
// TraceContext).
fields := autoprop.NewTextMapPropagator(propagation.TraceContext{}).Fields()
sort.Strings(fields)
fmt.Println(fields)
// Output: [baggage x-b3-flags x-b3-sampled x-b3-spanid x-b3-traceid]
}

type myTextMapPropagator struct{ propagation.TextMapPropagator }

func (myTextMapPropagator) Fields() []string {
return []string{"my-header-val"}
}

func ExampleRegisterTextMapPropagator() {
// To use your own or a 3rd-party exporter via the OTEL_PROPAGATORS
// environment variable, it needs to be registered prior to calling
// NewTextMapPropagator.
autoprop.RegisterTextMapPropagator("custom-prop", myTextMapPropagator{})

_ = os.Setenv("OTEL_PROPAGATORS", "custom-prop")
fmt.Println(autoprop.NewTextMapPropagator().Fields())
// Output: [my-header-val]
}
25 changes: 25 additions & 0 deletions propagators/autoprop/go.mod
@@ -0,0 +1,25 @@
module go.opentelemetry.io/contrib/propagators/autoprop

go 1.17

require (
github.com/stretchr/testify v1.7.1
go.opentelemetry.io/contrib/propagators/aws v1.7.0
go.opentelemetry.io/contrib/propagators/b3 v1.7.0
go.opentelemetry.io/contrib/propagators/jaeger v1.7.0
go.opentelemetry.io/contrib/propagators/ot v1.7.0
go.opentelemetry.io/otel v1.7.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.opentelemetry.io/otel/sdk v1.7.0 // indirect
go.opentelemetry.io/otel/trace v1.7.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)
43 changes: 43 additions & 0 deletions propagators/autoprop/go.sum
@@ -0,0 +1,43 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
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.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
go.opentelemetry.io/contrib/propagators/aws v1.7.0 h1:hzLtX+K4YhsrBabA35uBYxCENb5rS/9Z9X8MToTlA3k=
go.opentelemetry.io/contrib/propagators/aws v1.7.0/go.mod h1:h/ql5T6e1XLRFplWNNdzLHp8eb0dkBu+xYOCYxerh0Q=
go.opentelemetry.io/contrib/propagators/b3 v1.7.0 h1:oRAenUhj+GFttfIp3gj7HYVzBhPOHgq/dWPDSmLCXSY=
go.opentelemetry.io/contrib/propagators/b3 v1.7.0/go.mod h1:gXx7AhL4xXCF42gpm9dQvdohoDa2qeyEx4eIIxqK+h4=
go.opentelemetry.io/contrib/propagators/jaeger v1.7.0 h1:x2mXKtONfOJFfNFSx4QXFx1fms6bKIPVvWvgdiaPdRI=
go.opentelemetry.io/contrib/propagators/jaeger v1.7.0/go.mod h1:kt2lNImfxV6dETRsDCENd6jU6G0mPRS+P0qlNuvtkTE=
go.opentelemetry.io/contrib/propagators/ot v1.7.0 h1:KPPToDRxyY/HI3qD4RqwWRbaQ65RIpF8uKWDqWkFHDA=
go.opentelemetry.io/contrib/propagators/ot v1.7.0/go.mod h1:5qxBZR730yb71uXc3bazxt2Si8o8LQK3iJTnSLca/BU=
go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM=
go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
go.opentelemetry.io/otel/sdk v1.7.0 h1:4OmStpcKVOfvDOgCt7UriAPtKolwIhxpnSNI/yK+1B0=
go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU=
go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o=
go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
129 changes: 129 additions & 0 deletions propagators/autoprop/propagator.go
@@ -0,0 +1,129 @@
// 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 autoprop // import "go.opentelemetry.io/contrib/propagators/autoprop"

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

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
)

// otelPropagatorsEnvKey is the environment variable name identifying
// propagators to use.
const otelPropagatorsEnvKey = "OTEL_PROPAGATORS"

// NewTextMapPropagator returns a new TextMapPropagator composited by props or
// one defined by the OTEL_PROPAGATORS environment variable. The
// TextMapPropagator defined by OTEL_PROPAGATORS, if set, will take precedence
// to the once composited by props.
//
// The propagators supported with the OTEL_PROPAGATORS environment variable by
// default are: tracecontext, baggage, b3, b3multi, jaeger, xray, ottrace, and
// none. Each of these values, and their combination, are supported in
// conformance with the OpenTelemetry specification. See
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#general-sdk-configuration
// for more information.
//
// The supported environment variable propagators can be extended to include
// custom 3rd-party TextMapPropagator. See the RegisterTextMapPropagator
// function for more information.
//
// If OTEL_PROPAGATORS is not defined and props is no provided, the returned
// TextMapPropagator will be a composite of the TraceContext and Baggage
// propagators.
func NewTextMapPropagator(props ...propagation.TextMapPropagator) propagation.TextMapPropagator {
// Environment variable defined propagator has precedence over arguments.
envProp, err := parseEnv()
if err != nil {
// Communicate to the user their supplied value will not be used.
otel.Handle(err)
}
if envProp != nil {
return envProp
}

switch len(props) {
case 0:
// Default to TraceContext and Baggage.
return propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{}, propagation.Baggage{},
)
case 1:
// Do not add overhead with a composite propagator wrapping a single
// propagator, return it directly.
return props[0]
default:
return propagation.NewCompositeTextMapPropagator(props...)
}
}

// errUnknownPropagator is returned when an unknown propagator name is used in
// the OTEL_PROPAGATORS environment variable.
var errUnknownPropagator = errors.New("unknown propagator")

// parseEnv returns the composite TextMapPropagators defined by the
// OTEL_PROPAGATORS environment variable. A nil TextMapPropagator is returned
// if no propagator is defined for the environment variable. A no-op
// TextMapPropagator will be returned if "none" is defined anywhere in the
// environment variable.
func parseEnv() (propagation.TextMapPropagator, error) {
propStrs, defined := os.LookupEnv(otelPropagatorsEnvKey)
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
}
}

0 comments on commit 5205111

Please sign in to comment.