blob: 7b41b9f4e14af8c0a0cd589595741844fe1d035c [file] [log] [blame]
%% 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.
%% @doc The parse transform used for lager messages.
%% 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.
-module(lager_transform).
-include("lager.hrl").
-export([parse_transform/2]).
%% @private
parse_transform(AST, _Options) ->
walk_ast([], AST).
walk_ast(Acc, []) ->
lists:reverse(Acc);
walk_ast(Acc, [{attribute, _, module, {Module, _PmodArgs}}=H|T]) ->
%% A wild parameterized module appears!
put(module, Module),
walk_ast([H|Acc], T);
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}}, Arguments0} = Stmt) ->
case lists:member(Severity, ?LEVELS) of
true ->
DefaultAttrs = {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}, []}]}]},
{nil, Line}}}}},
{Traces, Message, Arguments} = case Arguments0 of
[Format] ->
{DefaultAttrs, Format, {nil, Line}};
[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 Arg1 of
{cons, _, {tuple, _, _}, _} ->
{concat_lists(Arg1, DefaultAttrs),
Arg2, {nil,Line}};
_ ->
{DefaultAttrs, Arg1, Arg2}
end;
[Attrs, Format, Args] ->
{concat_lists(Attrs, DefaultAttrs), Format, Args}
end,
{block, Line,
[
{call, Line, {remote, Line, {atom,Line1,lager},{atom,Line2,dispatch_log}},
[
{atom, Line3, Severity},
{atom, Line3, get(module)},
{atom, Line3, get(function)},
{integer, Line3, Line},
{call, Line3, {atom, Line3 ,self}, []},
Traces,
Message,
Arguments
]
}
]}; % block contents
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) ->
[transform_statement(S) || S <- Stmt];
transform_statement(Stmt) ->
Stmt.
%% concat 2 list ASTs by replacing the terminating [] in A with the contents of B
concat_lists({nil, _Line}, B) ->
B;
concat_lists({cons, Line, Element, Tail}, B) ->
{cons, Line, Element, concat_lists(Tail, B)}.