feat: setup logging, tracing and metrics in standalone AI Gateway (#27068)

Adds logging, tracing and metrics setup to standalone AI Gateway.
Existing options are re-used when possible.
This commit is contained in:
Paweł Banaszewski
2026-07-09 14:02:18 +00:00
committed by GitHub
parent ef0b5585d5
commit bab8ce9d41
7 changed files with 441 additions and 121 deletions
+23 -19
View File
@@ -41,26 +41,10 @@ func Middleware(tracerProvider trace.TracerProvider) func(http.Handler) http.Han
return
}
// Extract the trace context from the request headers.
tmp := otel.GetTextMapPropagator()
hc := propagation.HeaderCarrier(r.Header)
ctx := tmp.Extract(r.Context(), hc)
// start span with default span name. Span name will be updated to "method route" format once request finishes.
ctx, span := tracer.Start(ctx, fmt.Sprintf("%s %s", r.Method, r.RequestURI))
// Start span with default span name. Span name will be updated to
// "method route" format once request finishes.
r, span := StartHTTPSpan(tracer, rw, r, fmt.Sprintf("%s %s", r.Method, r.RequestURI))
defer span.End()
r = r.WithContext(ctx)
if span.SpanContext().HasTraceID() && span.SpanContext().HasSpanID() {
// Technically these values are included in the Traceparent
// header, but they are easier to read for humans this way.
rw.Header().Set("X-Trace-ID", span.SpanContext().TraceID().String())
rw.Header().Set("X-Span-ID", span.SpanContext().SpanID().String())
// Inject the trace context into the response headers.
hc := propagation.HeaderCarrier(rw.Header())
tmp.Inject(ctx, hc)
}
sw, ok := rw.(*StatusWriter)
if !ok {
@@ -75,6 +59,26 @@ func Middleware(tracerProvider trace.TracerProvider) func(http.Handler) http.Han
}
}
// StartHTTPSpan starts a span, propagating inbound trace context and writing
// X-Trace-ID/X-Span-ID response headers. The caller must end the span.
func StartHTTPSpan(tracer trace.Tracer, rw http.ResponseWriter, r *http.Request, name string) (*http.Request, trace.Span) {
propagator := otel.GetTextMapPropagator()
ctx := propagator.Extract(r.Context(), propagation.HeaderCarrier(r.Header))
ctx, span := tracer.Start(ctx, name)
r = r.WithContext(ctx)
if span.SpanContext().HasTraceID() && span.SpanContext().HasSpanID() {
// Technically these values are included in the Traceparent header, but
// they are easier to read for humans this way.
rw.Header().Set("X-Trace-ID", span.SpanContext().TraceID().String())
rw.Header().Set("X-Span-ID", span.SpanContext().SpanID().String())
propagator.Inject(ctx, propagation.HeaderCarrier(rw.Header()))
}
return r, span
}
// EndHTTPSpan captures request and response data after the handler is done.
func EndHTTPSpan(r *http.Request, status int, span trace.Span) {
// set the resource name as we get it only once the handler is executed