檢視原始碼 日誌記錄實用指南

使用,特別是設定 Logger 有時可能會很困難,因為有許多不同的選項可以更改,而且通常有多種方法可以達到相同的結果。本使用者指南試圖透過提供許多不同的 Logger 使用範例來提供幫助。

如需更多 Logger 實際使用案例的範例,Fred Hebert 的部落格文章Erlang/OTP 21 的新日誌記錄器是一個很好的起點。

注意

如果您發現本指南中缺少某些常見的 Logger 用法,請在 github 上開啟一個 pull request,並提出建議的補充內容

取得 Logger 資訊

1> logger:i(primary).
Primary configuration:
    Level: notice
    Filter Default: log
    Filters:
        (none)

也可以使用 logger:get_primary_config() 來取得設定。

另請參閱

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

我的進度報告去哪裡了?

在 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 巨集,則會自動新增檔案和行號。

另請參閱

設定處理器

我們不將日誌列印到 stdout,而是將它們列印到輪換的檔案日誌。

$ 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

另請參閱

僅限偵錯的處理器

新增一個將 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 巨集,並向巨集提供報告。例如,如果您要記錄錯誤

?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 函數,其中第二個引數是格式化選項。

另請參閱

篩選器

篩選器用於在日誌事件到達處理器之前移除或變更它們。

處理程序篩選器

如果我們只想要來自特定處理程序的偵錯訊息,則可以使用類似以下的篩選器來執行此操作

%% 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

另請參閱