Skip to content

Commit 5ab1ed2

Browse files
Merge pull request #98 from getsentry/filter
Filter Events
2 parents 7016a24 + e695f44 commit 5ab1ed2

File tree

10 files changed

+118
-9
lines changed

10 files changed

+118
-9
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## master
44

5+
* Enhancements
6+
* Allow filtering which exceptions are sent via `Sentry.EventFilter` behaviour
7+
58
* Bug Fixes
69
* Fix usage of deprecated modules
710

docs/config.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ Optional settings
5252
Set this to true if you want to capture all exceptions that occur even outside of a request cycle. This
5353
defaults to false.
5454

55+
.. describe:: filter
56+
57+
Set this to a module that implements the ``Sentry.EventFilter`` behaviour if you would like to prevent
58+
certain exceptions from being sent. See below for further documentation.
59+
5560
Testing Your Configuration
5661
--------------------------
5762

docs/index.rst

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,33 @@ new process and it fails you might lose your context. That said using the contex
107107
# adds an breadcrumb to the request to help debug
108108
Sentry.Context.add_breadcrumb(%{my: "crumb"})
109109
110+
Filtering Events
111+
--------------
112+
113+
If you would like to prevent certain exceptions, the :filter configuration option
114+
allows you to implement the ``Sentry.EventFilter`` behaviour. The first argument is the
115+
source of the event, and the second is the exception to be sent. ``Sentry.Plug``
116+
will have a source of ``:plug``, and ``Sentry.Logger`` will have a source of ``:logger``.
117+
If an exception does not come from either of those sources, the source will be nil
118+
unless the ``:event_source`` option is passed to ``Sentry.capture_exception/2``
119+
120+
A configuration like below will prevent sending ``Phoenix.Router.NoRouteError`` from ``Sentry.Plug``, but
121+
allows other exceptions to be sent.
122+
123+
.. code-block:: elixir
124+
# sentry_event_filter.exs
125+
defmodule MyApp.SentryEventFilter do
126+
@behaviour Sentry.EventFilter
127+
128+
def exclude_exception?(:plug, %Elixir.Phoenix.Router.NoRouteError{}), do: true
129+
def exclude_exception?(_, ), do: false
130+
end
131+
132+
# config.exs
133+
config :sentry, filter: MyApp.SentryEventFilter,
134+
included_environments: ~w(production staging),
135+
environment_name: System.get_env("RELEASE_LEVEL") || "development"
136+
110137
Deep Dive
111138
---------
112139

docs/usage.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ With calls to ``capture_exception`` additional data can be supplied as a keyword
7171
"email" => "clever-girl"
7272
}
7373
74+
.. describe:: event_source
75+
76+
The source of the event. Used by the `Sentry.EventFilter` behaviour.
7477

7578
Breadcrumbs
7679
-----------

lib/sentry.ex

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,40 @@ defmodule Sentry do
5151
our local development machines, exceptions will never be sent, because the
5252
default value is not in the list of `included_environments`.
5353
54+
## Filtering Exceptions
55+
56+
If you would like to prevent certain exceptions, the :filter configuration option
57+
allows you to implement the `Sentry.EventFilter` behaviour. The first argument is the
58+
source of the event, and the second is the exception to be sent. `Sentry.Plug`
59+
will have a source of `:plug`, and `Sentry.Logger` will have a source of `:logger`.
60+
If an exception does not come from either of those sources, the source will be nil
61+
unless the `:event_source` option is passed to `Sentry.capture_exception/2`
62+
63+
A configuration like below will prevent sending `Phoenix.Router.NoRouteError` from `Sentry.Plug`, but
64+
allows other exceptions to be sent.
65+
66+
# sentry_event_filter.exs
67+
defmodule MyApp.SentryEventFilter do
68+
@behaviour Sentry.EventFilter
69+
70+
def exclude_exception?(:plug, %Elixir.Phoenix.Router.NoRouteError{}), do: true
71+
def exclude_exception?(_, ), do: false
72+
end
73+
74+
# config.exs
75+
config :sentry, filter: MyApp.SentryEventFilter,
76+
included_environments: ~w(production staging),
77+
environment_name: System.get_env("RELEASE_LEVEL") || "development"
78+
5479
## Capturing Exceptions
5580
56-
Simply calling `capture_exception\2` will send the event.
81+
Simply calling `capture_exception/2` will send the event.
5782
5883
Sentry.capture_exception(my_exception)
84+
Sentry.capture_exception(other_exception, [source_name: :my_source])
85+
86+
### Options
87+
* `:event_source` - The source passed as the first argument to `Sentry.EventFilter.exclude_exception?/2`
5988
6089
## Configuring The `Logger` Backend
6190
@@ -82,11 +111,16 @@ defmodule Sentry do
82111
@doc """
83112
Parses and submits an exception to Sentry if current environment is in included_environments.
84113
"""
85-
@spec capture_exception(Exception.t, Keyword.t) :: {:ok, String.t} | :error
114+
@spec capture_exception(Exception.t, Keyword.t) :: {:ok, String.t} | :error | :excluded
86115
def capture_exception(exception, opts \\ []) do
87-
exception
88-
|> Event.transform_exception(opts)
89-
|> send_event()
116+
filter_module = Application.get_env(:sentry, :filter, Sentry.DefaultEventFilter)
117+
{source, opts} = Keyword.pop(opts, :event_source)
118+
if(filter_module.exclude_exception?(exception, source)) do
119+
:excluded
120+
else
121+
Event.transform_exception(exception, opts)
122+
|> send_event()
123+
end
90124
end
91125

