error_logger_acc.erl 3.0 KB

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