gen_event

    一整套gen_event由两部分组成:事件管理器(一个)和事件处理器(0个或多个)。事件管理器被实现为一个进程,事件处理器被实现为回调模块。

    例1,向Erlang标准的错误日志管理器注册自己的事件处理器。

    Erlang标准的错误日志管理器是一个注册名为error_logger的进程,该进程在所有Erlang/OTP系统中都存在。当你调用error_logger模块中的日志函数(info_msg, error_msg)时,所有的日志事件都会被发送给这个进程。如果你注册了自己的事件处理器,则你可以捕获这些日志事件。

-module(custom_error_report).
-behaviour(gen_event).
-export([register_with_logger/0,register/0]).
-export([init/1, handle_event/2, handle_call/2,handle_info/2,terminate/2,code_change/3]).

register_with_logger()->
  error_logger:add_report_handler(?MODULE).    %% 使用error_logger封装好的函数进行注册

register() ->
  gen_event:add_handler(error_logger,?MODULE,[]). %% 和error_logger:add_report_handler(?MODULE)
                                                  %%  等价

init([]) ->
  {ok,[]}.

handle_event(_Event,State) ->
  io:format("event:~p~n",[_Event]),
  {ok, State}.
  %remove_handler.                           %% 从管理器移除这个handler(模块)

handle_call(_Request,State) ->
  {ok, ok, State}.
handle_info(_Info,State) ->
  {ok,State}.

terminate(Reason, _State) ->
  io:format("terminate: ~p~n",[Reason]),
  ok. 
code_change(_OldVsn, State, _Extra) ->
  {ok, State}.

   shell上执行

1> custom_error_report:register().
ok
2> 
2> error_logger:info_msg("this is an information message.~n").
event:{info_msg,<0.24.0>,{<0.31.0>,"this is an information message.~n",[]}}
ok
3> 
=INFO REPORT==== 1-Jan-2002::05:12:51 ===
this is an information message.

   事件处理器可以注册多个,同一个回调模块还可以重复注册。

3> custom_error_report:register_with_logger().                
ok
4> 
4> error_logger:info_msg("this is a nother information message.~n"). 
event:{info_msg,<0.24.0>,
                {<0.31.0>,"this is a nother information message.~n",[]}}
event:{info_msg,<0.24.0>,
                {<0.31.0>,"this is a nother information message.~n",[]}}
ok
=INFO REPORT==== 1-Jan-2002::05:17:24 ===
this is a nother information message.

    至此,error_logger和两个事件处理器(custom_error_report模块)的关系

gen_event

   

1. 启动事件管理器

    从上面的例子中可以看到,要使用gen_event,必须启动一个事件管理器进程。用gen_event:start_link或gen_event:start来启动。

5> gen_event:start_link({local, error_man}).
{ok,<0.37.0>}
6> 
6> gen_event:start({local, error_man1}).    
{ok,<0.39.0>}
8>
8> gen_event:start().                   
{ok,<0.42.0>}

  start_link,start可以注册一个本地的名字,或者不注册名字,但后续注册接口需要进程的PID。(start_link和start的区别:如果gen_event是监督树的一部分时必须用start_link,其他情况下用start)。

 2.添加/删除事件管理器

   用gen_event:add_handler来向事件管理器添加回调模块。用gen_event:delete_handler删除回调模块。

9> gen_event:add_handler(error_man,custom_error_report,[]).
ok

3.向事件管理器通知事件

   用gen_event:notify向事件管理器进程发送事件消息

10> gen_event:notify(error_man, "this is a notifying message").
event:"this is a notifying message"
ok

4.注意事项

  (1) State和事件处理器相关,即如果有多个事件处理器,则每个事件处理器都有各自的State,体现在每个回调模块内。

  (2) init在事件处理器注册成功(add_handler)后调用,terminate在事件处理器被移除(delete_handler)后调用 。

  

   例2

   把日志信息写到终端的回调模块可能是这样的:

-module(terminal_logger).
-behaviour(gen_event).

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

init(_Args) ->
    {ok, []}.

handle_event(ErrorMsg, State) ->
    io:format("***Error*** ~p~n", [ErrorMsg]),
    {ok, State}.

terminate(_Args, _State) ->
    ok.


   把日志信息写入文件的回调模块可能是这样:

-module(file_logger).
-behaviour(gen_event).

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

init(File) ->
    {ok, Fd} = file:open(File, read),
    {ok, Fd}.

handle_event(ErrorMsg, Fd) ->
    io:format(Fd, "***Error*** ~p~n", [ErrorMsg]),
    {ok, Fd}.

terminate(_Args, Fd) ->
    file:close(Fd).


你可能感兴趣的:(erlang,gen_event)