Skip to content

Commit 0c4d89c

Browse files
authored
Merge pull request rails#46299 from spickermann/error-reporter-to-handle-multiple-exception-classes
Allow ErrorReporter to handle several error classes
2 parents 00547aa + 1a9b887 commit 0c4d89c

File tree

4 files changed

+58
-9
lines changed

4 files changed

+58
-9
lines changed

activesupport/CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
* `Rails.error.handle` and `Rails.error.record` filter now by multiple error classes.
2+
3+
```ruby
4+
Rails.error.handle(IOError, ArgumentError) do
5+
1 + '1' # raises TypeError
6+
end
7+
1 + 1 # TypeErrors are not IOErrors or ArgumentError, so this will *not* be handled
8+
```
9+
10+
*Martin Spickermann*
11+
112
* `Class#subclasses` and `Class#descendants` now automatically filter reloaded classes.
213

314
Previously they could return old implementations of reloadable classes that have been

activesupport/lib/active_support/error_reporter.rb

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def initialize(*subscribers, logger: nil)
4242
# 1 + '1'
4343
# end
4444
#
45-
# Can be restricted to handle only a specific error class:
45+
# Can be restricted to handle only specific error classes:
4646
#
4747
# maybe_tags = Rails.error.handle(Redis::BaseError) { redis.get("tags") }
4848
#
@@ -69,9 +69,10 @@ def initialize(*subscribers, logger: nil)
6969
# * +:source+ - This value is passed along to subscribers to indicate the
7070
# source of the error. Subscribers can use this value to ignore certain
7171
# errors. Defaults to <tt>"application"</tt>.
72-
def handle(error_class = StandardError, severity: :warning, context: {}, fallback: nil, source: DEFAULT_SOURCE)
72+
def handle(*error_classes, severity: :warning, context: {}, fallback: nil, source: DEFAULT_SOURCE)
73+
error_classes = [StandardError] if error_classes.blank?
7374
yield
74-
rescue error_class => error
75+
rescue *error_classes => error
7576
report(error, handled: true, severity: severity, context: context, source: source)
7677
fallback.call if fallback
7778
end
@@ -84,7 +85,7 @@ def handle(error_class = StandardError, severity: :warning, context: {}, fallbac
8485
# 1 + '1'
8586
# end
8687
#
87-
# Can be restricted to handle only a specific error class:
88+
# Can be restricted to handle only specific error classes:
8889
#
8990
# tags = Rails.error.record(Redis::BaseError) { redis.get("tags") }
9091
#
@@ -104,9 +105,10 @@ def handle(error_class = StandardError, severity: :warning, context: {}, fallbac
104105
# * +:source+ - This value is passed along to subscribers to indicate the
105106
# source of the error. Subscribers can use this value to ignore certain
106107
# errors. Defaults to <tt>"application"</tt>.
107-
def record(error_class = StandardError, severity: :error, context: {}, source: DEFAULT_SOURCE)
108+
def record(*error_classes, severity: :error, context: {}, source: DEFAULT_SOURCE)
109+
error_classes = [StandardError] if error_classes.blank?
108110
yield
109-
rescue error_class => error
111+
rescue *error_classes => error
110112
report(error, handled: false, severity: severity, context: context, source: source)
111113
raise
112114
end

activesupport/test/error_reporter_test.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,23 @@ class ErrorReporterTest < ActiveSupport::TestCase
6868
assert_equal [], @subscriber.events
6969
end
7070

71+
test "#handle can be scoped to several exception classes" do
72+
assert_raises ArgumentError do
73+
@reporter.handle(NameError, NoMethodError) do
74+
raise ArgumentError
75+
end
76+
end
77+
assert_equal [], @subscriber.events
78+
end
79+
80+
test "#handle swallows and reports matching errors" do
81+
error = ArgumentError.new("Oops")
82+
@reporter.handle(NameError, ArgumentError) do
83+
raise error
84+
end
85+
assert_equal [[error, true, :warning, "application", {}]], @subscriber.events
86+
end
87+
7188
test "#handle passes through the return value" do
7289
result = @reporter.handle do
7390
2 + 2
@@ -125,6 +142,25 @@ class ErrorReporterTest < ActiveSupport::TestCase
125142
assert_equal [], @subscriber.events
126143
end
127144

145+
test "#record can be scoped to several exception classes" do
146+
assert_raises ArgumentError do
147+
@reporter.record(NameError, NoMethodError) do
148+
raise ArgumentError
149+
end
150+
end
151+
assert_equal [], @subscriber.events
152+
end
153+
154+
test "#record report any matching, unhandled error and re-raise them" do
155+
error = ArgumentError.new("Oops")
156+
assert_raises ArgumentError do
157+
@reporter.record(NameError, ArgumentError) do
158+
raise error
159+
end
160+
end
161+
assert_equal [[error, false, :error, "application", {}]], @subscriber.events
162+
end
163+
128164
test "#record passes through the return value" do
129165
result = @reporter.record do
130166
2 + 2

guides/source/error_reporting.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,9 @@ Rails.error.handle(context: {user_id: user.id}, severity: :info) do
131131
end
132132
```
133133

134-
### Filtering by Error Class
134+
### Filtering by Error Classes
135135

136-
With `Rails.error.handle` and `Rails.error.record`, you can also choose to only report errors of a certain class. For example:
136+
With `Rails.error.handle` and `Rails.error.record`, you can also choose to only report errors of certain classes. For example:
137137

138138
```ruby
139139
Rails.error.handle(IOError) do
@@ -176,4 +176,4 @@ module MySdk
176176
end
177177
```
178178

179-
If you register an error subscriber, but still have other error mechanisms like a Rack middleware, you may end up with errors reported multiple times. You should either remove your other mechanisms or adjust your report functionality so it skips reporting an exception it has seen before.
179+
If you register an error subscriber, but still have other error mechanisms like a Rack middleware, you may end up with errors reported multiple times. You should either remove your other mechanisms or adjust your report functionality so it skips reporting an exception it has seen before.

0 commit comments

Comments
 (0)