Copyright © 2014-2016 Takeru Ohta <phjgt308@gmail.com>
Logger Interface
This module mainly provides logger related functions. A logger has own headers, metadata, filter and can issue log messages to a destination channel.
> error_logger:tty(false). % Suppresses annoying warnings for the sake of brevity
> {ok, _} = logi_channel:install_sink(logi_builtin_sink_io_device:new(foo), info). % Installs a sink to the default channel
> logi:info("hello world").
2015-11-09 08:18:34.954 [info] nonode@nohost <0.91.0> erl_eval:do_apply:673 [] hello world
context() = logger_instance()
This is provided only for maintaining compatibility with v0.0.12
context_ref() = logger()
This is provided only for maintaining compatibility with v0.0.12
frequency_policy() = always | once | {once_in_times, Times::pos_integer()} | {interval, MilliSeconds::non_neg_integer()}
headers() = #{}
The headers of a log message.
Headers are intended to be included in the outputs written by sinks.log_option() = {logger, logger()} | {location, logi_location:location()} | {headers, headers()} | {metadata, metadata()} | {timestamp, erlang:timestamp()} | {frequency, frequency_policy()}
log_options() = [log_option()]
logger() = logger_id() | logger_instance()
A logger
logger_id() = atom()
The ID of a saved logger instance (see: save/2
).
new([{channel, LoggerId}])
.
abstract datatype: logger_instance()
A logger instance
logger_map_form() = #{channel => logi_channel:id(), headers => headers(), metadata => metadata(), filter => logi_filter:filter(), next => logger_instance()}
The map representation of a logger.
filter
and next
fields are optional
(e.g. If a logger has no filter, the filter
field is omitted from the corresponding map).
metadata() = #{}
The metadata of a log message
Metadata are not intended to be included directly in the outputs written by sinks. The main purpose of metadata is to provide means to convey information from the log issuer to filters or sinks.new_option() = {channel, logi_channel:id()} | {headers, headers()} | {metadata, metadata()} | {filter, logi_filter:filter()} | {next, logger_instance()}
[channel]
- The destination channel
- The log messages issued by the created logger will (logically) send to the channel
- Default: logi_channel:default_channel()
[headers]
- The headers of the created logger
- Default: #{}
[metadata]
- The metadata of the created logger
- Default: #{}
[filter] - A log message filter - Default: none (optional)
[next] - A next logger - An application of the some function (e.g.log/4
) to the created logger is also applied to the next logger
- Default: none (optional)
new_options() = [new_option()]
severity() = debug | info | notice | warning | error | critical | alert | emergency
Severity of a log message
It follwed the severities which are described in [RFC 5424](https://tools.ietf.org/html/rfc5424#section-6.2.1).alert(Format::io:format()) -> logger_instance()
Equivalent to alert(Format, []).
alert(Format::io:format(), Data::[term()]) -> logger_instance()
Equivalent to alert(Format, Data, []).
alert(Format::io:format(), Data::[term()], Options::log_options()) -> logger_instance()
Equivalent to log(alert, Format, Data, Options).
critical(Format::io:format()) -> logger_instance()
Equivalent to critical(Format, []).
critical(Format::io:format(), Data::[term()]) -> logger_instance()
Equivalent to critical(Format, Data, []).
critical(Format::io:format(), Data::[term()], Options::log_options()) -> logger_instance()
Equivalent to log(critical, Format, Data, Options).
debug(Format::io:format()) -> logger_instance()
Equivalent to debug(Format, []).
debug(Format::io:format(), Data::[term()]) -> logger_instance()
Equivalent to debug(Format, Data, []).
debug(Format::io:format(), Data::[term()], Options::log_options()) -> logger_instance()
Equivalent to log(debug, Format, Data, Options).
debug_opt(Format::io:format(), Data::[term()], Options::log_options()) -> logger_instance()
Equivalent to debug(Format, Data, Options).
default_logger() -> logger_id()
Returns the default logger
The default channellogi_channel:default_channel/0
which corresponds to the logger
is started automatically when logi
application was started.
delete_headers(Keys::[term()]) -> logger_instance()
Equivalent to delete_headers(Keys, []).
delete_headers(Keys::[term()], Options) -> logger_instance()
Deletes headers which associated with Keys
If the logger has nested loggers, the function is applied to them recursively.
[logger]
- The logger to which the operation applies.
- Default: logi:default_logger()
.
> Logger = logi:new([{headers, #{a => 1, b => 2}}]).
> logi:to_map(logi:delete_headers([a], [{logger, Logger}])).
#{channel => logi_default_log,headers => #{b => 2},metadata => #{}}
delete_metadata(Keys::[term()]) -> logger_instance()
Equivalent to delete_metadata(Keys, []).
delete_metadata(Keys::[term()], Options) -> logger_instance()
Deletes metadata entries which associated with Keys
If the logger has nested loggers, the function is applied to them recursively.
[logger]
- The logger to which the operation applies.
- Default: logi:default_logger()
.
> Logger = logi:new([{metadata, #{a => 1, b => 2}}]).
> logi:to_map(logi:delete_metadata([a], [{logger, Logger}])).
#{channel => logi_default_log,headers => #{},metadata => #{b => 2}}
emergency(Format::io:format()) -> logger_instance()
Equivalent to emergency(Format, []).
emergency(Format::io:format(), Data::[term()]) -> logger_instance()
Equivalent to emergency(Format, Data, []).
emergency(Format::io:format(), Data::[term()], Options::log_options()) -> logger_instance()
Equivalent to log(emergency, Format, Data, Options).
ensure_to_be_instance(Logger::logger()) -> logger_instance()
Returns the logger instance associated to Logger
> logi:ensure_to_be_instance(unsaved).
{logi_logger,unsaved,#{},#{},undefined,undefined}
> logi:save(saved, logi:new([{channel, hoge}])).
> logi:ensure_to_be_instance(hoge).
{logi_logger,hoge,#{},#{},undefined,undefined}
> logi:ensure_to_be_instance(logi:new([{channel, instance}])).
{logi_logger,instance,#{},#{},undefined,undefined}
erase() -> [{logger_id(), logger_instance()}]
Returns the saved loggers and deletes them from the process dictionary.
> logi:save(hoge, logi:new()).
> logi:erase().
[{hoge,{logi_logger,logi_default_log,#{},#{},undefined,undefined}}]
> logi:erase().
[]
erase(LoggerId::logger_id()) -> logger_instance() | undefined
Returns the logger associated with LoggerId
and deletes it from the process dictionary.
Returns undefined
if no logger is associated with LoggerId
.
> logi:save(hoge, logi:new()).
> logi:erase(hoge).
{logi_logger,logi_default_log,#{},#{},undefined,undefined}
> logi:erase(hoge).
undefined
error(Format::io:format()) -> logger_instance()
Equivalent to error(Format, []).
error(Format::io:format(), Data::[term()]) -> logger_instance()
Equivalent to error(Format, Data, []).
error(Format::io:format(), Data::[term()], Options::log_options()) -> logger_instance()
Equivalent to log(error, Format, Data, Options).
from_list(Loggers::[logger()]) -> logger_instance()
Aggregates Loggers
into a logger instance
The head logger in Loggers
becomes the root of the aggregation.
e.g. from_list([new(), new(), new()])
is equivalent to new([{next, new([{next, new()}])}])
.
> GetChannel = fun (Logger) -> maps:get(channel, logi:to_map(Logger)) end.
> Logger0 = logi:new([{channel, aaa}]).
> Logger1 = logi:new([{channel, bbb}]).
> Logger2 = logi:new([{channel, ccc}, {next, logi:new([{channel, ccc_sub}])}]).
> [aaa, bbb] = lists:map(GetChannel, logi:to_list(logi:from_list([Logger0, Logger1]))).
> [ccc, ccc_sub, aaa, bbb] = lists:map(GetChannel, logi:to_list(logi:from_list([Logger2, Logger0, Logger1]))).
from_map(Map::logger_map_form()) -> logger_instance()
Creates a new logger instance from Map
Default Value:
- channel: logi_channel:default_channel()
- headers: #{}
- metadata: #{}
- filter: none (optional)
- next: none (optional)
> logi:to_map(logi:from_map(#{})).
#{channel => logi_default_log,headers => #{},metadata => #{}}
info(Format::io:format()) -> logger_instance()
Equivalent to info(Format, []).
info(Format::io:format(), Data::[term()]) -> logger_instance()
Equivalent to info(Format, Data, []).
info(Format::io:format(), Data::[term()], Options::log_options()) -> logger_instance()
Equivalent to log(info, Format, Data, Options).
info_opt(Format::io:format(), Data::[term()], Options::log_options()) -> logger_instance()
Equivalent to info(Format, Data, Options).
is_logger(X::logger() | term()) -> boolean()
Returns true
if X
is a logger, otherwise false
is_severity(X::severity() | term()) -> boolean()
Returns true
if X
is a severity, otherwise false
load(LoggerId::logger_id()) -> {ok, logger_instance()} | error
Loads a logger which associated with the ID LoggerId
from the process dictionary
> error = logi:load(hoge_log).
> logi:save(hoge_log, logi:new()).
> {ok, _} = logi:load(hoge_log).
load_context() -> logger_instance()
Equivalent to ensure_to_be_instance(default_logger()).
load_default() -> {ok, logger_instance()} | error
Equivalent to load(default_logger()).
log(Severity::severity(), Format::io:format(), Data::[term()], Options::log_options()) -> logger_instance()
Issues a log message to the destination channel.
If the logger has a filter, the message will be passed to it.
And if the message has not been discarded by a filter,
the logger will (logically) send it to the destination channel.
Finally, the message will be consumed by the sinks which are installed to the channel.
But the sinks which does not satisfy specified condition (i.e. logi_sink:condition/0
) are ignored.
> {ok, _} = logi_channel:install_sink(logi_builtin_sink_io_device:new(sample), info). % Installs a sink to the default channel
> logi:log(debug, "hello world", [], []). % There are no applicable sinks (the severity is too low)
> logi:log(info, "hello world", [], []). % The log message is consumed by the above sink
2015-10-22 13:16:37.003 [info] nonode@nohost <0.91.0> erl_eval:do_apply:673 [] hello world
If the logger has nested loggers, the function is applied to them recursively.
> {ok, _} = logi_channel:install_sink(logi_builtin_sink_io_device:new(sample), info). % Installs a sink to the default channel
> Logger = logi:from_list([logi:new([{headers, #{id => hoge}}]), logi:new([{headers, #{id => fuga}}])]).
> logi:log(info, "hello world", [], [{logger, Logger}]).
2015-10-22 13:28:10.332 [info] nonode@nohost <0.91.0> erl_eval:do_apply:673 [id=hoge] hello world
2015-10-22 13:28:10.332 [info] nonode@nohost <0.91.0> erl_eval:do_apply:673 [id=fuga] hello world
Typically, it is preferred to log messages through the wrapper functions (i.e. logi:Severity/{1,2,3}
)
rather than calling the function directly.
If the {parse_transform, logi_transform}
compiler option is specified,
the invocation of the wrapper functions will be transformed to a more efficient code at compile time.
logi:info("hello world)
will be transformed to a code such as following:
%% Current location (Application
, Module
, Function
and Line
) is detected at compile time
Location = logi_location:unsafe_new(self(), Application, Module, Function, Line),
case logi:'_ready'(info, Location, Options) of
{Logger, []} -> Logger;
{Logger, ListOfContextAndSinks} -> logi:_write
(ListOfContextAndSinks, Format, Data)
end.
From the efficiency point of view, the following two points are important:
- 1. An implicit call of logi_location:guess_location/0
to guess the currrent location is replaced by the more efficient and accurate code
- 2. If it is unnecessary (e.g. there are no applicable sinks), Format
and Data
will not be evaluated
logi_location:guess_location/0
is a heavy function,
so if it is called at runtime, a warning will be emitted via the error_logger
module.
See also: logi_location:guess_location/0.
make_context() -> logger_instance()
Equivalent to new().
make_context(ChannelId::logi_channel:id()) -> logger_instance()
Equivalent to new([{channel, ChannelId}]).
new() -> logger_instance()
Equivalent to new([]).
new(Options::new_options()) -> logger_instance()
Creates a new logger instance
notice(Format::io:format()) -> logger_instance()
Equivalent to notice(Format, []).
notice(Format::io:format(), Data::[term()]) -> logger_instance()
Equivalent to notice(Format, Data, []).
notice(Format::io:format(), Data::[term()], Options::log_options()) -> logger_instance()
Equivalent to log(notice, Format, Data, Options).
notice_opt(Format::io:format(), Data::[term()], Options::log_options()) -> logger_instance()
Equivalent to notice(Format, Data, Options).
save(LoggerId::logger_id(), Logger::logger()) -> logger_instance() | undefined
Saves Logger
with the ID LoggerId
to the process dictionary
If LoggerId
already exists, the old logger instance is deleted and replaced by Logger
and the function returns the old instance.
Otherwise it returns undefined
.
> Logger = logi:new().
> logi:save(sample_log, Logger).
% The following two expression is equivalent.
> logi:info("hello world", [{logger, Logger}]). % referred by instance
> logi:info("hello world", [{logger, sample_log}]). % referred by ID
save_as_default(Logger::logger()) -> logger_instance() | undefined
Equivalent to save(default_logger(), Logger).
save_context(Logger::logger()) -> logger_instance() | undefined
Equivalent to save_as_default(Logger).
set_headers(Headers::headers() | list()) -> logger_instance()
Equivalent to set_headers(Headers, []).
set_headers(Headers::headers() | V0_0_12_Compatible, Options) -> logger_instance()
Sets headers of the logger
If the logger has nested loggers, the function is applied to them recursively.
[logger]
- The logger to which the operation applies.
- Default: logi:default_logger()
.
[if_exists]
- If the value is supersede
, the existing headers are deleted and replaced by Headers
.
- If the value is overwrite
, the existing headers and Headers
are merged and the rear has priority when a key collision occurs.
- If the value is ignore
, the existing headers and Headers
are merged and the former has priority when a key collision occurs.
- Default: overwrite
> Logger = logi:new([{headers, #{a => 10, b => 20}}]).
> Set = fun (Headers, IfExists) ->
L = logi:set_headers(Headers, [{logger, Logger}, {if_exists, IfExists}]),
maps:get(headers, logi:to_map(L))
end.
> true = #{a => 0, c => 30} =:= Set(#{a => 0, c => 30}, supersede).
> true = #{a => 0, b => 20, c => 30} =:= Set(#{a => 0, c => 30}, overwrite).
> true = #{a => 10, b => 20, c => 30} =:= Set(#{a => 0, c => 30}, ignore).
set_metadata(Metadata::metadata()) -> logger_instance()
Equivalent to set_metadata(Metadata, []).
set_metadata(Metadata::metadata(), Options) -> logger_instance()
Sets metadata of the logger
If the logger has nested loggers, the function is applied to them recursively.
[logger]
- The logger to which the operation applies.
- Default: logi:default_logger()
.
[if_exists]
- If the value is supersede
, the existing metadata is deleted and replaced by Metadata
.
- If the value is overwrite
, the existing metadata and Metadata
are merged and the rear has priority when a key collision occurs.
- If the value is ignore
, the existing metadata and Metadata
are merged and the former has priority when a key collision occurs.
- Default: overwrite
> Logger = logi:new([{metadata, #{a => 10, b => 20}}]).
> Set = fun (Metadata, IfExists) ->
L = logi:set_metadata(Metadata, [{logger, Logger}, {if_exists, IfExists}]),
maps:get(metadata, logi:to_map(L))
end.
> true = #{a => 0, c => 30} =:= Set(#{a => 0, c => 30}, supersede).
> true = #{a => 0, b => 20, c => 30} =:= Set(#{a => 0, c => 30}, overwrite).
> true = #{a => 10, b => 20, c => 30} =:= Set(#{a => 0, c => 30}, ignore).
severities() -> [severity()]
Returns the available severity list
The list are ordered by the their severity level (see:severity_level/1
).
severity_level(Severity::severity()) -> 1..8
Returns the level of Severity
to_list(Logger::logger()) -> [logger_instance()]
Flattens the nested logger
The nested loggers are collected as a flat list.
The next
fields of the resulting loggers are removed.
> Logger0 = logi:new().
> Logger1 = logi:new([{next, Logger0}]).
> Logger2 = logi:new([{next, Logger1}]).
> [Logger0] = logi:to_list(Logger0).
> [Logger0, Logger0] = logi:to_list(Logger1).
> [Logger0, Logger0, Logger0] = logi:to_list(Logger2).
to_map(Logger::logger()) -> logger_map_form()
Converts Logger
into a map form
The optional entries (i.e. filter
and next
) will be omitted from the resulting map if the value is not set.
> logi:to_map(logi:new()).
#{channel => logi_default_log,headers => #{},metadata => #{}}
> logi:to_map(logi:new([{next, logi:new()}])).
#{channel => logi_default_log,
headers => #{},
metadata => #{},
next => {logi_logger,logi_default_log,#{},#{},undefined,undefined}}
verbose(Format::io:format()) -> logger_instance()
Equivalent to verbose(Format, []).
verbose(Format::io:format(), Data::[term()]) -> logger_instance()
Equivalent to verbose(Format, Data, []).
verbose(Format::io:format(), Data::[term()], Options::log_options()) -> logger_instance()
Equivalent to log(debug, Format, Data, Options).
verbose_opt(Format::io:format(), Data::[term()], Options::log_options()) -> logger_instance()
Equivalent to debug(Format, Data, Options).
warning(Format::io:format()) -> logger_instance()
Equivalent to warning(Format, []).
warning(Format::io:format(), Data::[term()]) -> logger_instance()
Equivalent to warning(Format, Data, []).
warning(Format::io:format(), Data::[term()], Options::log_options()) -> logger_instance()
Equivalent to log(warning, Format, Data, Options).
warning_opt(Format::io:format(), Data::[term()], Options::log_options()) -> logger_instance()
Equivalent to warning(Format, Data, Options).
which_loggers() -> [logger_id()]
Returns the ID list of the saved loggers
> logi:save(hoge, logi:new()).
> logi:which_loggers().
[hoge]
Generated by EDoc