From b0d17f9bc338926564738f42bcd5b4426e29b4ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Mon, 10 Jun 2024 17:22:54 +0200 Subject: [PATCH 1/4] Add Phoenix integration --- lib/error_tracker/application.ex | 1 + lib/error_tracker/integrations/phoenix.ex | 32 +++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 lib/error_tracker/integrations/phoenix.ex diff --git a/lib/error_tracker/application.ex b/lib/error_tracker/application.ex index e9f8a27..0970ad2 100644 --- a/lib/error_tracker/application.ex +++ b/lib/error_tracker/application.ex @@ -11,5 +11,6 @@ defmodule ErrorTracker.Application do defp attach_handlers do ErrorTracker.Integrations.Oban.attach() + ErrorTracker.Integrations.Phoenix.attach() end end diff --git a/lib/error_tracker/integrations/phoenix.ex b/lib/error_tracker/integrations/phoenix.ex new file mode 100644 index 0000000..a0e70b4 --- /dev/null +++ b/lib/error_tracker/integrations/phoenix.ex @@ -0,0 +1,32 @@ +defmodule ErrorTracker.Integrations.Phoenix do + alias ErrorTracker.Integrations.Plug, as: PlugIntegration + + def attach do + if Application.spec(:phoenix) do + :telemetry.attach( + __MODULE__, + [:phoenix, :router_dispatch, :exception], + &__MODULE__.handle_exception/4, + [] + ) + end + end + + def handle_exception( + [:phoenix, :router_dispatch, :exception], + _measurements, + %{reason: %Plug.Conn.WrapperError{conn: conn, reason: reason, stack: stack}}, + _opts + ) do + PlugIntegration.report_error(conn, reason, stack) + end + + def handle_exception( + [:phoenix, :router_dispatch, :exception], + _measurements, + %{reason: reason, stacktrace: stack, conn: conn}, + _opts + ) do + PlugIntegration.report_error(conn, reason, stack) + end +end From b778eeb3277b73734a8de0acf05854323e8d6b03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Mon, 10 Jun 2024 17:24:30 +0200 Subject: [PATCH 2/4] Plug integration ready for most kinds of Plug systems --- lib/error_tracker/integrations/plug.ex | 43 +++++++++++++++++++++----- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/lib/error_tracker/integrations/plug.ex b/lib/error_tracker/integrations/plug.ex index a0b9c5d..10688b5 100644 --- a/lib/error_tracker/integrations/plug.ex +++ b/lib/error_tracker/integrations/plug.ex @@ -1,17 +1,46 @@ defmodule ErrorTracker.Integrations.Plug do defmacro __using__(_opts) do quote do - use Plug.ErrorHandler + @before_compile unquote(__MODULE__) + end + end + + defmacro __before_compile__(_) do + quote do + defoverridable call: 2 + + def call(conn, opts) do + try do + super(conn, opts) + rescue + e in Plug.Conn.WrapperError -> + unquote(__MODULE__).report_error(conn, e, e.stack) - @impl Plug.ErrorHandler - def handle_errors(conn, %{kind: :error, reason: exception, stack: stack}) do - ErrorTracker.report(exception, stack) + Plug.Conn.WrapperError.reraise(e) - :ok + e -> + stack = __STACKTRACE__ + unquote(__MODULE__).report_error(conn, e, stack) + + :erlang.raise(:error, e, stack) + catch + kind, reason -> + stack = __STACKTRACE__ + unquote(__MODULE__).report_error(conn, reason, stack) + + :erlang.raise(kind, reason, stack) + end end + end + end - def handle_errors(conn, _throw_or_exit) do - :ok + def report_error(_conn, reason, stack) do + unless Process.get(:error_tracker_router_exception_reported) do + # TODO: Add metadata from conn when implemented + try do + ErrorTracker.report(reason, stack) + after + Process.put(:error_tracker_router_exception_reported, true) end end end From 3c20e72a0b83643bfe3b725572987a7fd63331ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Mon, 10 Jun 2024 17:28:03 +0200 Subject: [PATCH 3/4] Improve dev server --- dev.exs | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/dev.exs b/dev.exs index 524f4fa..a6003a2 100644 --- a/dev.exs +++ b/dev.exs @@ -55,13 +55,19 @@ defmodule ErrorTrackerDevWeb.PageController do content(conn, """

ErrorTracker Dev Server

Open ErrorTracker
-
Generate 404 Error
+
Generate Plug exception
+
Generate Router 404
+
Raise NoRouteError from a controller
Generate Exception
""") end + def call(conn, :noroute) do + raise Phoenix.Router.NoRouteError, conn: conn, router: ErrorTrackerDevWeb.Router + end + def call(_conn, :exception) do - raise "This is an error" + raise "This is a controller exception" end defp content(conn, content) do @@ -71,9 +77,18 @@ defmodule ErrorTrackerDevWeb.PageController do end end +defmodule ErrorTrackerDevWeb.ErrorView do + def render("404.html", _assigns) do + "This is a 404" + end + + def render("500.html", _assigns) do + "This is a 500" + end +end + defmodule ErrorTrackerDevWeb.Router do use Phoenix.Router - use ErrorTracker.Integrations.Plug pipeline :browser do plug :fetch_session @@ -83,12 +98,14 @@ defmodule ErrorTrackerDevWeb.Router do scope "/" do pipe_through :browser get "/", ErrorTrackerDevWeb.PageController, :index + get "/noroute", ErrorTrackerDevWeb.PageController, :noroute get "/exception", ErrorTrackerDevWeb.PageController, :exception end end defmodule ErrorTrackerDevWeb.Endpoint do use Phoenix.Endpoint, otp_app: :error_tracker + use ErrorTracker.Integrations.Plug @session_options [ store: :cookie, @@ -107,7 +124,11 @@ defmodule ErrorTrackerDevWeb.Endpoint do plug Plug.RequestId plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint] + plug :maybe_exception plug ErrorTrackerDevWeb.Router + + def maybe_exception(%Plug.Conn{path_info: ["plug-exception"]}, _), do: raise("Plug exception") + def maybe_exception(conn, _), do: conn end defmodule Migration0 do From 65dd7125623ef4a672b418a58c5ad787f9d7f8fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Mon, 10 Jun 2024 17:32:16 +0200 Subject: [PATCH 4/4] Update README --- README.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e94e809..3024954 100644 --- a/README.md +++ b/README.md @@ -11,20 +11,16 @@ config :error_tracker, repo: MyApp.Repo ``` -Attach to Oban events: +And you are ready to go! -```elixir -defmodule MyApp.Application do - def start(_type, _args) do - ErrorTracker.Integrations.Oban.attach() - end -end -``` +By default Phoenix and Oban integrations will start registering exceptions. -Attach to Plug errors: +If you want to also catch exceptions before your Phoenix Router (in plugs used +on your Endpoint) or your application just use `Plug` but not `Phoenix`, you can +attach to those errors with: ```elixir defmodule MyApp.Endpoint do - use ErrorTracker.Plug + use ErrorTracker.Integrations.Plug end ```