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 }