Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

306 lignes
15 KiB

il y a 14 ans
il y a 14 ans
il y a 14 ans
il y a 14 ans
il y a 14 ans
il y a 14 ans
il y a 14 ans
il y a 14 ans
il y a 14 ans
il y a 14 ans
il y a 14 ans
il y a 14 ans
  1. %% Copyright (c) 2011-2012 Basho Technologies, Inc. All Rights Reserved.
  2. %%
  3. %% This file is provided to you under the Apache License,
  4. %% Version 2.0 (the "License"); you may not use this file
  5. %% except in compliance with the License. You may obtain
  6. %% a copy of the License at
  7. %%
  8. %% http://www.apache.org/licenses/LICENSE-2.0
  9. %%
  10. %% Unless required by applicable law or agreed to in writing,
  11. %% software distributed under the License is distributed on an
  12. %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  13. %% KIND, either express or implied. See the License for the
  14. %% specific language governing permissions and limitations
  15. %% under the License.
  16. %% @doc The parse transform used for lager messages.
  17. %% This parse transform rewrites functions calls to lager:Severity/1,2 into
  18. %% a more complicated function that captures module, function, line, pid and
  19. %% time as well. The entire function call is then wrapped in a case that
  20. %% checks the lager_config 'loglevel' value, so the code isn't executed if
  21. %% nothing wishes to consume the message.
  22. -module(lager_transform).
  23. -include("lager.hrl").
  24. -export([parse_transform/2]).
  25. %% @private
  26. parse_transform(AST, Options) ->
  27. TruncSize = proplists:get_value(lager_truncation_size, Options, ?DEFAULT_TRUNCATION),
  28. Enable = proplists:get_value(lager_print_records_flag, Options, true),
  29. Sinks = [lager] ++ proplists:get_value(lager_extra_sinks, Options, []),
  30. put(print_records_flag, Enable),
  31. put(truncation_size, TruncSize),
  32. put(sinks, Sinks),
  33. erlang:put(records, []),
  34. %% .app file should either be in the outdir, or the same dir as the source file
  35. guess_application(proplists:get_value(outdir, Options), hd(AST)),
  36. walk_ast([], AST).
  37. walk_ast(Acc, []) ->
  38. case get(print_records_flag) of
  39. true ->
  40. insert_record_attribute(Acc);
  41. false ->
  42. lists:reverse(Acc)
  43. end;
  44. walk_ast(Acc, [{attribute, _, module, {Module, _PmodArgs}}=H|T]) ->
  45. %% A wild parameterized module appears!
  46. put(module, Module),
  47. walk_ast([H|Acc], T);
  48. walk_ast(Acc, [{attribute, _, module, Module}=H|T]) ->
  49. put(module, Module),
  50. walk_ast([H|Acc], T);
  51. walk_ast(Acc, [{function, Line, Name, Arity, Clauses}|T]) ->
  52. put(function, Name),
  53. walk_ast([{function, Line, Name, Arity,
  54. walk_clauses([], Clauses)}|Acc], T);
  55. walk_ast(Acc, [{attribute, _, record, {Name, Fields}}=H|T]) ->
  56. FieldNames = lists:map(fun({record_field, _, {atom, _, FieldName}}) ->
  57. FieldName;
  58. ({record_field, _, {atom, _, FieldName}, _Default}) ->
  59. FieldName
  60. end, Fields),
  61. stash_record({Name, FieldNames}),
  62. walk_ast([H|Acc], T);
  63. walk_ast(Acc, [H|T]) ->
  64. walk_ast([H|Acc], T).
  65. walk_clauses(Acc, []) ->
  66. lists:reverse(Acc);
  67. walk_clauses(Acc, [{clause, Line, Arguments, Guards, Body}|T]) ->
  68. walk_clauses([{clause, Line, Arguments, Guards, walk_body([], Body)}|Acc], T).
  69. walk_body(Acc, []) ->
  70. lists:reverse(Acc);
  71. walk_body(Acc, [H|T]) ->
  72. walk_body([transform_statement(H, get(sinks))|Acc], T).
  73. transform_statement({call, Line, {remote, _Line1, {atom, _Line2, Module},
  74. {atom, _Line3, Function}}, Arguments0} = Stmt,
  75. Sinks) ->
  76. case lists:member(Module, Sinks) of
  77. true ->
  78. case lists:member(Function, ?LEVELS) of
  79. true ->
  80. SinkName = lager_util:make_internal_sink_name(Module),
  81. do_transform(Line, SinkName, Function, Arguments0);
  82. false ->
  83. case lists:keyfind(Function, 1, ?LEVELS_UNSAFE) of
  84. {Function, Severity} ->
  85. SinkName = lager_util:make_internal_sink_name(Module),
  86. do_transform(Line, SinkName, Severity, Arguments0, unsafe);
  87. false ->
  88. Stmt
  89. end
  90. end;
  91. false ->
  92. list_to_tuple(transform_statement(tuple_to_list(Stmt), Sinks))
  93. end;
  94. transform_statement(Stmt, Sinks) when is_tuple(Stmt) ->
  95. list_to_tuple(transform_statement(tuple_to_list(Stmt), Sinks));
  96. transform_statement(Stmt, Sinks) when is_list(Stmt) ->
  97. [transform_statement(S, Sinks) || S <- Stmt];
  98. transform_statement(Stmt, _Sinks) ->
  99. Stmt.
  100. do_transform(Line, SinkName, Severity, Arguments0) ->
  101. do_transform(Line, SinkName, Severity, Arguments0, safe).
  102. do_transform(Line, SinkName, Severity, Arguments0, Safety) ->
  103. SeverityAsInt=lager_util:level_to_num(Severity),
  104. DefaultAttrs0 = {cons, Line, {tuple, Line, [
  105. {atom, Line, module}, {atom, Line, get(module)}]},
  106. {cons, Line, {tuple, Line, [
  107. {atom, Line, function}, {atom, Line, get(function)}]},
  108. {cons, Line, {tuple, Line, [
  109. {atom, Line, line},
  110. {integer, Line, Line}]},
  111. {cons, Line, {tuple, Line, [
  112. {atom, Line, pid},
  113. {call, Line, {atom, Line, pid_to_list}, [
  114. {call, Line, {atom, Line ,self}, []}]}]},
  115. {cons, Line, {tuple, Line, [
  116. {atom, Line, node},
  117. {call, Line, {atom, Line, node}, []}]},
  118. %% get the metadata with lager:md(), this will always return a list so we can use it as the tail here
  119. {call, Line, {remote, Line, {atom, Line, lager}, {atom, Line, md}}, []}}}}}},
  120. %{nil, Line}}}}}}},
  121. DefaultAttrs = case erlang:get(application) of
  122. undefined ->
  123. DefaultAttrs0;
  124. App ->
  125. %% stick the application in the attribute list
  126. concat_lists({cons, Line, {tuple, Line, [
  127. {atom, Line, application},
  128. {atom, Line, App}]},
  129. {nil, Line}}, DefaultAttrs0)
  130. end,
  131. {Meta, Message, Arguments} = case Arguments0 of
  132. [Format] ->
  133. {DefaultAttrs, Format, {atom, Line, none}};
  134. [Arg1, Arg2] ->
  135. %% some ambiguity here, figure out if these arguments are
  136. %% [Format, Args] or [Attr, Format].
  137. %% The trace attributes will be a list of tuples, so check
  138. %% for that.
  139. case {element(1, Arg1), Arg1} of
  140. {_, {cons, _, {tuple, _, _}, _}} ->
  141. {concat_lists(Arg1, DefaultAttrs),
  142. Arg2, {atom, Line, none}};
  143. {Type, _} when Type == var;
  144. Type == lc;
  145. Type == call;
  146. Type == record_field ->
  147. %% crap, its not a literal. look at the second
  148. %% argument to see if it is a string
  149. case Arg2 of
  150. {string, _, _} ->
  151. {concat_lists(Arg1, DefaultAttrs),
  152. Arg2, {atom, Line, none}};
  153. _ ->
  154. %% not a string, going to have to guess
  155. %% it's the argument list
  156. {DefaultAttrs, Arg1, Arg2}
  157. end;
  158. _ ->
  159. {DefaultAttrs, Arg1, Arg2}
  160. end;
  161. [Attrs, Format, Args] ->
  162. {concat_lists(Attrs, DefaultAttrs), Format, Args}
  163. end,
  164. %% Generate some unique variable names so we don't accidentaly export from case clauses.
  165. %% Note that these are not actual atoms, but the AST treats variable names as atoms.
  166. LevelVar = make_varname("__Level", Line),
  167. TracesVar = make_varname("__Traces", Line),
  168. PidVar = make_varname("__Pid", Line),
  169. LogFun = case Safety of
  170. safe ->
  171. do_log;
  172. unsafe ->
  173. do_log_unsafe
  174. end,
  175. %% Wrap the call to lager:dispatch_log/6 in case that will avoid doing any work if this message is not elegible for logging
  176. %% See lager.erl (lines 89-100) for lager:dispatch_log/6
  177. %% case {whereis(Sink), whereis(?DEFAULT_SINK), lager_config:get({Sink, loglevel}, {?LOG_NONE, []})} of
  178. {'case',Line,
  179. {tuple,Line,
  180. [{call,Line,{atom,Line,whereis},[{atom,Line,SinkName}]},
  181. {call,Line,{atom,Line,whereis},[{atom,Line,?DEFAULT_SINK}]},
  182. {call,Line,
  183. {remote,Line,{atom,Line,lager_config},{atom,Line,get}},
  184. [{tuple,Line,[{atom,Line,SinkName},{atom,Line,loglevel}]},
  185. {tuple,Line,[{integer,Line,0},{nil,Line}]}]}]},
  186. %% {undefined, undefined, _} -> {error, lager_not_running};
  187. [{clause,Line,
  188. [{tuple,Line,
  189. [{atom,Line,undefined},{atom,Line,undefined},{var,Line,'_'}]}],
  190. [],
  191. %% trick the linter into avoiding a 'term constructed but not used' error:
  192. %% (fun() -> {error, lager_not_running} end)()
  193. [{call, Line, {'fun', Line, {clauses, [{clause, Line, [],[], [{tuple, Line, [{atom, Line, error},{atom, Line, lager_not_running}]}]}]}}, []}]
  194. },
  195. %% {undefined, _, _} -> {error, {sink_not_configured, Sink}};
  196. {clause,Line,
  197. [{tuple,Line,
  198. [{atom,Line,undefined},{var,Line,'_'},{var,Line,'_'}]}],
  199. [],
  200. %% same trick as above to avoid linter error
  201. [{call, Line, {'fun', Line, {clauses, [{clause, Line, [],[], [{tuple,Line, [{atom,Line,error}, {tuple,Line,[{atom,Line,sink_not_configured},{atom,Line,SinkName}]}]}]}]}}, []}]
  202. },
  203. %% {SinkPid, _, {Level, Traces}} when ... -> lager:do_log/9;
  204. {clause,Line,
  205. [{tuple,Line,
  206. [{var,Line,PidVar},
  207. {var,Line,'_'},
  208. {tuple,Line,[{var,Line,LevelVar},{var,Line,TracesVar}]}]}],
  209. [[{op, Line, 'orelse',
  210. {op, Line, '/=', {op, Line, 'band', {var, Line, LevelVar}, {integer, Line, SeverityAsInt}}, {integer, Line, 0}},
  211. {op, Line, '/=', {var, Line, TracesVar}, {nil, Line}}}]],
  212. [{call,Line,{remote, Line, {atom, Line, lager}, {atom, Line, LogFun}},
  213. [{atom,Line,Severity},
  214. Meta,
  215. Message,
  216. Arguments,
  217. {integer, Line, get(truncation_size)},
  218. {integer, Line, SeverityAsInt},
  219. {var, Line, LevelVar},
  220. {var, Line, TracesVar},
  221. {atom, Line, SinkName},
  222. {var, Line, PidVar}]}]},
  223. %% _ -> ok
  224. {clause,Line,[{var,Line,'_'}],[],[{atom,Line,ok}]}]}.
  225. make_varname(Prefix, Line) ->
  226. list_to_atom(Prefix ++ atom_to_list(get(module)) ++ integer_to_list(Line)).
  227. %% concat 2 list ASTs by replacing the terminating [] in A with the contents of B
  228. concat_lists({var, Line, _Name}=Var, B) ->
  229. %% concatenating a var with a cons
  230. {call, Line, {remote, Line, {atom, Line, lists},{atom, Line, flatten}},
  231. [{cons, Line, Var, B}]};
  232. concat_lists({lc, Line, _Body, _Generator} = LC, B) ->
  233. %% concatenating a LC with a cons
  234. {call, Line, {remote, Line, {atom, Line, lists},{atom, Line, flatten}},
  235. [{cons, Line, LC, B}]};
  236. concat_lists({call, Line, _Function, _Args} = Call, B) ->
  237. %% concatenating a call with a cons
  238. {call, Line, {remote, Line, {atom, Line, lists},{atom, Line, flatten}},
  239. [{cons, Line, Call, B}]};
  240. concat_lists({record_field, Line, _Var, _Record, _Field} = Rec, B) ->
  241. %% concatenating a record_field with a cons
  242. {call, Line, {remote, Line, {atom, Line, lists},{atom, Line, flatten}},
  243. [{cons, Line, Rec, B}]};
  244. concat_lists({nil, _Line}, B) ->
  245. B;
  246. concat_lists({cons, Line, Element, Tail}, B) ->
  247. {cons, Line, Element, concat_lists(Tail, B)}.
  248. stash_record(Record) ->
  249. Records = case erlang:get(records) of
  250. undefined ->
  251. [];
  252. R ->
  253. R
  254. end,
  255. erlang:put(records, [Record|Records]).
  256. insert_record_attribute(AST) ->
  257. lists:foldl(fun({attribute, Line, module, _}=E, Acc) ->
  258. [E, {attribute, Line, lager_records, erlang:get(records)}|Acc];
  259. (E, Acc) ->
  260. [E|Acc]
  261. end, [], AST).
  262. guess_application(Dirname, Attr) when Dirname /= undefined ->
  263. case find_app_file(Dirname) of
  264. no_idea ->
  265. %% try it based on source file directory (app.src most likely)
  266. guess_application(undefined, Attr);
  267. _ ->
  268. ok
  269. end;
  270. guess_application(undefined, {attribute, _, file, {Filename, _}}) ->
  271. Dir = filename:dirname(Filename),
  272. find_app_file(Dir);
  273. guess_application(_, _) ->
  274. ok.
  275. find_app_file(Dir) ->
  276. case filelib:wildcard(Dir++"/*.{app,app.src}") of
  277. [] ->
  278. no_idea;
  279. [File] ->
  280. case file:consult(File) of
  281. {ok, [{application, Appname, _Attributes}|_]} ->
  282. erlang:put(application, Appname);
  283. _ ->
  284. no_idea
  285. end;
  286. _ ->
  287. %% multiple files, uh oh
  288. no_idea
  289. end.