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  }