Packages

appsignal

1.6.4
2.17.4 2.17.3 2.17.2 2.17.1 2.17.0 2.16.0 2.15.11 2.15.10 2.15.9 2.15.8 2.15.7 2.15.6 2.15.5 2.15.4 2.15.3 2.15.2 2.15.1 2.15.0 2.14.1 2.14.0 2.13.4 2.13.3 2.13.2 2.13.1 2.13.0 2.12.3 2.12.2 2.12.1 2.12.0 2.11.0 2.10.2 2.10.1 2.10.0 2.9.2 2.9.1 2.9.0 2.8.3 2.8.2 2.8.1 2.8.0 2.7.13 2.7.12 2.7.11 2.7.10 2.7.9 2.7.8 2.7.7 2.7.6 2.7.5 2.7.4 2.7.3 2.7.2 2.7.1 2.7.0 2.7.0-beta.1 2.6.1 2.6.0 2.5.3 2.5.2 2.5.1 2.5.0 2.4.3 2.4.2 2.4.1 2.4.0 2.3.1 2.3.0 2.2.19 2.2.18 2.2.17 2.2.16 2.2.15 2.2.14 2.2.13 2.2.12 retired 2.2.11 2.2.10 2.2.9 2.2.8 2.2.7 2.2.6 2.2.5 2.2.4 2.2.3 2.2.2 2.2.1 2.2.0 2.1.15 2.1.14 2.1.13 2.1.12 2.1.11 2.1.10 2.1.9 2.1.8 2.1.7 2.1.7-beta.1 2.1.6 2.1.5 2.1.4 2.1.3 2.1.2 2.1.1 2.1.0 2.0.8 2.0.7 2.0.6 2.0.6-beta.1 2.0.5 retired 2.0.4 retired 2.0.3 retired 2.0.2 retired 2.0.1 retired 2.0.0 retired 2.0.0-beta.11 2.0.0-beta.10 2.0.0-beta.9 2.0.0-beta.8 2.0.0-beta.7 2.0.0-beta.6 2.0.0-beta.5 2.0.0-beta.4 2.0.0-beta.3 2.0.0-beta.2 2.0.0-beta.1 2.0.0-alpha.7 2.0.0-alpha.6 2.0.0-alpha.5 2.0.0-alpha.4 2.0.0-alpha.3 2.0.0-alpha.2 2.0.0-alpha.1 1.13.5 1.13.4 1.13.3 1.13.2 1.13.1 1.13.0 1.13.0-beta.2 1.13.0-beta.1 1.12.1 1.12.0 1.12.0-beta.3 1.12.0-beta.2 1.12.0-beta.1 1.11.8 1.11.7 1.11.6 1.11.5 1.11.4 1.11.3 1.11.2 1.11.1 1.11.0 1.11.0-beta.1 1.10.13 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 1.10.0 1.9.4 1.9.3 1.9.2 1.9.1 1.9.1-beta.1 1.9.0 1.9.0-beta.1 1.9.0-alpha.1 1.8.2 1.8.1 1.8.1-beta.2 1.8.1-beta.1 1.8.0 1.7.2 1.7.1 1.7.0 1.7.0-beta.2 1.7.0-beta.1 1.7.0-alpha.4 1.7.0-alpha.3 1.7.0-alpha.2 1.7.0-alpha.1 1.6.7 1.6.6 1.6.6-beta.2 1.6.6-beta.1 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.6.0-beta.1 1.6.0-alpha.1 1.5.1 1.5.0 1.5.0-beta.2 1.5.0-beta.1 1.4.11-beta.1 1.4.10 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.4.0-alpha.1 1.3.6 1.3.6-beta.1 1.3.5 1.3.4 1.3.4-beta.1 1.3.3 1.3.2 1.3.1 1.3.0 1.3.0-beta.3 1.3.0-beta.2 1.3.0-beta.1 1.2.3 1.2.2 1.2.1 1.2.0 1.1.1 1.1.0 1.0.4 1.0.3 1.0.2 1.0.1 1.0.0 1.0.0-rc.1 0.13.0 0.12.3 0.12.2 0.12.1 0.12.0 0.11.6 0.11.5 0.11.4 0.11.3 0.11.2 0.11.1 0.11.0 0.10.0 0.9.2 0.9.1 0.9.0 0.8.0 0.7.1 0.7.0 0.6.2 0.6.1 0.6.0 0.5.0 0.4.0 0.3.0 0.2.0 0.1.0 0.0.9 0.0.8 0.0.7 0.0.6 0.0.5 0.0.4 0.0.3 0.0.2 0.0.1

Collects error and performance data from your Elixir applications and sends it to AppSignal

Current section

Files

Jump to
appsignal lib appsignal error_handler.ex
Raw

lib/appsignal/error_handler.ex

