Skip to content

Commit c2e42a2

Browse files
Merge pull request rails#42592 from flavorjones/issue-9343
Skip logging backtrace when exception is in `rescue_responses`
2 parents 1a01b7c + dc2d948 commit c2e42a2

File tree

8 files changed

+72
-4
lines changed

8 files changed

+72
-4
lines changed

actionpack/CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
* Configuration setting to skip logging an uncaught exception backtrace when the exception is
2+
present in `rescued_responses`.
3+
4+
It may be too noisy to get all backtraces logged for applications that manage uncaught
5+
exceptions via `rescued_responses` and `exceptions_app`.
6+
`config.action_dispatch.log_rescued_responses` (defaults to `true`) can be set to `false` in
7+
this case, so that only exceptions not found in `rescued_responses` will be logged.
8+
9+
*Alexander Azarov*, *Mike Dalessio*
10+
111
* Ignore file fixtures on `db:fixtures:load`
212

313
*Kevin Sjöberg*

actionpack/lib/action_dispatch/middleware/debug_exceptions.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module ActionDispatch
1010
# This middleware is responsible for logging exceptions and
1111
# showing a debugging page in case the request is local.
1212
class DebugExceptions
13+
cattr_accessor :log_rescued_responses, instance_accessor: false, default: true
1314
cattr_reader :interceptors, instance_accessor: false, default: []
1415

1516
def self.register_interceptor(object = nil, &block)
@@ -135,6 +136,7 @@ def log_error(request, wrapper)
135136
logger = logger(request)
136137

137138
return unless logger
139+
return if !log_rescued_responses?(request) && wrapper.rescue_response?
138140

139141
exception = wrapper.exception
140142
trace = wrapper.exception_trace
@@ -176,5 +178,10 @@ def routes_inspector(exception)
176178
def api_request?(content_type)
177179
@response_format == :api && !content_type.html?
178180
end
181+
182+
def log_rescued_responses?(request)
183+
per_request = request.get_header("action_dispatch.log_rescued_responses")
184+
per_request.nil? ? @@log_rescued_responses : per_request
185+
end
179186
end
180187
end

actionpack/lib/action_dispatch/middleware/exception_wrapper.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ def self.status_code_for_exception(class_name)
118118
Rack::Utils.status_code(@@rescue_responses[class_name])
119119
end
120120

121+
def rescue_response?
122+
@@rescue_responses.key?(exception.class.name)
123+
end
124+
121125
def source_extracts
122126
backtrace.map do |trace|
123127
file, line_number = extract_file_and_line_number(trace)

actionpack/lib/action_dispatch/railtie.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class Railtie < Rails::Railtie # :nodoc:
2525
config.action_dispatch.perform_deep_munge = true
2626
config.action_dispatch.request_id_header = "X-Request-Id"
2727
config.action_dispatch.return_only_request_media_type_on_content_type = true
28+
config.action_dispatch.log_rescued_responses = true
2829

