github.com/blend/go-sdk@v1.20240719.1/tracing/httptrace/tracer.go (about) 1 /* 2 3 Copyright (c) 2024 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package httptrace 9 10 import ( 11 "context" 12 "fmt" 13 "net/http" 14 "time" 15 16 opentracing "github.com/opentracing/opentracing-go" 17 opentracingExt "github.com/opentracing/opentracing-go/ext" 18 19 "github.com/blend/go-sdk/tracing" 20 "github.com/blend/go-sdk/webutil" 21 ) 22 23 var ( 24 _ webutil.HTTPTracer = (*httpTracer)(nil) 25 _ webutil.HTTPTraceFinisher = (*httpTraceFinisher)(nil) 26 ) 27 28 // Tracer returns an HTTP tracer. 29 func Tracer(tracer opentracing.Tracer) webutil.HTTPTracer { 30 return &httpTracer{tracer: tracer} 31 } 32 33 type httpTracer struct { 34 tracer opentracing.Tracer 35 } 36 37 // StartHTTPSpan opens a span and creates a new request with a modified 38 // context, based on the span that was opened. 39 func StartHTTPSpan(ctx context.Context, tracer opentracing.Tracer, req *http.Request, resource string, startTime time.Time, extra ...opentracing.StartSpanOption) (opentracing.Span, *http.Request) { 40 // set up basic start options (these are mostly tags). 41 startOptions := []opentracing.StartSpanOption{ 42 opentracingExt.SpanKindRPCServer, 43 opentracing.Tag{Key: tracing.TagKeyResourceName, Value: fmt.Sprintf("%s %s", req.Method, resource)}, 44 opentracing.Tag{Key: tracing.TagKeySpanType, Value: tracing.SpanTypeWeb}, 45 opentracing.Tag{Key: tracing.TagKeyHTTPMethod, Value: req.Method}, 46 opentracing.Tag{Key: tracing.TagKeyHTTPURL, Value: req.URL.Path}, 47 opentracing.Tag{Key: "http.remote_addr", Value: webutil.GetRemoteAddr(req)}, 48 opentracing.Tag{Key: "http.host", Value: webutil.GetHost(req)}, 49 opentracing.Tag{Key: "http.user_agent", Value: webutil.GetUserAgent(req)}, 50 tracing.TagMeasured(), 51 opentracing.StartTime(startTime), 52 } 53 startOptions = append(startOptions, extra...) 54 55 // try to extract an incoming span context 56 // this is typically done if we're a service being called in a chain from another (more ancestral) 57 // span context. 58 spanContext, err := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header)) 59 if spanContext != nil && err == nil { 60 startOptions = append(startOptions, opentracing.ChildOf(spanContext)) 61 } 62 63 // start the span. 64 span, spanCtx := tracing.StartSpanFromContext(ctx, tracer, tracing.OperationHTTPRequest, startOptions...) 65 // inject the new context 66 newReq := req.WithContext(spanCtx) 67 return span, newReq 68 } 69 70 // Start opens a span and creates a new request with a modified context, based 71 // on the span that was opened. 72 func (ht httpTracer) Start(req *http.Request) (webutil.HTTPTraceFinisher, *http.Request) { 73 resource := req.URL.Path 74 startTime := time.Now().UTC() 75 span, newReq := StartHTTPSpan(req.Context(), ht.tracer, req, resource, startTime) 76 return &httpTraceFinisher{span: span}, newReq 77 } 78 79 type httpTraceFinisher struct { 80 span opentracing.Span 81 } 82 83 func (htf httpTraceFinisher) Finish(statusCode int, err error) { 84 if htf.span == nil { 85 return 86 } 87 tracing.SpanError(htf.span, err) 88 htf.span.SetTag(tracing.TagKeyHTTPCode, fmt.Sprint(statusCode)) 89 htf.span.Finish() 90 }