本文章目的是解决产品化环境下,将日志输出到日志文件而不在erlang shell终端中显示,方便在shell中执行调试函数!
1、sasl
sasl提供错误日志,系统过载保护等,它有定义了下面三个日志处理器
sasl_report_tty_h : 将日志输出到控制台
sasl_report_file_h : 将日志输出到单个文件
error_logger_mf_h :循环日志文件记录
sasl启动配置文件elog.config 如下
[{sasl, [{sasl_error_logger, false}, {errlog_type, error}, {error_logger_mf_dir, "/data/sasl/log"}, {error_logger_mf_maxbytes, 10485760}, {error_logger_maxfiles, 10} ] }].
{sasl_error_logger,false}表示错误信息不会输出到控制台(看sasl.erl代码)
{sasl_error_logger,tty} 表示错误信息输出到控制台(默认情况下也是输出到控制台)
{sasl_error_logger,{file,"../log/test.log"} 表示错误信息写到单一文件test.log
{errorlog_type,error} 表示只输出error信息,也可以是info,warnning等
{error_logger_mf_dir,"data/sasl/log"} 表示输出到循环日志文件log
sasl.erl 代码内容
start(_, []) ->
Handler = get_sasl_error_logger(),
Type = get_sasl_error_logger_type(),
Mf = get_error_logger_mf(),
add_sasl_error_logger(Handler, Type),
add_error_logger_mf(Mf),
State = #state{sasl_error_logger = Handler, error_logger_mf = Mf},
case supervisor:start_link({local, sasl_sup}, sasl, []) of
{ok, Pid} -> {ok, Pid, State};
Error -> Error
end.
...
get_sasl_error_logger() ->
case application:get_env(sasl, sasl_error_logger) of
{ok, false} -> undefined;
{ok, tty} -> tty;
{ok, {file, File}} when is_list(File) -> {file, File, [write]};
{ok, {file, File, Modes}} when is_list(File), is_list(Modes) ->
{file, File, Modes};
{ok, Bad} -> exit({bad_config, {sasl, {sasl_error_logger, Bad}}});
_ -> undefined
end.
get_sasl_error_logger_type() ->
case application:get_env(sasl, errlog_type) of
{ok, error} -> error;
{ok, progress} -> progress;
{ok, all} -> all;
{ok, Bad} -> exit({bad_config, {sasl, {errlog_type, Bad}}});
_ -> all
end.
get_error_logger_mf() ->
case catch get_mf() of
{'EXIT', Reason} ->
exit(Reason);
Mf ->
Mf
end.
get_mf() ->
Dir = get_mf_dir(),
MaxB = get_mf_maxb(),
MaxF = get_mf_maxf(),
case {Dir, MaxB, MaxF} of
{undefined,undefined,undefined} ->
undefined;
{undefined,_,_} ->
exit({missing_config, {sasl, error_logger_mf_dir}});
{_,undefined,_} ->
exit({missing_config, {sasl, error_logger_mf_maxbytes}});
{_,_,undefined} ->
exit({missing_config, {sasl, error_logger_mf_maxfiles}});
R ->
R
end.
get_mf_dir() ->
case application:get_env(sasl, error_logger_mf_dir) of
{ok, false} -> undefined;
{ok, Dir} when is_list(Dir) -> Dir;
undefined -> undefined;
{ok, Bad} -> exit({bad_config, {sasl, {error_logger_mf_dir, Bad}}})
end.
get_mf_maxb() ->
case application:get_env(sasl, error_logger_mf_maxbytes) of
{ok, MaxB} when is_integer(MaxB) -> MaxB;
undefined -> undefined;
{ok, Bad} -> exit({bad_config, {sasl, {error_logger_mf_maxbytes, Bad}}})
end.
get_mf_maxf() ->
case application:get_env(sasl, error_logger_mf_maxfiles) of
{ok, MaxF} when is_integer(MaxF), MaxF > 0, MaxF < 256 -> MaxF;
undefined -> undefined;
{ok, Bad} -> exit({bad_config, {sasl, {error_logger_mf_maxfiles, Bad}}})
end.
erl -boot start_sasl -config elog
%% 查看handler
gen_event:which_handlers(error_logger).
[log_mf_h,error_logger,error_logger_tty_h]
]
问题来了,明明设了{sasl_error_logger,false},handler列表中为啥error_logger_tty_h还存在呢? 下面再看一下kernel模块
2、kernel
kernel.app 部分配置如下
error_logger, file_server_2, fixtable_server, global_group, global_name_server, heart, init, kernel_config, kernel_sup, net_kernel, net_sup, rex, user, os_server, ddll_server, erl_epmd, inet_db, pg2]}, {applications, []}, {env, [{error_logger, tty}]},
kernel.erl部分源码
start(_, []) ->
case supervisor:start_link({local, kernel_sup}, kernel, []) of
{ok, Pid} ->
%% add signal handler
case whereis(erl_signal_server) of
%% in case of minimal mode
undefined -> ok;
_ ->
ok = gen_event:add_handler(erl_signal_server, erl_signal_handler, [])
end,
%% add error handler
Type = get_error_logger_type(),
case error_logger:swap_handler(Type) of
ok -> {ok, Pid, []};
Error ->
%% Not necessary since the node will crash anyway:
exit(Pid, shutdown),
Error
end;
Error -> Error
end.
...
get_error_logger_type() ->
case application:get_env(kernel, error_logger) of
{ok, tty} -> tty;
{ok, {file, File}} when is_list(File) -> {logfile, File};
{ok, false} -> false;
{ok, silent} -> silent;
undefined -> tty; % default value
{ok, Bad} -> exit({bad_config, {kernel, {error_logger, Bad}}})
end.
kernel启动的时候,通过读取kernel.app的配置,更换error_logger处理句柄为tty,也只有设置为silent的时候才不会将错误日志输出到控制台
erl -kernel error_logger silent -boot start_sasl -config elog
%% 查看error_logger处理句柄
gen_event:which_handlers(error_logger).
[log_mf_h,error_logger]
这个时候,tty句柄就不在了
3 、成熟的日志系统
ejabberd的日志系统。它包含两个部分:
dynamic_compile.erl 动态编译基础模块
ejabberd_logger_h.erl 这是个gen_event behavior模块,可以定制我们写日志的行为
ejabberd_loglevel.h 这个是ejabberd日志系统的精华,可以在运行时动态调节日志的输出级别。
用法很简单:
error_logger:add_report_handler(ejabberd_logger_h, LogPath),
ejabberd_loglevel:set(4) //级别4是info日志
这个时候,就可以将日志输出到文档,tty不会再显示日志信息