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

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package tracing
     5  
     6  import (
     7  	"fmt"
     8  	"net"
     9  	"net/http"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/go-kit/log"
    14  	"github.com/go-kit/log/level"
    15  	"github.com/opentracing/opentracing-go"
    16  	"github.com/opentracing/opentracing-go/ext"
    17  	"github.com/thanos-io/thanos/pkg/tracing/migration"
    18  )
    19  
    20  // HTTPMiddleware returns an HTTP handler that injects the given tracer and starts a new server span.
    21  // If any client span is fetched from the wire, we include that as our parent.
    22  func HTTPMiddleware(tracer opentracing.Tracer, name string, logger log.Logger, next http.Handler) http.HandlerFunc {
    23  	operationName := fmt.Sprintf("/%s HTTP[server]", name)
    24  
    25  	return func(w http.ResponseWriter, r *http.Request) {
    26  		wireContext, err := tracer.Extract(
    27  			opentracing.HTTPHeaders,
    28  			opentracing.HTTPHeadersCarrier(r.Header),
    29  		)
    30  		if err != nil && err != opentracing.ErrSpanContextNotFound {
    31  			level.Error(logger).Log("msg", "failed to extract tracer from request", "operationName", operationName, "err", err)
    32  		}
    33  
    34  		opts := []opentracing.StartSpanOption{ext.RPCServerOption(wireContext)}
    35  		// Check for force tracing header and add it as a tag at the start of span.
    36  		// This is required for the OpenTelemetry sampler to force tracing.
    37  		if r.Header.Get(ForceTracingBaggageKey) != "" {
    38  			opts = append(opts, opentracing.Tag{Key: migration.ForceTracingAttributeKey, Value: "true"})
    39  		}
    40  
    41  		span := tracer.StartSpan(
    42  			operationName,
    43  			opts...,
    44  		)
    45  		ext.HTTPMethod.Set(span, r.Method)
    46  		ext.HTTPUrl.Set(span, r.URL.String())
    47  
    48  		// If client specified ForceTracingBaggageKey header, ensure span includes it to force tracing.
    49  		span.SetBaggageItem(strings.ToLower(ForceTracingBaggageKey), r.Header.Get(ForceTracingBaggageKey))
    50  
    51  		if t, ok := tracer.(Tracer); ok {
    52  			if traceID, ok := t.GetTraceIDFromSpanContext(span.Context()); ok {
    53  				w.Header().Set(traceIDResponseHeader, traceID)
    54  			}
    55  		} else {
    56  			// Alternative to set trace ID header, if bridge tracer is being used.
    57  			if traceID, ok := migration.GetTraceIDFromBridgeSpan(span); ok {
    58  				w.Header().Set(traceIDResponseHeader, traceID)
    59  			}
    60  		}
    61  
    62  		next.ServeHTTP(w, r.WithContext(opentracing.ContextWithSpan(ContextWithTracer(r.Context(), tracer), span)))
    63  		span.Finish()
    64  	}
    65  }
    66  
    67  type tripperware struct {
    68  	logger log.Logger
    69  	next   http.RoundTripper
    70  }
    71  
    72  func (t *tripperware) RoundTrip(r *http.Request) (*http.Response, error) {
    73  	tracer := tracerFromContext(r.Context())
    74  	if tracer == nil {
    75  		// No tracer, programmatic mistake.
    76  		level.Warn(t.logger).Log("msg", "Tracer not found in context.")
    77  		return t.next.RoundTrip(r)
    78  	}
    79  
    80  	span := opentracing.SpanFromContext(r.Context())
    81  	if span == nil {
    82  		// No span.
    83  		return t.next.RoundTrip(r)
    84  	}
    85  
    86  	ext.HTTPMethod.Set(span, r.Method)
    87  	ext.HTTPUrl.Set(span, r.URL.String())
    88  	host, portString, err := net.SplitHostPort(r.URL.Host)
    89  	if err == nil {
    90  		ext.PeerHostname.Set(span, host)
    91  		if port, err := strconv.Atoi(portString); err != nil {
    92  			ext.PeerPort.Set(span, uint16(port))
    93  		}
    94  	} else {
    95  		ext.PeerHostname.Set(span, r.URL.Host)
    96  	}
    97  
    98  	// There's nothing we can do with any errors here.
    99  	if err = tracer.Inject(
   100  		span.Context(),
   101  		opentracing.HTTPHeaders,
   102  		opentracing.HTTPHeadersCarrier(r.Header),
   103  	); err != nil {
   104  		level.Warn(t.logger).Log("msg", "failed to inject trace", "err", err)
   105  	}
   106  
   107  	resp, err := t.next.RoundTrip(r)
   108  	return resp, err
   109  }
   110  
   111  // HTTPTripperware returns HTTP tripper that assumes given span in context as client child span and injects it into the wire.
   112  // NOTE: It assumes tracer is given in request context. Also, it is caller responsibility to finish span.
   113  func HTTPTripperware(logger log.Logger, next http.RoundTripper) http.RoundTripper {
   114  	return &tripperware{
   115  		logger: logger,
   116  		next:   next,
   117  	}
   118  }