Skip to content

Commit

Permalink
Add OpenCensus bridge internal package (#2146)
Browse files Browse the repository at this point in the history
* Add internal pkg to bridge/opencensus

* Use the bridge/opencensus/internal pkg

* Correct testing comments

* Remove added unit tests
  • Loading branch information
MrAlias committed Aug 6, 2021
1 parent fcf945a commit d18c135
Show file tree
Hide file tree
Showing 12 changed files with 466 additions and 238 deletions.
165 changes: 2 additions & 163 deletions bridge/opencensus/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,176 +15,15 @@
package opencensus

import (
"context"
"fmt"

octrace "go.opencensus.io/trace"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/bridge/opencensus/utils"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/bridge/opencensus/internal"
"go.opentelemetry.io/otel/trace"
)

// NewTracer returns an implementation of the OpenCensus Tracer interface which
// uses OpenTelemetry APIs. Using this implementation of Tracer "upgrades"
// libraries that use OpenCensus to OpenTelemetry to facilitate a migration.
func NewTracer(tracer trace.Tracer) octrace.Tracer {
return &otelTracer{tracer: tracer}
}

type otelTracer struct {
tracer trace.Tracer
}

var _ octrace.Tracer = (*otelTracer)(nil)

func (o *otelTracer) StartSpan(ctx context.Context, name string, s ...octrace.StartOption) (context.Context, *octrace.Span) {
ctx, sp := o.tracer.Start(ctx, name, convertStartOptions(s, name)...)
return ctx, octrace.NewSpan(&span{otSpan: sp})
}

func convertStartOptions(optFns []octrace.StartOption, name string) []trace.SpanStartOption {
var ocOpts octrace.StartOptions
for _, fn := range optFns {
fn(&ocOpts)
}
otOpts := []trace.SpanStartOption{}
switch ocOpts.SpanKind {
case octrace.SpanKindClient:
otOpts = append(otOpts, trace.WithSpanKind(trace.SpanKindClient))
case octrace.SpanKindServer:
otOpts = append(otOpts, trace.WithSpanKind(trace.SpanKindServer))
case octrace.SpanKindUnspecified:
otOpts = append(otOpts, trace.WithSpanKind(trace.SpanKindUnspecified))
}

if ocOpts.Sampler != nil {
otel.Handle(fmt.Errorf("ignoring custom sampler for span %q created by OpenCensus because OpenTelemetry does not support creating a span with a custom sampler", name))
}
return otOpts
}

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, utils.OCSpanContextToOTel(parent))
return o.StartSpan(ctx, name, s...)
}

func (o *otelTracer) FromContext(ctx context.Context) *octrace.Span {
otSpan := trace.SpanFromContext(ctx)
return octrace.NewSpan(&span{otSpan: otSpan})
}

func (o *otelTracer) NewContext(parent context.Context, s *octrace.Span) context.Context {
if otSpan, ok := s.Internal().(*span); ok {
return trace.ContextWithSpan(parent, otSpan.otSpan)
}
otel.Handle(fmt.Errorf("unable to create context with span %q, since it was created using a different tracer", s.String()))
return parent
}

type span struct {
otSpan trace.Span
}

func (s *span) IsRecordingEvents() bool {
return s.otSpan.IsRecording()
}

func (s *span) End() {
s.otSpan.End()
}

func (s *span) SpanContext() octrace.SpanContext {
return utils.OTelSpanContextToOC(s.otSpan.SpanContext())
}

func (s *span) SetName(name string) {
s.otSpan.SetName(name)
}

func (s *span) SetStatus(status octrace.Status) {
s.otSpan.SetStatus(codes.Code(status.Code), status.Message)
}

func (s *span) AddAttributes(attributes ...octrace.Attribute) {
s.otSpan.SetAttributes(convertAttributes(attributes)...)
}

func convertAttributes(attributes []octrace.Attribute) []attribute.KeyValue {
otAttributes := make([]attribute.KeyValue, len(attributes))
for i, a := range attributes {
otAttributes[i] = attribute.KeyValue{
Key: attribute.Key(a.Key()),
Value: convertValue(a.Value()),
}
}
return otAttributes
}

func convertValue(ocval interface{}) attribute.Value {
switch v := ocval.(type) {
case bool:
return attribute.BoolValue(v)
case int64:
return attribute.Int64Value(v)
case float64:
return attribute.Float64Value(v)
case string:
return attribute.StringValue(v)
default:
return attribute.StringValue("unknown")
}
}

func (s *span) Annotate(attributes []octrace.Attribute, str string) {
s.otSpan.AddEvent(str, trace.WithAttributes(convertAttributes(attributes)...))
}

func (s *span) Annotatef(attributes []octrace.Attribute, format string, a ...interface{}) {
s.Annotate(attributes, fmt.Sprintf(format, a...))
}

var (
uncompressedKey = attribute.Key("uncompressed byte size")
compressedKey = attribute.Key("compressed byte size")
)

func (s *span) AddMessageSendEvent(messageID, uncompressedByteSize, compressedByteSize int64) {
s.otSpan.AddEvent("message send",
trace.WithAttributes(
attribute.KeyValue{
Key: uncompressedKey,
Value: attribute.Int64Value(uncompressedByteSize),
},
attribute.KeyValue{
Key: compressedKey,
Value: attribute.Int64Value(compressedByteSize),
}),
)
}

