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.

782 regels
33 KiB

4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
4 jaren geleden
  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.