Skip to content

Commit

Permalink
Move propagators out of API and into their own package (#1118)
Browse files Browse the repository at this point in the history
* Add new propagators package

* Move B3 propagator to propagators

Update all `api/trace` dependencies in the propagator.

Export the isDeferred and isDebug methods of the SpanContext. These are
needed by the B3 propagator to track trace state.

* Move W3C trace context propagator to propagators

* Update package docs with supported encodings

* Move unified propagators code to own file

* Update b3 exported documentation

* Update trace_context exported documentation

* Add code examples for B3 propagator

* Add TraceContext example code

* Remove internal package

Move common testing declarations to the propagators_test.go file.

* Add changes to Changelog

* Add test to check default propagators
  • Loading branch information
MrAlias authored Sep 8, 2020
1 parent bc1a592 commit f1dad21
Show file tree
Hide file tree
Showing 16 changed files with 325 additions and 188 deletions.
5 changes: 2 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Replace the `LinkedTo` span option in `go.opentelemetry.io/otel/api/trace` with `WithLinks`.
This is be more consistent with our other option patterns, i.e. passing the item to be configured directly instead of its component parts, and provides a cleaner function signature. (#1108)
- The `go.opentelemetry.io/otel/api/trace` `TracerOption` was changed to an interface to conform to project option conventions. (#1109)
- Move the `B3` and `TraceContext` from within the `go.opentelemetry.io/otel/api/trace` package to their own `go.opentelemetry.io/otel/propagators` package.
This removal of the propagators is reflective of the OpenTelemetry specification for these propagators as well as cleans up the `go.opentelemetry.io/otel/api/trace` API. (#1118)
- Rename Jaeger tags used for instrumentation library information to reflect changes in OpenTelemetry specification. (#1119)

### Changed

- Rename `ProbabilitySampler` to `TraceIDRatioBased` and change semantics to ignore parent span sampling status. (#1115)

## [0.11.0] - 2020-08-24
Expand Down
3 changes: 2 additions & 1 deletion api/global/internal/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/api/propagation"
"go.opentelemetry.io/otel/api/trace"
"go.opentelemetry.io/otel/propagators"
)

type (
Expand Down Expand Up @@ -122,7 +123,7 @@ func defaultPropagatorsValue() *atomic.Value {
// getDefaultPropagators returns a default Propagators, configured
// with W3C trace and correlation context propagation.
func getDefaultPropagators() propagation.Propagators {
tcPropagator := trace.TraceContext{}
tcPropagator := propagators.TraceContext{}
ccPropagator := correlation.CorrelationContext{}
return propagation.New(
propagation.WithExtractors(tcPropagator, ccPropagator),
Expand Down
8 changes: 4 additions & 4 deletions api/trace/span_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,13 @@ func (sc SpanContext) HasSpanID() bool {
return sc.SpanID.IsValid()
}

// isDeferred returns if the deferred bit is set in the trace flags.
func (sc SpanContext) isDeferred() bool {
// IsDeferred returns if the deferred bit is set in the trace flags.
func (sc SpanContext) IsDeferred() bool {
return sc.TraceFlags&FlagsDeferred == FlagsDeferred
}

// isDebug returns if the debug bit is set in the trace flags.
func (sc SpanContext) isDebug() bool {
// IsDebug returns if the debug bit is set in the trace flags.
func (sc SpanContext) IsDebug() bool {
return sc.TraceFlags&FlagsDebug == FlagsDebug
}

Expand Down
78 changes: 39 additions & 39 deletions api/trace/b3_propagator.go → propagators/b3.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package trace
package propagators

import (
"context"
"errors"
"strings"

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

const (
Expand All @@ -43,7 +44,7 @@ const (
)

var (
empty = EmptySpanContext()
empty = trace.EmptySpanContext()

errInvalidSampledByte = errors.New("invalid B3 Sampled found")
errInvalidSampledHeader = errors.New("invalid B3 Sampled header found")
Expand Down Expand Up @@ -78,40 +79,36 @@ const (
B3Unspecified B3Encoding = 0
)

// B3 propagator serializes SpanContext to/from B3 Headers.
// This propagator supports both versions of B3 headers,
// 1. Single Header:
// b3: {TraceId}-{SpanId}-{SamplingState}-{ParentSpanId}
// 2. Multiple Headers:
// x-b3-traceid: {TraceId}
// x-b3-parentspanid: {ParentSpanId}
// x-b3-spanid: {SpanId}
// x-b3-sampled: {SamplingState}
// x-b3-flags: {DebugFlag}
// B3 is a propagator that supports the B3 HTTP encoding.
//
// Both single (https://github.com/openzipkin/b3-propagation#single-header)
// and multiple (https://github.com/openzipkin/b3-propagation#single-header)
// header encoding are supported.
type B3 struct {
// InjectEncoding are the B3 encodings used when injecting trace
// information. If no encoding is specified (i.e. `B3Unspecified`)
// `B3MultipleHeader` will be used as the default.
// `B3MultipleHeader` will be used as the default as it is the most
// backwards compatible.
InjectEncoding B3Encoding
}

var _ propagation.HTTPPropagator = B3{}

// Inject injects a context into the supplier as B3 headers.
// Inject injects a context into the supplier as B3 HTTP headers.
// The parent span ID is omitted because it is not tracked in the
// SpanContext.
func (b3 B3) Inject(ctx context.Context, supplier propagation.HTTPSupplier) {
sc := SpanFromContext(ctx).SpanContext()
sc := trace.SpanFromContext(ctx).SpanContext()

if b3.InjectEncoding.supports(B3SingleHeader) {
header := []string{}
if sc.TraceID.IsValid() && sc.SpanID.IsValid() {
header = append(header, sc.TraceID.String(), sc.SpanID.String())
}

if sc.isDebug() {
if sc.IsDebug() {
header = append(header, "d")
} else if !sc.isDeferred() {
} else if !sc.IsDeferred() {
if sc.IsSampled() {
header = append(header, "1")
} else {
Expand All @@ -128,10 +125,10 @@ func (b3 B3) Inject(ctx context.Context, supplier propagation.HTTPSupplier) {
supplier.Set(b3SpanIDHeader, sc.SpanID.String())
}

if sc.isDebug() {
if sc.IsDebug() {
// Since Debug implies deferred, don't also send "X-B3-Sampled".
supplier.Set(b3DebugFlagHeader, "1")
} else if !sc.isDeferred() {
} else if !sc.IsDeferred() {
if sc.IsSampled() {
supplier.Set(b3SampledHeader, "1")
} else {
Expand All @@ -141,18 +138,19 @@ func (b3 B3) Inject(ctx context.Context, supplier propagation.HTTPSupplier) {
}
}

// Extract extracts a context from the supplier if it contains B3 headers.
// Extract extracts a context from the supplier if it contains B3 HTTP
// headers.
func (b3 B3) Extract(ctx context.Context, supplier propagation.HTTPSupplier) context.Context {
var (
sc SpanContext
sc trace.SpanContext
err error
)

// Default to Single Header if a valid value exists.
if h := supplier.Get(b3ContextHeader); h != "" {
sc, err = extractSingle(h)
if err == nil && sc.IsValid() {
return ContextWithRemoteSpanContext(ctx, sc)
return trace.ContextWithRemoteSpanContext(ctx, sc)
}
// The Single Header value was invalid, fallback to Multiple Header.
}
Expand All @@ -168,9 +166,11 @@ func (b3 B3) Extract(ctx context.Context, supplier propagation.HTTPSupplier) con
if err != nil || !sc.IsValid() {
return ctx
}
return ContextWithRemoteSpanContext(ctx, sc)
return trace.ContextWithRemoteSpanContext(ctx, sc)
}

// GetAllKeys returns the HTTP header names this propagator will use when
// injecting.
func (b3 B3) GetAllKeys() []string {
header := []string{}
if b3.InjectEncoding.supports(B3SingleHeader) {
Expand All @@ -186,11 +186,11 @@ func (b3 B3) GetAllKeys() []string {
// Multiple header. It is based on the implementation found here:
// https://github.com/openzipkin/zipkin-go/blob/v0.2.2/propagation/b3/spancontext.go
// and adapted to support a SpanContext.
func extractMultiple(traceID, spanID, parentSpanID, sampled, flags string) (SpanContext, error) {
func extractMultiple(traceID, spanID, parentSpanID, sampled, flags string) (trace.SpanContext, error) {
var (
err error
requiredCount int
sc = SpanContext{}
sc = trace.SpanContext{}
)

// correct values for an existing sampled header are "0" and "1".
Expand All @@ -200,17 +200,17 @@ func extractMultiple(traceID, spanID, parentSpanID, sampled, flags string) (Span
case "0", "false":
// Zero value for TraceFlags sample bit is unset.
case "1", "true":
sc.TraceFlags = FlagsSampled
sc.TraceFlags = trace.FlagsSampled
case "":
sc.TraceFlags = FlagsDeferred
sc.TraceFlags = trace.FlagsDeferred
default:
return empty, errInvalidSampledHeader
}

// The only accepted value for Flags is "1". This will set Debug to
// true. All other values and omission of header will be ignored.
if flags == "1" {
sc.TraceFlags |= FlagsDebug
sc.TraceFlags |= trace.FlagsDebug
}

if traceID != "" {
Expand All @@ -220,14 +220,14 @@ func extractMultiple(traceID, spanID, parentSpanID, sampled, flags string) (Span
// Pad 64-bit trace IDs.
id = b3TraceIDPadding + traceID
}
if sc.TraceID, err = IDFromHex(id); err != nil {
if sc.TraceID, err = trace.IDFromHex(id); err != nil {
return empty, errInvalidTraceIDHeader
}
}

if spanID != "" {
requiredCount++
if sc.SpanID, err = SpanIDFromHex(spanID); err != nil {
if sc.SpanID, err = trace.SpanIDFromHex(spanID); err != nil {
return empty, errInvalidSpanIDHeader
}
}
Expand All @@ -241,7 +241,7 @@ func extractMultiple(traceID, spanID, parentSpanID, sampled, flags string) (Span
return empty, errInvalidScopeParent
}
// Validate parent span ID but we do not use it so do not save it.
if _, err = SpanIDFromHex(parentSpanID); err != nil {
if _, err = trace.SpanIDFromHex(parentSpanID); err != nil {
return empty, errInvalidParentSpanIDHeader
}
}
Expand All @@ -253,13 +253,13 @@ func extractMultiple(traceID, spanID, parentSpanID, sampled, flags string) (Span
// Single header. It is based on the implementation found here:
// https://github.com/openzipkin/zipkin-go/blob/v0.2.2/propagation/b3/spancontext.go
// and adapted to support a SpanContext.
func extractSingle(contextHeader string) (SpanContext, error) {
func extractSingle(contextHeader string) (trace.SpanContext, error) {
if contextHeader == "" {
return empty, errEmptyContext
}

var (
sc = SpanContext{}
sc = trace.SpanContext{}
sampling string
)

Expand All @@ -285,13 +285,13 @@ func extractSingle(contextHeader string) (SpanContext, error) {
return empty, errInvalidTraceIDValue
}
var err error
sc.TraceID, err = IDFromHex(traceID)
sc.TraceID, err = trace.IDFromHex(traceID)
if err != nil {
return empty, errInvalidTraceIDValue
}
pos += separatorWidth // {traceID}-

sc.SpanID, err = SpanIDFromHex(contextHeader[pos : pos+spanIDWidth])
sc.SpanID, err = trace.SpanIDFromHex(contextHeader[pos : pos+spanIDWidth])
if err != nil {
return empty, errInvalidSpanIDValue
}
Expand All @@ -315,7 +315,7 @@ func extractSingle(contextHeader string) (SpanContext, error) {

// Validate parent span ID but we do not use it so do not
// save it.
_, err = SpanIDFromHex(contextHeader[pos:])
_, err = trace.SpanIDFromHex(contextHeader[pos:])
if err != nil {
return empty, errInvalidParentSpanIDValue
}
Expand All @@ -328,11 +328,11 @@ func extractSingle(contextHeader string) (SpanContext, error) {
}
switch sampling {
case "":
sc.TraceFlags = FlagsDeferred
sc.TraceFlags = trace.FlagsDeferred
case "d":
sc.TraceFlags = FlagsDebug
sc.TraceFlags = trace.FlagsDebug
case "1":
sc.TraceFlags = FlagsSampled
sc.TraceFlags = trace.FlagsSampled
case "0":
// Zero value for TraceFlags sample bit is unset.
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package tracetest_test
package propagators_test

import (
"context"
"net/http"
"testing"

"go.opentelemetry.io/otel/api/trace"
"go.opentelemetry.io/otel/propagators"
)

func BenchmarkExtractB3(b *testing.B) {
Expand All @@ -38,7 +39,7 @@ func BenchmarkExtractB3(b *testing.B) {
}

for _, tg := range testGroup {
propagator := trace.B3{}
propagator := propagators.B3{}
for _, tt := range tg.tests {
traceBenchmark(tg.name+"/"+tt.name, b, func(b *testing.B) {
ctx := context.Background()
Expand Down Expand Up @@ -73,7 +74,7 @@ func BenchmarkInjectB3(b *testing.B) {

for _, tg := range testGroup {
for _, tt := range tg.tests {
propagator := trace.B3{InjectEncoding: tt.encoding}
propagator := propagators.B3{InjectEncoding: tt.encoding}
traceBenchmark(tg.name+"/"+tt.name, b, func(b *testing.B) {
req, _ := http.NewRequest("GET", "http://example.com", nil)
ctx := trace.ContextWithSpan(
Expand Down
Loading

0 comments on commit f1dad21

Please sign in to comment.