Turns error reasons into human-readable text.
Elixir code commonly returns errors as the reason in an {:error, reason}
tuple, where reason can be a string, an exception, a changeset, or any term.
Ark.Error renders any of these into a message you can log or show, without
having to match on the shape first.
iex> Ark.Error.to_string({:error, "database is down"})
"database is down"to_iodata/2 and to_string/2 accept, among others:
- a binary message, returned as-is
- an exception struct, rendered with
Exception.message/1 - an
{exception, stacktrace}pair, rendered as a banner - a nested
{:error, reason}or{:shutdown, reason}tuple - an
Ecto.ChangesetorEcto.InvalidChangesetError, when Ecto is loaded - any other term, rendered with
inspect/1
Audiences
Both functions take an audience, either :private (the default) or
:public. The private rendering is meant for logs and developers, and falls
back to inspect/1 for any term. The public rendering is meant for messages
shown to end users:
an
{exception, stacktrace}pair is rendered withException.message/1instead of the bannera
{tag, reason}tuple with an atom tag is rendered as"(tag) "followed by the rendering ofreasonatoms and numbers are rendered with
inspect/1any other term produces
"unknown error"while the full term is logged at the:errorleveliex> Ark.Error.to_string({:error, %{secret: "shh"}}, :private) "%{secret: \"shh\"}"
iex> Ark.Error.to_string({:error, :timeout}, :public) ":timeout"
iex> Ark.Error.to_string({:error, %{secret: "shh"}}, :public) "unknown error"
iex> Ark.Error.to_string({:error, {:enoent, "/etc/hosts"}}, :public) "(enoent) /etc/hosts"
Custom error formatting
An error can also be a {module, tag, data} triple, which lets a module
render its own errors. When module exports format_reason/3, it is called
with tag, data and the audience to produce the message. A
format_reason/2 callback without the audience is also supported:
defmodule MyApp.Upload do
@spec format_reason(term, term, Ark.Error.audience()) :: iodata
def format_reason(:too_large, size, _audience) do
"file is too large: #{size} bytes"
end
def format_reason(other, data, audience) do
Ark.Error.format_fallback(__MODULE__, other, data, audience)
end
end
Ark.Error.to_string({MyApp.Upload, :too_large, 5_000_000})
# => "file is too large: 5000000 bytes"format_fallback/4 renders any tag the module does not handle, so a single
catch-all clause covers every remaining case.
Logging helpers
log_error/2 and debug_error/2 format a reason for the :private
audience and send it to Logger at the :error and :debug levels:
require Ark.Error
Ark.Error.log_error({:error, :timeout}, request_id: request_id)
Summary
Functions
Formats error with to_string/2 for the :private audience and logs it at
the :debug level.
Renders a {module, tag, data} error that the module does not handle itself.
Formats error with to_string/2 for the :private audience and logs it at
the :error level.
Renders an error reason as iodata/0.
Renders an error reason as a binary.
Types
Functions
Formats error with to_string/2 for the :private audience and logs it at
the :debug level.
Behaves like log_error/2 but logs through Logger.debug/2.
Renders a {module, tag, data} error that the module does not handle itself.
Use this as the catch-all clause of a module's format_reason/3, as shown in
Ark.Error. The triple is rendered like any other term for the given
audience.
Formats error with to_string/2 for the :private audience and logs it at
the :error level.
metadata is passed through to Logger.error/2. Require the module first,
since this is a macro.
require Ark.Error
Ark.Error.log_error({:error, :timeout}, request_id: request_id)
Renders an error reason as iodata/0.
Accepts the shapes listed in Ark.Error. Returning iodata avoids building
intermediate strings, which is convenient when the result goes straight to
Logger or IO.
iex> IO.iodata_to_binary(Ark.Error.to_iodata({:shutdown, "node left"}))
"(shutdown) node left"
Renders an error reason as a binary.
Same as to_iodata/2, with the result collapsed into a single string.
iex> Ark.Error.to_string({:error, :enoent})
":enoent"