internal/middleware_logger/middleware_logger.go (58 lines of code) (raw):
package middleware_logger //nolint:staticcheck
import (
"log/slog"
"net/http"
"os"
"time"
"gitlab.com/gitlab-org/gitlab-zoekt-indexer/internal/dev_debug"
)
func SlogMiddleware(logger *slog.Logger) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// Wrap the ResponseWriter to capture the status and any potential errors
ww := &enhancedWriter{ResponseWriter: w, status: http.StatusOK}
// Process the request
next.ServeHTTP(ww, r)
// Log the request details
duration := time.Since(start)
logFields := []any{
"method", r.Method,
"path", r.URL.Path,
"host", r.Host,
"status", ww.status,
"duration_ms", duration.Milliseconds(),
}
// Include error message if available
if ww.errMessage != "" {
logFields = append(logFields, "error", ww.errMessage)
logger.Error("HTTP request", logFields...)
return
}
logger.Info("HTTP request", logFields...)
})
}
}
func SetUpLogger() *slog.Logger {
level := slog.LevelInfo
if dev_debug.IsEnabled() {
level = slog.LevelDebug
}
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: level}))
slog.SetDefault(logger)
return logger
}
// enhancedWriter wraps http.ResponseWriter to capture the HTTP status code and any error messages.
type enhancedWriter struct {
http.ResponseWriter
status int
errMessage string
}
func (w *enhancedWriter) WriteHeader(code int) {
w.status = code
if code >= 400 { // Capture error status codes
w.errMessage = http.StatusText(code)
}
w.ResponseWriter.WriteHeader(code)
}
// Use Write to capture error messages written directly to the response body
func (w *enhancedWriter) Write(b []byte) (int, error) {
if w.status >= 400 && w.errMessage == "" {
w.errMessage = string(b) // Capture body content as error message
}
return w.ResponseWriter.Write(b)
}