- Logging: structured slog output with method, URL, status, duration - DefaultHeaders/UserAgent: inject headers without overwriting existing - BearerAuth/BasicAuth: per-request token resolution and static credentials - Recovery: catches panics in the RoundTripper chain
39 lines
1.1 KiB
Go
39 lines
1.1 KiB
Go
package middleware
|
|
|
|
import (
|
|
"log/slog"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
// Logging returns a middleware that logs each request's method, URL, status
|
|
// code, duration, and error (if any) using the provided structured logger.
|
|
// Successful responses are logged at Info level; errors at Error level.
|
|
func Logging(logger *slog.Logger) Middleware {
|
|
return func(next http.RoundTripper) http.RoundTripper {
|
|
return RoundTripperFunc(func(req *http.Request) (*http.Response, error) {
|
|
start := time.Now()
|
|
|
|
resp, err := next.RoundTrip(req)
|
|
|
|
duration := time.Since(start)
|
|
attrs := []slog.Attr{
|
|
slog.String("method", req.Method),
|
|
slog.String("url", req.URL.String()),
|
|
slog.Duration("duration", duration),
|
|
}
|
|
|
|
if err != nil {
|
|
attrs = append(attrs, slog.String("error", err.Error()))
|
|
logger.LogAttrs(req.Context(), slog.LevelError, "request failed", attrs...)
|
|
return resp, err
|
|
}
|
|
|
|
attrs = append(attrs, slog.Int("status", resp.StatusCode))
|
|
logger.LogAttrs(req.Context(), slog.LevelInfo, "request completed", attrs...)
|
|
|
|
return resp, nil
|
|
})
|
|
}
|
|
}
|