为什么80%的码农都做不了架构师?>>>   

使用 lager 的时候,在编译应用的时候,需要加入选项 {parse_transform, lager_transform}
erlc 会在编译你的项目源代码的时候,把生成的 abstract format forms 交给 lager_transform 模块的 parse_transform 函数处理,完成后,erlc 会把这些经过修改的 抽象码 编译成 bytecode。
项目中使用 lager 比如,
    lager:error("Some message")
经过转换后,到底变成了什么样呢?
入口:
parse_transform(AST, Options) ->
%% AST 为源代码的abstract format形式
    TruncSize = proplists:get_value(lager_truncation_size, Options, ?DEFAULT_TRUNCATION),
    put(truncation_size, TruncSize),
    erlang:put(records, []),
    %% .app file should either be in the outdir, or the same dir as the source file
    guess_application(proplists:get_value(outdir, Options), hd(AST)),
%%  hd(AST) 结果  {attribute,1,file,{"../src/hello.erl",1}    hd == head of
%%  guess_application,读取src目录下的*.app.src文件,获取应用的名字,放到进程字典中
    walk_ast([], AST).

%% 遍历abstract format forms,修改 lager:error 语句。(附录一个abstract format的例子)
walk_ast (Acc, []) ->
    insert_record_attribute(Acc);
walk_ast(Acc, [{attribute, _, module, {Module, _PmodArgs}}=H|T]) ->    %%处理module
    %% A wild parameterized module appears!
    put(module, Module),
    walk_ast([H|Acc], T);
walk_ast(Acc, [{attribute, _, module, Module}=H|T]) ->     %%处理module
    put(module, Module),
    walk_ast([H|Acc], T);
walk_ast(Acc, [{function, Line, Name, Arity, Clauses}|T]) ->    %% 处理function,关键点
    put(function, Name),
    walk_ast([{function, Line, Name, Arity,
                walk_clauses([], Clauses)}|Acc], T);
walk_ast(Acc, [{attribute, _, record, {Name, Fields}}=H|T]) ->  %% 处理record
%% 把record定义,存储到进程字典中;比如一个record定义为,-record(hello, {a, b}),存储到进程字典中为{hello, [a, b]}
    FieldNames = lists:map(fun({record_field, _, {atom, _, FieldName}}) ->
                FieldName;
            ({record_field, _, {atom, _, FieldName}, _Default}) ->
                FieldName
        end, Fields),
    stash_record({Name, FieldNames}),
    walk_ast([H|Acc], T);
walk_ast(Acc, [H|T]) ->
    walk_ast([H|Acc], T).

%% 处理函数从句(;分割几部分)
walk_clauses (Acc, []) ->
    lists:reverse(Acc);
walk_clauses(Acc, [{clause, Line, Arguments, Guards, Body}|T]) ->
    walk_clauses([{clause, Line, Arguments, Guards, walk_body([], Body)}|Acc], T).

%% 处理函数从句的语句(-> 之后的部分)
walk_body (Acc, []) ->
    lists:reverse(Acc);
walk_body(Acc, [H|T]) ->
    walk_body([transform_statement(H)|Acc], T).

transform_statement 处理baody语句(也就是表达式列表)中的每一条,这个函数一共有5个从句
%% 处理 lager:error("log") 这种调用
%% erlc编译过程中会修改abstract format forms,改成  {call, Line, {remote, Line, {atom,Line,lager},{atom,Line,dispatch_log}} 这种形式
%% 也就是说最终应用在运行过程中会调用 lager:dispatch_log函数,并传递相关参数
transform_statement ({call, Line, {remote, _Line1, {atom, _Line2,  lager },
            {atom, _Line3,  Severity }}, Arguments0} = Stmt) ->
    case lists:member(Severity, ?LEVELS) of
        true ->
            DefaultAttrs0 = {cons, Line, {tuple, Line, [
                        {atom, Line, module}, {atom, Line, get(module)}]},
                    {cons, Line, {tuple, Line, [
                                {atom, Line, function}, {atom, Line, get(function)}]},
                        {cons, Line, {tuple, Line, [
                                    {atom, Line, line},
                                    {integer, Line, Line}]},
                        {cons, Line, {tuple, Line, [
                                    {atom, Line, pid},
                                    {call, Line, {atom, Line, pid_to_list}, [
                                            {call, Line, {atom, Line ,self}, []}]}]},
                        {cons, Line, {tuple, Line, [
                                    {atom, Line, node},
                                    {call, Line, {atom, Line, node}, []}]},
                         {nil, Line}}}}}},            %% 这个元组的定义乍看上去恐怖,再看还是很恐怖
            %% 转换成代码后,是这样的,[{module, mod},{function, fun},{line, lin},{pid,pid_to_list(self())},{node, node()}]
            DefaultAttrs = case erlang:get(application) of
                undefined ->
                    DefaultAttrs0;
                App ->
                    %% stick the application in the attribute list
                    concat_lists({cons, Line, {tuple, Line, [
                                    {atom, Line, application},
                                    {atom, Line, App}]},
                            {nil, Line}}, DefaultAttrs0)
            end,        %% DefaultAttrs 最终的格式嵌套的包含后面这些信息 application-module-function-line-pid-node
            {Traces, Message, Arguments} = case Arguments0 of    %% 处理函数调用时的参数
                [Format] ->
                    {DefaultAttrs, Format, {atom, Line, none}};
                [Arg1, Arg2] ->
                    %% some ambiguity here, figure out if these arguments are
                    %% [Format, Args] or [Attr, Format].
                    %% The trace attributes will be a list of tuples, so check
                    %% for that.
                    case {element(1, Arg1), Arg1} of
                        {_, {cons, _, {tuple, _, _}, _}} ->
                            {concat_lists(Arg1, DefaultAttrs),
                                Arg2, {atom, Line, none}};
                        {Type, _} when Type == var;
                                       Type == lc;
                                       Type == call;
                                       Type == record_field ->
                            %% crap, its not a literal. look at the second %% 作者骂娘了?
                            %% argument to see if it is a string
                            case Arg2 of
                                {string, _, _} ->
                                    {concat_lists(Arg1, DefaultAttrs),
                                        Arg2, {atom, Line, none}};
                                _ ->
                                    %% not a string, going to have to guess
                                    %% it's the argument list
                                    {DefaultAttrs, Arg1, Arg2}
                            end;
                        _ ->
                            {DefaultAttrs, Arg1, Arg2}
                    end;
                [Attrs, Format, Args] ->
                    {concat_lists(Attrs, DefaultAttrs), Format, Args}
            end,
            {call, Line, {remote, Line, {atom,Line,lager},{atom,Line,dispatch_log}}, %% lager:dispatch_log()
                [                                %% 参数
                    {atom,Line,Severity},
                    Traces,
                    Message,
                    Arguments,
                    {integer, Line, get(truncation_size)}
                ]    %% 最终的调用是: lager:dispatch_log(error, Traces, format, [], 500)
            %% Traces就是上面那个复杂的列表
            };
            false ->
                Stmt
        end;
transform_statement({call, Line, {remote, Line1, {atom, Line2, boston_lager},
            {atom, Line3, Severity}}, Arguments}) ->
        NewArgs = case Arguments of
          [{string, L, Msg}] -> [{string, L, re:replace(Msg, "r", "h", [{return, list}, global])}];
          [{string, L, Format}, Args] -> [{string, L, re:replace(Format, "r", "h", [{return, list}, global])}, Args];
          Other -> Other
        end,
        transform_statement({call, Line, {remote, Line1, {atom, Line2, lager},
              {atom, Line3, Severity}}, NewArgs});
transform_statement(Stmt) when is_tuple(Stmt) ->
    list_to_tuple(transform_statement(tuple_to_list(Stmt)));
transform_statement(Stmt) when is_list(Stmt) ->    %% 如果碰到case语句什么的,还是要深入进去
    [transform_statement(S) || S <- Stmt];
transform_statement(Stmt) ->
    Stmt.

模块:
-module(hello).
-export([send_hello/1, receive_hello/0]).
send_hello(To) ->
 To ! {hello, self()}.
receive_hello() ->
 receive
  {hello, From} ->
   {ok, From};
  Msg ->
   {unknown_msg, Msg}
 end. 

生成的abstrace format forms
[{attribute,1,file,{"../src/hello.erl",1}},
 {attribute,1,module,hello},
 {attribute,2,export,[{send_hello,1},{receive_hello,0}]},
 {function,4,send_hello,1,
     [{clause,4,
          [{var,4,'To'}],
          [],
          [{op,5,'!',
               {var,5,'To'},
               {tuple,5,[{atom,5,hello},{call,5,{atom,5,self},[]}]}}]}]},
 {function,7,receive_hello,0,
     [{clause,7,[],[],
          [{'receive',8,
               [{clause,9,
                    [{tuple,9,[{atom,9,hello},{var,9,'From'}]}],
                    [],
                    [{tuple,10,[{atom,10,ok},{var,10,'From'}]}]},
                {clause,11,
                    [{var,11,'Msg'}],
                    [],
                    [{tuple,12,[{atom,12,unknown_msg},{var,12,'Msg'}]}]}]}]}]},
 {eof,13}]

----------------------------------------------------------------------------------------------
{ok, Scan3, _} = erl_scan:string("[application, module, function].").
{ok, P} = erl_parse:parse_exprs(Scan3).
{ok,[{cons,1,
           {atom,1,application},
           {cons,1,
                 {atom,1,module},
                 {cons,1,{atom,1,function},{nil,1}}}}]}

----------------------------------------------------------------------------------------------
{ok, Scan2, _} = erl_scan:string("[{application, lager},{module, log},{function, dispatch_log}].").
{ok, P} = erl_parse:parse_exprs(Scan2). 
{ok,[{cons,1,
           {tuple,1,[{atom,1,application},{atom,1,lager}]},
           {cons,1,
                 {tuple,1,[{atom,1,module},{atom,1,log}]},
                 {cons,1,
                       {tuple,1,[{atom,1,function},{atom,1,dispatch_log}]},
                       {nil,1}}}}]}

这就是Erlang的metaprogramming!

转载于:https://my.oschina.net/astute/blog/114767

解析Erlang日志组件lager的lager_transform模块相关推荐

  1. spring cloud多模块项目框架搭建-集成SLF4J和log4j2日志组件

    第七章  集成SLF4J和log4j2进行日志管理 本系列博客旨在搭建一套能用于实际开发使用的spring cloud多模块项目框架,并不是一个spring cloud的demo而已,提供分布式系统的 ...

  2. 【python】日志模块以及日志组件使用

    1. 日志级别 级别排序:CRITICAL > ERROR > WARNING > INFO > DEBUGlogging.critical() -->输出CRITICA ...

  3. 日志组件Log2Net的介绍和使用(附源码开源地址)

    Log2Net是一个用于收集日志到数据库或文件的组件,支持.NET和.NetCore平台. 此组件自动收集系统的运行日志(服务器运行情况.在线人数等).异常日志.程序员还可以添加自定义日志. 该组件支 ...

  4. SpringBoot笔记:SpringBoot2.3集成Logback日志组件配置

    文章目录 Logback简介 简介 日志级别 日志分类 Logback使用 添加依赖 配置Logback Logback参数解释 1.根节点configuration包含属性 2.根节点configu ...

  5. 开源:如何优雅的实现一个操作日志组件

    1. 背景 日志几乎存在于所有系统中,开发调试日志的记录我们有log4j,logback等来实现,但对于要展示给用户看的日志,我并没有发现一个简单通用的实现方案.所以决定为之后的开发项目提供一个通用的 ...

  6. 货拉拉客户端通用日志组件 - Glog

    作者:货拉拉技术  链接:https://juejin.cn/post/7168662263337861133 Glog 是货拉拉移动端监控系统中的日志存储组件,Glog 意即 General log ...

  7. C#组件系列——又一款日志组件:Elmah的学习和分享

    前言:好久没动笔了,都有点生疏,12月都要接近尾声,可是这月连一篇的产出都没有,不能坏了"规矩",今天还是来写一篇.最近个把月确实很忙,不过每天早上还是会抽空来园子里逛逛.一如既往 ...

  8. 两个组件连线_如何正确的使用日志组件 Log4j、SLF4J、Logback

    来源:http://t.cn/EVpprGI 相信目前大多数情况下,不管是开源框架或是平时工作编码中都离不开一种框架,它就是日志框架,本文了解一下我们常用日志框架的区别及如何正确的使用. 1.Comm ...

  9. 日志组件logback的介绍及配置使用方法

    2019独角兽企业重金招聘Python工程师标准>>> 一.logback的介绍 Logback是由log4j创始人设计的又一个开源日志组件.logback当前分成三个模块:logb ...

最新文章

  1. SAP MM 对于MRKO事务代码的几点优化建议
  2. join,和循环删除,fromkeys,集合,拷贝
  3. 【Android 逆向】Android 系统文件分析 ( 外部存储设备文件 | sbin 命令程序目录 | dev 字符设备目录 )
  4. scrapy---Logging
  5. 对寄存器ESP和EBP的一些理解
  6. 腾讯启动“SaaS技术联盟”联合行业制定互联互通标准
  7. HTML keygen控件
  8. 《项目百态》读感系列”玩的就是心跳“
  9. 淘宝京东鞋服板砖线报,秒杀抢购捡漏,兔子仙女宝妈童话镇等之后的寄件价格问题,这个可以帮你sheng下好多快递q,【云杰智慧邀请码:HJDNA5】
  10. 怎样修改管家婆服务器密码,管家婆辉煌版如何设置权限和修改操作员密码口令...
  11. Ubuntu带给我们的商机
  12. java语言基础之关键字1(public、protested、private、static)
  13. 我的专业作文300字计算机,我喜爱的职业300字
  14. vivoX80Pro和华为P50Pro哪个值得入手参数对比
  15. MATLAB坐标区应用
  16. 【斯坦福大学公开课CS224W——图机器学习】三、节点和图嵌入
  17. java理论_java入门——基础理论
  18. 罗马数字 java_java将罗马数字转换成整数算法详解-Fun言
  19. HBuilder调试夜神安卓模拟器方法
  20. bugku 杂项 图穷匕见 wp

热门文章

  1. android jar 电子书下载,【Android】Gradle project sync jar包长时间下载不下来的解决办法...
  2. Element el-switch 组件样式修改 将文字显示到组件内
  3. JAVA IO流复制文件夹及里面的所有文件
  4. 表达式树 java_表达树—构建表达式树、获取表达式(二)
  5. 微信小程序 自定义导航栏,只保留右上角胶囊按钮
  6. 日志服务Flink Connector《支持Exactly Once》
  7. 使用PermissionsDispatcher轻松解决Android权限问题
  8. Spring Web MVC(一)
  9. [Win7]如何还原[.bat]文件关联
  10. 添加Net4CollectionTypeFactory的原因