You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

671 line
26 KiB

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