Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .credo.exs
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,6 @@
{Credo.Check.Warning.BoolOperationOnSameValues},
{Credo.Check.Warning.IExPry},
{Credo.Check.Warning.IoInspect},
{Credo.Check.Warning.NameRedeclarationByAssignment},
{Credo.Check.Warning.NameRedeclarationByCase},
{Credo.Check.Warning.NameRedeclarationByDef},
{Credo.Check.Warning.NameRedeclarationByFn},
{Credo.Check.Warning.OperationOnSameValues},
{Credo.Check.Warning.OperationWithConstantResult},
{Credo.Check.Warning.UnusedEnumOperation},
Expand Down
24 changes: 6 additions & 18 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,38 +1,26 @@
language: elixir
elixir:
- 1.3.4
- 1.4
- 1.5
- 1.6
- 1.7
otp_release:
- 18.3
- 19.3
- 20.2
- 21.0
env:
- STRICT=true
- STRICT=false
matrix:
exclude:
- elixir: 1.6
env: STRICT=false
- elixir: 1.3.4
env: STRICT=true
- elixir: 1.4
- otp_release: 20.2
env: STRICT=true
- elixir: 1.5
env: STRICT=true
- elixir: 1.6
otp_release: 18.3
- elixir: 1.3.4
otp_release: 20.2
- otp_release: 21.0
env: STRICT=false
notifications:
email:
- mitch@rokkincat.com
script:
- if [ "$STRICT" = "true" ]; then mix compile --warnings-as-errors; fi
- mix test
- mix credo
- if [ "$STRICT" = "true" ]; then mix dialyzer; fi
- if [ "$STRICT" = "true" ]; then travis_wait mix dialyzer; fi
- if [ "$STRICT" = "true" ]; then mix format --dry-run --check-formatted; fi
cache:
directories:
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

## master

## 7.0.0 (2018-09-05)

* Enhancements
* Replace Poison with configurable JSON library
* Implement `Sentry.LoggerBackend`

* Breaking Changes
* Require Elixir 1.7+
* Remove `Sentry.Logger`

## 6.4.2 (2018-09-05)

* Enhancements
Expand Down
29 changes: 19 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,25 @@ The Official Sentry Client for Elixir which provides a simple API to capture exc

