From e51cec191324bc8e6d9fafaca09c6615d9cee1ce Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Tue, 14 Apr 2026 14:07:55 +1000 Subject: [PATCH] feat: make HTTP header to log attribute mapping configurable Replace the hard-coded X-Blox-Workstation-Id header extraction in LoggingMiddleware with a general-purpose header-attrs config map in the log block. Any header can now be mapped to a log attribute via config: log { header-attrs = { "X-Blox-Workstation-Id" = "blox.workstation_id" } } Amp-Thread-ID: https://ampcode.com/threads/T-019d8a28-e244-724f-834e-79e196aab7c2 Co-authored-by: Amp --- cmd/cachewd/main.go | 6 +++--- internal/httputil/logging.go | 11 ++++++++--- internal/logging/logging.go | 7 ++++--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/cmd/cachewd/main.go b/cmd/cachewd/main.go index de8d6025..97276bfb 100644 --- a/cmd/cachewd/main.go +++ b/cmd/cachewd/main.go @@ -108,7 +108,7 @@ func main() { logger.InfoContext(ctx, "Starting cachewd", "bind", globalConfig.Bind) - server, err := newServer(ctx, mux, globalConfig.Bind, globalConfig.MetricsConfig, globalConfig.OPAConfig) + server, err := newServer(ctx, mux, globalConfig.Bind, globalConfig.MetricsConfig, globalConfig.OPAConfig, globalConfig.LoggingConfig) fatalIfError(ctx, logger, err, "Failed to create server") err = server.ListenAndServe() @@ -220,7 +220,7 @@ func extractPathPrefix(path string) string { return prefix } -func newServer(ctx context.Context, muxHandler http.Handler, bind string, metricsConfig metrics.Config, opaConfig opa.Config) (*http.Server, error) { +func newServer(ctx context.Context, muxHandler http.Handler, bind string, metricsConfig metrics.Config, opaConfig opa.Config, loggingConfig logging.Config) (*http.Server, error) { var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { labeler, _ := otelhttp.LabelerFromContext(r.Context()) labeler.Add(attribute.String("cachew.http.path.prefix", extractPathPrefix(r.URL.Path))) @@ -238,7 +238,7 @@ func newServer(ctx context.Context, muxHandler http.Handler, bind string, metric otelhttp.WithTracerProvider(otel.GetTracerProvider()), )(handler) - handler = httputil.LoggingMiddleware(handler) + handler = httputil.LoggingMiddleware(loggingConfig.HeaderAttrs, handler) logger := logging.FromContext(ctx) return &http.Server{ diff --git a/internal/httputil/logging.go b/internal/httputil/logging.go index aad2d1e1..3b89ba1d 100644 --- a/internal/httputil/logging.go +++ b/internal/httputil/logging.go @@ -6,12 +6,17 @@ import ( "github.com/block/cachew/internal/logging" ) -func LoggingMiddleware(next http.Handler) http.Handler { +// LoggingMiddleware adds method/URI to the logger and optionally extracts +// request headers into log attributes based on the provided headerAttrs map. +func LoggingMiddleware(headerAttrs map[string]string, next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Propagate attributes tot the handlers. logger := logging.FromContext(r.Context()).With("method", r.Method, "uri", r.RequestURI) + for header, attr := range headerAttrs { + if v := r.Header.Get(header); v != "" { + logger = logger.With(attr, v) + } + } r = r.WithContext(logging.ContextWithLogger(r.Context(), logger)) - logger.Debug("Request received") next.ServeHTTP(w, r) }) } diff --git a/internal/logging/logging.go b/internal/logging/logging.go index 20a17968..83152ccd 100644 --- a/internal/logging/logging.go +++ b/internal/logging/logging.go @@ -10,9 +10,10 @@ import ( ) type Config struct { - JSON bool `hcl:"json,optional" help:"Enable JSON logging."` - Level slog.Level `hcl:"level" help:"Set the logging level." default:"info"` - Remap map[string]string `hcl:"remap,optional" help:"Remap field names from old to new (e.g., msg=message, time=timestamp)."` + JSON bool `hcl:"json,optional" help:"Enable JSON logging."` + Level slog.Level `hcl:"level" help:"Set the logging level." default:"info"` + Remap map[string]string `hcl:"remap,optional" help:"Remap field names from old to new (e.g., msg=message, time=timestamp)."` + HeaderAttrs map[string]string `hcl:"header-attrs,optional" help:"Map HTTP header names to log attribute names (e.g., X-Request-Id=request_id)."` } var levelVar = &slog.LevelVar{} //nolint:gochecknoglobals