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

483 行
18 KiB

  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. %% API
  22. -export([start/0,
  23. log/3, log/4, log/5,
  24. md/0, md/1,
  25. trace/2, trace/3, trace_file/2, trace_file/3, trace_file/4, trace_console/1, trace_console/2,
  26. clear_all_traces/0, stop_trace/1, stop_trace/3, status/0,
  27. get_loglevel/1, set_loglevel/2, set_loglevel/3, set_loglevel/4, get_loglevels/1,
  28. update_loglevel_config/1, posix_error/1,
  29. safe_format/3, safe_format_chop/3, dispatch_log/5, dispatch_log/6, dispatch_log/9,
  30. do_log/10, pr/2]).
  31. -type log_level() :: debug | info | notice | warning | error | critical | alert | emergency.
  32. -type log_level_number() :: 0..7.
  33. -export_type([log_level/0, log_level_number/0]).
  34. %% API
  35. %% @doc Start the application. Mainly useful for using `-s lager' as a command
  36. %% line switch to the VM to make lager start on boot.
  37. start() -> start(lager).
  38. start(App) ->
  39. start_ok(App, application:start(App, permanent)).
  40. start_ok(_App, ok) -> ok;
  41. start_ok(_App, {error, {already_started, _App}}) -> ok;
  42. start_ok(App, {error, {not_started, Dep}}) ->
  43. ok = start(Dep),
  44. start(App);
  45. start_ok(App, {error, Reason}) ->
  46. erlang:error({app_start_failed, App, Reason}).
  47. %% @doc Get lager metadata for current process
  48. -spec md() -> [{atom(), any()}].
  49. md() ->
  50. case erlang:get(?LAGER_MD_KEY) of
  51. undefined -> [];
  52. MD -> MD
  53. end.
  54. %% @doc Set lager metadata for current process.
  55. %% Will badarg if you don't supply a list of {key, value} tuples keyed by atoms.
  56. -spec md([{atom(), any()},...]) -> ok.
  57. md(NewMD) when is_list(NewMD) ->
  58. %% make sure its actually a real proplist
  59. case lists:all(
  60. fun({Key, _Value}) when is_atom(Key) -> true;
  61. (_) -> false
  62. end, NewMD) of
  63. true ->
  64. erlang:put(?LAGER_MD_KEY, NewMD),
  65. ok;
  66. false ->
  67. erlang:error(badarg)
  68. end;
  69. md(_) ->
  70. erlang:error(badarg).
  71. dispatch_log(Severity, Metadata, Format, Args, Size) when is_atom(Severity)->
  72. dispatch_log(?DEFAULT_SINK, Severity, Metadata, Format, Args, Size).
  73. -spec dispatch_log(atom(), log_level(), list(), string(), list() | none, pos_integer()) -> ok | {error, lager_not_running}.
  74. %% this is the same check that the parse transform bakes into the module at compile time
  75. dispatch_log(Sink, Severity, Metadata, Format, Args, Size) when is_atom(Severity)->
  76. SeverityAsInt=lager_util:level_to_num(Severity),
  77. case {whereis(Sink), lager_config:get({Sink, loglevel}, {?LOG_NONE, []})} of
  78. {undefined, _} ->
  79. {error, lager_not_running};
  80. {SinkPid, {Level, Traces}} when (Level band SeverityAsInt) /= 0 orelse Traces /= [] ->
  81. do_log(Severity, Metadata, Format, Args, Size, SeverityAsInt, Level, Traces, Sink, SinkPid);
  82. _ ->
  83. ok
  84. end.
  85. %% @private Should only be called externally from code generated from the parse transform
  86. do_log(Severity, Metadata, Format, Args, Size, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid) when is_atom(Severity) ->
  87. {Destinations, TraceSinkPid} = case TraceFilters of
  88. [] ->
  89. {[], undefined};
  90. _ ->
  91. {lager_util:check_traces(Metadata,SeverityAsInt,TraceFilters,[]), whereis(?TRACE_SINK)}
  92. end,
  93. case (LevelThreshold band SeverityAsInt) /= 0 orelse Destinations /= [] of
  94. true ->
  95. Msg = case Args of
  96. A when is_list(A) ->
  97. safe_format_chop(Format,Args,Size);
  98. _ ->
  99. Format
  100. end,
  101. LagerMsg = lager_msg:new(Msg,
  102. Severity, Metadata, Destinations),
  103. case lager_config:get({Sink, async}, false) of %% this needs to be able to get value from a non-default sink
  104. true ->
  105. gen_event:notify(SinkPid, {log, LagerMsg});
  106. false ->
  107. gen_event:sync_notify(SinkPid, {log, LagerMsg})
  108. end,
  109. case TraceSinkPid /= undefined of
  110. true ->
  111. gen_event:notify(TraceSinkPid, {log, LagerMsg});
  112. false ->
  113. ok
  114. end;
  115. false ->
  116. ok
  117. end.
  118. %% backwards compatible with beams compiled with lager 1.x
  119. dispatch_log(Severity, _Module, _Function, _Line, _Pid, Metadata, Format, Args, Size) ->
  120. dispatch_log(Severity, Metadata, Format, Args, Size).
  121. %% TODO:
  122. %% Consider making log2/4 that takes the Level, Pid and Message params of log/3
  123. %% along with a Sink param??
  124. %% @doc Manually log a message into lager without using the parse transform.
  125. -spec log(log_level(), pid() | atom() | [tuple(),...], list()) -> ok | {error, lager_not_running}.
  126. log(Level, Pid, Message) when is_pid(Pid); is_atom(Pid) ->
  127. dispatch_log(Level, [{pid,Pid}], Message, [], ?DEFAULT_TRUNCATION);
  128. log(Level, Metadata, Message) when is_list(Metadata) ->
  129. dispatch_log(Level, Metadata, Message, [], ?DEFAULT_TRUNCATION).
  130. %% @doc Manually log a message into lager without using the parse transform.
  131. -spec log(log_level(), pid() | atom() | [tuple(),...], string(), list()) -> ok | {error, lager_not_running}.
  132. log(Level, Pid, Format, Args) when is_pid(Pid); is_atom(Pid) ->
  133. dispatch_log(Level, [{pid,Pid}], Format, Args, ?DEFAULT_TRUNCATION);
  134. log(Level, Metadata, Format, Args) when is_list(Metadata) ->
  135. dispatch_log(Level, Metadata, Format, Args, ?DEFAULT_TRUNCATION).
  136. %% @doc Manually log a message into lager without using the parse transform.
  137. -spec log(atom(), log_level(), pid() | atom() | [tuple(),...], string(), list()) -> ok | {error, lager_not_running}.
  138. log(Sink, Level, Pid, Format, Args) when is_pid(Pid); is_atom(Pid) ->
  139. dispatch_log(Sink, Level, [{pid,Pid}], Format, Args, ?DEFAULT_TRUNCATION);
  140. log(Sink, Level, Metadata, Format, Args) when is_list(Metadata) ->
  141. dispatch_log(Sink, Level, Metadata, Format, Args, ?DEFAULT_TRUNCATION).
  142. validate_trace_filters(Filters, Level, Backend) ->
  143. Sink = proplists:get_value(sink, Filters, ?DEFAULT_SINK),
  144. {Sink,
  145. lager_util:validate_trace({
  146. proplists:delete(sink, Filters),
  147. Level,
  148. Backend
  149. })
  150. }.
  151. trace_file(File, Filter) ->
  152. trace_file(File, Filter, debug, []).
  153. trace_file(File, Filter, Level) when is_atom(Level) ->
  154. trace_file(File, Filter, Level, []);
  155. trace_file(File, Filter, Options) when is_list(Options) ->
  156. trace_file(File, Filter, debug, Options).
  157. trace_file(File, Filter, Level, Options) ->
  158. case validate_trace_filters(Filter, Level, {lager_file_backend, File}) of
  159. {Sink, {ok, Trace}} ->
  160. Handlers = lager_config:global_get(handlers, []),
  161. %% check if this file backend is already installed
  162. Res = case lists:keyfind({lager_file_backend, File}, 1, Handlers) of
  163. false ->
  164. %% install the handler
  165. LogFileConfig =
  166. lists:keystore(level, 1,
  167. lists:keystore(file, 1,
  168. Options,
  169. {file, File}),
  170. {level, none}),
  171. HandlerInfo =
  172. lager_app:start_handler(Sink, lager_file_backend,
  173. LogFileConfig),
  174. lager_config:global_set(handlers, [HandlerInfo|Handlers]),
  175. {ok, installed};
  176. {_Watcher, _Handler, Sink} ->
  177. {ok, exists};
  178. {_Watcher, _Handler, _OtherSink} ->
  179. {error, file_in_use}
  180. end,
  181. case Res of
  182. {ok, _} ->
  183. %% XXX Double-check this logic for {ok, exists}
  184. add_trace_to_loglevel_config(Trace, Sink),
  185. {ok, {{lager_file_backend, File}, Filter, Level}};
  186. {error, _} = E ->
  187. E
  188. end;
  189. {_Sink, Error} ->
  190. Error
  191. end.
  192. trace_console(Filter) ->
  193. trace_console(Filter, debug).
  194. trace_console(Filter, Level) ->
  195. trace(lager_console_backend, Filter, Level).
  196. trace(Backend, Filter) ->
  197. trace(Backend, Filter, debug).
  198. trace({lager_file_backend, File}, Filter, Level) ->
  199. trace_file(File, Filter, Level);
  200. trace(Backend, Filter, Level) ->
  201. case validate_trace_filters(Filter, Level, Backend) of
  202. {Sink, {ok, Trace}} ->
  203. add_trace_to_loglevel_config(Trace, Sink),
  204. {ok, {Backend, Filter, Level}};
  205. {_Sink, Error} ->
  206. Error
  207. end.
  208. stop_trace(Backend, Filter, Level) ->
  209. case validate_trace_filters(Filter, Level, Backend) of
  210. {Sink, {ok, Trace}} ->
  211. stop_trace_int(Trace, Sink);
  212. {_Sink, Error} ->
  213. Error
  214. end.
  215. stop_trace({Backend, Filter, Level}) ->
  216. stop_trace(Backend, Filter, Level).
  217. stop_trace_int({Backend, _Filter, _Level} = Trace, Sink) ->
  218. {Level, Traces} = lager_config:get({Sink, loglevel}),
  219. NewTraces = lists:delete(Trace, Traces),
  220. _ = lager_util:trace_filter([ element(1, T) || T <- NewTraces ]),
  221. %MinLevel = minimum_loglevel(get_loglevels() ++ get_trace_levels(NewTraces)),
  222. lager_config:set({Sink, loglevel}, {Level, NewTraces}),
  223. case get_loglevel(Sink, Backend) of
  224. none ->
  225. %% check no other traces point here
  226. case lists:keyfind(Backend, 3, NewTraces) of
  227. false ->
  228. gen_event:delete_handler(Sink, Backend, []);
  229. _ ->
  230. ok
  231. end;
  232. _ ->
  233. ok
  234. end,
  235. ok.
  236. clear_all_traces() ->
  237. Handlers = lager_config:global_get(handlers, []),
  238. _ = lager_util:trace_filter(none),
  239. lists:foreach(fun({_Watcher, Handler, Sink}) ->
  240. case get_loglevel(Sink, Handler) of
  241. none ->
  242. gen_event:delete_handler(Sink, Handler, []);
  243. _ ->
  244. ok
  245. end
  246. end, Handlers).
  247. %% XXX Needs heavy revision
  248. status() ->
  249. Handlers = lager_config:global_get(handlers, []),
  250. TraceCount = case length(element(2, lager_config:get(loglevel))) of
  251. 0 -> 1;
  252. N -> N
  253. end,
  254. Status = ["Lager status:\n",
  255. [begin
  256. Level = get_loglevel(Handler),
  257. case Handler of
  258. {lager_file_backend, File} ->
  259. io_lib:format("File ~s at level ~p\n", [File, Level]);
  260. lager_console_backend ->
  261. io_lib:format("Console at level ~p\n", [Level]);
  262. _ ->
  263. []
  264. end
  265. end || Handler <- Handlers],
  266. "Active Traces:\n",
  267. [begin
  268. LevelName = case Level of
  269. {mask, Mask} ->
  270. case lager_util:mask_to_levels(Mask) of
  271. [] -> none;
  272. Levels -> hd(Levels)
  273. end;
  274. Num ->
  275. lager_util:num_to_level(Num)
  276. end,
  277. io_lib:format("Tracing messages matching ~p at level ~p to ~p\n",
  278. [Filter, LevelName, Destination])
  279. end || {Filter, Level, Destination} <- element(2, lager_config:get(loglevel))],
  280. [
  281. "Tracing Reductions:\n",
  282. case ?DEFAULT_TRACER:info('query') of
  283. {null, false} -> "";
  284. Query -> io_lib:format("~p~n", [Query])
  285. end
  286. ],
  287. [
  288. "Tracing Statistics:\n ",
  289. [ begin
  290. [" ", atom_to_list(Table), ": ",
  291. integer_to_list(?DEFAULT_TRACER:info(Table) div TraceCount),
  292. "\n"]
  293. end || Table <- [input, output, filter] ]
  294. ]],
  295. io:put_chars(Status).
  296. %% @doc Set the loglevel for a particular backend.
  297. set_loglevel(Handler, Level) when is_atom(Level) ->
  298. set_loglevel(?DEFAULT_SINK, Handler, undefined, Level).
  299. %% @doc Set the loglevel for a particular backend that has multiple identifiers
  300. %% (eg. the file backend).
  301. set_loglevel(Handler, Ident, Level) when is_atom(Level) ->
  302. set_loglevel(?DEFAULT_SINK, Handler, Ident, Level).
  303. %% @doc Set the loglevel for a particular sink's backend that potentially has
  304. %% multiple identifiers. (Use `undefined' if it doesn't have any.)
  305. set_loglevel(Sink, Handler, Ident, Level) when is_atom(Level) ->
  306. HandlerArg = case Ident of
  307. undefined -> Handler;
  308. _ -> {Handler, Ident}
  309. end,
  310. Reply = gen_event:call(Sink, HandlerArg, {set_loglevel, Level}, infinity),
  311. update_loglevel_config(Sink),
  312. Reply.
  313. %% @doc Get the loglevel for a particular backend on the default sink. In the case that the backend
  314. %% has multiple identifiers, the lowest is returned.
  315. get_loglevel(Handler) ->
  316. get_loglevel(?DEFAULT_SINK, Handler).
  317. %% @doc Get the loglevel for a particular sink's backend. In the case that the backend
  318. %% has multiple identifiers, the lowest is returned.
  319. get_loglevel(Sink, Handler) ->
  320. case gen_event:call(Sink, Handler, get_loglevel, infinity) of
  321. {mask, Mask} ->
  322. case lager_util:mask_to_levels(Mask) of
  323. [] -> none;
  324. Levels -> hd(Levels)
  325. end;
  326. X when is_integer(X) ->
  327. lager_util:num_to_level(X);
  328. Y -> Y
  329. end.
  330. %% @doc Try to convert an atom to a posix error, but fall back on printing the
  331. %% term if its not a valid posix error code.
  332. posix_error(Error) when is_atom(Error) ->
  333. case erl_posix_msg:message(Error) of
  334. "unknown POSIX error" -> atom_to_list(Error);
  335. Message -> Message
  336. end;
  337. posix_error(Error) ->
  338. safe_format_chop("~p", [Error], ?DEFAULT_TRUNCATION).
  339. %% @private
  340. get_loglevels(Sink) ->
  341. [gen_event:call(Sink, Handler, get_loglevel, infinity) ||
  342. Handler <- gen_event:which_handlers(Sink)].
  343. %% @private
  344. add_trace_to_loglevel_config(Trace, Sink) ->
  345. {MinLevel, Traces} = lager_config:get({Sink, loglevel}),
  346. case lists:member(Trace, Traces) of
  347. false ->
  348. NewTraces = [Trace|Traces],
  349. _ = lager_util:trace_filter([ element(1, T) || T <- NewTraces]),
  350. lager_config:set({Sink, loglevel}, {MinLevel, [Trace|Traces]});
  351. _ ->
  352. ok
  353. end.
  354. %% @doc recalculate min log level
  355. update_loglevel_config(Sink) ->
  356. {_, Traces} = lager_config:get({Sink, loglevel}, {ignore_me, []}),
  357. MinLog = minimum_loglevel(get_loglevels(Sink)),
  358. lager_config:set({Sink, loglevel}, {MinLog, Traces}).
  359. %% @private
  360. minimum_loglevel(Levels) ->
  361. lists:foldl(fun({mask, Mask}, Acc) ->
  362. Mask bor Acc;
  363. (Level, Acc) when is_integer(Level) ->
  364. {mask, Mask} = lager_util:config_to_mask(lager_util:num_to_level(Level)),
  365. Mask bor Acc;
  366. (_, Acc) ->
  367. Acc
  368. end, 0, Levels).
  369. %% @doc Print the format string `Fmt' with `Args' safely with a size
  370. %% limit of `Limit'. If the format string is invalid, or not enough
  371. %% arguments are supplied 'FORMAT ERROR' is printed with the offending
  372. %% arguments. The caller is NOT crashed.
  373. safe_format(Fmt, Args, Limit) ->
  374. safe_format(Fmt, Args, Limit, []).
  375. safe_format(Fmt, Args, Limit, Options) ->
  376. try lager_trunc_io:format(Fmt, Args, Limit, Options)
  377. catch
  378. _:_ -> lager_trunc_io:format("FORMAT ERROR: ~p ~p", [Fmt, Args], Limit)
  379. end.
  380. %% @private
  381. safe_format_chop(Fmt, Args, Limit) ->
  382. safe_format(Fmt, Args, Limit, [{chomp, true}]).
  383. %% @doc Print a record lager found during parse transform
  384. pr(Record, Module) when is_tuple(Record), is_atom(element(1, Record)) ->
  385. try
  386. case is_record_known(Record, Module) of
  387. false ->
  388. Record;
  389. {RecordName, RecordFields} ->
  390. {'$lager_record', RecordName,
  391. zip(RecordFields, tl(tuple_to_list(Record)), Module, [])}
  392. end
  393. catch
  394. error:undef ->
  395. Record
  396. end;
  397. pr(Record, _) ->
  398. Record.
  399. zip([FieldName|RecordFields], [FieldValue|Record], Module, ToReturn) ->
  400. case is_tuple(FieldValue) andalso
  401. tuple_size(FieldValue) > 0 andalso
  402. is_atom(element(1, FieldValue)) andalso
  403. is_record_known(FieldValue, Module) of
  404. false ->
  405. zip(RecordFields, Record, Module, [{FieldName, FieldValue}|ToReturn]);
  406. _Else ->
  407. F = {FieldName, pr(FieldValue, Module)},
  408. zip(RecordFields, Record, Module, [F|ToReturn])
  409. end;
  410. zip([], [], _Module, ToReturn) ->
  411. lists:reverse(ToReturn).
  412. is_record_known(Record, Module) ->
  413. Name = element(1, Record),
  414. Attrs = Module:module_info(attributes),
  415. case lists:keyfind(lager_records, 1, Attrs) of
  416. false -> false;
  417. {lager_records, Records} ->
  418. case lists:keyfind(Name, 1, Records) of
  419. false -> false;
  420. {Name, RecordFields} ->
  421. case (tuple_size(Record) - 1) =:= length(RecordFields) of
  422. false -> false;
  423. true -> {Name, RecordFields}
  424. end
  425. end
  426. end.