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