查看源码 日志记录指南
使用特别是配置 Logger 有时可能会很困难,因为有很多不同的选项可以更改,而且通常有不止一种方法可以达到相同的结果。本用户指南尝试通过提供许多不同的使用 Logger 的示例来提供帮助。
有关使用 Logger 的更多实际用例示例,Fred Hebert 的博客文章Erlang/OTP 21 的新 Logger是一个很好的起点。
注意
如果您发现本指南中缺少某些常见的 Logger 用法,请在 github 上打开一个拉取请求,并提出建议的添加内容
获取 Logger 信息
打印主要 Logger 配置。
1> logger:i(primary).
Primary configuration:
Level: notice
Filter Default: log
Filters:
(none)
也可以使用logger:get_primary_config()
获取配置。
另请参阅
- logger:i()
- 配置在日志记录用户指南中
打印所有处理程序的配置。
2> logger:i(handlers).
Handler configuration:
Id: default
Module: logger_std_h
Level: all
Formatter:
Module: logger_formatter
Config:
legacy_header: true
single_line: false
Filter Default: stop
Filters:
Id: remote_gl
Fun: fun logger_filters:remote_gl/2
Arg: stop
Id: domain
Fun: fun logger_filters:domain/2
Arg: {log,super,[otp,sasl]}
Id: no_domain
Fun: fun logger_filters:domain/2
Arg: {log,undefined,[]}
Handler Config:
burst_limit_enable: true
burst_limit_max_count: 500
burst_limit_window_time: 1000
drop_mode_qlen: 200
filesync_repeat_interval: no_repeat
flush_qlen: 1000
overload_kill_enable: false
overload_kill_mem_size: 3000000
overload_kill_qlen: 20000
overload_kill_restart_after: 5000
sync_mode_qlen: 10
type: standard_io
您还可以使用logger:i(HandlerName)
打印特定处理程序的配置,或者使用logger:get_handler_config()
或logger:get_handler_config(HandlerName)
(对于特定处理程序)获取配置。
另请参阅
logger:i()
- 配置在日志记录用户指南中
配置 Logger
我的进度报告去哪了?
在 OTP-21 中,默认的主要日志级别是notice
。这意味着默认情况下不会打印许多日志消息。这包括主管的进度报告。为了获取进度报告,您需要将主要日志级别提高到info
$ erl -kernel logger_level info
=PROGRESS REPORT==== 4-Nov-2019::16:33:11.742069 ===
application: kernel
started_at: nonode@nohost
=PROGRESS REPORT==== 4-Nov-2019::16:33:11.746546 ===
application: stdlib
started_at: nonode@nohost
Eshell V10.5.3 (abort with ^G)
1>
配置 Logger 格式化程序
为了更好地适应您现有的日志记录基础设施,Logger 可以以您想要的任何方式格式化其日志消息。您可以使用内置的格式化程序,也可以构建自己的格式化程序。
单行配置
由于单行日志记录是内置格式化程序的默认设置,您只需提供空映射作为配置即可。下面的示例使用sys.config
来更改格式化程序的配置。
$ cat sys.config
[{kernel,
[{logger,
[{handler, default, logger_std_h,
#{ formatter => {logger_formatter, #{ }}}}]}]}].
$ erl -config sys
Eshell V10.5.1 (abort with ^G)
1> logger:error("Oh noes, an error").
1962-10-03T11:07:47.466763-04:00 error: Oh noes, an error
但是,如果您只想为当前会话更改它,您也可以这样做。
1> logger:set_handler_config(default, formatter, {logger_formatter, #{}}).
ok
2> logger:error("Oh noes, another error").
1962-10-04T15:34:02.648713-04:00 error: Oh noes, another error
另请参阅
向日志条目添加文件和行号
您可以使用格式化程序模板来更改打印到日志的内容
$ cat sys.config
[{kernel,
[{logger,
[{handler, default, logger_std_h,
#{ formatter => {logger_formatter,
#{ template => [time," ", file,":",line," ",level,": ",msg,"\n"] }}}}]}]}].
$ erl -config sys
Eshell V10.5.1 (abort with ^G)
1> logger:error("Oh noes, more errors",#{ file => "shell.erl", line => 1 }).
1962-10-05T07:37:44.104241+02:00 shell.erl:1 error: Oh noes, more errors
请注意,文件和行号必须由logger:log/3
的调用者在元数据中添加,否则 Logger 将不知道它从哪里被调用。如果您使用kernel/include/logger.hrl
中的?LOG_ERROR
宏,则会自动添加文件和行号。
另请参阅
配置处理程序
将日志打印到文件
我们不将日志打印到标准输出,而是将它们打印到轮换文件日志。
$ cat sys.config
[{kernel,
[{logger,
[{handler, default, logger_std_h,
#{ config => #{ file => "log/erlang.log",
max_no_bytes => 4096,
max_no_files => 5},
formatter => {logger_formatter, #{}}}}]}]}].
$ erl -config sys
Eshell V10.5.1 (abort with ^G)
1> logger:error("Oh noes, even more errors").
ok
2> erlang:halt().
$ cat log/erlang.log
2019-10-07T11:47:16.837958+02:00 error: Oh noes, even more errors
另请参阅
logger_std_h
- 处理程序在日志记录用户指南中
仅调试处理程序
添加一个将debug
日志事件打印到文件的处理程序,而默认处理程序仅将notice
级别以下的事件打印到标准输出。
$ cat sys.config
[{kernel,
[{logger_level, all},
{logger,
[{handler, default, logger_std_h,
#{ level => notice }},
{handler, debug, logger_std_h,
#{ filters => [{debug,{fun logger_filters:level/2, {stop, neq, debug}}}],
config => #{ file => "log/debug.log" } }}
]}]}].
$ erl -config sys
Eshell V10.5.1 (abort with ^G)
1> logger:error("Oh noes, even more errors").
=ERROR REPORT==== 9-Oct-2019::14:40:54.784162 ===
Oh noes, even more errors
ok
2> logger:debug("A debug event").
ok
3> erlang:halt().
$ cat log/debug.log
2019-10-09T14:41:03.680541+02:00 debug: A debug event
在上面的配置中,我们首先将主要日志级别提高到最大值,以便调试日志事件到达处理程序。然后,我们将默认处理程序配置为仅记录 notice 及以下的事件,处理程序的默认日志级别是all
。然后,调试处理程序配置了一个过滤器,以阻止任何不是调试级别的消息。
也可以使用logger
模块在已运行的系统中进行相同的更改。然后你这样做
$ erl
1> logger:set_handler_config(default, level, notice).
ok
2> logger:add_handler(debug, logger_std_h, #{
filters => [{debug,{fun logger_filters:level/2, {stop, neq, debug}}}],
config => #{ file => "log/debug.log" } }).
ok
3> logger:set_primary_config(level, all).
ok
重要的是,在调整默认处理程序的级别之前,不要提高主要日志级别,否则您的标准输出可能会被调试日志消息淹没。
另请参阅
logger_std_h
- 过滤器在日志记录用户指南中
日志记录
记录什么以及如何记录
记录某些内容的最简单方法是使用 Logger 宏并向宏提供报告。例如,如果您想记录一个错误
?LOG_ERROR(#{ what => http_error, status => 418, src => ClientIP, dst => ServerIP }).
这将在默认日志中打印以下内容
=ERROR REPORT==== 10-Oct-2019::12:13:10.089073 ===
dst: {8,8,4,4}
src: {8,8,8,8}
status: 418
what: http_error
或者如果您使用单行格式化程序,则打印如下内容
2019-10-10T12:14:11.921843+02:00 error: dst: {8,8,4,4}, src: {8,8,8,8}, status: 418, what: http_error
另请参阅
- 日志消息在日志记录用户指南中
报告回调和事件打印
如果您想进行结构化日志记录,但仍想控制最终日志消息的格式化方式,您可以在日志事件的元数据中提供一个report_cb
。
ReportCB = fun(#{ what := What, status := Status, src := Src, dst := Dst }) ->
{ok, #hostent{ h_name = SrcName }} = inet:gethostbyaddr(Src),
{ok, #hostent{ h_name = DstName }} = inet:gethostbyaddr(Dst),
{"What: ~p~nStatus: ~p~nSrc: ~s (~s)~nDst: ~s (~s)~n",
[What, Status, inet:ntoa(Src), SrcName, inet:ntoa(Dst), DstName]}
end,
?LOG_ERROR(#{ what => http_error, status => 418, src => ClientIP, dst => ServerIP },
#{ report_cb => ReportCB }).
这将打印以下内容
=ERROR REPORT==== 10-Oct-2019::13:29:02.230863 ===
What: http_error
Status: 418
Src: 8.8.8.8 (dns.google)
Dst: 192.121.151.106 (erlang.org)
请注意,打印顺序已更改,我还添加了 IP 地址的反向 DNS 查找。使用单行格式化程序时,这不会打印得很好,但是您也可以使用带有 2 个参数的 report_cb 函数,其中第二个参数是格式化选项。
另请参阅
- 日志消息在日志记录用户指南中
- Logger 报告回调
过滤器
过滤器用于在日志事件到达处理程序之前删除或更改日志事件。
进程过滤器
如果我们只想要来自特定进程的调试消息,可以使用如下过滤器来实现
%% Initial setup to use a filter for the level filter instead of the primary level
PrimaryLevel = maps:get(level, logger:get_primary_config()),
ok = logger:add_primary_filter(primary_level,
{fun logger_filters:level/2, {log, gteq, PrimaryLevel}}),
logger:set_primary_config(filter_default, stop),
logger:set_primary_config(level, all),
%% Test that things work as they should
logger:notice("Notice should be logged"),
logger:debug("Should not be logged"),
%% Add the filter to allow PidToLog to send debug events
PidToLog = self(),
PidFilter = fun(LogEvent, _) when PidToLog =:= self() -> LogEvent;
(_LogEvent, _) -> ignore end,
ok = logger:add_primary_filter(pid, {PidFilter,[]}),
logger:debug("Debug should be logged").
需要进行一些设置才能使过滤器决定是否应允许特定进程记录日志。这是因为默认的主要日志级别是 notice,并且它在主要过滤器之前强制执行。因此,为了使 pid 过滤器有用,我们必须将主要日志级别提高到all
,然后添加一个级别过滤器,该过滤器仅允许 notice 或更高级别的特定消息通过。完成设置后,添加一个允许特定 pid 通过的过滤器很简单。
请注意,通过过滤器而不是通过级别执行主要日志级别过滤的成本要高得多,因此请确保在生产节点上启用它之前,您的系统可以处理额外的负载。
另请参阅
域
域用于指定特定日志事件的来源子系统。默认情况下,默认处理程序仅记录域为[otp]
或没有域的事件。如果您想将 SSL 日志事件包含到默认处理程序日志中,您可以这样做
1> logger:add_handler_filter(default,ssl_domain,
{fun logger_filters:domain/2,{log,sub,[otp,ssl]}}).
2> application:ensure_all_started(ssl).
{ok,[crypto,asn1,public_key,ssl]}
3> ssl:connect("www.erlang.org",443,[{log_level,debug}]).
%% lots of text