From 84012cbd1ca4f987265e81e7b3bea9dfb5945212 Mon Sep 17 00:00:00 2001 From: Claudio Ortolina Date: Sun, 3 May 2026 14:46:40 +0100 Subject: [PATCH] Update to error_tracker 0.9 Includes amendments resulting from new events, and proper handling of the muted attribute --- lib/error_tracker/error_notifier.ex | 25 ++++++- mix.exs | 2 +- mix.lock | 2 +- test/error_tracker/error_notifier_test.exs | 76 +++++++++++++++++++++- 4 files changed, 99 insertions(+), 6 deletions(-) diff --git a/lib/error_tracker/error_notifier.ex b/lib/error_tracker/error_notifier.ex index 9de2f4fa..ee7c7998 100644 --- a/lib/error_tracker/error_notifier.ex +++ b/lib/error_tracker/error_notifier.ex @@ -30,10 +30,32 @@ defmodule ErrorTracker.ErrorNotifier do reason = truncate_reason(metadata.occurrence.reason) {_result, new_state} = - maybe_notify(metadata.occurrence, false, "New Error! (#{reason})", state) + maybe_notify( + metadata.occurrence, + metadata.error.muted, + "New Error! (#{reason})", + state + ) {:noreply, new_state} + [:error_tracker, :error, :unresolved] -> + if metadata.occurrence do + reason = truncate_reason(metadata.occurrence.reason) + + {_result, new_state} = + maybe_notify( + metadata.occurrence, + metadata.error.muted, + "Regression! (#{reason})", + state + ) + + {:noreply, new_state} + else + {:noreply, state} + end + [:error_tracker, :occurrence, :new] -> reason = truncate_reason(metadata.occurrence.reason) @@ -67,6 +89,7 @@ defmodule ErrorTracker.ErrorNotifier do defp attach_telemetry do events = [ [:error_tracker, :error, :new], + [:error_tracker, :error, :unresolved], [:error_tracker, :occurrence, :new] ] diff --git a/mix.exs b/mix.exs index 3b93bd52..90c0757b 100644 --- a/mix.exs +++ b/mix.exs @@ -171,7 +171,7 @@ defmodule MusicLibrary.MixProject do {:lazy_html, ">= 0.1.0", only: :test}, # Prod error/perf tooling - {:error_tracker, "~> 0.7"}, + {:error_tracker, "~> 0.9"}, {:phoenix_live_dashboard, "~> 0.8.3"}, {:recon, "~> 2.5"}, {:telemetry_metrics, "~> 1.0"}, diff --git a/mix.lock b/mix.lock index cc6b0cd7..2c46da07 100644 --- a/mix.lock +++ b/mix.lock @@ -19,7 +19,7 @@ "ecto_sqlite3": {:hex, :ecto_sqlite3, "0.22.0", "edab2d0f701b7dd05dcf7e2d97769c106aff62b5cfddc000d1dd6f46b9cbd8c3", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.13.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:exqlite, "~> 0.22", [hex: :exqlite, repo: "hexpm", optional: false]}], "hexpm", "5af9e031bffcc5da0b7bca90c271a7b1e7c04a93fecf7f6cd35bc1b1921a64bd"}, "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, "erlex": {:hex, :erlex, "0.2.8", "cd8116f20f3c0afe376d1e8d1f0ae2452337729f68be016ea544a72f767d9c12", [:mix], [], "hexpm", "9d66ff9fedf69e49dc3fd12831e12a8a37b76f8651dd21cd45fcf5561a8a7590"}, - "error_tracker": {:hex, :error_tracker, "0.8.0", "15774836205c1b4cd8275b594d99c10f4de168eb00573743526b6a919f2aca4a", [:mix], [{:ecto, "~> 3.13", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.13", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, ">= 0.0.0", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:igniter, "~> 0.5", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:myxql, ">= 0.0.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:phoenix_ecto, "~> 4.6", [hex: :phoenix_ecto, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "00ab8342932de7ad63be567ddd36ea238c840c122c7b2c5d1a42638ce6f39220"}, + "error_tracker": {:hex, :error_tracker, "0.9.0", "fef10b1230be1b051452930ba4db9f809cdab9705fe87891c25f5e551cead372", [:mix], [{:ecto, "~> 3.13", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.13", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, ">= 0.0.0", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:igniter, "~> 0.5", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:myxql, ">= 0.0.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:phoenix_ecto, "~> 4.6", [hex: :phoenix_ecto, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "1de7d89ec9034c3b7282b6bd2d0584ca98cddc51e87bf0979ba71f6182803ac4"}, "esbuild": {:hex, :esbuild, "0.10.0", "b0aa3388a1c23e727c5a3e7427c932d89ee791746b0081bbe56103e9ef3d291f", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "468489cda427b974a7cc9f03ace55368a83e1a7be12fba7e30969af78e5f8c70"}, "ex_slop": {:hex, :ex_slop, "0.4.0", "06c39628e2a278a9adeaf76047f7b98002a453b53a38b48faa3921835675c680", [:mix], [{:credo, "~> 1.7", [hex: :credo, repo: "hexpm", optional: false]}], "hexpm", "563da973e0251ebd69785a21873ea566158c95b123a5dccf075c0c687e4acc2e"}, "expo": {:hex, :expo, "1.1.1", "4202e1d2ca6e2b3b63e02f69cfe0a404f77702b041d02b58597c00992b601db5", [:mix], [], "hexpm", "5fb308b9cb359ae200b7e23d37c76978673aa1b06e2b3075d814ce12c5811640"}, diff --git a/test/error_tracker/error_notifier_test.exs b/test/error_tracker/error_notifier_test.exs index 47952f60..18169fed 100644 --- a/test/error_tracker/error_notifier_test.exs +++ b/test/error_tracker/error_notifier_test.exs @@ -119,7 +119,7 @@ defmodule ErrorTracker.ErrorNotifierTest do SwooshSandbox.allow(self(), pid) :telemetry.execute([:error_tracker, :error, :new], %{}, %{ - error: %ErrorTracker.Error{id: 1}, + error: %ErrorTracker.Error{id: 1, muted: false}, occurrence: occurrence(%{error_id: 1}) }) @@ -148,7 +148,7 @@ defmodule ErrorTracker.ErrorNotifierTest do SwooshSandbox.allow(self(), pid) :telemetry.execute([:error_tracker, :error, :new], %{}, %{ - error: %Error{id: 1}, + error: %Error{id: 1, muted: false}, occurrence: occurrence(%{error_id: 1}) }) @@ -176,7 +176,7 @@ defmodule ErrorTracker.ErrorNotifierTest do SwooshSandbox.allow(self(), pid) :telemetry.execute([:error_tracker, :error, :new], %{}, %{ - error: %Error{id: 99}, + error: %Error{id: 99, muted: false}, occurrence: occurrence(%{error_id: 99}) }) @@ -185,5 +185,75 @@ defmodule ErrorTracker.ErrorNotifierTest do GenServer.stop(pid) end + + test "sends regression email on :error_tracker :error :unresolved event" do + Application.put_env(:music_library, ErrorNotifier, @config) + + {:ok, pid} = ErrorNotifier.start_link([]) + SwooshSandbox.allow(self(), pid) + + :telemetry.execute([:error_tracker, :error, :unresolved], %{}, %{ + error: %Error{id: 1, muted: false}, + occurrence: occurrence(%{error_id: 1}) + }) + + :sys.get_state(ErrorNotifier) + assert_email_sent(subject: ~r/MusicLibrary/) + + GenServer.stop(pid) + end + + test "skips when unresolved has nil occurrence (manual UI unresolve)" do + Application.put_env(:music_library, ErrorNotifier, @config) + + {:ok, pid} = ErrorNotifier.start_link([]) + SwooshSandbox.allow(self(), pid) + + :telemetry.execute([:error_tracker, :error, :unresolved], %{}, %{ + error: %Error{id: 1, muted: false}, + occurrence: nil + }) + + :sys.get_state(ErrorNotifier) + refute_email_sent() + + GenServer.stop(pid) + end + + test "skips regression email when error is muted" do + Application.put_env(:music_library, ErrorNotifier, @config) + + {:ok, pid} = ErrorNotifier.start_link([]) + SwooshSandbox.allow(self(), pid) + + :telemetry.execute([:error_tracker, :error, :unresolved], %{}, %{ + error: %Error{id: 1, muted: true}, + occurrence: occurrence(%{error_id: 1}) + }) + + :sys.get_state(ErrorNotifier) + refute_email_sent() + + GenServer.stop(pid) + end + + test "skips notification for :new event when error struct is muted" do + Application.put_env(:music_library, ErrorNotifier, @config) + + {:ok, pid} = ErrorNotifier.start_link([]) + SwooshSandbox.allow(self(), pid) + + # Defensive: new errors are never muted in practice (PR #174), + # but the handler reads metadata.error.muted so we verify it is respected. + :telemetry.execute([:error_tracker, :error, :new], %{}, %{ + error: %Error{id: 3, muted: true}, + occurrence: occurrence(%{error_id: 3}) + }) + + :sys.get_state(ErrorNotifier) + refute_email_sent() + + GenServer.stop(pid) + end end end