Current section

22 Versions

Jump to

Compare versions

8 files changed
+185 additions
-129 deletions
  @@ -0,0 +1,15 @@
1 + # Changelog
2 +
3 + ## Unreleased
4 +
5 + ## 2.1.0
6 +
7 + ### Fixed
8 + - Add support for poison 4.0 by loosening version requirement [#102](https://github.com/bugsnag-elixir/bugsnag-elixir/pull/102)
9 + - Removed warning on Elixir 1.10 [#92](https://github.com/bugsnag-elixir/bugsnag-elixir/pull/92)
10 + - Removed deprecated Supervisor.Spec
11 + - Moved to using `extra_applications`
12 +
13 + ### Removed
14 + - Removed Bugsnag.json_library/0 [#101](https://github.com/bugsnag-elixir/bugsnag-elixir/pull/101)
15 + - Removed Bugsnag.should_notify/2
  @@ -1,31 +1,38 @@
1 - # Bugsnag Elixir [![Build Status](https://travis-ci.org/jarednorman/bugsnag-elixir.svg?branch=master)](https://travis-ci.org/jarednorman/bugsnag-elixir)
1 + # Bugsnag Elixir
2 + [![Elixir CI](https://github.com/bugsnag-elixir/bugsnag-elixir/workflows/Elixir%20CI/badge.svg)](https://github.com/bugsnag-elixir/bugsnag-elixir/actions)
3 + [![Bugsnag version](https://img.shields.io/hexpm/v/bugsnag.svg)](https://hex.pm/packages/bugsnag)
4 + [![Hex.pm](https://img.shields.io/hexpm/dt/bugsnag.svg)](https://hex.pm/packages/bugsnag)
2 5
3 6 Capture exceptions and send them to the [Bugsnag](https://www.bugsnag.com/) API!
4 7
5 8 đź”— See also: [Plugsnag], to snag exceptions in your Phoenix application.
6 9
7 - [Plugsnag]: https://github.com/jarednorman/plugsnag
10 + [Plugsnag]: https://github.com/bugsnag-elixir/plugsnag
8 11
9 12 <!-- MarkdownTOC autolink="true" bracket="round" levels="1,2,3" -->
10 13
11 - - [Installation](#installation)
12 - - [Configuration](#configuration)
13 - - [Example](#example)
14 - - [`api_key`](#api_key)
15 - - [`release_stage`](#release_stage)
16 - - [`notify_release_stages`](#notify_release_stages)
17 - - [`hostname`](#hostname)
18 - - [`app_type`](#app_type)
19 - - [`app_version`](#app_version)
20 - - [`sanitizer`](#sanitizer)
21 - - [`in_project`](#in_project)
22 - - [`endpoint_url`](#endpoint_url)
23 - - [`use_logger`](#use_logger)
24 - - [`exception_filter`](#exception_filter)
25 - - [`json_library`](#json_library)
26 - - [Usage](#usage)
27 - - [Manual Reporting](#manual-reporting)
28 - - [Reporting Options](#reporting-options)
14 + - [Bugsnag Elixir](#bugsnag-elixir)
15 + - [Installation](#installation)
16 + - [Configuration](#configuration)
17 + - [Example](#example)
18 + - [`api_key`](#api_key)
19 + - [`release_stage`](#release_stage)
20 + - [`notify_release_stages`](#notify_release_stages)
21 + - [`hostname`](#hostname)
22 + - [`app_type`](#app_type)
23 + - [`app_version`](#app_version)
24 + - [`sanitizer`](#sanitizer)
25 + - [`in_project`](#in_project)
26 + - [String Matching](#string-matching)
27 + - [Regex Matching](#regex-matching)
28 + - [Custom Function](#custom-function)
29 + - [`endpoint_url`](#endpoint_url)
30 + - [`use_logger`](#use_logger)
31 + - [`exception_filter`](#exception_filter)
32 + - [`json_library`](#json_library)
33 + - [Usage](#usage)
34 + - [Manual Reporting](#manual-reporting)
35 + - [Reporting Options](#reporting-options)
29 36
30 37 <!-- /MarkdownTOC -->
31 38
  @@ -36,10 +43,10 @@ Capture exceptions and send them to the [Bugsnag](https://www.bugsnag.com/) API!
36 43 # mix.exs
37 44 defp deps do
38 45 [
39 - {:bugsnag, "~> 2.0.0"},
46 + {:bugsnag, "~> 2.1.0"},
40 47 # pick ONE of these JSON encoding libraries:
41 48 {:jason, "~> 1.0"},
42 - {:posion, "~> 3.0"}
49 + {:poison, "~> 4.0"}
43 50 ]
44 51 end
45 52 ```
  @@ -302,7 +309,7 @@ The following options allow adding more data to the report:
302 309 * `name` - Full name of the user
303 310 * `email` - Email address of the user
304 311 * `os_version` — Sets the reported OS version of the error
305 - * `stacktrace` — Allows passing in a stack trace, e.g. from `System.stacktrace`
312 + * `stacktrace` — Allows passing in a stack trace, e.g. from `__STACKTRACE__`
306 313 * `metadata` - Map of arbitrary metadata to include with the report
307 314
308 315 [See the Bugsnag docs][2] for more information on these fields.
  @@ -1,14 +1,15 @@
1 1 {<<"app">>,<<"bugsnag">>}.
2 2 {<<"build_tools">>,[<<"mix">>]}.
3 3 {<<"description">>,<<"An Elixir interface to the Bugsnag API.">>}.
4 - {<<"elixir">>,<<"~> 1.3">>}.
4 + {<<"elixir">>,<<"~> 1.6">>}.
5 5 {<<"files">>,
6 - [<<"lib">>,<<"lib/bugsnag.ex">>,<<"lib/bugsnag">>,
7 - <<"lib/bugsnag/logger.ex">>,<<"lib/bugsnag/payload.ex">>,
8 - <<".formatter.exs">>,<<"mix.exs">>,<<"README.md">>,<<"LICENSE">>]}.
6 + [<<"lib">>,<<"lib/bugsnag">>,<<"lib/bugsnag/logger.ex">>,
7 + <<"lib/bugsnag/reporter.ex">>,<<"lib/bugsnag/payload.ex">>,
8 + <<"lib/bugsnag.ex">>,<<".formatter.exs">>,<<"mix.exs">>,<<"README.md">>,
9 + <<"LICENSE">>,<<"CHANGELOG.md">>]}.
9 10 {<<"licenses">>,[<<"MIT">>]}.
10 11 {<<"links">>,
11 - [{<<"github">>,<<"https://github.com/jarednorman/bugsnag-elixir">>}]}.
12 + [{<<"github">>,<<"https://github.com/bugsnag-elixir/bugsnag-elixir">>}]}.
12 13 {<<"name">>,<<"bugsnag">>}.
13 14 {<<"requirements">>,
14 15 [[{<<"app">>,<<"httpoison">>},
  @@ -25,5 +26,5 @@
25 26 {<<"name">>,<<"poison">>},
26 27 {<<"optional">>,true},
27 28 {<<"repository">>,<<"hexpm">>},
28 - {<<"requirement">>,<<"~> 1.5 or ~> 2.0 or ~> 3.0">>}]]}.
29 - {<<"version">>,<<"2.0.0">>}.
29 + {<<"requirement">>,<<">= 1.5.0">>}]]}.
30 + {<<"version">>,<<"2.1.0">>}.
  @@ -1,105 +1,62 @@
1 1 defmodule Bugsnag do
2 2 use Application
3 - import Supervisor.Spec
4 3 require Logger
5 4
6 - alias Bugsnag.Payload
7 -
8 - @notify_url "https://notify.bugsnag.com"
9 - @request_headers [{"Content-Type", "application/json"}]
10 -
11 5 def start(_type, _args) do
12 - config =
13 - default_config()
14 - |> Keyword.merge(Application.get_all_env(:bugsnag))
15 - |> Enum.map(fn {k, v} -> {k, eval_config(v)} end)
16 - |> Keyword.update!(:notify_release_stages, fn stages ->
17 - if(is_binary(stages), do: String.split(stages, ","), else: stages)
18 - end)
6 + config = load_config()
19 7
20 8 if to_string(config[:use_logger]) == "true" do
21 9 :error_logger.add_report_handler(Bugsnag.Logger)
22 10 end
23 11
24 12 # Update Application config with evaluated configuration
25 - # It's needed for use in Bugsnag.Payload, could be removed
26 - # by using GenServer instead of this kind of app.
13 + # It's needed for use in Bugsnag.Payload
27 14 Enum.each(config, fn {k, v} ->
28 15 Application.put_env(:bugsnag, k, v)
29 16 end)
30 17
31 - if !config[:api_key] and reported_stage() do
18 + if is_nil(config[:api_key]) and reported_stage?() do
32 19 Logger.warn("Bugsnag api_key is not configured, errors will not be reported")
33 20 end
34 21
35 22 children = [
36 - supervisor(Task.Supervisor, [[name: Bugsnag.TaskSupervisor, restart: :transient]])
23 + {Task.Supervisor, name: Bugsnag.TaskSupervisor}
37 24 ]
38 25
39 - opts = [strategy: :one_for_one, name: Bugsnag.Supervisor]
40 - Supervisor.start_link(children, opts)
26 + Supervisor.start_link(children, strategy: :one_for_one, name: Bugsnag.Supervisor)
41 27 end
42 28
43 29 @doc """
44 30 Report the exception without waiting for the result of the Bugsnag API call.
45 - (I.e. this might fail silently)
31 +
32 + (i.e. this might fail silently)
46 33 """
47 - def report(exception, options \\ []) do
48 - Task.Supervisor.start_child(Bugsnag.TaskSupervisor, __MODULE__, :sync_report, [
49 - exception,
50 - add_stacktrace(options)
51 - ])
52 - end
34 + @spec report(exception :: term(), opts :: list()) :: :ok | {:error, :cannot_start_task}
35 + defdelegate report(exception, opts \\ []), to: Bugsnag.Reporter
53 36
54 - def json_library(), do: Application.get_env(:bugsnag, :json_library, Jason)
37 + @doc "Report the exception and wait for the result. Returns `:ok` or `{:error, reason}`."
38 + @spec sync_report(exception :: term(), opts :: list()) :: :ok | {:error, reason :: term()}
39 + defdelegate sync_report(exception, opts \\ []), to: Bugsnag.Reporter
55 40
56 - defp add_stacktrace(options) do
57 - if options[:stacktrace], do: options, else: put_in(options[:stacktrace], System.stacktrace())
58 - end
59 -
60 - @doc "Report the exception and wait for the result. Returns `ok` or `{:error, reason}`."
61 - def sync_report(exception, options \\ []) do
62 - stacktrace = options[:stacktrace] || System.stacktrace()
63 -
64 - if should_notify(exception, stacktrace) do
65 - if Application.get_env(:bugsnag, :api_key) do
66 - exception
67 - |> Payload.new(stacktrace, options)
68 - |> Payload.encode()
69 - |> send_notification
70 - |> case do
71 - {:ok, %{status_code: 200}} -> :ok
72 - {:ok, %{status_code: other}} -> {:error, "status_#{other}"}
73 - {:error, %{reason: reason}} -> {:error, reason}
74 - _ -> {:error, :unknown}
75 - end
76 - else
77 - Logger.warn("Bugsnag api_key is not configured, error not reported")
78 - {:error, %{reason: "API key is not configured"}}
79 - end
80 - else
81 - {:ok, :not_sent}
82 - end
83 - end
84 -
85 - defp send_notification(body) do
86 - HTTPoison.post(notify_url(), body, @request_headers)
87 - end
88 -
89 - defp reported_stage() do
41 + defp reported_stage?() do
90 42 release_stage = Application.get_env(:bugsnag, :release_stage)
91 43 notify_stages = Application.get_env(:bugsnag, :notify_release_stages)
92 44 release_stage && is_list(notify_stages) && Enum.member?(notify_stages, release_stage)
93 45 end
94 46
95 - def should_notify(exception, stacktrace) do
96 - reported_stage() && test_filter(exception_filter(), exception, stacktrace)
47 + defp load_config do
48 + default_config()
49 + |> Keyword.merge(Application.get_all_env(:bugsnag))
50 + |> Enum.map(fn {k, v} -> {k, eval_config(v)} end)
51 + |> Keyword.update!(:notify_release_stages, fn stages ->
52 + if(is_binary(stages), do: String.split(stages, ","), else: stages)
53 + end)
97 54 end
98 55
99 56 defp default_config do
100 57 [
101 58 api_key: {:system, "BUGSNAG_API_KEY", nil},
102 - endpoint_url: {:system, "BUGSNAG_ENDPOINT_URL", @notify_url},
59 + endpoint_url: {:system, "BUGSNAG_ENDPOINT_URL", "https://notify.bugsnag.com"},
103 60 use_logger: {:system, "BUGSNAG_USE_LOGGER", true},
104 61 release_stage: {:system, "BUGSNAG_RELEASE_STAGE", "production"},
105 62 notify_release_stages: {:system, "BUGSNAG_NOTIFY_RELEASE_STAGES", ["production"]},
  @@ -122,24 +79,4 @@ defmodule Bugsnag do
122 79 end
123 80
124 81 defp eval_config(value), do: value
125 -
126 - defp notify_url do
127 - Application.get_env(:bugsnag, :endpoint_url, @notify_url)
128 - end
129 -
130 - defp exception_filter() do
131 - Application.get_env(:bugsnag, :exception_filter)
132 - end
133 -
134 - defp test_filter(nil, _, _), do: true
135 -
136 - defp test_filter(module, exception, stacktrace) do
137 - try do
138 - module.should_notify(exception, stacktrace)
139 - rescue
140 - _ ->
141 - # Swallowing error in order to avoid exception loops
142 - true
143 - end
144 - end
145 82 end
  @@ -29,14 +29,8 @@ defmodule Bugsnag.Logger do
29 29 end
30 30 rescue
31 31 ex ->
32 - error_type =
33 - Exception.normalize(:error, ex).__struct__
34 - |> Atom.to_string()
35 - |> String.replace(~r{\AElixir\.}, "")
36 -
37 - reason = Exception.message(ex)
38 -
39 - Logger.warn("Unable to notify Bugsnag #{error_type}: #{reason}")
32 + error_message = Exception.format(:error, ex)
33 + Logger.warn("Unable to notify Bugsnag. #{error_message}")
40 34 end
41 35
42 36 {:ok, state}
Loading more files…