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  }