Parse Transform
Lager最有味道的地方之一就是每次打印日志都会显示模块名/函数名/行号,而且不是使用的宏,其中诀窍便是编译时添加{parse_tranform, lager_transform}选项
Erlang官方介绍如下
{parse_transform,Module}
编译erl文件时,若引用以上字段,则会在文件生成abstract code之后,调用Module:parse_transform/2处理abstract code转化出新的abstract code
Lager项目就是使用以上选项实现的代码转换,如代码中调用的lager:debug/info/error在服务启动后是找到相关函数的,以上编译后实际调用的lager:dispatch_log/9
获取abstract code
编写简易模块
-module(hello).
-export([hello/0]).
hello() ->
io:format("Hello author, you are so cool !~n", []).
获取abstract code
Eshell V7.3 (abort with ^G)
1> c(hello, [debug_info]).
{ok,hello}
2> rp(beam_lib:chunks(hello, [abstract_code])).
{ok,{hello,
[{abstract_code,
{raw_abstract_v1,
[{attribute,1,file,{"hello.erl",1}},
{attribute,1,module,hello},
{attribute,3,export,[{hello,0}]},
{function,5,hello,0,
[{clause,5,[],[],
[{call,6,
{remote,6,{atom,6,io},{atom,6,format}},
[{string,6,"Hello author, you are so cool !~n"},
{nil,6}]}]}]},
{eof,7}]}}]}}
ok
调整模块,加入lager调用,自行获取abstract code对照parse_transform/2处理,会发现lager:debug/info/error等实际调用为lager:dispatch_log/9
-module(hello).
-compile([{parse_transform, lager_transform}]).
-export([hello/0]).
hello() ->
lager:info("Hello author, you are so man ! ~n", []),
io:format("Hello author, you are so cool !~n", []).
编译完成后,反编译可获取代码实际转化内容
-module(hello).
-lager_records([]).
-compile([]).
-export([hello/0]).
hello() ->
case {whereis(lager_event),
lager_config:get(loglevel, {0, []})}
of
{undefined, _} ->
fun () -> {error, lager_not_running} end();
{__Pidhello6, {__Levelhello6, __Traceshello6}}
when __Levelhello6 band 64 /= 0 orelse
__Traceshello6 /= [] ->
lager:do_log(info,
[{application, msync}, {module, hello},
{function, hello}, {line, 6},
{pid, pid_to_list(self())}, {node, node()}
| lager:md()],
"Hello author, you are so man ! ~n", [], 4096, 64,
__Levelhello6, __Traceshello6, __Pidhello6);
_ -> ok
end,
io:format("Hello author, you are so cool !~n", []).