Skip to content

Commit 6c13340

Browse files
Merge pull request #447 from aselder/throw-exit
Send Sentry reports on uncaught throws/exits
2 parents dac9c3e + 72d4c9a commit 6c13340

File tree

4 files changed

+105
-1
lines changed

4 files changed

+105
-1
lines changed

lib/sentry/plug_capture.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ defmodule Sentry.PlugCapture do
4545
e ->
4646
_ = Sentry.capture_exception(e, stacktrace: __STACKTRACE__, event_source: :plug)
4747
:erlang.raise(:error, e, __STACKTRACE__)
48+
catch
49+
kind, reason ->
50+
message = "Uncaught #{kind} - #{inspect(reason)}"
51+
stack = __STACKTRACE__
52+
Sentry.capture_message(message, stacktrace: stack, event_source: :plug)
53+
:erlang.raise(kind, reason, stack)
4854
end
4955
end
5056
end

test/plug_capture_test.exs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ defmodule Sentry.PlugCaptureTest do
77
defmodule PhoenixController do
88
use Phoenix.Controller
99
def error(_conn, _params), do: raise("PhoenixError")
10+
def exit(_conn, _params), do: exit(:test)
11+
def throw(_conn, _params), do: throw(:test)
1012

1113
def assigns(conn, _params) do
1214
_test = conn.assigns2.test
@@ -17,6 +19,8 @@ defmodule Sentry.PlugCaptureTest do
1719
use Phoenix.Router
1820

1921
get "/error_route", PhoenixController, :error
22+
get "/exit_route", PhoenixController, :exit
23+
get "/throw_route", PhoenixController, :throw
2024
get "/assigns_route", PhoenixController, :assigns
2125
end
2226

@@ -56,6 +60,40 @@ defmodule Sentry.PlugCaptureTest do
5660
end)
5761
end
5862

63+
test "sends throws to Sentry" do
64+
bypass = Bypass.open()
65+
66+
Bypass.expect(bypass, fn conn ->
67+
{:ok, body, conn} = Plug.Conn.read_body(conn)
68+
_json = Jason.decode!(body)
69+
Plug.Conn.resp(conn, 200, ~s<{"id": "340"}>)
70+
end)
71+
72+
modify_env(:sentry, dsn: "http://public:secret@localhost:#{bypass.port}/1")
73+
74+
catch_throw(
75+
conn(:get, "/throw_route")
76+
|> Sentry.ExamplePlugApplication.call([])
77+
)
78+
end
79+
80+
test "sends exits to Sentry" do
81+
bypass = Bypass.open()
82+
83+
Bypass.expect(bypass, fn conn ->
84+
{:ok, body, conn} = Plug.Conn.read_body(conn)
85+
_json = Jason.decode!(body)
86+
Plug.Conn.resp(conn, 200, ~s<{"id": "340"}>)
87+
end)
88+
89+
modify_env(:sentry, dsn: "http://public:secret@localhost:#{bypass.port}/1")
90+
91+
catch_exit(
92+
conn(:get, "/exit_route")
93+
|> Sentry.ExamplePlugApplication.call([])
94+
)
95+
end
96+
5997
test "works with Sentry.PlugContext" do
6098
bypass = Bypass.open()
6199

@@ -131,6 +169,56 @@ defmodule Sentry.PlugCaptureTest do
131169
end)
132170
end
133171

172+
test "reports exits occurring in Phoenix Endpoint" do
173+
bypass = Bypass.open()
174+
175+
Bypass.expect(bypass, fn conn ->
176+
{:ok, body, conn} = Plug.Conn.read_body(conn)
177+
json = Jason.decode!(body)
178+
assert json["culprit"] == "Sentry.PlugCaptureTest.PhoenixController.exit/2"
179+
assert json["message"] == "Uncaught exit - :test"
180+
Plug.Conn.resp(conn, 200, ~s<{"id": "340"}>)
181+
end)
182+
183+
modify_env(:sentry,
184+
dsn: "http://public:secret@localhost:#{bypass.port}/1",
185+
"#{__MODULE__.PhoenixEndpoint}": [
186+
render_errors: [view: Sentry.ErrorView, accepts: ~w(html)]
187+
]
188+
)
189+
190+
{:ok, _} = PhoenixEndpoint.start_link()
191+
192+
capture_log(fn ->
193+
catch_exit(conn(:get, "/exit_route") |> PhoenixEndpoint.call([]))
194+
end)
195+
end
196+
197+
test "reports throws occurring in Phoenix Endpoint" do
198+
bypass = Bypass.open()
199+
200+
Bypass.expect(bypass, fn conn ->
201+
{:ok, body, conn} = Plug.Conn.read_body(conn)
202+
json = Jason.decode!(body)
203+
assert json["culprit"] == "Sentry.PlugCaptureTest.PhoenixController.throw/2"
204+
assert json["message"] == "Uncaught throw - :test"
205+
Plug.Conn.resp(conn, 200, ~s<{"id": "340"}>)
206+
end)
207+
208+
modify_env(:sentry,
209+
dsn: "http://public:secret@localhost:#{bypass.port}/1",
210+
"#{__MODULE__.PhoenixEndpoint}": [
211+
render_errors: [view: Sentry.ErrorView, accepts: ~w(html)]
212+
]
213+
)
214+
215+
{:ok, _} = PhoenixEndpoint.start_link()
216+
217+
capture_log(fn ->
218+
catch_throw(conn(:get, "/throw_route") |> PhoenixEndpoint.call([]))
219+
end)
220+
end
221+
134222
test "can render feedback form in Phoenix ErrorView" do
135223
bypass = Bypass.open()
136224

test/sources_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ defmodule Sentry.SourcesTest do
5555
test "exception makes call to Sentry API" do
5656
correct_context = %{
5757
"context_line" => " raise RuntimeError, \"Error\"",
58-
"post_context" => [" end", "", " post \"/error_route\" do"],
58+
"post_context" => [" end", "", " get \"/exit_route\" do"],
5959
"pre_context" => ["", " get \"/error_route\" do", " _ = conn"]
6060
}
6161

test/support/example_plug_application.ex

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ defmodule Sentry.ExamplePlugApplication do
1313
raise RuntimeError, "Error"
1414
end
1515

16+
get "/exit_route" do
17+
_ = conn
18+
exit(:test)
19+
end
20+
21+
get "/throw_route" do
22+
_ = conn
23+
throw(:test)
24+
end
25+
1626
post "/error_route" do
1727
_ = conn
1828
raise RuntimeError, "Error"

0 commit comments

Comments
 (0)