From ebe0ccdf756e43dca48125b2786498158d12eccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sat, 9 Nov 2024 12:56:31 +0100 Subject: [PATCH 01/17] Add bread_crumbs field --- lib/error_tracker/migration/mysql.ex | 2 +- lib/error_tracker/migration/mysql/v04.ex | 11 +++++++++++ lib/error_tracker/migration/postgres.ex | 2 +- lib/error_tracker/migration/postgres/v04.ex | 11 +++++++++++ lib/error_tracker/migration/sqlite.ex | 2 +- lib/error_tracker/migration/sqlite/v04.ex | 11 +++++++++++ lib/error_tracker/schemas/occurrence.ex | 6 ++++-- 7 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 lib/error_tracker/migration/mysql/v04.ex create mode 100644 lib/error_tracker/migration/postgres/v04.ex create mode 100644 lib/error_tracker/migration/sqlite/v04.ex diff --git a/lib/error_tracker/migration/mysql.ex b/lib/error_tracker/migration/mysql.ex index de38c01..0c28986 100644 --- a/lib/error_tracker/migration/mysql.ex +++ b/lib/error_tracker/migration/mysql.ex @@ -7,7 +7,7 @@ defmodule ErrorTracker.Migration.MySQL do alias ErrorTracker.Migration.SQLMigrator @initial_version 3 - @current_version 3 + @current_version 4 @impl ErrorTracker.Migration def up(opts) do diff --git a/lib/error_tracker/migration/mysql/v04.ex b/lib/error_tracker/migration/mysql/v04.ex new file mode 100644 index 0000000..dc3f689 --- /dev/null +++ b/lib/error_tracker/migration/mysql/v04.ex @@ -0,0 +1,11 @@ +defmodule ErrorTracker.Migration.MySQL.V04 do + @moduledoc false + + use Ecto.Migration + + def change(_opts) do + alter table(:error_tracker_occurrences) do + add :bread_crumbs, {:array, :string}, default: [], null: false + end + end +end diff --git a/lib/error_tracker/migration/postgres.ex b/lib/error_tracker/migration/postgres.ex index 54af369..ccb7014 100644 --- a/lib/error_tracker/migration/postgres.ex +++ b/lib/error_tracker/migration/postgres.ex @@ -7,7 +7,7 @@ defmodule ErrorTracker.Migration.Postgres do alias ErrorTracker.Migration.SQLMigrator @initial_version 1 - @current_version 3 + @current_version 4 @default_prefix "public" @impl ErrorTracker.Migration diff --git a/lib/error_tracker/migration/postgres/v04.ex b/lib/error_tracker/migration/postgres/v04.ex new file mode 100644 index 0000000..f057538 --- /dev/null +++ b/lib/error_tracker/migration/postgres/v04.ex @@ -0,0 +1,11 @@ +defmodule ErrorTracker.Migration.Postgres.V04 do + @moduledoc false + + use Ecto.Migration + + def change(_opts) do + alter table(:error_tracker_occurrences) do + add :bread_crumbs, {:array, :string}, default: [], null: false + end + end +end diff --git a/lib/error_tracker/migration/sqlite.ex b/lib/error_tracker/migration/sqlite.ex index 04fd7a7..383446a 100644 --- a/lib/error_tracker/migration/sqlite.ex +++ b/lib/error_tracker/migration/sqlite.ex @@ -7,7 +7,7 @@ defmodule ErrorTracker.Migration.SQLite do alias ErrorTracker.Migration.SQLMigrator @initial_version 2 - @current_version 3 + @current_version 4 @impl ErrorTracker.Migration def up(opts) do diff --git a/lib/error_tracker/migration/sqlite/v04.ex b/lib/error_tracker/migration/sqlite/v04.ex new file mode 100644 index 0000000..5e748d6 --- /dev/null +++ b/lib/error_tracker/migration/sqlite/v04.ex @@ -0,0 +1,11 @@ +defmodule ErrorTracker.Migration.SQLite.V04 do + @moduledoc false + + use Ecto.Migration + + def change(_opts) do + alter table(:error_tracker_occurrences) do + add :bread_crumbs, {:array, :string}, default: [], null: false + end + end +end diff --git a/lib/error_tracker/schemas/occurrence.ex b/lib/error_tracker/schemas/occurrence.ex index 226c284..f943f82 100644 --- a/lib/error_tracker/schemas/occurrence.ex +++ b/lib/error_tracker/schemas/occurrence.ex @@ -15,9 +15,11 @@ defmodule ErrorTracker.Occurrence do @type t :: %__MODULE__{} schema "error_tracker_occurrences" do - field :context, :map field :reason, :string + field :context, :map + field :bread_crumbs, {:array, :string} + embeds_one :stacktrace, ErrorTracker.Stacktrace belongs_to :error, ErrorTracker.Error @@ -27,7 +29,7 @@ defmodule ErrorTracker.Occurrence do @doc false def changeset(occurrence, attrs) do occurrence - |> cast(attrs, [:context, :reason]) + |> cast(attrs, [:context, :reason, :bread_crumbs]) |> maybe_put_stacktrace() |> validate_required([:reason, :stacktrace]) |> validate_context() From ef62b65acc636ab870758b8698ad3023e29b06a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 10 Nov 2024 12:28:23 +0100 Subject: [PATCH 02/17] Fix migrations --- lib/error_tracker/migration/mysql/v04.ex | 8 +++++++- lib/error_tracker/migration/postgres/v04.ex | 8 +++++++- lib/error_tracker/migration/sqlite/v04.ex | 8 +++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/error_tracker/migration/mysql/v04.ex b/lib/error_tracker/migration/mysql/v04.ex index dc3f689..992e60e 100644 --- a/lib/error_tracker/migration/mysql/v04.ex +++ b/lib/error_tracker/migration/mysql/v04.ex @@ -3,9 +3,15 @@ defmodule ErrorTracker.Migration.MySQL.V04 do use Ecto.Migration - def change(_opts) do + def up(_opts) do alter table(:error_tracker_occurrences) do add :bread_crumbs, {:array, :string}, default: [], null: false end end + + def down(_opts) do + alter table(:error_tracker_occurrences) do + remove :bread_crumbs + end + end end diff --git a/lib/error_tracker/migration/postgres/v04.ex b/lib/error_tracker/migration/postgres/v04.ex index f057538..9b6e3fa 100644 --- a/lib/error_tracker/migration/postgres/v04.ex +++ b/lib/error_tracker/migration/postgres/v04.ex @@ -3,9 +3,15 @@ defmodule ErrorTracker.Migration.Postgres.V04 do use Ecto.Migration - def change(_opts) do + def up(_opts) do alter table(:error_tracker_occurrences) do add :bread_crumbs, {:array, :string}, default: [], null: false end end + + def down(_opts) do + alter table(:error_tracker_occurrences) do + remove :bread_crumbs + end + end end diff --git a/lib/error_tracker/migration/sqlite/v04.ex b/lib/error_tracker/migration/sqlite/v04.ex index 5e748d6..b4d974d 100644 --- a/lib/error_tracker/migration/sqlite/v04.ex +++ b/lib/error_tracker/migration/sqlite/v04.ex @@ -3,9 +3,15 @@ defmodule ErrorTracker.Migration.SQLite.V04 do use Ecto.Migration - def change(_opts) do + def up(_opts) do alter table(:error_tracker_occurrences) do add :bread_crumbs, {:array, :string}, default: [], null: false end end + + def down(_opts) do + alter table(:error_tracker_occurrences) do + remove :bread_crumbs + end + end end From a9d12e5aa86483a7a45fd195c0b94ead784995d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 10 Nov 2024 13:30:10 +0100 Subject: [PATCH 03/17] Fix MySQL migration --- lib/error_tracker/migration/mysql/v04.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/error_tracker/migration/mysql/v04.ex b/lib/error_tracker/migration/mysql/v04.ex index 992e60e..4b58964 100644 --- a/lib/error_tracker/migration/mysql/v04.ex +++ b/lib/error_tracker/migration/mysql/v04.ex @@ -5,7 +5,7 @@ defmodule ErrorTracker.Migration.MySQL.V04 do def up(_opts) do alter table(:error_tracker_occurrences) do - add :bread_crumbs, {:array, :string}, default: [], null: false + add :bread_crumbs, :json, null: true end end From 08ee5855afcece0602a1b3774a978c0c9bb6eb04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 10 Nov 2024 13:30:41 +0100 Subject: [PATCH 04/17] Store breadcrumbs and supporting functions. --- lib/error_tracker.ex | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/lib/error_tracker.ex b/lib/error_tracker.ex index 0c5d09f..11a3a84 100644 --- a/lib/error_tracker.ex +++ b/lib/error_tracker.ex @@ -119,15 +119,14 @@ defmodule ErrorTracker do {:ok, stacktrace} = ErrorTracker.Stacktrace.new(stacktrace) {:ok, error} = Error.new(kind, reason, stacktrace) context = Map.merge(get_context(), given_context) - - context = - if bread_crumbs = bread_crumbs(exception), - do: Map.put(context, "bread_crumbs", bread_crumbs), - else: context + bread_crumbs = exception_bread_crumbs(exception) ++ get_bread_crumbs() if enabled?() && !ignored?(error, context) do sanitized_context = sanitize_context(context) - {_error, occurrence} = upsert_error!(error, stacktrace, sanitized_context, reason) + + {_error, occurrence} = + upsert_error!(error, stacktrace, sanitized_context, bread_crumbs, reason) + occurrence else :noop @@ -205,6 +204,24 @@ defmodule ErrorTracker do Process.get(:error_tracker_context, %{}) end + @spec set_context(String.t()) :: list(String.t()) + def add_bread_crumb(bread_crumb) when is_binary(bread_crumb) do + current_bread_crumbs = Process.get(:error_tracker_bread_crumbs, []) + new_bread_crumbs = current_bread_crumbs ++ [bread_crumb] + + Process.put(:error_tracker_bread_crumbs, new_bread_crumbs) + + new_bread_crumbs + end + + @doc """ + Obtain the context of the current process. + """ + @spec get_bread_crumbs() :: list(String.t()) + def get_bread_crumbs do + Process.get(:error_tracker_bread_crumbs, []) + end + defp enabled? do !!Application.get_env(:error_tracker, :enabled, true) end @@ -237,15 +254,15 @@ defmodule ErrorTracker do end end - defp bread_crumbs(exception) do + defp exception_bread_crumbs(exception) do case exception do - {_kind, exception} -> bread_crumbs(exception) - %{bread_crumbs: bread_crumbs} -> bread_crumbs - _other -> nil + {_kind, exception} -> exception_bread_crumbs(exception) + %{bread_crumbs: bread_crumbs} -> [bread_crumbs] + _other -> [] end end - defp upsert_error!(error, stacktrace, context, reason) do + defp upsert_error!(error, stacktrace, context, bread_crumbs, reason) do existing_status = Repo.one(from e in Error, where: [fingerprint: ^error.fingerprint], select: e.status) @@ -271,6 +288,7 @@ defmodule ErrorTracker do |> Occurrence.changeset(%{ stacktrace: stacktrace, context: context, + bread_crumbs: bread_crumbs, reason: reason }) |> Repo.insert!() From 6decd4494e8f5764b709dd0a97be5e3eef2dea60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 10 Nov 2024 13:31:09 +0100 Subject: [PATCH 05/17] Add breadcrumbs to dev.exs scripts. --- dev.exs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dev.exs b/dev.exs index 3d69251..2fb8011 100644 --- a/dev.exs +++ b/dev.exs @@ -71,14 +71,17 @@ defmodule ErrorTrackerDevWeb.PageController do end def call(conn, :noroute) do + ErrorTracker.add_bread_crumb("ErrorTrackerDevWeb.PageController.no_route") raise Phoenix.Router.NoRouteError, conn: conn, router: ErrorTrackerDevWeb.Router end def call(_conn, :exception) do + ErrorTracker.add_bread_crumb("ErrorTrackerDevWeb.PageController.exception") raise "This is a controller exception" end def call(_conn, :exit) do + ErrorTracker.add_bread_crumb("ErrorTrackerDevWeb.PageController.exit") exit(:timeout) end @@ -142,10 +145,16 @@ defmodule ErrorTrackerDevWeb.Endpoint do plug Plug.RequestId plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint] + plug :add_bread_crumb plug :maybe_exception plug :set_csp plug ErrorTrackerDevWeb.Router + def add_bread_crumb(conn, _) do + ErrorTracker.add_bread_crumb("ErrorTrackerDevWeb.Endpoint.add_bread_crumb") + conn + end + def maybe_exception(%Plug.Conn{path_info: ["plug-exception"]}, _), do: raise("Plug exception") def maybe_exception(conn, _), do: conn From fb52036ae4ace075391f5ce9d409fe901abfe549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 10 Nov 2024 13:40:04 +0100 Subject: [PATCH 06/17] Update UI to show bread crumbs --- lib/error_tracker/web/live/show.html.heex | 14 ++++++++++++++ priv/static/app.css | 16 ++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/lib/error_tracker/web/live/show.html.heex b/lib/error_tracker/web/live/show.html.heex index b14cda4..da6e94a 100644 --- a/lib/error_tracker/web/live/show.html.heex +++ b/lib/error_tracker/web/live/show.html.heex @@ -25,6 +25,20 @@ <%= @error.source_line %> + <.section :if={@occurrence.bread_crumbs != []} title="Bread crumbs"> +
+ + + + + +
<%= length(@occurrence.bread_crumbs) - index %>.<%= bread_crumb %>
+
+ + <.section :if={@occurrence.stacktrace.lines != []} title="Stacktrace">
diff --git a/priv/static/app.css b/priv/static/app.css index f2ce984..30edff5 100644 --- a/priv/static/app.css +++ b/priv/static/app.css @@ -885,6 +885,10 @@ select { width: 2.5rem; } +.w-11 { + width: 2.75rem; +} + .w-28 { width: 7rem; } @@ -1129,6 +1133,10 @@ select { padding-bottom: 11.5px; } +.pl-2 { + padding-left: 0.5rem; +} + .pr-2 { padding-right: 0.5rem; } @@ -1145,6 +1153,10 @@ select { text-align: center; } +.text-right { + text-align: right; +} + .align-top { vertical-align: top; } @@ -1318,6 +1330,10 @@ select { border-radius: 4px; } +.last\:border-b-0:last-child { + border-bottom-width: 0px; +} + .last-of-type\:border-b-0:last-of-type { border-bottom-width: 0px; } From a6edf1355fc19b4dfd4889a4e128f3ae939c577f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 10 Nov 2024 13:42:40 +0100 Subject: [PATCH 07/17] Format --- lib/error_tracker/web/live/show.html.heex | 6 +- priv/static/app.css | 70 ++++++++++++++++++++--- 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/lib/error_tracker/web/live/show.html.heex b/lib/error_tracker/web/live/show.html.heex index da6e94a..2080a1b 100644 --- a/lib/error_tracker/web/live/show.html.heex +++ b/lib/error_tracker/web/live/show.html.heex @@ -32,8 +32,10 @@ :for={{bread_crumb, index} <- Enum.with_index(@occurrence.bread_crumbs)} class="border-b bg-gray-400/10 border-gray-900 last:border-b-0" > - <%= length(@occurrence.bread_crumbs) - index %>. - <%= bread_crumb %> + + <%= length(@occurrence.bread_crumbs) - index %>. + + <%= bread_crumb %>
diff --git a/priv/static/app.css b/priv/static/app.css index 30edff5..c9fdfdd 100644 --- a/priv/static/app.css +++ b/priv/static/app.css @@ -885,10 +885,6 @@ select { width: 2.5rem; } -.w-11 { - width: 2.75rem; -} - .w-28 { width: 7rem; } @@ -909,6 +905,42 @@ select { width: 100%; } +.w-16 { + width: 4rem; +} + +.w-12 { + width: 3rem; +} + +.w-8 { + width: 2rem; +} + +.w-7 { + width: 1.75rem; +} + +.w-6 { + width: 1.5rem; +} + +.w-\[5rem\] { + width: 5rem; +} + +.w-11 { + width: 2.75rem; +} + +.min-w-12 { + min-width: 3rem; +} + +.table-auto { + table-layout: auto; +} + .table-fixed { table-layout: fixed; } @@ -1133,10 +1165,6 @@ select { padding-bottom: 11.5px; } -.pl-2 { - padding-left: 0.5rem; -} - .pr-2 { padding-right: 0.5rem; } @@ -1145,6 +1173,10 @@ select { padding-right: 1.25rem; } +.pl-2 { + padding-left: 0.5rem; +} + .text-left { text-align: left; } @@ -1330,10 +1362,32 @@ select { border-radius: 4px; } +.first\:border-y-0:first-child { + border-top-width: 0px; + border-bottom-width: 0px; +} + +.first\:border-t-0:first-child { + border-top-width: 0px; +} + .last\:border-b-0:last-child { border-bottom-width: 0px; } +.first-of-type\:border-y-0:first-of-type { + border-top-width: 0px; + border-bottom-width: 0px; +} + +.first-of-type\:border-t-0:first-of-type { + border-top-width: 0px; +} + +.first-of-type\:border-b-0:first-of-type { + border-bottom-width: 0px; +} + .last-of-type\:border-b-0:last-of-type { border-bottom-width: 0px; } From f459bb480da33e5ca9c21f90de88cfbd3cfb3794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 10 Nov 2024 13:50:16 +0100 Subject: [PATCH 08/17] Fix how Splode breadcrumbs are used --- lib/error_tracker.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/error_tracker.ex b/lib/error_tracker.ex index 11a3a84..0e16de8 100644 --- a/lib/error_tracker.ex +++ b/lib/error_tracker.ex @@ -257,7 +257,7 @@ defmodule ErrorTracker do defp exception_bread_crumbs(exception) do case exception do {_kind, exception} -> exception_bread_crumbs(exception) - %{bread_crumbs: bread_crumbs} -> [bread_crumbs] + %{bread_crumbs: bread_crumbs} -> bread_crumbs _other -> [] end end From 3954c3832b2fa5892643c966bf5b5c732e04efb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 10 Nov 2024 13:52:36 +0100 Subject: [PATCH 09/17] Update tests --- test/error_tracker_test.exs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/error_tracker_test.exs b/test/error_tracker_test.exs index aacf7b2..42f9d0e 100644 --- a/test/error_tracker_test.exs +++ b/test/error_tracker_test.exs @@ -100,7 +100,7 @@ defmodule ErrorTrackerTest do assert report_error(fn -> raise "Sample error" end) == :noop end - test "includes bread crumbs in the context if present" do + test "includes bread crumbs if present" do bread_crumbs = ["bread crumb 1", "bread crumb 2"] occurrence = @@ -108,7 +108,7 @@ defmodule ErrorTrackerTest do raise ErrorWithBreadcrumbs, message: "test", bread_crumbs: bread_crumbs end) - assert occurrence.context["bread_crumbs"] == bread_crumbs + assert occurrence.bread_crumbs == bread_crumbs end end From 69346f33ff802da34b1c34bf43e1da0b358f70dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 10 Nov 2024 14:16:08 +0100 Subject: [PATCH 10/17] Rename bread_crumbs to breadcrumbs. --- dev.exs | 12 ++++----- lib/error_tracker.ex | 30 ++++++++++----------- lib/error_tracker/migration/mysql/v04.ex | 4 +-- lib/error_tracker/migration/postgres/v04.ex | 4 +-- lib/error_tracker/migration/sqlite/v04.ex | 4 +-- lib/error_tracker/schemas/occurrence.ex | 4 +-- lib/error_tracker/web/live/show.html.heex | 8 +++--- test/error_tracker_test.exs | 6 ++--- 8 files changed, 36 insertions(+), 36 deletions(-) diff --git a/dev.exs b/dev.exs index 2fb8011..b155aff 100644 --- a/dev.exs +++ b/dev.exs @@ -71,17 +71,17 @@ defmodule ErrorTrackerDevWeb.PageController do end def call(conn, :noroute) do - ErrorTracker.add_bread_crumb("ErrorTrackerDevWeb.PageController.no_route") + ErrorTracker.add_breadcrumb("ErrorTrackerDevWeb.PageController.no_route") raise Phoenix.Router.NoRouteError, conn: conn, router: ErrorTrackerDevWeb.Router end def call(_conn, :exception) do - ErrorTracker.add_bread_crumb("ErrorTrackerDevWeb.PageController.exception") + ErrorTracker.add_breadcrumb("ErrorTrackerDevWeb.PageController.exception") raise "This is a controller exception" end def call(_conn, :exit) do - ErrorTracker.add_bread_crumb("ErrorTrackerDevWeb.PageController.exit") + ErrorTracker.add_breadcrumb("ErrorTrackerDevWeb.PageController.exit") exit(:timeout) end @@ -145,13 +145,13 @@ defmodule ErrorTrackerDevWeb.Endpoint do plug Plug.RequestId plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint] - plug :add_bread_crumb + plug :add_breadcrumb plug :maybe_exception plug :set_csp plug ErrorTrackerDevWeb.Router - def add_bread_crumb(conn, _) do - ErrorTracker.add_bread_crumb("ErrorTrackerDevWeb.Endpoint.add_bread_crumb") + def add_breadcrumb(conn, _) do + ErrorTracker.add_breadcrumb("ErrorTrackerDevWeb.Endpoint.add_breadcrumb") conn end diff --git a/lib/error_tracker.ex b/lib/error_tracker.ex index 0e16de8..cef47b7 100644 --- a/lib/error_tracker.ex +++ b/lib/error_tracker.ex @@ -119,13 +119,13 @@ defmodule ErrorTracker do {:ok, stacktrace} = ErrorTracker.Stacktrace.new(stacktrace) {:ok, error} = Error.new(kind, reason, stacktrace) context = Map.merge(get_context(), given_context) - bread_crumbs = exception_bread_crumbs(exception) ++ get_bread_crumbs() + breadcrumbs = exception_breadcrumbs(exception) ++ get_breadcrumbs() if enabled?() && !ignored?(error, context) do sanitized_context = sanitize_context(context) {_error, occurrence} = - upsert_error!(error, stacktrace, sanitized_context, bread_crumbs, reason) + upsert_error!(error, stacktrace, sanitized_context, breadcrumbs, reason) occurrence else @@ -205,21 +205,21 @@ defmodule ErrorTracker do end @spec set_context(String.t()) :: list(String.t()) - def add_bread_crumb(bread_crumb) when is_binary(bread_crumb) do - current_bread_crumbs = Process.get(:error_tracker_bread_crumbs, []) - new_bread_crumbs = current_bread_crumbs ++ [bread_crumb] + def add_breadcrumb(breadcrumb) when is_binary(breadcrumb) do + current_breadcrumbs = Process.get(:error_tracker_breadcrumbs, []) + new_breadcrumbs = current_breadcrumbs ++ [breadcrumb] - Process.put(:error_tracker_bread_crumbs, new_bread_crumbs) + Process.put(:error_tracker_breadcrumbs, new_breadcrumbs) - new_bread_crumbs + new_breadcrumbs end @doc """ Obtain the context of the current process. """ - @spec get_bread_crumbs() :: list(String.t()) - def get_bread_crumbs do - Process.get(:error_tracker_bread_crumbs, []) + @spec get_breadcrumbs() :: list(String.t()) + def get_breadcrumbs do + Process.get(:error_tracker_breadcrumbs, []) end defp enabled? do @@ -254,15 +254,15 @@ defmodule ErrorTracker do end end - defp exception_bread_crumbs(exception) do + defp exception_breadcrumbs(exception) do case exception do - {_kind, exception} -> exception_bread_crumbs(exception) - %{bread_crumbs: bread_crumbs} -> bread_crumbs + {_kind, exception} -> exception_breadcrumbs(exception) + %{bread_crumbs: breadcrumbs} -> breadcrumbs _other -> [] end end - defp upsert_error!(error, stacktrace, context, bread_crumbs, reason) do + defp upsert_error!(error, stacktrace, context, breadcrumbs, reason) do existing_status = Repo.one(from e in Error, where: [fingerprint: ^error.fingerprint], select: e.status) @@ -288,7 +288,7 @@ defmodule ErrorTracker do |> Occurrence.changeset(%{ stacktrace: stacktrace, context: context, - bread_crumbs: bread_crumbs, + breadcrumbs: breadcrumbs, reason: reason }) |> Repo.insert!() diff --git a/lib/error_tracker/migration/mysql/v04.ex b/lib/error_tracker/migration/mysql/v04.ex index 4b58964..3b9a3aa 100644 --- a/lib/error_tracker/migration/mysql/v04.ex +++ b/lib/error_tracker/migration/mysql/v04.ex @@ -5,13 +5,13 @@ defmodule ErrorTracker.Migration.MySQL.V04 do def up(_opts) do alter table(:error_tracker_occurrences) do - add :bread_crumbs, :json, null: true + add :breadcrumbs, :json, null: true end end def down(_opts) do alter table(:error_tracker_occurrences) do - remove :bread_crumbs + remove :breadcrumbs end end end diff --git a/lib/error_tracker/migration/postgres/v04.ex b/lib/error_tracker/migration/postgres/v04.ex index 9b6e3fa..1fb05a1 100644 --- a/lib/error_tracker/migration/postgres/v04.ex +++ b/lib/error_tracker/migration/postgres/v04.ex @@ -5,13 +5,13 @@ defmodule ErrorTracker.Migration.Postgres.V04 do def up(_opts) do alter table(:error_tracker_occurrences) do - add :bread_crumbs, {:array, :string}, default: [], null: false + add :breadcrumbs, {:array, :string}, default: [], null: false end end def down(_opts) do alter table(:error_tracker_occurrences) do - remove :bread_crumbs + remove :breadcrumbs end end end diff --git a/lib/error_tracker/migration/sqlite/v04.ex b/lib/error_tracker/migration/sqlite/v04.ex index b4d974d..95fa579 100644 --- a/lib/error_tracker/migration/sqlite/v04.ex +++ b/lib/error_tracker/migration/sqlite/v04.ex @@ -5,13 +5,13 @@ defmodule ErrorTracker.Migration.SQLite.V04 do def up(_opts) do alter table(:error_tracker_occurrences) do - add :bread_crumbs, {:array, :string}, default: [], null: false + add :breadcrumbs, {:array, :string}, default: [], null: false end end def down(_opts) do alter table(:error_tracker_occurrences) do - remove :bread_crumbs + remove :breadcrumbs end end end diff --git a/lib/error_tracker/schemas/occurrence.ex b/lib/error_tracker/schemas/occurrence.ex index f943f82..1c4288e 100644 --- a/lib/error_tracker/schemas/occurrence.ex +++ b/lib/error_tracker/schemas/occurrence.ex @@ -18,7 +18,7 @@ defmodule ErrorTracker.Occurrence do field :reason, :string field :context, :map - field :bread_crumbs, {:array, :string} + field :breadcrumbs, {:array, :string} embeds_one :stacktrace, ErrorTracker.Stacktrace belongs_to :error, ErrorTracker.Error @@ -29,7 +29,7 @@ defmodule ErrorTracker.Occurrence do @doc false def changeset(occurrence, attrs) do occurrence - |> cast(attrs, [:context, :reason, :bread_crumbs]) + |> cast(attrs, [:context, :reason, :breadcrumbs]) |> maybe_put_stacktrace() |> validate_required([:reason, :stacktrace]) |> validate_context() diff --git a/lib/error_tracker/web/live/show.html.heex b/lib/error_tracker/web/live/show.html.heex index 2080a1b..fce34af 100644 --- a/lib/error_tracker/web/live/show.html.heex +++ b/lib/error_tracker/web/live/show.html.heex @@ -25,17 +25,17 @@ <%= @error.source_line %> - <.section :if={@occurrence.bread_crumbs != []} title="Bread crumbs"> + <.section :if={@occurrence.breadcrumbs != []} title="Bread crumbs">
- +
- <%= length(@occurrence.bread_crumbs) - index %>. + <%= length(@occurrence.breadcrumbs) - index %>. <%= bread_crumb %><%= breadcrumb %>
diff --git a/test/error_tracker_test.exs b/test/error_tracker_test.exs index 42f9d0e..bcf830b 100644 --- a/test/error_tracker_test.exs +++ b/test/error_tracker_test.exs @@ -101,14 +101,14 @@ defmodule ErrorTrackerTest do end test "includes bread crumbs if present" do - bread_crumbs = ["bread crumb 1", "bread crumb 2"] + breadcrumbs = ["breadcrumb 1", "breadcrumb 2"] occurrence = report_error(fn -> - raise ErrorWithBreadcrumbs, message: "test", bread_crumbs: bread_crumbs + raise ErrorWithBreadcrumbs, message: "test", bread_crumbs: breadcrumbs end) - assert occurrence.bread_crumbs == bread_crumbs + assert occurrence.breadcrumbs == breadcrumbs end end From f3ba7f868b853c669e96c9301d5a7ebd8435bf7b Mon Sep 17 00:00:00 2001 From: crbelaus Date: Wed, 13 Nov 2024 18:09:05 +0100 Subject: [PATCH 11/17] Add custom exception with breadcrumbs --- dev.exs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dev.exs b/dev.exs index b155aff..895749d 100644 --- a/dev.exs +++ b/dev.exs @@ -77,7 +77,10 @@ defmodule ErrorTrackerDevWeb.PageController do def call(_conn, :exception) do ErrorTracker.add_breadcrumb("ErrorTrackerDevWeb.PageController.exception") - raise "This is a controller exception" + + raise CustomException, + message: "This is a controller exception", + bread_crumbs: ["First", "Second"] end def call(_conn, :exit) do @@ -92,6 +95,10 @@ defmodule ErrorTrackerDevWeb.PageController do end end +defmodule CustomException do + defexception [:message, :bread_crumbs] +end + defmodule ErrorTrackerDevWeb.ErrorView do def render("404.html", _assigns) do "This is a 404" From 3f8ef4ae16fe94a38c7c865632d0595125d9e28e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Thu, 21 Nov 2024 20:10:02 +0100 Subject: [PATCH 12/17] Fix spec --- lib/error_tracker.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/error_tracker.ex b/lib/error_tracker.ex index cef47b7..9539a86 100644 --- a/lib/error_tracker.ex +++ b/lib/error_tracker.ex @@ -204,7 +204,7 @@ defmodule ErrorTracker do Process.get(:error_tracker_context, %{}) end - @spec set_context(String.t()) :: list(String.t()) + @spec add_breadcrumb(String.t()) :: list(String.t()) def add_breadcrumb(breadcrumb) when is_binary(breadcrumb) do current_breadcrumbs = Process.get(:error_tracker_breadcrumbs, []) new_breadcrumbs = current_breadcrumbs ++ [breadcrumb] From c29a9acd90ffbd9cad71909cfee6a80037d3228d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Thu, 21 Nov 2024 20:20:57 +0100 Subject: [PATCH 13/17] Update breadcrumbs order on error creation --- lib/error_tracker.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/error_tracker.ex b/lib/error_tracker.ex index 9539a86..5f8ba63 100644 --- a/lib/error_tracker.ex +++ b/lib/error_tracker.ex @@ -119,7 +119,7 @@ defmodule ErrorTracker do {:ok, stacktrace} = ErrorTracker.Stacktrace.new(stacktrace) {:ok, error} = Error.new(kind, reason, stacktrace) context = Map.merge(get_context(), given_context) - breadcrumbs = exception_breadcrumbs(exception) ++ get_breadcrumbs() + breadcrumbs = get_breadcrumbs() ++ exception_breadcrumbs(exception) if enabled?() && !ignored?(error, context) do sanitized_context = sanitize_context(context) From 42c312aed3099a79579afc5a64930747c9ec3ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Thu, 21 Nov 2024 20:23:55 +0100 Subject: [PATCH 14/17] Fix breadcrumb sorting --- lib/error_tracker/web/live/show.html.heex | 5 +- priv/static/app.css | 70 +++-------------------- 2 files changed, 12 insertions(+), 63 deletions(-) diff --git a/lib/error_tracker/web/live/show.html.heex b/lib/error_tracker/web/live/show.html.heex index fce34af..765c8b4 100644 --- a/lib/error_tracker/web/live/show.html.heex +++ b/lib/error_tracker/web/live/show.html.heex @@ -29,7 +29,10 @@
Enum.reverse() |> Enum.with_index() + } class="border-b bg-gray-400/10 border-gray-900 last:border-b-0" >
diff --git a/priv/static/app.css b/priv/static/app.css index c9fdfdd..30edff5 100644 --- a/priv/static/app.css +++ b/priv/static/app.css @@ -885,6 +885,10 @@ select { width: 2.5rem; } +.w-11 { + width: 2.75rem; +} + .w-28 { width: 7rem; } @@ -905,42 +909,6 @@ select { width: 100%; } -.w-16 { - width: 4rem; -} - -.w-12 { - width: 3rem; -} - -.w-8 { - width: 2rem; -} - -.w-7 { - width: 1.75rem; -} - -.w-6 { - width: 1.5rem; -} - -.w-\[5rem\] { - width: 5rem; -} - -.w-11 { - width: 2.75rem; -} - -.min-w-12 { - min-width: 3rem; -} - -.table-auto { - table-layout: auto; -} - .table-fixed { table-layout: fixed; } @@ -1165,6 +1133,10 @@ select { padding-bottom: 11.5px; } +.pl-2 { + padding-left: 0.5rem; +} + .pr-2 { padding-right: 0.5rem; } @@ -1173,10 +1145,6 @@ select { padding-right: 1.25rem; } -.pl-2 { - padding-left: 0.5rem; -} - .text-left { text-align: left; } @@ -1362,32 +1330,10 @@ select { border-radius: 4px; } -.first\:border-y-0:first-child { - border-top-width: 0px; - border-bottom-width: 0px; -} - -.first\:border-t-0:first-child { - border-top-width: 0px; -} - .last\:border-b-0:last-child { border-bottom-width: 0px; } -.first-of-type\:border-y-0:first-of-type { - border-top-width: 0px; - border-bottom-width: 0px; -} - -.first-of-type\:border-t-0:first-of-type { - border-top-width: 0px; -} - -.first-of-type\:border-b-0:first-of-type { - border-bottom-width: 0px; -} - .last-of-type\:border-b-0:last-of-type { border-bottom-width: 0px; } From c4a2f67d7968769c6dab6cedb260d39835151b84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Thu, 21 Nov 2024 20:40:21 +0100 Subject: [PATCH 15/17] Add some documentation --- lib/error_tracker.ex | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/lib/error_tracker.ex b/lib/error_tracker.ex index 5f8ba63..6511316 100644 --- a/lib/error_tracker.ex +++ b/lib/error_tracker.ex @@ -60,6 +60,24 @@ defmodule ErrorTracker do As we had seen before, you can use `ErrorTracker.report/3` to manually report an error. The third parameter of this function is optional and allows you to include extra context that will be tracked along with the error. + + ## Breadcrumbs + + Aside from contextual information, it is sometimes useful to know in which points + of your code the code was executed in a given request / process. + + Using breadcrumbs allows you to add that information to any error generated and + stored on a given process / request. And if you are using `Ash` or `Splode`their + exceptions' breadcrumbs will be automatically populated. + + If you want to add a breadcrumb you can do so: + + ```elixir + ErrorTracker.add_breadcrumb("Executed my super secret code") + ``` + + Breadcrumbs can be viewed in the dashboard while viewing the details of an + occurrence. """ @typedoc """ @@ -204,6 +222,22 @@ defmodule ErrorTracker do Process.get(:error_tracker_context, %{}) end + @doc """ + Adds a breadcrumb to the current process. + + The new breadcrumb will be added as the most recent entry of the breadcrumbs + list. + + ## Breadcrumbs limit + + Breadcrumbs are a powerful tool that allows to add an infinite number of + entries. However, it is not recommended to store errors with an excessive + amount of breadcrumbs. + + As they are stored as an array of strings under the hood, storing many + entries per error can lead to some delays and using extra disk space on the + database. + """ @spec add_breadcrumb(String.t()) :: list(String.t()) def add_breadcrumb(breadcrumb) when is_binary(breadcrumb) do current_breadcrumbs = Process.get(:error_tracker_breadcrumbs, []) @@ -215,7 +249,7 @@ defmodule ErrorTracker do end @doc """ - Obtain the context of the current process. + Obtain the breadcrumbs of the current process. """ @spec get_breadcrumbs() :: list(String.t()) def get_breadcrumbs do From 3e7ea0087894b8159b9abfec5899001264e25552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Thu, 21 Nov 2024 20:45:08 +0100 Subject: [PATCH 16/17] Add more extra tests --- test/error_tracker_test.exs | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/test/error_tracker_test.exs b/test/error_tracker_test.exs index bcf830b..00538fe 100644 --- a/test/error_tracker_test.exs +++ b/test/error_tracker_test.exs @@ -100,7 +100,7 @@ defmodule ErrorTrackerTest do assert report_error(fn -> raise "Sample error" end) == :noop end - test "includes bread crumbs if present" do + test "includes breadcrumbs if present" do breadcrumbs = ["breadcrumb 1", "breadcrumb 2"] occurrence = @@ -110,6 +110,27 @@ defmodule ErrorTrackerTest do assert occurrence.breadcrumbs == breadcrumbs end + + test "includes breadcrumbs if stored by the user" do + ErrorTracker.add_breadcrumb("breadcrumb 1") + ErrorTracker.add_breadcrumb("breadcrumb 2") + + occurrence = report_error(fn -> raise "Sample error" end) + + assert occurrence.breadcrumbs == ["breadcrumb 1", "breadcrumb 2"] + end + + test "merges breadcrumbs stored by the user and contained on the exception" do + ErrorTracker.add_breadcrumb("breadcrumb 1") + ErrorTracker.add_breadcrumb("breadcrumb 2") + + occurrence = + report_error(fn -> + raise ErrorWithBreadcrumbs, message: "test", bread_crumbs: ["breadcrumb 3"] + end) + + assert occurrence.breadcrumbs == ["breadcrumb 1", "breadcrumb 2", "breadcrumb 3"] + end end describe inspect(&ErrorTracker.resolve/1) do @@ -129,6 +150,15 @@ defmodule ErrorTrackerTest do assert {:ok, %Error{status: :unresolved}} = ErrorTracker.unresolve(resolved) end end + + describe inspect(&ErrorTracker.add_breadcrumb/1) do + test "adds an entry to the breadcrumbs list" do + ErrorTracker.add_breadcrumb("breadcrumb 1") + ErrorTracker.add_breadcrumb("breadcrumb 2") + + assert ["breadcrumb 1", "breadcrumb 2"] = ErrorTracker.get_breadcrumbs() + end + end end defmodule ErrorWithBreadcrumbs do From b439f156ac474b507ca27af104d360363fe7d694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Thu, 21 Nov 2024 20:51:49 +0100 Subject: [PATCH 17/17] Update migration version in Getting Started --- guides/Getting Started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/Getting Started.md b/guides/Getting Started.md index d2d0024..391eb39 100644 --- a/guides/Getting Started.md +++ b/guides/Getting Started.md @@ -56,7 +56,7 @@ Open the generated migration and call the `up` and `down` functions on `ErrorTra defmodule MyApp.Repo.Migrations.AddErrorTracker do use Ecto.Migration - def up, do: ErrorTracker.Migration.up(version: 3) + def up, do: ErrorTracker.Migration.up(version: 4) # We specify `version: 1` in `down`, to ensure we remove all migrations. def down, do: ErrorTracker.Migration.down(version: 1)