Skip to content

Commit

Permalink
feat!: improves clarity of manual handling/reporting functions
Browse files Browse the repository at this point in the history
  • Loading branch information
grzuy committed Oct 15, 2024
1 parent e422c12 commit d6ec996
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 55 deletions.
85 changes: 51 additions & 34 deletions lib/tower.ex
Original file line number Diff line number Diff line change
Expand Up @@ -142,22 +142,22 @@ defmodule Tower do
- [`TowerSlack`](https://hexdocs.pm/tower_slack) ([`tower_slack`](https://hex.pm/packages/tower_slack))
- and properly set the `config :tower, :reporters, [...]` configuration key
## Manual handling
## Manual reporting
If either, for whatever reason when using automated exception handling, an exception condition is
not reaching Tower handling, or you just need or want to manually handle possible errors, you can
manually ask Tower to handle exceptions, throws or exits.
not reaching Tower handling, or you just need or want to manually handle and report errors, you can
manually ask Tower to report exceptions, throws or exits.
try do
# possibly crashing code
rescue
exception ->
Tower.handle_exception(exception, __STACKTRACE__)
Tower.report_exception(exception, __STACKTRACE__)
catch
:throw, value ->
Tower.handle_throw(value, __STACKTRACE__)
Tower.report_throw(value, __STACKTRACE__)
:exit, reason when not Tower.is_normal_exit(reason) ->
Tower.handle_exit(reason, __STACKTRACE__)
Tower.report_exit(reason, __STACKTRACE__)
end
or more generally
Expand All @@ -166,7 +166,7 @@ defmodule Tower do
# possibly crashing code
catch
kind, reason ->
Tower.handle_caught(kind, reason, __STACKTRACE__)
Tower.report(kind, reason, __STACKTRACE__)
end
which will in turn call the appropriate function based on the caught `kind` and `reason` values
Expand Down Expand Up @@ -273,7 +273,7 @@ defmodule Tower do
Detaches the handlers.
That means it stops the automatic handling of errors.
You can still manually call `Tower` `handle_*` functions and reporters will be informed about
You can still manually call `Tower` `report_*` functions and reporters will be informed about
those events.
"""
@spec detach() :: :ok | {:error, reason :: term()}
Expand All @@ -286,7 +286,7 @@ defmodule Tower do
end

@doc """
Asks Tower to handle a manually caught error.
Asks Tower to report a manually handled error.
## Example
Expand All @@ -295,7 +295,7 @@ defmodule Tower do
catch
# Note this will also catch and handle normal (`:normal` and `:shutdown`) exits
kind, reason ->
Tower.handle_caught(kind, reason, __STACKTRACE__)
Tower.report(kind, reason, __STACKTRACE__)
end
## Options
Expand All @@ -308,103 +308,120 @@ defmodule Tower do
up to each reporter if and how to handle it.
"""
@spec handle_caught(Exception.kind(), Event.reason(), Exception.stacktrace(), Keyword.t()) ::
@spec report(Exception.kind(), Event.reason(), Exception.stacktrace(), Keyword.t()) ::
:ok
def handle_caught(kind, reason, stacktrace, options \\ []) do
def report(kind, reason, stacktrace, options \\ []) do
Event.from_caught(kind, reason, stacktrace, options)
|> report_event()
end

@deprecated "Use Tower.report/3,4 instead."
defdelegate handle_caught(kind, reason, stacktrace, options \\ []), to: __MODULE__, as: :report

@doc """
Asks Tower to handle a manually caught exception.
Asks Tower to report a manually handled exception.
## Example
try do
# possibly crashing code
rescue
exception ->
Tower.handle_exception(exception, __STACKTRACE__)
Tower.report_exception(exception, __STACKTRACE__)
end
## Options
* Accepts same options as `handle_caught/4#options`.
* Accepts same options as `report/4#options`.
"""
@spec handle_exception(Exception.t(), Exception.stacktrace(), Keyword.t()) :: :ok
def handle_exception(exception, stacktrace, options \\ [])
@spec report_exception(Exception.t(), Exception.stacktrace(), Keyword.t()) :: :ok
def report_exception(exception, stacktrace, options \\ [])
when is_exception(exception) and is_list(stacktrace) do
unless exception.__struct__ in ignored_exceptions() do
Event.from_exception(exception, stacktrace, options)
|> report_event()
end
end

@deprecated "Use Tower.report_exception/2,3 instead."
defdelegate handle_exception(exception, stacktrace, options \\ []),
to: __MODULE__,
as: :report_exception

@doc """
Asks Tower to handle a manually caught throw.
Asks Tower to report a manually handled throw.
## Example
try do
# possibly throwing code
catch
thrown_value ->
Tower.handle_throw(thrown_value, __STACKTRACE__)
Tower.report_throw(thrown_value, __STACKTRACE__)
end
## Options
* Accepts same options as `handle_caught/4#options`.
* Accepts same options as `report/4#options`.
"""
@spec handle_throw(term(), Exception.stacktrace(), Keyword.t()) :: :ok
def handle_throw(reason, stacktrace, options \\ []) do
@spec report_throw(term(), Exception.stacktrace(), Keyword.t()) :: :ok
def report_throw(reason, stacktrace, options \\ []) do
Event.from_throw(reason, stacktrace, options)
|> report_event()
end

@deprecated "Use Tower.report_throw/2,3 instead."
defdelegate handle_throw(reason, stacktrace, options \\ []), to: __MODULE__, as: :report_throw

@doc """
Asks Tower to handle a manually caught exit.
Asks Tower to report a manually handled exit.
## Example
try do
# possibly exiting code
catch
:exit, reason when not Tower.is_normal_exit(reason) ->
Tower.handle_exit(reason, __STACKTRACE__)
Tower.report_exit(reason, __STACKTRACE__)
end
## Options
* Accepts same options as `handle_caught/4#options`.
* Accepts same options as `report/4#options`.
"""
@spec handle_exit(term(), Exception.stacktrace(), Keyword.t()) :: :ok
def handle_exit(reason, stacktrace, options \\ []) do
@spec report_exit(term(), Exception.stacktrace(), Keyword.t()) :: :ok
def report_exit(reason, stacktrace, options \\ []) do
Event.from_exit(reason, stacktrace, options)
|> report_event()
end

@deprecated "Use Tower.report_exit/2,3 instead."
defdelegate handle_exit(reason, stacktrace, options \\ []), to: __MODULE__, as: :report_exit

@doc """
Asks Tower to handle a message of certain level.
Asks Tower to report a message of certain level.
## Examples
Tower.handle_message(:emergency, "System is falling apart")
Tower.report_message(:emergency, "System is falling apart")
Tower.handle_message(:error, "Unknown error has occurred", metadata: %{any_key: "here"})
Tower.report_message(:error, "Unknown error has occurred", metadata: %{any_key: "here"})
Tower.handle_message(:info, "Just something interesting", metadata: %{interesting: "additional data"})
Tower.report_message(:info, "Just something interesting", metadata: %{interesting: "additional data"})
## Options
* Accepts same options as `handle_caught/4#options`.
* Accepts same options as `report/4#options`.
"""
@spec handle_message(Event.level(), term(), Keyword.t()) :: :ok
def handle_message(level, message, options \\ []) do
@spec report_message(Event.level(), term(), Keyword.t()) :: :ok
def report_message(level, message, options \\ []) do
Event.from_message(level, message, options)
|> report_event()
end

@deprecated "Use Tower.report_message/2,3 instead."
defdelegate handle_message(level, message, options \\ []), to: __MODULE__, as: :report_message

@doc """
Compares event level severity.
Expand Down
2 changes: 1 addition & 1 deletion lib/tower/bandit_exception_handler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ defmodule Tower.BanditExceptionHandler do
stacktrace: stacktrace
}) do
Exception.normalize(:error, reason, stacktrace)
|> Tower.handle_exception(stacktrace, plug_conn: conn)
|> Tower.report_exception(stacktrace, plug_conn: conn)
end

