aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/src/error_logger.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel/src/error_logger.erl')
-rw-r--r--lib/kernel/src/error_logger.erl387
1 files changed, 387 insertions, 0 deletions
diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl
new file mode 100644
index 0000000000..cafdc52e84
--- /dev/null
+++ b/lib/kernel/src/error_logger.erl
@@ -0,0 +1,387 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(error_logger).
+
+-export([start/0,start_link/0,format/2,error_msg/1,error_msg/2,error_report/1,
+ error_report/2,info_report/1,info_report/2,warning_report/1,
+ warning_report/2,error_info/1,
+ info_msg/1,info_msg/2,warning_msg/1,warning_msg/2,
+ logfile/1,tty/1,swap_handler/1,
+ add_report_handler/1,add_report_handler/2,
+ delete_report_handler/1]).
+
+-export([init/1,
+ handle_event/2, handle_call/2, handle_info/2,
+ terminate/2]).
+
+-define(buffer_size, 10).
+
+%%-----------------------------------------------------------------
+%% Types used in this file
+%%-----------------------------------------------------------------
+
+-type msg_tag() :: 'error' | 'error_report'
+ | 'info' | 'info_msg' | 'info_report'
+ | 'warning_msg' | 'warning_report'.
+
+-type state() :: {non_neg_integer(), non_neg_integer(), [term()]}.
+
+%%-----------------------------------------------------------------
+
+-spec start() -> {'ok', pid()} | {'error', any()}.
+
+start() ->
+ case gen_event:start({local, error_logger}) of
+ {ok, Pid} ->
+ simple_logger(?buffer_size),
+ {ok, Pid};
+ Error -> Error
+ end.
+
+-spec start_link() -> {'ok', pid()} | {'error', any()}.
+
+start_link() ->
+ case gen_event:start_link({local, error_logger}) of
+ {ok, Pid} ->
+ simple_logger(?buffer_size),
+ {ok, Pid};
+ Error -> Error
+ end.
+
+%%-----------------------------------------------------------------
+%% These two simple old functions generate events tagged 'error'
+%% Used for simple messages; error or information.
+%%-----------------------------------------------------------------
+
+-spec error_msg(Format :: string()) -> 'ok'.
+
+error_msg(Format) ->
+ error_msg(Format,[]).
+
+-spec error_msg(Format :: string(), Args :: list()) -> 'ok'.
+
+error_msg(Format, Args) ->
+ notify({error, group_leader(), {self(), Format, Args}}).
+
+-spec format(Format :: string(), Args :: list()) -> 'ok'.
+
+format(Format, Args) ->
+ notify({error, group_leader(), {self(), Format, Args}}).
+
+%%-----------------------------------------------------------------
+%% This functions should be used for error reports. Events
+%% are tagged 'error_report'.
+%% The 'std_error' error_report type can always be used.
+%%-----------------------------------------------------------------
+
+-spec error_report(Report :: any()) -> 'ok'.
+
+error_report(Report) ->
+ error_report(std_error, Report).
+
+-spec error_report(Type :: any(), Report :: any()) -> 'ok'.
+
+error_report(Type, Report) ->
+ notify({error_report, group_leader(), {self(), Type, Report}}).
+
+%%-----------------------------------------------------------------
+%% This function should be used for warning reports.
+%% These might be mapped to error reports or info reports,
+%% depending on emulator flags. Events that ore not mapped
+%% are tagged 'info_report'.
+%% The 'std_warning' info_report type can always be used and is
+%% mapped to std_info or std_error accordingly.
+%%-----------------------------------------------------------------
+
+-spec warning_report(Report :: any()) -> 'ok'.
+
+warning_report(Report) ->
+ warning_report(std_warning, Report).
+
+-spec warning_report(Type :: any(), Report :: any()) -> 'ok'.
+
+warning_report(Type, Report) ->
+ {Tag, NType} = case error_logger:warning_map() of
+ info ->
+ if
+ Type =:= std_warning ->
+ {info_report, std_info};
+ true ->
+ {info_report, Type}
+ end;
+ warning ->
+ {warning_report, Type};
+ error ->
+ if
+ Type =:= std_warning ->
+ {error_report, std_error};
+ true ->
+ {error_report, Type}
+ end
+ end,
+ notify({Tag, group_leader(), {self(), NType, Report}}).
+
+%%-----------------------------------------------------------------
+%% This function provides similar functions as error_msg for
+%% warning messages, like warning report it might get mapped to
+%% other types of reports.
+%%-----------------------------------------------------------------
+
+-spec warning_msg(Format :: string()) -> 'ok'.
+
+warning_msg(Format) ->
+ warning_msg(Format,[]).
+
+-spec warning_msg(Format :: string(), Args :: list()) -> 'ok'.
+
+warning_msg(Format, Args) ->
+ Tag = case error_logger:warning_map() of
+ warning ->
+ warning_msg;
+ info ->
+ info_msg;
+ error ->
+ error
+ end,
+ notify({Tag, group_leader(), {self(), Format, Args}}).
+
+%%-----------------------------------------------------------------
+%% This function should be used for information reports. Events
+%% are tagged 'info_report'.
+%% The 'std_info' info_report type can always be used.
+%%-----------------------------------------------------------------
+
+-spec info_report(Report :: any()) -> 'ok'.
+
+info_report(Report) ->
+ info_report(std_info, Report).
+
+-spec info_report(Type :: any(), Report :: any()) -> 'ok'.
+
+info_report(Type, Report) ->
+ notify({info_report, group_leader(), {self(), Type, Report}}).
+
+%%-----------------------------------------------------------------
+%% This function provides similar functions as error_msg for
+%% information messages.
+%%-----------------------------------------------------------------
+
+-spec info_msg(Format :: string()) -> 'ok'.
+
+info_msg(Format) ->
+ info_msg(Format,[]).
+
+-spec info_msg(Format :: string(), Args :: list()) -> 'ok'.
+
+info_msg(Format, Args) ->
+ notify({info_msg, group_leader(), {self(), Format, Args}}).
+
+%%-----------------------------------------------------------------
+%% Used by the init process. Events are tagged 'info'.
+%%-----------------------------------------------------------------
+
+-spec error_info(Error :: any()) -> 'ok'.
+
+error_info(Error) ->
+ notify({info, group_leader(), {self(), Error, []}}).
+
+-spec notify({msg_tag(), pid(), {pid(), any(), any()}}) -> 'ok'.
+
+notify(Msg) ->
+ gen_event:notify(error_logger, Msg).
+
+-type swap_handler_type() :: 'false' | 'silent' | 'tty' | {'logfile', string()}.
+-spec swap_handler(Type :: swap_handler_type()) -> any().
+
+swap_handler(tty) ->
+ gen_event:swap_handler(error_logger, {error_logger, swap},
+ {error_logger_tty_h, []}),
+ simple_logger();
+swap_handler({logfile, File}) ->
+ gen_event:swap_handler(error_logger, {error_logger, swap},
+ {error_logger_file_h, File}),
+ simple_logger();
+swap_handler(silent) ->
+ gen_event:delete_handler(error_logger, error_logger, delete),
+ simple_logger();
+swap_handler(false) ->
+ ok. % keep primitive event handler as-is
+
+-spec add_report_handler(Module :: atom()) -> any().
+
+add_report_handler(Module) when is_atom(Module) ->
+ gen_event:add_handler(error_logger, Module, []).
+
+-spec add_report_handler(atom(), any()) -> any().
+
+add_report_handler(Module, Args) when is_atom(Module) ->
+ gen_event:add_handler(error_logger, Module, Args).
+
+-spec delete_report_handler(Module :: atom()) -> any().
+
+delete_report_handler(Module) when is_atom(Module) ->
+ gen_event:delete_handler(error_logger, Module, []).
+
+%% Start the lowest level error_logger handler with Buffer.
+
+simple_logger(Buffer_size) when is_integer(Buffer_size) ->
+ gen_event:add_handler(error_logger, error_logger, Buffer_size).
+
+%% Start the lowest level error_logger handler without Buffer.
+
+simple_logger() ->
+ gen_event:add_handler(error_logger, error_logger, []).
+
+%% Log all errors to File for all eternity
+
+-spec logfile(Request :: {'open', string()}) -> 'ok' | {'error',any()}
+ ; (Request :: 'close') -> 'ok' | {'error', any()}
+ ; (Request :: 'filename') -> atom() | string() | {'error', any()}.
+
+logfile({open, File}) ->
+ case lists:member(error_logger_file_h,
+ gen_event:which_handlers(error_logger)) of
+ true ->
+ {error, allready_have_logfile};
+ _ ->
+ gen_event:add_handler(error_logger, error_logger_file_h, File)
+ end;
+logfile(close) ->
+ case gen_event:delete_handler(error_logger, error_logger_file_h, normal) of
+ {error,Reason} ->
+ {error,Reason};
+ _ ->
+ ok
+ end;
+logfile(filename) ->
+ case gen_event:call(error_logger, error_logger_file_h, filename) of
+ {error,_} ->
+ {error, no_log_file};
+ Val ->
+ Val
+ end.
+
+%% Possibly turn off all tty printouts, maybe we only want the errors
+%% to go to a file
+
+-spec tty(Flag :: boolean()) -> 'ok'.
+
+tty(true) ->
+ Hs = gen_event:which_handlers(error_logger),
+ case lists:member(error_logger_tty_h, Hs) of
+ false ->
+ gen_event:add_handler(error_logger, error_logger_tty_h, []);
+ true ->
+ ignore
+ end,
+ ok;
+tty(false) ->
+ gen_event:delete_handler(error_logger, error_logger_tty_h, []),
+ ok.
+
+
+%%% ---------------------------------------------------
+%%% This is the default error_logger handler.
+%%% ---------------------------------------------------
+
+-spec init(term()) -> {'ok', state() | []}.
+
+init(Max) when is_integer(Max) ->
+ {ok, {Max, 0, []}};
+%% This one is called if someone took over from us, and now wants to
+%% go back.
+init({go_back, _PostState}) ->
+ {ok, {?buffer_size, 0, []}};
+init(_) -> %% Start and just relay to other
+ {ok, []}. %% node if node(GLeader) =/= node().
+
+-spec handle_event(term(), state()) -> {'ok', state()}.
+
+handle_event({Type, GL, Msg}, State) when node(GL) =/= node() ->
+ gen_event:notify({error_logger, node(GL)},{Type, GL, Msg}),
+ %% handle_event2({Type, GL, Msg}, State); %% Shall we do something
+ {ok, State}; %% at this node too ???
+handle_event({info_report, _, {_, Type, _}}, State) when Type =/= std_info ->
+ {ok, State}; %% Ignore other info reports here
+handle_event(Event, State) ->
+ handle_event2(Event, State).
+
+-spec handle_info(term(), state()) -> {'ok', state()}.
+
+handle_info({emulator, GL, Chars}, State) when node(GL) =/= node() ->
+ {error_logger, node(GL)} ! {emulator, GL, add_node(Chars,self())},
+ {ok, State};
+handle_info({emulator, GL, Chars}, State) ->
+ handle_event2({emulator, GL, Chars}, State);
+handle_info(_, State) ->
+ {ok, State}.
+
+-spec handle_call(term(), state()) -> {'ok', {'error', 'bad_query'}, state()}.
+
+handle_call(_Query, State) -> {ok, {error, bad_query}, State}.
+
+-spec terminate(term(), state()) -> {'error_logger', [term()]}.
+
+terminate(swap, {_, 0, Buff}) ->
+ {error_logger, Buff};
+terminate(swap, {_, Lost, Buff}) ->
+ Myevent = {info, group_leader(), {self(), {lost_messages, Lost}, []}},
+ {error_logger, [tag_event(Myevent)|Buff]};
+terminate(_, _) ->
+ {error_logger, []}.
+
+handle_event2(Event, {1, Lost, Buff}) ->
+ display(tag_event(Event)),
+ {ok, {1, Lost+1, Buff}};
+handle_event2(Event, {N, Lost, Buff}) ->
+ Tagged = tag_event(Event),
+ display(Tagged),
+ {ok, {N-1, Lost, [Tagged|Buff]}};
+handle_event2(_, State) ->
+ {ok, State}.
+
+tag_event(Event) ->
+ {erlang:localtime(), Event}.
+
+display({Tag,{error,_,{_,Format,Args}}}) ->
+ display2(Tag,Format,Args);
+display({Tag,{error_report,_,{_,Type,Report}}}) ->
+ display2(Tag,Type,Report);
+display({Tag,{info_report,_,{_,Type,Report}}}) ->
+ display2(Tag,Type,Report);
+display({Tag,{info,_,{_,Error,_}}}) ->
+ display2(Tag,Error,[]);
+display({Tag,{info_msg,_,{_,Format,Args}}}) ->
+ display2(Tag,Format,Args);
+display({Tag,{warning_report,_,{_,Type,Report}}}) ->
+ display2(Tag,Type,Report);
+display({Tag,{warning_msg,_,{_,Format,Args}}}) ->
+ display2(Tag,Format,Args);
+display({Tag,{emulator,_,Chars}}) ->
+ display2(Tag,Chars,[]).
+
+add_node(X, Pid) when is_atom(X) ->
+ add_node(atom_to_list(X), Pid);
+add_node(X, Pid) ->
+ lists:concat([X,"** at node ",node(Pid)," **~n"]).
+
+%% Can't do io_lib:format
+
+display2(Tag,F,A) ->
+ erlang:display({error_logger,Tag,F,A}).