Gotta-ni

Random text by shino

Logger doc を読んだメモ

Table of Contents

はじめに (Top of chapter)

シェルを起動してみてみる

1> logger:get_config().
#{handlers =>
      [#{config =>
             #{handler_pid => <0.69.0>,
               mode_tab => #Ref<0.624384036.2451177473.63388>,
               type => standard_io},
         filter_default => stop,
         filters =>
             [{remote_gl,{fun logger_filters:remote_gl/2,stop}},
              {domain,{fun logger_filters:domain/2,
                       {log,super,[otp,sasl]}}},
              {no_domain,{fun logger_filters:domain/2,
                          {log,undefined,[]}}}],
         formatter =>
             {logger_formatter,#{legacy_header => true,single_line => false}},
         id => default,level => all,module => logger_std_h}],
  module_levels => [],
  primary =>
      #{filter_default => log,filters => [],level => notice}}

Overview

2つのハンドラーがある場合の構成図 OTP doc

Logger API

マクロをみてみる。 lib/kernel/include/logger.hrl から抜粋。

-define(LOG_INFO(A),?DO_LOG(info,[A])).
-define(LOG_INFO(A,B),?DO_LOG(info,[A,B])).
-define(LOG_INFO(A,B,C),?DO_LOG(info,[A,B,C])).

-define(LOCATION,#{mfa=>{?MODULE,?FUNCTION_NAME,?FUNCTION_ARITY},
                   line=>?LINE,
                   file=>?FILE}).

-define(DO_LOG(Level,Args),
        case logger:allow(Level,?MODULE) of
            true ->
                apply(logger,macro_log,[?LOCATION,Level|Args]);
            false ->
                ok
        end).
-endif.

ログレベル

lib/kernel/src/logger_internal.hrl から抜粋

-define(LOG_NONE,-1).
-define(EMERGENCY,0).
-define(ALERT,1).
-define(CRITICAL,2).
-define(ERROR,3).
-define(WARNING,4).
-define(NOTICE,5).
-define(INFO,6).
-define(DEBUG,7).
-define(LOG_ALL,10).

ログメッセージ

レポートの例