defp handle_event_metadata(event_metadata) do
Expand Down
16 changes: 8 additions & 8 deletions lib/tower/logger_handler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -46,42 +46,42 @@ defmodule Tower.LoggerHandler do
%{level: :error, meta: %{crash_reason: {exception, stacktrace}}} = log_event
)
when is_exception(exception) and is_list(stacktrace) do
Tower.handle_exception(exception, stacktrace, log_event: log_event)
Tower.report_exception(exception, stacktrace, log_event: log_event)
end

defp handle_log_event(
%{level: :error, meta: %{crash_reason: {{:nocatch, reason}, stacktrace}}} = log_event
)
when is_list(stacktrace) do
Tower.handle_throw(reason, stacktrace, log_event: log_event)
Tower.report_throw(reason, stacktrace, log_event: log_event)
end

defp handle_log_event(
%{level: :error, meta: %{crash_reason: {exit_reason, stacktrace}}} = log_event
)
when is_list(stacktrace) do
Tower.handle_exit(exit_reason, stacktrace, log_event: log_event)
Tower.report_exit(exit_reason, stacktrace, log_event: log_event)
end

defp handle_log_event(%{level: :error, meta: %{crash_reason: exit_reason}} = log_event) do
Tower.handle_exit(exit_reason, [], log_event: log_event)
Tower.report_exit(exit_reason, [], log_event: log_event)
end

