Skip to content

Commit 5f5d814

Browse files
authored
Mute error occurrences (#130)
Errors can now be muted from the UI. This information is included in the new occurrence telemetry events, so integrations can choose to ignore muted errors and avoid sending notifications about them.
1 parent 0849bd3 commit 5f5d814

File tree

19 files changed

+290
-55
lines changed

19 files changed

+290
-55
lines changed

dev.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#
55
Mix.install([
66
{:ecto_sqlite3, ">= 0.0.0"},
7-
{:error_tracker, path: "."},
7+
{:error_tracker, path: ".", force: true},
88
{:phoenix_playground, "~> 0.1.7"}
99
])
1010

guides/Getting Started.md

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Open the generated migration and call the `up` and `down` functions on `ErrorTra
5656
defmodule MyApp.Repo.Migrations.AddErrorTracker do
5757
use Ecto.Migration
5858

59-
def up, do: ErrorTracker.Migration.up(version: 4)
59+
def up, do: ErrorTracker.Migration.up(version: 5)
6060

6161
# We specify `version: 1` in `down`, to ensure we remove all migrations.
6262
def down, do: ErrorTracker.Migration.down(version: 1)
@@ -152,9 +152,27 @@ environments where you may want to prune old errors that have been resolved.
152152
The `ErrorTracker.Plugins.Pruner` module provides automatic pruning functionality with a configurable
153153
interval and error age.
154154

155-
## Ignoring errors
155+
## Ignoring and Muting Errors
156+
157+
ErrorTracker provides two different ways to silence errors:
158+
159+
### Ignoring Errors
156160

157161
ErrorTracker tracks every error by default. In certain cases some errors may be expected or just not interesting to track.
158-
ErrorTracker provides functionality that allows you to ignore errors based on their attributes and context.
162+
The `ErrorTracker.Ignorer` behaviour allows you to ignore errors based on their attributes and context.
163+
164+
When an error is ignored, its occurrences are not tracked at all. This is useful for expected errors that you don't want to store in your database.
165+
166+
### Muting Errors
167+
168+
Sometimes you may want to keep tracking error occurrences but avoid receiving notifications about them. For these cases,
169+
ErrorTracker allows you to mute specific errors.
170+
171+
When an error is muted:
172+
- New occurrences are still tracked and stored in the database
173+
- You can still see the error and its occurrences in the web UI
174+
- [Telemetry events](ErrorTracker.Telemetry.html) for new occurrences include the `muted: true` flag so you can ignore them as needed.
175+
176+
This is particularly useful for noisy errors that you want to keep tracking but don't want to receive notifications about.
159177

160-
Take a look at the `ErrorTracker.Ignorer` behaviour for more information about how to implement your own ignorer.
178+
You can mute and unmute errors manually through the web UI or programmatically using the `ErrorTracker.mute/1` and `ErrorTracker.unmute/1` functions.

lib/error_tracker.ex

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,7 @@ defmodule ErrorTracker do
141141
if enabled?() && !ignored?(error, context) do
142142
sanitized_context = sanitize_context(context)
143143

144-
{_error, occurrence} =
145-
upsert_error!(error, stacktrace, sanitized_context, breadcrumbs, reason)
146-
147-
occurrence
144+
upsert_error!(error, stacktrace, sanitized_context, breadcrumbs, reason)
148145
else
149146
:noop
150147
end
@@ -179,6 +176,37 @@ defmodule ErrorTracker do
179176
end
180177
end
181178

179+
@doc """
180+
Mutes the error so new occurrences won't send telemetry events.
181+
182+
When an error is muted:
183+
- New occurrences are still tracked and stored in the database
184+
- No telemetry events are emitted for new occurrences
185+
- You can still see the error and its occurrences in the web UI
186+
187+
This is useful for noisy errors that you want to keep tracking but don't want to
188+
receive notifications about.
189+
"""
190+
@spec mute(Error.t()) :: {:ok, Error.t()} | {:error, Ecto.Changeset.t()}
191+
def mute(error = %Error{}) do
192+
changeset = Ecto.Changeset.change(error, muted: true)
193+
194+
Repo.update(changeset)
195+
end
196+
197+
@doc """
198+
Unmutes the error so new occurrences will send telemetry events again.
199+
200+
This reverses the effect of `mute/1`, allowing telemetry events to be emitted
201+
for new occurrences of this error again.
202+
"""
203+
@spec unmute(Error.t()) :: {:ok, Error.t()} | {:error, Ecto.Changeset.t()}
204+
def unmute(error = %Error{}) do
205+
changeset = Ecto.Changeset.change(error, muted: false)
206+
207+
Repo.update(changeset)
208+
end
209+
182210
@doc """
183211
Sets the current process context.
184212
@@ -300,8 +328,16 @@ defmodule ErrorTracker do
300328
end
301329

302330
defp upsert_error!(error, stacktrace, context, breadcrumbs, reason) do
303-
existing_status =
304-
Repo.one(from e in Error, where: [fingerprint: ^error.fingerprint], select: e.status)
331+
status_and_muted_query =
332+
from e in Error,
333+
where: [fingerprint: ^error.fingerprint],
334+
select: {e.status, e.muted}
335+
336+
{existing_status, muted} =
337+
case Repo.one(status_and_muted_query) do
338+
{existing_status, muted} -> {existing_status, muted}
339+
nil -> {nil, false}
340+
end
305341

306342
{:ok, {error, occurrence}} =
307343
Repo.transaction(fn ->
@@ -333,6 +369,8 @@ defmodule ErrorTracker do
333369
{error, occurrence}
334370
end)
335371

372+
occurrence = %Occurrence{occurrence | error: error}
373+
336374
# If the error existed and was marked as resolved before this exception,
337375
# sent a Telemetry event
338376
# If it is a new error, sent a Telemetry event
@@ -342,9 +380,7 @@ defmodule ErrorTracker do
342380
nil -> Telemetry.new_error(error)
343381
end
344382

345-
# Always send a new occurrence Telemetry event
346-
Telemetry.new_occurrence(occurrence)
347-
348-
{error, occurrence}
383+
Telemetry.new_occurrence(occurrence, muted)
384+
occurrence
349385
end
350386
end

lib/error_tracker/ignorer.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ defmodule ErrorTracker.Ignorer do
22
@moduledoc """
33
Behaviour for ignoring errors.
44
5+
> #### Ignoring vs muting errors {: .info}
6+
>
7+
> Ignoring an error keeps it from being tracked by the ErrorTracker. While this may be useful in
8+
> certain cases, in other cases you may prefer to track the error but don't send telemetry events.
9+
> Take a look at the `ErrorTracker.mute/1` function to see how to mute errors.
10+
511
The ErrorTracker tracks every error that happens in your application. In certain cases you may
612
want to ignore some errors and don't track them. To do so you can implement this behaviour.
713

lib/error_tracker/migration/mysql.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ defmodule ErrorTracker.Migration.MySQL do
77
alias ErrorTracker.Migration.SQLMigrator
88

99
@initial_version 3
10-
@current_version 4
10+
@current_version 5
1111

1212
@impl ErrorTracker.Migration
1313
def up(opts) do
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
defmodule ErrorTracker.Migration.MySQL.V05 do
2+
@moduledoc false
3+
4+
use Ecto.Migration
5+
6+
def up(_opts) do
7+
alter table(:error_tracker_errors) do
8+
add :muted, :boolean, default: false, null: false
9+
end
10+
end
11+
12+
def down(_opts) do
13+
alter table(:error_tracker_errors) do
14+
remove :muted
15+
end
16+
end
17+
end

lib/error_tracker/migration/postgres.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ defmodule ErrorTracker.Migration.Postgres do
77
alias ErrorTracker.Migration.SQLMigrator
88

99
@initial_version 1
10-
@current_version 4
10+
@current_version 5
1111
@default_prefix "public"
1212

1313
@impl ErrorTracker.Migration
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
defmodule ErrorTracker.Migration.Postgres.V05 do
2+
@moduledoc false
3+
4+
use Ecto.Migration
5+
6+
def up(%{prefix: prefix}) do
7+
alter table(:error_tracker_errors, prefix: prefix) do
8+
add :muted, :boolean, default: false, null: false
9+
end
10+
end
11+
12+
def down(%{prefix: prefix}) do
13+
alter table(:error_tracker_errors, prefix: prefix) do
14+
remove :muted
15+
end
16+
end
17+
end

lib/error_tracker/migration/sqlite.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ defmodule ErrorTracker.Migration.SQLite do
77
alias ErrorTracker.Migration.SQLMigrator
88

99
@initial_version 2
10-
@current_version 4
10+
@current_version 5
1111

1212
@impl ErrorTracker.Migration
1313
def up(opts) do
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
defmodule ErrorTracker.Migration.SQLite.V05 do
2+
@moduledoc false
3+
4+
use Ecto.Migration
5+
6+
def up(_opts) do
7+
alter table(:error_tracker_errors) do
8+
add :muted, :boolean, default: false, null: false
9+
end
10+
end
11+
12+
def down(_opts) do
13+
alter table(:error_tracker_errors) do
14+
remove :muted
15+
end
16+
end
17+
end

0 commit comments

Comments
 (0)