From 5d3ce5baf85d969a0f02aa808a21e65e71ceb7bd Mon Sep 17 00:00:00 2001 From: agent-company Date: Mon, 20 Apr 2026 15:10:08 +0000 Subject: [PATCH] feat: add request-id and duration_ms to structured logging middleware Enhance the logging middleware with a randomly generated request-id (X-Request-ID header) for request tracing, duration in milliseconds for easier metric aggregation, and user-agent for client identification. Closes leeworks-agents/gitea-mobile#200 Co-Authored-By: Claude Opus 4.6 (1M context) --- internal/middleware/logging.go | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/internal/middleware/logging.go b/internal/middleware/logging.go index 7a1043d..00ccc2e 100644 --- a/internal/middleware/logging.go +++ b/internal/middleware/logging.go @@ -1,6 +1,8 @@ package middleware import ( + "crypto/rand" + "encoding/hex" "log/slog" "net/http" "time" @@ -17,21 +19,39 @@ func (rw *responseWriter) WriteHeader(code int) { rw.ResponseWriter.WriteHeader(code) } -// Logging returns middleware that logs each HTTP request with structured logging. +// generateRequestID creates a short random hex string for request tracing. +func generateRequestID() string { + b := make([]byte, 8) + if _, err := rand.Read(b); err != nil { + return "unknown" + } + return hex.EncodeToString(b) +} + +// Logging returns middleware that logs each HTTP request with structured fields: +// method, path, status, duration (ms), request-id, and remote address. func Logging() func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() + requestID := generateRequestID() rw := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK} + // Set request ID header for downstream correlation. + w.Header().Set("X-Request-ID", requestID) + next.ServeHTTP(rw, r) + duration := time.Since(start) slog.Info("http request", "method", r.Method, "path", r.URL.Path, "status", rw.statusCode, - "duration", time.Since(start).String(), + "duration_ms", duration.Milliseconds(), + "duration", duration.String(), + "request_id", requestID, "remote", r.RemoteAddr, + "user_agent", r.UserAgent(), ) }) }