defp handle_log_event(%{level: level, msg: {:string, reason_chardata}} = log_event) do
if should_handle?(level) do
Tower.handle_message(level, IO.chardata_to_string(reason_chardata), log_event: log_event)
Tower.report_message(level, IO.chardata_to_string(reason_chardata), log_event: log_event)
end
end

defp handle_log_event(%{level: level, msg: {:report, report}} = log_event) do
if should_handle?(level) do
Tower.handle_message(level, report, log_event: log_event)
Tower.report_message(level, report, log_event: log_event)
end
end

defp handle_log_event(%{level: level, msg: {format, args}} = log_event) when is_list(args) do
if should_handle?(level) do
Tower.handle_message(level, formatted_message(format, args), log_event: log_event)
Tower.report_message(level, formatted_message(format, args), log_event: log_event)
end
end

Expand All @@ -90,7 +90,7 @@ defmodule Tower.LoggerHandler do
safe_log(:warning, "[Tower.LoggerHandler] UNRECOGNIZED LOG EVENT log_event=#{log_event_str}")

if should_handle?(level) do
Tower.handle_message(
Tower.report_message(
level,
"Unrecognized log event",
log_event: log_event,
Expand Down
2 changes: 1 addition & 1 deletion lib/tower/oban_exception_handler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ defmodule Tower.ObanExceptionHandler do
%{kind: kind, reason: reason, stacktrace: stacktrace},
_handler_config
) do
Tower.handle_caught(kind, reason, stacktrace)
Tower.report(kind, reason, stacktrace)
end

def handle_event(
Expand Down
2 changes: 1 addition & 1 deletion test/tower/tower_plug_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ defmodule TowerPlugTest do
end

test "reports message plug_conn manually" do
Tower.handle_message(
Tower.report_message(
:info,
"Something interesting",
plug_conn: Plug.Conn.assign(%Plug.Conn{}, :hello, "world")
Expand Down
20 changes: 10 additions & 10 deletions test/tower_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ defmodule TowerTest do
end

test "reports message manually" do
Tower.handle_message(:info, "Something interesting", metadata: %{something: "else"})
Tower.report_message(:info, "Something interesting", metadata: %{something: "else"})

assert_eventually(
[
Expand Down Expand Up @@ -337,7 +337,7 @@ defmodule TowerTest do
raise "an error"
catch
kind, reason ->
Tower.handle_caught(kind, reason, __STACKTRACE__)
Tower.report(kind, reason, __STACKTRACE__)
end
end)

Expand All @@ -363,7 +363,7 @@ defmodule TowerTest do
raise "an error"
rescue
e ->
Tower.handle_exception(e, __STACKTRACE__)
Tower.report_exception(e, __STACKTRACE__)
end
end)

Expand Down Expand Up @@ -391,7 +391,7 @@ defmodule TowerTest do
throw("error")
catch
kind, reason ->
Tower.handle_caught(kind, reason, __STACKTRACE__)
Tower.report(kind, reason, __STACKTRACE__)
end
end)

Expand All @@ -417,7 +417,7 @@ defmodule TowerTest do
throw("error")
catch
x ->
Tower.handle_throw(x, __STACKTRACE__)
Tower.report_throw(x, __STACKTRACE__)
end
end)

Expand Down Expand Up @@ -445,7 +445,7 @@ defmodule TowerTest do
exit(:abnormal)
catch
kind, reason ->
Tower.handle_caught(kind, reason, __STACKTRACE__)
Tower.report(kind, reason, __STACKTRACE__)
end
end)

Expand All @@ -471,7 +471,7 @@ defmodule TowerTest do
exit(:normal)
catch
:exit, reason when not Tower.is_normal_exit(reason) ->
Tower.handle_exit(reason, __STACKTRACE__)
Tower.report_exit(reason, __STACKTRACE__)
end
end)

Expand All @@ -480,7 +480,7 @@ defmodule TowerTest do
exit(:shutdown)
catch
:exit, reason when not Tower.is_normal_exit(reason) ->
Tower.handle_exit(reason, __STACKTRACE__)
Tower.report_exit(reason, __STACKTRACE__)
end
end)

Expand All @@ -489,7 +489,7 @@ defmodule TowerTest do
exit({:shutdown, 0})
catch
:exit, reason when not Tower.is_normal_exit(reason) ->
Tower.handle_exit(reason, __STACKTRACE__)
Tower.report_exit(reason, __STACKTRACE__)
end
end)

Expand All @@ -502,7 +502,7 @@ defmodule TowerTest do
exit(:abnormal)
catch
:exit, reason when not Tower.is_normal_exit(reason) ->
Tower.handle_exit(reason, __STACKTRACE__)
Tower.report_exit(reason, __STACKTRACE__)
end
end)

Expand Down

0 comments on commit d6ec996

Please sign in to comment.