<6>error_logger 使用

erlang中日志管理主要有error_loggger 模块,这个模块在系统启动时候就会启动,不过要自己配置是如何管理,整个管理是通过gen_event来注册事件来管理的
常用的
erl -kernel error_logger tty
erl -kernel error_logger false
erl -kernel error_loggger silent
erl -kernel error_logger \{file,\"filename\"\}
这几种方法,tyy是打印终端,false是默认,也是终端,silent是什么也不做,file是写到日志文件,看代码
start_link() ->
case gen_event:start_link({local, error_logger}) of
{ok, Pid} ->
simple_logger(?buffer_size),
{ok, Pid};
Error -> Error
end.
simple_logger(Buffer_size) when is_integer(Buffer_size) ->
gen_event:add_handler(error_logger, error_logger, Buffer_size).
就是将自己error_logger模块设置为handler;

当用户调用函数info_msg/2, error_msg/2 如何呢?
info_msg(Format, Args) ->
notify({info_msg, group_leader(), {self(), Format, Args}}).
notify(Msg) ->
gen_event:notify(error_logger, Msg).
handle_event(Event, State) ->
handle_event2(Event, State).

handle_event2(Event, {1, Lost, Buff}) ->
display(tag_event(Event)),
{ok, {1, Lost+1, Buff}};
handle_event2(Event, {N, Lost, Buff}) ->
Tagged = tag_event(Event),
display(Tagged),
{ok, {N-1, Lost, [Tagged|Buff]}};
handle_event2(_, State) ->
{ok, State}.
最后调用dispaly就是显示了,就是把信息格式化输出
那么是如何将信息输出到文件,或者干脆就不输出呢,


在启动的过程中,kernel application使用一个标准的事件处理句柄来代替之前的那个简单版本,这个版本会将输出到控制台的结果做个漂亮的格式化。当然,可以通过kernel的配置选项来修改上述行为,例如将结果输出到文件或者干脆什么都不干
kernel.erl文件:

start(_, []) ->
case supervisor:start_link({local, kernel_sup}, kernel, []) of
{ok, Pid} ->
Type = get_error_logger_type(),
error_logger:swap_handler(Type),
{ok, Pid, []};
Error -> Error
end.

首先,kernel把自己注册为一个名为kernel_sup的监控树进程,其次立刻就更换了error_logger的处理句柄。go on:

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选项

继续接着上面的代码往下走:

swap_handler(tty) ->
gen_event:swap_handler(error_logger, {error_logger, swap},
{error_logger_tty_h, []}),
simple_logger();
swap_handler({logfile, File}) ->
gen_event:swap_handler(error_logger, {error_logger, swap},
{error_logger_file_h, File}),
simple_logger();
swap_handler(silent) ->
gen_event:delete_handler(error_logger, error_logger, delete),
simple_logger();
swap_handler(false) ->
ok. % keep primitive event handler as-is

swap_handler就是替换日志的处理handle
当用户设置了 参数就将 handler替换为error_logger_tty_h或者error_logger_file_h模块来处理
error_logger_tty就是格式化,然后用终端输出
error_logger_file就是文件处理
error_logger_file_h.erl

首先初始化打开文件
%% This one is used when we takeover from the simple error_logger.
init({File, {error_logger, Buf}}) ->
case init(File, error_logger) of
{ok, {Fd, File, PrevHandler}} ->
write_events(Fd, Buf),
{ok, {Fd, File, PrevHandler}};
Error ->
Error
end;

接下来就是各种处理,以info_msg 为例
handle_event(Event, {Fd, File, PrevHandler}) ->
write_event(Fd, tag_event(Event)),
{ok, {Fd, File, PrevHandler}};
write_event(Fd, {Time, {info_msg, _GL, {Pid, Format, Args}}}) ->
T = write_time(maybe_utc(Time), "INFO REPORT"),
case catch io_lib:format(add_node(Format,Pid), Args) of
S when is_list(S) ->
io:format(Fd, T ++ S, []);
_ ->
F = add_node("ERROR: ~p - ~p~n", Pid),
io:format(Fd, T ++ F, [Format,Args])
end;
也就是如果你在error_logger注册事件,那么来的event包含的信息格式就是
info_msg, group_leader(), {self(), Format, Args}
需要自己处理,才能够得到合理的格式,例如
error_logger:add_file_handler(file_logger).
file_logger.erl代码如下
-module(file_logger).
-behaviour(gen_event).

-export([init/1, handle_event/2, terminate/2]).

init(File) ->
{ok, Fd} = file:open(File, write),
{ok, Fd}.
handle_event(ErrorMsg, Fd) ->
io:format(Fd, "***Error*** ~p~n", [ErrorMsg]),
{ok, Fd}.
terminate(_Args, Fd) ->
file:close(Fd)._logger, "mzh").

那么当调用error_logger:info_msg("aaaaa").时候,在文件mzh中就
***Error*** {info_msg,<0.24.0>,{<0.57.0>,"aaaa",[]}}
看来不经过格式化输出的信息很难懂啊

error_logger的信息打印有很多接口,
例如
info_msg
info_report
error_msg
error_report
warn_report 这些,有什么区别呢?以info信息为例
info_msg就是将信息写入文件,info_report呢
info_repot/1也同样写如,
info_report/2是将信息写入自定义的handle里面
info_report(Type, Report) -> ok
Types:
Type = any()
Report = report()
Sends a user defined information report event to the error logger. An event handler to handle the event is supposed to have been added. The event is ignored by the standard event handler.
最后还有warning_report/2要看系统的设置,
用warning_map()获取,也可以在启动时候,用erl +W(i,w)来设置

从error_logger_file_h.erl的write_event可以看出,系统代码,没有处理 type为
info_report, error_report的代码,也就是info_report/2, error_report/2,而
info_report/1,和error_report/1的type会被转化为std_info,照常处理

write_event(Fd, {Time, {info_report, _GL, {Pid, std_info, Rep}}}) ->
T = write_time(maybe_utc(Time), "INFO REPORT"),
S = format_report(Rep),
io:format(Fd, T ++ S ++ add_node("", Pid), []);

如果自己想要记录另一份log,只需要拷贝error_logger_file_h.erl稍作改动,注册自己的handler即可,

erlang的日志系统经常使用rb, 启动时候通过
erl -config log
也可以两个同时使用,就是记录两处log日志
erl -boot start_sasl \
-kernel error_logger \{file,\"mzh3\"\} \
-config log
就ok,


部分代码分析来自http://www.qingliangcn.com/2010/03/erlang%E4%B8%AD%E6%97%A5%E5%BF%97%E7%AE%A1%E7%90%86%E4%B8%89%E4%B8%A4%E8%AF%9D/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值