Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

638 rader
25 KiB

14 år sedan
14 år sedan
14 år sedan
14 år sedan
14 år sedan
14 år sedan
14 år sedan
14 år sedan
14 år sedan
14 år sedan
10 år sedan
10 år sedan
10 år sedan
10 år sedan
10 år sedan
10 år sedan
10 år sedan
14 år sedan
10 år sedan
14 år sedan
10 år sedan
14 år sedan
14 år sedan
14 år sedan
  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).