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.

483 lines
18 KiB

14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
10 years ago
14 years ago
10 years ago
14 years ago
14 years ago
14 years ago
  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.