您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

638 行
25 KiB

10 年前
10 年前
10 年前
10 年前
10 年前
10 年前
10 年前
  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 lager logging framework.
  17. -module(lager).
  18. -include("lager.hrl").
  19. -define(LAGER_MD_KEY, '__lager_metadata').
  20. -define(TRACE_SINK, '__trace_sink').
  21. -define(ROTATE_TIMEOUT, 100000).
  22. %% API
  23. -export([start/0,
  24. log/3, log/4, log/5,
  25. log_unsafe/4,
  26. md/0, md/1,
  27. rotate_handler/1, rotate_handler/2, rotate_sink/1, rotate_all/0,
  28. trace/2, trace/3, trace_file/2, trace_file/3, trace_file/4, trace_console/1, trace_console/2,
  29. list_all_sinks/0, clear_all_traces/0, stop_trace/1, stop_trace/3, status/0,
  30. get_loglevel/1, get_loglevel/2, set_loglevel/2, set_loglevel/3, set_loglevel/4, get_loglevels/1,
  31. update_loglevel_config/1, posix_error/1, set_loghwm/2, set_loghwm/3, set_loghwm/4,
  32. safe_format/3, safe_format_chop/3, unsafe_format/2, dispatch_log/5, dispatch_log/7, dispatch_log/9,
  33. do_log/9, do_log/10, do_log_unsafe/10, pr/2, pr/3, pr_stacktrace/1, pr_stacktrace/2]).
  34. -type log_level() :: debug | info | notice | warning | error | critical | alert | emergency.
  35. -type log_level_number() :: 0..7.
  36. -export_type([log_level/0, log_level_number/0]).
  37. %% API
  38. %% @doc Start the application. Mainly useful for using `-s lager' as a command
  39. %% line switch to the VM to make lager start on boot.
  40. start() -> start(lager).
  41. start(App) ->
  42. start_ok(App, application:start(App, permanent)).
  43. start_ok(_App, ok) -> ok;
  44. start_ok(_App, {error, {already_started, _App}}) -> ok;
  45. start_ok(App, {error, {not_started, Dep}}) ->
  46. ok = start(Dep),
  47. start(App);
  48. start_ok(App, {error, Reason}) ->
  49. erlang:error({app_start_failed, App, Reason}).
  50. %% @doc Get lager metadata for current process
  51. -spec md() -> [{atom(), any()}].
  52. md() ->
  53. case erlang:get(?LAGER_MD_KEY) of
  54. undefined -> [];
  55. MD -> MD
  56. end.
  57. %% @doc Set lager metadata for current process.
  58. %% Will badarg if you don't supply a list of {key, value} tuples keyed by atoms.
  59. -spec md([{atom(), any()},...]) -> ok.
  60. md(NewMD) when is_list(NewMD) ->
  61. %% make sure its actually a real proplist
  62. case lists:all(
  63. fun({Key, _Value}) when is_atom(Key) -> true;
  64. (_) -> false
  65. end, NewMD) of
  66. true ->
  67. erlang:put(?LAGER_MD_KEY, NewMD),
  68. ok;
  69. false ->
  70. erlang:error(badarg)
  71. end;
  72. md(_) ->
  73. erlang:error(badarg).
  74. -spec dispatch_log(atom(), log_level(), list(), string(), list() | none, pos_integer(), safe | unsafe) -> ok | {error, lager_not_running} | {error, {sink_not_configured, atom()}}.
  75. %% this is the same check that the parse transform bakes into the module at compile time
  76. %% see lager_transform (lines 173-216)
  77. dispatch_log(Sink, Severity, Metadata, Format, Args, Size, Safety) when is_atom(Severity)->
  78. SeverityAsInt=lager_util:level_to_num(Severity),
  79. case {whereis(Sink), whereis(?DEFAULT_SINK), lager_config:get({Sink, loglevel}, {?LOG_NONE, []})} of
  80. {undefined, undefined, _} -> {error, lager_not_running};
  81. {undefined, _LagerEventPid0, _} -> {error, {sink_not_configured, Sink}};
  82. {SinkPid, _LagerEventPid1, {Level, Traces}} when Safety =:= safe andalso ( (Level band SeverityAsInt) /= 0 orelse Traces /= [] ) ->
  83. do_log(Severity, Metadata, Format, Args, Size, SeverityAsInt, Level, Traces, Sink, SinkPid);
  84. {SinkPid, _LagerEventPid1, {Level, Traces}} when Safety =:= unsafe andalso ( (Level band SeverityAsInt) /= 0 orelse Traces /= [] ) ->
  85. do_log_unsafe(Severity, Metadata, Format, Args, Size, SeverityAsInt, Level, Traces, Sink, SinkPid);
  86. _ -> ok
  87. end.
  88. %% @private Should only be called externally from code generated from the parse transform
  89. do_log(Severity, Metadata, Format, Args, Size, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid) when is_atom(Severity) ->
  90. FormatFun = fun() -> safe_format_chop(Format, Args, Size) end,
  91. do_log_impl(Severity, Metadata, Format, Args, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid, FormatFun).
  92. do_log_impl(Severity, Metadata, Format, Args, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid, FormatFun) ->
  93. {Destinations, TraceSinkPid} = case TraceFilters of
  94. [] ->
  95. {[], undefined};
  96. _ ->
  97. {lager_util:check_traces(Metadata,SeverityAsInt,TraceFilters,[]), whereis(?TRACE_SINK)}
  98. end,
  99. case (LevelThreshold band SeverityAsInt) /= 0 orelse Destinations /= [] of
  100. true ->
  101. Msg = case Args of
  102. A when is_list(A) ->
  103. FormatFun();
  104. _ ->
  105. Format
  106. end,
  107. LagerMsg = lager_msg:new(Msg,
  108. Severity, Metadata, Destinations),
  109. case lager_config:get({Sink, async}, false) of
  110. true ->
  111. gen_event:notify(SinkPid, {log, LagerMsg});
  112. false ->
  113. gen_event:sync_notify(SinkPid, {log, LagerMsg})
  114. end,
  115. case TraceSinkPid /= undefined of
  116. true ->
  117. gen_event:notify(TraceSinkPid, {log, LagerMsg});
  118. false ->
  119. ok
  120. end;
  121. false ->
  122. ok
  123. end.
  124. %% @private Should only be called externally from code generated from the parse transform
  125. %% Specifically, it would be level ++ `_unsafe' as in `info_unsafe'.
  126. do_log_unsafe(Severity, Metadata, Format, Args, _Size, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid) when is_atom(Severity) ->
  127. FormatFun = fun() -> unsafe_format(Format, Args) end,
  128. do_log_impl(Severity, Metadata, Format, Args, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid, FormatFun).
  129. %% backwards compatible with beams compiled with lager 1.x
  130. dispatch_log(Severity, _Module, _Function, _Line, _Pid, Metadata, Format, Args, Size) ->
  131. dispatch_log(Severity, Metadata, Format, Args, Size).
  132. %% backwards compatible with beams compiled with lager 2.x
  133. dispatch_log(Severity, Metadata, Format, Args, Size) ->
  134. dispatch_log(?DEFAULT_SINK, Severity, Metadata, Format, Args, Size, safe).
  135. %% backwards compatible with beams compiled with lager 2.x
  136. do_log(Severity, Metadata, Format, Args, Size, SeverityAsInt, LevelThreshold, TraceFilters, SinkPid) ->
  137. do_log(Severity, Metadata, Format, Args, Size, SeverityAsInt,
  138. LevelThreshold, TraceFilters, ?DEFAULT_SINK, SinkPid).
  139. %% TODO:
  140. %% Consider making log2/4 that takes the Level, Pid and Message params of log/3
  141. %% along with a Sink param??
  142. %% @doc Manually log a message into lager without using the parse transform.
  143. -spec log(log_level(), pid() | atom() | [tuple(),...], list()) -> ok | {error, lager_not_running}.
  144. log(Level, Pid, Message) when is_pid(Pid); is_atom(Pid) ->
  145. dispatch_log(Level, [{pid,Pid}], Message, [], ?DEFAULT_TRUNCATION);
  146. log(Level, Metadata, Message) when is_list(Metadata) ->
  147. dispatch_log(Level, Metadata, Message, [], ?DEFAULT_TRUNCATION).
  148. %% @doc Manually log a message into lager without using the parse transform.
  149. -spec log(log_level(), pid() | atom() | [tuple(),...], string(), list()) -> ok | {error, lager_not_running}.
  150. log(Level, Pid, Format, Args) when is_pid(Pid); is_atom(Pid) ->
  151. dispatch_log(Level, [{pid,Pid}], Format, Args, ?DEFAULT_TRUNCATION);
  152. log(Level, Metadata, Format, Args) when is_list(Metadata) ->
  153. dispatch_log(Level, Metadata, Format, Args, ?DEFAULT_TRUNCATION).
  154. log_unsafe(Level, Metadata, Format, Args) when is_list(Metadata) ->
  155. dispatch_log(?DEFAULT_SINK, Level, Metadata, Format, Args, ?DEFAULT_TRUNCATION, unsafe).
  156. %% @doc Manually log a message into lager without using the parse transform.
  157. -spec log(atom(), log_level(), pid() | atom() | [tuple(),...], string(), list()) -> ok | {error, lager_not_running}.
  158. log(Sink, Level, Pid, Format, Args) when is_pid(Pid); is_atom(Pid) ->
  159. dispatch_log(Sink, Level, [{pid,Pid}], Format, Args, ?DEFAULT_TRUNCATION, safe);
  160. log(Sink, Level, Metadata, Format, Args) when is_list(Metadata) ->
  161. dispatch_log(Sink, Level, Metadata, Format, Args, ?DEFAULT_TRUNCATION, safe).
  162. validate_trace_filters(Filters, Level, Backend) ->
  163. Sink = proplists:get_value(sink, Filters, ?DEFAULT_SINK),
  164. {Sink,
  165. lager_util:validate_trace({
  166. proplists:delete(sink, Filters),
  167. Level,
  168. Backend
  169. })
  170. }.
  171. trace_file(File, Filter) ->
  172. trace_file(File, Filter, debug, []).
  173. trace_file(File, Filter, Level) when is_atom(Level) ->
  174. trace_file(File, Filter, Level, []);
  175. trace_file(File, Filter, Options) when is_list(Options) ->
  176. trace_file(File, Filter, debug, Options).
  177. trace_file(File, Filter, Level, Options) ->
  178. FileName = lager_util:expand_path(File),
  179. case validate_trace_filters(Filter, Level, {lager_file_backend, FileName}) of
  180. {Sink, {ok, Trace}} ->
  181. Handlers = lager_config:global_get(handlers, []),
  182. %% check if this file backend is already installed
  183. Res = case lists:keyfind({lager_file_backend, FileName}, 1, Handlers) of
  184. false ->
  185. %% install the handler
  186. LogFileConfig =
  187. lists:keystore(level, 1,
  188. lists:keystore(file, 1,
  189. Options,
  190. {file, FileName}),
  191. {level, none}),
  192. HandlerInfo =
  193. lager_app:start_handler(Sink, {lager_file_backend, FileName},
  194. LogFileConfig),
  195. lager_config:global_set(handlers, [HandlerInfo|Handlers]),
  196. {ok, installed};
  197. {_Watcher, _Handler, Sink} ->
  198. {ok, exists};
  199. {_Watcher, _Handler, _OtherSink} ->
  200. {error, file_in_use}
  201. end,
  202. case Res of
  203. {ok, _} ->
  204. add_trace_to_loglevel_config(Trace, Sink),
  205. {ok, {{lager_file_backend, FileName}, Filter, Level}};
  206. {error, _} = E ->
  207. E
  208. end;
  209. {_Sink, Error} ->
  210. Error
  211. end.
  212. trace_console(Filter) ->
  213. trace_console(Filter, debug).
  214. trace_console(Filter, Level) ->
  215. trace(lager_console_backend, Filter, Level).
  216. trace(Backend, Filter) ->
  217. trace(Backend, Filter, debug).
  218. trace({lager_file_backend, File}, Filter, Level) ->
  219. trace_file(File, Filter, Level);
  220. trace(Backend, Filter, Level) ->
  221. case validate_trace_filters(Filter, Level, Backend) of
  222. {Sink, {ok, Trace}} ->
  223. add_trace_to_loglevel_config(Trace, Sink),
  224. {ok, {Backend, Filter, Level}};
  225. {_Sink, Error} ->
  226. Error
  227. end.
  228. stop_trace(Backend, Filter, Level) ->
  229. case validate_trace_filters(Filter, Level, Backend) of
  230. {Sink, {ok, Trace}} ->
  231. stop_trace_int(Trace, Sink);
  232. {_Sink, Error} ->
  233. Error
  234. end.
  235. stop_trace({Backend, Filter, Level}) ->
  236. stop_trace(Backend, Filter, Level).
  237. %% Important: validate_trace_filters orders the arguments of
  238. %% trace tuples differently than the way outside callers have
  239. %% the trace tuple.
  240. %%
  241. %% That is to say, outside they are represented as
  242. %% `{Backend, Filter, Level}'
  243. %%
  244. %% and when they come back from validation, they're
  245. %% `{Filter, Level, Backend}'
  246. stop_trace_int({_Filter, _Level, Backend} = Trace, Sink) ->
  247. {Level, Traces} = lager_config:get({Sink, loglevel}),
  248. NewTraces = lists:delete(Trace, Traces),
  249. _ = lager_util:trace_filter([ element(1, T) || T <- NewTraces ]),
  250. %MinLevel = minimum_loglevel(get_loglevels() ++ get_trace_levels(NewTraces)),
  251. lager_config:set({Sink, loglevel}, {Level, NewTraces}),
  252. case get_loglevel(Sink, Backend) of
  253. none ->
  254. %% check no other traces point here
  255. case lists:keyfind(Backend, 3, NewTraces) of
  256. false ->
  257. gen_event:delete_handler(Sink, Backend, []),
  258. lager_config:global_set(handlers,
  259. lists:keydelete(Backend, 1,
  260. lager_config:global_get(handlers)));
  261. _ ->
  262. ok
  263. end;
  264. _ ->
  265. ok
  266. end,
  267. ok.
  268. list_all_sinks() ->
  269. sets:to_list(
  270. lists:foldl(fun({_Watcher, _Handler, Sink}, Set) ->
  271. sets:add_element(Sink, Set)
  272. end,
  273. sets:new(),
  274. lager_config:global_get(handlers, []))).
  275. clear_traces_by_sink(Sinks) ->
  276. lists:foreach(fun(S) ->
  277. {Level, _Traces} =
  278. lager_config:get({S, loglevel}),
  279. lager_config:set({S, loglevel},
  280. {Level, []})
  281. end,
  282. Sinks).
  283. clear_all_traces() ->
  284. Handlers = lager_config:global_get(handlers, []),
  285. clear_traces_by_sink(list_all_sinks()),
  286. _ = lager_util:trace_filter(none),
  287. lager_config:global_set(handlers,
  288. lists:filter(
  289. fun({Handler, _Watcher, Sink}) ->
  290. case get_loglevel(Sink, Handler) of
  291. none ->
  292. gen_event:delete_handler(Sink, Handler, []),
  293. false;
  294. _ ->
  295. true
  296. end
  297. end, Handlers)).
  298. find_traces(Sinks) ->
  299. lists:foldl(fun(S, Acc) ->
  300. {_Level, Traces} = lager_config:get({S, loglevel}),
  301. Acc ++ lists:map(fun(T) -> {S, T} end, Traces)
  302. end,
  303. [],
  304. Sinks).
  305. status() ->
  306. Handlers = lager_config:global_get(handlers, []),
  307. Sinks = lists:sort(list_all_sinks()),
  308. Traces = find_traces(Sinks),
  309. TraceCount = case length(Traces) of
  310. 0 -> 1;
  311. N -> N
  312. end,
  313. Status = ["Lager status:\n",
  314. [begin
  315. Level = get_loglevel(Sink, Handler),
  316. case Handler of
  317. {lager_file_backend, File} ->
  318. io_lib:format("File ~s (~s) at level ~p\n", [File, Sink, Level]);
  319. lager_console_backend ->
  320. io_lib:format("Console (~s) at level ~p\n", [Sink, Level]);
  321. _ ->
  322. []
  323. end
  324. end || {Handler, _Watcher, Sink} <- lists:sort(fun({_, _, S1},
  325. {_, _, S2}) -> S1 =< S2 end,
  326. Handlers)],
  327. "Active Traces:\n",
  328. [begin
  329. LevelName = case Level of
  330. {mask, Mask} ->
  331. case lager_util:mask_to_levels(Mask) of
  332. [] -> none;
  333. Levels -> hd(Levels)
  334. end;
  335. Num ->
  336. lager_util:num_to_level(Num)
  337. end,
  338. io_lib:format("Tracing messages matching ~p (sink ~s) at level ~p to ~p\n",
  339. [Filter, Sink, LevelName, Destination])
  340. end || {Sink, {Filter, Level, Destination}} <- Traces],
  341. [
  342. "Tracing Reductions:\n",
  343. case ?DEFAULT_TRACER:info('query') of
  344. {null, false} -> "";
  345. Query -> io_lib:format("~p~n", [Query])
  346. end
  347. ],
  348. [
  349. "Tracing Statistics:\n ",
  350. [ begin
  351. [" ", atom_to_list(Table), ": ",
  352. integer_to_list(?DEFAULT_TRACER:info(Table) div TraceCount),
  353. "\n"]
  354. end || Table <- [input, output, filter] ]
  355. ]],
  356. io:put_chars(Status).
  357. %% @doc Set the loglevel for a particular backend.
  358. set_loglevel(Handler, Level) when is_atom(Level) ->
  359. set_loglevel(?DEFAULT_SINK, Handler, undefined, Level).
  360. %% @doc Set the loglevel for a particular backend that has multiple identifiers
  361. %% (eg. the file backend).
  362. set_loglevel(Handler, Ident, Level) when is_atom(Level) ->
  363. set_loglevel(?DEFAULT_SINK, Handler, Ident, Level).
  364. %% @doc Set the loglevel for a particular sink's backend that potentially has
  365. %% multiple identifiers. (Use `undefined' if it doesn't have any.)
  366. set_loglevel(Sink, Handler, Ident, Level) when is_atom(Level) ->
  367. HandlerArg = case Ident of
  368. undefined -> Handler;
  369. _ -> {Handler, Ident}
  370. end,
  371. Reply = gen_event:call(Sink, HandlerArg, {set_loglevel, Level}, infinity),
  372. update_loglevel_config(Sink),
  373. Reply.
  374. %% @doc Get the loglevel for a particular backend on the default sink. In the case that the backend
  375. %% has multiple identifiers, the lowest is returned.
  376. get_loglevel(Handler) ->
  377. get_loglevel(?DEFAULT_SINK, Handler).
  378. %% @doc Get the loglevel for a particular sink's backend. In the case that the backend
  379. %% has multiple identifiers, the lowest is returned.
  380. get_loglevel(Sink, Handler) ->
  381. case gen_event:call(Sink, Handler, get_loglevel, infinity) of
  382. {mask, Mask} ->
  383. case lager_util:mask_to_levels(Mask) of
  384. [] -> none;
  385. Levels -> hd(Levels)
  386. end;
  387. X when is_integer(X) ->
  388. lager_util:num_to_level(X);
  389. Y -> Y
  390. end.
  391. %% @doc Try to convert an atom to a posix error, but fall back on printing the
  392. %% term if its not a valid posix error code.
  393. posix_error(Error) when is_atom(Error) ->
  394. case erl_posix_msg:message(Error) of
  395. "unknown POSIX error" -> atom_to_list(Error);
  396. Message -> Message
  397. end;
  398. posix_error(Error) ->
  399. safe_format_chop("~p", [Error], ?DEFAULT_TRUNCATION).
  400. %% @private
  401. get_loglevels(Sink) ->
  402. [gen_event:call(Sink, Handler, get_loglevel, infinity) ||
  403. Handler <- gen_event:which_handlers(Sink)].
  404. %% @doc Set the loghwm for the default sink.
  405. set_loghwm(Handler, Hwm) when is_integer(Hwm) ->
  406. set_loghwm(?DEFAULT_SINK, Handler, Hwm).
  407. %% @doc Set the loghwm for a particular backend.
  408. set_loghwm(Sink, Handler, Hwm) when is_integer(Hwm) ->
  409. gen_event:call(Sink, Handler, {set_loghwm, Hwm}, infinity).
  410. %% @doc Set the loghwm (log high water mark) for file backends with multiple identifiers
  411. set_loghwm(Sink, Handler, Ident, Hwm) when is_integer(Hwm) ->
  412. gen_event:call(Sink, {Handler, Ident}, {set_loghwm, Hwm}, infinity).
  413. %% @private
  414. add_trace_to_loglevel_config(Trace, Sink) ->
  415. {MinLevel, Traces} = lager_config:get({Sink, loglevel}),
  416. case lists:member(Trace, Traces) of
  417. false ->
  418. NewTraces = [Trace|Traces],
  419. _ = lager_util:trace_filter([ element(1, T) || T <- NewTraces]),
  420. lager_config:set({Sink, loglevel}, {MinLevel, [Trace|Traces]});
  421. _ ->
  422. ok
  423. end.
  424. %% @doc recalculate min log level
  425. update_loglevel_config(error_logger) ->
  426. %% Not a sink under our control, part of the Erlang logging
  427. %% utility that error_logger_lager_h attaches to
  428. true;
  429. update_loglevel_config(Sink) ->
  430. {_, Traces} = lager_config:get({Sink, loglevel}, {ignore_me, []}),
  431. MinLog = minimum_loglevel(get_loglevels(Sink)),
  432. lager_config:set({Sink, loglevel}, {MinLog, Traces}).
  433. %% @private
  434. minimum_loglevel(Levels) ->
  435. lists:foldl(fun({mask, Mask}, Acc) ->
  436. Mask bor Acc;
  437. (Level, Acc) when is_integer(Level) ->
  438. {mask, Mask} = lager_util:config_to_mask(lager_util:num_to_level(Level)),
  439. Mask bor Acc;
  440. (_, Acc) ->
  441. Acc
  442. end, 0, Levels).
  443. %% @doc Print the format string `Fmt' with `Args' safely with a size
  444. %% limit of `Limit'. If the format string is invalid, or not enough
  445. %% arguments are supplied 'FORMAT ERROR' is printed with the offending
  446. %% arguments. The caller is NOT crashed.
  447. safe_format(Fmt, Args, Limit) ->
  448. safe_format(Fmt, Args, Limit, []).
  449. safe_format(Fmt, Args, Limit, Options) ->
  450. try lager_trunc_io:format(Fmt, Args, Limit, Options)
  451. catch
  452. _:_ -> lager_trunc_io:format("FORMAT ERROR: ~p ~p", [Fmt, Args], Limit)
  453. end.
  454. %% @private
  455. safe_format_chop(Fmt, Args, Limit) ->
  456. safe_format(Fmt, Args, Limit, [{chomp, true}]).
  457. %% @private Print the format string `Fmt' with `Args' without a size limit.
  458. %% This is unsafe because the output of this function is unbounded.
  459. %%
  460. %% Log messages with unbounded size will kill your application dead as
  461. %% OTP mechanisms stuggle to cope with them. So this function is
  462. %% intended <b>only</b> for messages which have a reasonable bounded
  463. %% size before they're formatted.
  464. %%
  465. %% If the format string is invalid or not enough arguments are
  466. %% supplied a 'FORMAT ERROR' message is printed instead with the
  467. %% offending arguments. The caller is NOT crashed.
  468. unsafe_format(Fmt, Args) ->
  469. try io_lib:format(Fmt, Args)
  470. catch
  471. _:_ -> io_lib:format("FORMAT ERROR: ~p ~p", [Fmt, Args])
  472. end.
  473. %% @doc Print a record lager found during parse transform
  474. pr(Record, Module) when is_tuple(Record), is_atom(element(1, Record)) ->
  475. pr(Record, Module, []);
  476. pr(Record, _) ->
  477. Record.
  478. %% @doc Print a record lager found during parse transform
  479. pr(Record, Module, Options) when is_tuple(Record), is_atom(element(1, Record)), is_list(Options) ->
  480. try
  481. case is_record_known(Record, Module) of
  482. false ->
  483. Record;
  484. {RecordName, RecordFields} ->
  485. {'$lager_record', RecordName,
  486. zip(RecordFields, tl(tuple_to_list(Record)), Module, Options, [])}
  487. end
  488. catch
  489. error:undef ->
  490. Record
  491. end;
  492. pr(Record, _, _) ->
  493. Record.
  494. zip([FieldName|RecordFields], [FieldValue|Record], Module, Options, ToReturn) ->
  495. Compress = lists:member(compress, Options),
  496. case is_tuple(FieldValue) andalso
  497. tuple_size(FieldValue) > 0 andalso
  498. is_atom(element(1, FieldValue)) andalso
  499. is_record_known(FieldValue, Module) of
  500. false when Compress andalso FieldValue =:= undefined ->
  501. zip(RecordFields, Record, Module, Options, ToReturn);
  502. false ->
  503. zip(RecordFields, Record, Module, Options, [{FieldName, FieldValue}|ToReturn]);
  504. _Else ->
  505. F = {FieldName, pr(FieldValue, Module, Options)},
  506. zip(RecordFields, Record, Module, Options, [F|ToReturn])
  507. end;
  508. zip([], [], _Module, _Compress, ToReturn) ->
  509. lists:reverse(ToReturn).
  510. is_record_known(Record, Module) ->
  511. Name = element(1, Record),
  512. Attrs = Module:module_info(attributes),
  513. case lists:keyfind(lager_records, 1, Attrs) of
  514. false -> false;
  515. {lager_records, Records} ->
  516. case lists:keyfind(Name, 1, Records) of
  517. false -> false;
  518. {Name, RecordFields} ->
  519. case (tuple_size(Record) - 1) =:= length(RecordFields) of
  520. false -> false;
  521. true -> {Name, RecordFields}
  522. end
  523. end
  524. end.
  525. %% @doc Print stacktrace in human readable form
  526. pr_stacktrace(Stacktrace) ->
  527. Indent = "\n ",
  528. lists:foldl(
  529. fun(Entry, Acc) ->
  530. Acc ++ Indent ++ error_logger_lager_h:format_mfa(Entry)
  531. end,
  532. [],
  533. lists:reverse(Stacktrace)).
  534. pr_stacktrace(Stacktrace, {Class, Reason}) ->
  535. lists:flatten(
  536. pr_stacktrace(Stacktrace) ++ "\n" ++ io_lib:format("~s:~p", [Class, Reason])).
  537. rotate_sink(Sink) ->
  538. Handlers = lager_config:global_get(handlers),
  539. RotateHandlers = lists:filtermap(
  540. fun({Handler,_,S}) when S == Sink -> {true, {Handler, Sink}};
  541. (_) -> false
  542. end,
  543. Handlers),
  544. rotate_handlers(RotateHandlers).
  545. rotate_all() ->
  546. rotate_handlers(lists:map(fun({H,_,S}) -> {H, S} end,
  547. lager_config:global_get(handlers))).
  548. rotate_handlers(Handlers) ->
  549. [ rotate_handler(Handler, Sink) || {Handler, Sink} <- Handlers ].
  550. rotate_handler(Handler) ->
  551. Handlers = lager_config:global_get(handlers),
  552. case lists:keyfind(Handler, 1, Handlers) of
  553. {Handler, _, Sink} -> rotate_handler(Handler, Sink);
  554. false -> ok
  555. end.
  556. rotate_handler(Handler, Sink) ->
  557. gen_event:call(Sink, Handler, rotate, ?ROTATE_TIMEOUT).