Report handler panics through the logger and answer 500 (closes #187)
All checks were successful
check / check (push) Successful in 2m54s
All checks were successful
check / check (push) Successful in 2m54s
chi v1.5.5's middleware.Recoverer neither logged a handler panic nor answered 500. Its pretty-printer scans the stack for a frame beginning "panic(0x", which the runtime no longer emits, so the scan never terminates early and every line reaches decorateFuncCallLine, which slices pkg[strings.Index(pkg, "."):] without checking for -1. That second panic escaped chi's own deferred function, so its WriteHeader(500) never ran: net/http closed the connection and reported its own crash, losing the original panic value entirely. Middleware.Recoverer replaces it. It writes one ERROR record through internal/logger carrying the panic value, the stack and the request id, and answers 500. http.ErrAbortHandler is re-panicked rather than swallowed, and a response the handler already committed is left alone rather than overwritten. It is registered inside every middleware that observes the response, so the 500 is the status the access log records and the metrics count, and outside the sentryhttp handler, whose Repanic option needs something further out to catch what it re-raises. Every growable field on the record is bounded in encoded bytes, through the same internal/logfield budget the access log spends: 512 for the panic value, since a handler may build one out of the request, 128 for the request id, which a client supplies outright through X-Request-Id, and 8192 for the stack, cut at its far end so the panic site survives. MaxPanicLogLineBytes states the resulting ceiling at 10240 over an arithmetic sum of 9121. Driving all three past their budgets at once measured 9009 bytes on the JSON handler and 8982 to 8983 on the text one in one checkout, and the real case through the shipped chain measures roughly 3960. Those figures are illustrations rather than invariants: the stack's own content decides where its cut lands, and debug.Stack() embeds absolute source paths, so both move. No test asserts a figure; the tests assert the ceiling and that each growable field was cut. Because the panic record no longer reaches net/http's error log, the carve-outs in README.md and in the MaxAccessLogLineBytes doc comment that described that path are removed rather than reworded. What replaces them states the ceiling the record is now written under, and internal/server/recoverer_test.go asserts that "http: panic serving" appears in neither of the process's streams.
This commit is contained in:
@@ -133,16 +133,12 @@ const (
|
||||
// - The "log" delivery target, which exists to write the whole
|
||||
// inbound event to the log. Deliberate; see
|
||||
// internal/delivery/target_log.go.
|
||||
// - The widest line the service can write, which is neither an
|
||||
// access log line nor client-chosen. A handler panic arrives
|
||||
// through net/http's nil ErrorLog as one record carrying a
|
||||
// whole goroutine stack, above this figure — measured at
|
||||
// roughly 2,770 bytes. The exact width is not an invariant: it
|
||||
// moves with the goroutine number and with the source paths
|
||||
// baked into the stack. That it exceeds this ceiling does not
|
||||
// move. The value is the runtime's, not a client's; see
|
||||
// README.md and
|
||||
// https://git.eeqj.de/sneak/webhooker/issues/187.
|
||||
// - The record a recovered panic writes, which is not an access
|
||||
// log line: its client-supplied fields are charged the same
|
||||
// budgets, but it carries a whole goroutine stack as well and
|
||||
// is wider than this figure. It has its own stated ceiling,
|
||||
// MaxPanicLogLineBytes in recoverer.go, and is written once
|
||||
// per recovered panic rather than once per request.
|
||||
MaxAccessLogLineBytes = 2560
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user