View Source LoggerBackends (logger_backends v1.0.0)
:gen_event
-based logger handlers with overload protection.
This module provides backends for Elixir's Logger with built-in overload protection. This was the default mechanism for hooking into Elixir's Logger until Elixir v1.15.
Elixir backends run in a single separate process which comes with overload protection. All backends run in this same process as a unified front for handling log events.
The available backends by default are:
LoggerBackends.Console
- logs messages to the console (see its documentation for more information)
Developers may also implement their own backends, an option that is explored in more detail later.
Backends can be added and removed via add/2
and remove/2
functions.
This is often done in your Application.start/2
callback:
@impl true
def start(_type, _args) do
LoggerBackends.add(MyCustomBackend)
# ...
end
The backend can be configured in your config files:
config :logger, MyCustomBackend,
some_config: ...
application-configuration
Application configuration
Application configuration goes under the :logger
application for
backwards compatibility. The following keys must be set before
the :logger
application (and this application) are started.
:discard_threshold_periodic_check
- a periodic check that checks and reports if logger is discarding messages. It logs a warning message whenever the system is (or continues) in discard mode and it logs a warning message whenever if the system was discarding messages but stopped doing so after the previous check. By default it runs every30_000
milliseconds.:start_options
- passes start options to LoggerBackends's main process, such as:spawn_opt
and:hibernate_after
. All options inGenServer.option/0
are accepted, except:name
.
runtime-configuration
Runtime configuration
The following keys can be set at runtime via the configure/1
function.
In your config files, they also go under the :logger
application
for backwards compatibility.
:utc_log
- whentrue
, uses UTC in logs. By default it uses local time (i.e., it defaults tofalse
).:truncate
- the maximum message size to be logged (in bytes). Defaults to 8192 bytes. Note this configuration is approximate. Truncated messages will have" (truncated)"
at the end. The atom:infinity
can be passed to disable this behavior.:sync_threshold
- if theLogger
manager has more than:sync_threshold
messages in its queue,Logger
will change to sync mode, to apply backpressure to the clients.Logger
will return to async mode once the number of messages in the queue is reduced to one below thesync_threshold
. Defaults to 20 messages.:sync_threshold
can be set to0
to force sync mode.:discard_threshold
- if theLogger
manager has more than:discard_threshold
messages in its queue,Logger
will change to discard mode and messages will be discarded directly in the clients.Logger
will return to sync mode once the number of messages in the queue is reduced to one below thediscard_threshold
. Defaults to 500 messages.
custom-backends
Custom backends
Any developer can create their own backend. Since Logger
is an
event manager powered by :gen_event
, writing a new backend
is a matter of creating an event handler, as described in the
:gen_event
documentation.
From now on, we will be using the term "event handler" to refer to your custom backend, as we head into implementation details.
The event manager and all added event handlers are automatically
supervised by Logger
. If a backend fails to start by returning
{:error, :ignore}
from its init/1
callback, then it's not added
to the backends but nothing fails. If a backend fails to start by
returning {:error, reason}
from its init/1
callback, the system
will fail to start.
Once initialized, the handler should be designed to handle the following events:
{level, group_leader, {Logger, message, timestamp, metadata}}
where:level
is one of:debug
,:info
,:warn
, or:error
, as previously described (for compatibility with pre 1.10 backends the:notice
will be translated to:info
and all messages above:error
will be translated to:error
)group_leader
is the group leader of the process which logged the message{Logger, message, timestamp, metadata}
is a tuple containing information about the logged message:- the first element is always the atom
Logger
message
is the actual message (as chardata)timestamp
is the timestamp for when the message was logged, as a{{year, month, day}, {hour, minute, second, millisecond}}
tuplemetadata
is a keyword list of metadata used when logging the message
- the first element is always the atom
:flush
It is recommended that handlers ignore messages where the group leader is in a different node than the one where the handler is installed. For example:
def handle_event({_level, gl, {Logger, _, _, _}}, state)
when node(gl) != node() do
{:ok, state}
end
In the case of the event :flush
handlers should flush any pending
data. This event is triggered by Logger.flush/0
.
Furthermore, backends can be configured via the configure_backend/2
function which requires event handlers to handle calls of the
following format:
{:configure, options}
where options
is a keyword list. The result of the call is the result
returned by configure_backend/2
. The recommended return value for
successful configuration is :ok
. For example:
def handle_call({:configure, options}, state) do
new_state = reconfigure_state(state, options)
{:ok, :ok, new_state}
end
It is recommended that backends support at least the following configuration options:
:level
- the logging level for that backend:format
- the logging format for that backend:metadata
- the metadata to include in that backend
Check the LoggerBackends.Console
implementation in Elixir's codebase
for examples on how to handle the recommendations in this section and
how to process the existing options.
Link to this section Summary
Functions
Adds a new backend.
Applies runtime configuration to all backends.
Configures a given backend.
Removes a backend.
Link to this section Types
@type backend() :: :gen_event.handler()
A logger handler.
Link to this section Functions
@spec add( backend(), keyword() ) :: Supervisor.on_start_child()
Adds a new backend.
Adding a backend calls the init/1
function in that backend
with the name of the backend as its argument. For example,
calling
LoggerBackends.add(MyBackend)
will call MyBackend.init(MyBackend)
to initialize the new
backend. If the backend's init/1
callback returns {:ok, _}
,
then this function returns {:ok, pid}
. If the handler returns
{:error, :ignore}
from init/1
, this function still returns
{:ok, pid}
but the handler is not started. If the handler
returns {:error, reason}
from init/1
, this function returns
{:error, {reason, info}}
where info
is more information on
the backend that failed to start.
options
Options
:flush
- whentrue
, guarantees all messages currently sent toLogger
are processed before the backend is added
@spec configure(keyword()) :: :ok
Applies runtime configuration to all backends.
See the module doc for more information.
Configures a given backend.
Removes a backend.
options
Options
:flush
- whentrue
, guarantees all messages currently sent toLogger
are processed before the backend is removed