Skip to content

Commit c0c94fa

Browse files
authored
Fix Oban.PerformError grouping (#92)
This change improves how we store data from `Oban.PerformError` errors. Those errors don't come from exceptions, as they are delivered via Telemetry when the worker ends up with an `:error` or an `{:error, any()}` tuple. As they are not exceptions, there is no stack trace, and they were been detected as the same error by our fingerprinting algo. This changes introduces an fix that adds the worker module as the only stacktrace of the occurrence, so the error can populate the `source_function` attribute and different workers generate different error groupings. We cannot generate different error groups per different outputs of a worker. In that case, you should use `raise`, that generates a full stack trace and works as expected. Closes #87
1 parent ac839f5 commit c0c94fa

File tree

4 files changed

+24
-5
lines changed

4 files changed

+24
-5
lines changed

lib/error_tracker/integrations/oban.ex

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,20 @@ defmodule ErrorTracker.Integrations.Oban do
1212
It works using Oban's Telemetry events, so you don't need to modify anything
1313
on your application.
1414
15+
> #### A note on errors grouping {: .warning}
16+
>
17+
> All errors reported using `:error` or `{:error, any()}` as the output of
18+
> your `perform/2` worker function are going to be grouped together (one group
19+
> of those of errors per worker).
20+
>
21+
> The reason of that behaviour is that those errors do not generate an exception,
22+
> so no stack trace is detected and they are stored as happening in the same
23+
> place.
24+
>
25+
> If you want errors of your workers to be grouped as you may expect on other
26+
> integrations, you should raise exceptions to report errors instead of gracefully
27+
> returning an error value.
28+
1529
### Default context
1630
1731
By default we store some context for you on errors generated in an Oban
@@ -58,9 +72,14 @@ defmodule ErrorTracker.Integrations.Oban do
5872
end
5973

6074
def handle_event([:oban, :job, :exception], _measurements, metadata, :no_config) do
61-
%{reason: exception, stacktrace: stacktrace} = metadata
75+
%{reason: exception, stacktrace: stacktrace, job: job} = metadata
6276
state = Map.get(metadata, :state, :failure)
6377

78+
stacktrace =
79+
if stacktrace == [],
80+
do: [{String.to_existing_atom("Elixir." <> job.worker), :perform, 2, []}],
81+
else: stacktrace
82+
6483
ErrorTracker.report(exception, stacktrace, %{state: state})
6584
end
6685
end

lib/error_tracker/schemas/error.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ defmodule ErrorTracker.Error do
3232

3333
{source_line, source_function} =
3434
if source do
35-
source_line = if source.line, do: "#{source.file}:#{source.line}", else: "nofile"
35+
source_line = if source.line, do: "#{source.file}:#{source.line}", else: "(nofile)"
3636
source_function = "#{source.module}.#{source.function}/#{source.arity}"
3737

3838
{source_line, source_function}

lib/error_tracker/web/live/show.html.heex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
<td class="px-2 align-top"><pre>(<%= line.application || @app %>)</pre></td>
4949
<td>
5050
<pre><%= "#{sanitize_module(line.module)}.#{line.function}/#{line.arity}" %>
51-
<%= if line.line, do: "#{line.file}:#{line.line}", else: "nofile" %></pre>
51+
<%= if line.line, do: "#{line.file}:#{line.line}", else: "(nofile)" %></pre>
5252
</td>
5353
</tr>
5454
</tbody>

test/error_tracker_test.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ defmodule ErrorTrackerTest do
4040
assert error.source_line =~ @relative_file_path
4141
else
4242
assert error.source_function == "erlang.+/2"
43-
assert error.source_line == "nofile"
43+
assert error.source_line == "(nofile)"
4444
end
4545
end
4646

@@ -54,7 +54,7 @@ defmodule ErrorTrackerTest do
5454
assert error.kind == to_string(UndefinedFunctionError)
5555
assert error.reason =~ "is undefined or private"
5656
assert error.source_function == Exception.format_mfa(m, f, Enum.count(a))
57-
assert error.source_line == "nofile"
57+
assert error.source_line == "(nofile)"
5858
end
5959

6060
test "reports throws" do

0 commit comments

Comments
 (0)