feat: logger_middleware — middleware HTTP con logs estructurados (infra)

Middleware que envuelve cualquier http.Handler y emite un log info por
cada request con method, path, status y duration_ms. Hereda los campos
contextuales del Logger (app, version, request_id...) y se compone con
HTTPMiddlewareChain + HTTPCORSMiddleware.

Diferencia con http_logger_middleware: este escribe JSON estructurado via
slog en vez de texto plano a un io.Writer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-18 17:14:36 +02:00
parent de297f1d41
commit e030b105b1
2 changed files with 81 additions and 0 deletions
+38
View File
@@ -0,0 +1,38 @@
package infra
import (
"net/http"
"time"
)
// LoggerMiddleware retorna un Middleware que emite un log estructurado por cada request HTTP.
// Cada request produce una entrada a nivel info con method, path, status y duration_ms.
// Respeta los campos contextuales que ya tenga el logger (app, version, request_id...).
func LoggerMiddleware(logger *Logger) Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
rw := &loggerResponseWriter{ResponseWriter: w, status: http.StatusOK}
next.ServeHTTP(rw, r)
duration := time.Since(start)
LogInfo(logger, "http request",
"method", r.Method,
"path", r.URL.Path,
"status", rw.status,
"duration_ms", duration.Milliseconds(),
)
})
}
}
// loggerResponseWriter captura el status code escrito al ResponseWriter.
// Nombrado distinto de responseWriter (http_logger_middleware.go) para evitar colision.
type loggerResponseWriter struct {
http.ResponseWriter
status int
}
func (rw *loggerResponseWriter) WriteHeader(status int) {
rw.status = status
rw.ResponseWriter.WriteHeader(status)
}