@@ -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
350386end
0 commit comments