一整套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模块)的关系
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).