lager_transform.erl 5.37 KB
Newer Older
Andrew Thompson's avatar
Andrew Thompson committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
%% Copyright (c) 2011 Basho Technologies, Inc.  All Rights Reserved.
%%
%% This file is provided to you under the Apache License,
%% Version 2.0 (the "License"); you may not use this file
%% except in compliance with the License.  You may obtain
%% a copy of the License at
%%
%%   http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing,
%% software distributed under the License is distributed on an
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
%% KIND, either express or implied.  See the License for the
%% specific language governing permissions and limitations
%% under the License.

-module(lager_transform).
-export([parse_transform/2]).

-define(LEVELS, [debug, info, notice, warning, error, critical, alert,
        emergency]).

%% This parse transform rewrites functions calls to lager:Severity/1,2 into
%% a more complicated function that captures module, function, line, pid and
%% time as well. The entire function call is then wrapped in a case that
%$ checks the mochiglobal 'loglevel' value, so the code isn't executed if
%% nothing wishes to consume the message.

parse_transform(AST, _Options) ->
    %io:format("~n~p~n", [AST]),
    walk_ast([], AST).

walk_ast(Acc, []) ->
    lists:reverse(Acc);
walk_ast(Acc, [{attribute, _, module, Module}=H|T]) ->
    put(module, Module),
    walk_ast([H|Acc], T);
walk_ast(Acc, [{function, Line, Name, Arity, Clauses}|T]) ->
    put(function, Name),
    walk_ast([{function, Line, Name, Arity,
                walk_clauses([], Clauses)}|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({call, Line, {remote, Line1, {atom, Line2, lager},
            {atom, Line3, Severity}}, Arguments} = Stmt) ->
    case lists:member(Severity, ?LEVELS) of
        true ->
            %io:format("call to lager ~p on line ~p in function ~p in module ~p~n",
                %[Severity, Line, get(function), get(module)]),
            %% a case to check the mochiglobal 'loglevel' key against the
            %% message we're trying to log
            {'case',Line,
                    {op,Line,'=<',
                        {call,Line,
                            {remote,Line,{atom,Line,lager_util},{atom,Line,level_to_num}},
                            [{call,Line,
                                    {remote,Line,{atom,Line,lager_mochiglobal},{atom,Line,get}},
                                    [{atom,Line,loglevel}]}]},
                        {call,Line,
                            {remote,Line,{atom,Line,lager_util},{atom,Line,level_to_num}},
                            [{atom,Line,Severity}]}},
                    [{clause,Line,
                            [{atom,Line,true}], %% yes, we log!
                            [],
                            [{call, Line, {remote, Line1, {atom, Line2, lager},
                                        {atom, Line3, log}}, [
                                        {atom, Line3, Severity},
                                        {atom, Line3, get(module)},
                                        {atom, Line3, get(function)},
                                        {integer, Line3, Line},
                                        {call, Line3, {atom, Line3 ,self}, []},
                                        {call, Line3, {remote, Line3,
                                                {atom, Line3 ,riak_err_stdlib},
                                                {atom,Line3,maybe_utc}},
                                            [{call,Line3,{remote,Line3,
                                                        {atom,Line3,erlang},
                                                        {atom,Line3,localtime}},[]}]}
                                        | Arguments
                                    ]}]},
                        %% No, don't log
                        {clause,Line3,[{var,Line3,'_'}],[],[{atom,Line3,ok}]}]};
            false ->
                %io:format("skipping non-log lager call ~p~n", [Severity]),
                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({'case', Line, Expr, Clauses}) ->
    {'case', Line, Expr, walk_clauses([], Clauses)};
transform_statement({'if', Line, Clauses}) ->
    {'if', Line, walk_clauses([], Clauses)};
transform_statement({block, Line, Body}) ->
    {block, Line, walk_body([], Body)};
transform_statement({lc, Line, Expression, Generator}) ->
    {lc, Line, transform_statement(Expression), Generator};
transform_statement({match, Line, Var, Expression}) ->
    {match, Line, Var, transform_statement(Expression)};
transform_statement(Stmt) ->
    Stmt.