2023-06-29 00:23:08 +00:00
|
|
|
package middleware
|
|
|
|
|
|
|
|
import (
|
2023-06-30 16:51:06 +00:00
|
|
|
"context"
|
2023-06-29 00:23:08 +00:00
|
|
|
"net/http"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
chi_middleware "github.com/go-chi/chi/v5/middleware"
|
|
|
|
"golang.org/x/exp/slog"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Slogger is a slog-enabled logging middleware.
|
|
|
|
// It logs the start and end of the request, and logs info
|
|
|
|
// about the request itself, response status, and response time.
|
|
|
|
|
|
|
|
// Slogger returns a log handler that uses the given slog logger as the base.
|
|
|
|
func Slogger(sl *slog.Logger) func(next http.Handler) http.Handler {
|
|
|
|
|
|
|
|
logger := sl.WithGroup("http")
|
|
|
|
return func(next http.Handler) http.Handler {
|
|
|
|
|
|
|
|
// this triple-nested function is strange, but basically the Slogger() call makes a new middleware function (above)
|
|
|
|
// the middleware function returns a handler that calls the next handler in the chain(wrapping it)
|
|
|
|
|
|
|
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
// wrap writer allows us to get info on the response from further handlers.
|
|
|
|
ww := chi_middleware.NewWrapResponseWriter(w, r.ProtoMajor)
|
|
|
|
t1 := time.Now()
|
|
|
|
// attrs is stored to allow for the helpers to add additional elements to the main record.
|
|
|
|
attrs := make([]slog.Attr, 0)
|
|
|
|
|
|
|
|
// This function runs at the end and adds all the response details to the attrs before logging them.
|
|
|
|
defer func() {
|
|
|
|
attrs = append(attrs, slog.Int("status_code", ww.Status()))
|
|
|
|
attrs = append(attrs, slog.Int("resp_size", ww.BytesWritten()))
|
|
|
|
attrs = append(attrs, slog.Duration("duration", time.Since(t1)))
|
|
|
|
attrs = append(attrs, slog.String("method", r.Method))
|
|
|
|
logger.LogAttrs(r.Context(), slog.LevelInfo, r.RequestURI, attrs...)
|
|
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
// embed the logger and the attrs for later items in the chain.
|
2023-09-19 19:17:22 +00:00
|
|
|
ctx := context.WithValue(r.Context(), SloggerAttrsKey, attrs)
|
|
|
|
ctx = context.WithValue(ctx, SloggerLogKey, logger)
|
|
|
|
// push it to the request and serve the next handler
|
|
|
|
r = r.WithContext(ctx)
|
2023-06-29 00:23:08 +00:00
|
|
|
next.ServeHTTP(ww, r)
|
|
|
|
}
|
|
|
|
|
|
|
|
return http.HandlerFunc(fn)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type slogKeyType int
|
|
|
|
|
|
|
|
const (
|
|
|
|
SloggerLogKey slogKeyType = iota
|
|
|
|
SloggerAttrsKey
|
|
|
|
)
|
|
|
|
|
2023-06-30 16:51:06 +00:00
|
|
|
func AddSlogAttr(r *http.Request, attr slog.Attr) {
|
2023-06-29 00:23:08 +00:00
|
|
|
ctx := r.Context()
|
|
|
|
attrs, ok := ctx.Value(SloggerAttrsKey).([]slog.Attr)
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
attrs = append(attrs, attr)
|
2023-06-30 16:51:06 +00:00
|
|
|
|
2023-06-29 00:23:08 +00:00
|
|
|
}
|
2023-09-19 19:17:22 +00:00
|
|
|
|
|
|
|
// TODO: write rest of functions
|