Skip to content

Commit 22ea6bf

Browse files
committed
Add support for :skip_retries to Oban.ErrorReporter
1 parent 7414c9d commit 22ea6bf

File tree

3 files changed

+72
-17
lines changed

3 files changed

+72
-17
lines changed

lib/sentry/application.ex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ defmodule Sentry.Application do
6464
end
6565

6666
if config[:oban][:capture_errors] do
67-
Sentry.Integrations.Oban.ErrorReporter.attach()
67+
Sentry.Integrations.Oban.ErrorReporter.attach_telemetry_handler(
68+
Keyword.get(config[:oban], :error_reporter, [])
69+
)
6870
end
6971

7072
if config[:quantum][:cron][:enabled] do

lib/sentry/integrations/oban/error_reporter.ex

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ defmodule Sentry.Integrations.Oban.ErrorReporter do
44
# See this blog post:
55
# https://getoban.pro/articles/enhancing-error-reporting
66

7-
@spec attach() :: :ok
8-
def attach do
7+
@spec attach_telemetry_handler(keyword()) :: :ok
8+
def attach_telemetry_handler(config) do
99
_ =
1010
:telemetry.attach(
1111
__MODULE__,
1212
[:oban, :job, :exception],
1313
&__MODULE__.handle_event/4,
14-
:no_config
14+
config
1515
)
1616

1717
:ok
@@ -21,9 +21,24 @@ defmodule Sentry.Integrations.Oban.ErrorReporter do
2121
[atom(), ...],
2222
term(),
2323
%{required(:job) => struct(), optional(term()) => term()},
24-
:no_config
24+
keyword()
2525
) :: :ok
26-
def handle_event([:oban, :job, :exception], _measurements, %{job: job} = _metadata, :no_config) do
26+
def handle_event([:oban, :job, :exception], _measurements, %{job: job} = _metadata, config) do
27+
if should_report?(job, config) do
28+
do_report(job)
29+
else
30+
:ok
31+
end
32+
end
33+
34+
defp should_report?(job, config) do
35+
case Keyword.get(config, :skip_retries) do
36+
true -> job.attempt == job.max_attempts
37+
_ -> true
38+
end
39+
end
40+
41+
defp do_report(job) do
2742
%{reason: reason, stacktrace: stacktrace} = job.unsaved_error
2843

2944
stacktrace =
@@ -39,15 +54,13 @@ defmodule Sentry.Integrations.Oban.ErrorReporter do
3954
[inspect(reason)]
4055
end
4156

42-
opts =
43-
[
44-
stacktrace: stacktrace,
45-
tags: %{oban_worker: job.worker, oban_queue: job.queue, oban_state: job.state},
46-
fingerprint: [inspect(job.worker)] ++ fingerprint_opts,
47-
extra:
48-
Map.take(job, [:args, :attempt, :id, :max_attempts, :meta, :queue, :tags, :worker]),
49-
integration_meta: %{oban: %{job: job}}
50-
]
57+
opts = [
58+
stacktrace: stacktrace,
59+
tags: %{oban_worker: job.worker, oban_queue: job.queue, oban_state: job.state},
60+
fingerprint: [inspect(job.worker)] ++ fingerprint_opts,
61+
extra: Map.take(job, [:args, :attempt, :id, :max_attempts, :meta, :queue, :tags, :worker]),
62+
integration_meta: %{oban: %{job: job}}
63+
]
5164

5265
_ =
5366
if is_exception(reason) do

test/sentry/integrations/oban/error_reporter_test.exs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ defmodule Sentry.Integrations.Oban.ErrorReporterTest do
3030
[:oban, :job, :exception],
3131
%{},
3232
%{job: job},
33-
:no_config
33+
[]
3434
)
3535

3636
assert [event] = Sentry.Test.pop_sentry_reports()
@@ -69,7 +69,7 @@ defmodule Sentry.Integrations.Oban.ErrorReporterTest do
6969
[:oban, :job, :exception],
7070
%{},
7171
%{job: job},
72-
:no_config
72+
[]
7373
)
7474

7575
assert [event] = Sentry.Test.pop_sentry_reports()
@@ -94,5 +94,45 @@ defmodule Sentry.Integrations.Oban.ErrorReporterTest do
9494
assert event.tags.oban_state == "available"
9595
assert event.tags.oban_worker == "Sentry.Integrations.Oban.ErrorReporterTest.MyWorker"
9696
end
97+
98+
test "with skip_retries: true, only reports final attempt failures" do
99+
job =
100+
%{"id" => "123", "entity" => "user", "type" => "delete"}
101+
|> MyWorker.new()
102+
|> Ecto.Changeset.apply_action!(:validate)
103+
|> Map.replace!(:unsaved_error, %{
104+
reason: %RuntimeError{message: "oops"},
105+
kind: :error,
106+
stacktrace: []
107+
})
108+
109+
Sentry.Test.start_collecting()
110+
111+
job_attempt_1 = Map.merge(job, %{attempt: 1, max_attempts: 3})
112+
113+
assert :ok =
114+
ErrorReporter.handle_event(
115+
[:oban, :job, :exception],
116+
%{},
117+
%{job: job_attempt_1},
118+
skip_retries: true
119+
)
120+
121+
assert [] = Sentry.Test.pop_sentry_reports()
122+
123+
job_attempt_3 = Map.merge(job, %{attempt: 3, max_attempts: 3})
124+
125+
assert :ok =
126+
ErrorReporter.handle_event(
127+
[:oban, :job, :exception],
128+
%{},
129+
%{job: job_attempt_3},
130+
skip_retries: true
131+
)
132+
133+
assert [event] = Sentry.Test.pop_sentry_reports()
134+
assert event.original_exception == %RuntimeError{message: "oops"}
135+
assert event.tags.oban_worker == "Sentry.Integrations.Oban.ErrorReporterTest.MyWorker"
136+
end
97137
end
98138
end

0 commit comments

Comments
 (0)