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  }