92126
@doc """

lib/sentry/event_filter.ex

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
defmodule Sentry.EventFilter do
2+
@callback exclude_exception?(atom, Exception.t) :: any
3+
end
4+
5+
defmodule Sentry.DefaultEventFilter do
6+
@behaviour Sentry.EventFilter
7+
def exclude_exception?(_, _), do: false
8+
end

lib/sentry/logger.ex

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@ defmodule Sentry.Logger do
3434
case error_info do
3535
{_kind, {exception, stacktrace}, _stack} when is_list(stacktrace) ->
3636
opts = Keyword.put(opts, :stacktrace, stacktrace)
37-
Sentry.capture_exception(exception, opts)
37+
|> Keyword.put(:event_source, :logger)
38+
Sentry.capture_exception(exception, opts)
3839
{_kind, exception, stacktrace} ->
3940
opts = Keyword.put(opts, :stacktrace, stacktrace)
40-
Sentry.capture_exception(exception, opts)
41+
|> Keyword.put(:event_source, :logger)
42+
Sentry.capture_exception(exception, opts)
4143
end
4244
rescue
4345
ex ->

lib/sentry/plug.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ defmodule Sentry.Plug do
8787
request_id_header: unquote(request_id_header)]
8888
request = Sentry.Plug.build_request_interface_data(conn, opts)
8989
exception = Exception.normalize(kind, reason, stack)
90-
Sentry.capture_exception(exception, [stacktrace: stack, request: request])
90+
Sentry.capture_exception(exception, [stacktrace: stack, request: request, event_source: :plug])
9191
end
9292
end
9393
end

test/sentry_test.exs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
11
defmodule SentryTest do
2-
use ExUnit.Case, async: true
2+
use ExUnit.Case
3+
4+
test "excludes events properly" do
5+
Application.put_env(:sentry, :filter, Sentry.TestFilter)
6+
7+
8+
bypass = Bypass.open
9+
Bypass.expect bypass, fn conn ->
10+
{:ok, body, conn} = Plug.Conn.read_body(conn)
11+
assert body =~ "RuntimeError"
12+
assert conn.request_path == "/api/1/store/"
13+
assert conn.method == "POST"
14+
Plug.Conn.resp(conn, 200, ~s<{"id": "340"}>)
15+
end
16+
17+
Application.put_env(:sentry, :dsn, "http://public:secret@localhost:#{bypass.port}/1")
18+
Application.put_env(:sentry, :included_environments, [:test])
19+
Application.put_env(:sentry, :environment_name, :test)
20+
21+
assert {:ok, _} = Sentry.capture_exception(%RuntimeError{message: "error"}, [event_source: :plug])
22+
assert :excluded = Sentry.capture_exception(%ArithmeticError{message: "error"}, [event_source: :plug])
23+
end
324
end

test/support/test_filter.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
defmodule Sentry.TestFilter do
2+
@behaviour Sentry.EventFilter
3+
4+
def exclude_exception?(%ArithmeticError{}, :plug), do: true
5+
def exclude_exception?(_, _), do: false
6+
end

0 commit comments

Comments
 (0)