github.com/thanos-io/thanos@v0.32.5/pkg/tracing/migration/bridge.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package migration
     5  
     6  import (
     7  	"context"
     8  	"io"
     9  	"time"
    10  
    11  	"github.com/go-kit/log"
    12  	"github.com/go-kit/log/level"
    13  	"github.com/opentracing/opentracing-go"
    14  	"go.opentelemetry.io/contrib/propagators/autoprop"
    15  	"go.opentelemetry.io/otel"
    16  	bridge "go.opentelemetry.io/otel/bridge/opentracing"
    17  	tracesdk "go.opentelemetry.io/otel/sdk/trace"
    18  	"go.opentelemetry.io/otel/trace"
    19  )
    20  
    21  // Bridge is a method to facilitate migration from OpenTracing (OT) to
    22  // OpenTelemetry (OTEL). It pairs an OTEL tracer with a so-called bridge
    23  // tracer, which satisfies the OT Tracer interface. This makes it possible
    24  // for OT instrumentation to work with an OTEL tracer.
    25  //
    26  // NOTE: After instrumentation migration is finished, this bridge should be
    27  // removed.
    28  func Bridge(tp *tracesdk.TracerProvider, l log.Logger) (opentracing.Tracer, io.Closer) {
    29  	otel.SetErrorHandler(otelErrHandler(func(err error) {
    30  		level.Error(l).Log("msg", "OpenTelemetry ErrorHandler", "err", err)
    31  	}))
    32  	otel.SetTextMapPropagator(autoprop.NewTextMapPropagator())
    33  	otel.SetTracerProvider(tp)
    34  
    35  	bridgeTracer, _ := bridge.NewTracerPair(tp.Tracer(""))
    36  	bridgeTracer.SetWarningHandler(func(warn string) {
    37  		level.Warn(l).Log("msg", "OpenTelemetry BridgeWarningHandler", "warn", warn)
    38  	})
    39  	bridgeTracer.SetTextMapPropagator(autoprop.NewTextMapPropagator())
    40  
    41  	tpShutdownFunc := func() error {
    42  		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    43  		defer cancel()
    44  
    45  		return tp.Shutdown(ctx)
    46  	}
    47  
    48  	return &bridgeTracerWrapper{bt: bridgeTracer}, shutdownAsCloser(tpShutdownFunc)
    49  }
    50  
    51  func GetTraceIDFromBridgeSpan(span opentracing.Span) (string, bool) {
    52  	ctx := bridge.NewBridgeTracer().ContextWithSpanHook(context.Background(), span)
    53  	otelSpan := trace.SpanFromContext(ctx)
    54  	if otelSpan.SpanContext().IsSampled() && otelSpan.SpanContext().IsValid() {
    55  		return otelSpan.SpanContext().TraceID().String(), true
    56  	}
    57  
    58  	return "", false
    59  }
    60  
    61  type otelErrHandler func(err error)
    62  
    63  func (o otelErrHandler) Handle(err error) {
    64  	o(err)
    65  }
    66  
    67  // Workaround to satisfy io.Closer interface.
    68  type shutdownAsCloser func() error
    69  
    70  func (s shutdownAsCloser) Close() error {
    71  	return s()
    72  }
    73  
    74  // This wrapper is necessary to enable proper trace propagation for gRPC
    75  // calls between components. The bridge.BridgeTracer currently supports injection /
    76  // extraction of only single carrier type which is opentracing.HTTPHeadersCarrier.
    77  // (see https://github.com/open-telemetry/opentelemetry-go/blob/main/bridge/opentracing/bridge.go#L626)
    78  //
    79  // To work around this, this wrapper extends Inject / Extract methods to "convert"
    80  // other carrier types to opentracing.HTTPHeadersCarrier, in order to propagate
    81  // data correctly. This is currently, at minimum, required for proper functioning
    82  // of propagation in the gRPC middleware, which uses metadata.MD as a carrier.
    83  // (see https://github.com/grpc-ecosystem/go-grpc-middleware/blob/v2.0.0-rc.2/interceptors/tracing/client.go#L95)
    84  type bridgeTracerWrapper struct {
    85  	bt *bridge.BridgeTracer
    86  }
    87  
    88  func (b *bridgeTracerWrapper) StartSpan(operationName string, opts ...opentracing.StartSpanOption) opentracing.Span {
    89  	return b.bt.StartSpan(operationName, opts...)
    90  }
    91  
    92  func (b *bridgeTracerWrapper) Inject(sm opentracing.SpanContext, format interface{}, carrier interface{}) error {
    93  	otCarrier := opentracing.HTTPHeadersCarrier{}
    94  	err := b.bt.Inject(sm, format, otCarrier)
    95  	if err != nil {
    96  		return err
    97  	}
    98  
    99  	if tmw, ok := carrier.(opentracing.TextMapWriter); ok {
   100  		err := otCarrier.ForeachKey(func(key, val string) error {
   101  			tmw.Set(key, val)
   102  			return nil
   103  		})
   104  		if err != nil {
   105  			return err
   106  		}
   107  	}
   108  
   109  	return b.bt.Inject(sm, format, carrier)
   110  }
   111  
   112  func (b *bridgeTracerWrapper) Extract(format interface{}, carrier interface{}) (opentracing.SpanContext, error) {
   113  	if tmr, ok := carrier.(opentracing.TextMapReader); ok {
   114  		otCarrier := opentracing.HTTPHeadersCarrier{}
   115  		err := tmr.ForeachKey(func(key, val string) error {
   116  			otCarrier.Set(key, val)
   117  			return nil
   118  		})
   119  		if err != nil {
   120  			return nil, err
   121  		}
   122  
   123  		return b.bt.Extract(format, otCarrier)
   124  	}
   125  
   126  	return b.bt.Extract(format, carrier)
   127  }