logger:debug(#{got => connection_request, id => Id, state => State},
             #{report_cb => fun(R) -> {"~p",[R]} end})

メタデータ

フィルター

関数

logger_filters:domain/2 の実行例

16> logger_filters:domain(#{meta => #{domain => [my_app]}}, {log, equal, [my_app]}).
#{meta => #{domain => [my_app]}}
17> logger_filters:domain(#{meta => #{domain => [my_app]}}, {log, equal, [otp]}).
ignore

使用例

logger:set_handler_config(default, filter_default, log).
Filter = {fun logger_filters:domain/2, {stop, sub, [otp, sasl]}}.
logger:add_handler_filter(default, no_sasl, Filter).

ハンドラー

log(LogEvent, Config) -> void()

error_logger まわりでソースをいくつか参照しておく。

add_report_handler(Module, Args) when is_atom(Module) ->
    _ = logger:add_handler(?MODULE,?MODULE,#{level=>info,filter_default=>log}),
    gen_event:add_handler(?MODULE, Module, Args).
start() ->
    case whereis(?MODULE) of
        undefined ->
            ErrorLogger =
                #{id => ?MODULE,
                  start => {?MODULE, start_link, []},
                  restart => transient,
                  shutdown => 2000,
                  type => worker,
                  modules => dynamic},
            case supervisor:start_child(logger_sup, ErrorLogger) of
                {ok,_} ->
                    ok;
                Error ->
                    Error
            end;
        _ ->
            ok
    end.

start_link() ->
    gen_event:start_link({local, ?MODULE},
                         [{spawn_opt,[{message_queue_data, off_heap}]}]).

フォーマッター

設定

プライマリロガー設定 (詳細略)

ハンドラー設定 (詳細略)

Kernel 設定パラメータ

設定例: standard_io の代わりにファイルに出力する

[{kernel,
  [{logger,
    [{handler, default, logger_std_h,  % {handler, HandlerId, Module,
      #{config => #{type => {file,"log/erlang.log"}}}}  % Config}
    ]}]}].

設定例: 一行ログ形式にする

[{kernel,
  [{logger,
    [{handler, default, logger_std_h,
      #{formatter => {logger_formatter, #{single_line => true}}}}
    ]}]}].

設定例: pid を出力する

[{kernel,
  [{logger,
    [{handler, default, logger_std_h,
      #{formatter => {logger_formatter,
                        #{template => [time," ",pid," ",msg,"\n"]}}}}
    ]}]}].

設定例: エラーレベルのファイルとデバッグログファイルに分けて出力する

[{kernel,
  [{logger,
    [{handler, default, logger_std_h,
      #{level => error,
        config => #{type => {file, "log/erlang.log"}}}},
     {handler, info, logger_std_h,
      #{level => debug,
        config => #{type => {file, "log/debug.log"}}}}
    ]}]}].

error_logger との後方互換性

実装

gen_server.erl から error_logger 互換の呼び出し部分抜粋:

error_info(Reason, Name, From, Msg, Mod, State, Debug) ->
    ?LOG_ERROR(#{label=>{gen_server,terminate},
                 name=>Name,
                 last_message=>Msg,
                 state=>format_status(terminate, Mod, get(), State),
                 reason=>Reason,
                 client_info=>client_stacktrace(From)},
               #{domain=>[otp],
                 report_cb=>fun gen_server:format_log/1,
                 error_logger=>#{tag=>error}}),

error_logger.erl では #{error_logger => #{tag => Tag}} で拾う:

do_log(Level,{Format,Args},#{?MODULE:=#{tag:=Tag}}=Meta)

error_logger はその他の形式は捨てる

lager との関連

logger_sup
     |
  (child)
     |
     +-- error_logger (gen_event)
              |           - logger のハンドラー
              |           - error_logger:start() で開始される
      (gen_event handler)
              |
              +-- error_logger_lager_h
                          - error_logger (gen_event) のハンドラー
                          - sup. tree としては lager_handler_watcher_sup 配下に居る

エラーハンドリング

例: ファイルにログ出力するハンドラーを追加する

(略)

例: ハンドラーを実装する

(略)

ハンドラーを過負荷から保護する

メッセージキューの長さ

設定例

logger:add_handler(my_standard_h, logger_std_h,
                   #{config => #{type => {file,"./system_info.log"},
                                 sync_mode_qlen => 100,
                                 drop_mode_qlen => 1000,
                                 flush_qlen => 2000}}).

ログ要求のバースト制御

設定例

logger:add_handler(my_disk_log_h, logger_disk_log_h,
                   #{config => #{file => "./my_disk_log",
                                 burst_limit_enable => true,
                                 burst_limit_max_count => 20,
                                 burst_limit_window_time => 500}}).

過負荷に陥ったハンドラーの終了

Note

リファレンスからいくつか抜粋

ドメイン

[logger_filters](http://erlang.org/doc/man/logger_filters.html#domain-2}

TODO

以下、ドキュメント以外の話

simple logger

sys.config

[
 {kernel, [
           {logger,
            [
             %% デフォルトの Logger ハンドラーを無効化
             {handler, default, undefined}
            ]}
].

起動直後の Logger 設定、simple ハンドラーが居る:

#{handlers =>
      [#{filter_default => log,filters => [],
         formatter => {logger_formatter,#{}},
         id => error_logger,level => info,module => error_logger},
       #{filter_default => stop,
         filters =>
             [{remote_gl,{fun logger_filters:remote_gl/2,stop}},
              {domain,{fun logger_filters:domain/2,
                       {log,super,[otp,sasl]}}},
              {no_domain,{fun logger_filters:domain/2,
                          {log,undefined,[]}}}],
         formatter => {logger_formatter,#{}},
         id => simple,                        %% simple ハンドラー
         level => all,
         module => logger_simple_h}],
  module_levels => [],
  primary =>
      #{filter_default => log,filters => [],level => notice}}
[
 {kernel, [
           {logger,
            [
             %% デフォルトの Logger ハンドラーを無効化
             %% ソースコード上ここは参照されないが、ドキュメントにあるとおりにしておく
             {handler, default, undefined}
            ]},
           %% simple ハンドラーを無効化する
           {error_logger, silent}

].

この設定で起動したときの Logger 設定、simple が居ない(Lager を上げているので error_logger がある):

#{handlers =>
      [#{filter_default => log,filters => [],
         formatter => {logger_formatter,#{}},
         id => error_logger,level => info,module => error_logger}],
  module_levels => [],
  primary =>
      #{filter_default => log,filters => [],level => notice}}