Erlang中日志管理三两话

来源:互联网 发布:大数据引擎基础 编辑:程序博客网 时间:2024/04/29 02:56

原创文章,转载请注明: 转载自庆亮的博客

本文链接地址: Erlang中日志管理三两话

日志系统的重要性就不罗嗦了,直接开始吧。

 

一、基本概念

 

Erlang中,通过两个概念管理错误事情:事件管理器(event manager)和事件处理句柄(event handles)。通常各种错误、警告和消息事件都会有Erlang运行时系统发送给事件管理器。在Erlang中,默认的事情管理器为error logger,其进程名注册为error_logger。默认情况下error_logger把这些事件直接输出到控制台上。

 

       在系统启动的一开始,error_logger只有一个简单的事件处理句柄,该处理句柄只是做缓冲和原始格式的打印操作。查看error_logger的源码,可以看到其启动过程如下:

 

-spec start() -> {'ok', pid()} | {'error', any()}.

 

start() ->

    case gen_event:start({local, error_logger}) of

    {ok, Pid} ->

        simple_logger(?buffer_size),

        {ok, Pid};

    Error -> Error

    end.

 

-spec start_link() -> {'ok', pid()} | {'error', any()}.

 

start_link() ->

    case gen_event:start_link({local, error_logger}) of

    {ok, Pid} ->

        simple_logger(?buffer_size),

        {ok, Pid};

    Error -> Error

end.

 

OK,只是简单的注册了一个进程,看看simple_logger的内容:

 

simple_logger(Buffer_size) when is_integer(Buffer_size) ->

gen_event:add_handler(error_logger, error_logger, Buffer_size).

 

看来error_logger在一开始的时候除了充当manager,也担当了handler的角色。

 

在启动的过程中,kernel application使用一个标准的事件处理句柄来代替之前的那个简单版本,这个版本会将输出到控制台的结果做个漂亮的格式化。当然,可以通过kernel的配置选项来修改上述行为,例如将结果输出到文件或者干脆什么都不干(忽略任何event)。口说无凭,还是继续看源码吧:

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

 

可以看出,如果我们真的希望什么都不做,那么应该传递参数 silent,而不是false,因为false情况下原来的原始处理句柄并没有被移除掉。

 

打开命令提示符,做个简单的实验(>表示命令提示符)

 

实验一,定制为false参数:

 

> erl -kernel error_logger false  //传递false参数

2> error_logger:error_msg("Haha").  // 尝试产生一个error event

ok{error_logger,{{

2010,2> 3,10},{15,9,1}},"Haha",[]}  //这里是结果

 

实验二,定制为silent参数:

 

> erl -kernel error_logger silent  //传递silent 参数

1>  error_logger:error_msg("Haha").

ok   // 看到这里只有ok,没有其他任何信息了!

2>

 

三、SASL

 

SASL全称System Architecture Support Libraries,提供如下几个服务:

alarm_handler

overload

rb

release_handler

systools

SASL带有error_logger的事件处理句柄用于格式化SASL错误和crash报告,这些句柄如下:

sasl_report_tty_h   sasl_report_file_h   error_logger_mf_h

细节参考OTP文档可知。

SASL的默认event handler会将crash报告、supervisor和进程报告输出到控制台,如果你希望看到这些信息,那么在erlang启动时需要加上指定的参数以启动sasl

为了方便日志的查看,通常sasl和其他日志是分开输出的,可以在启动erl时使用如下命令行参数:

-sasl sasl_error_logger {file,"/data/ log/sasl.log"}

 

 

四、站在巨人的肩膀上

 

这里并不打算介绍一个gen_event behavior模块的写法,而是想介绍一个非常强大、成熟的一个日志系统: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) //级别4info日志

 

原理很简单,利用code模块的load_binary来实现动态代码替换,但是确实非常强大,可以在需要的时候打开某些特定级别的日志,在系统负载高的时候或者不需要的时候关闭它。

原创粉丝点击