构造基于OTP的系统(erlang学习笔记)

通用的事件处理

event_handler.erl

-module(event_handler).
-export([make/1, add_handler/2, event/2]).

make(Name) ->
  register(Name, spawn(fun() -> my_handler(fun no_op/1) end)).

add_handler(Name, Fun) -> Name ! {add, Fun}.

event(Name, X) -> Name ! {event, X}.
my_handler(Fun) ->
  receive
    {add, Fun1} ->
      my_handler(Fun1);
    {event, Any} ->
      (catch Fun(Any)), my_handler(Fun)
  end.
no_op(_) -> void.

创建了一个“啥也不干”的事件处理程序,它的名字是Name(一个原子)。这提供了一个可以发送事件的位置。
event_handler: event(Name,X)

向Name事件处理程序发送一个X事件。

event_handler:add_handler(Name,Fun)
给Name事件处理程序增加一个事件处理方法Fun。现在,当事件X发生的时候,事件处理程序会执行Fun(X)。

现在我们就来建立一个事件处理程序,并产生一个错误事件:

1>event_handler:make(errors).
true
2>event _handler:event(errors,hi).
{event,hi}

我们可以看到什么也没发生,因为我们没有在事件处理程序中安装回调模块。
要让事件处理程序做点事,我们需要写一个回调模块,然后把它安装到事件处理程序中去。下面的代码就是一个事件处理程序回调模块:
 motor_controller.erl

-module(motor_controller).
-export([add_event_handler/0]).
add_event_handler() ->
  event_handler:add_handler(errors, fun controller/1).
controller(too_hot) ->
  io:format("Turn off the motor~n");
controller(X) ->
  io:format("~w ignored event:~p~n", [?MODULE, X]).

编译,然后安装它:

3>c(motor_controller) .
{ok,motor_controller}
4>motor_controller:add_event_handler().
{add,#Fun}

现在,我们来给这个处理程序发送一个事件,它应该被motor:controller:controller/1函数处理。

5>event_handler:event(errors,cool).
motor_controller ignored event: cool
{event,cool}
6>event_handler:event(errors,too_hot).
Turn off the motor
{event,too_hot}

首先,程序提供了一个可以发送事件的名字。这里它是注册为errors的一个进程。然后我们定义了向这个进程发送消息时所使用的协议。值得留意的一点是,我们这里并没有明确当事件到达的时候要做什么动作。实际上,这个时候做的动作就是noOp(X),它就是“什么也不做”的动作。之后,在下一步,程序安装了一个自定义的事件处理程序。

其实,所谓的事件处理程序,最为核心的概念就在于它提供了一个基础架构,我们可以安装自定义的处理程序。

错误日志的基础架构也遵循这一模式。这也就意味着我们同样可以在出错日志中安装各种处理程序,以便在事件发生的时候做不同的事情。除此之外,警报处理系统也遵循着相同的架构模式。
 

错误日志

 OTP系统已经内置了一个可自定义的错误日志模块。我们可以从3种不同的视角来看错误日志。程序员的视角关注代码中要记录一个错误日志的函数调用;配置的视角关注错误日志如何存储以及被保存在哪里;报告的视角则关心错误发生之后,如何进行分析。我们将逐条讲述这些内容。

记录一个错误

错误日志的API很简单,下面是这些API的一部分 

@spec error_logger:error-msg(String) -> ok

向错误日志发送一个错误消息。

1> error_logger: error_msg("An error has occurred\n").
ERROR REPORTmm= 4-Jan-2024::23:42:11 ===
An error has occurred
ok
@spec error_logger: error_msg(Format,Data) ->ok

向错误日志发送一个错误消息它的参数与io:format(Format,Data)函数的参数一样。

2> error _logger:error_msg("~s,an error has occurred\n",["Joe"]).
=ERROR REPORT=== 4-Jan-2024::24:23:17 ===
Joe,an error has occurred
ok
@spec error_logger:error_report(Report) -> ok

 向错误日志发送一个标准错误报告。

@type Report = [{Tag,Data} | term()] | string() | term()]
@type Tag = term()
@type Data = term()
3>error_logger:error_report([{tag1,data1},a_term,{tag2,data}]).
=ERROR REPORT==== 4-Jan-2024::27:31:44 ===
tag1: data1
a_term
tag2: data

 这些只是可用错误日志API中的一小部分

