From e8335a94a4868689e96f15f11b8bc6fae66ddbce Mon Sep 17 00:00:00 2001 From: weijiany Date: Tue, 3 Sep 2024 22:40:04 +0800 Subject: [PATCH] Record trace id and EntryPoint span id into access log --- docs/content/observability/access-logs.md | 2 ++ pkg/middlewares/accesslog/logdata.go | 5 ++++ pkg/middlewares/observability/entrypoint.go | 7 ++++++ .../observability/entrypoint_test.go | 25 +++++++++++++++++++ 4 files changed, 39 insertions(+) diff --git a/docs/content/observability/access-logs.md b/docs/content/observability/access-logs.md index f5481eb96..eb30e0200 100644 --- a/docs/content/observability/access-logs.md +++ b/docs/content/observability/access-logs.md @@ -250,6 +250,8 @@ accessLog: | `TLSVersion` | The TLS version used by the connection (e.g. `1.2`) (if connection is TLS). | | `TLSCipher` | The TLS cipher used by the connection (e.g. `TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA`) (if connection is TLS) | | `TLSClientSubject` | The string representation of the TLS client certificate's Subject (e.g. `CN=username,O=organization`) | + | `TraceId` | A consistent identifier for tracking requests across services, including upstream ones managed by Traefik, shown as a 32-hex digit string | + | `SpanId` | A unique identifier for Traefik’s root span (EntryPoint) within a request trace, formatted as a 16-hex digit string. | ## Log Rotation diff --git a/pkg/middlewares/accesslog/logdata.go b/pkg/middlewares/accesslog/logdata.go index ef3f3be6c..19dc43d6c 100644 --- a/pkg/middlewares/accesslog/logdata.go +++ b/pkg/middlewares/accesslog/logdata.go @@ -77,6 +77,11 @@ const ( TLSCipher = "TLSCipher" // TLSClientSubject is the string representation of the TLS client certificate's Subject. TLSClientSubject = "TLSClientSubject" + + // TraceID is the consistent identifier for tracking requests across services, including upstream ones managed by Traefik, shown as a 32-hex digit string. + TraceID = "TraceId" + // SpanID is the unique identifier for Traefik’s root span (EntryPoint) within a request trace, formatted as a 16-hex digit string. + SpanID = "SpanId" ) // These are written out in the default case when no config is provided to specify keys of interest. diff --git a/pkg/middlewares/observability/entrypoint.go b/pkg/middlewares/observability/entrypoint.go index 0de455b92..4849f25b2 100644 --- a/pkg/middlewares/observability/entrypoint.go +++ b/pkg/middlewares/observability/entrypoint.go @@ -11,6 +11,7 @@ import ( "github.com/containous/alice" "github.com/traefik/traefik/v3/pkg/metrics" "github.com/traefik/traefik/v3/pkg/middlewares" + "github.com/traefik/traefik/v3/pkg/middlewares/accesslog" "github.com/traefik/traefik/v3/pkg/tracing" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" @@ -69,6 +70,12 @@ func (e *entryPointTracing) ServeHTTP(rw http.ResponseWriter, req *http.Request) e.tracer.CaptureServerRequest(span, req) + if logData := accesslog.GetLogData(req); logData != nil { + spanContext := span.SpanContext() + logData.Core[accesslog.TraceID] = spanContext.TraceID().String() + logData.Core[accesslog.SpanID] = spanContext.SpanID().String() + } + recorder := newStatusCodeRecorder(rw, http.StatusOK) e.next.ServeHTTP(recorder, req) diff --git a/pkg/middlewares/observability/entrypoint_test.go b/pkg/middlewares/observability/entrypoint_test.go index f32c052c8..3e7a90870 100644 --- a/pkg/middlewares/observability/entrypoint_test.go +++ b/pkg/middlewares/observability/entrypoint_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/metrics" + "github.com/traefik/traefik/v3/pkg/middlewares/accesslog" "github.com/traefik/traefik/v3/pkg/tracing" "github.com/traefik/traefik/v3/pkg/types" "go.opentelemetry.io/otel/attribute" @@ -181,3 +182,27 @@ func TestEntryPointMiddleware_metrics(t *testing.T) { }) } } + +func TestEntryPointMiddleware_tracingInfoIntoLog(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "http://www.test.com/", http.NoBody) + req = req.WithContext( + context.WithValue( + req.Context(), + accesslog.DataTableKey, + &accesslog.LogData{Core: accesslog.CoreLogData{}}, + ), + ) + + next := http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {}) + + tracer := &mockTracer{} + + handler := newEntryPoint(context.Background(), tracing.NewTracer(tracer, []string{}, []string{}, []string{}), nil, "test", next) + handler.ServeHTTP(httptest.NewRecorder(), req) + + expectedSpanCtx := tracer.spans[0].SpanContext() + + logData := accesslog.GetLogData(req) + assert.Equal(t, expectedSpanCtx.TraceID().String(), logData.Core[accesslog.TraceID]) + assert.Equal(t, expectedSpanCtx.SpanID().String(), logData.Core[accesslog.SpanID]) +}