From 2eb0dfe3a8629642b5eba7e9ee608bf9ab9355f9 Mon Sep 17 00:00:00 2001 From: Pankaj Chaurasia Date: Thu, 8 Oct 2026 16:42:34 +0530 Subject: [PATCH] fix(proxy): include cfRay and lbProbe in HTTP request log context newHTTPLogger discarded the zerolog.Context returned by ctx.Str and ctx.Bool. zerolog.Context methods have value receivers and return the updated context, so the cfRay and lbProbe fields were never added to the per-request logger. Since TUN-8238 (2024.2.1), origin request error lines and the debug request/response lines carry no Ray ID, so they can't be correlated with Cloudflare edge logs or origin access logs. Assign the returned context, and add a test covering the Cf-Ray, load balancer probe and plain request cases. Fixes #1756 --- proxy/logger.go | 4 +- proxy/logger_test.go | 87 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 proxy/logger_test.go diff --git a/proxy/logger.go b/proxy/logger.go index fa202964865..7e7899fb5ff 100644 --- a/proxy/logger.go +++ b/proxy/logger.go @@ -33,10 +33,10 @@ func newHTTPLogger(logger *zerolog.Logger, connIndex uint8, req *http.Request, r cfRay := connection.FindCfRayHeader(req) lbProbe := connection.IsLBProbeRequest(req) if cfRay != "" { - ctx.Str(logFieldCFRay, cfRay) + ctx = ctx.Str(logFieldCFRay, cfRay) } if lbProbe { - ctx.Bool(logFieldLBProbe, lbProbe) + ctx = ctx.Bool(logFieldLBProbe, lbProbe) } return ctx. Str(logFieldOriginService, serviceName). diff --git a/proxy/logger_test.go b/proxy/logger_test.go new file mode 100644 index 00000000000..c021ced368e --- /dev/null +++ b/proxy/logger_test.go @@ -0,0 +1,87 @@ +package proxy + +import ( + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "strconv" + "testing" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/cloudflare/cloudflared/management" +) + +// User-Agent prefix that connection.IsLBProbeRequest treats as a Cloudflare load balancer health probe. +const testLBProbeUserAgent = "Mozilla/5.0 (compatible; Cloudflare-Traffic-Manager/1.0; +https://www.cloudflare.com/traffic-manager/; pool-id: 0123456789abcdef)" + +func TestNewHTTPLoggerContextFields(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + cfRay string + userAgent string + wantCFRay string + wantLBProbe bool + }{ + { + name: "request with Cf-Ray header", + cfRay: "8a1b2c3d4e5f6a7b-SJC", + wantCFRay: "8a1b2c3d4e5f6a7b-SJC", + }, + { + name: "load balancer probe", + userAgent: testLBProbeUserAgent, + wantLBProbe: true, + }, + { + name: "request without Cf-Ray header", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + var output bytes.Buffer + baseLogger := zerolog.New(&output) + + req := httptest.NewRequest(http.MethodGet, "https://example.com/path", nil) + if tt.cfRay != "" { + req.Header.Set("Cf-Ray", tt.cfRay) + } + if tt.userAgent != "" { + req.Header.Set("User-Agent", tt.userAgent) + } + + logger := newHTTPLogger(&baseLogger, 2, req, 3, "http://localhost:8080") + logger.Error().Msg("request failed") + + decoder := json.NewDecoder(&output) + decoder.UseNumber() + var fields map[string]any + require.NoError(t, decoder.Decode(&fields)) + + assert.Equal(t, json.Number(strconv.Itoa(int(management.HTTP))), fields[management.EventTypeKey]) + assert.Equal(t, json.Number("2"), fields[logFieldConnIndex]) + assert.Equal(t, json.Number("3"), fields[logFieldRule]) + assert.Equal(t, "http://localhost:8080", fields[logFieldOriginService]) + + if tt.wantCFRay != "" { + assert.Equal(t, tt.wantCFRay, fields[logFieldCFRay]) + } else { + assert.NotContains(t, fields, logFieldCFRay) + } + + if tt.wantLBProbe { + assert.Equal(t, tt.wantLBProbe, fields[logFieldLBProbe]) + } else { + assert.NotContains(t, fields, logFieldLBProbe) + } + }) + } +}