%%%---------------------------------------------------------------------- %%% File : ejabberd_logger_h.erl %%% Author : Alexey Shchepin %%% Purpose : %%% Created : 23 Oct 2003 by Alexey Shchepin %%%---------------------------------------------------------------------- -module(ejabberd_logger_h). -author('alexey@sevcom.net'). %%-compile(export_all). %%-export([Function/Arity, ...]). -behaviour(gen_event). %% gen_event callbacks -export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2, code_change/3, reopen_log/0]). -record(state, {fd, file}). %%%---------------------------------------------------------------------- %%% Callback functions from gen_event %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, State} | %% Other %%---------------------------------------------------------------------- init(File) -> case file:open(File, [append]) of {ok, Fd} -> {ok, #state{fd = Fd, file = File}}; Error -> Error end. %%---------------------------------------------------------------------- %% Func: handle_event/2 %% Returns: {ok, State} | %% {swap_handler, Args1, State1, Mod2, Args2} | %% remove_handler %%---------------------------------------------------------------------- handle_event(Event, State) -> write_event(State#state.fd, {erlang:localtime(), Event}), {ok, State}. %%---------------------------------------------------------------------- %% Func: handle_call/2 %% Returns: {ok, Reply, State} | %% {swap_handler, Reply, Args1, State1, Mod2, Args2} | %% {remove_handler, Reply} %%---------------------------------------------------------------------- handle_call(_Request, State) -> Reply = ok, {ok, Reply, State}. %%---------------------------------------------------------------------- %% Func: handle_info/2 %% Returns: {ok, State} | %% {swap_handler, Args1, State1, Mod2, Args2} | %% remove_handler %%---------------------------------------------------------------------- handle_info({'EXIT', _Fd, _Reason}, _State) -> remove_handler; handle_info({emulator, _GL, reopen}, State) -> file:close(State#state.fd), case file:open(State#state.file, [append]) of {ok, Fd} -> {ok, State#state{fd = Fd}}; Error -> Error end; handle_info({emulator, GL, Chars}, State) -> write_event(State#state.fd, {erlang:localtime(), {emulator, GL, Chars}}), {ok, State}; handle_info(_Info, State) -> {ok, State}. %%---------------------------------------------------------------------- %% Func: terminate/2 %% Purpose: Shutdown the server %% Returns: any %%---------------------------------------------------------------------- terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. reopen_log() -> error_logger ! {emulator, noproc, reopen}. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- % Copied from erlang_logger_file_h.erl write_event(Fd, {Time, {error, _GL, {Pid, Format, Args}}}) -> T = write_time(Time), case catch io_lib:format(add_node(Format,Pid), Args) of S when list(S) -> io:format(Fd, T ++ S, []); _ -> F = add_node("ERROR: ~p - ~p~n", Pid), io:format(Fd, T ++ F, [Format,Args]) end; write_event(Fd, {Time, {emulator, _GL, Chars}}) -> T = write_time(Time), case catch io_lib:format(Chars, []) of S when list(S) -> io:format(Fd, T ++ S, []); _ -> io:format(Fd, T ++ "ERROR: ~p ~n", [Chars]) end; write_event(Fd, {Time, {info, _GL, {Pid, Info, _}}}) -> T = write_time(Time), io:format(Fd, T ++ add_node("~p~n",Pid),[Info]); write_event(Fd, {Time, {error_report, _GL, {Pid, std_error, Rep}}}) -> T = write_time(Time), S = format_report(Rep), io:format(Fd, T ++ S ++ add_node("", Pid), []); write_event(Fd, {Time, {info_report, _GL, {Pid, std_info, Rep}}}) -> T = write_time(Time, "INFO REPORT"), S = format_report(Rep), io:format(Fd, T ++ S ++ add_node("", Pid), []); write_event(Fd, {Time, {info_msg, _GL, {Pid, Format, Args}}}) -> T = write_time(Time, "INFO REPORT"), case catch io_lib:format(add_node(Format,Pid), Args) of S when list(S) -> io:format(Fd, T ++ S, []); _ -> F = add_node("ERROR: ~p - ~p~n", Pid), io:format(Fd, T ++ F, [Format,Args]) end; write_event(_, _) -> ok. format_report(Rep) when list(Rep) -> case string_p(Rep) of true -> io_lib:format("~s~n",[Rep]); _ -> format_rep(Rep) end; format_report(Rep) -> io_lib:format("~p~n",[Rep]). format_rep([{Tag,Data}|Rep]) -> io_lib:format(" ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep); format_rep([Other|Rep]) -> io_lib:format(" ~p~n",[Other]) ++ format_rep(Rep); format_rep(_) -> []. add_node(X, Pid) when atom(X) -> add_node(atom_to_list(X), Pid); add_node(X, Pid) when node(Pid) /= node() -> lists:concat([X,"** at node ",node(Pid)," **~n"]); add_node(X, _) -> X. string_p([]) -> false; string_p(Term) -> string_p1(Term). string_p1([H|T]) when integer(H), H >= $\s, H < 255 -> string_p1(T); string_p1([$\n|T]) -> string_p1(T); string_p1([$\r|T]) -> string_p1(T); string_p1([$\t|T]) -> string_p1(T); string_p1([$\v|T]) -> string_p1(T); string_p1([$\b|T]) -> string_p1(T); string_p1([$\f|T]) -> string_p1(T); string_p1([$\e|T]) -> string_p1(T); string_p1([H|T]) when list(H) -> case string_p1(H) of true -> string_p1(T); _ -> false end; string_p1([]) -> true; string_p1(_) -> false. write_time(Time) -> write_time(Time, "ERROR REPORT"). write_time({{Y,Mo,D},{H,Mi,S}}, Type) -> io_lib:format("~n=~s==== ~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w ===~n", [Type, Y, Mo, D, H, Mi, S]).