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的實現流程分析:
一、判斷lager_event進程有沒有啓動
二、利用lager_config模塊拿到最低日誌級別和traces
三、判斷當前日誌級別和traces是否爲空
四、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.
五、
Msg   =   case   Args   of
      A   when   is_list ( A )   ->
           safe_format_chop ( Format , Args , Size );
      _   ->
           Format
      end
這段代碼的做用就是把Format和Args變成字符串,而且不超出size的限制

六、gen_event:sync_notify(Pid, {log, lager_msg:new(Msg, Timestamp, Severity, Metadata, Destinations)})
Pid是lager_event事件管理器的進程號
也就是向事件管理器發送消息
注意這是一個同步發送,全部的事件處理器處理完成後,才返回ok

七、分析 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。

八、分析 lager_msg:new(Msg, Timestamp, Severity, Metadata, Destinations)
Msg就是最終要打印的字符串
Timestamp就是7中分析的元組
Severity是日誌級別
Metadata是屬性列表
Destinations是模塊ID列表

lager_msg:new最後返回一個#lager_msg{...}

九、gen_event:sync_notify(Pid, {log, lager_msg:new(Msg, Timestamp, Severity, Metadata, Destinations)})
也就是說應用代碼中的log:error(....),最終會異步的想lager_event時間管理器發送{log, #lager_msg{}}

下一步分析,事件處理器接收到消息後的處理流程。lager_file_backend會存儲消息到文件...
相關文章
相關標籤/搜索