Move B3 propagator from main repo to contrib repo (#344)
* Move B3 context propagator from go.opentelemetry.io/otel repo. * Add to dependabot.yml * Update README. Add doc.go * Updated CHANGELOG to reflect addition of B3 propagator. Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									22ec41426a
								
							
						
					
					
						commit
						c5771c8dda
					
				| 
						 | 
				
			
			@ -309,6 +309,15 @@ updates:
 | 
			
		|||
      day: "sunday"
 | 
			
		||||
  -
 | 
			
		||||
    package-ecosystem: "gomod"
 | 
			
		||||
    directory: "/propagators"
 | 
			
		||||
    labels:
 | 
			
		||||
      - dependencies
 | 
			
		||||
      - go
 | 
			
		||||
      - "Skip Changelog"
 | 
			
		||||
    schedule:
 | 
			
		||||
      interval: "weekly"
 | 
			
		||||
      day: "sunday"
 | 
			
		||||
  - package-ecosystem: "gomod"
 | 
			
		||||
    directory: "/tools"
 | 
			
		||||
    labels:
 | 
			
		||||
      - dependencies
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 | 
			
		|||
- Benchmark tests for the gRPC instrumentation. (#296)
 | 
			
		||||
- Integration testing for the gRPC instrumentation. (#297)
 | 
			
		||||
- Allow custom labels to be added to net/http metrics. (#306)
 | 
			
		||||
- Added B3 propagator, moving it out of open.telemetry.io/otel repo. (#344)
 | 
			
		||||
 | 
			
		||||
### Changed
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,8 @@ Collection of 3rd-party instrumentation and exporters for [OpenTelemetry-Go](htt
 | 
			
		|||
 | 
			
		||||
- [Instrumentation](./instrumentation/): Packages providing OpenTelemetry instrumentation for 3rd-party libraries.
 | 
			
		||||
- [Exporters](./exporters/): Packages providing OpenTelemetry exporters for 3rd-party telemetry systems.
 | 
			
		||||
- [Propagators](./propagators/): Packages providing Opentelemetry context propagators for 3rd-party propagation formats.
 | 
			
		||||
- [Detectors](./detectors/): Packages providing OpenTelemetry resource detectors for 3rd-party cloud computing environments.
 | 
			
		||||
 | 
			
		||||
## Contributing
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,103 @@
 | 
			
		|||
// 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 b3_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"go.opentelemetry.io/contrib/propagators/b3"
 | 
			
		||||
	"go.opentelemetry.io/otel/api/trace"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func BenchmarkExtractB3(b *testing.B) {
 | 
			
		||||
	testGroup := []struct {
 | 
			
		||||
		name  string
 | 
			
		||||
		tests []extractTest
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:  "valid headers",
 | 
			
		||||
			tests: extractHeaders,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:  "invalid headers",
 | 
			
		||||
			tests: extractInvalidHeaders,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tg := range testGroup {
 | 
			
		||||
		propagator := b3.B3{}
 | 
			
		||||
		for _, tt := range tg.tests {
 | 
			
		||||
			traceBenchmark(tg.name+"/"+tt.name, b, func(b *testing.B) {
 | 
			
		||||
				ctx := context.Background()
 | 
			
		||||
				req, _ := http.NewRequest("GET", "http://example.com", nil)
 | 
			
		||||
				for h, v := range tt.headers {
 | 
			
		||||
					req.Header.Set(h, v)
 | 
			
		||||
				}
 | 
			
		||||
				b.ReportAllocs()
 | 
			
		||||
				b.ResetTimer()
 | 
			
		||||
				for i := 0; i < b.N; i++ {
 | 
			
		||||
					_ = propagator.Extract(ctx, req.Header)
 | 
			
		||||
				}
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func BenchmarkInjectB3(b *testing.B) {
 | 
			
		||||
	testGroup := []struct {
 | 
			
		||||
		name  string
 | 
			
		||||
		tests []injectTest
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:  "valid headers",
 | 
			
		||||
			tests: injectHeader,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:  "invalid headers",
 | 
			
		||||
			tests: injectInvalidHeader,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tg := range testGroup {
 | 
			
		||||
		for _, tt := range tg.tests {
 | 
			
		||||
			propagator := b3.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(
 | 
			
		||||
					context.Background(),
 | 
			
		||||
					testSpan{sc: tt.sc},
 | 
			
		||||
				)
 | 
			
		||||
				b.ReportAllocs()
 | 
			
		||||
				b.ResetTimer()
 | 
			
		||||
				for i := 0; i < b.N; i++ {
 | 
			
		||||
					propagator.Inject(ctx, req.Header)
 | 
			
		||||
				}
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func traceBenchmark(name string, b *testing.B, fn func(*testing.B)) {
 | 
			
		||||
	b.Run(name, func(b *testing.B) {
 | 
			
		||||
		b.ReportAllocs()
 | 
			
		||||
		fn(b)
 | 
			
		||||
	})
 | 
			
		||||
	b.Run(name, func(b *testing.B) {
 | 
			
		||||
		b.ReportAllocs()
 | 
			
		||||
		fn(b)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -0,0 +1,42 @@
 | 
			
		|||
// 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 b3_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"go.opentelemetry.io/contrib/propagators/b3"
 | 
			
		||||
	"go.opentelemetry.io/otel/api/global"
 | 
			
		||||
	"go.opentelemetry.io/otel/api/propagation"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func ExampleB3() {
 | 
			
		||||
	b3 := b3.B3{}
 | 
			
		||||
	// Register the B3 propagator globally.
 | 
			
		||||
	global.SetPropagators(propagation.New(
 | 
			
		||||
		propagation.WithExtractors(b3),
 | 
			
		||||
		propagation.WithInjectors(b3),
 | 
			
		||||
	))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ExampleB3_injectEncoding() {
 | 
			
		||||
	// Create a B3 propagator configured to inject context with both multiple
 | 
			
		||||
	// and single header B3 HTTP encoding.
 | 
			
		||||
	b3 := b3.B3{
 | 
			
		||||
		InjectEncoding: b3.B3MultipleHeader | b3.B3SingleHeader,
 | 
			
		||||
	}
 | 
			
		||||
	global.SetPropagators(propagation.New(
 | 
			
		||||
		propagation.WithExtractors(b3),
 | 
			
		||||
		propagation.WithInjectors(b3),
 | 
			
		||||
	))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,178 @@
 | 
			
		|||
// 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 b3_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/go-cmp/cmp"
 | 
			
		||||
 | 
			
		||||
	mocktracer "go.opentelemetry.io/contrib/internal/trace"
 | 
			
		||||
	"go.opentelemetry.io/contrib/propagators/b3"
 | 
			
		||||
	"go.opentelemetry.io/otel/api/propagation"
 | 
			
		||||
	"go.opentelemetry.io/otel/api/trace"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	mockTracer  = mocktracer.NewTracer("")
 | 
			
		||||
	_, mockSpan = mockTracer.Start(context.Background(), "")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestExtractB3(t *testing.T) {
 | 
			
		||||
	testGroup := []struct {
 | 
			
		||||
		name  string
 | 
			
		||||
		tests []extractTest
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:  "valid extract headers",
 | 
			
		||||
			tests: extractHeaders,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:  "invalid extract headers",
 | 
			
		||||
			tests: extractInvalidHeaders,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tg := range testGroup {
 | 
			
		||||
		propagator := b3.B3{}
 | 
			
		||||
		props := propagation.New(propagation.WithExtractors(propagator))
 | 
			
		||||
 | 
			
		||||
		for _, tt := range tg.tests {
 | 
			
		||||
			t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
				req, _ := http.NewRequest("GET", "http://example.com", nil)
 | 
			
		||||
				for h, v := range tt.headers {
 | 
			
		||||
					req.Header.Set(h, v)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				ctx := context.Background()
 | 
			
		||||
				ctx = propagation.ExtractHTTP(ctx, props, req.Header)
 | 
			
		||||
				gotSc := trace.RemoteSpanContextFromContext(ctx)
 | 
			
		||||
				if diff := cmp.Diff(gotSc, tt.wantSc); diff != "" {
 | 
			
		||||
					t.Errorf("%s: %s: -got +want %s", tg.name, tt.name, diff)
 | 
			
		||||
				}
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type testSpan struct {
 | 
			
		||||
	trace.Span
 | 
			
		||||
	sc trace.SpanContext
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s testSpan) SpanContext() trace.SpanContext {
 | 
			
		||||
	return s.sc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestInjectB3(t *testing.T) {
 | 
			
		||||
	testGroup := []struct {
 | 
			
		||||
		name  string
 | 
			
		||||
		tests []injectTest
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:  "valid inject headers",
 | 
			
		||||
			tests: injectHeader,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:  "invalid inject headers",
 | 
			
		||||
			tests: injectInvalidHeader,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tg := range testGroup {
 | 
			
		||||
		for _, tt := range tg.tests {
 | 
			
		||||
			propagator := b3.B3{InjectEncoding: tt.encoding}
 | 
			
		||||
			t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
				req, _ := http.NewRequest("GET", "http://example.com", nil)
 | 
			
		||||
				ctx := trace.ContextWithSpan(
 | 
			
		||||
					context.Background(),
 | 
			
		||||
					testSpan{
 | 
			
		||||
						Span: mockSpan,
 | 
			
		||||
						sc:   tt.sc,
 | 
			
		||||
					},
 | 
			
		||||
				)
 | 
			
		||||
				propagator.Inject(ctx, req.Header)
 | 
			
		||||
 | 
			
		||||
				for h, v := range tt.wantHeaders {
 | 
			
		||||
					got, want := req.Header.Get(h), v
 | 
			
		||||
					if diff := cmp.Diff(got, want); diff != "" {
 | 
			
		||||
						t.Errorf("%s: %s, header=%s: -got +want %s", tg.name, tt.name, h, diff)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				for _, h := range tt.doNotWantHeaders {
 | 
			
		||||
					v, gotOk := req.Header[h]
 | 
			
		||||
					if diff := cmp.Diff(gotOk, false); diff != "" {
 | 
			
		||||
						t.Errorf("%s: %s, header=%s: -got +want %s, value=%s", tg.name, tt.name, h, diff, v)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestB3Propagator_GetAllKeys(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name       string
 | 
			
		||||
		propagator b3.B3
 | 
			
		||||
		want       []string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:       "no encoding specified",
 | 
			
		||||
			propagator: b3.B3{},
 | 
			
		||||
			want: []string{
 | 
			
		||||
				b3TraceID,
 | 
			
		||||
				b3SpanID,
 | 
			
		||||
				b3Sampled,
 | 
			
		||||
				b3Flags,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:       "B3MultipleHeader encoding specified",
 | 
			
		||||
			propagator: b3.B3{InjectEncoding: b3.B3MultipleHeader},
 | 
			
		||||
			want: []string{
 | 
			
		||||
				b3TraceID,
 | 
			
		||||
				b3SpanID,
 | 
			
		||||
				b3Sampled,
 | 
			
		||||
				b3Flags,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:       "B3SingleHeader encoding specified",
 | 
			
		||||
			propagator: b3.B3{InjectEncoding: b3.B3SingleHeader},
 | 
			
		||||
			want: []string{
 | 
			
		||||
				b3Context,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:       "B3SingleHeader and B3MultipleHeader encoding specified",
 | 
			
		||||
			propagator: b3.B3{InjectEncoding: b3.B3SingleHeader | b3.B3MultipleHeader},
 | 
			
		||||
			want: []string{
 | 
			
		||||
				b3Context,
 | 
			
		||||
				b3TraceID,
 | 
			
		||||
				b3SpanID,
 | 
			
		||||
				b3Sampled,
 | 
			
		||||
				b3Flags,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		if diff := cmp.Diff(test.propagator.GetAllKeys(), test.want); diff != "" {
 | 
			
		||||
			t.Errorf("%s: GetAllKeys: -got +want %s", test.name, diff)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,344 @@
 | 
			
		|||
// 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 b3
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"go.opentelemetry.io/otel/api/propagation"
 | 
			
		||||
	"go.opentelemetry.io/otel/api/trace"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// Default B3 Header names.
 | 
			
		||||
	b3ContextHeader      = "b3"
 | 
			
		||||
	b3DebugFlagHeader    = "x-b3-flags"
 | 
			
		||||
	b3TraceIDHeader      = "x-b3-traceid"
 | 
			
		||||
	b3SpanIDHeader       = "x-b3-spanid"
 | 
			
		||||
	b3SampledHeader      = "x-b3-sampled"
 | 
			
		||||
	b3ParentSpanIDHeader = "x-b3-parentspanid"
 | 
			
		||||
 | 
			
		||||
	b3TraceIDPadding = "0000000000000000"
 | 
			
		||||
 | 
			
		||||
	// B3 Single Header encoding widths.
 | 
			
		||||
	separatorWidth      = 1       // Single "-" character.
 | 
			
		||||
	samplingWidth       = 1       // Single hex character.
 | 
			
		||||
	traceID64BitsWidth  = 64 / 4  // 16 hex character Trace ID.
 | 
			
		||||
	traceID128BitsWidth = 128 / 4 // 32 hex character Trace ID.
 | 
			
		||||
	spanIDWidth         = 16      // 16 hex character ID.
 | 
			
		||||
	parentSpanIDWidth   = 16      // 16 hex character ID.
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	empty = trace.EmptySpanContext()
 | 
			
		||||
 | 
			
		||||
	errInvalidSampledByte        = errors.New("invalid B3 Sampled found")
 | 
			
		||||
	errInvalidSampledHeader      = errors.New("invalid B3 Sampled header found")
 | 
			
		||||
	errInvalidTraceIDHeader      = errors.New("invalid B3 traceID header found")
 | 
			
		||||
	errInvalidSpanIDHeader       = errors.New("invalid B3 spanID header found")
 | 
			
		||||
	errInvalidParentSpanIDHeader = errors.New("invalid B3 ParentSpanID header found")
 | 
			
		||||
	errInvalidScope              = errors.New("require either both traceID and spanID or none")
 | 
			
		||||
	errInvalidScopeParent        = errors.New("ParentSpanID requires both traceID and spanID to be available")
 | 
			
		||||
	errInvalidScopeParentSingle  = errors.New("ParentSpanID requires traceID, spanID and Sampled to be available")
 | 
			
		||||
	errEmptyContext              = errors.New("empty request context")
 | 
			
		||||
	errInvalidTraceIDValue       = errors.New("invalid B3 traceID value found")
 | 
			
		||||
	errInvalidSpanIDValue        = errors.New("invalid B3 spanID value found")
 | 
			
		||||
	errInvalidParentSpanIDValue  = errors.New("invalid B3 ParentSpanID value found")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Encoding is a bitmask representation of the B3 encoding type.
 | 
			
		||||
type Encoding uint8
 | 
			
		||||
 | 
			
		||||
// supports returns if e has o bit(s) set.
 | 
			
		||||
func (e Encoding) supports(o Encoding) bool {
 | 
			
		||||
	return e&o == o
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// B3MultipleHeader is a B3 encoding that uses multiple headers to
 | 
			
		||||
	// transmit tracing information all prefixed with `x-b3-`.
 | 
			
		||||
	B3MultipleHeader Encoding = 1 << iota
 | 
			
		||||
	// B3SingleHeader is a B3 encoding that uses a single header named `b3`
 | 
			
		||||
	// to transmit tracing information.
 | 
			
		||||
	B3SingleHeader
 | 
			
		||||
	// B3Unspecified is an unspecified B3 encoding.
 | 
			
		||||
	B3Unspecified Encoding = 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}
 | 
			
		||||
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.
 | 
			
		||||
	InjectEncoding Encoding
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ propagation.HTTPPropagator = B3{}
 | 
			
		||||
 | 
			
		||||
// Inject injects a context into the supplier as B3 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 := 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.TraceFlags&trace.FlagsDebug == trace.FlagsDebug {
 | 
			
		||||
			header = append(header, "d")
 | 
			
		||||
		} else if !(sc.TraceFlags&trace.FlagsDeferred == trace.FlagsDeferred) {
 | 
			
		||||
			if sc.IsSampled() {
 | 
			
		||||
				header = append(header, "1")
 | 
			
		||||
			} else {
 | 
			
		||||
				header = append(header, "0")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		supplier.Set(b3ContextHeader, strings.Join(header, "-"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if b3.InjectEncoding.supports(B3MultipleHeader) || b3.InjectEncoding == B3Unspecified {
 | 
			
		||||
		if sc.TraceID.IsValid() && sc.SpanID.IsValid() {
 | 
			
		||||
			supplier.Set(b3TraceIDHeader, sc.TraceID.String())
 | 
			
		||||
			supplier.Set(b3SpanIDHeader, sc.SpanID.String())
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if sc.TraceFlags&trace.FlagsDebug == trace.FlagsDebug {
 | 
			
		||||
			// Since Debug implies deferred, don't also send "X-B3-Sampled".
 | 
			
		||||
			supplier.Set(b3DebugFlagHeader, "1")
 | 
			
		||||
		} else if !(sc.TraceFlags&trace.FlagsDeferred == trace.FlagsDeferred) {
 | 
			
		||||
			if sc.IsSampled() {
 | 
			
		||||
				supplier.Set(b3SampledHeader, "1")
 | 
			
		||||
			} else {
 | 
			
		||||
				supplier.Set(b3SampledHeader, "0")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extract extracts a context from the supplier if it contains B3 headers.
 | 
			
		||||
func (b3 B3) Extract(ctx context.Context, supplier propagation.HTTPSupplier) context.Context {
 | 
			
		||||
	var (
 | 
			
		||||
		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 trace.ContextWithRemoteSpanContext(ctx, sc)
 | 
			
		||||
		}
 | 
			
		||||
		// The Single Header value was invalid, fallback to Multiple Header.
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		traceID      = supplier.Get(b3TraceIDHeader)
 | 
			
		||||
		spanID       = supplier.Get(b3SpanIDHeader)
 | 
			
		||||
		parentSpanID = supplier.Get(b3ParentSpanIDHeader)
 | 
			
		||||
		sampled      = supplier.Get(b3SampledHeader)
 | 
			
		||||
		debugFlag    = supplier.Get(b3DebugFlagHeader)
 | 
			
		||||
	)
 | 
			
		||||
	sc, err = extractMultiple(traceID, spanID, parentSpanID, sampled, debugFlag)
 | 
			
		||||
	if err != nil || !sc.IsValid() {
 | 
			
		||||
		return ctx
 | 
			
		||||
	}
 | 
			
		||||
	return trace.ContextWithRemoteSpanContext(ctx, sc)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b3 B3) GetAllKeys() []string {
 | 
			
		||||
	header := []string{}
 | 
			
		||||
	if b3.InjectEncoding.supports(B3SingleHeader) {
 | 
			
		||||
		header = append(header, b3ContextHeader)
 | 
			
		||||
	}
 | 
			
		||||
	if b3.InjectEncoding.supports(B3MultipleHeader) || b3.InjectEncoding == B3Unspecified {
 | 
			
		||||
		header = append(header, b3TraceIDHeader, b3SpanIDHeader, b3SampledHeader, b3DebugFlagHeader)
 | 
			
		||||
	}
 | 
			
		||||
	return header
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// extractMultiple reconstructs a SpanContext from header values based on B3
 | 
			
		||||
// 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) (trace.SpanContext, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		err           error
 | 
			
		||||
		requiredCount int
 | 
			
		||||
		sc            = trace.SpanContext{}
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// correct values for an existing sampled header are "0" and "1".
 | 
			
		||||
	// For legacy support and  being lenient to other tracing implementations we
 | 
			
		||||
	// allow "true" and "false" as inputs for interop purposes.
 | 
			
		||||
	switch strings.ToLower(sampled) {
 | 
			
		||||
	case "0", "false":
 | 
			
		||||
		// Zero value for TraceFlags sample bit is unset.
 | 
			
		||||
	case "1", "true":
 | 
			
		||||
		sc.TraceFlags = trace.FlagsSampled
 | 
			
		||||
	case "":
 | 
			
		||||
		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 |= trace.FlagsDebug
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if traceID != "" {
 | 
			
		||||
		requiredCount++
 | 
			
		||||
		id := traceID
 | 
			
		||||
		if len(traceID) == 16 {
 | 
			
		||||
			// Pad 64-bit trace IDs.
 | 
			
		||||
			id = b3TraceIDPadding + traceID
 | 
			
		||||
		}
 | 
			
		||||
		if sc.TraceID, err = trace.IDFromHex(id); err != nil {
 | 
			
		||||
			return empty, errInvalidTraceIDHeader
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if spanID != "" {
 | 
			
		||||
		requiredCount++
 | 
			
		||||
		if sc.SpanID, err = trace.SpanIDFromHex(spanID); err != nil {
 | 
			
		||||
			return empty, errInvalidSpanIDHeader
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if requiredCount != 0 && requiredCount != 2 {
 | 
			
		||||
		return empty, errInvalidScope
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if parentSpanID != "" {
 | 
			
		||||
		if requiredCount == 0 {
 | 
			
		||||
			return empty, errInvalidScopeParent
 | 
			
		||||
		}
 | 
			
		||||
		// Validate parent span ID but we do not use it so do not save it.
 | 
			
		||||
		if _, err = trace.SpanIDFromHex(parentSpanID); err != nil {
 | 
			
		||||
			return empty, errInvalidParentSpanIDHeader
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return sc, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// extractSingle reconstructs a SpanContext from contextHeader based on a B3
 | 
			
		||||
// 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) (trace.SpanContext, error) {
 | 
			
		||||
	if contextHeader == "" {
 | 
			
		||||
		return empty, errEmptyContext
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		sc       = trace.SpanContext{}
 | 
			
		||||
		sampling string
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	headerLen := len(contextHeader)
 | 
			
		||||
 | 
			
		||||
	if headerLen == samplingWidth {
 | 
			
		||||
		sampling = contextHeader
 | 
			
		||||
	} else if headerLen == traceID64BitsWidth || headerLen == traceID128BitsWidth {
 | 
			
		||||
		// Trace ID by itself is invalid.
 | 
			
		||||
		return empty, errInvalidScope
 | 
			
		||||
	} else if headerLen >= traceID64BitsWidth+spanIDWidth+separatorWidth {
 | 
			
		||||
		pos := 0
 | 
			
		||||
		var traceID string
 | 
			
		||||
		if string(contextHeader[traceID64BitsWidth]) == "-" {
 | 
			
		||||
			// traceID must be 64 bits
 | 
			
		||||
			pos += traceID64BitsWidth // {traceID}
 | 
			
		||||
			traceID = b3TraceIDPadding + string(contextHeader[0:pos])
 | 
			
		||||
		} else if string(contextHeader[32]) == "-" {
 | 
			
		||||
			// traceID must be 128 bits
 | 
			
		||||
			pos += traceID128BitsWidth // {traceID}
 | 
			
		||||
			traceID = string(contextHeader[0:pos])
 | 
			
		||||
		} else {
 | 
			
		||||
			return empty, errInvalidTraceIDValue
 | 
			
		||||
		}
 | 
			
		||||
		var err error
 | 
			
		||||
		sc.TraceID, err = trace.IDFromHex(traceID)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return empty, errInvalidTraceIDValue
 | 
			
		||||
		}
 | 
			
		||||
		pos += separatorWidth // {traceID}-
 | 
			
		||||
 | 
			
		||||
		sc.SpanID, err = trace.SpanIDFromHex(contextHeader[pos : pos+spanIDWidth])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return empty, errInvalidSpanIDValue
 | 
			
		||||
		}
 | 
			
		||||
		pos += spanIDWidth // {traceID}-{spanID}
 | 
			
		||||
 | 
			
		||||
		if headerLen > pos {
 | 
			
		||||
			if headerLen == pos+separatorWidth {
 | 
			
		||||
				// {traceID}-{spanID}- is invalid.
 | 
			
		||||
				return empty, errInvalidSampledByte
 | 
			
		||||
			}
 | 
			
		||||
			pos += separatorWidth // {traceID}-{spanID}-
 | 
			
		||||
 | 
			
		||||
			if headerLen == pos+samplingWidth {
 | 
			
		||||
				sampling = string(contextHeader[pos])
 | 
			
		||||
			} else if headerLen == pos+parentSpanIDWidth {
 | 
			
		||||
				// {traceID}-{spanID}-{parentSpanID} is invalid.
 | 
			
		||||
				return empty, errInvalidScopeParentSingle
 | 
			
		||||
			} else if headerLen == pos+samplingWidth+separatorWidth+parentSpanIDWidth {
 | 
			
		||||
				sampling = string(contextHeader[pos])
 | 
			
		||||
				pos += samplingWidth + separatorWidth // {traceID}-{spanID}-{sampling}-
 | 
			
		||||
 | 
			
		||||
				// Validate parent span ID but we do not use it so do not
 | 
			
		||||
				// save it.
 | 
			
		||||
				_, err = trace.SpanIDFromHex(contextHeader[pos:])
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return empty, errInvalidParentSpanIDValue
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				return empty, errInvalidParentSpanIDValue
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		return empty, errInvalidTraceIDValue
 | 
			
		||||
	}
 | 
			
		||||
	switch sampling {
 | 
			
		||||
	case "":
 | 
			
		||||
		sc.TraceFlags = trace.FlagsDeferred
 | 
			
		||||
	case "d":
 | 
			
		||||
		sc.TraceFlags = trace.FlagsDebug
 | 
			
		||||
	case "1":
 | 
			
		||||
		sc.TraceFlags = trace.FlagsSampled
 | 
			
		||||
	case "0":
 | 
			
		||||
		// Zero value for TraceFlags sample bit is unset.
 | 
			
		||||
	default:
 | 
			
		||||
		return empty, errInvalidSampledByte
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return sc, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,325 @@
 | 
			
		|||
// 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 b3
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
 | 
			
		||||
	"go.opentelemetry.io/otel/api/trace"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	traceID    = trace.ID{0, 0, 0, 0, 0, 0, 0, 0x7b, 0, 0, 0, 0, 0, 0, 0x1, 0xc8}
 | 
			
		||||
	traceIDStr = "000000000000007b00000000000001c8"
 | 
			
		||||
	spanID     = trace.SpanID{0, 0, 0, 0, 0, 0, 0, 0x7b}
 | 
			
		||||
	spanIDStr  = "000000000000007b"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestExtractMultiple(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		traceID      string
 | 
			
		||||
		spanID       string
 | 
			
		||||
		parentSpanID string
 | 
			
		||||
		sampled      string
 | 
			
		||||
		flags        string
 | 
			
		||||
		expected     trace.SpanContext
 | 
			
		||||
		err          error
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			"", "", "", "0", "",
 | 
			
		||||
			trace.SpanContext{},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"", "", "", "", "",
 | 
			
		||||
			trace.SpanContext{TraceFlags: trace.FlagsDeferred},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"", "", "", "1", "",
 | 
			
		||||
			trace.SpanContext{TraceFlags: trace.FlagsSampled},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"", "", "", "", "1",
 | 
			
		||||
			trace.SpanContext{TraceFlags: trace.FlagsDeferred | trace.FlagsDebug},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"", "", "", "0", "1",
 | 
			
		||||
			trace.SpanContext{TraceFlags: trace.FlagsDebug},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"", "", "", "1", "1",
 | 
			
		||||
			trace.SpanContext{TraceFlags: trace.FlagsSampled | trace.FlagsDebug},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			traceIDStr, spanIDStr, "", "", "",
 | 
			
		||||
			trace.SpanContext{TraceID: traceID, SpanID: spanID, TraceFlags: trace.FlagsDeferred},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			traceIDStr, spanIDStr, "", "0", "",
 | 
			
		||||
			trace.SpanContext{TraceID: traceID, SpanID: spanID},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		// Ensure backwards compatibility.
 | 
			
		||||
		{
 | 
			
		||||
			traceIDStr, spanIDStr, "", "false", "",
 | 
			
		||||
			trace.SpanContext{TraceID: traceID, SpanID: spanID},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			traceIDStr, spanIDStr, "", "1", "",
 | 
			
		||||
			trace.SpanContext{TraceID: traceID, SpanID: spanID, TraceFlags: trace.FlagsSampled},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		// Ensure backwards compatibility.
 | 
			
		||||
		{
 | 
			
		||||
			traceIDStr, spanIDStr, "", "true", "",
 | 
			
		||||
			trace.SpanContext{TraceID: traceID, SpanID: spanID, TraceFlags: trace.FlagsSampled},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			traceIDStr, spanIDStr, "", "a", "",
 | 
			
		||||
			empty,
 | 
			
		||||
			errInvalidSampledHeader,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			traceIDStr, spanIDStr, "", "1", "1",
 | 
			
		||||
			trace.SpanContext{TraceID: traceID, SpanID: spanID, TraceFlags: trace.FlagsSampled | trace.FlagsDebug},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		// Invalid flags are discarded.
 | 
			
		||||
		{
 | 
			
		||||
			traceIDStr, spanIDStr, "", "1", "invalid",
 | 
			
		||||
			trace.SpanContext{TraceID: traceID, SpanID: spanID, TraceFlags: trace.FlagsSampled},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		// Support short trace IDs.
 | 
			
		||||
		{
 | 
			
		||||
			"00000000000001c8", spanIDStr, "", "0", "",
 | 
			
		||||
			trace.SpanContext{
 | 
			
		||||
				TraceID: trace.ID{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1, 0xc8},
 | 
			
		||||
				SpanID:  spanID,
 | 
			
		||||
			},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"00000000000001c", spanIDStr, "", "0", "",
 | 
			
		||||
			empty,
 | 
			
		||||
			errInvalidTraceIDHeader,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"00000000000001c80", spanIDStr, "", "0", "",
 | 
			
		||||
			empty,
 | 
			
		||||
			errInvalidTraceIDHeader,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			traceIDStr[:len(traceIDStr)-2], spanIDStr, "", "0", "",
 | 
			
		||||
			empty,
 | 
			
		||||
			errInvalidTraceIDHeader,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			traceIDStr + "0", spanIDStr, "", "0", "",
 | 
			
		||||
			empty,
 | 
			
		||||
			errInvalidTraceIDHeader,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			traceIDStr, "00000000000001c", "", "0", "",
 | 
			
		||||
			empty,
 | 
			
		||||
			errInvalidSpanIDHeader,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			traceIDStr, "00000000000001c80", "", "0", "",
 | 
			
		||||
			empty,
 | 
			
		||||
			errInvalidSpanIDHeader,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			traceIDStr, "", "", "0", "",
 | 
			
		||||
			empty,
 | 
			
		||||
			errInvalidScope,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"", spanIDStr, "", "0", "",
 | 
			
		||||
			empty,
 | 
			
		||||
			errInvalidScope,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"", "", spanIDStr, "0", "",
 | 
			
		||||
			empty,
 | 
			
		||||
			errInvalidScopeParent,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			traceIDStr, spanIDStr, "00000000000001c8", "0", "",
 | 
			
		||||
			trace.SpanContext{TraceID: traceID, SpanID: spanID},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			traceIDStr, spanIDStr, "00000000000001c", "0", "",
 | 
			
		||||
			empty,
 | 
			
		||||
			errInvalidParentSpanIDHeader,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			traceIDStr, spanIDStr, "00000000000001c80", "0", "",
 | 
			
		||||
			empty,
 | 
			
		||||
			errInvalidParentSpanIDHeader,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		actual, err := extractMultiple(
 | 
			
		||||
			test.traceID,
 | 
			
		||||
			test.spanID,
 | 
			
		||||
			test.parentSpanID,
 | 
			
		||||
			test.sampled,
 | 
			
		||||
			test.flags,
 | 
			
		||||
		)
 | 
			
		||||
		info := []interface{}{
 | 
			
		||||
			"trace ID: %q, span ID: %q, parent span ID: %q, sampled: %q, flags: %q",
 | 
			
		||||
			test.traceID,
 | 
			
		||||
			test.spanID,
 | 
			
		||||
			test.parentSpanID,
 | 
			
		||||
			test.sampled,
 | 
			
		||||
			test.flags,
 | 
			
		||||
		}
 | 
			
		||||
		if !assert.Equal(t, test.err, err, info...) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		assert.Equal(t, test.expected, actual, info...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestExtractSingle(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		header   string
 | 
			
		||||
		expected trace.SpanContext
 | 
			
		||||
		err      error
 | 
			
		||||
	}{
 | 
			
		||||
		{"0", trace.SpanContext{}, nil},
 | 
			
		||||
		{"1", trace.SpanContext{TraceFlags: trace.FlagsSampled}, nil},
 | 
			
		||||
		{"d", trace.SpanContext{TraceFlags: trace.FlagsDebug}, nil},
 | 
			
		||||
		{"a", empty, errInvalidSampledByte},
 | 
			
		||||
		{"3", empty, errInvalidSampledByte},
 | 
			
		||||
		{"000000000000007b", empty, errInvalidScope},
 | 
			
		||||
		{"000000000000007b00000000000001c8", empty, errInvalidScope},
 | 
			
		||||
		// Support short trace IDs.
 | 
			
		||||
		{
 | 
			
		||||
			"00000000000001c8-000000000000007b",
 | 
			
		||||
			trace.SpanContext{
 | 
			
		||||
				TraceID:    trace.ID{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1, 0xc8},
 | 
			
		||||
				SpanID:     spanID,
 | 
			
		||||
				TraceFlags: trace.FlagsDeferred,
 | 
			
		||||
			},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"000000000000007b00000000000001c8-000000000000007b",
 | 
			
		||||
			trace.SpanContext{
 | 
			
		||||
				TraceID:    traceID,
 | 
			
		||||
				SpanID:     spanID,
 | 
			
		||||
				TraceFlags: trace.FlagsDeferred,
 | 
			
		||||
			},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"000000000000007b00000000000001c8-000000000000007b-",
 | 
			
		||||
			empty,
 | 
			
		||||
			errInvalidSampledByte,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"000000000000007b00000000000001c8-000000000000007b-3",
 | 
			
		||||
			empty,
 | 
			
		||||
			errInvalidSampledByte,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"000000000000007b00000000000001c8-000000000000007b-00000000000001c8",
 | 
			
		||||
			empty,
 | 
			
		||||
			errInvalidScopeParentSingle,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"000000000000007b00000000000001c8-000000000000007b-1",
 | 
			
		||||
			trace.SpanContext{TraceID: traceID, SpanID: spanID, TraceFlags: trace.FlagsSampled},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		// ParentSpanID is discarded, but should still result in a parsable header.
 | 
			
		||||
		{
 | 
			
		||||
			"000000000000007b00000000000001c8-000000000000007b-1-00000000000001c8",
 | 
			
		||||
			trace.SpanContext{TraceID: traceID, SpanID: spanID, TraceFlags: trace.FlagsSampled},
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"000000000000007b00000000000001c8-000000000000007b-1-00000000000001c",
 | 
			
		||||
			empty,
 | 
			
		||||
			errInvalidParentSpanIDValue,
 | 
			
		||||
		},
 | 
			
		||||
		{"", empty, errEmptyContext},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		actual, err := extractSingle(test.header)
 | 
			
		||||
		if !assert.Equal(t, test.err, err, "header: %s", test.header) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		assert.Equal(t, test.expected, actual, "header: %s", test.header)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestB3EncodingOperations(t *testing.T) {
 | 
			
		||||
	encodings := []Encoding{
 | 
			
		||||
		B3MultipleHeader,
 | 
			
		||||
		B3SingleHeader,
 | 
			
		||||
		B3Unspecified,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Test for overflow (or something really unexpected).
 | 
			
		||||
	for i, e := range encodings {
 | 
			
		||||
		for j := i + 1; j < i+len(encodings); j++ {
 | 
			
		||||
			o := encodings[j%len(encodings)]
 | 
			
		||||
			assert.False(t, e == o, "%v == %v", e, o)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// B3Unspecified is a special case, it supports only itself, but is
 | 
			
		||||
	// supported by everything.
 | 
			
		||||
	assert.True(t, B3Unspecified.supports(B3Unspecified))
 | 
			
		||||
	for _, e := range encodings[:len(encodings)-1] {
 | 
			
		||||
		assert.False(t, B3Unspecified.supports(e), e)
 | 
			
		||||
		assert.True(t, e.supports(B3Unspecified), e)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Skip the special case for B3Unspecified.
 | 
			
		||||
	for i, e := range encodings[:len(encodings)-1] {
 | 
			
		||||
		// Everything should support itself.
 | 
			
		||||
		assert.True(t, e.supports(e))
 | 
			
		||||
		for j := i + 1; j < i+len(encodings); j++ {
 | 
			
		||||
			o := encodings[j%len(encodings)]
 | 
			
		||||
			// Any "or" combination should be supportive of an operand.
 | 
			
		||||
			assert.True(t, (e | o).supports(e), "(%[0]v|%[1]v).supports(%[0]v)", e, o)
 | 
			
		||||
			// Bitmasks should be unique.
 | 
			
		||||
			assert.False(t, o.supports(e), "%v.supports(%v)", o, e)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Encoding.supports should be more inclusive than equality.
 | 
			
		||||
	all := ^B3Unspecified
 | 
			
		||||
	for _, e := range encodings {
 | 
			
		||||
		assert.True(t, all.supports(e))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
// 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.
 | 
			
		||||
 | 
			
		||||
// This package implements the B3 propagator specification as defined
 | 
			
		||||
// at https://github.com/openzipkin/b3-propagation
 | 
			
		||||
package b3 // import "go.opentelemetry.io/contrib/propagators/b3"
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
// 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.
 | 
			
		||||
 | 
			
		||||
// This module contains all of its functionality in its subpackages.
 | 
			
		||||
// This top-level package is to allow clients to pull in all propagator
 | 
			
		||||
// implementations at once using
 | 
			
		||||
//    require go.opentelemetry.io/contrib/propagators
 | 
			
		||||
package propagators
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
module go.opentelemetry.io/contrib/propagators
 | 
			
		||||
 | 
			
		||||
go 1.14
 | 
			
		||||
 | 
			
		||||
replace go.opentelemetry.io/contrib => ./..
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/google/go-cmp v0.5.1
 | 
			
		||||
	github.com/stretchr/testify v1.6.1
 | 
			
		||||
	go.opentelemetry.io/contrib v0.11.0
 | 
			
		||||
	go.opentelemetry.io/otel v0.11.0
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,26 @@
 | 
			
		|||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
 | 
			
		||||
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/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
 | 
			
		||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
			
		||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 | 
			
		||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 | 
			
		||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
			
		||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 | 
			
		||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
			
		||||
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.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
 | 
			
		||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
			
		||||
go.opentelemetry.io/otel v0.11.0 h1:IN2tzQa9Gc4ZVKnTaMbPVcHjvzOdg5n9QfnmlqiET7E=
 | 
			
		||||
go.opentelemetry.io/otel v0.11.0/go.mod h1:G8UCk+KooF2HLkgo8RHX9epABH/aRGYET7gQOqBVdB0=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 | 
			
		||||
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/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
		Loading…
	
		Reference in New Issue