Skip to content

Commit e6b97ae

Browse files
authored
feat: Include occurrence in error telemetry events (#173)
Add the triggering `%Occurrence{}` struct to the metadata of `[:error_tracker, :error, :new]` and `[:error_tracker, :error, :unresolved]` events. Manual unresolves (via the UI) emit `occurrence: nil` since no occurrence is involved.
1 parent d8d7b6c commit e6b97ae

3 files changed

Lines changed: 45 additions & 14 deletions

File tree

lib/error_tracker.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -381,9 +381,9 @@ defmodule ErrorTracker do
381381
# sent a Telemetry event
382382
# If it is a new error, sent a Telemetry event
383383
case existing_status do
384-
:resolved -> Telemetry.unresolved_error(error)
384+
:resolved -> Telemetry.previously_resolved_error(error, occurrence)
385385
:unresolved -> :noop
386-
nil -> Telemetry.new_error(error)
386+
nil -> Telemetry.new_error(error, occurrence)
387387
end
388388

389389
Telemetry.new_occurrence(occurrence, muted)

lib/error_tracker/telemetry.ex

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,31 +31,41 @@ defmodule ErrorTracker.Telemetry do
3131
Each event is emitted with some measures and metadata, which can be used to
3232
receive information without having to query the database again:
3333
34-
| event | measures | metadata |
35-
| --------------------------------------- | -------------- | ----------------------------------|
36-
| `[:error_tracker, :error, :new]` | `:system_time` | `:error` |
37-
| `[:error_tracker, :error, :unresolved]` | `:system_time` | `:error` |
38-
| `[:error_tracker, :error, :resolved]` | `:system_time` | `:error` |
39-
| `[:error_tracker, :occurrence, :new]` | `:system_time` | `:occurrence`, `:error`, `:muted` |
34+
| event | measures | metadata |
35+
| --------------------------------------- | -------------- | ----------------------------------------------- |
36+
| `[:error_tracker, :error, :new]` | `:system_time` | `:error`, `:occurrence` |
37+
| `[:error_tracker, :error, :unresolved]` | `:system_time` | `:error`, `:occurrence` (nullable) |
38+
| `[:error_tracker, :error, :resolved]` | `:system_time` | `:error` |
39+
| `[:error_tracker, :occurrence, :new]` | `:system_time` | `:occurrence`, `:error`, `:muted` |
4040
4141
The metadata keys contain the following data:
4242
4343
* `:error` - An `%ErrorTracker.Error{}` struct representing the error.
4444
* `:occurrence` - An `%ErrorTracker.Occurrence{}` struct representing the occurrence.
45+
For `:new` error events this is the first occurrence. For `:unresolved` events this
46+
is the occurrence that triggered the state change, or `nil` when the error was manually
47+
unresolved from the UI.
4548
* `:muted` - A boolean indicating whether the error is muted or not.
4649
"""
4750

4851
@doc false
49-
def new_error(%ErrorTracker.Error{} = error) do
52+
def new_error(%ErrorTracker.Error{} = error, %ErrorTracker.Occurrence{} = occurrence) do
5053
measurements = %{system_time: System.system_time()}
51-
metadata = %{error: error}
54+
metadata = %{error: error, occurrence: occurrence}
5255
:telemetry.execute([:error_tracker, :error, :new], measurements, metadata)
5356
end
5457

5558
@doc false
5659
def unresolved_error(%ErrorTracker.Error{} = error) do
5760
measurements = %{system_time: System.system_time()}
58-
metadata = %{error: error}
61+
metadata = %{error: error, occurrence: nil}
62+
:telemetry.execute([:error_tracker, :error, :unresolved], measurements, metadata)
63+
end
64+
65+
@doc false
66+
def previously_resolved_error(%ErrorTracker.Error{} = error, %ErrorTracker.Occurrence{} = occurrence) do
67+
measurements = %{system_time: System.system_time()}
68+
metadata = %{error: error, occurrence: occurrence}
5969
:telemetry.execute([:error_tracker, :error, :unresolved], measurements, metadata)
6070
end
6171

test/error_tracker/telemetry_test.exs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ defmodule ErrorTracker.TelemetryTest do
1919
end
2020

2121
# Since the error is new, both the new error and new occurrence events will be emitted
22-
%Occurrence{error: error = %Error{}} = ErrorTracker.report(exception, stacktrace)
23-
assert_receive {:telemetry_event, [:error_tracker, :error, :new], _, %{error: %Error{}}}
22+
occurrence = %Occurrence{error: error = %Error{}} = ErrorTracker.report(exception, stacktrace)
23+
assert_receive {:telemetry_event, [:error_tracker, :error, :new], _, %{error: %Error{}, occurrence: ^occurrence}}
2424

2525
assert_receive {:telemetry_event, [:error_tracker, :occurrence, :new], _, %{occurrence: %Occurrence{}, muted: false}}
2626

@@ -49,7 +49,28 @@ defmodule ErrorTracker.TelemetryTest do
4949
# The unresolved event will be emitted
5050
{:ok, _unresolved} = ErrorTracker.unresolve(resolved)
5151

52-
assert_receive {:telemetry_event, [:error_tracker, :error, :unresolved], _, %{error: %Error{}}}
52+
assert_receive {:telemetry_event, [:error_tracker, :error, :unresolved], _, %{error: %Error{}, occurrence: nil}}
53+
end
54+
55+
test "events are emitted for previously resolved errors" do
56+
{exception, stacktrace} =
57+
try do
58+
raise "This error was resolved but came back"
59+
rescue
60+
e -> {e, __STACKTRACE__}
61+
end
62+
63+
%Occurrence{error: error = %Error{}} = ErrorTracker.report(exception, stacktrace)
64+
65+
ErrorTracker.resolve(error)
66+
67+
occurrence = ErrorTracker.report(exception, stacktrace)
68+
69+
assert_receive {:telemetry_event, [:error_tracker, :error, :unresolved], _,
70+
%{
71+
error: %Error{reason: "This error was resolved but came back"},
72+
occurrence: ^occurrence
73+
}}
5374
end
5475

5576
test "the :unresolved event carries the muted flag for muted errors" do

0 commit comments

Comments
 (0)