diff --git a/src/lager_transform.erl b/src/lager_transform.erl index 6a45040..1759da1 100644 --- a/src/lager_transform.erl +++ b/src/lager_transform.erl @@ -107,12 +107,15 @@ transform_statement({call, Line, {remote, _Line1, {atom, _Line2, lager}, %% [Format, Args] or [Attr, Format]. %% The trace attributes will be a list of tuples, so check %% for that. - case Arg1 of - {cons, _, {tuple, _, _}, _} -> + case {element(1, Arg1), Arg1} of + {_, {cons, _, {tuple, _, _}, _}} -> {concat_lists(Arg1, DefaultAttrs), Arg2, {atom, Line, none}}; - {var, _, _} -> - %% crap, its a variable. look at the second + {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, _, _} -> @@ -120,7 +123,7 @@ transform_statement({call, Line, {remote, _Line1, {atom, _Line2, lager}, Arg2, {atom, Line, none}}; _ -> %% not a string, going to have to guess - %% its the argument list + %% it's the argument list {DefaultAttrs, Arg1, Arg2} end; _ -> @@ -158,10 +161,22 @@ transform_statement(Stmt) -> Stmt. %% concat 2 list ASTs by replacing the terminating [] in A with the contents of B -concat_lists({var, Line, Name}, B) -> +concat_lists({var, Line, _Name}=Var, B) -> %% concatenating a var with a cons {call, Line, {remote, Line, {atom, Line, lists},{atom, Line, flatten}}, - [{cons, Line, {var, Line, Name}, B}]}; + [{cons, Line, Var, B}]}; +concat_lists({lc, Line, _Body, _Generator} = LC, B) -> + %% concatenating a LC with a cons + {call, Line, {remote, Line, {atom, Line, lists},{atom, Line, flatten}}, + [{cons, Line, LC, B}]}; +concat_lists({call, Line, _Function, _Args} = Call, B) -> + %% concatenating a call with a cons + {call, Line, {remote, Line, {atom, Line, lists},{atom, Line, flatten}}, + [{cons, Line, Call, B}]}; +concat_lists({record_field, Line, _Var, _Record, _Field} = Rec, B) -> + %% concatenating a record_field with a cons + {call, Line, {remote, Line, {atom, Line, lists},{atom, Line, flatten}}, + [{cons, Line, Rec, B}]}; concat_lists({nil, _Line}, B) -> B; concat_lists({cons, Line, Element, Tail}, B) -> diff --git a/test/lager_test_backend.erl b/test/lager_test_backend.erl index 9da5be3..25b3a1b 100644 --- a/test/lager_test_backend.erl +++ b/test/lager_test_backend.erl @@ -24,6 +24,7 @@ code_change/3]). -record(state, {level, buffer, ignored}). +-record(test, {attrs, format, args}). -compile([{parse_transform, lager_transform}]). -ifdef(TEST). @@ -210,6 +211,95 @@ lager_test_() -> ok end }, + {"list comprehension inplace of literals in logging statements work", + fun() -> + ?assertEqual(0, count()), + Attr = [{a, alpha}, {b, beta}], + Fmt = "format ~p", + Args = [world], + lager:info([{K, atom_to_list(V)} || {K, V} <- Attr], "hello"), + lager:info([{K, atom_to_list(V)} || {K, V} <- Attr], "hello ~p", [{atom, X} || X <- Args]), + lager:info([X || X <- Fmt], [world]), + lager:info("hello ~p", [{atom, X} || X <- Args]), + lager:info([{K, atom_to_list(V)} || {K, V} <- Attr], "hello ~p", [{atom, X} || X <- Args]), + lager:info([{d, delta}, {g, gamma}], Fmt, [{atom, X} || X <- Args]), + ?assertEqual(6, count()), + {_Level, _Time, Message, Metadata} = pop(), + ?assertMatch([{a, "alpha"}, {b, "beta"}|_], Metadata), + ?assertEqual("hello", lists:flatten(Message)), + {_Level, _Time2, Message2, _Metadata2} = pop(), + ?assertEqual("hello {atom,world}", lists:flatten(Message2)), + {_Level, _Time3, Message3, _Metadata3} = pop(), + ?assertEqual("format world", lists:flatten(Message3)), + {_Level, _Time4, Message4, _Metadata4} = pop(), + ?assertEqual("hello {atom,world}", lists:flatten(Message4)), + {_Level, _Time5, Message5, _Metadata5} = pop(), + ?assertEqual("hello {atom,world}", lists:flatten(Message5)), + {_Level, _Time6, Message6, Metadata6} = pop(), + ?assertMatch([{d, delta}, {g, gamma}|_], Metadata6), + ?assertEqual("format {atom,world}", lists:flatten(Message6)), + ok + end + }, + {"function calls inplace of literals in logging statements work", + fun() -> + ?assertEqual(0, count()), + put(attrs, [{a, alpha}, {b, beta}]), + put(format, "format ~p"), + put(args, [world]), + lager:info(get(attrs), "hello"), + lager:info(get(attrs), "hello ~p", get(args)), + lager:info(get(format), [world]), + lager:info("hello ~p", erlang:get(args)), + lager:info(fun() -> get(attrs) end(), "hello ~p", get(args)), + lager:info([{d, delta}, {g, gamma}], get(format), get(args)), + ?assertEqual(6, count()), + {_Level, _Time, Message, Metadata} = pop(), + ?assertMatch([{a, alpha}, {b, beta}|_], Metadata), + ?assertEqual("hello", lists:flatten(Message)), + {_Level, _Time2, Message2, _Metadata2} = pop(), + ?assertEqual("hello world", lists:flatten(Message2)), + {_Level, _Time3, Message3, _Metadata3} = pop(), + ?assertEqual("format world", lists:flatten(Message3)), + {_Level, _Time4, Message4, _Metadata4} = pop(), + ?assertEqual("hello world", lists:flatten(Message4)), + {_Level, _Time5, Message5, _Metadata5} = pop(), + ?assertEqual("hello world", lists:flatten(Message5)), + {_Level, _Time6, Message6, Metadata6} = pop(), + ?assertMatch([{d, delta}, {g, gamma}|_], Metadata6), + ?assertEqual("format world", lists:flatten(Message6)), + ok + end + }, + {"record fields inplace of literals in logging statements work", + fun() -> + ?assertEqual(0, count()), + Test = #test{attrs=[{a, alpha}, {b, beta}], format="format ~p", args=[world]}, + lager:info(Test#test.attrs, "hello"), + lager:info(Test#test.attrs, "hello ~p", Test#test.args), + lager:info(Test#test.format, [world]), + lager:info("hello ~p", Test#test.args), + lager:info(Test#test.attrs, "hello ~p", Test#test.args), + lager:info([{d, delta}, {g, gamma}], Test#test.format, Test#test.args), + ?assertEqual(6, count()), + {_Level, _Time, Message, Metadata} = pop(), + ?assertMatch([{a, alpha}, {b, beta}|_], Metadata), + ?assertEqual("hello", lists:flatten(Message)), + {_Level, _Time2, Message2, _Metadata2} = pop(), + ?assertEqual("hello world", lists:flatten(Message2)), + {_Level, _Time3, Message3, _Metadata3} = pop(), + ?assertEqual("format world", lists:flatten(Message3)), + {_Level, _Time4, Message4, _Metadata4} = pop(), + ?assertEqual("hello world", lists:flatten(Message4)), + {_Level, _Time5, Message5, _Metadata5} = pop(), + ?assertEqual("hello world", lists:flatten(Message5)), + {_Level, _Time6, Message6, Metadata6} = pop(), + ?assertMatch([{d, delta}, {g, gamma}|_], Metadata6), + ?assertEqual("format world", lists:flatten(Message6)), + ok + end + }, + {"log messages below the threshold are ignored", fun() -> ?assertEqual(0, count()),