2026-05-25 15:36:49 +03:00
|
|
|
package middleware
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"log/slog"
|
|
|
|
|
"net/http"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type statusRecorder struct {
|
|
|
|
|
http.ResponseWriter
|
|
|
|
|
status int
|
|
|
|
|
bytes int
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *statusRecorder) WriteHeader(status int) {
|
|
|
|
|
r.status = status
|
|
|
|
|
r.ResponseWriter.WriteHeader(status)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *statusRecorder) Write(data []byte) (int, error) {
|
|
|
|
|
if r.status == 0 {
|
|
|
|
|
r.status = http.StatusOK
|
|
|
|
|
}
|
|
|
|
|
n, err := r.ResponseWriter.Write(data)
|
|
|
|
|
r.bytes += n
|
|
|
|
|
return n, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Logger(logger *slog.Logger) Middleware {
|
|
|
|
|
return func(next http.Handler) http.Handler {
|
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
start := time.Now()
|
|
|
|
|
recorder := &statusRecorder{ResponseWriter: w}
|
|
|
|
|
|
|
|
|
|
next.ServeHTTP(recorder, r)
|
|
|
|
|
|
|
|
|
|
status := recorder.status
|
|
|
|
|
if status == 0 {
|
|
|
|
|
status = http.StatusOK
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger.Info("http request",
|
2026-05-25 16:26:47 +03:00
|
|
|
"source", "http",
|
2026-05-25 16:52:57 +03:00
|
|
|
"severity", "dev",
|
2026-05-25 16:26:47 +03:00
|
|
|
"code", status,
|
2026-05-25 15:36:49 +03:00
|
|
|
"method", r.Method,
|
|
|
|
|
"path", r.URL.Path,
|
|
|
|
|
"status", status,
|
|
|
|
|
"bytes", recorder.bytes,
|
|
|
|
|
"duration_ms", time.Since(start).Milliseconds(),
|
|
|
|
|
"request_id", RequestIDFromContext(r.Context()),
|
|
|
|
|
"remote_addr", r.RemoteAddr,
|
|
|
|
|
"user_agent", r.UserAgent(),
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|