Skip to content

Commit 8a06473

Browse files
authored
Plug & Phoenix basic integration (#4)
This commit improves the Plug integration to be compatible with more scenarios and adds a Phoenix plug and play integration using Telemetry events.
1 parent 55842e6 commit 8a06473

5 files changed

Lines changed: 99 additions & 20 deletions

File tree

README.md

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,16 @@ config :error_tracker,
1111
repo: MyApp.Repo
1212
```
1313

14-
Attach to Oban events:
14+
And you are ready to go!
1515

16-
```elixir
17-
defmodule MyApp.Application do
18-
def start(_type, _args) do
19-
ErrorTracker.Integrations.Oban.attach()
20-
end
21-
end
22-
```
16+
By default Phoenix and Oban integrations will start registering exceptions.
2317

24-
Attach to Plug errors:
18+
If you want to also catch exceptions before your Phoenix Router (in plugs used
19+
on your Endpoint) or your application just use `Plug` but not `Phoenix`, you can
20+
attach to those errors with:
2521

2622
```elixir
2723
defmodule MyApp.Endpoint do
28-
use ErrorTracker.Plug
24+
use ErrorTracker.Integrations.Plug
2925
end
3026
```

dev.exs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,19 @@ defmodule ErrorTrackerDevWeb.PageController do
5555
content(conn, """
5656
<h2>ErrorTracker Dev Server</h2>
5757
<div><a href="/errors">Open ErrorTracker</a></div>
58-
<div><a href="/404">Generate 404 Error</a></div>
58+
<div><a href="/plug-exception">Generate Plug exception</a></div>
59+
<div><a href="/404">Generate Router 404</a></div>
60+
<div><a href="/noroute">Raise NoRouteError from a controller</a></div>
5961
<div><a href="/exception">Generate Exception</a></div>
6062
""")
6163
end
6264

65+
def call(conn, :noroute) do
66+
raise Phoenix.Router.NoRouteError, conn: conn, router: ErrorTrackerDevWeb.Router
67+
end
68+
6369
def call(_conn, :exception) do
64-
raise "This is an error"
70+
raise "This is a controller exception"
6571
end
6672

6773
defp content(conn, content) do
@@ -71,9 +77,18 @@ defmodule ErrorTrackerDevWeb.PageController do
7177
end
7278
end
7379

80+
defmodule ErrorTrackerDevWeb.ErrorView do
81+
def render("404.html", _assigns) do
82+
"This is a 404"
83+
end
84+
85+
def render("500.html", _assigns) do
86+
"This is a 500"
87+
end
88+
end
89+
7490
defmodule ErrorTrackerDevWeb.Router do
7591
use Phoenix.Router
76-
use ErrorTracker.Integrations.Plug
7792

7893
pipeline :browser do
7994
plug :fetch_session
@@ -83,12 +98,14 @@ defmodule ErrorTrackerDevWeb.Router do
8398
scope "/" do
8499
pipe_through :browser
85100
get "/", ErrorTrackerDevWeb.PageController, :index
101+
get "/noroute", ErrorTrackerDevWeb.PageController, :noroute
86102
get "/exception", ErrorTrackerDevWeb.PageController, :exception
87103
end
88104
end
89105

90106
defmodule ErrorTrackerDevWeb.Endpoint do
91107
use Phoenix.Endpoint, otp_app: :error_tracker
108+
use ErrorTracker.Integrations.Plug
92109

93110
@session_options [
94111
store: :cookie,
@@ -107,7 +124,11 @@ defmodule ErrorTrackerDevWeb.Endpoint do
107124

108125
plug Plug.RequestId
109126
plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]
127+
plug :maybe_exception
110128
plug ErrorTrackerDevWeb.Router
129+
130+
def maybe_exception(%Plug.Conn{path_info: ["plug-exception"]}, _), do: raise("Plug exception")
131+
def maybe_exception(conn, _), do: conn
111132
end
112133

113134
defmodule Migration0 do

lib/error_tracker/application.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ defmodule ErrorTracker.Application do
1111

1212
defp attach_handlers do
1313
ErrorTracker.Integrations.Oban.attach()
14+
ErrorTracker.Integrations.Phoenix.attach()
1415
end
1516
end
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
defmodule ErrorTracker.Integrations.Phoenix do
2+
alias ErrorTracker.Integrations.Plug, as: PlugIntegration
3+
4+
def attach do
5+
if Application.spec(:phoenix) do
6+
:telemetry.attach(
7+
__MODULE__,
8+
[:phoenix, :router_dispatch, :exception],
9+
&__MODULE__.handle_exception/4,
10+
[]
11+
)
12+
end
13+
end
14+
15+
def handle_exception(
16+
[:phoenix, :router_dispatch, :exception],
17+
_measurements,
18+
%{reason: %Plug.Conn.WrapperError{conn: conn, reason: reason, stack: stack}},
19+
_opts
20+
) do
21+
PlugIntegration.report_error(conn, reason, stack)
22+
end
23+
24+
def handle_exception(
25+
[:phoenix, :router_dispatch, :exception],
26+
_measurements,
27+
%{reason: reason, stacktrace: stack, conn: conn},
28+
_opts
29+
) do
30+
PlugIntegration.report_error(conn, reason, stack)
31+
end
32+
end

lib/error_tracker/integrations/plug.ex

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,46 @@
11
defmodule ErrorTracker.Integrations.Plug do
22
defmacro __using__(_opts) do
33
quote do
4-
use Plug.ErrorHandler
4+
@before_compile unquote(__MODULE__)
5+
end
6+
end
7+
8+
defmacro __before_compile__(_) do
9+
quote do
10+
defoverridable call: 2
11+
12+
def call(conn, opts) do
13+
try do
14+
super(conn, opts)
15+
rescue
16+
e in Plug.Conn.WrapperError ->
17+
unquote(__MODULE__).report_error(conn, e, e.stack)
518

6-
@impl Plug.ErrorHandler
7-
def handle_errors(conn, %{kind: :error, reason: exception, stack: stack}) do
8-
ErrorTracker.report(exception, stack)
19+
Plug.Conn.WrapperError.reraise(e)
920

10-
:ok
21+
e ->
22+
stack = __STACKTRACE__
23+
unquote(__MODULE__).report_error(conn, e, stack)
24+
25+
:erlang.raise(:error, e, stack)
26+
catch
27+
kind, reason ->
28+
stack = __STACKTRACE__
29+
unquote(__MODULE__).report_error(conn, reason, stack)
30+
31+
:erlang.raise(kind, reason, stack)
32+
end
1133
end
34+
end
35+
end
1236

13-
def handle_errors(conn, _throw_or_exit) do
14-
:ok
37+
def report_error(_conn, reason, stack) do
38+
unless Process.get(:error_tracker_router_exception_reported) do
39+
# TODO: Add metadata from conn when implemented
40+
try do
41+
ErrorTracker.report(reason, stack)
42+
after
43+
Process.put(:error_tracker_router_exception_reported, true)
1544
end
1645
end
1746
end

0 commit comments

Comments
 (0)