report browser and error_logger
Martin Bjorklund
mbj@REDACTED
Wed Jan 9 21:42:31 CET 2002
Hal Snyder <hal@REDACTED> wrote:
> I think I am missing something obvious. The error_logger looks like a
> very useful thing for tracking events in OTP and in one's own
> applications.
>
> I think the way to use it is thus:
>
> - add the sasl app to your .rel file
> - tune error file settings by setting sasl_error_logger,
> error_logger_mf_dir, and so forth in your .config file
> - write messages with error_logger:error_msg/1 and
> error_logger:info_msg/1
> - view results with rb: commands
>
> 1. Is the above typical in real OTP apps?
Since I implemented this in OTP, I've done some rethinking - why
bother to store the logs in binary format anyway? In most cases it's
better to store the error log in plain text, but using a wrap log so
the log won't grow. This is what we do in our systems.
I've attached disk_log_h, a module which should have been implemented
years ago. It's a gen_event handler for disk_log. This handler can
be used e.g. for an error log, or whatever. This module could very
well be part of OTP (it's intended to be).
disk_log_h needs a small patch to disk_log.erl, also attached.
Finally, I've included some code that uses this to format error_logger
as plain text.
/martin
-------------- next part --------------
%%%----------------------------------------------------------------------
%%% File : disk_log_h.erl
%%% Author : Martin Bjorklund <mbj@REDACTED>
%%% Purpose : gen_event handler for disk_log. The process which owns
%%% the log is the gen_event process, thus no extra process
%%% communcation overhead is involved.
%%% The alternative is to let the gen_event process send the
%%% message to a disk_log process, which then sends it to
%%% disk. In this case, the disk_log process is avoided.
%%%
%%% This module is intended to replace log_mf_h.erl.
%%% Created : 1 Dec 2000 by Martin Bjorklund <mbj@REDACTED>
%%%----------------------------------------------------------------------
-module(disk_log_h).
-author('mbj@REDACTED').
-behaviour(gen_event).
%% External exports
-export([init/2, info/2]).
%% gen_event callbacks
-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2]).
-record(state, {log, cnt, func}).
-include_lib("kernel/src/disk_log.hrl").
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
%%-----------------------------------------------------------------
%% This module is intended to be used as a gen_event handler. Instead
%% of duplicating the functions to gen_event (add_handler etc), it
%% described here hoe to use these function with this module.
%%
%% The init function expects a list [Func, Opts], where:
%% Func = fun(Event) -> false | binary() | [binary()]
%% Opts = <as disk_log:open>
%% To add a hander to a gen_event process, call e.g.:
%% gen_event:add_handler(EventMgr, disk_log_h, [Func, Opts])
%%
%% No terminate arguments are needed.
%%
%% Here's a minimal but working example:
%%
%% tr_event(Event) ->
%% list_to_binary(io_lib:format("tr_event: ~p\n", [Event])).
%%
%% start() ->
%% Args = disk_log_h:init({?MODULE, tr_event}, [{name, tst},
%% {format, external},
%% {file, "/tmp/tst.log"}]),
%% gen_event:add_handler(error_logger, {disk_log_h, tst}, Args).
%%
%% stop ->
%% gen_event:delete_handler(error, logger, {disk_log_h, tst}).
%%
%%-----------------------------------------------------------------
init(Func, DiskLogOpts) ->
[Func, DiskLogOpts].
info(EventMgr, Handler) ->
gen_event:call(EventMgr, Handler, info).
%%%----------------------------------------------------------------------
%%% Callback functions from gen_event
%%%----------------------------------------------------------------------
%%----------------------------------------------------------------------
%% Func: init/1
%% Returns: {ok, S} |
%% Other
%%----------------------------------------------------------------------
init([Func, Opts]) ->
case disk_log:ll_open(Opts) of
{ok, _, Log, Cnt} ->
{ok, #state{log = Log, cnt = Cnt, func = Func}};
Error ->
Error
end.
%%----------------------------------------------------------------------
%% Func: handle_event/2
%% Returns: {ok, S} |
%% {swap_handler, Args1, S1, Mod2, Args2} |
%% remove_handler
%%----------------------------------------------------------------------
handle_event(Event, S) ->
case (S#state.func)(Event) of
false ->
{ok, S};
Bin ->
case disk_log:do_log(S#state.log, Bin) of
{N, L1} when integer(N) ->
{ok, S#state{cnt = S#state.cnt+N, log = L1}};
{error, {error, {full, _Name}}, L1, 0} ->
{ok, S#state{log = L1}};
{error, Error, L1, N} ->
Error;
Error ->
Error
end
end.
%%----------------------------------------------------------------------
%% Func: handle_call/2
%% Returns: {ok, Reply, S} |
%% {swap_handler, Reply, Args1, S1, Mod2, Args2} |
%% {remove_handler, Reply}
%%----------------------------------------------------------------------
handle_call(info, S) ->
Reply = disk_log:do_info(S#state.log, S#state.cnt),
{ok, Reply, S}.
%%----------------------------------------------------------------------
%% Func: handle_info/2
%% Returns: {ok, S} |
%% {swap_handler, Args1, S1, Mod2, Args2} |
%% remove_handler
%%----------------------------------------------------------------------
handle_info({emulator, GL, Chars}, S) ->
%% this is very unfortunate...
handle_event({emulator, GL, Chars}, S);
handle_info(Info, S) ->
{ok, S}.
%%----------------------------------------------------------------------
%% Func: terminate/2
%% Purpose: Shutdown the server
%% Returns: any
%%----------------------------------------------------------------------
terminate(Arg, S) ->
disk_log:ll_close(S#state.log).
%%%----------------------------------------------------------------------
%%% Internal functions
%%%----------------------------------------------------------------------
-------------- next part --------------
*** disk_log.erl Mon Oct 8 10:37:59 2001
--- /tmp/disk_log.erl Wed Jan 9 21:30:53 2002
***************
*** 37,42 ****
--- 37,45 ----
%% To be used by wrap_log_reader only.
-export([ichunk_end/2]).
+ %% To be used by disk_log_h only.
+ -export([ll_open/1, ll_close/1, do_log/2, do_info/2]).
+
%% To be used for debugging only:
-export([pid2name/1]).
***************
*** 64,69 ****
--- 67,81 ----
open(A) ->
disk_log_server:open(check_arg(A, #arg{options = A})).
+ ll_open(A) ->
+ case check_arg(A, #arg{options = A}) of
+ {ok, L} -> do_open(L);
+ Error -> Error
+ end.
+
+ ll_close(Log) ->
+ close_disk_log2(Log).
+
log(Log, Term) ->
req(Log, {log, term_to_binary(Term)}).
-------------- next part --------------
%%%-------------------------------------------------------------------
%%% File : logger.erl
%%% Created : 9 Jan 2002 by Martin Bjorklund <mbj@REDACTED>
%%% Purpose : Simple module to show how disk_log_h can be used
%%% to format error_logger as plain text to a wrap log.
%%%-------------------------------------------------------------------
-module(logger).
-author('magnus@REDACTED').
-author('mbj@REDACTED').
-export([add_error_logger_mf/4, delete_error_logger_mf/0]).
-export([form_all/1, form_no_progress/1]).
%% Type = all | error
add_error_logger_mf(File, MaxB, MaxF, Type) ->
Opts = [{name, logger},
{file, File},
{type, wrap},
{format, external},
{size, {MaxB, MaxF}}],
gen_event:add_handler(error_logger,
{disk_log_h, logger},
disk_log_h:init(form_func(Type), Opts)).
delete_error_logger_mf() ->
gen_event:delete_handler(error_logger, {disk_log_h, logger}, stop).
form_func(all) -> {logger, form_all};
form_func(_) -> {logger, form_no_progress}.
form_all({Type, GL, Msg}) when node(GL) /= node() ->
false;
form_all(Event) ->
Str =
case Event of
{error_report, _GL, {Pid, Type, Report}} ->
[mk_hdr("ERROR REPORT", Type, Pid),
io_lib:format("~p\n", [Report])];
{info_report, _GL, {Pid, Type, Report}} ->
[mk_hdr("INFO REPORT", Type, Pid),
io_lib:format("~p\n", [Report])];
{error, _GL, {Pid, Format, Args}} ->
[mk_hdr("ERROR", undefined, Pid),
io_lib:format(Format, Args)];
{info_msg, _GL, {Pid, Format, Args}} ->
[mk_hdr("INFO MSG", undefined, Pid),
io_lib:format(Format, Args)];
{info, _GL, {Pid, Term, _Empty}} ->
[mk_hdr("INFO", undefined, Pid),
io_lib:format("~p\n", [Term])];
{emulator, _GL, EStr} ->
[mk_hdr("EMULATOR", undefined, undefined),
EStr];
_ ->
[mk_hdr("UNKNOWN", undefined, undefined),
io_lib:format("~p\n", [Event])]
end,
list_to_binary([Str, "\n"]).
mk_hdr(HStr, Type, Who) ->
["== ", t2s(erlang:localtime()), " == ", HStr, " - ",
pstr(Type), " ", pstr(Who), "\n"].
pstr(undefined) -> "";
pstr(T) -> io_lib:format("~p", [T]).
t2s({{Year,Month,Day},{Hour,Minute,Second}}) ->
io_lib:format("~w-~s-~w::~2..0w:~2..0w:~2..0w",
[Day,m2s(Month),Year,Hour,Minute,Second]).
m2s(1) -> "Jan";
m2s(2) -> "Feb";
m2s(3) -> "Mar";
m2s(4) -> "Apr";
m2s(5) -> "May";
m2s(6) -> "Jun";
m2s(7) -> "Jul";
m2s(8) -> "Aug";
m2s(9) -> "Sep";
m2s(10) -> "Oct";
m2s(11) -> "Nov";
m2s(12) -> "Dec".
form_no_progress({Type, GL, Msg}) when node(GL) /= node() ->
false;
form_no_progress({info_report, _, {_, progress, [{application,_},
{started_at, _}]}} = Event) ->
form_all(Event);
form_no_progress({info_report, _, {_, progress, _}}) ->
false;
form_no_progress(Event) ->
form_all(Event).
More information about the erlang-questions
mailing list