Handle throwing in controller action in log subscriber

When throw was used in a controller action, and there is matching catch
around the request in a Rack middleware, then :exception won't be
present in the event payload.

This is because ActiveSupport::Notifications::Instrumenter.instrument
sets :exception in a rescue handler, but rescue is never called in a
throw/catch scenario:

  catch(:halt) do
    begin
      throw :halt
    rescue Exception => e
      puts "rescue" # never reached
    ensure
      puts "ensure"
    end
  end

Missing :exception was actually handled prior to Rails 6.1.0, but an
optimization updated the code to assume this was present. So this can be
considered a regression fix.
This commit is contained in:
Janko Marohnić 2021-01-23 23:48:25 +01:00
parent dde814e906
commit 53adf53bc5
No known key found for this signature in database
GPG Key ID: 84166B4FB1C84F3E
3 changed files with 17 additions and 1 deletions

View File

@ -1,5 +1,9 @@
## Unreleased
* Fix error in `ActionController::LogSubscriber` that would happen when throwing inside a controller action.
*Janko Marohnić*
* Change the request method to a `GET` when passing failed requests down to `config.exceptions_app`.
*Alex Robbin*

View File

@ -23,7 +23,7 @@ module ActionController
additions = ActionController::Base.log_process_action(payload)
status = payload[:status]
if status.nil? && (exception_class_name = payload[:exception].first)
if status.nil? && (exception_class_name = payload[:exception]&.first)
status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
end

View File

@ -64,6 +64,10 @@ module Another
render inline: "<%= cache_unless(true, 'foo') { 'bar' } %>"
end
def with_throw
throw :halt
end
def with_exception
raise Exception
end
@ -190,6 +194,14 @@ class ACLogSubscriberTest < ActionController::TestCase
assert_match(/Completed 200 OK in \d+ms/, logs[1])
end
def test_process_action_with_throw
catch(:halt) do
get :with_throw
wait
end
assert_match(/Completed in \d+ms/, logs[1])
end
def test_append_info_to_payload_is_called_even_with_exception
begin
get :with_exception