rewrite from lager
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

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