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.

593 lines
26 KiB

14 years ago
14 years ago
  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, _, NewShaper} ->
  105. {ok, State#state{shaper=NewShaper}}
  106. end.
  107. handle_info({shaper_expired, ?MODULE}, #state{sink=Sink, shaper=Shaper} = State) ->
  108. ?LOGFMT(Sink, warning, self(),
  109. "lager_error_logger_h dropped ~p messages in the last second that exceeded the limit of ~p messages/sec",
  110. [Shaper#lager_shaper.dropped, Shaper#lager_shaper.hwm]),
  111. {ok, State#state{shaper=Shaper#lager_shaper{dropped=0, mps=1, lasttime=os:timestamp()}}};
  112. handle_info(_Info, State) ->
  113. {ok, State}.
  114. terminate(_Reason, _State) ->
  115. ok.
  116. code_change(_OldVsn, {state, Shaper, GLStrategy}, _Extra) ->
  117. Raw = application:get_env(lager, error_logger_format_raw, false),
  118. {ok, #state{
  119. sink=configured_sink(),
  120. shaper=Shaper,
  121. groupleader_strategy=GLStrategy,
  122. raw=Raw
  123. }};
  124. code_change(_OldVsn, {state, Sink, Shaper, GLS}, _Extra) ->
  125. Raw = application:get_env(lager, error_logger_format_raw, false),
  126. {ok, #state{sink=Sink, shaper=Shaper, groupleader_strategy=GLS, raw=Raw}};
  127. code_change(_OldVsn, State, _Extra) ->
  128. {ok, State}.
  129. %% internal functions
  130. configured_sink() ->
  131. case proplists:get_value(?ERROR_LOGGER_SINK, application:get_env(lager, extra_sinks, [])) of
  132. undefined -> ?DEFAULT_SINK;
  133. _ -> ?ERROR_LOGGER_SINK
  134. end.
  135. eval_gl(Event, #state{groupleader_strategy=GlStrategy0}=State) when is_pid(element(2, Event)) ->
  136. case element(2, Event) of
  137. GL when node(GL) =/= node(), GlStrategy0 =:= ignore ->
  138. gen_event:notify({error_logger, node(GL)}, Event),
  139. {ok, State};
  140. GL when node(GL) =/= node(), GlStrategy0 =:= mirror ->
  141. gen_event:notify({error_logger, node(GL)}, Event),
  142. log_event(Event, State);
  143. _ ->
  144. log_event(Event, State)
  145. end;
  146. eval_gl(Event, State) ->
  147. log_event(Event, State).
  148. log_event(Event, #state{sink=Sink} = State) ->
  149. case Event of
  150. {error, _GL, {Pid, Fmt, Args}} ->
  151. FormatRaw = State#state.raw,
  152. case {FormatRaw, Fmt} of
  153. {false, "** Generic server "++_} ->
  154. %% gen_server terminate
  155. {Reason, Name} = case Args of
  156. [N, _Msg, _State, R] ->
  157. {R, N};
  158. [N, _Msg, _State, R, _Client] ->
  159. %% OTP 20 crash reports where the client pid is dead don't include the stacktrace
  160. {R, N};
  161. [N, _Msg, _State, R, _Client, _Stacktrace] ->
  162. %% OTP 20 crash reports contain the pid of the client and stacktrace
  163. %% TODO do something with them
  164. {R, N}
  165. end,
  166. ?CRASH_LOG(Event),
  167. {Md, Formatted} = format_reason_md(Reason),
  168. ?LOGFMT(Sink, error, [{pid, Pid}, {name, Name} | Md], "gen_server ~w terminated with reason: ~s",
  169. [Name, Formatted]);
  170. {false, "** State machine "++_} ->
  171. %% Check if the terminated process is gen_fsm or gen_statem
  172. %% since they generate the same exit message
  173. {Type, Name, StateName, Reason} = case Args of
  174. [TName, _Msg, TStateName, _StateData, TReason] ->
  175. {gen_fsm, TName, TStateName, TReason};
  176. [TName, _Msg, {TStateName, _StateData}, _ExitType, TReason, _FsmType, Stacktrace] ->
  177. {gen_statem, TName, TStateName, {TReason, Stacktrace}}
  178. end,
  179. {Md, Formatted} = format_reason_md(Reason),
  180. ?CRASH_LOG(Event),
  181. ?LOGFMT(Sink, error, [{pid, Pid}, {name, Name} | Md], "~s ~w in state ~w terminated with reason: ~s",
  182. [Type, Name, StateName, Formatted]);
  183. {false, "** gen_event handler"++_} ->
  184. %% gen_event handler terminate
  185. [ID, Name, _Msg, _State, Reason] = Args,
  186. {Md, Formatted} = format_reason_md(Reason),
  187. ?CRASH_LOG(Event),
  188. ?LOGFMT(Sink, error, [{pid, Pid}, {name, Name} | Md], "gen_event ~w installed in ~w terminated with reason: ~s",
  189. [ID, Name, Formatted]);
  190. {false, "** Cowboy handler"++_} ->
  191. %% Cowboy HTTP server error
  192. ?CRASH_LOG(Event),
  193. case Args of
  194. [Module, Function, Arity, _Request, _State] ->
  195. %% we only get the 5-element list when its a non-exported function
  196. ?LOGFMT(Sink, error, Pid,
  197. "Cowboy handler ~p terminated with reason: call to undefined function ~p:~p/~p",
  198. [Module, Module, Function, Arity]);
  199. [Module, Function, Arity, _Class, Reason | Tail] ->
  200. %% any other cowboy error_format list *always* ends with the stacktrace
  201. StackTrace = lists:last(Tail),
  202. {Md, Formatted} = format_reason_md({Reason, StackTrace}),
  203. ?LOGFMT(Sink, error, [{pid, Pid} | Md],
  204. "Cowboy handler ~p terminated in ~p:~p/~p with reason: ~s",
  205. [Module, Module, Function, Arity, Formatted])
  206. end;
  207. {false, "Ranch listener "++_} ->
  208. %% Ranch errors
  209. ?CRASH_LOG(Event),
  210. case Args of
  211. [Ref, _Protocol, Worker, {[{reason, Reason}, {mfa, {Module, Function, Arity}}, {stacktrace, StackTrace} | _], _}] ->
  212. {Md, Formatted} = format_reason_md({Reason, StackTrace}),
  213. ?LOGFMT(Sink, error, [{pid, Worker} | Md],
  214. "Ranch listener ~p terminated in ~p:~p/~p with reason: ~s",
  215. [Ref, Module, Function, Arity, Formatted]);
  216. [Ref, _Protocol, Worker, Reason] ->
  217. {Md, Formatted} = format_reason_md(Reason),
  218. ?LOGFMT(Sink, error, [{pid, Worker} | Md],
  219. "Ranch listener ~p terminated with reason: ~s",
  220. [Ref, Formatted]);
  221. [Ref, Protocol, Ret] ->
  222. %% ranch_conns_sup.erl module line 119-123 has three parameters error msg, log it.
  223. {Md, Formatted} = format_reason_md(Ret),
  224. ?LOGFMT(Sink, error, [{pid, Protocol} | Md],
  225. "Ranch listener ~p terminated with result:~s",
  226. [Ref, Formatted])
  227. end;
  228. {false, "webmachine error"++_} ->
  229. %% Webmachine HTTP server error
  230. ?CRASH_LOG(Event),
  231. [Path, Error] = Args,
  232. %% webmachine likes to mangle the stack, for some reason
  233. StackTrace = case Error of
  234. {error, {error, Reason, Stack}} ->
  235. {Reason, Stack};
  236. _ ->
  237. Error
  238. end,
  239. {Md, Formatted} = format_reason_md(StackTrace),
  240. ?LOGFMT(Sink, error, [{pid, Pid} | Md], "Webmachine error at path ~p : ~s", [Path, Formatted]);
  241. _ ->
  242. ?CRASH_LOG(Event),
  243. ?LOGFMT(Sink, error, Pid, Fmt, Args)
  244. end;
  245. {error_report, _GL, {Pid, std_error, D}} ->
  246. ?CRASH_LOG(Event),
  247. ?LOGMSG(Sink, error, Pid, print_silly_list(D));
  248. {error_report, _GL, {Pid, supervisor_report, D}} ->
  249. ?CRASH_LOG(Event),
  250. case lists:sort(D) of
  251. [{errorContext, Ctx}, {offender, Off}, {reason, Reason}, {supervisor, Name}] ->
  252. Offender = format_offender(Off),
  253. {Md, Formatted} = format_reason_md(Reason),
  254. ?LOGFMT(Sink, error, [{pid, Pid} | Md],
  255. "Supervisor ~w had child ~s exit with reason ~s in context ~w",
  256. [supervisor_name(Name), Offender, Formatted, Ctx]);
  257. _ ->
  258. ?LOGMSG(Sink, error, Pid, "SUPERVISOR REPORT " ++ print_silly_list(D))
  259. end;
  260. {error_report, _GL, {Pid, crash_report, [Self, Neighbours]}} ->
  261. ?CRASH_LOG(Event),
  262. {Md, Formatted} = format_crash_report(Self, Neighbours),
  263. ?LOGMSG(Sink, error, [{pid, Pid} | Md], "CRASH REPORT " ++ Formatted);
  264. {warning_msg, _GL, {Pid, Fmt, Args}} ->
  265. ?LOGFMT(Sink, warning, Pid, Fmt, Args);
  266. {warning_report, _GL, {Pid, std_warning, Report}} ->
  267. ?LOGMSG(Sink, warning, Pid, print_silly_list(Report));
  268. {info_msg, _GL, {Pid, Fmt, Args}} ->
  269. ?LOGFMT(Sink, info, Pid, Fmt, Args);
  270. {info_report, _GL, {Pid, std_info, D}} when is_list(D) ->
  271. Details = lists:sort(D),
  272. case Details of
  273. [{application, App}, {exited, Reason}, {type, _Type}] ->
  274. case application:get_env(lager, suppress_application_start_stop) of
  275. {ok, true} when Reason == stopped ->
  276. ok;
  277. _ ->
  278. {Md, Formatted} = format_reason_md(Reason),
  279. ?LOGFMT(Sink, info, [{pid, Pid} | Md], "Application ~w exited with reason: ~s",
  280. [App, Formatted])
  281. end;
  282. _ ->
  283. ?LOGMSG(Sink, info, Pid, print_silly_list(D))
  284. end;
  285. {info_report, _GL, {Pid, std_info, D}} ->
  286. ?LOGFMT(Sink, info, Pid, "~w", [D]);
  287. {info_report, _GL, {P, progress, D}} ->
  288. Details = lists:sort(D),
  289. case Details of
  290. [{application, App}, {started_at, Node}] ->
  291. case application:get_env(lager, suppress_application_start_stop) of
  292. {ok, true} ->
  293. ok;
  294. _ ->
  295. ?LOGFMT(Sink, info, P, "Application ~w started on node ~w",
  296. [App, Node])
  297. end;
  298. [{started, Started}, {supervisor, Name}] ->
  299. case application:get_env(lager, suppress_supervisor_start_stop, false) of
  300. true ->
  301. ok;
  302. _ ->
  303. MFA = format_mfa(get_value(mfargs, Started)),
  304. Pid = get_value(pid, Started),
  305. ?LOGFMT(Sink, debug, P, "Supervisor ~w started ~s at pid ~w",
  306. [supervisor_name(Name), MFA, Pid])
  307. end;
  308. _ ->
  309. ?LOGMSG(Sink, info, P, "PROGRESS REPORT " ++ print_silly_list(D))
  310. end;
  311. _ ->
  312. ?LOGFMT(Sink, warning, self(), "Unexpected error_logger event ~w", [Event])
  313. end,
  314. {ok, State}.
  315. format_crash_report(Report, Neighbours) ->
  316. Name = case get_value(registered_name, Report, []) of
  317. [] ->
  318. %% process_info(Pid, registered_name) returns [] for unregistered processes
  319. get_value(pid, Report);
  320. Atom -> Atom
  321. end,
  322. Md0 = case get_value(dictionary, Report, []) of
  323. [] ->
  324. %% process_info(Pid, registered_name) returns [] for unregistered processes
  325. [];
  326. Dict ->
  327. %% pull the lager metadata out of the process dictionary, if we can
  328. get_value('_lager_metadata', Dict, [])
  329. end,
  330. {Class, Reason, Trace} = get_value(error_info, Report),
  331. {Md, ReasonStr} = format_reason_md({Reason, Trace}),
  332. Type = case Class of
  333. exit -> "exited";
  334. _ -> "crashed"
  335. end,
  336. {Md0 ++ Md, io_lib:format("Process ~w with ~w neighbours ~s with reason: ~s",
  337. [Name, length(Neighbours), Type, ReasonStr])}.
  338. format_offender(Off) ->
  339. case get_value(mfargs, Off) of
  340. undefined ->
  341. %% supervisor_bridge
  342. io_lib:format("at module ~w at ~w",
  343. [get_value(mod, Off), get_value(pid, Off)]);
  344. MFArgs ->
  345. %% regular supervisor
  346. {_, MFA} = format_mfa_md(MFArgs),
  347. %% In 2014 the error report changed from `name' to
  348. %% `id', so try that first.
  349. Name = case get_value(id, Off) of
  350. undefined ->
  351. get_value(name, Off);
  352. Id ->
  353. Id
  354. end,
  355. io_lib:format("~p started with ~s at ~w",
  356. [Name, MFA, get_value(pid, Off)])
  357. end.
  358. %% backwards compatability shim
  359. format_reason(Reason) ->
  360. element(2, format_reason_md(Reason)).
  361. -spec format_reason_md(Stacktrace:: any()) -> {Metadata:: [{atom(), any()}], String :: list()}.
  362. format_reason_md({'function not exported', [{M, F, A},MFA|_]}) ->
  363. {Md, Formatted} = format_mfa_md(MFA),
  364. {_, Formatted2} = format_mfa_md({M, F, length(A)}),
  365. {[{reason, 'function not exported'} | Md],
  366. ["call to undefined function ", Formatted2,
  367. " from ", Formatted]};
  368. format_reason_md({'function not exported', [{M, F, A, _Props},MFA|_]}) ->
  369. %% R15 line numbers
  370. {Md, Formatted} = format_mfa_md(MFA),
  371. {_, Formatted2} = format_mfa_md({M, F, length(A)}),
  372. {[{reason, 'function not exported'} | Md],
  373. ["call to undefined function ", Formatted2,
  374. " from ", Formatted]};
  375. format_reason_md({undef, [MFA|_]}) ->
  376. {Md, Formatted} = format_mfa_md(MFA),
  377. {[{reason, undef} | Md],
  378. ["call to undefined function ", Formatted]};
  379. format_reason_md({bad_return, {_MFA, {'EXIT', Reason}}}) ->
  380. format_reason_md(Reason);
  381. format_reason_md({bad_return, {MFA, Val}}) ->
  382. {Md, Formatted} = format_mfa_md(MFA),
  383. {[{reason, bad_return} | Md],
  384. ["bad return value ", print_val(Val), " from ", Formatted]};
  385. format_reason_md({bad_return_value, Val}) ->
  386. {[{reason, bad_return}],
  387. ["bad return value: ", print_val(Val)]};
  388. format_reason_md({{bad_return_value, Val}, MFA}) ->
  389. {Md, Formatted} = format_mfa_md(MFA),
  390. {[{reason, bad_return_value} | Md],
  391. ["bad return value: ", print_val(Val), " in ", Formatted]};
  392. format_reason_md({{badrecord, Record}, [MFA|_]}) ->
  393. {Md, Formatted} = format_mfa_md(MFA),
  394. {[{reason, badrecord} | Md],
  395. ["bad record ", print_val(Record), " in ", Formatted]};
  396. format_reason_md({{case_clause, Val}, [MFA|_]}) ->
  397. {Md, Formatted} = format_mfa_md(MFA),
  398. {[{reason, case_clause} | Md],
  399. ["no case clause matching ", print_val(Val), " in ", Formatted]};
  400. format_reason_md({function_clause, [MFA|_]}) ->
  401. {Md, Formatted} = format_mfa_md(MFA),
  402. {[{reason, function_clause} | Md],
  403. ["no function clause matching ", Formatted]};
  404. format_reason_md({if_clause, [MFA|_]}) ->
  405. {Md, Formatted} = format_mfa_md(MFA),
  406. {[{reason, if_clause} | Md],
  407. ["no true branch found while evaluating if expression in ", Formatted]};
  408. format_reason_md({{try_clause, Val}, [MFA|_]}) ->
  409. {Md, Formatted} = format_mfa_md(MFA),
  410. {[{reason, try_clause} | Md],
  411. ["no try clause matching ", print_val(Val), " in ", Formatted]};
  412. format_reason_md({badarith, [MFA|_]}) ->
  413. {Md, Formatted} = format_mfa_md(MFA),
  414. {[{reason, badarith} | Md],
  415. ["bad arithmetic expression in ", Formatted]};
  416. format_reason_md({{badmatch, Val}, [MFA|_]}) ->
  417. {Md, Formatted} = format_mfa_md(MFA),
  418. {[{reason, badmatch} | Md],
  419. ["no match of right hand value ", print_val(Val), " in ", Formatted]};
  420. format_reason_md({emfile, _Trace}) ->
  421. {[{reason, emfile}],
  422. "maximum number of file descriptors exhausted, check ulimit -n"};
  423. format_reason_md({system_limit, [{M, F, _}|_] = Trace}) ->
  424. Limit = case {M, F} of
  425. {erlang, open_port} ->
  426. "maximum number of ports exceeded";
  427. {erlang, spawn} ->
  428. "maximum number of processes exceeded";
  429. {erlang, spawn_opt} ->
  430. "maximum number of processes exceeded";
  431. {erlang, list_to_atom} ->
  432. "tried to create an atom larger than 255, or maximum atom count exceeded";
  433. {ets, new} ->
  434. "maximum number of ETS tables exceeded";
  435. _ ->
  436. {Str, _} = lager_trunc_io:print(Trace, 500),
  437. Str
  438. end,
  439. {[{reason, system_limit}], ["system limit: ", Limit]};
  440. format_reason_md({badarg, [MFA,MFA2|_]}) ->
  441. case MFA of
  442. {_M, _F, A, _Props} when is_list(A) ->
  443. %% R15 line numbers
  444. {Md, Formatted} = format_mfa_md(MFA2),
  445. {_, Formatted2} = format_mfa_md(MFA),
  446. {[{reason, badarg} | Md],
  447. ["bad argument in call to ", Formatted2, " in ", Formatted]};
  448. {_M, _F, A} when is_list(A) ->
  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. _ ->
  454. %% seems to be generated by a bad call to a BIF
  455. {Md, Formatted} = format_mfa_md(MFA),
  456. {[{reason, badarg} | Md],
  457. ["bad argument in ", Formatted]}
  458. end;
  459. format_reason_md({{badarg, Stack}, _}) ->
  460. format_reason_md({badarg, Stack});
  461. format_reason_md({{badarity, {Fun, Args}}, [MFA|_]}) ->
  462. {arity, Arity} = lists:keyfind(arity, 1, erlang:fun_info(Fun)),
  463. {Md, Formatted} = format_mfa_md(MFA),
  464. {[{reason, badarity} | Md],
  465. [io_lib:format("fun called with wrong arity of ~w instead of ~w in ",
  466. [length(Args), Arity]), Formatted]};
  467. format_reason_md({noproc, MFA}) ->
  468. {Md, Formatted} = format_mfa_md(MFA),
  469. {[{reason, noproc} | Md],
  470. ["no such process or port in call to ", Formatted]};
  471. format_reason_md({{badfun, Term}, [MFA|_]}) ->
  472. {Md, Formatted} = format_mfa_md(MFA),
  473. {[{reason, badfun} | Md],
  474. ["bad function ", print_val(Term), " in ", Formatted]};
  475. format_reason_md({Reason, [{M, F, A}|_]}) when is_atom(M), is_atom(F), is_integer(A) ->
  476. {Md, Formatted} = format_reason_md(Reason),
  477. {_, Formatted2} = format_mfa_md({M, F, A}),
  478. {Md, [Formatted, " in ", Formatted2]};
  479. format_reason_md({Reason, [{M, F, A, Props}|_]}) when is_atom(M), is_atom(F), is_integer(A), is_list(Props) ->
  480. %% line numbers
  481. {Md, Formatted} = format_reason_md(Reason),
  482. {_, Formatted2} = format_mfa_md({M, F, A, Props}),
  483. {Md, [Formatted, " in ", Formatted2]};
  484. format_reason_md(Reason) ->
  485. {Str, _} = lager_trunc_io:print(Reason, 500),
  486. {[], Str}.
  487. %% backwards compatability shim
  488. format_mfa(MFA) ->
  489. element(2, format_mfa_md(MFA)).
  490. -spec format_mfa_md(any()) -> {[{atom(), any()}], list()}.
  491. format_mfa_md({M, F, A}) when is_list(A) ->
  492. {FmtStr, Args} = format_args(A, [], []),
  493. {[{module, M}, {function, F}], io_lib:format("~w:~w("++FmtStr++")", [M, F | Args])};
  494. format_mfa_md({M, F, A}) when is_integer(A) ->
  495. {[{module, M}, {function, F}], io_lib:format("~w:~w/~w", [M, F, A])};
  496. format_mfa_md({M, F, A, Props}) when is_list(Props) ->
  497. case get_value(line, Props) of
  498. undefined ->
  499. format_mfa_md({M, F, A});
  500. Line ->
  501. {Md, Formatted} = format_mfa_md({M, F, A}),
  502. {[{line, Line} | Md], [Formatted, io_lib:format(" line ~w", [Line])]}
  503. end;
  504. format_mfa_md([{M, F, A}| _]) ->
  505. %% this kind of weird stacktrace can be generated by a uncaught throw in a gen_server
  506. format_mfa_md({M, F, A});
  507. format_mfa_md([{M, F, A, Props}| _]) when is_list(Props) ->
  508. %% this kind of weird stacktrace can be generated by a uncaught throw in a gen_server
  509. %% TODO we might not always want to print the first MFA we see here, often it is more helpful
  510. %% to print a lower one, but it is hard to programatically decide.
  511. format_mfa_md({M, F, A, Props});
  512. format_mfa_md(Other) ->
  513. {[], io_lib:format("~w", [Other])}.
  514. format_args([], FmtAcc, ArgsAcc) ->
  515. {string:join(lists:reverse(FmtAcc), ", "), lists:reverse(ArgsAcc)};
  516. format_args([H|T], FmtAcc, ArgsAcc) ->
  517. {Str, _} = lager_trunc_io:print(H, 100),
  518. format_args(T, ["~s"|FmtAcc], [Str|ArgsAcc]).
  519. print_silly_list(L) when is_list(L) ->
  520. case lager_stdlib:string_p(L) of
  521. true ->
  522. lager_trunc_io:format("~s", [L], ?DEFAULT_TRUNCATION);
  523. _ ->
  524. print_silly_list(L, [], [])
  525. end;
  526. print_silly_list(L) ->
  527. {Str, _} = lager_trunc_io:print(L, ?DEFAULT_TRUNCATION),
  528. Str.
  529. print_silly_list([], Fmt, Acc) ->
  530. lager_trunc_io:format(string:join(lists:reverse(Fmt), ", "),
  531. lists:reverse(Acc), ?DEFAULT_TRUNCATION);
  532. print_silly_list([{K,V}|T], Fmt, Acc) ->
  533. print_silly_list(T, ["~p: ~p" | Fmt], [V, K | Acc]);
  534. print_silly_list([H|T], Fmt, Acc) ->
  535. print_silly_list(T, ["~p" | Fmt], [H | Acc]).
  536. print_val(Val) ->
  537. {Str, _} = lager_trunc_io:print(Val, 500),
  538. Str.
  539. %% @doc Faster than proplists, but with the same API as long as you don't need to
  540. %% handle bare atom keys
  541. get_value(Key, Value) ->
  542. get_value(Key, Value, undefined).
  543. get_value(Key, List, Default) ->
  544. case lists:keyfind(Key, 1, List) of
  545. false -> Default;
  546. {Key, Value} -> Value
  547. end.
  548. supervisor_name({local, Name}) -> Name;
  549. supervisor_name(Name) -> Name.