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

753 行
34 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年前
  1. -module(rumErrLoggerH).
  2. %% 一个error_logger后端,用于将事件重定向到eRum。
  3. %% 错误消息和崩溃日志也可以选择写入崩溃日志。 @see rumCrashLog
  4. -include("eRum.hrl").
  5. -behaviour(gen_event).
  6. -export([
  7. setHighWater/1
  8. , formatReason/1
  9. , formatMfa/1
  10. , formatArgs/2
  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} = formatReasonMd(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} = formatReasonMd(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} = formatReasonMd(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} = formatReasonMd(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} = formatReasonMd({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} = formatReasonMd({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} = formatReasonMd({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} = formatReasonMd(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} = formatReasonMd(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} = formatReasonMd(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, printSillyList(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 = formatOffender(Off),
  285. {Md, Formatted} = formatReasonMd(Reason),
  286. ?LOGFMT(Sink, error, [{pid, Pid} | Md],
  287. "Supervisor ~w had child ~s exit with reason ~s in context ~w",
  288. [supervisorName(Name), Offender, Formatted, Ctx]);
  289. _ ->
  290. ?LOGMSG(Sink, error, Pid, <<"SUPERVISOR REPORT ", (printSillyList(D))/binary>>)
  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, printSillyList(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} = formatReasonMd(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, printSillyList(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 = formatMfa(get_value(mfargs, Started)),
  336. Pid = get_value(pid, Started),
  337. ?LOGFMT(Sink, debug, P, "Supervisor ~w started ~s at pid ~w",
  338. [supervisorName(Name), MFA, Pid])
  339. end;
  340. _ ->
  341. ?LOGMSG(Sink, info, P, <<"PROGRESS REPORT ", (printSillyList(D))/binary>>)
  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} = formatReasonMd({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. formatOffender(Off) ->
  375. case get_value(mfargs, Off) of
  376. undefined ->
  377. %% supervisor_bridge
  378. eFmt:formatBin(<<"at module ~w at ~w">>, [get_value(mod, Off), get_value(pid, Off)]);
  379. MFArgs ->
  380. %% regular supervisor
  381. {_, MFA} = formatMfaMd(MFArgs),
  382. %% In 2014 the error report changed from `name' to
  383. %% `id', so try that first.
  384. Name =
  385. case get_value(id, Off) of
  386. undefined ->
  387. get_value(name, Off);
  388. Id ->
  389. Id
  390. end,
  391. eFmt:formatBin(<<"at module ~w at ~w">>, [Name, MFA, get_value(pid, Off)])
  392. end.
  393. %% backwards compatability shim
  394. formatReason(Reason) ->
  395. element(2, formatReasonMd(Reason)).
  396. -spec formatReasonMd(Stacktrace :: any()) -> {Metadata :: [{atom(), any()}], String :: list()}.
  397. formatReasonMd({'function not exported', [{M, F, A}, MFA | _]}) ->
  398. {Md, Formatted} = formatMfaMd(MFA),
  399. {_, Formatted2} = formatMfaMd({M, F, length(A)}),
  400. {[{reason, 'function not exported'} | Md], <<"call to undefined function ", Formatted2/binary, " from ", Formatted/binary>>};
  401. formatReasonMd({'function not exported', [{M, F, A, _Props}, MFA | _]}) ->
  402. %% R15 line numbers
  403. {Md, Formatted} = formatMfaMd(MFA),
  404. {_, Formatted2} = formatMfaMd({M, F, length(A)}),
  405. {[{reason, 'function not exported'} | Md], <<"call to undefined function ", Formatted2/binary, " from ", Formatted/binary>>};
  406. formatReasonMd({undef, [MFA | _]}) ->
  407. {Md, Formatted} = formatMfaMd(MFA),
  408. {[{reason, undef} | Md], <<"call to undefined function ", Formatted/binary>>};
  409. formatReasonMd({bad_return, {_MFA, {'EXIT', Reason}}}) ->
  410. formatReasonMd(Reason);
  411. formatReasonMd({bad_return, {MFA, Val}}) ->
  412. {Md, Formatted} = formatMfaMd(MFA),
  413. {[{reason, bad_return} | Md], <<"bad return value ", (formatVal(Val))/binary, " from ", Formatted/binary>>};
  414. formatReasonMd({bad_return_value, Val}) ->
  415. {[{reason, bad_return}], <<"bad return value: ", (formatVal(Val))/binary>>};
  416. formatReasonMd({{bad_return_value, Val}, MFA}) ->
  417. {Md, Formatted} = formatMfaMd(MFA),
  418. {[{reason, bad_return_value} | Md], <<"bad return value: ", (formatVal(Val))/binary, " in ", Formatted/binary>>};
  419. formatReasonMd({{badrecord, Record}, [MFA | _]}) ->
  420. {Md, Formatted} = formatMfaMd(MFA),
  421. {[{reason, badrecord} | Md], <<"bad record ", (formatVal(Record))/binary, " in ", Formatted/binary>>};
  422. formatReasonMd({{case_clause, Val}, [MFA | _]}) ->
  423. {Md, Formatted} = formatMfaMd(MFA),
  424. {[{reason, case_clause} | Md], <<"no case clause matching ", (formatVal(Val))/binary, " in ", Formatted/binary>>};
  425. formatReasonMd({function_clause, [MFA | _]}) ->
  426. {Md, Formatted} = formatMfaMd(MFA),
  427. {[{reason, function_clause} | Md], <<"no function clause matching ", Formatted/binary>>};
  428. formatReasonMd({if_clause, [MFA | _]}) ->
  429. {Md, Formatted} = formatMfaMd(MFA),
  430. {[{reason, if_clause} | Md], <<"no true branch found while evaluating if expression in ", Formatted/binary>>};
  431. formatReasonMd({{try_clause, Val}, [MFA | _]}) ->
  432. {Md, Formatted} = formatMfaMd(MFA),
  433. {[{reason, try_clause} | Md], <<"no try clause matching ", (formatVal(Val))/binary, " in ", Formatted/binary>>};
  434. formatReasonMd({badarith, [MFA | _]}) ->
  435. {Md, Formatted} = formatMfaMd(MFA),
  436. {[{reason, badarith} | Md], <<"bad arithmetic expression in ", Formatted/binary>>};
  437. formatReasonMd({{badmatch, Val}, [MFA | _]}) ->
  438. {Md, Formatted} = formatMfaMd(MFA),
  439. {[{reason, badmatch} | Md], <<"no match of right hand value ", (formatVal(Val))/binary, " in ", Formatted/binary>>};
  440. formatReasonMd({emfile, _Trace}) ->
  441. {[{reason, emfile}], <<"maximum number of file descriptors exhausted, check ulimit -n">>};
  442. formatReasonMd({system_limit, [{M, F, _} | _] = Trace}) ->
  443. Limit =
  444. case {M, F} of
  445. {erlang, open_port} ->
  446. <<"maximum number of ports exceeded">>;
  447. {erlang, spawn} ->
  448. <<"maximum number of processes exceeded">>;
  449. {erlang, spawn_opt} ->
  450. <<"maximum number of processes exceeded">>;
  451. {erlang, list_to_atom} ->
  452. <<"tried to create an atom larger than 255, or maximum atom count exceeded">>;
  453. {ets, new} ->
  454. <<"maximum number of ETS tables exceeded">>;
  455. _ ->
  456. eFmt:formatBin(<<"~p">>, [Trace], [{charsLimit, 500}])
  457. end,
  458. {[{reason, system_limit}], <<"system limit: ", Limit/binary>>};
  459. formatReasonMd({badarg, [MFA, MFA2 | _]}) ->
  460. case MFA of
  461. {_M, _F, A, _Props} when is_list(A) ->
  462. %% R15 line numbers
  463. {Md, Formatted} = formatMfaMd(MFA2),
  464. {_, Formatted2} = formatMfaMd(MFA),
  465. {[{reason, badarg} | Md], <<"bad argument in call to ", Formatted2, " in ", Formatted/binary>>};
  466. {_M, _F, A} when is_list(A) ->
  467. {Md, Formatted} = formatMfaMd(MFA2),
  468. {_, Formatted2} = formatMfaMd(MFA),
  469. {[{reason, badarg} | Md], <<"bad argument in call to ", Formatted2, " in ", Formatted/binary>>};
  470. _ ->
  471. %% seems to be generated by a bad call to a BIF
  472. {Md, Formatted} = formatMfaMd(MFA),
  473. {[{reason, badarg} | Md], <<"bad argument in ", Formatted/binary>>}
  474. end;
  475. formatReasonMd({{badarg, Stack}, _}) ->
  476. formatReasonMd({badarg, Stack});
  477. formatReasonMd({{badarity, {Fun, Args}}, [MFA | _]}) ->
  478. {arity, Arity} = lists:keyfind(arity, 1, erlang:fun_info(Fun)),
  479. {Md, Formatted} = formatMfaMd(MFA),
  480. {[{reason, badarity} | Md], eFmt:formatBin("fun called with wrong arity of ~w instead of ~w in ~s", [length(Args), Arity, Formatted])};
  481. formatReasonMd({noproc, MFA}) ->
  482. {Md, Formatted} = formatMfaMd(MFA),
  483. {[{reason, noproc} | Md], <<"no such process or port in call to ", Formatted/binary>>};
  484. formatReasonMd({{badfun, Term}, [MFA | _]}) ->
  485. {Md, Formatted} = formatMfaMd(MFA),
  486. {[{reason, badfun} | Md], <<"bad function ", (formatVal(Term))/binary, " in ", Formatted/binary>>};
  487. formatReasonMd({Reason, [{M, F, A} | _]}) when is_atom(M), is_atom(F), is_integer(A) ->
  488. {Md, Formatted} = formatReasonMd(Reason),
  489. {_, Formatted2} = formatMfaMd({M, F, A}),
  490. {Md, <<Formatted/binary, " in ", Formatted2/binary>>};
  491. formatReasonMd({Reason, [{M, F, A, Props} | _]}) when is_atom(M), is_atom(F), is_integer(A), is_list(Props) ->
  492. %% line numbers
  493. {Md, Formatted} = formatReasonMd(Reason),
  494. {_, Formatted2} = formatMfaMd({M, F, A, Props}),
  495. {Md, <<Formatted/binary, " in ", Formatted2/binary>>};
  496. formatReasonMd(Reason) ->
  497. eFtm:formatBin(<<"~p">>, [Reason], [{charsLimit, 500}]).
  498. %% backwards compatability shim
  499. formatMfa(MFA) ->
  500. element(2, formatMfaMd(MFA)).
  501. -spec formatMfaMd(any()) -> {[{atom(), any()}], list()}.
  502. formatMfaMd({M, F, A}) when is_list(A) ->
  503. ArgsStr = formatArgs(A, <<>>),
  504. {[{module, M}, {function, F}], eFtm:formatBin(<<"~w:~w(", ArgsStr/binary, ")">>, [M, F])};
  505. formatMfaMd({M, F, A}) when is_integer(A) ->
  506. {[{module, M}, {function, F}], eFtm:formatBin(<<"~w:~w/~w">>, [M, F, A])};
  507. formatMfaMd({M, F, A, Props}) when is_list(Props) ->
  508. case get_value(line, Props) of
  509. undefined ->
  510. formatMfaMd({M, F, A});
  511. Line ->
  512. {Md, Formatted} = formatMfaMd({M, F, A}),
  513. {[{line, Line} | Md], <<Formatted/binary, " line ", (integer_to_binary(Line))/binary>>}
  514. end;
  515. formatMfaMd([{M, F, A} | _]) ->
  516. %% this kind of weird stacktrace can be generated by a uncaught throw in a gen_server
  517. formatMfaMd({M, F, A});
  518. formatMfaMd([{M, F, A, Props} | _]) when is_list(Props) ->
  519. %% this kind of weird stacktrace can be generated by a uncaught throw in a gen_server
  520. %% TODO we might not always want to print the first MFA we see here, often it is more helpful
  521. %% to print a lower one, but it is hard to programatically decide.
  522. formatMfaMd({M, F, A, Props});
  523. formatMfaMd(Other) ->
  524. {[], eFtm:formatBin(<<"~w">>, [Other])}.
  525. formatArgs([], ArgsAcc) ->
  526. ArgsAcc;
  527. formatArgs([H], ArgsAcc) ->
  528. Str = eFmt:formatBin(<<"~p">>, [H], [{charsLimit, 100}]),
  529. <<ArgsAcc/binary, Str/binary>>;
  530. formatArgs([H | T], ArgsAcc) ->
  531. Str = eFmt:formatBin(<<"~p">>, [H], [{charsLimit, 100}]),
  532. formatArgs(T, <<ArgsAcc/binary, Str/binary, ",">>).
  533. formatVal(Val) ->
  534. eFmt:formatBin(<<"~p">>, [Val], [{charsLimit, 500}]).
  535. printSillyList(L) ->
  536. eFmt:formatBin(<<"~p">>, [L], [{charsLimit, ?RumDefTruncation}]).
  537. %% @doc Faster than proplists, but with the same API as long as you don't need to
  538. %% handle bare atom keys
  539. get_value(Key, Value) ->
  540. get_value(Key, Value, undefined).
  541. get_value(Key, List, Default) ->
  542. case lists:keyfind(Key, 1, List) of
  543. false -> Default;
  544. {Key, Value} -> Value
  545. end.
  546. supervisorName({local, Name}) -> Name;
  547. supervisorName(Name) -> Name.
  548. -ifdef(TEST).
  549. -include_lib("eunit/include/eunit.hrl").
  550. no_silent_hwm_drops_test_() ->
  551. {timeout, 10000,
  552. [
  553. fun() ->
  554. error_logger:tty(false),
  555. application:load(lager),
  556. application:set_env(lager, handlers, [{lager_test_backend, warning}]),
  557. application:set_env(lager, errLoggerRedirect, true),
  558. application:set_env(lager, errLoggerHwm, 5),
  559. application:set_env(lager, errLoggerFlushQueue, false),
  560. application:set_env(lager, suppress_supervisor_start_stop, true),
  561. application:set_env(lager, suppress_application_start_stop, true),
  562. application:unset_env(lager, crash_log),
  563. eRum:start(),
  564. try
  565. {_, _, MS} = os:timestamp(),
  566. timer:sleep((1000000 - MS) div 1000 + 1),
  567. % start close to the beginning of a new second
  568. [error_logger:error_msg("Foo ~p~n", [K]) || K <- lists:seq(1, 15)],
  569. wait_for_message("lager_error_logger_h dropped 10 messages in the last second that exceeded the limit of 5 messages/sec", 100, 50),
  570. % and once again
  571. [error_logger:error_msg("Foo1 ~p~n", [K]) || K <- lists:seq(1, 20)],
  572. wait_for_message("lager_error_logger_h dropped 15 messages in the last second that exceeded the limit of 5 messages/sec", 100, 50)
  573. after
  574. application:stop(lager),
  575. application:stop(goldrush),
  576. error_logger:tty(true)
  577. end
  578. end
  579. ]
  580. }.
  581. shaper_does_not_forward_sup_progress_messages_to_info_level_backend_test_() ->
  582. {timeout, 10000,
  583. [fun() ->
  584. error_logger:tty(false),
  585. application:load(lager),
  586. application:set_env(lager, handlers, [{lager_test_backend, info}]),
  587. application:set_env(lager, errLoggerRedirect, true),
  588. application:set_env(lager, errLoggerHwm, 5),
  589. application:set_env(lager, suppress_supervisor_start_stop, false),
  590. application:set_env(lager, suppress_application_start_stop, false),
  591. application:unset_env(lager, crash_log),
  592. eRum:start(),
  593. try
  594. PidPlaceholder = self(),
  595. SupervisorMsg =
  596. [{supervisor, {PidPlaceholder, rabbit_connection_sup}},
  597. {started,
  598. [{pid, PidPlaceholder},
  599. {name, helper_sup},
  600. {mfargs,
  601. {rabbit_connection_helper_sup, start_link, []}},
  602. {restart_type, intrinsic},
  603. {shutdown, infinity},
  604. {child_type, supervisor}]}],
  605. ApplicationExit =
  606. [{application, error_logger_lager_h_test},
  607. {exited, stopped},
  608. {type, permanent}],
  609. error_logger:info_report("This is not a progress message"),
  610. error_logger:info_report(ApplicationExit),
  611. [error_logger:info_report(progress, SupervisorMsg) || _K <- lists:seq(0, 100)],
  612. error_logger:info_report("This is not a progress message 2"),
  613. % Note: this gets logged in slow environments:
  614. % Application lager started on node nonode@nohost
  615. wait_for_count(fun lager_test_backend:count/0, [3, 4], 100, 50),
  616. % Note: this debug msg gets ignored in slow environments:
  617. % Lager installed handler lager_test_backend into lager_event
  618. wait_for_count(fun lager_test_backend:count_ignored/0, [0, 1], 100, 50)
  619. after
  620. application:stop(lager),
  621. application:stop(goldrush),
  622. error_logger:tty(true)
  623. end
  624. end
  625. ]
  626. }.
  627. supressed_messages_are_not_counted_for_hwm_test_() ->
  628. {timeout, 10000,
  629. [fun() ->
  630. error_logger:tty(false),
  631. application:load(lager),
  632. application:set_env(lager, handlers, [{lager_test_backend, debug}]),
  633. application:set_env(lager, errLoggerRedirect, true),
  634. application:set_env(lager, errLoggerHwm, 5),
  635. application:set_env(lager, suppress_supervisor_start_stop, true),
  636. application:set_env(lager, suppress_application_start_stop, true),
  637. application:unset_env(lager, crash_log),
  638. eRum:start(),
  639. try
  640. PidPlaceholder = self(),
  641. SupervisorMsg =
  642. [{supervisor, {PidPlaceholder, rabbit_connection_sup}},
  643. {started,
  644. [{pid, PidPlaceholder},
  645. {name, helper_sup},
  646. {mfargs,
  647. {rabbit_connection_helper_sup, start_link, []}},
  648. {restart_type, intrinsic},
  649. {shutdown, infinity},
  650. {child_type, supervisor}]}],
  651. ApplicationExit =
  652. [{application, error_logger_lager_h_test},
  653. {exited, stopped},
  654. {type, permanent}],
  655. lager_test_backend:flush(),
  656. error_logger:info_report("This is not a progress message"),
  657. [error_logger:info_report(ApplicationExit) || _K <- lists:seq(0, 100)],
  658. [error_logger:info_report(progress, SupervisorMsg) || _K <- lists:seq(0, 100)],
  659. error_logger:info_report("This is not a progress message 2"),
  660. wait_for_count(fun lager_test_backend:count/0, 2, 100, 50),
  661. wait_for_count(fun lager_test_backend:count_ignored/0, 0, 100, 50)
  662. after
  663. application:stop(lager),
  664. application:stop(goldrush),
  665. error_logger:tty(true)
  666. end
  667. end
  668. ]
  669. }.
  670. wait_for_message(Expected, Tries, Sleep) ->
  671. maybe_find_expected_message(lager_test_backend:get_buffer(), Expected, Tries, Sleep).
  672. maybe_find_expected_message(_Buffer, Expected, 0, _Sleep) ->
  673. throw({not_found, Expected});
  674. maybe_find_expected_message([], Expected, Tries, Sleep) ->
  675. timer:sleep(Sleep),
  676. maybe_find_expected_message(lager_test_backend:get_buffer(), Expected, Tries - 1, Sleep);
  677. maybe_find_expected_message([{_Severity, _Date, Msg, _Metadata} | T], Expected, Tries, Sleep) ->
  678. case lists:flatten(Msg) of
  679. Expected ->
  680. ok;
  681. _ ->
  682. maybe_find_expected_message(T, Expected, Tries, Sleep)
  683. end.
  684. wait_for_count(Fun, _Expected, 0, _Sleep) ->
  685. Actual = Fun(),
  686. Msg = io_lib:format("wait_for_count: fun ~p final value: ~p~n", [Fun, Actual]),
  687. throw({failed, Msg});
  688. wait_for_count(Fun, Expected, Tries, Sleep) when is_list(Expected) ->
  689. Actual = Fun(),
  690. case lists:member(Actual, Expected) of
  691. true ->
  692. ok;
  693. false ->
  694. timer:sleep(Sleep),
  695. wait_for_count(Fun, Expected, Tries - 1, Sleep)
  696. end;
  697. wait_for_count(Fun, Expected, Tries, Sleep) ->
  698. case Fun() of
  699. Expected ->
  700. ok;
  701. _ ->
  702. timer:sleep(Sleep),
  703. wait_for_count(Fun, Expected, Tries - 1, Sleep)
  704. end.
  705. -endif.