55 lines
1.5 KiB
Go
55 lines
1.5 KiB
Go
package middleware
|
|
|
|
import (
|
|
"net/http"
|
|
"runtime/debug"
|
|
|
|
"github.com/jordan/composed4/pkg/httpcontext"
|
|
"github.com/jordan/composed4/pkg/logging"
|
|
)
|
|
|
|
// Recoverer is middleware that recovers from panics, logs the error with stack trace
|
|
// using slog, and returns a 500 Internal Server Error response.
|
|
//
|
|
// The middleware captures:
|
|
// - request_id: For request correlation
|
|
// - method, path, remote_addr: Request context
|
|
// - panic: The recovered panic value
|
|
// - stack_trace: Full stack trace for debugging
|
|
//
|
|
// Usage:
|
|
//
|
|
// r.Use(middleware.RequestID())
|
|
// r.Use(middleware.RequestLogger(logger))
|
|
// r.Use(middleware.Recoverer(logger))
|
|
func Recoverer(logger *logging.Logger) func(next http.Handler) http.Handler {
|
|
return func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
defer func() {
|
|
if rvr := recover(); rvr != nil {
|
|
// Capture stack trace for debugging
|
|
stack := debug.Stack()
|
|
|
|
// Get request ID from context
|
|
requestID, _ := httpcontext.GetRequestID(r.Context())
|
|
|
|
// Log panic with full context and stack trace
|
|
logger.Error("panic recovered",
|
|
"request_id", requestID,
|
|
"method", r.Method,
|
|
"path", r.URL.Path,
|
|
"remote_addr", r.RemoteAddr,
|
|
"panic", rvr,
|
|
"stack_trace", string(stack),
|
|
)
|
|
|
|
// Return 500 Internal Server Error to client
|
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
|
}
|
|
}()
|
|
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
}
|