func (s *span) AddMessageReceiveEvent(messageID, uncompressedByteSize, compressedByteSize int64) {
s.otSpan.AddEvent("message receive",
trace.WithAttributes(
attribute.KeyValue{
Key: uncompressedKey,
Value: attribute.Int64Value(uncompressedByteSize),
},
attribute.KeyValue{
Key: compressedKey,
Value: attribute.Int64Value(compressedByteSize),
}),
)
}

func (s *span) AddLink(l octrace.Link) {
otel.Handle(fmt.Errorf("ignoring OpenCensus link %+v for span %q because OpenTelemetry doesn't support setting links after creation", l, s.String()))
}

func (s *span) String() string {
return fmt.Sprintf("span %s", s.otSpan.SpanContext().SpanID().String())
return internal.NewTracer(tracer)
}
15 changes: 8 additions & 7 deletions bridge/opencensus/bridge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
octrace "go.opencensus.io/trace"

"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/bridge/opencensus/internal"
"go.opentelemetry.io/otel/bridge/opencensus/utils"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/oteltest"
Expand Down Expand Up @@ -129,7 +130,7 @@ func TestToFromContext(t *testing.T) {
defer otSpan1.End()

// Use NewContext instead of the context from Start
ctx = octrace.NewContext(ctx, octrace.NewSpan(&span{otSpan: otSpan1}))
ctx = octrace.NewContext(ctx, internal.NewSpan(otSpan1))

ctx, _ = tracer.Start(ctx, "OpenTelemetrySpan2")

Expand Down Expand Up @@ -256,22 +257,22 @@ func TestSetThings(t *testing.T) {
if v := annotateEvent.Attributes[attribute.Key("string")]; v.AsString() != "annotateval" {
t.Errorf("Got annotateEvent.Attributes[string] = %v, expected annotateval", v.AsString())
}
if sendEvent.Name != "message send" {
if sendEvent.Name != internal.MessageSendEvent {
t.Errorf("Got sendEvent.Name = %v, expected message send", sendEvent.Name)
}
if v := sendEvent.Attributes[uncompressedKey]; v.AsInt64() != 456 {
if v := sendEvent.Attributes[internal.UncompressedKey]; v.AsInt64() != 456 {
t.Errorf("Got sendEvent.Attributes[uncompressedKey] = %v, expected 456", v.AsInt64())
}
if v := sendEvent.Attributes[compressedKey]; v.AsInt64() != 789 {
if v := sendEvent.Attributes[internal.CompressedKey]; v.AsInt64() != 789 {
t.Errorf("Got sendEvent.Attributes[compressedKey] = %v, expected 789", v.AsInt64())
}
if receiveEvent.Name != "message receive" {
if receiveEvent.Name != internal.MessageReceiveEvent {
t.Errorf("Got receiveEvent.Name = %v, expected message receive", receiveEvent.Name)
}
if v := receiveEvent.Attributes[uncompressedKey]; v.AsInt64() != 135 {
if v := receiveEvent.Attributes[internal.UncompressedKey]; v.AsInt64() != 135 {
t.Errorf("Got receiveEvent.Attributes[uncompressedKey] = %v, expected 135", v.AsInt64())
}
if v := receiveEvent.Attributes[compressedKey]; v.AsInt64() != 369 {
if v := receiveEvent.Attributes[internal.CompressedKey]; v.AsInt64() != 369 {
t.Errorf("Got receiveEvent.Attributes[compressedKey] = %v, expected 369", v.AsInt64())
}
}
21 changes: 21 additions & 0 deletions bridge/opencensus/internal/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// 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 internal

import "go.opentelemetry.io/otel"

// Handle is the package level function to handle errors. It can be
// overwritten for testing.
var Handle = otel.Handle
47 changes: 47 additions & 0 deletions bridge/opencensus/internal/oc2otel/attributes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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 oc2otel

import (
octrace "go.opencensus.io/trace"

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

func Attributes(attr []octrace.Attribute) []attribute.KeyValue {
otelAttr := make([]attribute.KeyValue, len(attr))
for i, a := range attr {
otelAttr[i] = attribute.KeyValue{
Key: attribute.Key(a.Key()),
Value: AttributeValue(a.Value()),
}
}
return otelAttr
}

func AttributeValue(ocval interface{}) attribute.Value {
switch v := ocval.(type) {
case bool:
return attribute.BoolValue(v)
case int64:
return attribute.Int64Value(v)
case float64:
return attribute.Float64Value(v)
case string:
return attribute.StringValue(v)
default:
return attribute.StringValue("unknown")
}
}
33 changes: 33 additions & 0 deletions bridge/opencensus/internal/oc2otel/span_context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// 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 oc2otel

import (
octrace "go.opencensus.io/trace"

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

func SpanContext(sc octrace.SpanContext) trace.SpanContext {
var traceFlags trace.TraceFlags
if sc.IsSampled() {
traceFlags = trace.FlagsSampled
}
return trace.NewSpanContext(trace.SpanContextConfig{
TraceID: trace.TraceID(sc.TraceID),
SpanID: trace.SpanID(sc.SpanID),
TraceFlags: traceFlags,
})
}
Loading

0 comments on commit d18c135

Please sign in to comment.