当前位置: 首页 > 编程日记 > 正文

解析Erlang日志组件lager的lager_transform模块

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

使用 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

相关文章:

Session 常见操作

对于敏感、重要的信息&#xff0c;建议要存储在服务器端&#xff08;Session是存储在服务器端的&#xff09;&#xff0c;不能存储在浏览器中&#xff0c;如用户名、余额、等级、验证码等信息 Session依赖于Cookie session数据的获取 session:请求上下文对象&#xff0c;用于处…

C++的STL队列实现栈

使用C的队列STL实现一个栈的数据结构 实现以下四个函数&#xff1a; 1.push(x) : 将元素x压入栈中 2.pop() : 弹出(移除)栈顶元素 3.top() : 返回栈顶元素 4.empty() : 判断栈是否是空 队列的数据结构为先入先出&#xff0c;栈的数据结构为先入后出&#xff1b; 即队列的元素顺…

PHP实现XML传输

sendXML.php <!--发送XML的页面--> <?php$xml_data <xml>...</xml>;//发送的xml $url http://localhost/getXML.php;//接收XML地址$header "Content-type: text/xml";//定义content-type为xml $ch curl_init(); //初始化curl curl_setop…

wordpress短代码转php,WordPress中的shortcode短代码功能使用详解

WordPress 从 2.5 的版本开始&#xff0c;增加了一个 shortcode (短代码) API ,类似于 BBS 上的 BBCode &#xff0c; shortcode 也可以很方便的为文章或页面增加功能&#xff0c;并且 shortcode 的比起 BBCode 更加灵活和强大。下面 Kayo 为大家介绍一下 shortcode 。一.short…

在ESXi主机上关闭无响应的虚拟机

适用情况该方法适用于以下情况: ESXi主机上的虚拟机不能关闭。虚拟机无响应且不能停止。目的这篇文章描述在ESXi环境中如何正确的关闭一台无响应的虚拟机。注意&#xff1a; 这篇文章只适用于ESXi主机&#xff0c;不适用于ESX主机。对于ESX主机&#xff0c;请参考 Powering off…

React和Jquery比较

Jquery的工作方式&#xff1a; 假如你需要给一个按扭添加一个点击事件。 首先根据CSS规则找到对应的dom元素&#xff0c;挂上一个匿名事件处理函数&#xff0c;在事件处理函数中&#xff0c;选中那个需要被修改的DOM元素&#xff0c;读取他的文本值&#xff0c;加以修改&#x…

C++的STL栈实现队列

使用内部存储结构为栈的方法实现一个队列&#xff0c;要求实现该队列的如下方法&#xff1a; 1.push(x) : 将元素x压入队列中 2.pop() : 弹出(移除)队列头部元素 3.peek() : 返回队列头部元素(即为front) 4.empty() : 判断队列是否是空 栈的数据结构为先入后出&#xff0c;队列…

如何设置SOLR的高亮 (highlight)?

打开SOLR的核心配置文件&#xff1a; solrconfig.xml找到 standard request handler写入以下XML配置代码&#xff1a;[c-sharp] view plaincopy <requestHandler name"standard" class"solr.SearchHandler" default"true"> <!-- def…

java快排算法解读,java 快排的思路与算法

java 快排的思路与算法有时候面试的时候的会问道Arrays.sort()是怎么实现的&#xff0c;我以前根本不知道是什么东西&#xff0c;最近点进去看了一下。直接吓傻&#xff0c;//看到这个时候还是比较淡定的&#xff0c;可怕的事情来了。public static void sort(int[] a) {DualPi…

SQL:EXISTS的用法理解(转)

摘自&#xff1a;http://www.cnblogs.com/netserver/archive/2008/12/25/1362615.html 比如在Northwind数据库中有一个查询为 SELECT c.CustomerId,CompanyName FROM Customers c WHERE EXISTS( SELECT OrderID FROM Orders o WHERE o.CustomerIDc.CustomerID) 这里面的EXISTS…

002.Heartbeat部署及httpd高可用

一 前期准备 1.1 依赖准备 编译安装需要依赖的包&#xff0c;如gcc等&#xff1a;yum -y install gcc gcc-c make glibc kernel-devel kernel-headers autoconf automake libtool glib2-devel libxml2 libxml2-devel libxslt-devel libtool-ltdl-devel wget asciidoc libuuid-d…

C++的STL栈实现获取栈中最小元素的成员

实现一个获取栈中最小数据成员的函数&#xff0c;该栈支持如下操作&#xff1a; 1.push(x) : 将元素x压入栈中 2.pop() : 弹出(移除)栈顶元素 3.top() : 返回栈顶元素 4.getMin() : 返回栈内最小元素 要求时间复杂度为O(1) 这里关键是如何获取最小值&#xff0c;栈中的元素不断…

java servlet applet,详解Java Servlet与Applet比较

Java Servlet与Applet相似之处&#xff1a;◆它们不是独立的应用程序&#xff0c;没有main()方法。◆它们不是由用户或程序员调用&#xff0c;而是由另外一个应用程序(容器)调用。◆它们都有一个生存周期&#xff0c;包含init()和destroy()方法。Java Servlet与Applet不同之处&…

NTP时间同步服务器搭建

转载&#xff1a;http://blog.s135.com/post/281/ 一、搭建时间同步服务器1、编译安装ntp server tar zxvf ntp-4.2.6.tar.gzcd ntp-4.2.6./configure --prefix/usr/local/ntp --enable-all-clocks --enable-parse-clocksmake && make install注&#xff1a;如以上下载…

OI基础系列之最大子数组问题