2930
config.action_dispatch.default_headers = {
3031
"X-Frame-Options" => "SAMEORIGIN",
@@ -47,6 +48,7 @@ class Railtie < Rails::Railtie # :nodoc:
4748
self.ignore_accept_header = app.config.action_dispatch.ignore_accept_header
4849
self.return_only_media_type_on_content_type = app.config.action_dispatch.return_only_request_media_type_on_content_type
4950
ActionDispatch::Request::Utils.perform_deep_munge = app.config.action_dispatch.perform_deep_munge
51+
ActionDispatch::DebugExceptions.log_rescued_responses = app.config.action_dispatch.log_rescued_responses
5052
end
5153

5254
ActiveSupport.on_load(:action_dispatch_response) do

actionpack/test/dispatch/debug_exceptions_test.rb

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -515,9 +515,9 @@ def call(env)
515515
backtrace_cleaner = ActiveSupport::BacktraceCleaner.new
516516
backtrace_cleaner.add_silencer { true }
517517

518-
env = { "action_dispatch.show_exceptions" => true,
519-
"action_dispatch.logger" => Logger.new(output),
520-
"action_dispatch.backtrace_cleaner" => backtrace_cleaner }
518+
env = { "action_dispatch.show_exceptions" => true,
519+
"action_dispatch.logger" => Logger.new(output),
520+
"action_dispatch.backtrace_cleaner" => backtrace_cleaner }
521521

522522
get "/", headers: env
523523
assert_operator((output.rewind && output.read).lines.count, :>, 10)
@@ -544,7 +544,7 @@ def call(env)
544544
assert_equal 3, log.lines.count
545545
end
546546

547-
test "doesn't log the framework backtrace when error type is a invalid mine type" do
547+
test "doesn't log the framework backtrace when error type is a invalid mime type" do
548548
@app = ProductionApp
549549

550550
output = StringIO.new
@@ -566,6 +566,20 @@ def call(env)
566566
assert_equal 3, log.lines.count
567567
end
568568

569+
test "skips logging when rescued" do
570+
@app = DevelopmentApp
571+
572+
output = StringIO.new
573+
574+
env = { "action_dispatch.show_exceptions" => true,
575+
"action_dispatch.logger" => Logger.new(output),
576+
"action_dispatch.log_rescued_responses" => false }
577+
578+
get "/parameter_missing", headers: env
579+
assert_response 400
580+
assert_empty (output.rewind && output.read).lines
581+
end
582+
569583
test "display backtrace when error type is SyntaxError" do
570584
@app = DevelopmentApp
571585

actionpack/test/dispatch/exception_wrapper_test.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@ def backtrace
6666
assert_equal 400, wrapper.status_code
6767
end
6868

69+
test "#rescue_response? returns false for an exception that's not in rescue_responses" do
70+
exception = RuntimeError.new
71+
wrapper = ExceptionWrapper.new(@cleaner, exception)
72+
assert_equal false, wrapper.rescue_response?
73+
end
74+
75+
test "#rescue_response? returns true for an exception that is in rescue_responses" do
76+
exception = ActionController::RoutingError.new("")
77+
wrapper = ExceptionWrapper.new(@cleaner, exception)
78+
assert_equal true, wrapper.rescue_response?
79+
end
80+
6981
test "#application_trace cannot be nil" do
7082
nil_backtrace_wrapper = ExceptionWrapper.new(@cleaner, BadlyDefinedError.new)
7183
nil_cleaner_wrapper = ExceptionWrapper.new(nil, BadlyDefinedError.new)

guides/source/configuring.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,9 @@ Defaults to `'signed cookie'`.
689689
in the `ActionDispatch::SSL` middleware. Defaults to `308` as defined in
690690
https://tools.ietf.org/html/rfc7538.
691691
692+
* `config.action_dispatch.log_rescued_responses` enables logging those unhandled
693+
exceptions configured in `rescue_responses`. It defaults to `true`.
694+
692695
* `ActionDispatch::Callbacks.before` takes a block of code to run before the request.
693696
694697
* `ActionDispatch::Callbacks.after` takes a block of code to run after the request.

railties/test/application/configuration_test.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3159,6 +3159,22 @@ class MyLogger < ::Logger
31593159
assert_equal false, ActionDispatch::Request.return_only_media_type_on_content_type
31603160
end
31613161

3162+
test "ActionDispatch::DebugExceptions.log_rescued_responses is true by default" do
3163+
app "development"
3164+
3165+
assert_equal true, ActionDispatch::DebugExceptions.log_rescued_responses
3166+
end
3167+
3168+
test "ActionDispatch::DebugExceptions.log_rescued_responses can be configured" do
3169+
add_to_config <<-RUBY
3170+
config.action_dispatch.log_rescued_responses = false
3171+
RUBY
3172+
3173+
app "development"
3174+
3175+
assert_equal false, ActionDispatch::DebugExceptions.log_rescued_responses
3176+
end
3177+
31623178
test "logs a warning when running SQLite3 in production" do
31633179
restore_sqlite3_warning
31643180
app_file "config/initializers/active_record.rb", <<~RUBY

0 commit comments

Comments
 (0)