Skip to content

Commit 3b19491

Browse files
authored
Extract handle_errors to allow composition (#41)
* extract handle_errors to allow composition * remove compilation warning in tests
1 parent c026b58 commit 3b19491

File tree

3 files changed

+68
-42
lines changed

3 files changed

+68
-42
lines changed

README.md

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Plugsnag
1+
# Plugsnag
22
![Elixir CI](https://github.com/bugsnag-elixir/plugsnag/workflows/Elixir%20CI/badge.svg)
33
[![Plugsnag version](https://img.shields.io/hexpm/v/plugsnag.svg)](https://hex.pm/packages/plugsnag)
44
[![Hex.pm](https://img.shields.io/hexpm/dt/plugsnag.svg)](https://hex.pm/packages/plugsnag)
@@ -17,29 +17,30 @@ Just throw it in your deps in your `mix.exs`:
1717
```
1818

1919
Then you'll need to configure it with your API key as
20-
per [the bugsnag-elixir
21-
docs](https://github.com/jarednorman/bugsnag-elixir).
20+
per [the bugsnag-elixir docs](https://github.com/jarednorman/bugsnag-elixir).
2221

23-
If you're using Elixir < 1.4 make sure that `plugsnag` and `bugsnag` apps are started in your mix.exs. If you are using Elixir 1.4, the applications will be automatically started because they are dependencies.
24-
25-
For example:
22+
To use the plug, `use` it in your router. For example in an Phoenix app:
2623

2724
```elixir
28-
def application do
29-
[mod: {MyApp, []},
30-
applications: [:logger, :plugsnag, :bugsnag]
31-
]
32-
end
25+
defmodule YourApp.Router do
26+
use Phoenix.Router
27+
use Plugsnag
28+
29+
# ...
30+
end
3331
```
3432

35-
To use the plug, `use` it in your router. For example in an Phoenix app:
33+
If you want to define your own `handle_errors` functions using [Plug.ErrorHandler](https://hexdocs.pm/plug/Plug.ErrorHandler.html), then you can call `Plugsnag.handle_errors/{2,3}` directly.
3634

3735
```elixir
3836
defmodule YourApp.Router do
3937
use Phoenix.Router
40-
use Plugsnag
41-
38+
use Plug.ErrorHandler
4239
# ...
40+
defp handle_errors(conn, assigns) do
41+
Plugsnag.handle_errors(conn, assigns)
42+
# do your own handling
43+
end
4344
end
4445
```
4546

lib/plugsnag.ex

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,36 @@ defmodule Plugsnag do
33
quote location: :keep do
44
use Plug.ErrorHandler
55

6-
defp handle_errors(conn, %{reason: exception, kind: :error} = assigns) do
7-
# Only handle exceptions that get rendered as an HTTP 5xx status
8-
if Plug.Exception.status(exception) >= 500 do
9-
do_handle_errors(conn, assigns)
10-
end
6+
defp handle_errors(conn, assigns) do
7+
Plugsnag.handle_errors(conn, assigns, unquote(options))
118
end
9+
end
10+
end
1211

13-
defp handle_errors(conn, %{reason: _exception} = assigns) do
14-
do_handle_errors(conn, assigns)
15-
end
12+
def handle_errors(conn, assigns, opts \\ [])
13+
14+
def handle_errors(conn, %{reason: exception, kind: :error} = assigns, opts) do
15+
# Only handle exceptions that get rendered as an HTTP 5xx status
16+
if Plug.Exception.status(exception) >= 500 do
17+
report_error(conn, assigns, opts)
18+
end
19+
end
1620

17-
defp do_handle_errors(conn, %{reason: exception}) do
18-
error_report_builder =
19-
unquote(
20-
Keyword.get(
21-
options,
22-
:error_report_builder,
23-
Plugsnag.BasicErrorReportBuilder
24-
)
25-
)
21+
def handle_errors(conn, %{reason: _exception} = assigns, opts) do
22+
report_error(conn, assigns, opts)
23+
end
2624

27-
options =
28-
%Plugsnag.ErrorReport{}
29-
|> error_report_builder.build_error_report(conn)
30-
|> Map.from_struct()
31-
|> Keyword.new()
25+
defp report_error(conn, %{reason: exception}, opts) do
26+
error_report_builder =
27+
Keyword.get(opts, :error_report_builder, Plugsnag.BasicErrorReportBuilder)
3228

33-
reporter = Application.get_env(:plugsnag, :reporter, Bugsnag)
34-
apply(reporter, :report, [exception | [options]])
35-
end
36-
end
29+
options =
30+
%Plugsnag.ErrorReport{}
31+
|> error_report_builder.build_error_report(conn)
32+
|> Map.from_struct()
33+
|> Keyword.new()
34+
35+
reporter = Application.get_env(:plugsnag, :reporter, Bugsnag)
36+
apply(reporter, :report, [exception | [options]])
3737
end
3838
end

test/plugsnag_test.exs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ defmodule PlugsnagTest do
1414
defmacro __using__(_env) do
1515
quote do
1616
def call(conn, _opts) do
17+
{:current_stacktrace, [_ | stacktrace]} = Process.info(self(), :current_stacktrace)
18+
1719
raise Plug.Conn.WrapperError,
1820
conn: conn,
1921
kind: :error,
20-
stack: System.stacktrace(),
22+
stack: stacktrace,
2123
reason: TestException.exception([])
2224
end
2325
end
@@ -28,10 +30,12 @@ defmodule PlugsnagTest do
2830
defmacro __using__(_env) do
2931
quote do
3032
def call(conn, _opts) do
33+
{:current_stacktrace, [_ | stacktrace]} = Process.info(self(), :current_stacktrace)
34+
3135
raise Plug.Conn.WrapperError,
3236
conn: conn,
3337
kind: :error,
34-
stack: System.stacktrace(),
38+
stack: stacktrace,
3539
reason: NotFoundException.exception([])
3640
end
3741
end
@@ -69,6 +73,27 @@ defmodule PlugsnagTest do
6973
assert_received {:report, {%TestException{}, _}}
7074
end
7175

76+
test "calling Plugsnag.handle_errors explicitly" do
77+
defmodule ExtendedPlug do
78+
use Plug.ErrorHandler
79+
use ErrorRaisingPlug
80+
81+
defp handle_errors(conn, %{reason: _exception} = assigns) do
82+
send(self(), :custom_handle)
83+
Plugsnag.handle_errors(conn, assigns)
84+
end
85+
end
86+
87+
conn = conn(:get, "/")
88+
89+
assert_raise Plug.Conn.WrapperError, "** (PlugsnagTest.TestException) oops", fn ->
90+
ExtendedPlug.call(conn, [])
91+
end
92+
93+
assert_received :custom_handle
94+
assert_received {:report, {%TestException{}, _}}
95+
end
96+
7297
test "includes connection metadata in the report" do
7398
conn = conn(:get, "/?hello=computer")
7499

0 commit comments

Comments
 (0)