Middleware adds tracing to http routes.
(tracerProvider trace.TracerProvider)
| 18 | |
| 19 | // Middleware adds tracing to http routes. |
| 20 | func Middleware(tracerProvider trace.TracerProvider) func(http.Handler) http.Handler { |
| 21 | // We only want to create spans on the following route patterns, however |
| 22 | // we want the middleware to be very high in the middleware stack so it can |
| 23 | // capture the entire request. |
| 24 | re := patternmatcher.RoutePatterns{ |
| 25 | "/api", |
| 26 | "/api/**", |
| 27 | "/@*/*/apps/**", |
| 28 | "/%40*/*/apps/**", |
| 29 | "/external-auth/*/callback", |
| 30 | }.MustCompile() |
| 31 | |
| 32 | var tracer trace.Tracer |
| 33 | if tracerProvider != nil { |
| 34 | tracer = tracerProvider.Tracer(TracerName) |
| 35 | } |
| 36 | |
| 37 | return func(next http.Handler) http.Handler { |
| 38 | return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { |
| 39 | if tracer == nil || !re.MatchString(r.URL.Path) { |
| 40 | next.ServeHTTP(rw, r) |
| 41 | return |
| 42 | } |
| 43 | |
| 44 | // Extract the trace context from the request headers. |
| 45 | tmp := otel.GetTextMapPropagator() |
| 46 | hc := propagation.HeaderCarrier(r.Header) |
| 47 | ctx := tmp.Extract(r.Context(), hc) |
| 48 | |
| 49 | // start span with default span name. Span name will be updated to "method route" format once request finishes. |
| 50 | ctx, span := tracer.Start(ctx, fmt.Sprintf("%s %s", r.Method, r.RequestURI)) |
| 51 | defer span.End() |
| 52 | r = r.WithContext(ctx) |
| 53 | |
| 54 | if span.SpanContext().HasTraceID() && span.SpanContext().HasSpanID() { |
| 55 | // Technically these values are included in the Traceparent |
| 56 | // header, but they are easier to read for humans this way. |
| 57 | rw.Header().Set("X-Trace-ID", span.SpanContext().TraceID().String()) |
| 58 | rw.Header().Set("X-Span-ID", span.SpanContext().SpanID().String()) |
| 59 | |
| 60 | // Inject the trace context into the response headers. |
| 61 | hc := propagation.HeaderCarrier(rw.Header()) |
| 62 | tmp.Inject(ctx, hc) |
| 63 | } |
| 64 | |
| 65 | sw, ok := rw.(*StatusWriter) |
| 66 | if !ok { |
| 67 | panic(fmt.Sprintf("ResponseWriter not a *tracing.StatusWriter; got %T", rw)) |
| 68 | } |
| 69 | |
| 70 | // pass the span through the request context and serve the request to the next middleware |
| 71 | next.ServeHTTP(sw, r) |
| 72 | // capture response data |
| 73 | EndHTTPSpan(r, sw.Status, span) |
| 74 | }) |
| 75 | } |
| 76 | } |
| 77 |