84 lines
2.3 KiB
Go
84 lines
2.3 KiB
Go
package chislog
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/go-chi/chi/v5/middleware"
|
|
)
|
|
|
|
type Slogger interface {
|
|
Log(ctx context.Context, level slog.Level, msg string, args ...any)
|
|
}
|
|
|
|
// Logger returns a go-chi middleware that logs requests using the given Slogger.
|
|
func Logger(logger Slogger, opts ...Option) func(next http.Handler) http.Handler {
|
|
cfg := newConfig(opts...)
|
|
return func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
for _, f := range cfg.filters {
|
|
if !f(r) {
|
|
next.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
}
|
|
|
|
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
|
|
start := time.Now()
|
|
|
|
next.ServeHTTP(ww, r)
|
|
|
|
end := time.Now()
|
|
status := ww.Status()
|
|
|
|
logger.Log(
|
|
r.Context(),
|
|
statusLevel(status),
|
|
statusLabel(status),
|
|
slog.String("routePattern", chi.RouteContext(r.Context()).RoutePattern()),
|
|
slog.String("httpRequest.method", r.Method),
|
|
slog.String("httpRequest.uri", r.RequestURI),
|
|
slog.String("httpRequest.referer", r.Referer()),
|
|
slog.String("httpRequest.userAgent", r.UserAgent()),
|
|
slog.String("httpRequest.proto", r.Proto),
|
|
slog.String("httpRequest.ip", cfg.ipExtractor(r)),
|
|
slog.Int("httpResponse.status", status),
|
|
slog.Int("httpResponse.bytes", ww.BytesWritten()),
|
|
//nolint:gomnd
|
|
slog.Float64("httpResponse.duration", float64(end.Sub(start).Nanoseconds())/1000000.0), // in milliseconds
|
|
)
|
|
})
|
|
}
|
|
}
|
|
|
|
func statusLevel(status int) slog.Level {
|
|
switch {
|
|
case status < http.StatusContinue:
|
|
return slog.LevelWarn
|
|
case status >= http.StatusBadRequest && status < http.StatusInternalServerError:
|
|
return slog.LevelWarn
|
|
case status >= http.StatusInternalServerError:
|
|
return slog.LevelError
|
|
default: // for statuses 100 <= 400
|
|
return slog.LevelInfo
|
|
}
|
|
}
|
|
|
|
func statusLabel(status int) string {
|
|
switch {
|
|
case status >= http.StatusContinue && status < http.StatusMultipleChoices:
|
|
return "OK"
|
|
case status >= http.StatusMultipleChoices && status < http.StatusBadRequest:
|
|
return "Redirect"
|
|
case status >= http.StatusBadRequest && status < http.StatusInternalServerError:
|
|
return "Client Error"
|
|
case status >= http.StatusInternalServerError:
|
|
return "Server Error"
|
|
default:
|
|
return "Unknown"
|
|
}
|
|
}
|