rewrite from lager
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

780 wiersze
34 KiB

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