package api import ( "log/slog" "net/http" "time" "github.com/gin-gonic/gin" ) // requestLogger returns a Gin middleware that logs each request via slog. // Requests to /healthz are skipped to avoid health-check noise. func requestLogger(lg *slog.Logger) gin.HandlerFunc { if lg == nil { lg = slog.Default() } return func(c *gin.Context) { if c.FullPath() == "/healthz" || c.Request.URL.Path == "/healthz" { c.Next() return } start := time.Now() c.Next() status := c.Writer.Status() path := c.FullPath() if path == "" { path = c.Request.URL.Path } attrs := []any{ "method", c.Request.Method, "path", path, "status", status, "latency_ms", time.Since(start).Milliseconds(), "client_ip", c.ClientIP(), } if len(c.Errors) > 0 { attrs = append(attrs, "error", c.Errors.String()) } switch { case status >= http.StatusInternalServerError: lg.Error("http request completed", attrs...) case status >= http.StatusBadRequest: lg.Warn("http request completed", attrs...) default: lg.Info("http request completed", attrs...) } } }