Reputation: 3089
i'm looking for a solution for error handling in erlang.
the main challenge is running code piece, in any error context.
for instance (if its possible) overriding the SASL logger (or any similar behaviour), that will let me execute code, when exceptions are raised, at any point of the system.
cheers
Upvotes: 1
Views: 378
Reputation: 3729
You can write your own callback module which error_logger
can use to perform logging. This is only what sasl does (it has several logger implementations, sasl_report_tty_h
, sasl_report_file_h
, and log_mf_hd
, see sasl (application)).
To write your own implementation, it's not too difficult, you just have to implement a gen_event callback module which supports the necessary events. You can look at the implementations that sasl provides obviously, or that lager or any other logging app provides, but here's a simple implementation:
-module(example_logger).
-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2]).
init(Args) ->
io:format("example_logger: init(~p)~n", [Args]),
{ok, state} .
handle_event(Event, State) ->
io:format("example_logger:handle_event(~p, ~p)~n", [Event, State]),
{ok, State} .
handle_info(_Request, State) ->
{ok, State} .
handle_call(_Request, _State) ->
{error, unimplemented} .
terminate(_Reason, State) ->
{ok, State} .
This is just an indiscriminating dump to standard output to get you started of course, you should look at the error_logger module and gen_event to gain a full understanding of the details of the events and how this works.
An example exception without using the above module:
1> spawn(fun() -> 1/0 end).
<0.35.0>
=ERROR REPORT==== 13-Dec-2015::14:20:39 ===
Error in process <0.35.0> with exit value: {badarith,[{erlang,'/',[1,0],[]}]}
Now, adding the callback module, you can see it receives the exception too, as well as the tty handler:
2> error_logger:add_report_handler(example_logger, args).
example_logger: init(args)
ok
3> spawn(fun() -> 1/0 end).
example_logger:handle_event({error,<0.26.0>,
{emulator,"~s~n",
["Error in process <0.38.0> with exit value: {badarith,[{erlang,'/',[1,0],[]}]}\n"]}}, state)
=ERROR REPORT==== 13-Dec-2015::14:20:56 ===
Error in process <0.38.0> with exit value: {badarith,[{erlang,'/',[1,0],[]}]}
<0.38.0>
You can turn off the standard tty handler, leaving only your callback module:
4> error_logger:tty(false).
ok
5> spawn(fun() -> 1/0 end).
<0.41.0>
example_logger:handle_event({error,<0.26.0>,
{emulator,"~s~n",
["Error in process <0.41.0> with exit value: {badarith,[{erlang,'/',[1,0],[]}]}\n"]}}, state)
6>
Your callback module won't just receive exceptions, let's start another app, and you will see it gets information reports:
6> application:start(inets).
example_logger:handle_event({info_report,<0.45.0>,
{<0.47.0>,progress,
[{supervisor,{local,inets_sup}},
{started,
[{pid,<0.48.0>},
{name,ftp_sup},
{mfargs,{ftp_sup,start_link,[]}},
{restart_type,permanent},
{shutdown,infinity},
{child_type,supervisor}]}]}}, state)
example_logger:handle_event({info_report,<0.45.0>,
{<0.50.0>,progress,
[{supervisor,{local,httpc_profile_sup}},
{started,
[{pid,<0.51.0>},
{name,httpc_manager},
{mfargs,
{httpc_manager,start_link,
[default,only_session_cookies,inets]}},
{restart_type,permanent},
{shutdown,4000},
{child_type,worker}]}]}}, state)
example_logger:handle_event({info_report,<0.45.0>,
{<0.49.0>,progress,
[{supervisor,{local,httpc_sup}},
{started,
[{pid,<0.50.0>},
{name,httpc_profile_sup},
{mfargs,
{httpc_profile_sup,start_link,
[[{httpc,
{default,only_session_cookies}}]]}},
{restart_type,permanent},
{shutdown,infinity},
{child_type,supervisor}]}]}}, state)
example_logger:handle_event({info_report,<0.45.0>,
{<0.49.0>,progress,
[{supervisor,{local,httpc_sup}},
{started,
[{pid,<0.52.0>},
{name,httpc_handler_sup},
{mfargs,{httpc_handler_sup,start_link,[]}},
{restart_type,permanent},
{shutdown,infinity},
{child_type,supervisor}]}]}}, state)
example_logger:handle_event({info_report,<0.45.0>,
{<0.47.0>,progress,
[{supervisor,{local,inets_sup}},
{started,
[{pid,<0.49.0>},
{name,httpc_sup},
{mfargs,
{httpc_sup,start_link,
[[{httpc,
{default,only_session_cookies}}]]}},
{restart_type,permanent},
{shutdown,infinity},
{child_type,supervisor}]}]}}, state)
example_logger:handle_event({info_report,<0.45.0>,
{<0.47.0>,progress,
[{supervisor,{local,inets_sup}},
{started,
[{pid,<0.53.0>},
{name,httpd_sup},
{mfargs,{httpd_sup,start_link,[[]]}},
{restart_type,permanent},
{shutdown,infinity},
{child_type,supervisor}]}]}}, state)
example_logger:handle_event({info_report,<0.45.0>,
{<0.47.0>,progress,
[{supervisor,{local,inets_sup}},
{started,
[{pid,<0.54.0>},
{name,tftp_sup},
{mfargs,{tftp_sup,start_link,[[]]}},
{restart_type,permanent},
{shutdown,infinity},
{child_type,supervisor}]}]}}, state)
example_logger:handle_event({info_report,<0.25.0>,
{<0.7.0>,progress,
[{application,inets},
{started_at,nonode@nohost}]}}, state)
You can ignore those if you like of course, though you should probably be careful to make sure SOME logger does deal with them!
You can delete your handler, and without the tty handler, there will be no apparent output:
7> error_logger:delete_report_handler(example_logger).
{ok,state}
8>
8> spawn(fun() -> 1/0 end).
<0.38.0>
The doesn't mean there are no handlers of course, it means there are none writing those events to the standard output / tty.
Upvotes: 2