Log the real status of exception responses and honour log_rescued_responses - #64
Merged
Merged
Conversation
When a controller raises, no response row is logged, so 5xx and rescued responses like RecordNotFound (404) are never recorded. The error row is always fatal, although the gem silences DebugExceptions, which honours config.action_dispatch.log_rescued_responses (Rails 7.0+) and config.action_dispatch.debug_exception_log_level (Rails 7.1+). The tests render exceptions like a production app and pin a response row with the status the client gets (500, 404 from rescue_responses, also when a template error wraps the exception), a fatal error row by default, no error row for rescued responses when log_rescued_responses is false, and the error row at debug_exception_log_level. The two existing specs that raise now expect the response row too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Points logtail-rack at the claude/response-on-exception branch of logtail-ruby-rack in the root Gemfile and in every gemfiles/*.gemfile, so CI exercises HTTPEvents.status_for_exception. To be replaced by a follow-up commit that requires logtail-rack ~> 0.2, >= 0.2.9 once it's released. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…og settings The integration sets HTTPEvents.status_for_exception to ActionDispatch::ExceptionWrapper.new(nil, exception).status_code, the status ShowExceptions renders: config.action_dispatch.rescue_responses (404 for ActiveRecord::RecordNotFound), with ActionView::Template::Error unwrapped the way each Rails version does it, 500 otherwise. ErrorEvent now reads the request env keys DebugExceptions reads, whose logging the gem silences: no error row for rescued responses when action_dispatch.log_rescued_responses is false (Rails 7.0+), and the row at action_dispatch.debug_exception_log_level (Rails 7.1+). Without them, every exception is logged at fatal as before. The settings specs now change Rails.application.env_config, as Rails merges it into every request env and overrides per-request values. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
logtail-rack 0.2.8 times responses with the monotonic clock, which Timecop doesn't freeze, so "Completed 200 OK in 0.0ms" no longer holds. The last CI run on main still resolved logtail-rack 0.2.7. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ErrorEvent logged inside its rescue without a guard, so an error while building or writing the error event replaced the exception of the app. A StandardError there now goes to Logtail::Config.instance.debug, and the original exception is always re-raised. Kept local to ErrorEvent, so this doesn't depend on logtail-rack's log_safely. Both tests failed before the guard: the IOError of the logger and the ArgumentError of the event builder propagated instead of the app's error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… says config.load_defaults 7.1 sets config.action_dispatch.debug_exception_log_level to :error, so following it would turn every exception row of most modern apps, real 500s included, from fatal into error and break alerts on level = fatal. That is too big a change for a patch release, so the error row keeps its fatal level, as on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ErrorEvent stops reading action_dispatch.debug_exception_log_level and logs at fatal exactly as on main, since load_defaults 7.1 sets it to :error for most apps. Following it may come in a minor release. Honouring log_rescued_responses, the status resolver and the logging guard stay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eleased" This reverts commit 15f9f96.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PetrHeinz
marked this pull request as ready for review
October 2, 2026 12:08
PetrHeinz
added a commit
that referenced
this pull request
Oct 2, 2026
…level main has #64 as one squashed commit, so lib/logtail-rails/error_event.rb and spec/logtail-rails/error_event_spec.rb conflicted with this branch's copy of #64. Both are resolved to this branch's side, which is main's #64 plus this PR's change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When a controller raises, the gem logs the request row and a fatal error row, but no
http_response_sentrow. The status the client gets is never recorded, so dashboards and alerts on 5xx stay blind, and a 404 fromActiveRecord::RecordNotFoundshows up only as a fatal error. Yesterday's red-team reproduced this on all seven Rails versions it tested: an exception gave no response row at all, andRecordNotFoundwas logged as fatal.What changes:
Logtail::Integrations::Rack::HTTPEvents.status_for_exceptionresolver (from the logtail-rack PR Log a response row with status 500 when the app raises logtail-ruby-rack#27), so the response row that rack now logs for an exception gets the status Rails responds with:ActionDispatch::ExceptionWrapper.new(nil, exception).status_code. That's the same callShowExceptionsandDebugExceptionsuse, so it honoursconfig.action_dispatch.rescue_responses(RecordNotFound→ 404,ParameterMissing→ 400, anything else → 500) and unwrapsActionView::Template::Errorthe way Rails does, on every Rails version.DebugExceptions' own logging and logs the error row from itsErrorEventmiddleware instead. That middleware now honoursconfig.action_dispatch.log_rescued_responses = false(Rails 7.0+) likeDebugExceptionsdoes, through the same request env key: no error row for rescued responses such asRecordNotFound. The response row with the 404 is still logged.ErrorEvent's own logging can no longer replace the app's exception: aStandardErrorwhile building or writing the error row goes toLogtail::Config.instance.debug, and the original exception is always re-raised. This is local toErrorEvent, so the PR only needs the new rack API.Behaviour and compatibility:
Completed 404 Not Found in 12.3ms.config.action_dispatch.debug_exception_log_levelis left out on purpose:load_defaults 7.1sets it to:error, which would turn every exception row of most apps from fatal into error and break alerts onlevel = fatal.log_rescued_responsesoff stop getting error rows for rescued responses, matching their Rails logs. Nothing changes for apps that keep the default.config.action_dispatch.rescue_responses, which the integration follows.Targets the 0.2.15 patch release.
Depends on logtail-rack 0.2.9, which isn't released yet. The
TEMP: run CI against the logtail-rack branch until 0.2.9 is releasedcommit points the rootGemfileand everygemfiles/*.gemfileat theclaude/response-on-exceptionbranch of logtail-ruby-rack, so CI runs against the real API. The gemspec floor isn't bumped here: at merge time, after logtail-rack 0.2.9 is released, the TEMP commit gets replaced by a follow-up commit that requireslogtail-rack ~> 0.2, >= 0.2.9. Until then this must not be merged.It also carries the "Expect real response durations in the request specs" commit that the other open logtail-rails PRs share, byte for byte: logtail-rack 0.2.8 times responses with the monotonic clock, which Timecop doesn't freeze, so the three specs expecting
0.0msnow fail against the released rack too.The first commit only adds the tests and is expected to fail on CI; the TEMP commit and the fixes come after it.
🤖 Generated with Claude Code