配置错误日志

1.标准错误日志

 启动Erlang的时候,我们可以给系统设置如下一些启动参数。

$ erl -boot start_clean

这会创建一个适合程序开发的环境,只会提供错误日志的简单形式(不带启动参数的erl命令效果等同于erl -boot start_clean)。

$erl -boot start_sasl

这会创建一个适合产品化系统的环境。SASL是System Architecture Support Libraries的缩写,它负责错误日志、过载保护等。

2.不进行配置的SASL

这是在启动SASL时,不进行配置的情况:

$ erl -boot start_sasl

 现在我们调用error_logger的方法来报告错误:

1>error_logger: error_msg("This is an error\n").
=ERROR REPORT==== 4-Jan-2024::30:34:24 ===
This is an error
ok

注意,错误是在Erlang shell中报告出来的,错误报告取决于错误日志记录器的配置。
 

3.控制记录何种日志

错误日志记录器会产生下面几种类型的报告
 监管报告。在监管进程启动或者停止被监管的进程时,会产生这个报告。
进程报告。每次OTP监管进程启动或者停止的时候会产生这个报告。
崩溃报告。当被监管的进程退出时,如果它的退出原因不是normal或者shutdown,就会产生这个报告。

这3种报告是自动产生的,我们无需关心。

在日后对错误日志进行分析的时候,我们可以用这3种标签协助我们来检查问题,过滤日志条目。在对日志进行配置的时候,我们也可以指定,比如,只保存错误,其他的信息不予保存。下面我们就来写一个这样的配置文件。
 elog1.config

[{sasl,[{sasl_error_logger,false}]}].

如果用这个配置文件启动系统,只有错误报告会被记录,进程报告之类的全部被忽略掉。而且,所有的错误报告都在shell当中。

$ erl -boot start_sasl -config elogl
1> error_logger:error_msg("This is an error\n").
=ERROR REPORT==== 4-Jan-2024::34:24:29 ===
This is an error
ok
4.文本文件和shell

 下一个配置文件列出了shell中的错误报告,同时在shell中报告的所有东西的副本也生成一个文件: elog2.config

[{sasl,[{sasl_error_logger,{file,"/src/error_logs/THELOG"}}]}].

我们启动Erlang,生成一些错误信息,然后看看日志文件以检查这个配置文件的效果。

$ erl -boot start_sasl -config elog2
1>error_logger:error_msg("This is an error\n").
=ERROR REPORT==== 4-Jan-2024::38:15:52 ===
This is an error ok

 我们可以查看/src/error_logs/THELOG文件的内容.

5.循环日志和shell

日志会在shell以及一个循环日志中同时输出
  elog3.config

[{sasl,[
      {sas1_error_logger, false},
      {error_logger_mf_dir, " /src/error_logs"},
      {error_logger_mf_maxbytes, 10485760},
      {error_logger_mf_maxfiles, 10}
]}].
$erl -boot start_sasl -config elog3
1>error_logger:error_msg("This is an error\n").
=ERROR REPORT==== 4-Jan-2024::53:47:22 ===
This is an error
false

 当用这个配置运行系统的时候,所有的错误也会输出到这个循环日志中

6.产品化环境

elog4.config 

[{sasl,[
      {sasl_error_logger,false},
      {errlog_type,error},
      {error_logger_mf_dir, "/src/error_logs"},
      {error_logger_mf_maxbytes,10485760},
      {error_logger_mf_maxfiles,10}
]}].

运行的时候,这个配置与前一个配置的效果看起来很相似,区别在于,只有错误会被记录到日志中。

你可能感兴趣的:(erlang,学习,笔记)