error_logger_acc.erl 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. %% @doc A error_logger report handler that be used to capture expected errors in
  2. %% tests. The current error report handlers are disabled during the execution
  3. %% of a function. Afterwards, they are restored and the errors that occered are
  4. %% returned along with the return value of the fun.
  5. %%
  6. %% This module was created before OTP 21 when logger didn't exist. Logger
  7. %% support has been added later, but the log entries are still returned in the
  8. %% old error_logger format. Logger events which are not backwards compatible
  9. %% with error_logger are silently ignored, which is OK as long as only OTP
  10. %% proc_lib based processes are crashing and explicit logging is done via
  11. %% error_logger.
  12. %%
  13. %% TODO: Use logger by default and use error_logger only for old OTP releases.
  14. -module(error_logger_acc).
  15. -include("exception.hrl").
  16. %% Public API
  17. -export([capture/1]).
  18. -behaviour(gen_event).
  19. -export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2,
  20. code_change/3]).
  21. %% @doc Executes `Fun' and captures all logged errors returns as well as
  22. %% uncaught errors in `Fun'.
  23. -spec capture(fun (() -> ResultOfFun)) ->
  24. {ok, ResultOfFun, AccumulatedErrors} |
  25. {throw | error | exit, Reason, Trace, AccumulatedErrors}
  26. when ResultOfFun :: term(),
  27. Reason :: term(),
  28. Trace :: list(),
  29. AccumulatedErrors :: [{error|warning_msg|info_msg, string()} |
  30. {error_report|warning_report|info_report, term()}].
  31. capture(Fun) when is_function(Fun, 0) ->
  32. %% From OTP 21.0, error_logger is no longer started by default, but is
  33. %% automatically started when an event handler is added with
  34. %% error_logger:add_report_handler/1,2. The error_logger module is then
  35. %% also added as a handler to the new logger.
  36. error_logger:add_report_handler(?MODULE),
  37. OldHandlers = gen_event:which_handlers(error_logger) -- [?MODULE],
  38. lists:foreach(fun error_logger:delete_report_handler/1, OldHandlers),
  39. DefaultLoggerHandler = remove_default_logger_handler(),
  40. try Fun() of
  41. Result ->
  42. lists:foreach(fun error_logger:add_report_handler/1, OldHandlers),
  43. restore_default_logger_handler(DefaultLoggerHandler),
  44. {ok, Result, error_logger:delete_report_handler(?MODULE)}
  45. catch
  46. ?EXCEPTION(Class, Error, Stacktrace) ->
  47. lists:foreach(fun error_logger:add_report_handler/1, OldHandlers),
  48. AccumulatedErrors = error_logger:delete_report_handler(?MODULE),
  49. restore_default_logger_handler(DefaultLoggerHandler),
  50. {Class, Error, ?GET_STACK(Stacktrace), AccumulatedErrors}
  51. end.
  52. %% --- gen_event callbacks ---
  53. init([]) ->
  54. {ok, []}.
  55. handle_event({ErrorType, _Gleader, {_Pid, Format, Data}}, State) ->
  56. ShortError = if
  57. ErrorType == error; ErrorType == warning_msg; ErrorType == info_msg ->
  58. {ErrorType, lists:flatten(io_lib:format(Format, Data))};
  59. true ->
  60. {ErrorType, {Format, Data}}
  61. end,
  62. {ok, [ShortError | State]};
  63. handle_event(_OtherEvent, State) ->
  64. {ok, State}.
  65. handle_call(_Call, State) ->
  66. {ok, ignored, State}.
  67. handle_info(_Info, State) ->
  68. {ok, State}.
  69. terminate([], State) ->
  70. %% error_logger:delete_report_handler/1 called
  71. lists:reverse(State);
  72. terminate(_Arg, _State) ->
  73. %% terminating for some other reason.
  74. error_logger:info_msg("Accumulating error handler shutting down"),
  75. ok.
  76. code_change(_OldVsn, State, _Extra) ->
  77. {ok, State}.
  78. -ifdef(OTP_RELEASE).
  79. remove_default_logger_handler() ->
  80. {ok, Config} = logger:get_handler_config(default),
  81. ok = logger:remove_handler(default),
  82. Config.
  83. restore_default_logger_handler(#{id := Id, module := Module} = Config) ->
  84. logger:add_handler(Id, Module, Config).
  85. -else.
  86. remove_default_logger_handler() -> none.
  87. restore_default_logger_handler(none) -> ok.
  88. -endif.
  89. -ifdef(TEST).
  90. -include_lib("eunit/include/eunit.hrl").
  91. capture_success_test() ->
  92. Result = ?MODULE:capture(fun () ->
  93. error_logger:info_msg("Hello ~p", [world]),
  94. error_logger:info_msg("Hello ~p", [again]),
  95. foo
  96. end),
  97. ?assertEqual({ok, foo, [{info_msg, "Hello world"}, {info_msg, "Hello again"}]}, Result).
  98. capture_failure_test() ->
  99. Result = ?MODULE:capture(fun () ->
  100. error_logger:info_msg("Hello ~p", [world]),
  101. throw(foo)
  102. end),
  103. ?assertMatch({throw, foo, _Trace, [{info_msg, "Hello world"}]}, Result).
  104. -endif.