Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

631 lignes
27 KiB

il y a 14 ans
il y a 14 ans
  1. %% Copyright (c) 2011-2015 Basho Technologies, Inc. All Rights Reserved.
  2. %%
  3. %% This file is provided to you under the Apache License,
  4. %% Version 2.0 (the "License"); you may not use this file
  5. %% except in compliance with the License. You may obtain
  6. %% a copy of the License at
  7. %%
  8. %% http://www.apache.org/licenses/LICENSE-2.0
  9. %%
  10. %% Unless required by applicable law or agreed to in writing,
  11. %% software distributed under the License is distributed on an
  12. %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  13. %% KIND, either express or implied. See the License for the
  14. %% specific language governing permissions and limitations
  15. %% under the License.
  16. %% @doc A error_logger backend for redirecting events into lager.
  17. %% Error messages and crash logs are also optionally written to a crash log.
  18. %% @see lager_crash_log
  19. %% @private
  20. -module(error_logger_lager_h).
  21. -include("lager.hrl").
  22. -behaviour(gen_event).
  23. -export([set_high_water/1]).
  24. -export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2,
  25. code_change/3]).
  26. -export([format_reason/1, format_mfa/1, format_args/3]).
  27. -record(state, {
  28. sink :: atom(),
  29. shaper :: lager_shaper(),
  30. %% group leader strategy
  31. groupleader_strategy :: handle | ignore | mirror,
  32. raw :: boolean()
  33. }).
  34. -define(LOGMSG(Sink, Level, Pid, Msg),
  35. case ?SHOULD_LOG(Sink, Level) of
  36. true ->
  37. _ =lager:log(Sink, Level, Pid, Msg, []),
  38. ok;
  39. _ -> ok
  40. end).
  41. -define(LOGFMT(Sink, Level, Pid, Fmt, Args),
  42. case ?SHOULD_LOG(Sink, Level) of
  43. true ->
  44. _ = lager:log(Sink, Level, Pid, Fmt, Args),
  45. ok;
  46. _ -> ok
  47. end).
  48. -ifdef(TEST).
  49. -compile(export_all).
  50. %% Make CRASH synchronous when testing, to avoid timing headaches
  51. -define(CRASH_LOG(Event),
  52. catch(gen_server:call(lager_crash_log, {log, Event}))).
  53. -else.
  54. -define(CRASH_LOG(Event),
  55. gen_server:cast(lager_crash_log, {log, Event})).
  56. -endif.
  57. set_high_water(N) ->
  58. gen_event:call(error_logger, ?MODULE, {set_high_water, N}, infinity).
  59. -spec init(any()) -> {ok, #state{}}.
  60. init([HighWaterMark, GlStrategy]) ->
  61. Flush = application:get_env(lager, error_logger_flush_queue, true),
  62. FlushThr = application:get_env(lager, error_logger_flush_threshold, 0),
  63. Shaper = #lager_shaper{hwm=HighWaterMark, flush_queue = Flush, flush_threshold = FlushThr, filter=shaper_fun(), id=?MODULE},
  64. Raw = application:get_env(lager, error_logger_format_raw, false),
  65. Sink = configured_sink(),
  66. {ok, #state{sink=Sink, shaper=Shaper, groupleader_strategy=GlStrategy, raw=Raw}}.
  67. handle_call({set_high_water, N}, #state{shaper=Shaper} = State) ->
  68. NewShaper = Shaper#lager_shaper{hwm=N},
  69. {ok, ok, State#state{shaper = NewShaper}};
  70. handle_call(_Request, State) ->
  71. {ok, unknown_call, State}.
  72. shaper_fun() ->
  73. case {application:get_env(lager, suppress_supervisor_start_stop, false), application:get_env(lager, suppress_application_start_stop, false)} of
  74. {false, false} ->
  75. fun(_) -> false end;
  76. {true, true} ->
  77. fun suppress_supervisor_start_and_application_start/1;
  78. {false, true} ->
  79. fun suppress_application_start/1;
  80. {true, false} ->
  81. fun suppress_supervisor_start/1
  82. end.
  83. suppress_supervisor_start_and_application_start(E) ->
  84. suppress_supervisor_start(E) orelse suppress_application_start(E).
  85. suppress_application_start({info_report, _GL, {_Pid, std_info, D}}) when is_list(D) ->
  86. lists:member({exited, stopped}, D);
  87. suppress_application_start({info_report, _GL, {_P, progress, D}}) ->
  88. lists:keymember(application, 1, D) andalso lists:keymember(started_at, 1, D);
  89. suppress_application_start(_) ->
  90. false.
  91. suppress_supervisor_start({info_report, _GL, {_P, progress, D}}) ->
  92. lists:keymember(started, 1, D) andalso lists:keymember(supervisor, 1, D);
  93. suppress_supervisor_start(_) ->
  94. false.
  95. handle_event(Event, #state{sink=Sink, shaper=Shaper} = State) ->
  96. case lager_util:check_hwm(Shaper, Event) of
  97. {true, 0, NewShaper} ->
  98. eval_gl(Event, State#state{shaper=NewShaper});
  99. {true, Drop, #lager_shaper{hwm=Hwm} = NewShaper} when Drop > 0 ->
  100. ?LOGFMT(Sink, warning, self(),
  101. "lager_error_logger_h dropped ~p messages in the last second that exceeded the limit of ~p messages/sec",
  102. [Drop, Hwm]),
  103. eval_gl(Event, State#state{shaper=NewShaper});
  104. {false, _, #lager_shaper{dropped=D} = NewShaper} ->
  105. {ok, State#state{shaper=NewShaper#lager_shaper{dropped=D+1}}}
  106. end.
  107. handle_info({shaper_expired, ?MODULE}, #state{sink=Sink, shaper=Shaper} = State) ->
  108. case Shaper#lager_shaper.dropped of
  109. 0 ->
  110. ok;
  111. Dropped ->
  112. ?LOGFMT(Sink, warning, self(),
  113. "lager_error_logger_h dropped ~p messages in the last second that exceeded the limit of ~p messages/sec",
  114. [Dropped, Shaper#lager_shaper.hwm])
  115. end,
  116. {ok, State#state{shaper=Shaper#lager_shaper{dropped=0, mps=1, lasttime=os:timestamp()}}};
  117. handle_info(_Info, State) ->
  118. {ok, State}.
  119. terminate(_Reason, _State) ->
  120. ok.
  121. code_change(_OldVsn, {state, Shaper, GLStrategy}, _Extra) ->
  122. Raw = application:get_env(lager, error_logger_format_raw, false),
  123. {ok, #state{
  124. sink=configured_sink(),
  125. shaper=Shaper,
  126. groupleader_strategy=GLStrategy,
  127. raw=Raw
  128. }};
  129. code_change(_OldVsn, {state, Sink, Shaper, GLS}, _Extra) ->
  130. Raw = application:get_env(lager, error_logger_format_raw, false),
  131. {ok, #state{sink=Sink, shaper=Shaper, groupleader_strategy=GLS, raw=Raw}};
  132. code_change(_OldVsn, State, _Extra) ->
  133. {ok, State}.
  134. %% internal functions
  135. configured_sink() ->
  136. case proplists:get_value(?ERROR_LOGGER_SINK, application:get_env(lager, extra_sinks, [])) of
  137. undefined -> ?DEFAULT_SINK;
  138. _ -> ?ERROR_LOGGER_SINK
  139. end.
  140. eval_gl(Event, #state{groupleader_strategy=GlStrategy0}=State) when is_pid(element(2, Event)) ->
  141. case element(2, Event) of
  142. GL when node(GL) =/= node(), GlStrategy0 =:= ignore ->
  143. gen_event:notify({error_logger, node(GL)}, Event),
  144. {ok, State};
  145. GL when node(GL) =/= node(), GlStrategy0 =:= mirror ->
  146. gen_event:notify({error_logger, node(GL)}, Event),
  147. log_event(Event, State);
  148. _ ->
  149. log_event(Event, State)
  150. end;
  151. eval_gl(Event, State) ->
  152. log_event(Event, State).
  153. log_event(Event, #state{sink=Sink} = State) ->
  154. case Event of
  155. {error, _GL, {Pid, Fmt, Args}} ->
  156. FormatRaw = State#state.raw,
  157. case {FormatRaw, Fmt} of
  158. {false, "** Generic server "++_} ->
  159. %% gen_server terminate
  160. {Reason, Name} = case Args of
  161. [N, _Msg, _State, R] ->
  162. {R, N};
  163. [N, _Msg, _State, R, _Client] ->
  164. %% OTP 20 crash reports where the client pid is dead don't include the stacktrace
  165. {R, N};
  166. [N, _Msg, _State, R, _Client, _Stacktrace] ->
  167. %% OTP 20 crash reports contain the pid of the client and stacktrace
  168. %% TODO do something with them
  169. {R, N}
  170. end,
  171. ?CRASH_LOG(Event),
  172. {Md, Formatted} = format_reason_md(Reason),
  173. ?LOGFMT(Sink, error, [{pid, Pid}, {name, Name} | Md], "gen_server ~w terminated with reason: ~s",
  174. [Name, Formatted]);
  175. {false, "** State machine "++_} ->
  176. %% Check if the terminated process is gen_fsm or gen_statem
  177. %% since they generate the same exit message
  178. {Type, Name, StateName, Reason} = case Args of
  179. [TName, _Msg, TStateName, _StateData, TReason] ->
  180. {gen_fsm, TName, TStateName, TReason};
  181. [TName, _Msg, {TStateName, _StateData}, _ExitType, TReason, _FsmType, Stacktrace] ->
  182. {gen_statem, TName, TStateName, {TReason, Stacktrace}}
  183. end,
  184. {Md, Formatted} = format_reason_md(Reason),
  185. ?CRASH_LOG(Event),
  186. ?LOGFMT(Sink, error, [{pid, Pid}, {name, Name} | Md], "~s ~w in state ~w terminated with reason: ~s",
  187. [Type, Name, StateName, Formatted]);
  188. {false, "** gen_event handler"++_} ->
  189. %% gen_event handler terminate
  190. [ID, Name, _Msg, _State, Reason] = Args,
  191. {Md, Formatted} = format_reason_md(Reason),
  192. ?CRASH_LOG(Event),
  193. ?LOGFMT(Sink, error, [{pid, Pid}, {name, Name} | Md], "gen_event ~w installed in ~w terminated with reason: ~s",
  194. [ID, Name, Formatted]);
  195. {false, "** Cowboy handler"++_} ->
  196. %% Cowboy HTTP server error
  197. ?CRASH_LOG(Event),
  198. case Args of
  199. [Module, Function, Arity, _Request, _State] ->
  200. %% we only get the 5-element list when its a non-exported function
  201. ?LOGFMT(Sink, error, Pid,
  202. "Cowboy handler ~p terminated with reason: call to undefined function ~p:~p/~p",
  203. [Module, Module, Function, Arity]);
  204. [Module, Function, Arity, _Class, Reason | Tail] ->
  205. %% any other cowboy error_format list *always* ends with the stacktrace
  206. StackTrace = lists:last(Tail),
  207. {Md, Formatted} = format_reason_md({Reason, StackTrace}),
  208. ?LOGFMT(Sink, error, [{pid, Pid} | Md],
  209. "Cowboy handler ~p terminated in ~p:~p/~p with reason: ~s",
  210. [Module, Module, Function, Arity, Formatted])
  211. end;
  212. {false, "Ranch listener "++_} ->
  213. %% Ranch errors
  214. ?CRASH_LOG(Event),
  215. case Args of
  216. [Ref, _Protocol, Worker, {[{reason, Reason}, {mfa, {Module, Function, Arity}}, {stacktrace, StackTrace} | _], _}] ->
  217. {Md, Formatted} = format_reason_md({Reason, StackTrace}),
  218. ?LOGFMT(Sink, error, [{pid, Worker} | Md],
  219. "Ranch listener ~p terminated in ~p:~p/~p with reason: ~s",
  220. [Ref, Module, Function, Arity, Formatted]);
  221. [Ref, _Protocol, Worker, Reason] ->
  222. {Md, Formatted} = format_reason_md(Reason),
  223. ?LOGFMT(Sink, error, [{pid, Worker} | Md],
  224. "Ranch listener ~p terminated with reason: ~s",
  225. [Ref, Formatted]);
  226. [Ref, Protocol, Ret] ->
  227. %% ranch_conns_sup.erl module line 119-123 has three parameters error msg, log it.
  228. {Md, Formatted} = format_reason_md(Ret),
  229. ?LOGFMT(Sink, error, [{pid, Protocol} | Md],
  230. "Ranch listener ~p terminated with result:~s",
  231. [Ref, Formatted])
  232. end;
  233. {false, "webmachine error"++_} ->
  234. %% Webmachine HTTP server error
  235. ?CRASH_LOG(Event),
  236. [Path, Error] = Args,
  237. %% webmachine likes to mangle the stack, for some reason
  238. StackTrace = case Error of
  239. {error, {error, Reason, Stack}} ->
  240. {Reason, Stack};
  241. _ ->
  242. Error
  243. end,
  244. {Md, Formatted} = format_reason_md(StackTrace),
  245. ?LOGFMT(Sink, error, [{pid, Pid} | Md], "Webmachine error at path ~p : ~s", [Path, Formatted]);
  246. _ ->
  247. ?CRASH_LOG(Event),
  248. ?LOGFMT(Sink, error, Pid, Fmt, Args)
  249. end;
  250. {error_report, _GL, {Pid, std_error, D}} ->
  251. ?CRASH_LOG(Event),
  252. ?LOGMSG(Sink, error, Pid, print_silly_list(D));
  253. {error_report, _GL, {Pid, supervisor_report, D}} ->
  254. ?CRASH_LOG(Event),
  255. case lists:sort(D) of
  256. [{errorContext, Ctx}, {offender, Off}, {reason, Reason}, {supervisor, Name}] ->
  257. Offender = format_offender(Off),
  258. {Md, Formatted} = format_reason_md(Reason),
  259. ?LOGFMT(Sink, error, [{pid, Pid} | Md],
  260. "Supervisor ~w had child ~s exit with reason ~s in context ~w",
  261. [supervisor_name(Name), Offender, Formatted, Ctx]);
  262. _ ->
  263. ?LOGMSG(Sink, error, Pid, "SUPERVISOR REPORT " ++ print_silly_list(D))
  264. end;
  265. {error_report, _GL, {Pid, crash_report, [Self, Neighbours]}} ->
  266. ?CRASH_LOG(Event),
  267. {Md, Formatted} = format_crash_report(Self, Neighbours),
  268. ?LOGMSG(Sink, error, [{pid, Pid} | Md], "CRASH REPORT " ++ Formatted);
  269. {warning_msg, _GL, {Pid, Fmt, Args}} ->
  270. ?LOGFMT(Sink, warning, Pid, Fmt, Args);
  271. {warning_report, _GL, {Pid, std_warning, Report}} ->
  272. ?LOGMSG(Sink, warning, Pid, print_silly_list(Report));
  273. {info_msg, _GL, {Pid, Fmt, Args}} ->
  274. ?LOGFMT(Sink, info, Pid, Fmt, Args);
  275. {info_report, _GL, {Pid, std_info, D}} when is_list(D) ->
  276. Details = lists:sort(D),
  277. case Details of
  278. [{application, App}, {exited, Reason}, {type, _Type}] ->
  279. case application:get_env(lager, suppress_application_start_stop) of
  280. {ok, true} when Reason == stopped ->
  281. ok;
  282. _ ->
  283. {Md, Formatted} = format_reason_md(Reason),
  284. ?LOGFMT(Sink, info, [{pid, Pid} | Md], "Application ~w exited with reason: ~s",
  285. [App, Formatted])
  286. end;
  287. _ ->
  288. ?LOGMSG(Sink, info, Pid, print_silly_list(D))
  289. end;
  290. {info_report, _GL, {Pid, std_info, D}} ->
  291. ?LOGFMT(Sink, info, Pid, "~w", [D]);
  292. {info_report, _GL, {P, progress, D}} ->
  293. Details = lists:sort(D),
  294. case Details of
  295. [{application, App}, {started_at, Node}] ->
  296. case application:get_env(lager, suppress_application_start_stop) of
  297. {ok, true} ->
  298. ok;
  299. _ ->
  300. ?LOGFMT(Sink, info, P, "Application ~w started on node ~w",
  301. [App, Node])
  302. end;
  303. [{started, Started}, {supervisor, Name}] ->
  304. case application:get_env(lager, suppress_supervisor_start_stop, false) of
  305. true ->
  306. ok;
  307. _ ->
  308. MFA = format_mfa(get_value(mfargs, Started)),
  309. Pid = get_value(pid, Started),
  310. ?LOGFMT(Sink, debug, P, "Supervisor ~w started ~s at pid ~w",
  311. [supervisor_name(Name), MFA, Pid])
  312. end;
  313. _ ->
  314. ?LOGMSG(Sink, info, P, "PROGRESS REPORT " ++ print_silly_list(D))
  315. end;
  316. _ ->
  317. ?LOGFMT(Sink, warning, self(), "Unexpected error_logger event ~w", [Event])
  318. end,
  319. {ok, State}.
  320. format_crash_report(Report, Neighbours) ->
  321. Name = case get_value(registered_name, Report, []) of
  322. [] ->
  323. %% process_info(Pid, registered_name) returns [] for unregistered processes
  324. get_value(pid, Report);
  325. Atom -> Atom
  326. end,
  327. Md0 = case get_value(dictionary, Report, []) of
  328. [] ->
  329. %% process_info(Pid, registered_name) returns [] for unregistered processes
  330. [];
  331. Dict ->
  332. %% pull the lager metadata out of the process dictionary, if we can
  333. get_value('_lager_metadata', Dict, [])
  334. end,
  335. {Class, Reason, Trace} = get_value(error_info, Report),
  336. {Md, ReasonStr} = format_reason_md({Reason, Trace}),
  337. Type = case Class of
  338. exit -> "exited";
  339. _ -> "crashed"
  340. end,
  341. {Md0 ++ Md, io_lib:format("Process ~w with ~w neighbours ~s with reason: ~s",
  342. [Name, length(Neighbours), Type, ReasonStr])}.
  343. format_offender(Off) ->
  344. case get_value(mfargs, Off) of
  345. undefined ->
  346. %% supervisor_bridge
  347. io_lib:format("at module ~w at ~w",
  348. [get_value(mod, Off), get_value(pid, Off)]);
  349. MFArgs ->
  350. %% regular supervisor
  351. {_, MFA} = format_mfa_md(MFArgs),
  352. %% In 2014 the error report changed from `name' to
  353. %% `id', so try that first.
  354. Name = case get_value(id, Off) of
  355. undefined ->
  356. get_value(name, Off);
  357. Id ->
  358. Id
  359. end,
  360. io_lib:format("~p started with ~s at ~w",
  361. [Name, MFA, get_value(pid, Off)])
  362. end.
  363. %% backwards compatability shim
  364. format_reason(Reason) ->
  365. element(2, format_reason_md(Reason)).
  366. -spec format_reason_md(Stacktrace:: any()) -> {Metadata:: [{atom(), any()}], String :: list()}.
  367. format_reason_md({'function not exported', [{M, F, A},MFA|_]}) ->
  368. {Md, Formatted} = format_mfa_md(MFA),
  369. {_, Formatted2} = format_mfa_md({M, F, length(A)}),
  370. {[{reason, 'function not exported'} | Md],
  371. ["call to undefined function ", Formatted2,
  372. " from ", Formatted]};
  373. format_reason_md({'function not exported', [{M, F, A, _Props},MFA|_]}) ->
  374. %% R15 line numbers
  375. {Md, Formatted} = format_mfa_md(MFA),
  376. {_, Formatted2} = format_mfa_md({M, F, length(A)}),
  377. {[{reason, 'function not exported'} | Md],
  378. ["call to undefined function ", Formatted2,
  379. " from ", Formatted]};
  380. format_reason_md({undef, [MFA|_]}) ->
  381. {Md, Formatted} = format_mfa_md(MFA),
  382. {[{reason, undef} | Md],
  383. ["call to undefined function ", Formatted]};
  384. format_reason_md({bad_return, {_MFA, {'EXIT', Reason}}}) ->
  385. format_reason_md(Reason);
  386. format_reason_md({bad_return, {MFA, Val}}) ->
  387. {Md, Formatted} = format_mfa_md(MFA),
  388. {[{reason, bad_return} | Md],
  389. ["bad return value ", print_val(Val), " from ", Formatted]};
  390. format_reason_md({bad_return_value, Val}) ->
  391. {[{reason, bad_return}],
  392. ["bad return value: ", print_val(Val)]};
  393. format_reason_md({{bad_return_value, Val}, MFA}) ->
  394. {Md, Formatted} = format_mfa_md(MFA),
  395. {[{reason, bad_return_value} | Md],
  396. ["bad return value: ", print_val(Val), " in ", Formatted]};
  397. format_reason_md({{badrecord, Record}, [MFA|_]}) ->
  398. {Md, Formatted} = format_mfa_md(MFA),
  399. {[{reason, badrecord} | Md],
  400. ["bad record ", print_val(Record), " in ", Formatted]};
  401. format_reason_md({{case_clause, Val}, [MFA|_]}) ->
  402. {Md, Formatted} = format_mfa_md(MFA),
  403. {[{reason, case_clause} | Md],
  404. ["no case clause matching ", print_val(Val), " in ", Formatted]};
  405. format_reason_md({function_clause, [MFA|_]}) ->
  406. {Md, Formatted} = format_mfa_md(MFA),
  407. {[{reason, function_clause} | Md],
  408. ["no function clause matching ", Formatted]};
  409. format_reason_md({if_clause, [MFA|_]}) ->
  410. {Md, Formatted} = format_mfa_md(MFA),
  411. {[{reason, if_clause} | Md],
  412. ["no true branch found while evaluating if expression in ", Formatted]};
  413. format_reason_md({{try_clause, Val}, [MFA|_]}) ->
  414. {Md, Formatted} = format_mfa_md(MFA),
  415. {[{reason, try_clause} | Md],
  416. ["no try clause matching ", print_val(Val), " in ", Formatted]};
  417. format_reason_md({badarith, [MFA|_]}) ->
  418. {Md, Formatted} = format_mfa_md(MFA),
  419. {[{reason, badarith} | Md],
  420. ["bad arithmetic expression in ", Formatted]};
  421. format_reason_md({{badmatch, Val}, [MFA|_]}) ->
  422. {Md, Formatted} = format_mfa_md(MFA),
  423. {[{reason, badmatch} | Md],
  424. ["no match of right hand value ", print_val(Val), " in ", Formatted]};
  425. format_reason_md({emfile, _Trace}) ->
  426. {[{reason, emfile}],
  427. "maximum number of file descriptors exhausted, check ulimit -n"};
  428. format_reason_md({system_limit, [{M, F, _}|_] = Trace}) ->
  429. Limit = case {M, F} of
  430. {erlang, open_port} ->
  431. "maximum number of ports exceeded";
  432. {erlang, spawn} ->
  433. "maximum number of processes exceeded";
  434. {erlang, spawn_opt} ->
  435. "maximum number of processes exceeded";
  436. {erlang, list_to_atom} ->
  437. "tried to create an atom larger than 255, or maximum atom count exceeded";
  438. {ets, new} ->
  439. "maximum number of ETS tables exceeded";
  440. _ ->
  441. {Str, _} = lager_trunc_io:print(Trace, 500),
  442. Str
  443. end,
  444. {[{reason, system_limit}], ["system limit: ", Limit]};
  445. format_reason_md({badarg, [MFA,MFA2|_]}) ->
  446. case MFA of
  447. {_M, _F, A, _Props} when is_list(A) ->
  448. %% R15 line numbers
  449. {Md, Formatted} = format_mfa_md(MFA2),
  450. {_, Formatted2} = format_mfa_md(MFA),
  451. {[{reason, badarg} | Md],
  452. ["bad argument in call to ", Formatted2, " in ", Formatted]};
  453. {_M, _F, A} when is_list(A) ->
  454. {Md, Formatted} = format_mfa_md(MFA2),
  455. {_, Formatted2} = format_mfa_md(MFA),
  456. {[{reason, badarg} | Md],
  457. ["bad argument in call to ", Formatted2, " in ", Formatted]};
  458. _ ->
  459. %% seems to be generated by a bad call to a BIF
  460. {Md, Formatted} = format_mfa_md(MFA),
  461. {[{reason, badarg} | Md],
  462. ["bad argument in ", Formatted]}
  463. end;
  464. format_reason_md({{badarg, Stack}, _}) ->
  465. format_reason_md({badarg, Stack});
  466. format_reason_md({{badarity, {Fun, Args}}, [MFA|_]}) ->
  467. {arity, Arity} = lists:keyfind(arity, 1, erlang:fun_info(Fun)),
  468. {Md, Formatted} = format_mfa_md(MFA),
  469. {[{reason, badarity} | Md],
  470. [io_lib:format("fun called with wrong arity of ~w instead of ~w in ",
  471. [length(Args), Arity]), Formatted]};
  472. format_reason_md({noproc, MFA}) ->
  473. {Md, Formatted} = format_mfa_md(MFA),
  474. {[{reason, noproc} | Md],
  475. ["no such process or port in call to ", Formatted]};
  476. format_reason_md({{badfun, Term}, [MFA|_]}) ->
  477. {Md, Formatted} = format_mfa_md(MFA),
  478. {[{reason, badfun} | Md],
  479. ["bad function ", print_val(Term), " in ", Formatted]};
  480. format_reason_md({Reason, [{M, F, A}|_]}) when is_atom(M), is_atom(F), is_integer(A) ->
  481. {Md, Formatted} = format_reason_md(Reason),
  482. {_, Formatted2} = format_mfa_md({M, F, A}),
  483. {Md, [Formatted, " in ", Formatted2]};
  484. format_reason_md({Reason, [{M, F, A, Props}|_]}) when is_atom(M), is_atom(F), is_integer(A), is_list(Props) ->
  485. %% line numbers
  486. {Md, Formatted} = format_reason_md(Reason),
  487. {_, Formatted2} = format_mfa_md({M, F, A, Props}),
  488. {Md, [Formatted, " in ", Formatted2]};
  489. format_reason_md(Reason) ->
  490. {Str, _} = lager_trunc_io:print(Reason, 500),
  491. {[], Str}.
  492. %% backwards compatability shim
  493. format_mfa(MFA) ->
  494. element(2, format_mfa_md(MFA)).
  495. -spec format_mfa_md(any()) -> {[{atom(), any()}], list()}.
  496. format_mfa_md({M, F, A}) when is_list(A) ->
  497. {FmtStr, Args} = format_args(A, [], []),
  498. {[{module, M}, {function, F}], io_lib:format("~w:~w("++FmtStr++")", [M, F | Args])};
  499. format_mfa_md({M, F, A}) when is_integer(A) ->
  500. {[{module, M}, {function, F}], io_lib:format("~w:~w/~w", [M, F, A])};
  501. format_mfa_md({M, F, A, Props}) when is_list(Props) ->
  502. case get_value(line, Props) of
  503. undefined ->
  504. format_mfa_md({M, F, A});
  505. Line ->
  506. {Md, Formatted} = format_mfa_md({M, F, A}),
  507. {[{line, Line} | Md], [Formatted, io_lib:format(" line ~w", [Line])]}
  508. end;
  509. format_mfa_md([{M, F, A}| _]) ->
  510. %% this kind of weird stacktrace can be generated by a uncaught throw in a gen_server
  511. format_mfa_md({M, F, A});
  512. format_mfa_md([{M, F, A, Props}| _]) when is_list(Props) ->
  513. %% this kind of weird stacktrace can be generated by a uncaught throw in a gen_server
  514. %% TODO we might not always want to print the first MFA we see here, often it is more helpful
  515. %% to print a lower one, but it is hard to programatically decide.
  516. format_mfa_md({M, F, A, Props});
  517. format_mfa_md(Other) ->
  518. {[], io_lib:format("~w", [Other])}.
  519. format_args([], FmtAcc, ArgsAcc) ->
  520. {string:join(lists:reverse(FmtAcc), ", "), lists:reverse(ArgsAcc)};
  521. format_args([H|T], FmtAcc, ArgsAcc) ->
  522. {Str, _} = lager_trunc_io:print(H, 100),
  523. format_args(T, ["~s"|FmtAcc], [Str|ArgsAcc]).
  524. print_silly_list(L) when is_list(L) ->
  525. case lager_stdlib:string_p(L) of
  526. true ->
  527. lager_trunc_io:format("~s", [L], ?DEFAULT_TRUNCATION);
  528. _ ->
  529. print_silly_list(L, [], [])
  530. end;
  531. print_silly_list(L) ->
  532. {Str, _} = lager_trunc_io:print(L, ?DEFAULT_TRUNCATION),
  533. Str.
  534. print_silly_list([], Fmt, Acc) ->
  535. lager_trunc_io:format(string:join(lists:reverse(Fmt), ", "),
  536. lists:reverse(Acc), ?DEFAULT_TRUNCATION);
  537. print_silly_list([{K,V}|T], Fmt, Acc) ->
  538. print_silly_list(T, ["~p: ~p" | Fmt], [V, K | Acc]);
  539. print_silly_list([H|T], Fmt, Acc) ->
  540. print_silly_list(T, ["~p" | Fmt], [H | Acc]).
  541. print_val(Val) ->
  542. {Str, _} = lager_trunc_io:print(Val, 500),
  543. Str.
  544. %% @doc Faster than proplists, but with the same API as long as you don't need to
  545. %% handle bare atom keys
  546. get_value(Key, Value) ->
  547. get_value(Key, Value, undefined).
  548. get_value(Key, List, Default) ->
  549. case lists:keyfind(Key, 1, List) of
  550. false -> Default;
  551. {Key, Value} -> Value
  552. end.
  553. supervisor_name({local, Name}) -> Name;
  554. supervisor_name(Name) -> Name.
  555. -ifdef(TEST).
  556. -include_lib("eunit/include/eunit.hrl").
  557. no_silent_hwm_drops_test_() ->
  558. {timeout, 10000,
  559. [
  560. fun() ->
  561. error_logger:tty(false),
  562. application:load(lager),
  563. application:set_env(lager, handlers, [{lager_test_backend, warning}]),
  564. application:set_env(lager, error_logger_redirect, true),
  565. application:set_env(lager, error_logger_hwm, 5),
  566. application:set_env(lager, error_logger_flush_queue, false),
  567. application:unset_env(lager, crash_log),
  568. lager:start(),
  569. try
  570. {_, _, MS} = os:timestamp(),
  571. timer:sleep((1000000 - MS) div 1000 + 1),
  572. %start close to the beginning of a new second
  573. [error_logger:error_msg("Foo ~p~n", [K]) || K <- lists:seq(1, 15)],
  574. timer:sleep(1000),
  575. lager_handler_watcher:pop_until("lager_error_logger_h dropped 10 messages in the last second that exceeded the limit of 5 messages/sec",
  576. fun lists:flatten/1)
  577. after
  578. application:stop(lager),
  579. application:stop(goldrush),
  580. error_logger:tty(true)
  581. end
  582. end
  583. ]
  584. }.
  585. -endif.