1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-03-05 15:05:51 +02:00

Add opencensus binary propagation to bridge (#1334)

* add opencensus binary propagation bridge

* Update bridge/opencensus/binary/binary.go

Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>

* Update bridge/opencensus/doc.go

Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>

* address comments

Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
This commit is contained in:
David Ashpole 2020-11-16 10:35:25 -08:00 committed by GitHub
parent 73a05393a0
commit bbc9465ddb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 458 additions and 33 deletions

View File

@ -16,6 +16,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- `DeploymentEnvironmentKey` added to `go.opentelemetry.io/otel/semconv` package. (#1323)
- Add an opencensus to opentelemetry tracing bridge. (#1305)
- Add a parent context argument to `SpanProcessor.OnStart` to follow the specification. (#1333)
- Add an opencensus binary propagation implementation. (#1334)
### Changed

View File

@ -0,0 +1,86 @@
// 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 binary // import "go.opentelemetry.io/otel/bridge/opencensus/binary"
import (
"context"
ocpropagation "go.opencensus.io/trace/propagation"
"go.opentelemetry.io/otel/bridge/opencensus/utils"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/trace"
)
type key uint
const binaryKey key = 0
// binaryHeader is the same as traceContextKey is in opencensus:
// https://github.com/census-instrumentation/opencensus-go/blob/3fb168f674736c026e623310bfccb0691e6dec8a/plugin/ocgrpc/trace_common.go#L30
const binaryHeader = "grpc-trace-bin"
// Binary is an OpenTelemetry implementation of the OpenCensus grpc binary format.
// Binary propagation was temporarily removed from opentelemetry. See
// https://github.com/open-telemetry/opentelemetry-specification/issues/437
type Binary struct{}
var _ propagation.TextMapPropagator = Binary{}
// Inject injects context into the TextMapCarrier
func (b Binary) Inject(ctx context.Context, carrier propagation.TextMapCarrier) {
binaryContext := ctx.Value(binaryKey)
if state, ok := binaryContext.(string); binaryContext != nil && ok {
carrier.Set(binaryHeader, state)
}
sc := trace.SpanContextFromContext(ctx)
if !sc.IsValid() {
return
}
h := ocpropagation.Binary(utils.OTelSpanContextToOC(sc))
carrier.Set(binaryHeader, string(h))
}
// Extract extracts the SpanContext from the TextMapCarrier
func (b Binary) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context {
state := carrier.Get(binaryHeader)
if state != "" {
ctx = context.WithValue(ctx, binaryKey, state)
}
sc := b.extract(carrier)
if !sc.IsValid() {
return ctx
}
return trace.ContextWithRemoteSpanContext(ctx, sc)
}
func (b Binary) extract(carrier propagation.TextMapCarrier) trace.SpanContext {
h := carrier.Get(binaryHeader)
if h == "" {
return trace.SpanContext{}
}
ocContext, ok := ocpropagation.FromBinary([]byte(h))
if !ok {
return trace.SpanContext{}
}
return utils.OCSpanContextToOTel(ocContext)
}
// Fields returns the fields that this propagator modifies.
func (b Binary) Fields() []string {
return []string{binaryHeader}
}

View File

@ -0,0 +1,153 @@
// 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 binary
import (
"context"
"fmt"
"net/http"
"testing"
"go.opentelemetry.io/otel/oteltest"
"go.opentelemetry.io/otel/trace"
)
var (
traceID = trace.TraceID([16]byte{14, 54, 12})
spanID = trace.SpanID([8]byte{2, 8, 14, 20})
childSpanID = trace.SpanID([8]byte{0, 0, 0, 0, 0, 0, 0, 2})
headerFmt = "\x00\x00\x0e6\f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00%s\x02%s"
)
func TestFields(t *testing.T) {
b := Binary{}
fields := b.Fields()
if len(fields) != 1 {
t.Fatalf("Got %d fields, expected 1", len(fields))
}
if fields[0] != "grpc-trace-bin" {
t.Errorf("Got fields[0] == %s, expected grpc-trace-bin", fields[0])
}
}
func TestInject(t *testing.T) {
mockTracer := oteltest.DefaultTracer()
prop := Binary{}
for _, tt := range []struct {
desc string
sc trace.SpanContext
wantHeader string
}{
{
desc: "empty",
sc: trace.SpanContext{},
wantHeader: "",
},
{
desc: "valid spancontext, sampled",
sc: trace.SpanContext{
TraceID: traceID,
SpanID: spanID,
TraceFlags: trace.FlagsSampled,
},
wantHeader: fmt.Sprintf(headerFmt, "\x02", "\x01"),
},
{
desc: "valid spancontext, not sampled",
sc: trace.SpanContext{
TraceID: traceID,
SpanID: spanID,
},
wantHeader: fmt.Sprintf(headerFmt, "\x03", "\x00"),
},
{
desc: "valid spancontext, with unsupported bit set in traceflags",
sc: trace.SpanContext{
TraceID: traceID,
SpanID: spanID,
TraceFlags: 0xff,
},
wantHeader: fmt.Sprintf(headerFmt, "\x04", "\x01"),
},
{
desc: "invalid spancontext",
sc: trace.SpanContext{},
wantHeader: "",
},
} {
t.Run(tt.desc, func(t *testing.T) {
req, _ := http.NewRequest("GET", "http://example.com", nil)
ctx := context.Background()
if tt.sc.IsValid() {
ctx = trace.ContextWithRemoteSpanContext(ctx, tt.sc)
ctx, _ = mockTracer.Start(ctx, "inject")
}
prop.Inject(ctx, req.Header)
gotHeader := req.Header.Get("grpc-trace-bin")
if gotHeader != tt.wantHeader {
t.Errorf("Got header = %q, want %q", gotHeader, tt.wantHeader)
}
})
}
}
func TestExtract(t *testing.T) {
prop := Binary{}
for _, tt := range []struct {
desc string
header string
wantSc trace.SpanContext
}{
{
desc: "empty",
header: "",
wantSc: trace.SpanContext{},
},
{
desc: "header not binary",
header: "5435j345io34t5904w3jt894j3t854w89tp95jgt9",
wantSc: trace.SpanContext{},
},
{
desc: "valid binary header",
header: fmt.Sprintf(headerFmt, "\x02", "\x00"),
wantSc: trace.SpanContext{
TraceID: traceID,
SpanID: childSpanID,
},
},
{
desc: "valid binary and sampled",
header: fmt.Sprintf(headerFmt, "\x02", "\x01"),
wantSc: trace.SpanContext{
TraceID: traceID,
SpanID: childSpanID,
TraceFlags: trace.FlagsSampled,
},
},
} {
t.Run(tt.desc, func(t *testing.T) {
req, _ := http.NewRequest("GET", "http://example.com", nil)
req.Header.Set("grpc-trace-bin", tt.header)
ctx := context.Background()
ctx = prop.Extract(ctx, req.Header)
gotSc := trace.RemoteSpanContextFromContext(ctx)
if gotSc != tt.wantSc {
t.Errorf("Got SpanContext: %+v, wanted %+v", gotSc, tt.wantSc)
}
})
}
}

View File

@ -21,6 +21,7 @@ import (
octrace "go.opencensus.io/trace"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/bridge/opencensus/utils"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/label"
"go.opentelemetry.io/otel/trace"
@ -68,7 +69,7 @@ func convertStartOptions(optFns []octrace.StartOption, name string) []trace.Span
func (o *otelTracer) StartSpanWithRemoteParent(ctx context.Context, name string, parent octrace.SpanContext, s ...octrace.StartOption) (context.Context, *octrace.Span) {
// make sure span context is zero'd out so we use the remote parent
ctx = trace.ContextWithSpan(ctx, nil)
ctx = trace.ContextWithRemoteSpanContext(ctx, ocSpanContextToOTel(parent))
ctx = trace.ContextWithRemoteSpanContext(ctx, utils.OCSpanContextToOTel(parent))
return o.StartSpan(ctx, name, s...)
}
@ -98,7 +99,7 @@ func (s *span) End() {
}
func (s *span) SpanContext() octrace.SpanContext {
return otelSpanContextToOc(s.otSpan.SpanContext())
return utils.OTelSpanContextToOC(s.otSpan.SpanContext())
}
func (s *span) SetName(name string) {
@ -187,31 +188,3 @@ func (s *span) AddLink(l octrace.Link) {
func (s *span) String() string {
return fmt.Sprintf("span %s", s.otSpan.SpanContext().SpanID.String())
}
func otelSpanContextToOc(sc trace.SpanContext) octrace.SpanContext {
if sc.IsDebug() || sc.IsDeferred() {
otel.Handle(fmt.Errorf("ignoring OpenTelemetry Debug or Deferred trace flags for span %q because they are not supported by OpenCensus", sc.SpanID))
}
var to octrace.TraceOptions
if sc.IsSampled() {
// OpenCensus doesn't expose functions to directly set sampled
to = 0x1
}
return octrace.SpanContext{
TraceID: octrace.TraceID(sc.TraceID),
SpanID: octrace.SpanID(sc.SpanID),
TraceOptions: to,
}
}
func ocSpanContextToOTel(sc octrace.SpanContext) trace.SpanContext {
var traceFlags byte
if sc.IsSampled() {
traceFlags = trace.FlagsSampled
}
return trace.SpanContext{
TraceID: trace.TraceID(sc.TraceID),
SpanID: trace.SpanID(sc.SpanID),
TraceFlags: traceFlags,
}
}

View File

@ -20,6 +20,7 @@ import (
octrace "go.opencensus.io/trace"
"go.opentelemetry.io/otel/bridge/opencensus/utils"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/label"
"go.opentelemetry.io/otel/oteltest"
@ -101,7 +102,7 @@ func TestStartSpanWithRemoteParent(t *testing.T) {
ctx := context.Background()
ctx, parent := tracer.Start(ctx, "OpenTelemetrySpan1")
_, span := octrace.StartSpanWithRemoteParent(ctx, "OpenCensusSpan", otelSpanContextToOc(parent.SpanContext()))
_, span := octrace.StartSpanWithRemoteParent(ctx, "OpenCensusSpan", utils.OTelSpanContextToOC(parent.SpanContext()))
span.End()
spans := sr.Completed()

16
bridge/opencensus/doc.go Normal file
View File

@ -0,0 +1,16 @@
// 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 opencensus provides a migration bridge forwarding the OpenCensus API to the OpenTelemetry SDK.
package opencensus // import "go.opentelemetry.io/otel/bridge/opencensus"

View File

@ -1,6 +1,6 @@
module go.opentelemetry.io/opentelemetry-go/bridge/opencensus
module go.opentelemetry.io/otel/bridge/opencensus
go 1.15
go 1.14
require (
go.opencensus.io v0.22.6-0.20201102222123-380f4078db9f

View File

@ -0,0 +1,57 @@
// 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 utils // import "go.opentelemetry.io/otel/bridge/opencensus/utils"
import (
"fmt"
octrace "go.opencensus.io/trace"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/trace"
)
// OTelSpanContextToOC converts from an OpenTelemetry SpanContext to an
// OpenCensus SpanContext, and handles any incompatibilities with the global
// error handler.
func OTelSpanContextToOC(sc trace.SpanContext) octrace.SpanContext {
if sc.IsDebug() || sc.IsDeferred() {
otel.Handle(fmt.Errorf("ignoring OpenTelemetry Debug or Deferred trace flags for span %q because they are not supported by OpenCensus", sc.SpanID))
}
var to octrace.TraceOptions
if sc.IsSampled() {
// OpenCensus doesn't expose functions to directly set sampled
to = 0x1
}
return octrace.SpanContext{
TraceID: octrace.TraceID(sc.TraceID),
SpanID: octrace.SpanID(sc.SpanID),
TraceOptions: to,
}
}
// OCSpanContextToOTel converts from an OpenCensus SpanContext to an
// OpenTelemetry SpanContext.
func OCSpanContextToOTel(sc octrace.SpanContext) trace.SpanContext {
var traceFlags byte
if sc.IsSampled() {
traceFlags = trace.FlagsSampled
}
return trace.SpanContext{
TraceID: trace.TraceID(sc.TraceID),
SpanID: trace.SpanID(sc.SpanID),
TraceFlags: traceFlags,
}
}

View File

@ -0,0 +1,138 @@
// 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 utils
import (
"testing"
"go.opencensus.io/trace/tracestate"
octrace "go.opencensus.io/trace"
"go.opentelemetry.io/otel/trace"
)
func TestOTelSpanContextToOC(t *testing.T) {
for _, tc := range []struct {
description string
input trace.SpanContext
expected octrace.SpanContext
}{
{
description: "empty",
},
{
description: "sampled",
input: trace.SpanContext{
TraceID: trace.TraceID([16]byte{1}),
SpanID: trace.SpanID([8]byte{2}),
TraceFlags: trace.FlagsSampled,
},
expected: octrace.SpanContext{
TraceID: octrace.TraceID([16]byte{1}),
SpanID: octrace.SpanID([8]byte{2}),
TraceOptions: octrace.TraceOptions(0x1),
},
},
{
description: "not sampled",
input: trace.SpanContext{
TraceID: trace.TraceID([16]byte{1}),
SpanID: trace.SpanID([8]byte{2}),
},
expected: octrace.SpanContext{
TraceID: octrace.TraceID([16]byte{1}),
SpanID: octrace.SpanID([8]byte{2}),
TraceOptions: octrace.TraceOptions(0),
},
},
{
description: "debug flag",
input: trace.SpanContext{
TraceID: trace.TraceID([16]byte{1}),
SpanID: trace.SpanID([8]byte{2}),
TraceFlags: trace.FlagsDebug,
},
expected: octrace.SpanContext{
TraceID: octrace.TraceID([16]byte{1}),
SpanID: octrace.SpanID([8]byte{2}),
TraceOptions: octrace.TraceOptions(0),
},
},
} {
t.Run(tc.description, func(t *testing.T) {
output := OTelSpanContextToOC(tc.input)
if output != tc.expected {
t.Fatalf("Got %+v spancontext, exepected %+v.", output, tc.expected)
}
})
}
}
func TestOCSpanContextToOTel(t *testing.T) {
for _, tc := range []struct {
description string
input octrace.SpanContext
expected trace.SpanContext
}{
{
description: "empty",
},
{
description: "sampled",
input: octrace.SpanContext{
TraceID: octrace.TraceID([16]byte{1}),
SpanID: octrace.SpanID([8]byte{2}),
TraceOptions: octrace.TraceOptions(0x1),
},
expected: trace.SpanContext{
TraceID: trace.TraceID([16]byte{1}),
SpanID: trace.SpanID([8]byte{2}),
TraceFlags: trace.FlagsSampled,
},
},
{
description: "not sampled",
input: octrace.SpanContext{
TraceID: octrace.TraceID([16]byte{1}),
SpanID: octrace.SpanID([8]byte{2}),
TraceOptions: octrace.TraceOptions(0),
},
expected: trace.SpanContext{
TraceID: trace.TraceID([16]byte{1}),
SpanID: trace.SpanID([8]byte{2}),
},
},
{
description: "trace state is ignored",
input: octrace.SpanContext{
TraceID: octrace.TraceID([16]byte{1}),
SpanID: octrace.SpanID([8]byte{2}),
Tracestate: &tracestate.Tracestate{},
},
expected: trace.SpanContext{
TraceID: trace.TraceID([16]byte{1}),
SpanID: trace.SpanID([8]byte{2}),
},
},
} {
t.Run(tc.description, func(t *testing.T) {
output := OCSpanContextToOTel(tc.input)
if output != tc.expected {
t.Fatalf("Got %+v spancontext, exepected %+v.", output, tc.expected)
}
})
}
}