Skip to content

Commit 27e5f71

Browse files
authored
[RF-DOCS] Update Rails Error Reporting Guide [ci skip] (rails#52885)
- 'For Libraries' is made into a top section and renamed 'Error-reporting Libraries' - Various important paragraphs are reformatted into notes. - To aid understanding for beginners, links are added when various concepts are referenced e.g. 'railties', 'jobs' etc. - Sections are added on unexpected, unsubscribe and disable. - Various wording tweaks are made for improved readability and easier understanding.
1 parent fae425b commit 27e5f71

File tree

2 files changed

+156
-38
lines changed

2 files changed

+156
-38
lines changed

guides/source/documents.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@
175175
-
176176
name: Error Reporting in Rails Applications
177177
url: error_reporting.html
178-
description: This guide introduces ways to manage exceptions that occur in Ruby on Rails applications.
178+
description: This guide introduces ways to manage errors that occur in Ruby on Rails applications.
179179
-
180180
name: Debugging Rails Applications
181181
url: debugging_rails_applications.html

guides/source/error_reporting.md

Lines changed: 155 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Error Reporting in Rails Applications
44
========================
55

6-
This guide introduces ways to manage exceptions that occur in Ruby on Rails applications.
6+
This guide introduces ways to manage errors in a Rails application.
77

88
After reading this guide, you will know:
99

@@ -13,11 +13,16 @@ After reading this guide, you will know:
1313
--------------------------------------------------------------------------------
1414

1515
Error Reporting
16-
------------------------
16+
---------------
1717

18-
The Rails [error reporter](https://api.rubyonrails.org/classes/ActiveSupport/ErrorReporter.html) provides a standard way to collect exceptions that occur in your application and report them to your preferred service or location.
18+
The Rails [error
19+
reporter](https://api.rubyonrails.org/classes/ActiveSupport/ErrorReporter.html)
20+
provides a standard way to collect errors that occur in your application and
21+
report them to your preferred service or location (e.g. you could report the
22+
errors to a monitoring service such as
23+
Sentry).
1924

20-
The error reporter aims to replace boilerplate error-handling code like this:
25+
It aims to replace boilerplate error-handling code like this:
2126

2227
```ruby
2328
begin
@@ -35,17 +40,32 @@ Rails.error.handle(SomethingIsBroken) do
3540
end
3641
```
3742

38-
Rails wraps all executions (such as HTTP requests, jobs, and `rails runner` invocations) in the error reporter, so any unhandled errors raised in your app will automatically be reported to your error-reporting service via their subscribers.
43+
Rails wraps all executions (such as HTTP
44+
requests,
45+
[jobs](active_job_basics.html), and [rails runner](command_line.html#bin-rails-runner) invocations) in the error reporter,
46+
so any unhandled errors raised in your app will automatically be reported to
47+
your error-reporting service via their subscribers.
3948

40-
This means that third-party error-reporting libraries no longer need to insert a Rack middleware or do any monkey-patching to capture unhandled exceptions. Libraries that use ActiveSupport can also use this to non-intrusively report warnings that would previously have been lost in logs.
49+
This means that third-party error-reporting libraries no longer need to insert a
50+
[Rack](rails_on_rack.html) middleware or do any monkey-patching to capture
51+
unhandled errors. Libraries that use [Active
52+
Support](https://api.rubyonrails.org/classes/ActiveSupport.html) can also use
53+
this to non-intrusively report warnings that would previously have been lost in
54+
logs.
4155

42-
Using the Rails' error reporter is not required. All other means of capturing errors still work.
56+
NOTE: Using the Rails error reporter is optional, as other means of capturing
57+
errors still work.
4358

4459
### Subscribing to the Reporter
4560

46-
To use the error reporter, you need a _subscriber_. A subscriber is any object with a `report` method. When an error occurs in your application or is manually reported, the Rails error reporter will call this method with the error object and some options.
61+
To use the error reporter with an external service, you need a _subscriber_. A
62+
subscriber can be any Ruby object with a `report` method. When an error occurs
63+
in your application or is manually reported, the Rails error reporter will call
64+
this method with the error object and some options.
4765

48-
Some error-reporting libraries, such as [Sentry's](https://github.com/getsentry/sentry-ruby/blob/e18ce4b6dcce2ebd37778c1e96164684a1e9ebfc/sentry-rails/lib/sentry/rails/error_subscriber.rb) and [Honeybadger's](https://docs.honeybadger.io/lib/ruby/integration-guides/rails-exception-tracking/), automatically register a subscriber for you. Consult your provider's documentation for more details.
66+
NOTE: Some error-reporting libraries, such as Sentry's
67+
and Honeybadger's,
68+
automatically register a subscriber for you.
4969

5070
You may also create a custom subscriber. For example:
5171

@@ -58,23 +78,51 @@ class ErrorSubscriber
5878
end
5979
```
6080

61-
After defining the subscriber class, register it by calling [`Rails.error.subscribe`](https://api.rubyonrails.org/classes/ActiveSupport/ErrorReporter.html#method-i-subscribe) method:
81+
After defining the subscriber class, you can register it by calling the
82+
[`Rails.error.subscribe`](https://api.rubyonrails.org/classes/ActiveSupport/ErrorReporter.html#method-i-subscribe)
83+
method:
6284

6385
```ruby
6486
Rails.error.subscribe(ErrorSubscriber.new)
6587
```
6688

67-
You can register as many subscribers as you wish. Rails will call them in turn, in the order in which they were registered.
89+
You can register as many subscribers as you wish. Rails will call them in the
90+
order in which they were registered.
91+
92+
It is also possible to unregister a subscriber by calling
93+
[`Rails.error.unsubscribe`](https://api.rubyonrails.org/classes/ActiveSupport/ErrorReporter.html#method-i-unsubscribe).
94+
This may be useful if you'd like to replace or remove a subscriber added by one
95+
of your dependencies. Both `subscribe` and `unsubscribe` can take either a
96+
subscriber or a class as follows:
97+
98+
```ruby
99+
subscriber = ErrorSubscriber.new
100+
Rails.error.unsubscribe(subscriber)
101+
# or
102+
Rails.error.unsubscribe(ErrorSubscriber)
103+
```
68104

69-
NOTE: The Rails error-reporter will always call registered subscribers, regardless of your environment. However, many error-reporting services only report errors in production by default. You should configure and test your setup across environments as needed.
105+
NOTE: The Rails error reporter will always call registered subscribers,
106+
regardless of your environment. However, many error-reporting services only
107+
report errors in production by default. You should configure and test your setup
108+
across environments as needed.
70109

71110
### Using the Error Reporter
72111

73-
There are three ways you can use the error reporter:
112+
Rails error reporter has four methods that allow you to report methods in
113+
different ways:
114+
115+
* `Rails.error.handle`
116+
* `Rails.error.record`
117+
* `Rails.error.report`
118+
* `Rails.error.unexpected`
74119

75120
#### Reporting and Swallowing Errors
76121

77-
[`Rails.error.handle`](https://api.rubyonrails.org/classes/ActiveSupport/ErrorReporter.html#method-i-handle) will report any error raised within the block. It will then **swallow** the error, and the rest of your code outside the block will continue as normal.
122+
The
123+
[`Rails.error.handle`](https://api.rubyonrails.org/classes/ActiveSupport/ErrorReporter.html#method-i-handle)
124+
method will report any error raised within the block. It will then **swallow**
125+
the error, and the rest of your code outside the block will continue as normal.
78126

79127
```ruby
80128
result = Rails.error.handle do
@@ -84,7 +132,9 @@ result # => nil
84132
1 + 1 # This will be executed
85133
```
86134

87-
If no error is raised in the block, `Rails.error.handle` will return the result of the block, otherwise it will return `nil`. You can override this by providing a `fallback`:
135+
If no error is raised in the block, `Rails.error.handle` will return the result
136+
of the block, otherwise it will return `nil`. You can override this by providing
137+
a `fallback`:
88138

89139
```ruby
90140
user = Rails.error.handle(fallback: -> { User.anonymous }) do
@@ -94,7 +144,10 @@ end
94144

95145
#### Reporting and Re-raising Errors
96146

97-
[`Rails.error.record`](https://api.rubyonrails.org/classes/ActiveSupport/ErrorReporter.html#method-i-record) will report errors to all registered subscribers and then re-raise the error, meaning that the rest of your code won't execute.
147+
The
148+
[`Rails.error.record`](https://api.rubyonrails.org/classes/ActiveSupport/ErrorReporter.html#method-i-record)
149+
method will report errors to all registered subscribers and then **re-raise**
150+
the error, meaning that the rest of your code won't execute.
98151

99152
```ruby
100153
Rails.error.record do
@@ -103,11 +156,13 @@ end
103156
1 + 1 # This won't be executed
104157
```
105158

106-
If no error is raised in the block, `Rails.error.record` will return the result of the block.
159+
If no error is raised in the block, `Rails.error.record` will return the result
160+
of the block.
107161

108162
#### Manually Reporting Errors
109163

110-
You can also manually report errors by calling [`Rails.error.report`](https://api.rubyonrails.org/classes/ActiveSupport/ErrorReporter.html#method-i-report):
164+
You can also manually report errors by calling
165+
[`Rails.error.report`](https://api.rubyonrails.org/classes/ActiveSupport/ErrorReporter.html#method-i-report):
111166

112167
```ruby
113168
begin
@@ -117,39 +172,64 @@ rescue StandardError => e
117172
end
118173
```
119174

120-
Any options you pass will be passed on the error subscribers.
175+
Any options you pass will be passed on to the error subscribers.
121176

122-
### Error-reporting Options
177+
#### Reporting Unexpected Errors
178+
179+
You can report any unexpected error by calling
180+
[`Rails.error.unexpected`](https://api.rubyonrails.org/classes/ActiveSupport/ErrorReporter.html#method-i-unexpected").
123181

124-
All 3 reporting APIs (`#handle`, `#record`, and `#report`) support the following options, which are then passed along to all registered subscribers:
182+
When called in production, this method will return nil after the error is
183+
reported and the execution of your code will continue.
125184

126-
- `handled`: a `Boolean` to indicate if the error was handled. This is set to `true` by default. `#record` sets this to `false`.
127-
- `severity`: a `Symbol` describing the severity of the error. Expected values are: `:error`, `:warning`, and `:info`. `#handle` sets this to `:warning`, while `#record` sets it to `:error`.
128-
- `context`: a `Hash` to provide more context about the error, like request or user details
129-
- `source`: a `String` about the source of the error. The default source is `"application"`. Errors reported by internal libraries may set other sources; the Redis cache library may use `"redis_cache_store.active_support"`, for instance. Your subscriber can use the source to ignore errors you aren't interested in.
185+
When called in development, the error will be wrapped in a new error class (to
186+
ensure it's not being rescued higher in the stack) and surfaced to the developer
187+
for debugging.
188+
189+
For example:
130190

131191
```ruby
132-
Rails.error.handle(context: { user_id: user.id }, severity: :info) do
192+
def edit
193+
if published?
194+
Rails.error.unexpected("[BUG] Attempting to edit a published article, that shouldn't be possible")
195+
false
196+
end
133197
# ...
134198
end
135199
```
136200

137-
### Filtering by Error Classes
201+
NOTE: This method is intended to gracefully handle any errors that may occur in
202+
production, but that aren't anticipated to be the result of typical use.
138203

139-
With `Rails.error.handle` and `Rails.error.record`, you can also choose to only report errors of certain classes. For example:
204+
### Error-reporting Options
205+
206+
The reporting APIs `#handle`, `#record`, and `#report` support the following
207+
options, which are then passed along to all registered subscribers:
208+
209+
- `handled`: a `Boolean` to indicate if the error was handled. This is set to
210+
`true` by default. `#record` sets this to `false`.
211+
- `severity`: a `Symbol` describing the severity of the error. Expected values
212+
are: `:error`, `:warning`, and `:info`. `#handle` sets this to `:warning`,
213+
while `#record` sets it to `:error`.
214+
- `context`: a `Hash` to provide more context about the error, like request or
215+
user details
216+
- `source`: a `String` about the source of the error. The default source is
217+
`"application"`. Errors reported by internal libraries may set other sources;
218+
the Redis cache library may use `"redis_cache_store.active_support"`, for
219+
instance. Your subscriber can use the source to ignore errors you aren't
220+
interested in.
140221

141222
```ruby
142-
Rails.error.handle(IOError) do
143-
1 + '1' # raises TypeError
223+
Rails.error.handle(context: { user_id: user.id }, severity: :info) do
224+
# ...
144225
end
145-
1 + 1 # TypeErrors are not IOErrors, so this will *not* be executed
146226
```
147227

148-
Here, the `TypeError` will not be captured by the Rails error reporter. Only instances of `IOError` and its descendants will be reported. Any other errors will be raised as normal.
149-
150228
### Setting Context Globally
151229

152-
In addition to setting context through the `context` option, you can use the [`#set_context`](https://api.rubyonrails.org/classes/ActiveSupport/ErrorReporter.html#method-i-set_context) API. For example:
230+
In addition to setting context through the `context` option, you can use
231+
[`Rails.error.set_context`](https://api.rubyonrails.org/classes/ActiveSupport/ErrorReporter.html#method-i-set_context).
232+
For example:
153233

154234
```ruby
155235
Rails.error.set_context(section: "checkout", user_id: @user.id)
@@ -165,9 +245,44 @@ Rails.error.handle(context: { b: 3 }) { raise }
165245
# The reported context will be: {:a=>1, :b=>3}
166246
```
167247

168-
### For Libraries
248+
### Filtering by Error Classes
249+
250+
With `Rails.error.handle` and `Rails.error.record`, you can also choose to only
251+
report errors of certain classes. For example:
252+
253+
```ruby
254+
Rails.error.handle(IOError) do
255+
1 + '1' # raises TypeError
256+
end
257+
1 + 1 # TypeErrors are not IOErrors, so this will *not* be executed
258+
```
259+
260+
Here, the `TypeError` will not be captured by the Rails error reporter. Only
261+
instances of `IOError` and its descendants will be reported. Any other errors
262+
will be raised as normal.
263+
264+
### Disabling Notifications
265+
266+
You can prevent a subscriber from being notified of errors for the duration of a
267+
block by calling
268+
[`Rails.error.disable`](https://api.rubyonrails.org/classes/ActiveSupport/ErrorReporter.html#method-i-disable).
269+
Similarly to `subscribe` and `unsubscribe`, you can pass in either the
270+
subscriber itself, or its class.
271+
272+
```ruby
273+
Rails.error.disable(ErrorSubscriber) do
274+
1 + '1' # TypeError will not be reported via the ErrorSubscriber
275+
end
276+
```
277+
278+
NOTE: This can also be helpful for third-party error reporting services who may
279+
want to manage error handling a different way, or higher in the stack.
280+
281+
Error-reporting Libraries
282+
------------------------
169283

170-
Error-reporting libraries can register their subscribers in a `Railtie`:
284+
Error-reporting libraries can register their subscribers in a
285+
[Railtie](https://api.rubyonrails.org/classes/Rails/Railtie.html):
171286

172287
```ruby
173288
module MySdk
@@ -179,4 +294,7 @@ module MySdk
179294
end
180295
```
181296

182-
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.
297+
NOTE: If you register an error subscriber, but still have other error mechanisms
298+
like a Rack middleware, you may end up with errors reported multiple times. You
299+
should either remove your other mechanisms or adjust your report functionality
300+
so it skips reporting an error it has seen before.

0 commit comments

Comments
 (0)