budgit/internal/middleware/logging.go

70 lines
1.4 KiB
Go

package middleware
import (
"log/slog"
"net/http"
"strings"
"time"
)
// responseWriter wraps http.ResponseWriter to capture status code
type responseWriter struct {
http.ResponseWriter
statusCode int
written bool
}
func (rw *responseWriter) WriteHeader(code int) {
if !rw.written {
rw.statusCode = code
rw.written = true
rw.ResponseWriter.WriteHeader(code)
}
}
func (rw *responseWriter) Write(b []byte) (int, error) {
if !rw.written {
rw.WriteHeader(http.StatusOK)
}
return rw.ResponseWriter.Write(b)
}
// Paths to skip logging (static assets, etc.)
var skipLoggingPaths = []string{
"/assets/",
"/uploads/",
"/favicon.ico",
}
// RequestLogging logs HTTP requests with method, path, status, and duration
// Skips logging for paths defined in skipLoggingPaths
func RequestLogging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Skip logging for configured paths
for _, prefix := range skipLoggingPaths {
if strings.HasPrefix(r.URL.Path, prefix) {
next.ServeHTTP(w, r)
return
}
}
start := time.Now()
rw := &responseWriter{
ResponseWriter: w,
statusCode: http.StatusOK,
written: false,
}
next.ServeHTTP(rw, r)
duration := time.Since(start)
slog.Info("http request",
"method", r.Method,
"path", r.URL.Path,
"status", rw.statusCode,
"duration_ms", duration.Milliseconds(),
"remote_addr", getClientIP(r),
)
})
}