A handler's panic is logged and counted as 500 (closes #125)
check / check (push) Waiting to run
check / check (push) Waiting to run
Recoverer comes first, so it wrote the 500 only after the panic had passed the request log and the metrics, which both recorded 200. Write500OnPanic, placed after the request log and after the metrics, writes the 500 as the panic passes and panics again, so Sentry and Recoverer still get it. Recoverer now writes its 500 only when no status was written, so the server logs no superfluous WriteHeader call. The test also requires Recoverer's log line for the handler's panic, so it fails if Write500OnPanic stops passing the panic on. Model: opus-5-5
This commit was merged in pull request #133.
This commit is contained in:
@@ -297,12 +297,16 @@ func (s *Middleware) MaxBodyBytes(
|
||||
|
||||
// Recoverer returns middleware that recovers from a panic in a
|
||||
// downstream handler, logs the panic and stack trace through
|
||||
// slog, and responds 500 with no body. http.ErrAbortHandler is
|
||||
// re-panicked so the server can abort the response as intended.
|
||||
// slog, and responds 500 with no body, unless a status was already
|
||||
// written, as Write500OnPanic writes one for a handler's panic.
|
||||
// http.ErrAbortHandler is re-panicked so the server can abort the
|
||||
// response as intended.
|
||||
func (s *Middleware) Recoverer() func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
|
||||
|
||||
defer func() {
|
||||
rec := recover()
|
||||
if rec == nil {
|
||||
@@ -320,10 +324,43 @@ func (s *Middleware) Recoverer() func(http.Handler) http.Handler {
|
||||
"stack", string(debug.Stack()),
|
||||
)
|
||||
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
if ww.Status() == 0 {
|
||||
ww.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
}()
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
next.ServeHTTP(ww, r)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Write500OnPanic returns middleware that writes 500 when a downstream
|
||||
// handler panics before writing a status, and lets the panic go on to
|
||||
// Sentry and Recoverer. Recoverer, which comes first, would write its
|
||||
// 500 only after the request log and the metrics had recorded the
|
||||
// status they saw; placed after each of them, this makes them record
|
||||
// the 500.
|
||||
func (s *Middleware) Write500OnPanic() func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
|
||||
|
||||
defer func() {
|
||||
rec := recover()
|
||||
if rec == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if ww.Status() == 0 {
|
||||
ww.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
panic(rec)
|
||||
}()
|
||||
|
||||
next.ServeHTTP(ww, r)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user