Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OpenCensus bridge internal package #2146

Merged
merged 9 commits into from
Aug 6, 2021
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