defmodule Appsignal.ErrorHandler do
@moduledoc """
Error handler which sends all crash reports to the AppSignal backend.
When you add `:appsignal` to your application's dependencies, this
error logger will automatically be installed. All processes that are
supervised, like GenServers, Tasks, Agents, Supervisored will be
monitored for crashes. In the case of a crash, the AppSignal error
handler collects error information and sends it to the backend.
"""
require Logger
alias Appsignal.{Transaction, Backtrace}
@doc """
Retrieve the last Appsignal.Transaction.t that the error logger picked up
"""
@spec get_last_transaction :: Transaction.t | nil
def get_last_transaction do
:gen_event.call(:error_logger, Appsignal.ErrorHandler, :get_last_transaction)
end
def init(_) do
# state of the error handler holds the last matched transaction
{:ok, nil}
end
def handle_event(event, state) do
state =
case match_event(event) do
{origin, reason, message, stack, conn} ->
transaction = Transaction.lookup_or_create_transaction(origin)
if transaction != nil do
submit_transaction(transaction, normalize_reason(reason), message, stack, %{}, conn)
end
:nomatch ->
state
end
{:ok, state}
end
def handle_info(_, state) do
{:ok, state}
end
def submit_transaction(transaction, reason, message, stack, metadata, conn \\ nil)
def submit_transaction(transaction, reason, message, stack, metadata, nil) do
Transaction.set_error(transaction, reason, message, stack)
Transaction.set_meta_data(transaction, metadata)
Transaction.finish(transaction)
Transaction.complete(transaction)
Logger.debug fn ->
"Submitting #{inspect transaction}: #{message}"
end
transaction
end
if Appsignal.plug? do
def submit_transaction(transaction, reason, message, stack, metadata, conn) do
if conn do
Transaction.set_request_metadata(transaction, conn)
end
submit_transaction(transaction, reason, message, stack, metadata)
end
end
def handle_call(:get_last_transaction, state) do
{:ok, state, state}
end
@doc false
@spec match_event(term) :: {pid, term, String.t, list, %{}} | :nomatch
def match_event({:error_report, _gleader, {origin, :crash_report, report}}) do
match_error_report(origin, report)
end
def match_event(_event) do
:nomatch
end
defp match_error_report(origin, [[{:initial_call, _},
{:pid, pid},
{:registered_name, name},
{:error_info, {_kind, exception, stack}} | _], _linked]) do
msg = "Process #{crash_name(pid, name)} terminating"
stacktrace = extract_stacktrace(exception) || stack
{reason, message} = extract_reason_and_message(exception, msg)
{origin, reason, message, Backtrace.from_stacktrace(stacktrace), nil}
end
defp extract_stacktrace({_, stacktrace}) do
case stacktrace?(stacktrace) do
true -> stacktrace
false -> nil
end
end
defp extract_stacktrace(_), do: nil
defp stacktrace?(stacktrace) when is_list(stacktrace) do
Enum.all?(stacktrace, &stacktrace_line?/1)
end
defp stacktrace?(_), do: false
defp stacktrace_line?({_, _, _, [file: _, line: _]}), do: true
defp stacktrace_line?(_), do: false
@doc false
def format_stack(stacktrace) do
IO.warn "Appsignal.ErrorHandler.format_stack/1 is deprecated. Use Appsignal.Backtrace.from_stacktrace/1 instead."
Backtrace.from_stacktrace(stacktrace)
end
defp crash_name(pid, []), do: inspect(pid)
defp crash_name(pid, name), do: "#{inspect(name)} (#{inspect(pid)})"
@doc """
Extract a consise reason from the given error reason, stripping it from long stack traces and the like.
Also returns a 'message' which is supposed to contain extra error information.
"""
@spec extract_reason_and_message(any(), binary()) :: {any(), binary()}
if Appsignal.plug? do
def extract_reason_and_message(%Plug.Conn.WrapperError{reason: reason, kind: kind, stack: stacktrace}, message) do
kind
|> Exception.normalize(reason, stacktrace)
|> extract_reason_and_message(message)
end
end
def extract_reason_and_message(reason, message) when is_binary(reason) do
{reason, message}
end
def extract_reason_and_message(reason, message) when is_atom(reason) do
try do
{Exception.message(reason.exception([])), message}
rescue
UndefinedFunctionError -> {"#{inspect reason}", message}
end
end
def extract_reason_and_message(%Protocol.UndefinedError{value: {:error, {error = %{}, _stack}}}, message) do
extract_reason_and_message(error, message)
end
if Appsignal.phoenix? do
def extract_reason_and_message(%Phoenix.ActionClauseError{}, prefix) do
message = """
could not find a matching clause to process request.
This typically happens when there is a parameter mismatch but may
also happen when any of the other action arguments do not match.
"""
{"Phoenix.ActionClauseError", prefixed(prefix, message)}
end
def extract_reason_and_message(%Phoenix.Template.UndefinedError{assigns: %{conn: %{assigns: %{kind: :error, reason: reason}}}}, message) do
extract_reason_and_message(reason, message)
end
end
def extract_reason_and_message(%{__struct__: struct} = reason, message) do
msg = Exception.message(reason)
{"#{inspect struct}", prefixed(message, msg)}
end
def extract_reason_and_message({r = %{}, _}, message) do
extract_reason_and_message(r, message)
end
def extract_reason_and_message({kind, _} = reason, message) do
{inspect(kind), prefixed(message, inspect(reason))}
end
def extract_reason_and_message(any, message) do
# inspect any term; truncate it
{"#{inspect any}", message}
end
defp prefixed(nil, msg), do: msg
defp prefixed("", msg), do: msg
defp prefixed(pre, msg), do: pre <> ": " <> msg
@pid_or_ref_regex ~r/\<(\d+\.)+\d+\>/
def normalize_reason(reason) do
Regex.replace(@pid_or_ref_regex, reason, "<...>")
end
end