lager:error()调用的完整流程分析

这一节分析一条日志记录的完整流程

假设源代码中要打印日志,有这么一行   lager:error([{request, RequestID},{vhost, Vhost}], "Permission denied ~s", [User])

经过上一节的分析,那么经过lager的transform后,会变成:

lager:dispatch_log(error, Traces, Message, Arguments, 4096)

error:日志级别
Traces:需要追踪的日志属性,有预置的pid, module, function and line,还可以自定义
[{request,RequestID},{vhost,Vhost},{application,App},{module,Mod},{function,Fun},{line,Line},{pid,pid_to_list(self())},{node,node()}]
Message: "Permission denied ~s"
Arguments: [User]
truncation_size:4096,默认值

dispatch_log的实现流程分析:
1、判断lager_event进程有没有启动
2、利用lager_config模块拿到最低日志级别和traces
3、判断当前日志级别和traces是否为空
4、lager_util:check_traces(Metadata,SeverityAsInt,TraceFilters,[]),含有属性的Metadata和ServerityAsInt与TraceFilters(格式为:[{Filter, LevelMask, {lager_file_backend, File}}],Filter为key-value元组,LevelMask为{mask, 2#00001111},)进行匹配
%% (Metadata,SeverityAsInt,TraceFilters,[])
%% TraceFilters = [{Filter, LevelMask, {lager_file_backend, File}}]
%% Metadata = [{request,RequestID},{vhost,Vhost},{application,App},{module,Mod},{function,Fun},{line,Line},{pid,pid_to_list(self())},{node,node()}]
%% 函数的整体流程是去匹配每一个TraceFilter,也就是说同一条日志可能会匹配上多个Trace File
%% 函数最终的返回值是匹配的trace file对应的事件处理器的module id,{lager_file_backend, File}
check_traces(_, _,  [], Acc) ->
    lists:flatten(Acc);
check_traces(Attrs, Level, [{_, {mask, FilterLevel}, _}|Flows], Acc) when (Level band FilterLevel) == 0 ->
%% 首先匹配日志级别
    check_traces(Attrs, Level, Flows, Acc);
check_traces(Attrs, Level, [{Filter, _, _}|Flows], Acc) when length(Attrs) < length(Filter) ->
%% 过滤的属性比定义的属性还多,说明肯定有一个过滤属性没有,直接匹配Flows
    check_traces(Attrs, Level, Flows, Acc);
check_traces(Attrs, Level, [Flow|Flows], Acc) ->
    check_traces(Attrs, Level, Flows, [check_trace(Attrs, Flow)|Acc]).

%% 检查一个trace filter
%% Attrs是日志打印语句中的属性列表
%% Filter是我想要的属性的列表
%% Dest = {lager_file_backend, File},也就是module id
check_trace(Attrs, {Filter, _Level, Dest}) ->
    case check_trace_iter(Attrs, Filter) of
        true ->
            Dest;
        false ->
            []
    end.

%% Filter中的所有条件都满足,这个trace才算可以
check_trace_iter(_, []) ->
    true;
check_trace_iter(Attrs, [{Key, Match}|T]) ->
    case lists:keyfind(Key, 1, Attrs) of
        {Key, _} when Match == '*' ->
            check_trace_iter(Attrs, T);
        {Key, Match} ->
            check_trace_iter(Attrs, T);
        _ ->
            false
    end.
5、
Msg   =   case   Args   of
      A   when   is_list ( A )   ->
           safe_format_chop ( Format , Args , Size );
      _   ->
           Format
      end
这段代码的作用就是把Format和Args变成字符串,并且不超出size的限制

6、gen_event:sync_notify(Pid, {log, lager_msg:new(Msg, Timestamp, Severity, Metadata, Destinations)})
Pid是lager_event事件管理器的进程号
也就是向事件管理器发送消息
注意这是一个同步发送,所有的事件处理器处理完成后,才返回ok

7、分析 lager_util:format_time()
format_time() ->
    format_time(maybe_utc(localtime_ms())).
这块代码其实挺容易阅读的。
把当前时间最终变成 {[[2013],'-',[0,3],'-',[3,0]],[[1,8],':',[1,6],':',[5,5],'.',[2,4,9],' ', "UTC"]};如果sasl或stdlib应用配置了utd_log为true,那么时间最终会包含UTC。

8、分析 lager_msg:new(Msg, Timestamp, Severity, Metadata, Destinations)
Msg就是最终要打印的字符串
Timestamp就是7中分析的元组
Severity是日志级别
Metadata是属性列表
Destinations是模块ID列表

lager_msg:new最后返回一个#lager_msg{...}

9、gen_event:sync_notify(Pid, {log, lager_msg:new(Msg, Timestamp, Severity, Metadata, Destinations)})
也就是说应用代码中的log:error(....),最终会异步的想lager_event时间管理器发送{log, #lager_msg{}}

下一步分析,事件处理器接收到消息后的处理流程。lager_file_backend会存储消息到文件...










你可能感兴趣的:(erlang,error,log,lager,dispatch_log)