error_logger_acc.erl 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  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. -module(error_logger_acc).
  6. -include("exception.hrl").
  7. %% Public API
  8. -export([capture/1]).
  9. -behaviour(gen_event).
  10. -export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2,
  11. code_change/3]).
  12. %% @doc Executes `Fun' and captures all logged errors returns as well as
  13. %% uncaught errors in `Fun'.
  14. -spec capture(fun (() -> ResultOfFun)) ->
  15. {ok, ResultOfFun, AccumulatedErrors} |
  16. {throw | error | exit, Reason, Trace, AccumulatedErrors}
  17. when ResultOfFun :: term(),
  18. Reason :: term(),
  19. Trace :: list(),
  20. AccumulatedErrors :: [{error|warning_msg|info_msg, string()} |
  21. {error_report|warning_report|info_report, term()}].
  22. capture(Fun) when is_function(Fun, 0) ->
  23. start_error_logger(),
  24. OldHandlers = gen_event:which_handlers(error_logger),
  25. error_logger:add_report_handler(?MODULE),
  26. lists:foreach(fun error_logger:delete_report_handler/1, OldHandlers),
  27. try Fun() of
  28. Result ->
  29. lists:foreach(fun error_logger:add_report_handler/1, OldHandlers),
  30. {ok, Result, error_logger:delete_report_handler(?MODULE)}
  31. catch
  32. ?EXCEPTION(Class, Error, Stacktrace) ->
  33. lists:foreach(fun error_logger:add_report_handler/1, OldHandlers),
  34. AccumulatedErrors = error_logger:delete_report_handler(?MODULE),
  35. {Class, Error, ?GET_STACK(Stacktrace), AccumulatedErrors}
  36. end.
  37. %% --- gen_event callbacks ---
  38. init([]) ->
  39. {ok, []}.
  40. handle_event({ErrorType, _Gleader, {_Pid, Format, Data}}, State) ->
  41. ShortError = if
  42. ErrorType == error; ErrorType == warning_msg; ErrorType == info_msg ->
  43. {ErrorType, lists:flatten(io_lib:format(Format, Data))};
  44. true ->
  45. {ErrorType, {Format, Data}}
  46. end,
  47. {ok, [ShortError | State]};
  48. handle_event(_OtherEvent, State) ->
  49. {ok, State}.
  50. handle_call(_Call, State) ->
  51. {ok, ignored, State}.
  52. handle_info(_Info, State) ->
  53. {ok, State}.
  54. terminate([], State) ->
  55. %% error_logger:delete_report_handler/1 called
  56. lists:reverse(State);
  57. terminate(_Arg, _State) ->
  58. %% terminating for some other reason.
  59. error_logger:info_msg("Accumulating error handler shutting down"),
  60. ok.
  61. code_change(_OldVsn, State, _Extra) ->
  62. {ok, State}.
  63. %% error logger is no longer started since erlang 21, start it explicitly
  64. -ifdef(OTP_RELEASE).
  65. start_error_logger() ->
  66. error_logger:start(),
  67. logger:add_handler(error_logger,error_logger,#{level=>info,filter_default=>log}).
  68. -else.
  69. start_error_logger() ->
  70. ok.
  71. -endif.
  72. -ifdef(TEST).
  73. -include_lib("eunit/include/eunit.hrl").
  74. capture_success_test() ->
  75. Result = ?MODULE:capture(fun () ->
  76. error_logger:info_msg("Hello ~p", [world]),
  77. error_logger:info_msg("Hello ~p", [again]),
  78. foo
  79. end),
  80. ?assertEqual({ok, foo, [{info_msg, "Hello world"}, {info_msg, "Hello again"}]}, Result).
  81. capture_failure_test() ->
  82. Result = ?MODULE:capture(fun () ->
  83. error_logger:info_msg("Hello ~p", [world]),
  84. throw(foo)
  85. end),
  86. ?assertMatch({throw, foo, _Trace, [{info_msg, "Hello world"}]}, Result).
  87. -endif.