Skip to content

Commit 33d0b5d

Browse files
authored
Introduce active_job_report_on_retry_error (#2617)
* Set active_job_report_after_job_retries to true by default Refs #2598 Refs #2597 * Use test jobs defined in support file * Introduce active_job_report_on_retry_error * Fix spec * Adjust spec skipping logic * Update CHANGELOG.md * Update CHANGELOG.md
1 parent de75063 commit 33d0b5d

File tree

6 files changed

+52
-120
lines changed

6 files changed

+52
-120
lines changed

CHANGELOG.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
config.sidekiq.propagate_traces = false unless Rails.const_defined?('Server')
1414
```
1515
- Only expose `active_storage` keys on span data if `send_default_pii` is on ([#2589](https://github.com/getsentry/sentry-ruby/pull/2589))
16-
- Add `Sentry.capture_log` ([#2606](https://github.com/getsentry/sentry-ruby/pull/2606))
16+
- Add `Sentry.capture_log` ([#2606](https://github.com/getsentry/sentry-ruby/pull/2617))
17+
- New configuration option called `active_job_report_on_retry_error` which enables reporting errors on each retry error ([#2617](https://github.com/getsentry/sentry-ruby/pull/2617))
1718

1819
### Bug Fixes
1920

@@ -28,7 +29,6 @@
2829
### Internal
2930

3031
- Remove `user_segment` from DSC ([#2586](https://github.com/getsentry/sentry-ruby/pull/2586))
31-
- New configuration option called `report_after_job_retries` for ActiveJob which makes reporting exceptions only happen when the last retry attempt failed ([#2500](https://github.com/getsentry/sentry-ruby/pull/2500))
3232

3333
## 5.23.0
3434

sentry-rails/lib/sentry/rails/active_job.rb

+4-24
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ class SentryReporter
2424
SPAN_ORIGIN = "auto.queue.active_job"
2525

2626
EVENT_HANDLERS = {
27-
"enqueue_retry.active_job" => :retry_handler,
28-
"retry_stopped.active_job" => :retry_stopped_handler
27+
"enqueue_retry.active_job" => :retry_handler
2928
}
3029

3130
class << self
@@ -53,19 +52,13 @@ def record(job, &block)
5352
rescue Exception => e # rubocop:disable Lint/RescueException
5453
finish_sentry_transaction(transaction, 500)
5554

56-
maybe_capture_exception(job, e)
55+
capture_exception(job, e)
5756

5857
raise
5958
end
6059
end
6160
end
6261

63-
def maybe_capture_exception(job, e)
64-
unless Sentry.configuration.rails.active_job_report_after_job_retries
65-
capture_exception(job, e)
66-
end
67-
end
68-
6962
def capture_exception(job, e)
7063
Sentry::Rails.capture_exception(
7164
e,
@@ -92,24 +85,11 @@ def detach_event_handlers
9285
subscribers.clear
9386
end
9487

95-
# This handler does not capture error when `active_job_report_after_job_retries` is true
96-
# because in such case only the last retry that failed will capture exception
88+
# This handler does not capture error unless `active_job_report_on_retry_error` is true
9789
def retry_handler(*args)
9890
handle_error_event(*args) do |job, error|
9991
return if !Sentry.initialized? || job.already_supported_by_sentry_integration?
100-
return if Sentry.configuration.rails.active_job_report_after_job_retries
101-
102-
capture_exception(job, error)
103-
end
104-
end
105-
106-
# This handler does not capture error when `active_job_report_after_job_retries` is false
107-
# because in such cases regular execution flow that failed will capture it in `record`
108-
# method
109-
def retry_stopped_handler(*args)
110-
handle_error_event(*args) do |job, error|
111-
return if !Sentry.initialized? || job.already_supported_by_sentry_integration?
112-
return unless Sentry.configuration.rails.active_job_report_after_job_retries
92+
return unless Sentry.configuration.rails.active_job_report_on_retry_error
11393

11494
capture_exception(job, error)
11595
end

sentry-rails/lib/sentry/rails/configuration.rb

+3-4
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,8 @@ class Configuration
156156
# @return [Hash<String, Array<Symbol>>]
157157
attr_accessor :active_support_logger_subscription_items
158158

159-
# Set this option to true if you want Sentry to only capture the last job
160-
# retry if it fails.
161-
attr_accessor :active_job_report_after_job_retries
159+
# Set this option to true if you want Sentry to capture each retry failure
160+
attr_accessor :active_job_report_on_retry_error
162161

163162
def initialize
164163
@register_error_subscriber = false
@@ -176,7 +175,7 @@ def initialize
176175
@enable_db_query_source = true
177176
@db_query_source_threshold_ms = 100
178177
@active_support_logger_subscription_items = Sentry::Rails::ACTIVE_SUPPORT_LOGGER_SUBSCRIPTION_ITEMS_DEFAULT.dup
179-
@active_job_report_after_job_retries = false
178+
@active_job_report_on_retry_error = false
180179
end
181180
end
182181
end

sentry-rails/spec/sentry/rails/activejob_spec.rb

+35-88
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,6 @@
11
# frozen_string_literal: true
22

3-
require "spec_helper"
4-
require "active_job/railtie"
5-
6-
class NormalJob < ActiveJob::Base
7-
def perform
8-
"foo"
9-
end
10-
end
11-
12-
class FailedJob < ActiveJob::Base
13-
self.logger = nil
14-
15-
class TestError < RuntimeError
16-
end
17-
18-
def perform
19-
a = 1
20-
b = 0
21-
raise TestError, "Boom!"
22-
end
23-
end
24-
25-
class FailedWithExtraJob < FailedJob
26-
def perform
27-
Sentry.get_current_scope.set_extras(foo: :bar)
28-
super
29-
end
30-
end
31-
32-
class JobWithArgument < ActiveJob::Base
33-
def perform(*args, integer:, post:, **options)
34-
raise "foo"
35-
end
36-
end
37-
38-
class QueryPostJob < ActiveJob::Base
39-
self.logger = nil
40-
41-
def perform
42-
Post.all.to_a
43-
end
44-
end
45-
46-
class RescuedActiveJob < FailedWithExtraJob
47-
rescue_from TestError, with: :rescue_callback
48-
49-
def rescue_callback(error); end
50-
end
51-
52-
class ProblematicRescuedActiveJob < FailedWithExtraJob
53-
rescue_from TestError, with: :rescue_callback
54-
55-
def rescue_callback(error)
56-
raise "foo"
57-
end
58-
end
59-
60-
class NormalJobWithCron < NormalJob
61-
include Sentry::Cron::MonitorCheckIns
62-
sentry_monitor_check_ins
63-
end
64-
65-
class FailedJobWithCron < FailedJob
66-
include Sentry::Cron::MonitorCheckIns
67-
sentry_monitor_check_ins slug: "failed_job", monitor_config: Sentry::Cron::MonitorConfig.from_crontab("5 * * * *")
68-
end
69-
70-
class FailedJobWithRetryOn < FailedJob
71-
if respond_to? :retry_on
72-
retry_on StandardError, attempts: 3, wait: 0
73-
end
74-
end
3+
require_relative "../../support/test_jobs"
754

765
RSpec.describe "without Sentry initialized", type: :job do
776
it "runs job" do
@@ -109,7 +38,10 @@ class FailedJobWithRetryOn < FailedJob
10938
JobWithArgument.perform_now("foo", { bar: Sentry }, integer: 1, post: post, nested: { another_level: { post: post2 } })
11039
end.to raise_error(RuntimeError)
11140

41+
expect(transport.events.size).to be(1)
42+
11243
event = transport.events.last.to_json_compatible
44+
11345
expect(event.dig("extra", "arguments")).to eq(
11446
[
11547
"foo",
@@ -134,7 +66,10 @@ def post.to_global_id
13466
JobWithArgument.perform_now(integer: 1, post: post)
13567
end.to raise_error(RuntimeError)
13668

69+
expect(transport.events.size).to be(1)
70+
13771
event = transport.events.last.to_json_compatible
72+
13873
expect(event.dig("extra", "arguments")).to eq(
13974
[
14075
{
@@ -152,7 +87,10 @@ def post.to_global_id
15287
JobWithArgument.perform_now("foo", { bar: Sentry }, integer: 1, post: post, range: 1..3)
15388
end.to raise_error(RuntimeError)
15489

90+
expect(transport.events.size).to be(1)
91+
15592
event = transport.events.last.to_json_compatible
93+
15694
expect(event.dig("extra", "arguments")).to eq(
15795
[
15896
"foo",
@@ -174,7 +112,10 @@ def post.to_global_id
174112
JobWithArgument.perform_now("foo", { bar: Sentry }, integer: 1, post: post, range: range)
175113
end.to raise_error(RuntimeError)
176114

115+
expect(transport.events.size).to be(1)
116+
177117
event = transport.events.last.to_json_compatible
118+
178119
expect(event.dig("extra", "arguments")).to eq(
179120
[
180121
"foo",
@@ -192,7 +133,10 @@ def post.to_global_id
192133
it "adds useful context to extra" do
193134
expect { FailedJob.perform_now }.to raise_error(FailedJob::TestError)
194135

136+
expect(transport.events.size).to be(1)
137+
195138
event = transport.events.last.to_json_compatible
139+
196140
expect(event.dig("extra", "active_job")).to eq("FailedJob")
197141
expect(event.dig("extra", "job_id")).to be_a(String)
198142
expect(event.dig("extra", "provider_job_id")).to be_nil
@@ -207,6 +151,8 @@ def post.to_global_id
207151
it "clears context" do
208152
expect { FailedWithExtraJob.perform_now }.to raise_error(FailedWithExtraJob::TestError)
209153

154+
expect(transport.events.size).to be(1)
155+
210156
event = transport.events.last.to_json_compatible
211157

212158
expect(event["extra"]["foo"]).to eq("bar")
@@ -224,7 +170,8 @@ def post.to_global_id
224170
it "sends transaction" do
225171
QueryPostJob.perform_now
226172

227-
expect(transport.events.count).to eq(1)
173+
expect(transport.events.size).to be(1)
174+
228175
transaction = transport.events.last
229176
expect(transaction.transaction).to eq("QueryPostJob")
230177
expect(transaction.transaction_info).to eq({ source: :task })
@@ -242,7 +189,7 @@ def post.to_global_id
242189
it "sends transaction and associates it with the event" do
243190
expect { FailedWithExtraJob.perform_now }.to raise_error(FailedWithExtraJob::TestError)
244191

245-
expect(transport.events.count).to eq(2)
192+
expect(transport.events.size).to be(2)
246193

247194
transaction = transport.events.first
248195
expect(transaction.transaction).to eq("FailedWithExtraJob")
@@ -277,7 +224,7 @@ def perform
277224
DeserializationErrorJob.perform_now
278225
end.to raise_error(ActiveJob::DeserializationError, /divided by 0/)
279226

280-
expect(transport.events.size).to eq(1)
227+
expect(transport.events.size).to be(1)
281228

282229
event = transport.events.last.to_json_compatible
283230
expect(event.dig("exception", "values", 0, "type")).to eq("ZeroDivisionError")
@@ -434,14 +381,22 @@ def perform(event, hint)
434381
end
435382
end
436383

437-
describe "active_job_report_after_job_retries", skip: RAILS_VERSION < 7.0 do
384+
describe "Reporting on retry errors", skip: RAILS_VERSION < 7.0 do
438385
before do
439-
if defined?(JRUBY_VERSION) && JRUBY_VERSION == "9.4.12.0" && RAILS_VERSION <= 7.1
386+
if defined?(JRUBY_VERSION) && JRUBY_VERSION == "9.4.12.0"
440387
skip "This crashes on jruby + rails 7.0.0.x. See https://github.com/getsentry/sentry-ruby/issues/2612"
441388
end
442389
end
443390

444-
context "when active_job_report_after_job_retries is false" do
391+
context "when active_job_report_on_retry_error is true" do
392+
before do
393+
Sentry.configuration.rails.active_job_report_on_retry_error = true
394+
end
395+
396+
after do
397+
Sentry.configuration.rails.active_job_report_on_retry_error = false
398+
end
399+
445400
it "reports 3 exceptions" do
446401
allow(Sentry::Rails::ActiveJobExtensions::SentryReporter)
447402
.to receive(:capture_exception).and_call_original
@@ -458,16 +413,8 @@ def perform(event, hint)
458413
end
459414
end
460415

461-
context "when active_job_report_after_job_retries is true" do
462-
before do
463-
Sentry.configuration.rails.active_job_report_after_job_retries = true
464-
end
465-
466-
after do
467-
Sentry.configuration.rails.active_job_report_after_job_retries = false
468-
end
469-
470-
it "reports 1 exception" do
416+
context "when active_job_report_on_retry_error is false" do
417+
it "reports 1 exception on final attempt failure" do
471418
allow(Sentry::Rails::ActiveJobExtensions::SentryReporter)
472419
.to receive(:capture_exception).and_call_original
473420

sentry-rails/spec/sentry/rails/configuration_spec.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ class MySubscriber; end
6060
end
6161
end
6262

63-
describe "#active_job_report_after_job_retries" do
63+
describe "#active_job_report_on_retry_error" do
6464
it "has correct default value" do
65-
expect(subject.active_job_report_after_job_retries).to eq(false)
65+
expect(subject.active_job_report_on_retry_error).to be(false)
6666
end
6767
end
6868
end

sentry-rails/spec/support/test_jobs.rb

+6
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,9 @@ class FailedJobWithCron < FailedJob
6565
include Sentry::Cron::MonitorCheckIns
6666
sentry_monitor_check_ins slug: "failed_job", monitor_config: Sentry::Cron::MonitorConfig.from_crontab("5 * * * *")
6767
end
68+
69+
class FailedJobWithRetryOn < FailedJob
70+
if respond_to? :retry_on
71+
retry_on StandardError, attempts: 3, wait: 0
72+
end
73+
end

0 commit comments

Comments
 (0)