%% %% %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}).