Skip to content

Log a response row with status 500 when the app raises - #27

Merged
PetrHeinz merged 2 commits into
mainfrom
claude/response-on-exception
Oct 2, 2026
Merged

PetrHeinz merged 2 commits into
mainfrom
claude/response-on-exception

Conversation

@PetrHeinz

Copy link
Copy Markdown
Member

HTTPEvents calls the app without a rescue, so when the app raises, the request gets a Started … row but never an http_response_sent row: no status, no duration. 5xx responses are never recorded, and dashboards or alerts on status 500 never fire. Yesterday's red-team saw this in Rack, Sinatra and on every Rails version: a controller raising RuntimeError produced the request row and the error row, and no response row at all.

What changes:

  • HTTPEvents rescues Exception around the app call, logs the response event, and re-raises the original exception unchanged (same object, same backtrace).
  • The response row carries the status from the new HTTPEvents.status_for_exception setting and the duration up to the exception. It has no headers, body or content length. It is logged in normal mode (after the request row) and as the single event in collapsed mode, where the message reads GET /path completed with 500 Internal Server Error in 12.3ms.
  • status_for_exception is a callable that receives the exception and returns an Integer status. It defaults to DEFAULT_STATUS_FOR_EXCEPTION, which returns 500. If the callable raises itself, the row gets 500 and the error goes to Logtail::Config.instance.debug. Setting a value that doesn't respond to #call raises ArgumentError at configuration time, like silence_request.
  • Logging the response row can never replace the app's exception: a failure while building it goes to the debug logger only.
Logtail::Integrations::Rack::HTTPEvents.status_for_exception = lambda do |exception|
  exception.is_a?(MyApp::NotFound) ? 404 : 500
end

Behaviour and compatibility:

  • Requests whose app raises now produce one extra row. Nothing changes for requests that return a response, or for silenced requests.
  • The exception still propagates, so whatever sits outside the middleware (the server, an error page middleware) still decides the actual response. 500 matches what Rack servers send for an uncaught exception. Apps with an outer middleware that maps exceptions to other statuses can mirror that mapping in status_for_exception; the logtail-rails integration will set it from config.action_dispatch.rescue_responses (separate PR), so ActiveRecord::RecordNotFound is logged as 404 there.
  • No new dependencies, and the code runs on Ruby 2.5 and Rack 1.2 to 3.x.

Targets the 0.2.9 patch release. The logtail-rails PR for exception statuses runs its CI against this branch until 0.2.9 is released.

The first commit only adds the tests and is expected to fail on CI; the second commit makes them pass.

🤖 Generated with Claude Code

PetrHeinz and others added 2 commits October 1, 2026 18:39
HTTPEvents calls the app without a rescue, so a request whose app raises
gets a request row but never an http_response_sent row: no status and no
duration, and 5xx responses are never recorded.

The tests pin the new behaviour: exactly one response row with status 500
and the duration up to the exception, in normal and collapsed mode, with
the original exception re-raised unchanged; a status_for_exception
resolver for the status; and 500 when the resolver raises itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HTTPEvents now rescues Exception around the app call, logs the
http_response_sent event in normal and collapsed mode, and re-raises the
original exception unchanged. The row has the duration up to the exception,
no headers or body, and the status from the new status_for_exception
setting: a callable that receives the exception, 500 by default
(DEFAULT_STATUS_FOR_EXCEPTION), and 500 when the callable raises itself.

Building that row can't replace the app's exception: a failure goes to the
debug logger only. Adds a test for that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@PetrHeinz
PetrHeinz marked this pull request as ready for review October 2, 2026 09:18
@PetrHeinz
PetrHeinz merged commit d59c9ab into main Oct 2, 2026
24 checks passed
@PetrHeinz
PetrHeinz deleted the claude/response-on-exception branch October 2, 2026 11:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant