rewrite from lager
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

794 rindas
35 KiB

pirms 4 gadiem
  1. %% Copyright (c) 2011-2015 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 A error_logger backend for redirecting events into lager.
  17. %% Error messages and crash logs are also optionally written to a crash log.
  18. %% @see lager_crash_log
  19. %% @private
  20. -module(error_logger_lager_h).
  21. -include("lager.hrl").
  22. -behaviour(gen_event).
  23. -export([set_high_water/1]).
  24. -export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2,
  25. code_change/3]).
  26. -export([format_reason/1, format_mfa/1, format_args/3]).
  27. -record(state, {
  28. sink :: atom(),
  29. shaper :: lager_shaper(),
  30. %% group leader strategy
  31. groupleader_strategy :: handle | ignore | mirror,
  32. raw :: boolean()
  33. }).
  34. -define(LOGMSG(Sink, Level, Pid, Msg),
  35. case ?SHOULD_LOG(Sink, Level) of
  36. true ->
  37. _ =lager:log(Sink, Level, Pid, Msg, []),
  38. logged;
  39. _ -> no_log
  40. end).
  41. -define(LOGFMT(Sink, Level, Pid, Fmt, Args),
  42. case ?SHOULD_LOG(Sink, Level) of
  43. true ->
  44. _ = lager:log(Sink, Level, Pid, Fmt, Args),
  45. logged;
  46. _ -> no_log
  47. end).
  48. -ifdef(TEST).
  49. %% Make CRASH synchronous when testing, to avoid timing headaches
  50. -define(CRASH_LOG(Event),
  51. catch(gen_server:call(lager_crash_log, {log, Event}))).
  52. -else.
  53. -define(CRASH_LOG(Event),
  54. gen_server:cast(lager_crash_log, {log, Event})).
  55. -endif.
  56. set_high_water(N) ->
  57. gen_event:call(error_logger, ?MODULE, {set_high_water, N}, infinity).
  58. -spec init(any()) -> {ok, #state{}}.
  59. init([HighWaterMark, GlStrategy]) ->
  60. Flush = application:get_env(lager, error_logger_flush_queue, true),
  61. FlushThr = application:get_env(lager, error_logger_flush_threshold, 0),
  62. Shaper = #lager_shaper{hwm=HighWaterMark, flush_queue = Flush, flush_threshold = FlushThr, filter=shaper_fun(), id=?MODULE},
  63. Raw = application:get_env(lager, error_logger_format_raw, false),
  64. Sink = configured_sink(),
  65. {ok, #state{sink=Sink, shaper=Shaper, groupleader_strategy=GlStrategy, raw=Raw}}.
  66. handle_call({set_high_water, N}, #state{shaper=Shaper} = State) ->
  67. NewShaper = Shaper#lager_shaper{hwm=N},
  68. {ok, ok, State#state{shaper = NewShaper}};
  69. handle_call(_Request, State) ->
  70. {ok, unknown_call, State}.
  71. shaper_fun() ->
  72. case {application:get_env(lager, suppress_supervisor_start_stop, false), application:get_env(lager, suppress_application_start_stop, false)} of
  73. {false, false} ->
  74. fun(_) -> false end;
  75. {true, true} ->
  76. fun suppress_supervisor_start_and_application_start/1;
  77. {false, true} ->
  78. fun suppress_application_start/1;
  79. {true, false} ->
  80. fun suppress_supervisor_start/1
  81. end.
  82. suppress_supervisor_start_and_application_start(E) ->
  83. suppress_supervisor_start(E) orelse suppress_application_start(E).
  84. suppress_application_start({info_report, _GL, {_Pid, std_info, D}}) when is_list(D) ->
  85. lists:member({exited, stopped}, D);
  86. suppress_application_start({info_report, _GL, {_P, progress, D}}) ->
  87. lists:keymember(application, 1, D) andalso lists:keymember(started_at, 1, D);
  88. suppress_application_start(_) ->
  89. false.
  90. suppress_supervisor_start({info_report, _GL, {_P, progress, D}}) ->
  91. lists:keymember(started, 1, D) andalso lists:keymember(supervisor, 1, D);
  92. suppress_supervisor_start(_) ->
  93. false.
  94. handle_event(Event, #state{sink=Sink, shaper=Shaper} = State) ->
  95. case lager_util:check_hwm(Shaper, Event) of
  96. {true, 0, NewShaper} ->
  97. eval_gl(Event, State#state{shaper=NewShaper});
  98. {true, Drop, #lager_shaper{hwm=Hwm} = NewShaper} when Drop > 0 ->
  99. ?LOGFMT(Sink, warning, self(),
  100. "lager_error_logger_h dropped ~p messages in the last second that exceeded the limit of ~p messages/sec",
  101. [Drop, Hwm]),
  102. eval_gl(Event, State#state{shaper=NewShaper});
  103. {false, _, #lager_shaper{dropped=D} = NewShaper} ->
  104. {ok, State#state{shaper=NewShaper#lager_shaper{dropped=D+1}}}
  105. end.
  106. handle_info({shaper_expired, ?MODULE}, #state{sink=Sink, shaper=Shaper} = State) ->
  107. case Shaper#lager_shaper.dropped of
  108. 0 ->
  109. ok;
  110. Dropped ->
  111. ?LOGFMT(Sink, warning, self(),
  112. "lager_error_logger_h dropped ~p messages in the last second that exceeded the limit of ~p messages/sec",
  113. [Dropped, Shaper#lager_shaper.hwm])
  114. end,
  115. {ok, State#state{shaper=Shaper#lager_shaper{dropped=0, mps=0, lasttime=os:timestamp()}}};
  116. handle_info(_Info, State) ->
  117. {ok, State}.
  118. terminate(_Reason, _State) ->
  119. ok.
  120. code_change(_OldVsn, {state, Shaper, GLStrategy}, _Extra) ->
  121. Raw = application:get_env(lager, error_logger_format_raw, false),
  122. {ok, #state{
  123. sink=configured_sink(),
  124. shaper=Shaper,
  125. groupleader_strategy=GLStrategy,
  126. raw=Raw
  127. }};
  128. code_change(_OldVsn, {state, Sink, Shaper, GLS}, _Extra) ->
  129. Raw = application:get_env(lager, error_logger_format_raw, false),
  130. {ok, #state{sink=Sink, shaper=Shaper, groupleader_strategy=GLS, raw=Raw}};
  131. code_change(_OldVsn, State, _Extra) ->
  132. {ok, State}.
  133. %% internal functions
  134. configured_sink() ->
  135. case proplists:get_value(?ERROR_LOGGER_SINK, application:get_env(lager, extra_sinks, [])) of
  136. undefined -> ?DEFAULT_SINK;
  137. _ -> ?ERROR_LOGGER_SINK
  138. end.
  139. eval_gl(Event, #state{groupleader_strategy=GlStrategy0}=State) when is_pid(element(2, Event)) ->
  140. case element(2, Event) of
  141. GL when node(GL) =/= node(), GlStrategy0 =:= ignore ->
  142. gen_event:notify({error_logger, node(GL)}, Event),
  143. {ok, State};
  144. GL when node(GL) =/= node(), GlStrategy0 =:= mirror ->
  145. gen_event:notify({error_logger, node(GL)}, Event),
  146. log_event(Event, State);
  147. _ ->
  148. log_event(Event, State)
  149. end;
  150. eval_gl(Event, State) ->
  151. log_event(Event, State).
  152. log_event(Event, #state{sink=Sink} = State) ->
  153. DidLog = case Event of
  154. {error, _GL, {Pid, Fmt, Args}} ->
  155. FormatRaw = State#state.raw,
  156. case {FormatRaw, Fmt} of
  157. {false, "** Generic server "++_} ->
  158. %% gen_server terminate
  159. {Reason, Name} = case Args of
  160. [N, _Msg, _State, R] ->
  161. {R, N};
  162. [N, _Msg, _State, R, _Client] ->
  163. %% OTP 20 crash reports where the client pid is dead don't include the stacktrace
  164. {R, N};
  165. [N, _Msg, _State, R, _Client, _Stacktrace] ->
  166. %% OTP 20 crash reports contain the pid of the client and stacktrace
  167. %% TODO do something with them
  168. {R, N}
  169. end,
  170. ?CRASH_LOG(Event),
  171. {Md, Formatted} = format_reason_md(Reason),
  172. ?LOGFMT(Sink, error, [{pid, Pid}, {name, Name} | Md], "gen_server ~w terminated with reason: ~s",
  173. [Name, Formatted]);
  174. {false, "** State machine "++_} ->
  175. %% Check if the terminated process is gen_fsm or gen_statem
  176. %% since they generate the same exit message
  177. {Type, Name, StateName, Reason} = case Args of
  178. [TName, _Msg, TStateName, _StateData, TReason] ->
  179. {gen_fsm, TName, TStateName, TReason};
  180. %% Handle changed logging in gen_fsm stdlib-3.9 (TPid, ClientArgs)
  181. [TName, _Msg, TPid, TStateName, _StateData, TReason | _ClientArgs] when is_pid(TPid), is_atom(TStateName) ->
  182. {gen_fsm, TName, TStateName, TReason};
  183. %% Handle changed logging in gen_statem stdlib-3.9 (ClientArgs)
  184. [TName, _Msg, {TStateName, _StateData}, _ExitType, TReason, _CallbackMode, Stacktrace | _ClientArgs] ->
  185. {gen_statem, TName, TStateName, {TReason, Stacktrace}};
  186. %% Handle changed logging in gen_statem stdlib-3.9 (ClientArgs)
  187. [TName, {TStateName, _StateData}, _ExitType, TReason, _CallbackMode, Stacktrace | _ClientArgs] ->
  188. {gen_statem, TName, TStateName, {TReason, Stacktrace}};
  189. [TName, _Msg, [{TStateName, _StateData}], _ExitType, TReason, _CallbackMode, Stacktrace | _ClientArgs] ->
  190. %% sometimes gen_statem wraps its statename/data in a list for some reason???
  191. {gen_statem, TName, TStateName, {TReason, Stacktrace}}
  192. end,
  193. {Md, Formatted} = format_reason_md(Reason),
  194. ?CRASH_LOG(Event),
  195. ?LOGFMT(Sink, error, [{pid, Pid}, {name, Name} | Md], "~s ~w in state ~w terminated with reason: ~s",
  196. [Type, Name, StateName, Formatted]);
  197. {false, "** gen_event handler"++_} ->
  198. %% gen_event handler terminate
  199. [ID, Name, _Msg, _State, Reason] = Args,
  200. {Md, Formatted} = format_reason_md(Reason),
  201. ?CRASH_LOG(Event),
  202. ?LOGFMT(Sink, error, [{pid, Pid}, {name, Name} | Md], "gen_event ~w installed in ~w terminated with reason: ~s",
  203. [ID, Name, Formatted]);
  204. {false, "** Cowboy handler"++_} ->
  205. %% Cowboy HTTP server error
  206. ?CRASH_LOG(Event),
  207. case Args of
  208. [Module, Function, Arity, _Request, _State] ->
  209. %% we only get the 5-element list when its a non-exported function
  210. ?LOGFMT(Sink, error, Pid,
  211. "Cowboy handler ~p terminated with reason: call to undefined function ~p:~p/~p",
  212. [Module, Module, Function, Arity]);
  213. [Module, Function, Arity, _Class, Reason | Tail] ->
  214. %% any other cowboy error_format list *always* ends with the stacktrace
  215. StackTrace = lists:last(Tail),
  216. {Md, Formatted} = format_reason_md({Reason, StackTrace}),
  217. ?LOGFMT(Sink, error, [{pid, Pid} | Md],
  218. "Cowboy handler ~p terminated in ~p:~p/~p with reason: ~s",
  219. [Module, Module, Function, Arity, Formatted])
  220. end;
  221. {false, "Ranch listener "++_} ->
  222. %% Ranch errors
  223. ?CRASH_LOG(Event),
  224. case Args of
  225. %% Error logged by cowboy, which starts as ranch error
  226. [Ref, ConnectionPid, StreamID, RequestPid, Reason, StackTrace] ->
  227. {Md, Formatted} = format_reason_md({Reason, StackTrace}),
  228. ?LOGFMT(Sink, error, [{pid, RequestPid} | Md],
  229. "Cowboy stream ~p with ranch listener ~p and connection process ~p "
  230. "had its request process exit with reason: ~s",
  231. [StreamID, Ref, ConnectionPid, Formatted]);
  232. [Ref, _Protocol, Worker, {[{reason, Reason}, {mfa, {Module, Function, Arity}}, {stacktrace, StackTrace} | _], _}] ->
  233. {Md, Formatted} = format_reason_md({Reason, StackTrace}),
  234. ?LOGFMT(Sink, error, [{pid, Worker} | Md],
  235. "Ranch listener ~p terminated in ~p:~p/~p with reason: ~s",
  236. [Ref, Module, Function, Arity, Formatted]);
  237. [Ref, _Protocol, Worker, Reason] ->
  238. {Md, Formatted} = format_reason_md(Reason),
  239. ?LOGFMT(Sink, error, [{pid, Worker} | Md],
  240. "Ranch listener ~p terminated with reason: ~s",
  241. [Ref, Formatted]);
  242. [Ref, Protocol, Ret] ->
  243. %% ranch_conns_sup.erl module line 119-123 has three parameters error msg, log it.
  244. {Md, Formatted} = format_reason_md(Ret),
  245. ?LOGFMT(Sink, error, [{pid, Protocol} | Md],
  246. "Ranch listener ~p terminated with result:~s",
  247. [Ref, Formatted])
  248. end;
  249. {false, "webmachine error"++_} ->
  250. %% Webmachine HTTP server error
  251. ?CRASH_LOG(Event),
  252. [Path, Error] = Args,
  253. %% webmachine likes to mangle the stack, for some reason
  254. StackTrace = case Error of
  255. {error, {error, Reason, Stack}} ->
  256. {Reason, Stack};
  257. _ ->
  258. Error
  259. end,
  260. {Md, Formatted} = format_reason_md(StackTrace),
  261. ?LOGFMT(Sink, error, [{pid, Pid} | Md], "Webmachine error at path ~p : ~s", [Path, Formatted]);
  262. _ ->
  263. ?CRASH_LOG(Event),
  264. ?LOGFMT(Sink, error, Pid, Fmt, Args)
  265. end;
  266. {error_report, _GL, {Pid, std_error, D}} ->
  267. ?CRASH_LOG(Event),
  268. ?LOGMSG(Sink, error, Pid, print_silly_list(D));
  269. {error_report, _GL, {Pid, supervisor_report, D}} ->
  270. ?CRASH_LOG(Event),
  271. case lists:sort(D) of
  272. [{errorContext, Ctx}, {offender, Off}, {reason, Reason}, {supervisor, Name}] ->
  273. Offender = format_offender(Off),
  274. {Md, Formatted} = format_reason_md(Reason),
  275. ?LOGFMT(Sink, error, [{pid, Pid} | Md],
  276. "Supervisor ~w had child ~s exit with reason ~s in context ~w",
  277. [supervisor_name(Name), Offender, Formatted, Ctx]);
  278. _ ->
  279. ?LOGMSG(Sink, error, Pid, "SUPERVISOR REPORT " ++ print_silly_list(D))
  280. end;
  281. {error_report, _GL, {Pid, crash_report, [Self, Neighbours]}} ->
  282. ?CRASH_LOG(Event),
  283. {Md, Formatted} = format_crash_report(Self, Neighbours),
  284. ?LOGMSG(Sink, error, [{pid, Pid} | Md], "CRASH REPORT " ++ Formatted);
  285. {warning_msg, _GL, {Pid, Fmt, Args}} ->
  286. ?LOGFMT(Sink, warning, Pid, Fmt, Args);
  287. {warning_report, _GL, {Pid, std_warning, Report}} ->
  288. ?LOGMSG(Sink, warning, Pid, print_silly_list(Report));
  289. {info_msg, _GL, {Pid, Fmt, Args}} ->
  290. ?LOGFMT(Sink, info, Pid, Fmt, Args);
  291. {info_report, _GL, {Pid, std_info, D}} when is_list(D) ->
  292. Details = lists:sort(D),
  293. case Details of
  294. [{application, App}, {exited, Reason}, {type, _Type}] ->
  295. case application:get_env(lager, suppress_application_start_stop, false) of
  296. true when Reason == stopped ->
  297. no_log;
  298. _ ->
  299. {Md, Formatted} = format_reason_md(Reason),
  300. ?LOGFMT(Sink, info, [{pid, Pid} | Md], "Application ~w exited with reason: ~s",
  301. [App, Formatted])
  302. end;
  303. _ ->
  304. ?LOGMSG(Sink, info, Pid, print_silly_list(D))
  305. end;
  306. {info_report, _GL, {Pid, std_info, D}} ->
  307. ?LOGFMT(Sink, info, Pid, "~w", [D]);
  308. {info_report, _GL, {P, progress, D}} ->
  309. Details = lists:sort(D),
  310. case Details of
  311. [{application, App}, {started_at, Node}] ->
  312. case application:get_env(lager, suppress_application_start_stop, false) of
  313. true ->
  314. no_log;
  315. _ ->
  316. ?LOGFMT(Sink, info, P, "Application ~w started on node ~w",
  317. [App, Node])
  318. end;
  319. [{started, Started}, {supervisor, Name}] ->
  320. case application:get_env(lager, suppress_supervisor_start_stop, false) of
  321. true ->
  322. no_log;
  323. _ ->
  324. MFA = format_mfa(get_value(mfargs, Started)),
  325. Pid = get_value(pid, Started),
  326. ?LOGFMT(Sink, debug, P, "Supervisor ~w started ~s at pid ~w",
  327. [supervisor_name(Name), MFA, Pid])
  328. end;
  329. _ ->
  330. ?LOGMSG(Sink, info, P, "PROGRESS REPORT " ++ print_silly_list(D))
  331. end;
  332. _ ->
  333. ?LOGFMT(Sink, warning, self(), "Unexpected error_logger event ~w", [Event])
  334. end,
  335. case DidLog of
  336. logged ->
  337. {ok, State};
  338. no_log ->
  339. Shaper = State#state.shaper,
  340. {ok, State#state{
  341. shaper = Shaper#lager_shaper{
  342. mps = Shaper#lager_shaper.mps - 1
  343. }
  344. }
  345. }
  346. end.
  347. format_crash_report(Report, Neighbours) ->
  348. Name = case get_value(registered_name, Report, []) of
  349. [] ->
  350. %% process_info(Pid, registered_name) returns [] for unregistered processes
  351. get_value(pid, Report);
  352. Atom -> Atom
  353. end,
  354. Md0 = case get_value(dictionary, Report, []) of
  355. [] ->
  356. %% process_info(Pid, registered_name) returns [] for unregistered processes
  357. [];
  358. Dict ->
  359. %% pull the lager metadata out of the process dictionary, if we can
  360. get_value('_lager_metadata', Dict, [])
  361. end,
  362. {Class, Reason, Trace} = get_value(error_info, Report),
  363. {Md, ReasonStr} = format_reason_md({Reason, Trace}),
  364. Type = case Class of
  365. exit -> "exited";
  366. _ -> "crashed"
  367. end,
  368. {Md0 ++ Md, io_lib:format("Process ~w with ~w neighbours ~s with reason: ~s",
  369. [Name, length(Neighbours), Type, ReasonStr])}.
  370. format_offender(Off) ->
  371. case get_value(mfargs, Off) of
  372. undefined ->
  373. %% supervisor_bridge
  374. io_lib:format("at module ~w at ~w",
  375. [get_value(mod, Off), get_value(pid, Off)]);
  376. MFArgs ->
  377. %% regular supervisor
  378. {_, MFA} = format_mfa_md(MFArgs),
  379. %% In 2014 the error report changed from `name' to
  380. %% `id', so try that first.
  381. Name = case get_value(id, Off) of
  382. undefined ->
  383. get_value(name, Off);
  384. Id ->
  385. Id
  386. end,
  387. io_lib:format("~p started with ~s at ~w",
  388. [Name, MFA, get_value(pid, Off)])
  389. end.
  390. %% backwards compatability shim
  391. format_reason(Reason) ->
  392. element(2, format_reason_md(Reason)).
  393. -spec format_reason_md(Stacktrace:: any()) -> {Metadata:: [{atom(), any()}], String :: list()}.
  394. format_reason_md({'function not exported', [{M, F, A},MFA|_]}) ->
  395. {Md, Formatted} = format_mfa_md(MFA),
  396. {_, Formatted2} = format_mfa_md({M, F, length(A)}),
  397. {[{reason, 'function not exported'} | Md],
  398. ["call to undefined function ", Formatted2,
  399. " from ", Formatted]};
  400. format_reason_md({'function not exported', [{M, F, A, _Props},MFA|_]}) ->
  401. %% R15 line numbers
  402. {Md, Formatted} = format_mfa_md(MFA),
  403. {_, Formatted2} = format_mfa_md({M, F, length(A)}),
  404. {[{reason, 'function not exported'} | Md],
  405. ["call to undefined function ", Formatted2,
  406. " from ", Formatted]};
  407. format_reason_md({undef, [MFA|_]}) ->
  408. {Md, Formatted} = format_mfa_md(MFA),
  409. {[{reason, undef} | Md],
  410. ["call to undefined function ", Formatted]};
  411. format_reason_md({bad_return, {_MFA, {'EXIT', Reason}}}) ->
  412. format_reason_md(Reason);
  413. format_reason_md({bad_return, {MFA, Val}}) ->
  414. {Md, Formatted} = format_mfa_md(MFA),
  415. {[{reason, bad_return} | Md],
  416. ["bad return value ", print_val(Val), " from ", Formatted]};
  417. format_reason_md({bad_return_value, Val}) ->
  418. {[{reason, bad_return}],
  419. ["bad return value: ", print_val(Val)]};
  420. format_reason_md({{bad_return_value, Val}, MFA}) ->
  421. {Md, Formatted} = format_mfa_md(MFA),
  422. {[{reason, bad_return_value} | Md],
  423. ["bad return value: ", print_val(Val), " in ", Formatted]};
  424. format_reason_md({{badrecord, Record}, [MFA|_]}) ->
  425. {Md, Formatted} = format_mfa_md(MFA),
  426. {[{reason, badrecord} | Md],
  427. ["bad record ", print_val(Record), " in ", Formatted]};
  428. format_reason_md({{case_clause, Val}, [MFA|_]}) ->
  429. {Md, Formatted} = format_mfa_md(MFA),
  430. {[{reason, case_clause} | Md],
  431. ["no case clause matching ", print_val(Val), " in ", Formatted]};
  432. format_reason_md({function_clause, [MFA|_]}) ->
  433. {Md, Formatted} = format_mfa_md(MFA),
  434. {[{reason, function_clause} | Md],
  435. ["no function clause matching ", Formatted]};
  436. format_reason_md({if_clause, [MFA|_]}) ->
  437. {Md, Formatted} = format_mfa_md(MFA),
  438. {[{reason, if_clause} | Md],
  439. ["no true branch found while evaluating if expression in ", Formatted]};
  440. format_reason_md({{try_clause, Val}, [MFA|_]}) ->
  441. {Md, Formatted} = format_mfa_md(MFA),
  442. {[{reason, try_clause} | Md],
  443. ["no try clause matching ", print_val(Val), " in ", Formatted]};
  444. format_reason_md({badarith, [MFA|_]}) ->
  445. {Md, Formatted} = format_mfa_md(MFA),
  446. {[{reason, badarith} | Md],
  447. ["bad arithmetic expression in ", Formatted]};
  448. format_reason_md({{badmatch, Val}, [MFA|_]}) ->
  449. {Md, Formatted} = format_mfa_md(MFA),
  450. {[{reason, badmatch} | Md],
  451. ["no match of right hand value ", print_val(Val), " in ", Formatted]};
  452. format_reason_md({emfile, _Trace}) ->
  453. {[{reason, emfile}],
  454. "maximum number of file descriptors exhausted, check ulimit -n"};
  455. format_reason_md({system_limit, [{M, F, _}|_] = Trace}) ->
  456. Limit = case {M, F} of
  457. {erlang, open_port} ->
  458. "maximum number of ports exceeded";
  459. {erlang, spawn} ->
  460. "maximum number of processes exceeded";
  461. {erlang, spawn_opt} ->
  462. "maximum number of processes exceeded";
  463. {erlang, list_to_atom} ->
  464. "tried to create an atom larger than 255, or maximum atom count exceeded";
  465. {ets, new} ->
  466. "maximum number of ETS tables exceeded";
  467. _ ->
  468. {Str, _} = lager_trunc_io:print(Trace, 500),
  469. Str
  470. end,
  471. {[{reason, system_limit}], ["system limit: ", Limit]};
  472. format_reason_md({badarg, [MFA,MFA2|_]}) ->
  473. case MFA of
  474. {_M, _F, A, _Props} when is_list(A) ->
  475. %% R15 line numbers
  476. {Md, Formatted} = format_mfa_md(MFA2),
  477. {_, Formatted2} = format_mfa_md(MFA),
  478. {[{reason, badarg} | Md],
  479. ["bad argument in call to ", Formatted2, " in ", Formatted]};
  480. {_M, _F, A} when is_list(A) ->
  481. {Md, Formatted} = format_mfa_md(MFA2),
  482. {_, Formatted2} = format_mfa_md(MFA),
  483. {[{reason, badarg} | Md],
  484. ["bad argument in call to ", Formatted2, " in ", Formatted]};
  485. _ ->
  486. %% seems to be generated by a bad call to a BIF
  487. {Md, Formatted} = format_mfa_md(MFA),
  488. {[{reason, badarg} | Md],
  489. ["bad argument in ", Formatted]}
  490. end;
  491. format_reason_md({{badarg, Stack}, _}) ->
  492. format_reason_md({badarg, Stack});
  493. format_reason_md({{badarity, {Fun, Args}}, [MFA|_]}) ->
  494. {arity, Arity} = lists:keyfind(arity, 1, erlang:fun_info(Fun)),
  495. {Md, Formatted} = format_mfa_md(MFA),
  496. {[{reason, badarity} | Md],
  497. [io_lib:format("fun called with wrong arity of ~w instead of ~w in ",
  498. [length(Args), Arity]), Formatted]};
  499. format_reason_md({noproc, MFA}) ->
  500. {Md, Formatted} = format_mfa_md(MFA),
  501. {[{reason, noproc} | Md],
  502. ["no such process or port in call to ", Formatted]};
  503. format_reason_md({{badfun, Term}, [MFA|_]}) ->
  504. {Md, Formatted} = format_mfa_md(MFA),
  505. {[{reason, badfun} | Md],
  506. ["bad function ", print_val(Term), " in ", Formatted]};
  507. format_reason_md({Reason, [{M, F, A}|_]}) when is_atom(M), is_atom(F), is_integer(A) ->
  508. {Md, Formatted} = format_reason_md(Reason),
  509. {_, Formatted2} = format_mfa_md({M, F, A}),
  510. {Md, [Formatted, " in ", Formatted2]};
  511. format_reason_md({Reason, [{M, F, A, Props}|_]}) when is_atom(M), is_atom(F), is_integer(A), is_list(Props) ->
  512. %% line numbers
  513. {Md, Formatted} = format_reason_md(Reason),
  514. {_, Formatted2} = format_mfa_md({M, F, A, Props}),
  515. {Md, [Formatted, " in ", Formatted2]};
  516. format_reason_md(Reason) ->
  517. {Str, _} = lager_trunc_io:print(Reason, 500),
  518. {[], Str}.
  519. %% backwards compatability shim
  520. format_mfa(MFA) ->
  521. element(2, format_mfa_md(MFA)).
  522. -spec format_mfa_md(any()) -> {[{atom(), any()}], list()}.
  523. format_mfa_md({M, F, A}) when is_list(A) ->
  524. {FmtStr, Args} = format_args(A, [], []),
  525. {[{module, M}, {function, F}], io_lib:format("~w:~w("++FmtStr++")", [M, F | Args])};
  526. format_mfa_md({M, F, A}) when is_integer(A) ->
  527. {[{module, M}, {function, F}], io_lib:format("~w:~w/~w", [M, F, A])};
  528. format_mfa_md({M, F, A, Props}) when is_list(Props) ->
  529. case get_value(line, Props) of
  530. undefined ->
  531. format_mfa_md({M, F, A});
  532. Line ->
  533. {Md, Formatted} = format_mfa_md({M, F, A}),
  534. {[{line, Line} | Md], [Formatted, io_lib:format(" line ~w", [Line])]}
  535. end;
  536. format_mfa_md([{M, F, A}| _]) ->
  537. %% this kind of weird stacktrace can be generated by a uncaught throw in a gen_server
  538. format_mfa_md({M, F, A});
  539. format_mfa_md([{M, F, A, Props}| _]) when is_list(Props) ->
  540. %% this kind of weird stacktrace can be generated by a uncaught throw in a gen_server
  541. %% TODO we might not always want to print the first MFA we see here, often it is more helpful
  542. %% to print a lower one, but it is hard to programatically decide.
  543. format_mfa_md({M, F, A, Props});
  544. format_mfa_md(Other) ->
  545. {[], io_lib:format("~w", [Other])}.
  546. format_args([], FmtAcc, ArgsAcc) ->
  547. {string:join(lists:reverse(FmtAcc), ", "), lists:reverse(ArgsAcc)};
  548. format_args([H|T], FmtAcc, ArgsAcc) ->
  549. {Str, _} = lager_trunc_io:print(H, 100),
  550. format_args(T, ["~s"|FmtAcc], [Str|ArgsAcc]).
  551. print_silly_list(L) when is_list(L) ->
  552. case lager_stdlib:string_p(L) of
  553. true ->
  554. lager_trunc_io:format("~s", [L], ?DEFAULT_TRUNCATION);
  555. _ ->
  556. print_silly_list(L, [], [])
  557. end;
  558. print_silly_list(L) ->
  559. {Str, _} = lager_trunc_io:print(L, ?DEFAULT_TRUNCATION),
  560. Str.
  561. print_silly_list([], Fmt, Acc) ->
  562. lager_trunc_io:format(string:join(lists:reverse(Fmt), ", "),
  563. lists:reverse(Acc), ?DEFAULT_TRUNCATION);
  564. print_silly_list([{K,V}|T], Fmt, Acc) ->
  565. print_silly_list(T, ["~p: ~p" | Fmt], [V, K | Acc]);
  566. print_silly_list([H|T], Fmt, Acc) ->
  567. print_silly_list(T, ["~p" | Fmt], [H | Acc]).
  568. print_val(Val) ->
  569. {Str, _} = lager_trunc_io:print(Val, 500),
  570. Str.
  571. %% @doc Faster than proplists, but with the same API as long as you don't need to
  572. %% handle bare atom keys
  573. get_value(Key, Value) ->
  574. get_value(Key, Value, undefined).
  575. get_value(Key, List, Default) ->
  576. case lists:keyfind(Key, 1, List) of
  577. false -> Default;
  578. {Key, Value} -> Value
  579. end.
  580. supervisor_name({local, Name}) -> Name;
  581. supervisor_name(Name) -> Name.
  582. -ifdef(TEST).
  583. -include_lib("eunit/include/eunit.hrl").
  584. no_silent_hwm_drops_test_() ->
  585. {timeout, 10000,
  586. [
  587. fun() ->
  588. error_logger:tty(false),
  589. application:load(lager),
  590. application:set_env(lager, handlers, [{lager_test_backend, warning}]),
  591. application:set_env(lager, error_logger_redirect, true),
  592. application:set_env(lager, error_logger_hwm, 5),
  593. application:set_env(lager, error_logger_flush_queue, false),
  594. application:set_env(lager, suppress_supervisor_start_stop, true),
  595. application:set_env(lager, suppress_application_start_stop, true),
  596. application:unset_env(lager, crash_log),
  597. lager:start(),
  598. try
  599. {_, _, MS} = os:timestamp(),
  600. timer:sleep((1000000 - MS) div 1000 + 1),
  601. % start close to the beginning of a new second
  602. [error_logger:error_msg("Foo ~p~n", [K]) || K <- lists:seq(1, 15)],
  603. wait_for_message("lager_error_logger_h dropped 10 messages in the last second that exceeded the limit of 5 messages/sec", 100, 50),
  604. % and once again
  605. [error_logger:error_msg("Foo1 ~p~n", [K]) || K <- lists:seq(1, 20)],
  606. wait_for_message("lager_error_logger_h dropped 15 messages in the last second that exceeded the limit of 5 messages/sec", 100, 50)
  607. after
  608. application:stop(lager),
  609. application:stop(goldrush),
  610. error_logger:tty(true)
  611. end
  612. end
  613. ]
  614. }.
  615. shaper_does_not_forward_sup_progress_messages_to_info_level_backend_test_() ->
  616. {timeout, 10000,
  617. [fun() ->
  618. error_logger:tty(false),
  619. application:load(lager),
  620. application:set_env(lager, handlers, [{lager_test_backend, info}]),
  621. application:set_env(lager, error_logger_redirect, true),
  622. application:set_env(lager, error_logger_hwm, 5),
  623. application:set_env(lager, suppress_supervisor_start_stop, false),
  624. application:set_env(lager, suppress_application_start_stop, false),
  625. application:unset_env(lager, crash_log),
  626. lager:start(),
  627. try
  628. PidPlaceholder = self(),
  629. SupervisorMsg =
  630. [{supervisor, {PidPlaceholder,rabbit_connection_sup}},
  631. {started,
  632. [{pid, PidPlaceholder},
  633. {name,helper_sup},
  634. {mfargs,
  635. {rabbit_connection_helper_sup,start_link,[]}},
  636. {restart_type,intrinsic},
  637. {shutdown,infinity},
  638. {child_type,supervisor}]}],
  639. ApplicationExit =
  640. [{application, error_logger_lager_h_test},
  641. {exited, stopped},
  642. {type, permanent}],
  643. error_logger:info_report("This is not a progress message"),
  644. error_logger:info_report(ApplicationExit),
  645. [error_logger:info_report(progress, SupervisorMsg) || _K <- lists:seq(0, 100)],
  646. error_logger:info_report("This is not a progress message 2"),
  647. % Note: this gets logged in slow environments:
  648. % Application lager started on node nonode@nohost
  649. wait_for_count(fun lager_test_backend:count/0, [3, 4], 100, 50),
  650. % Note: this debug msg gets ignored in slow environments:
  651. % Lager installed handler lager_test_backend into lager_event
  652. wait_for_count(fun lager_test_backend:count_ignored/0, [0, 1], 100, 50)
  653. after
  654. application:stop(lager),
  655. application:stop(goldrush),
  656. error_logger:tty(true)
  657. end
  658. end
  659. ]
  660. }.
  661. supressed_messages_are_not_counted_for_hwm_test_() ->
  662. {timeout, 10000,
  663. [fun() ->
  664. error_logger:tty(false),
  665. application:load(lager),
  666. application:set_env(lager, handlers, [{lager_test_backend, debug}]),
  667. application:set_env(lager, error_logger_redirect, true),
  668. application:set_env(lager, error_logger_hwm, 5),
  669. application:set_env(lager, suppress_supervisor_start_stop, true),
  670. application:set_env(lager, suppress_application_start_stop, true),
  671. application:unset_env(lager, crash_log),
  672. lager:start(),
  673. try
  674. PidPlaceholder = self(),
  675. SupervisorMsg =
  676. [{supervisor, {PidPlaceholder,rabbit_connection_sup}},
  677. {started,
  678. [{pid, PidPlaceholder},
  679. {name,helper_sup},
  680. {mfargs,
  681. {rabbit_connection_helper_sup,start_link,[]}},
  682. {restart_type,intrinsic},
  683. {shutdown,infinity},
  684. {child_type,supervisor}]}],
  685. ApplicationExit =
  686. [{application, error_logger_lager_h_test},
  687. {exited, stopped},
  688. {type, permanent}],
  689. lager_test_backend:flush(),
  690. error_logger:info_report("This is not a progress message"),
  691. [error_logger:info_report(ApplicationExit) || _K <- lists:seq(0, 100)],
  692. [error_logger:info_report(progress, SupervisorMsg) || _K <- lists:seq(0, 100)],
  693. error_logger:info_report("This is not a progress message 2"),
  694. wait_for_count(fun lager_test_backend:count/0, 2, 100, 50),
  695. wait_for_count(fun lager_test_backend:count_ignored/0, 0, 100, 50)
  696. after
  697. application:stop(lager),
  698. application:stop(goldrush),
  699. error_logger:tty(true)
  700. end
  701. end
  702. ]
  703. }.
  704. wait_for_message(Expected, Tries, Sleep) ->
  705. maybe_find_expected_message(lager_test_backend:get_buffer(), Expected, Tries, Sleep).
  706. maybe_find_expected_message(_Buffer, Expected, 0, _Sleep) ->
  707. throw({not_found, Expected});
  708. maybe_find_expected_message([], Expected, Tries, Sleep) ->
  709. timer:sleep(Sleep),
  710. maybe_find_expected_message(lager_test_backend:get_buffer(), Expected, Tries - 1, Sleep);
  711. maybe_find_expected_message([{_Severity, _Date, Msg, _Metadata}|T], Expected, Tries, Sleep) ->
  712. case lists:flatten(Msg) of
  713. Expected ->
  714. ok;
  715. _ ->
  716. maybe_find_expected_message(T, Expected, Tries, Sleep)
  717. end.
  718. wait_for_count(Fun, _Expected, 0, _Sleep) ->
  719. Actual = Fun(),
  720. Msg = io_lib:format("wait_for_count: fun ~p final value: ~p~n", [Fun, Actual]),
  721. throw({failed, Msg});
  722. wait_for_count(Fun, Expected, Tries, Sleep) when is_list(Expected) ->
  723. Actual = Fun(),
  724. case lists:member(Actual, Expected) of
  725. true ->
  726. ok;
  727. false ->
  728. timer:sleep(Sleep),
  729. wait_for_count(Fun, Expected, Tries - 1, Sleep)
  730. end;
  731. wait_for_count(Fun, Expected, Tries, Sleep) ->
  732. case Fun() of
  733. Expected ->
  734. ok;
  735. _ ->
  736. timer:sleep(Sleep),
  737. wait_for_count(Fun, Expected, Tries - 1, Sleep)
  738. end.
  739. -endif.