.. 4 Gen_Event Behaviour .. _gen_event: =================== gen_eventビヘイビア =================== .. This chapter should be read in conjunction with gen_event(3), where all interface functions and callback functions are described in detail. この章を読む場合には、すべてのインタフェース関数やコールバック関数の詳細が書かれている、 ``gen_event(3)`` と一緒に読んでください。 .. 4.1 Event Handling Principles イベントハンドリングの原則 ========================== .. In OTP, an event manager is a named object to which events can be sent. An event could be, for example, an error, an alarm or some information that should be logged. OTP内には、 **イベント** を送ることができる、 **イベントマネージャ** という名前付きのオブジェクトがあります。イベントというのは、例えばエラーや警告、その他のログに保存すべき情報群などが考えられます。 .. In the event manager, zero, one or several event handlers are installed. When the event manager is notified about an event, the event will be processed by all the installed event handlers. For example, an event manager for handling errors can by default have a handler installed which writes error messages to the terminal. If the error messages during a certain period should be saved to a file as well, the user adds another event handler which does this. When logging to file is no longer necessary, this event handler is deleted. イベントマネージャには **イベントハンドラ** を複数インストールすることができます。イベントマネージャにイベントの通知がくると、インストールされたすべてのイベントハンドラで処理されます。例えば、エラーを取り扱うイベントマネージャをデフォルトでインストールすることができます。このイベントハンドラは、エラーメッセージをコンソールに書きだします。もし、エラーメッセージを一定期間ファイルに保存したいのであれば、そのようなイベントハンドラを作って登録すれば実現することができます。ログをファイルに保存する必要がなくなれば、そのイベントハンドラは削除されます。 .. An event manager is implemented as a process and each event handler is implemented as a callback module. イベントマネージャはプロセスとして実装され、イベントハンドラはコールバックモジュールとして実装されます。 .. The event manager essentially maintains a list of {Module, State} pairs, where each Module is an event handler, and State the internal state of that event handler. イベントマネージャは、 ``{モジュール, 状態}`` というペアのリストとして保持されます。このモジュールはイベントハンドラ、状態はイベントハンドラの内部ステートになります。 .. 4.2 Example サンプル ======== .. The callback module for the event handler writing error messages to the terminal could look like: エラーメッセージをコンソールに出力するイベントハンドラの実装となるコールバックモジュールのサンプルコードです。 .. code-block:: erlang -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. .. The callback module for the event handler writing error messages to a file could look like: エラーメッセージをファイルに書き出すイベントハンドラの実装となるコールバックモジュールのサンプルコードです。 .. code-block:: erlang -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). .. The code is explained in the next sections. コードについては次のセクションで説明します。 .. 4.3 Starting an Event Manager イベントマネージャの起動 ======================== .. To start an event manager for handling errors, as described in the example above, call the following function: 上記で説明したエラーハンドリングのためのイベントマネージャを起動するには、次の関数呼び出しをします。 .. code-block:: erlang gen_event:start_link({local, error_man}) .. This function spawns and links to a new process, an event manager. この関数は、新しいプロセスをspawnして、イベントマネージャと結びつけます。 .. The argument, {local, error_man} specifies the name. In this case, the event manager will be locally registered as error_man. 引数の ``{local, error_man}`` を使って名前を設定します。この場合には、イベントマネージャはローカルな ``error_man`` という名前で登録されます。 .. If the name is omitted, the event manager is not registered. Instead its pid must be used. The name could also be given as {global, Name}, in which case the event manager is registered using global:register_name/2. もし名前が省略されると、イベントマネージャは登録されません。その代わり、Pidを使う必要があります。名前には ``{global, 名前}`` という質得もできます。この場合、イベントマネージャは ``global:register_name/2`` を使って登録されます。 .. gen_event:start_link must be used if the event manager is part of a supervision tree, i.e. is started by a supervisor. There is another function gen_event:start to start a stand-alone event manager, i.e. an event manager which is not part of a supervision tree. イベントマネージャを監視ツリーの一部として仕様する場合には ``gen_event:start_link`` を使用すると、スーパバイザによって起動されます。スタンドアローンのイベントマネージャを起動するには、 ``gen_event:start`` という別の関数があります。この場合、監視ツリーの一部にはなりません。 .. 4.4 Adding an Event Handler イベントハンドラの追加 ====================== .. Here is an example using the shell on how to start an event manager and add an event handler to it: 次のコードは、イベントマネージャを起動し、イベントハンドラを追加します。対話モード内で動かしています。 .. code-block:: erlang 1> gen_event:start({local, error_man}). {ok,<0.31.0>} 2> gen_event:add_handler(error_man, terminal_logger, []). ok .. This function sends a message to the event manager registered as error_man, telling it to add the event handler terminal_logger. The event manager will call the callback function terminal_logger:init([]), where the argument [] is the third argument to add_handler. init is expected to return {ok, State}, where State is the internal state of the event handler. この関数は、イベントマネージャに対して、error_manとして登録するというメッセージを送り、terminal_loggerのイベントハンドラを追加するように伝えます。イベントマネージャは、コールバック関数の ``terminal_logger:init([])`` を呼び出します。この ``[]`` は、 ``add_handler`` の3番目の引数で、イベントハンドラの内部ステートを表します。 .. code-block:: erlang init(_Args) -> {ok, []}. .. Here, init does not need any input data and ignores its argument. Also, for terminal_logger the internal state is not used. For file_logger, the internal state is used to save the open file descriptor. ここでは、 ``init`` は入力データを必要としておらず、それを無視しています。terminal_loggerも内部ステートを利用してません。file_loggerはオープンしたファイルのデスクリプタを保存するために、内部ステートを利用しています。 .. code-block:: erlang init(File) -> {ok, Fd} = file:open(File, read), {ok, Fd}. .. 4.5 Notifying About Events イベントの通知 ============== .. code-block:: erlang 3> gen_event:notify(error_man, no_reply). ***Error*** no_reply ok .. error_man is the name of the event manager and no_reply is the event. error_manはイベントマネージャの名前で、no_replyがイベントです。 .. The event is made into a message and sent to the event manager. When the event is received, the event manager calls handle_event(Event, State) for each installed event handler, in the same order as they were added. The function is expected to return a tuple {ok, State1}, where State1 is a new value for the state of the event handler. イベントはメッセージになって、イベントマネージャに送られます。イベントを受け取ると、イベントマネージャは ``handler_event(Event, State)`` を、すべての登録されたイベントハンドラに対して、追加された順序で呼び出します。この関数は ``{ok, ステート1}`` というタプルを返すことが期待されています。この「ステート1」はイベントハンドラの新しいステートの値として使われます。 .. In terminal_logger: terminal_loggerのソースコード: .. code-block:: erlang handle_event(ErrorMsg, State) -> io:format("***Error*** ~p~n", [ErrorMsg]), {ok, State}. .. In file_logger: file_loggerのソースコード: handle_event(ErrorMsg, Fd) -> io:format(Fd, "***Error*** ~p~n", [ErrorMsg]), {ok, Fd}. .. 4.6 Deleting an Event Handler イベントハンドラの削除 ====================== .. code-block:: erlang 4> gen_event:delete_handler(error_man, terminal_logger, []). ok .. This function sends a message to the event manager registered as error_man, telling it to delete the event handler terminal_logger. The event manager will call the callback function terminal_logger:terminate([], State), where the argument [] is the third argument to delete_handler. terminate should be the opposite of init and do any necessary cleaning up. Its return value is ignored. この関数は、error_manという名前のイベントマネージャに対して、terminal_loggerというイベントハンドラを削除するように通知します。イベントマネージャはコールバック関数の ``terminal_logger:terminate([], State)`` を呼び出します。この時の引数は、 ``delete_handler`` の3番目の引数です。 ``terminate`` 関数は、 ``init`` 関数の逆で、必要な後処理を行います。返り値は無視されます。 .. For terminal_logger, no cleaning up is necessary: teminal_loggerの場合には後処理は不要: .. code-block:: erlang terminate(_Args, _State) -> ok. .. For file_logger, the file descriptor opened in init needs to be closed: file_loggerの場合には、 ``init`` の中で開いたファイルデスクリプタをクローズする必要があります。 .. code-block:: erlang terminate(_Args, Fd) -> file:close(Fd). .. 4.7 Stopping 停止 ==== .. When an event manager is stopped, it will give each of the installed event handlers the chance to clean up by calling terminate/2, the same way as when deleting a handler. イベントマネージャが停止させられると、 ``terminate/2`` を呼び出して、削除と同様にすべての登録済みのイベントハンドラの後片付け処理を行わせることができます。 .. 4.7.1 In a Supervision Tree 監視ツリー内 ------------ .. If the event manager is part of a supervision tree, no stop function is needed. The event manager will automatically be terminated by its supervisor. Exactly how this is done is defined by a shutdown strategy set in the supervisor. もし、イベントマネージャを監視ツリーの中で動かすのであれば、終了関数は不要です。監視ツリーが自動的にイベントマネージャを終了させます。正確には、スーパバイザの :ref:`shutdown_strategy <シャットダウン戦略>` を定義することで作業が完了します。 .. 4.7.2 Stand-Alone Event Managers スタンドアローンのイベントマネージャ ------------------------------------ .. An event manager can also be stopped by calling: イベントマネージャが監視ツリーの一部でない場合には次のように ``stop`` 関数を呼び出すと停止させることができます。 .. code-block:: erlang > gen_event:stop(error_man). ok Copyright (c) 1991-2009 Ericsson AB