OpenTelemetry provides bridges to help migrate from OpenCensus and OpenTracing to OpenTelemetry.
Bridges allow gradual migration:
Migrate from OpenCensus to OpenTelemetry while maintaining existing OpenCensus instrumentation.
Package: go.opentelemetry.io/otel/bridge/opencensus
go get go.opentelemetry.io/otel/bridge/opencensus@v1.38.0package main
import (
"context"
"log"
"go.opencensus.io/trace"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/bridge/opencensus"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
func main() {
ctx := context.Background()
// Setup OpenTelemetry
exporter, err := otlptracehttp.New(ctx)
if err != nil {
log.Fatal(err)
}
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
)
otel.SetTracerProvider(tp)
// Install OpenCensus bridge
opencensus.InstallTraceBridge(opencensus.WithTracerProvider(tp))
// Now OpenCensus instrumentation will send data to OpenTelemetry
ctx, span := trace.StartSpan(ctx, "opencensus-operation")
defer span.End()
// Your code using OpenCensus
}Installs the OpenCensus trace bridge, which overwrites the global OpenCensus tracer implementation.
func InstallTraceBridge(opts ...TraceOption)func WithTracerProvider(tp trace.TracerProvider) TraceOptionConvert between OpenCensus and OpenTelemetry span contexts.
func OTelSpanContextToOC(sc trace.SpanContext) octrace.SpanContext
func OCSpanContextToOTel(sc octrace.SpanContext) trace.SpanContextPhase 1: Bridge Installation
// Install bridge
opencensus.InstallTraceBridge(opencensus.WithTracerProvider(tp))
// Existing OpenCensus code works unchanged
ctx, span := trace.StartSpan(ctx, "operation")
defer span.End()Phase 2: Gradual Migration
// New code uses OpenTelemetry directly
import (
otelTrace "go.opentelemetry.io/otel/trace"
)
// New code
tracer := otel.Tracer("my-service")
ctx, span := tracer.Start(ctx, "new-operation")
defer span.End()
// Old code still uses OpenCensus (via bridge)
ctx, ocSpan := trace.StartSpan(ctx, "legacy-operation")
defer ocSpan.End()Phase 3: Complete Migration
// Remove OpenCensus dependency
// All code uses OpenTelemetry
tracer := otel.Tracer("my-service")
ctx, span := tracer.Start(ctx, "operation")
defer span.End()Use OpenTracing instrumentation with OpenTelemetry.
Package: go.opentelemetry.io/otel/bridge/opentracing
go get go.opentelemetry.io/otel/bridge/opentracing@v1.38.0package main
import (
"context"
"log"
"github.com/opentracing/opentracing-go"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/bridge/opentracing"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
func main() {
ctx := context.Background()
// Setup OpenTelemetry
exporter, err := otlptracehttp.New(ctx)
if err != nil {
log.Fatal(err)
}
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
)
otel.SetTracerProvider(tp)
// Create OpenTracing bridge
bridgeTracer, wrapperTracerProvider := opentracing.NewTracerPair(tp.Tracer("opentracing-bridge"))
opentracing.SetGlobalTracer(bridgeTracer)
defer wrapperTracerProvider.Shutdown(ctx)
// Now OpenTracing instrumentation will send data to OpenTelemetry
span := opentracing.GlobalTracer().StartSpan("opentracing-operation")
defer span.Finish()
// Your code using OpenTracing
}Creates an OpenTracing-compatible tracer and TracerProvider.
func NewTracerPair(tracer trace.Tracer) (*BridgeTracer, *WrapperTracerProvider)Phase 1: Bridge Installation
// Install bridge
bridgeTracer, wrapperTP := opentracing.NewTracerPair(tp.Tracer("opentracing-bridge"))
opentracing.SetGlobalTracer(bridgeTracer)
// Existing OpenTracing code works unchanged
span := opentracing.GlobalTracer().StartSpan("operation")
defer span.Finish()Phase 2: Gradual Migration
// New code uses OpenTelemetry directly
tracer := otel.Tracer("my-service")
ctx, span := tracer.Start(ctx, "new-operation")
defer span.End()
// Old code still uses OpenTracing (via bridge)
otSpan := opentracing.GlobalTracer().StartSpan("legacy-operation")
defer otSpan.Finish()Phase 3: Complete Migration
// Remove OpenTracing dependency
// All code uses OpenTelemetry
tracer := otel.Tracer("my-service")
ctx, span := tracer.Start(ctx, "operation")
defer span.End()package main
import (
"context"
"log"
"github.com/opentracing/opentracing-go"
ocTrace "go.opencensus.io/trace"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/bridge/opencensus"
"go.opentelemetry.io/otel/bridge/opentracing"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
func main() {
ctx := context.Background()
// Setup OpenTelemetry
exporter, err := otlptracehttp.New(ctx)
if err != nil {
log.Fatal(err)
}
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
)
otel.SetTracerProvider(tp)
// Install OpenCensus bridge
ocTracer := opencensus.NewTracer(tp.Tracer("opencensus-bridge"))
ocTrace.DefaultTracer = ocTracer
// Install OpenTracing bridge
otBridge, otWrapper := opentracing.NewTracerPair(tp.Tracer("opentracing-bridge"))
opentracing.SetGlobalTracer(otBridge)
defer func() {
otWrapper.Shutdown(ctx)
tp.Shutdown(ctx)
}()
// All three tracing systems work together
useOpenTelemetry(ctx)
useOpenCensus(ctx)
useOpenTracing()
}
func useOpenTelemetry(ctx context.Context) {
tracer := otel.Tracer("my-service")
ctx, span := tracer.Start(ctx, "otel-operation")
defer span.End()
// OpenTelemetry code
}
func useOpenCensus(ctx context.Context) {
ctx, span := ocTrace.StartSpan(ctx, "opencensus-operation")
defer span.End()
// OpenCensus code
}
func useOpenTracing() {
span := opentracing.GlobalTracer().StartSpan("opentracing-operation")
defer span.Finish()
// OpenTracing code
}| OpenCensus | OpenTelemetry |
|---|---|
trace.StartSpan(ctx, "name") | tracer.Start(ctx, "name") |
span.End() | span.End() |
span.AddAttributes() | span.SetAttributes() |
span.Annotate() | span.AddEvent() |
span.SetStatus() | span.SetStatus() |
Before (OpenCensus):
import "go.opencensus.io/trace"
ctx, span := trace.StartSpan(ctx, "operation")
defer span.End()
span.AddAttributes(
trace.StringAttribute("key", "value"),
)
span.Annotate([]trace.Attribute{}, "event")After (OpenTelemetry):
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
)
tracer := otel.Tracer("my-service")
ctx, span := tracer.Start(ctx, "operation")
defer span.End()
span.SetAttributes(
attribute.String("key", "value"),
)
span.AddEvent("event")| OpenTracing | OpenTelemetry |
|---|---|
tracer.StartSpan("name") | tracer.Start(ctx, "name") |
span.Finish() | span.End() |
span.SetTag() | span.SetAttributes() |
span.LogFields() | span.AddEvent() |
span.Context() | span.SpanContext() |
Before (OpenTracing):
import (
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
)
span := opentracing.StartSpan("operation")
defer span.Finish()
span.SetTag("key", "value")
span.LogFields(
log.String("event", "something happened"),
)After (OpenTelemetry):
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
)
tracer := otel.Tracer("my-service")
ctx, span := tracer.Start(ctx, "operation")
defer span.End()
span.SetAttributes(
attribute.String("key", "value"),
)
span.AddEvent("something happened")// Good: Use bridge during migration period
bridgeTracer, _ := opentracing.NewTracerPair(tp.Tracer("bridge"))
opentracing.SetGlobalTracer(bridgeTracer)
// Bad: Try to do full migration at once (risky)// New features use OpenTelemetry directly
func newFeature(ctx context.Context) {
tracer := otel.Tracer("my-service")
ctx, span := tracer.Start(ctx, "new-feature")
defer span.End()
}
// Legacy code continues using bridge// Ensure both old and new instrumentation work together
func testMixedInstrumentation(t *testing.T) {
// Test OpenTelemetry
// Test bridged OpenCensus/OpenTracing
// Test interaction between them
}// After migration is complete, remove bridge code
// and uninstall OpenCensus/OpenTracing dependencies