github.com/avenga/couper@v1.12.2/handler/middleware/trace.go (about)

     1  package middleware
     2  
     3  import (
     4  	"net/http"
     5  
     6  	"go.opentelemetry.io/otel"
     7  	"go.opentelemetry.io/otel/attribute"
     8  	semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
     9  	"go.opentelemetry.io/otel/trace"
    10  
    11  	"github.com/avenga/couper/config/request"
    12  	"github.com/avenga/couper/logging"
    13  	"github.com/avenga/couper/telemetry/instrumentation"
    14  )
    15  
    16  type TraceHandler struct {
    17  	handler http.Handler
    18  }
    19  
    20  func NewTraceHandler() Next {
    21  	return func(handler http.Handler) *NextHandler {
    22  		return NewHandler(&TraceHandler{
    23  			handler: handler,
    24  		}, handler)
    25  	}
    26  }
    27  
    28  func (th *TraceHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
    29  	spanName := req.URL.EscapedPath()
    30  	opts := []trace.SpanStartOption{
    31  		trace.WithAttributes(semconv.NetAttributesFromHTTPRequest("tcp", req)...),
    32  		trace.WithAttributes(semconv.EndUserAttributesFromHTTPRequest(req)...),
    33  		trace.WithAttributes(semconv.HTTPServerAttributesFromHTTPRequest("couper", spanName, req)...),
    34  		trace.WithSpanKind(trace.SpanKindServer),
    35  		trace.WithAttributes(attribute.String("couper.uid", req.Context().Value(request.UID).(string))),
    36  	}
    37  
    38  	tracer := otel.GetTracerProvider().Tracer(instrumentation.Name)
    39  	ctx, span := tracer.Start(req.Context(), spanName, opts...)
    40  	defer span.End()
    41  
    42  	*req = *req.WithContext(ctx)
    43  	th.handler.ServeHTTP(rw, req)
    44  
    45  	if rsw, ok := rw.(logging.RecorderInfo); ok {
    46  		attrs := semconv.HTTPAttributesFromHTTPStatusCode(rsw.StatusCode())
    47  		spanStatus, spanMessage := semconv.SpanStatusFromHTTPStatusCode(rsw.StatusCode())
    48  		span.SetAttributes(attrs...)
    49  		span.SetStatus(spanStatus, spanMessage)
    50  	}
    51  }