OI基础系列之最大子数组问题 ——Edward2414 oi退役了&#xff0c;虽然没取得多少成绩&#xff0c;也算是走过一会的人了。我相信绝大多数oi党都是自学成才&#xff0c;在此&#xff0c;我感谢那些把自己所学写到博客里的前辈们&#xff0c;没有你们&#xff0c;我不可能…

springCloud Zuul网关

1.springboot 仅2.0.x 支持&#xff0c;在此选择 2.0.7 2.新建Module eureka-zuul-client 3.导入依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/…

f-free 查看系统中空闲和使用的内存

文章目录前言语法格式以指定单位显示内存占用情况打印所有内存占用&#xff08;RAM SWAP&#xff09;打印间隔以及次数打印所有的列&#xff08;将buff和cache分开&#xff09;free各个空间含义swap交换空间cache页高速缓存free 与 available前言 free 支持查看空闲的和已使用…

对比两个同类型的泛型集合并返回差异泛型集合 ——两个List类名的比较

1: /// <summary> 2: /// 对比两个同类型的泛型集合并返回差异泛型集合 3: /// </summary> 4: /// <typeparam name"T">泛型类型</typeparam> 5: /// <param name"newModel">修改后的数据集合</param> 6: /// &…

php insert failed,较大的MySQL INSERT语句导致PHP错误

好吧,我正在编写代码,但是发生了一些奇怪的事情,我不认为我的代码是错误的…但是它仍在垂死,我不知道为什么…有错误&#xff1a;Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use …

js 取得文件大小

document.getElementById("file").files[0].size

Spring Boot thymeleaf模版支持,css,js等静态文件添加

Thymeleaf引入 Thymeleaf是一个Java模板引擎开发库&#xff0c;可以处理和生成HTML、XML、JavaScript、CSS和文本&#xff0c;在Web和非Web环境下都可以正常工作。 1.添加依赖包 <dependency><groupId>org.springframework.boot</groupId><artifactId>…

s-sed(stream editor) 文本填充和编辑 基本使用

文章目录前言语法格式sed 操作地址sed子命令sed正则表达式sed使用实例打印命令 p删除命令 d替换命令 s指定操作地址的范围 逗号 ,多重编辑命令 e下行插入命令 a上行插入命令 i修改命令 c获取下一行命令 n转换命令 y退出命令 q总结前言 sed是一个“非交互”式的字符流编辑器&am…

c语言动态迁移mysql,flask-migrate动态迁移数据库

了解flask_migrate需要先了解flask-script&#xff0c;那么flask-script的作用是什么呢&#xff1f;flask-script的作用是可以通过命令行的形式来操作Flask。例如通过命令跑一个开发版本的服务器、设置数据库&#xff0c;定时任务等。2.执行pip install flask-script来进行安装…

软考之路-网络攻击:主动攻击和被动攻击

被动攻击(针对路上的东西下手) 概念&#xff1a;就是网络窃听&#xff0c;窃取数据包并进行分析&#xff0c;从中窃取重要的敏感信息 措施&#xff1a;防止被动攻击的主要手段是数据加密传输 主动攻击(针对计算机下手) 概念&#xff1a;包括窃取、篡改、假冒和破坏 措施&#x…

edge.js架起node.js和.net互操作桥梁

今天要介绍的是edge.js这个github上刚兴起的开源项目&#xff0c;它可以让node.js和.net之间在in-process下互操作。.net版本在4.5及以上&#xff0c;因为.net4.5带来的Task&#xff0c;asyn&#xff0c;await关键字和node.js的Event模型正好匹配。如果你感兴趣的话&#xff0c…

connect() failed (111: Connection refused) while connecting to upstream, cli

php-fpm没有运行 执行如下命令查看是否启动了php-fpm&#xff0c;如果没有则启动你的php-fpm即可 netstat -ant | grep 9000没有运行为空&#xff0c;有运行显示 tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN 启动方法 sudo /usr/loca…

C++的STL 栈实现 判断栈的出栈顺序是否合理

有这样的题目&#xff1a; 已知从1至n的数字序列&#xff0c;按顺序入栈&#xff0c;每个数字入栈后即可出栈&#xff0c; 也可在栈中停留&#xff0c;等待后面的数字入栈出栈后&#xff0c;该数字再出栈&#xff0c;求该数字序列的出栈序列是否合法? 类似如下&#xff1a; 已…

fire.php,Fire PHP

项目介绍&#xff1a; Fire PHP 是基于 PHP JavaScript开发的跨平台的Firefox 的扩充套件&#xff0c;即PHP调试插件&#xff0c;可以帮你debug 后端PHP 的程式&#xff0c;其使用的技术跟某些IDE 一样&#xff0c;要求你在写程式时加入一些追踪用的代码。通过使用Firephp你可以…

json_encode时中文编码转正常状态

function json_encode_cn($data) {$data json_encode($data);return preg_replace("/\\\u([0-9a-f]{4})/ie", "iconv(UCS-2, UTF-8, pack(H*, $1));", $data); }直接json_encode()函数 ["\u6fb3\u5927\u5229\u4e9e","\u8056\u8a95\u5cf6&q…

验证URL链接和IP有效性的JS代码(正则表达式)

千里之行&#xff0c;始于足下&#xff0c;因之前毕业设计的耽误&#xff0c;没能在博客园记录我的程序猿体会&#xff0c;稍有遗憾&#xff0c;这么多的时间&#xff0c;我竟让他转瞬而过&#xff01;但没关系&#xff0c;再次出发&#xff0c;勿忘为什么出发&#xff01; 一下…