github.com/thanos-io/thanos@v0.32.5/pkg/tracing/tracing.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package tracing 5 6 import ( 7 "context" 8 9 "github.com/opentracing/opentracing-go" 10 "github.com/opentracing/opentracing-go/ext" 11 ) 12 13 const ( 14 // ForceTracingBaggageKey is a request header name that forces tracing sampling. 15 ForceTracingBaggageKey = "X-Thanos-Force-Tracing" 16 17 // traceIdResponseHeader is a response header name that stores trace ID. 18 traceIDResponseHeader = "X-Thanos-Trace-Id" 19 ) 20 21 // Aliases to avoid spreading opentracing package to Thanos code. 22 23 type Tag = opentracing.Tag 24 type Tags = opentracing.Tags 25 type Span = opentracing.Span 26 27 type contextKey struct{} 28 29 var tracerKey = contextKey{} 30 31 // Tracer interface to provide GetTraceIDFromSpanContext method. 32 type Tracer interface { 33 GetTraceIDFromSpanContext(ctx opentracing.SpanContext) (string, bool) 34 } 35 36 // ContextWithTracer returns a new `context.Context` that holds a reference to given opentracing.Tracer. 37 func ContextWithTracer(ctx context.Context, tracer opentracing.Tracer) context.Context { 38 return context.WithValue(ctx, tracerKey, tracer) 39 } 40 41 // tracerFromContext extracts opentracing.Tracer from the given context. 42 func tracerFromContext(ctx context.Context) opentracing.Tracer { 43 val := ctx.Value(tracerKey) 44 if sp, ok := val.(opentracing.Tracer); ok { 45 return sp 46 } 47 return nil 48 } 49 50 // CopyTraceContext copies the necessary trace context from given source context to target context. 51 func CopyTraceContext(trgt, src context.Context) context.Context { 52 ctx := ContextWithTracer(trgt, tracerFromContext(src)) 53 if parentSpan := opentracing.SpanFromContext(src); parentSpan != nil { 54 ctx = opentracing.ContextWithSpan(ctx, parentSpan) 55 } 56 return ctx 57 } 58 59 // StartSpan starts and returns span with `operationName` and hooking as child to a span found within given context if any. 60 // It uses opentracing.Tracer propagated in context. If no found, it uses noop tracer without notification. 61 func StartSpan(ctx context.Context, operationName string, opts ...opentracing.StartSpanOption) (Span, context.Context) { 62 tracer := tracerFromContext(ctx) 63 if tracer == nil { 64 // No tracing found, return noop span. 65 return opentracing.NoopTracer{}.StartSpan(operationName), ctx 66 } 67 68 var span Span 69 if parentSpan := opentracing.SpanFromContext(ctx); parentSpan != nil { 70 opts = append(opts, opentracing.ChildOf(parentSpan.Context())) 71 } 72 span = tracer.StartSpan(operationName, opts...) 73 return span, opentracing.ContextWithSpan(ctx, span) 74 } 75 76 // DoInSpanWithErr executes function doFn inside new span with `operationName` name and hooking as child to a span found within given context if any. 77 // It uses opentracing.Tracer propagated in context. If no found, it uses noop tracer notification. 78 // It logs the error inside the new span created, which differentiates it from DoInSpan and DoWithSpan. 79 func DoInSpanWithErr(ctx context.Context, operationName string, doFn func(context.Context) error, opts ...opentracing.StartSpanOption) error { 80 span, newCtx := StartSpan(ctx, operationName, opts...) 81 defer span.Finish() 82 err := doFn(newCtx) 83 if err != nil { 84 ext.LogError(span, err) 85 } 86 87 return err 88 } 89 90 // DoInSpan executes function doFn inside new span with `operationName` name and hooking as child to a span found within given context if any. 91 // It uses opentracing.Tracer propagated in context. If no found, it uses noop tracer notification. 92 func DoInSpan(ctx context.Context, operationName string, doFn func(context.Context), opts ...opentracing.StartSpanOption) { 93 span, newCtx := StartSpan(ctx, operationName, opts...) 94 defer span.Finish() 95 doFn(newCtx) 96 } 97 98 // DoWithSpan executes function doFn inside new span with `operationName` name and hooking as child to a span found within given context if any. 99 // It uses opentracing.Tracer propagated in context. If no found, it uses noop tracer notification. 100 func DoWithSpan(ctx context.Context, operationName string, doFn func(context.Context, Span), opts ...opentracing.StartSpanOption) { 101 span, newCtx := StartSpan(ctx, operationName, opts...) 102 defer span.Finish() 103 doFn(newCtx, span) 104 }