package tracing import ( "context" "net/http" "net/http/httptest" "strings" "testing" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" logf "sigs.k8s.io/controller-runtime/pkg/log" ) const sampleTraceparent = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01" func TestHTTPMiddleware(t *testing.T) { t.Parallel() newServer := func(lines *[]string, tp *sdktrace.TracerProvider) http.Handler { mux := http.NewServeMux() handler := func(w http.ResponseWriter, r *http.Request) { logf.FromContext(r.Context()).Info("handling") w.WriteHeader(http.StatusOK) } mux.HandleFunc("GET /healthz", handler) mux.HandleFunc("GET /v1/things/{id}", handler) return HTTPMiddleware("discovery", captureLogger(lines), WithTracerProvider(tp))(mux) } t.Run("route span, enriched handler logs, traceparent continuation", func(t *testing.T) { t.Parallel() sr := tracetest.NewSpanRecorder() tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) t.Cleanup(func() { _ = tp.Shutdown(context.Background()) }) var lines []string rec := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/v1/things/42", nil) req.Header.Set("traceparent", sampleTraceparent) newServer(&lines, tp).ServeHTTP(rec, req) ended := sr.Ended() if len(ended) != 1 { t.Fatalf("got %d spans, want 1", len(ended)) } if got := ended[0].Name(); got != "GET /v1/things/{id}" { t.Errorf("span name = %q, want route pattern", got) } wantTrace := "4bf92f3577b34da6a3ce929d0e0e4736" if got := ended[0].SpanContext().TraceID().String(); got != wantTrace { t.Errorf("span traceID = %s, want continuation of client trace %s", got, wantTrace) } if len(lines) != 1 { t.Fatalf("got %d log lines, want 1: %v", len(lines), lines) } if n := strings.Count(lines[0], `"traceID"`); n != 1 { t.Errorf("traceID appears %d times, want 1: %s", n, lines[0]) } if !strings.Contains(lines[0], wantTrace) { t.Errorf("handler log missing traceID %s: %s", wantTrace, lines[0]) } }) t.Run("healthz is not traced but still gets a logger", func(t *testing.T) { t.Parallel() sr := tracetest.NewSpanRecorder() tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) t.Cleanup(func() { _ = tp.Shutdown(context.Background()) }) var lines []string rec := httptest.NewRecorder() newServer(&lines, tp).ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/healthz", nil)) if len(sr.Ended()) != 0 { t.Fatalf("healthz produced %d spans, want 0", len(sr.Ended())) } if len(lines) != 1 || strings.Contains(lines[0], "traceID") { t.Fatalf("want one unenriched log line, got %v", lines) } }) }