[Documentation](https://hexdocs.pm/sentry/readme.html)

## Note on upgrading from Sentry 6.x to 7.x

Elixir 1.7 and Erlang/OTP 21 significantly changed how errors are transmitted (See "Erlang/OTP logger integration" [here](https://elixir-lang.org/blog/2018/07/25/elixir-v1-7-0-released/)). Sentry integrated heavily with Erlang's `:error_logger` module, but it is no longer the suggested path towards handling errors.

Sentry 7.x requires Elixir 1.7 and Sentry 6.x will be maintained for applications running prior versions. Documentation for Sentry 6.x can be found [here](https://hexdocs.pm/sentry/6.4.2/readme.html).

If you would like to upgrade a project to use Sentry 7.x, see [here](https://gist.github.com/mitchellhenke/4ab6dd8d0ebeaaf9821fb625e0037a4d).

## Installation

To use Sentry with your projects, edit your mix.exs file to add it as a dependency and add the `:sentry` package to your applications:
To use Sentry with your projects, edit your mix.exs file and add it as a dependency. Sentry does not install a JSON library itself, and requires users to have one available. Sentry will default to trying to use Jason for JSON operations, but can be configured to use other ones.

```elixir
defp application do
[applications: [:sentry, :logger]]
end

defp deps do
[{:sentry, "~> 6.4"}]
[
# ...
{:sentry, "~> 7.0"},
{:jason, "~> 1.1"},
]
end
```

Expand All @@ -41,9 +49,9 @@ More information on why this may be necessary can be found here: https://github.

### Capture Crashed Process Exceptions

This library comes with an extension to capture all error messages that the Plug handler might not. This is based on the Erlang [error_logger](http://erlang.org/doc/man/error_logger.html).
This library comes with an extension to capture all error messages that the Plug handler might not. This is based on [Logger.Backend](https://hexdocs.pm/logger/Logger.html#module-backends).

To set this up, add `:ok = :error_logger.add_report_handler(Sentry.Logger)` to your application's start function. Example:
To set this up, add `{:ok, _} = Logger.add_backend(Sentry.LoggerBackend)` to your application's start function. Example:

```elixir
def start(_type, _opts) do
Expand All @@ -54,7 +62,7 @@ def start(_type, _opts) do

opts = [strategy: :one_for_one, name: MyApp.Supervisor]

:ok = :error_logger.add_report_handler(Sentry.Logger)
{:ok, _} = Logger.add_backend(Sentry.LoggerBackend)

Supervisor.start_link(children, opts)
end
Expand All @@ -69,7 +77,7 @@ try do
ThisWillError.reall()
rescue
my_exception ->
Sentry.capture_exception(my_exception, [stacktrace: System.stacktrace(), extra: %{extra: information}])
Sentry.capture_exception(my_exception, [stacktrace: __STACKTRACE__, extra: %{extra: information}])
end
```

Expand Down Expand Up @@ -109,6 +117,7 @@ For optional settings check the [docs](https://hexdocs.pm/sentry/readme.html).
| `source_code_exclude_patterns` | False | `[~r"/_build/", ~r"/deps/", ~r"/priv/"]` | |
| `source_code_path_pattern` | False | `"**/*.ex"` | |
| `filter` | False | | Module where the filter rules are defined (see [Filtering Exceptions](https://hexdocs.pm/sentry/Sentry.html#module-filtering-exceptions)) |
| `json_library` | False | `Jason` | |

An example production config might look like this:

Expand Down
3 changes: 3 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ config :sentry,
included_environments: [:test],
client: Sentry.TestClient,
hackney_opts: [recv_timeout: 50]

config :ex_unit,
assert_receive_timeout: 500
32 changes: 30 additions & 2 deletions lib/sentry.ex
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,17 @@ defmodule Sentry do
)
]

opts = [strategy: :one_for_one, name: Sentry.Supervisor]
validate_json_config!()

opts = [strategy: :one_for_one, name: Sentry.Supervisor]
Supervisor.start_link(children, opts)
end

@doc """
Parses and submits an exception to Sentry if current environment is in included_environments.
`opts` argument is passed as the second argument to `Sentry.send_event/2`.
"""
@spec capture_exception(Exception.t(), Keyword.t()) :: send_result
@spec capture_exception(Exception.t() | atom() | {atom(), atom()}, Keyword.t()) :: send_result
def capture_exception(exception, opts \\ []) do
filter_module = Config.filter()
{source, opts} = Keyword.pop(opts, :event_source)
Expand Down Expand Up @@ -164,4 +165,31 @@ defmodule Sentry do
:ignored
end
end

defp validate_json_config!() do
case Config.json_library() do
nil ->
raise ArgumentError.exception("nil is not a valid :json_library configuration")

library ->
try do
with {:ok, %{}} <- library.decode("{}"),
{:ok, "{}"} <- library.encode(%{}) do
:ok
else
_ ->
raise ArgumentError.exception(
"configured :json_library #{inspect(library)} does not implement decode/1 and encode/1"
)
end
rescue
UndefinedFunctionError ->
reraise ArgumentError.exception("""
configured :json_library #{inspect(library)} is not available or does not implement decode/1 and encode/1.
Do you need to add #{inspect(library)} to your mix.exs?
"""),
__STACKTRACE__
end
end
end
end
10 changes: 7 additions & 3 deletions lib/sentry/client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,10 @@ defmodule Sentry.Client do
end

defp encode_and_send(event, result) do
json_library = Config.json_library()

render_event(event)
|> Poison.encode()
|> json_library.encode()
|> case do
{:ok, body} ->
do_send_event(event, body, result)
Expand Down Expand Up @@ -156,15 +158,17 @@ defmodule Sentry.Client do
Hackney options can be set via the `hackney_opts` configuration option.
"""
@spec request(String.t(), list({String.t(), String.t()}), String.t()) ::
{:ok, String.t()} | :error
{:ok, String.t()} | {:error, term()}
def request(url, headers, body) do
json_library = Config.json_library()

hackney_opts =
Config.hackney_opts()
|> Keyword.put_new(:pool, @hackney_pool_name)

with {:ok, 200, _, client} <- :hackney.request(:post, url, headers, body, hackney_opts),
{:ok, body} <- :hackney.body(client),
{:ok, json} <- Poison.decode(body) do
{:ok, json} <- json_library.decode(body) do
{:ok, Map.get(json, "id")}
else
{:ok, status, headers, client} ->
Expand Down
4 changes: 4 additions & 0 deletions lib/sentry/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ defmodule Sentry.Config do
get_config(:report_deps, default: true, check_dsn: false)
end

def json_library do
get_config(:json_library, default: Jason, check_dsn: false)
end

defp get_config(key, opts \\ []) when is_atom(key) do
default = Keyword.get(opts, :default)
check_dsn = Keyword.get(opts, :check_dsn, true)
Expand Down
3 changes: 1 addition & 2 deletions lib/sentry/event.ex
Original file line number Diff line number Diff line change
Expand Up @@ -246,10 +246,9 @@ defmodule Sentry.Event do
@spec args_from_stacktrace(Exception.stacktrace()) :: map()
def args_from_stacktrace([{_m, _f, a, _} | _]) when is_list(a) do
Enum.with_index(a)
|> Enum.map(fn {arg, index} ->
|> Enum.into(%{}, fn {arg, index} ->
{"arg#{index}", inspect(arg)}
end)
|> Enum.into(%{})
end

def args_from_stacktrace(_), do: %{}
Expand Down
126 changes: 0 additions & 126 deletions lib/sentry/logger.ex

This file was deleted.

Loading