diff --git a/bridge/opencensus/bridge.go b/bridge/opencensus/bridge.go index ecf1d5181ca..13f676fae46 100644 --- a/bridge/opencensus/bridge.go +++ b/bridge/opencensus/bridge.go @@ -15,15 +15,9 @@ 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" ) @@ -31,160 +25,5 @@ import ( // 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) } diff --git a/bridge/opencensus/bridge_test.go b/bridge/opencensus/bridge_test.go index 4616500dfa1..90c8c2e7a32 100644 --- a/bridge/opencensus/bridge_test.go +++ b/bridge/opencensus/bridge_test.go @@ -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" @@ -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") @@ -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()) } } diff --git a/bridge/opencensus/internal/handler.go b/bridge/opencensus/internal/handler.go new file mode 100644 index 00000000000..2d6a5160049 --- /dev/null +++ b/bridge/opencensus/internal/handler.go @@ -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 diff --git a/bridge/opencensus/internal/oc2otel/attributes.go b/bridge/opencensus/internal/oc2otel/attributes.go new file mode 100644 index 00000000000..aea7f42693c --- /dev/null +++ b/bridge/opencensus/internal/oc2otel/attributes.go @@ -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") + } +} diff --git a/bridge/opencensus/internal/oc2otel/span_context.go b/bridge/opencensus/internal/oc2otel/span_context.go new file mode 100644 index 00000000000..32c120c7b55 --- /dev/null +++ b/bridge/opencensus/internal/oc2otel/span_context.go @@ -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, + }) +} diff --git a/bridge/opencensus/utils/utils_test.go b/bridge/opencensus/internal/oc2otel/span_context_test.go similarity index 63% rename from bridge/opencensus/utils/utils_test.go rename to bridge/opencensus/internal/oc2otel/span_context_test.go index cc9674c7fdd..ba0f1d0bfcc 100644 --- a/bridge/opencensus/utils/utils_test.go +++ b/bridge/opencensus/internal/oc2otel/span_context_test.go @@ -12,63 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package utils +package oc2otel import ( "testing" - "go.opencensus.io/trace/tracestate" - octrace "go.opencensus.io/trace" + "go.opencensus.io/trace/tracestate" "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.NewSpanContext(trace.SpanContextConfig{ - 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.NewSpanContext(trace.SpanContextConfig{ - 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), - }, - }, - } { - 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) { +func TestSpanContextConversion(t *testing.T) { for _, tc := range []struct { description string input octrace.SpanContext @@ -116,7 +71,7 @@ func TestOCSpanContextToOTel(t *testing.T) { }, } { t.Run(tc.description, func(t *testing.T) { - output := OCSpanContextToOTel(tc.input) + output := SpanContext(tc.input) if !output.Equal(tc.expected) { t.Fatalf("Got %+v spancontext, exepected %+v.", output, tc.expected) } diff --git a/bridge/opencensus/internal/oc2otel/tracer_start_options.go b/bridge/opencensus/internal/oc2otel/tracer_start_options.go new file mode 100644 index 00000000000..b867329103d --- /dev/null +++ b/bridge/opencensus/internal/oc2otel/tracer_start_options.go @@ -0,0 +1,46 @@ +// 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 ( + "fmt" + + octrace "go.opencensus.io/trace" + + "go.opentelemetry.io/otel/trace" +) + +func StartOptions(optFuncs []octrace.StartOption) ([]trace.SpanStartOption, error) { + var ocOpts octrace.StartOptions + for _, fn := range optFuncs { + fn(&ocOpts) + } + + var otelOpts []trace.SpanStartOption + switch ocOpts.SpanKind { + case octrace.SpanKindClient: + otelOpts = append(otelOpts, trace.WithSpanKind(trace.SpanKindClient)) + case octrace.SpanKindServer: + otelOpts = append(otelOpts, trace.WithSpanKind(trace.SpanKindServer)) + case octrace.SpanKindUnspecified: + otelOpts = append(otelOpts, trace.WithSpanKind(trace.SpanKindUnspecified)) + } + + var err error + if ocOpts.Sampler != nil { + err = fmt.Errorf("unsupported sampler: %v", ocOpts.Sampler) + } + return otelOpts, err +} diff --git a/bridge/opencensus/internal/otel2oc/span_context.go b/bridge/opencensus/internal/otel2oc/span_context.go new file mode 100644 index 00000000000..a9bbf2c68f9 --- /dev/null +++ b/bridge/opencensus/internal/otel2oc/span_context.go @@ -0,0 +1,34 @@ +// 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 otel2oc + +import ( + octrace "go.opencensus.io/trace" + + "go.opentelemetry.io/otel/trace" +) + +func SpanContext(sc trace.SpanContext) octrace.SpanContext { + 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, + } +} diff --git a/bridge/opencensus/internal/otel2oc/span_context_test.go b/bridge/opencensus/internal/otel2oc/span_context_test.go new file mode 100644 index 00000000000..236aa89689a --- /dev/null +++ b/bridge/opencensus/internal/otel2oc/span_context_test.go @@ -0,0 +1,67 @@ +// 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 otel2oc + +import ( + "testing" + + octrace "go.opencensus.io/trace" + + "go.opentelemetry.io/otel/trace" +) + +func TestSpanContextConversion(t *testing.T) { + for _, tc := range []struct { + description string + input trace.SpanContext + expected octrace.SpanContext + }{ + { + description: "empty", + }, + { + description: "sampled", + input: trace.NewSpanContext(trace.SpanContextConfig{ + 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.NewSpanContext(trace.SpanContextConfig{ + 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), + }, + }, + } { + t.Run(tc.description, func(t *testing.T) { + output := SpanContext(tc.input) + if output != tc.expected { + t.Fatalf("Got %+v spancontext, exepected %+v.", output, tc.expected) + } + }) + } +} diff --git a/bridge/opencensus/internal/span.go b/bridge/opencensus/internal/span.go new file mode 100644 index 00000000000..0f8533d9fe6 --- /dev/null +++ b/bridge/opencensus/internal/span.go @@ -0,0 +1,131 @@ +// 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 ( + "fmt" + + octrace "go.opencensus.io/trace" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/bridge/opencensus/internal/oc2otel" + "go.opentelemetry.io/otel/bridge/opencensus/internal/otel2oc" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" +) + +const ( + // MessageSendEvent is the name of the message send event. + MessageSendEvent = "message send" + // MessageReceiveEvent is the name of the message receive event. + MessageReceiveEvent = "message receive" +) + +var ( + // UncompressedKey is used for the uncompressed byte size attribute. + UncompressedKey = attribute.Key("uncompressed byte size") + // CompressedKey is used for the compressed byte size attribute. + CompressedKey = attribute.Key("compressed byte size") +) + +// Span is an OpenCensus SpanInterface wrapper for an OpenTelemetry Span. +type Span struct { + otelSpan trace.Span +} + +// NewSpan returns an OpenCensus Span wrapping an OpenTelemetry Span. +func NewSpan(s trace.Span) *octrace.Span { + return octrace.NewSpan(&Span{otelSpan: s}) +} + +// IsRecordingEvents returns true if events are being recorded for this span. +func (s *Span) IsRecordingEvents() bool { + return s.otelSpan.IsRecording() +} + +// End ends thi span. +func (s *Span) End() { + s.otelSpan.End() +} + +// SpanContext returns the SpanContext of this span. +func (s *Span) SpanContext() octrace.SpanContext { + return otel2oc.SpanContext(s.otelSpan.SpanContext()) +} + +// SetName sets the name of this span, if it is recording events. +func (s *Span) SetName(name string) { + s.otelSpan.SetName(name) +} + +// SetStatus sets the status of this span, if it is recording events. +func (s *Span) SetStatus(status octrace.Status) { + s.otelSpan.SetStatus(codes.Code(status.Code), status.Message) +} + +// AddAttributes sets attributes in this span. +func (s *Span) AddAttributes(attributes ...octrace.Attribute) { + s.otelSpan.SetAttributes(oc2otel.Attributes(attributes)...) +} + +// Annotate adds an annotation with attributes to this span. +func (s *Span) Annotate(attributes []octrace.Attribute, str string) { + s.otelSpan.AddEvent(str, trace.WithAttributes(oc2otel.Attributes(attributes)...)) +} + +// Annotatef adds a formatted annotation with attributes to this span. +func (s *Span) Annotatef(attributes []octrace.Attribute, format string, a ...interface{}) { + s.Annotate(attributes, fmt.Sprintf(format, a...)) +} + +// AddMessageSendEvent adds a message send event to this span. +func (s *Span) AddMessageSendEvent(messageID, uncompressedByteSize, compressedByteSize int64) { + s.otelSpan.AddEvent(MessageSendEvent, + trace.WithAttributes( + attribute.KeyValue{ + Key: UncompressedKey, + Value: attribute.Int64Value(uncompressedByteSize), + }, + attribute.KeyValue{ + Key: CompressedKey, + Value: attribute.Int64Value(compressedByteSize), + }), + ) +} + +// AddMessageReceiveEvent adds a message receive event to this span. +func (s *Span) AddMessageReceiveEvent(messageID, uncompressedByteSize, compressedByteSize int64) { + s.otelSpan.AddEvent(MessageReceiveEvent, + trace.WithAttributes( + attribute.KeyValue{ + Key: UncompressedKey, + Value: attribute.Int64Value(uncompressedByteSize), + }, + attribute.KeyValue{ + Key: CompressedKey, + Value: attribute.Int64Value(compressedByteSize), + }), + ) +} + +// AddLink adds a link to this span. +func (s *Span) AddLink(l octrace.Link) { + Handle(fmt.Errorf("ignoring OpenCensus link %+v for span %q because OpenTelemetry doesn't support setting links after creation", l, s.String())) +} + +// String prints a string representation of this span. +func (s *Span) String() string { + return fmt.Sprintf("span %s", s.otelSpan.SpanContext().SpanID().String()) +} diff --git a/bridge/opencensus/internal/tracer.go b/bridge/opencensus/internal/tracer.go new file mode 100644 index 00000000000..8590933a2b9 --- /dev/null +++ b/bridge/opencensus/internal/tracer.go @@ -0,0 +1,69 @@ +// 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 ( + "context" + "fmt" + + octrace "go.opencensus.io/trace" + + "go.opentelemetry.io/otel/bridge/opencensus/internal/oc2otel" + "go.opentelemetry.io/otel/trace" +) + +// Tracer is an OpenCensus Tracer that wraps an OpenTelemetry Tracer. +type Tracer struct { + otelTracer trace.Tracer +} + +// NewTracer returns an OpenCensus Tracer that wraps the OpenTelemetry tracer. +func NewTracer(tracer trace.Tracer) octrace.Tracer { + return &Tracer{otelTracer: tracer} +} + +// StartSpan starts a new child span of the current span in the context. If +// there is no span in the context, it creates a new trace and span. +func (o *Tracer) StartSpan(ctx context.Context, name string, s ...octrace.StartOption) (context.Context, *octrace.Span) { + otelOpts, err := oc2otel.StartOptions(s) + if err != nil { + Handle(fmt.Errorf("starting span %q: %w", name, err)) + } + ctx, sp := o.otelTracer.Start(ctx, name, otelOpts...) + return ctx, NewSpan(sp) +} + +// StartSpanWithRemoteParent starts a new child span of the span from the +// given parent. +func (o *Tracer) 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, oc2otel.SpanContext(parent)) + return o.StartSpan(ctx, name, s...) +} + +// FromContext returns the Span stored in a context. +func (o *Tracer) FromContext(ctx context.Context) *octrace.Span { + return NewSpan(trace.SpanFromContext(ctx)) +} + +// NewContext returns a new context with the given Span attached. +func (o *Tracer) NewContext(parent context.Context, s *octrace.Span) context.Context { + if otSpan, ok := s.Internal().(*Span); ok { + return trace.ContextWithSpan(parent, otSpan.otelSpan) + } + Handle(fmt.Errorf("unable to create context with span %q, since it was created using a different tracer", s.String())) + return parent +} diff --git a/bridge/opencensus/utils/utils.go b/bridge/opencensus/utils/utils.go index 91f586d57a7..d6ff297530d 100644 --- a/bridge/opencensus/utils/utils.go +++ b/bridge/opencensus/utils/utils.go @@ -17,6 +17,8 @@ package utils // import "go.opentelemetry.io/otel/bridge/opencensus/utils" import ( octrace "go.opencensus.io/trace" + "go.opentelemetry.io/otel/bridge/opencensus/internal/oc2otel" + "go.opentelemetry.io/otel/bridge/opencensus/internal/otel2oc" "go.opentelemetry.io/otel/trace" ) @@ -24,28 +26,11 @@ import ( // OpenCensus SpanContext, and handles any incompatibilities with the global // error handler. func OTelSpanContextToOC(sc trace.SpanContext) octrace.SpanContext { - 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, - } + return otel2oc.SpanContext(sc) } // OCSpanContextToOTel converts from an OpenCensus SpanContext to an // OpenTelemetry SpanContext. func OCSpanContextToOTel(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, - }) + return oc2otel.SpanContext(sc) }