rewrite from lager
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.

756 line
26 KiB

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