From 5f27739e1eb5a5ba24871d09bafa63ce4a52b9b9 Mon Sep 17 00:00:00 2001 From: "John R. Daily" Date: Mon, 20 Apr 2015 16:44:43 -0400 Subject: [PATCH] At the lowest level of the transform, allow multiple sinks --- src/lager_transform.erl | 255 ++++++++++++++++++++-------------------- 1 file changed, 128 insertions(+), 127 deletions(-) diff --git a/src/lager_transform.erl b/src/lager_transform.erl index b25ff33..5ae5190 100644 --- a/src/lager_transform.erl +++ b/src/lager_transform.erl @@ -75,139 +75,140 @@ walk_clauses(Acc, [{clause, Line, Arguments, Guards, Body}|T]) -> walk_body(Acc, []) -> lists:reverse(Acc); walk_body(Acc, [H|T]) -> - walk_body([transform_statement(H)|Acc], T). + walk_body([transform_statement(H, [lager])|Acc], T). -transform_statement({call, Line, {remote, _Line1, {atom, _Line2, lager}, - {atom, _Line3, Severity}}, Arguments0} = Stmt) -> - case lists:member(Severity, ?LEVELS) of +transform_statement({call, Line, {remote, _Line1, {atom, _Line2, Module}, + {atom, _Line3, Function}}, Arguments0} = Stmt, + Sinks) -> + case lists:member(Module, Sinks) of true -> - SeverityAsInt=lager_util:level_to_num(Severity), - 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}, []}]}, - %% get the metadata with lager:md(), this will always return a list so we can use it as the tail here - {call, Line, {remote, Line, {atom, Line, lager}, {atom, Line, md}}, []}}}}}}, - %{nil, Line}}}}}}}, - 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, - {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, - %% Generate some unique variable names so we don't accidentaly export from case clauses. - %% Note that these are not actual atoms, but the AST treats variable names as atoms. - LevelVar = make_varname("__Level", Line), - TracesVar = make_varname("__Traces", Line), - PidVar = make_varname("__Pid", Line), - %% Wrap the call to lager_dispatch log in a case that will avoid doing any work if this message is not elegible for logging - %% case {whereis(lager_event(lager_event), lager_config:get(loglevel, {?LOG_NONE, []})} of - {'case', Line, - {tuple, Line, - [{call, Line, {atom, Line, whereis}, [{atom, Line, lager_event}]}, - {call, Line, {remote, Line, {atom, Line, lager_config}, {atom, Line, get}}, [{atom, Line, loglevel}, {tuple, Line, [{integer, Line, 0},{nil, Line}]}]}]}, - [ - %% {undefined, _} -> {error, lager_not_running} - {clause, Line, - [{tuple, Line, [{atom, Line, undefined}, {var, Line, '_'}]}], - [], - %% trick the linter into avoiding a 'term constructed by not used' error: - %% (fun() -> {error, lager_not_running} end)(); - [{call, Line, {'fun', Line, {clauses, [{clause, Line, [],[], [{tuple, Line, [{atom, Line, error},{atom, Line, lager_not_running}]}]}]}}, []}]}, - %% If we care about the loglevel, or there's any traces installed, we have do more checking - %% {Level, Traces} when (Level band SeverityAsInt) /= 0 orelse Traces /= [] -> - {clause, Line, - [{tuple, Line, [{var, Line, PidVar}, {tuple, Line, [{var, Line, LevelVar}, {var, Line, TracesVar}]}]}], - [[{op, Line, 'orelse', - {op, Line, '/=', {op, Line, 'band', {var, Line, LevelVar}, {integer, Line, SeverityAsInt}}, {integer, Line, 0}}, - {op, Line, '/=', {var, Line, TracesVar}, {nil, Line}}}]], - [ - %% do the call to lager:dispatch_log - {call, Line, {remote, Line, {atom, Line, lager}, {atom, Line, do_log}}, - [ - {atom,Line,Severity}, - Traces, - Message, - Arguments, - {integer, Line, get(truncation_size)}, - {integer, Line, SeverityAsInt}, - {var, Line, LevelVar}, - {var, Line, TracesVar}, - {atom, Line, lager_event}, - {var, Line, PidVar} - ] - } - ]}, - %% otherwise, do nothing - %% _ -> ok - {clause, Line, [{var, Line, '_'}],[],[{atom, Line, ok}]} - ]}; + case lists:member(Function, ?LEVELS) of + true -> + SinkName = list_to_atom(atom_to_list(Module) ++ "_event"), + do_transform(Line, SinkName, Function, Arguments0); + false -> + Stmt + end; false -> - Stmt + list_to_tuple(transform_statement(tuple_to_list(Stmt), Sinks)) 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) -> +transform_statement(Stmt, Sinks) when is_tuple(Stmt) -> + list_to_tuple(transform_statement(tuple_to_list(Stmt), Sinks)); +transform_statement(Stmt, Sinks) when is_list(Stmt) -> + [transform_statement(S, Sinks) || S <- Stmt]; +transform_statement(Stmt, _Sinks) -> Stmt. +do_transform(Line, SinkName, Severity, Arguments0) -> + SeverityAsInt=lager_util:level_to_num(Severity), + 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}, []}]}, + %% get the metadata with lager:md(), this will always return a list so we can use it as the tail here + {call, Line, {remote, Line, {atom, Line, lager}, {atom, Line, md}}, []}}}}}}, + %{nil, Line}}}}}}}, + 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, + {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, + %% Generate some unique variable names so we don't accidentaly export from case clauses. + %% Note that these are not actual atoms, but the AST treats variable names as atoms. + LevelVar = make_varname("__Level", Line), + TracesVar = make_varname("__Traces", Line), + PidVar = make_varname("__Pid", Line), + %% Wrap the call to lager_dispatch log in a case that will avoid doing any work if this message is not elegible for logging + %% case {whereis(lager_event(lager_event), lager_config:get(loglevel, {?LOG_NONE, []})} of + {'case', Line, + {tuple, Line, + [{call, Line, {atom, Line, whereis}, [{atom, Line, SinkName}]}, + {call, Line, {remote, Line, {atom, Line, lager_config}, {atom, Line, get}}, [{atom, Line, loglevel}, {tuple, Line, [{integer, Line, 0},{nil, Line}]}]}]}, + [ + %% {undefined, _} -> {error, lager_not_running} + {clause, Line, + [{tuple, Line, [{atom, Line, undefined}, {var, Line, '_'}]}], + [], + %% trick the linter into avoiding a 'term constructed by not used' error: + %% (fun() -> {error, lager_not_running} end)(); + [{call, Line, {'fun', Line, {clauses, [{clause, Line, [],[], [{tuple, Line, [{atom, Line, error},{atom, Line, lager_not_running}]}]}]}}, []}]}, + %% If we care about the loglevel, or there's any traces installed, we have do more checking + %% {Level, Traces} when (Level band SeverityAsInt) /= 0 orelse Traces /= [] -> + {clause, Line, + [{tuple, Line, [{var, Line, PidVar}, {tuple, Line, [{var, Line, LevelVar}, {var, Line, TracesVar}]}]}], + [[{op, Line, 'orelse', + {op, Line, '/=', {op, Line, 'band', {var, Line, LevelVar}, {integer, Line, SeverityAsInt}}, {integer, Line, 0}}, + {op, Line, '/=', {var, Line, TracesVar}, {nil, Line}}}]], + [ + %% do the call to lager:dispatch_log + {call, Line, {remote, Line, {atom, Line, lager}, {atom, Line, do_log}}, + [ + {atom,Line,Severity}, + Traces, + Message, + Arguments, + {integer, Line, get(truncation_size)}, + {integer, Line, SeverityAsInt}, + {var, Line, LevelVar}, + {var, Line, TracesVar}, + {atom, Line, SinkName}, + {var, Line, PidVar} + ] + } + ]}, + %% otherwise, do nothing + %% _ -> ok + {clause, Line, [{var, Line, '_'}],[],[{atom, Line, ok}]} + ]}. + make_varname(Prefix, Line) -> list_to_atom(Prefix ++ atom_to_list(get(module)) ++ integer_to_list(Line)).