Files
coder/coderd/tracing/httpmw.go
2022-05-19 17:43:07 -05:00

69 lines
2.1 KiB
Go

package tracing
import (
"fmt"
"net/http"
"github.com/go-chi/chi/middleware"
"github.com/go-chi/chi/v5"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
// HTTPMW adds tracing to http routes.
func HTTPMW(tracerProvider *sdktrace.TracerProvider, name string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
// do not trace if exporter has not be initialized
if tracerProvider == nil {
next.ServeHTTP(rw, r)
return
}
// start span with default span name. Span name will be updated to "method route" format once request finishes.
_, span := tracerProvider.Tracer(name).Start(r.Context(), fmt.Sprintf("%s %s", r.Method, r.RequestURI))
defer span.End()
wrw := middleware.NewWrapResponseWriter(rw, r.ProtoMajor)
// pass the span through the request context and serve the request to the next middleware
next.ServeHTTP(rw, r)
// set the resource name as we get it only once the handler is executed
route := chi.RouteContext(r.Context()).RoutePattern()
if route != "" {
span.SetName(fmt.Sprintf("%s %s", r.Method, route))
}
span.SetAttributes(attribute.KeyValue{
Key: "http.method",
Value: attribute.StringValue(r.Method),
})
span.SetAttributes(attribute.KeyValue{
Key: "http.route",
Value: attribute.StringValue(route),
})
span.SetAttributes(attribute.KeyValue{
Key: "http.path",
Value: attribute.StringValue(r.URL.EscapedPath()),
})
// set the status code
status := wrw.Status()
// 0 status means one has not yet been sent in which case net/http library will write StatusOK
if status == 0 {
status = http.StatusOK
}
span.SetAttributes(attribute.KeyValue{
Key: "http.status_code",
Value: attribute.IntValue(status),
})
// if 5XX we set the span to "error" status
if status >= 500 {
span.SetStatus(codes.Error, fmt.Sprintf("%d: %s", status, http.StatusText(status)))
}
})
}
}