Current section
22 Versions
Jump to
Current section
22 Versions
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 [](https://travis-ci.org/jarednorman/bugsnag-elixir) |
| 1 | + # Bugsnag Elixir |
| 2 | + [](https://github.com/bugsnag-elixir/bugsnag-elixir/actions) |
| 3 | + [](https://hex.pm/packages/bugsnag) |
| 4 | + [](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…