Skip to content

Commit

Permalink
add opencensus binary propagation bridge
Browse files Browse the repository at this point in the history
  • Loading branch information
dashpole committed Nov 13, 2020
1 parent 63a1114 commit 041ee1a
Show file tree
Hide file tree
Showing 9 changed files with 442 additions and 33 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- A `TextMapPropagator` and associated `TextMapCarrier` are added to the `go.opentelemetry.io/otel/oteltest` package to test TextMap type propagators and their use. (#1259)
- `SpanContextFromContext` returns `SpanContext` from context. (#1255)
- Add an opencensus to opentelemetry tracing bridge. (#1305)
- Add an opencensus binary propagation implementation. (#1334)

### Changed

Expand Down
86 changes: 86 additions & 0 deletions bridge/opencensus/binary/binary.go
Original file line number Diff line number Diff line change
@@ -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

// traceContextKey is the same as 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}
}
138 changes: 138 additions & 0 deletions bridge/opencensus/binary/binary_test.go
Original file line number Diff line number Diff line change
@@ -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 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: "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: "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)
}
})
}
}
33 changes: 3 additions & 30 deletions bridge/opencensus/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -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/global"
"go.opentelemetry.io/otel/label"
Expand Down Expand Up @@ -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...)
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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() {
global.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,
}
}
3 changes: 2 additions & 1 deletion bridge/opencensus/bridge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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()
Expand Down
15 changes: 15 additions & 0 deletions bridge/opencensus/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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 // import "go.opentelemetry.io/otel/bridge/opencensus"
4 changes: 2 additions & 2 deletions bridge/opencensus/go.mod
Original file line number Diff line number Diff line change
@@ -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
Expand Down
57 changes: 57 additions & 0 deletions bridge/opencensus/utils/utils.go
Original file line number Diff line number Diff line change
@@ -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/global"
"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() {
global.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,
}
}
Loading

0 comments on commit 041ee1a

Please sign in to comment.