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 }