blob: c6d7074f198b3fe405cfd2e82e8df81d158be3f0 [file] [log] [blame]
%%% Copyright (c) 2007- Facebook
%%% Distributed under the Thrift Software License
%%%
%%% See accompanying file LICENSE or visit the Thrift site at:
%%% http://developers.facebook.com/thrift/
-module(thrift_logger).
-behaviour(gen_event).
-include("thrift.hrl").
-include("oop.hrl").
%% gen_event callbacks
-export([init/1, handle_event/2, handle_call/2,
handle_info/2, terminate/2, code_change/3]).
-export([install/0]).
%%
-record(state, {}).
-define(GS_TERM_FORMAT, "** Generic server ~p terminating \n** Last message in was ~p~n** When Server state == ~p~n** Reason for termination == ~n** ~p~n").
%%%
%%% ensure the regular logger is out and ours is in
%%%
install() ->
%% remove loggers
io:format("starting logger~n"),
lists:foreach(fun(Logger) ->
case Logger of
_ -> gen_event:delete_handler(error_logger, Logger, normal)
end end,
gen_event:which_handlers(error_logger)),
%% TODO(cpiro): sasl someday?
%% gen_event:add_handler(error_logger, sasl_report_file_h, {LogFile, all}),
gen_event:add_handler(error_logger, ?MODULE, []),
ok.
%%%
%%% init
%%%
init([]) ->
State = #state{},
{ok, State}.
%%%
%%% handle_event
%%%
handle_event2(Symbol, Pid, Type, Message, State) -> % Message must be a string
{ok, MessageSafe, NL} = regexp:gsub(Message, "[\n]+", " "), % collapse whitespace to one space
Type1 =
case Type of
"" -> "";
_ -> sformat("~p ", [Type])
end,
Banner =
case config(show_pid) of
true ->
sformat("~s ~s ~s", [Symbol, Pid, Type1]);
false ->
sformat("~s~i ~s", [Symbol, Pid, Type1])
end,
BannerLen = length(Banner),
%% there's no way to see if Message is a string? just try
Output = sformat("~s", [Message]),
OutputSafe = sformat("~s", [MessageSafe]),
Length =
case (length(OutputSafe) + BannerLen) < config(term_width) of
true -> short;
false -> long
end,
OneLine =
case NL == 0 of
true -> oneliner;
false -> multiline
end,
case { config(force_one_line), Length, OneLine } of
%% one line and short ... print as is
{_, short, oneliner} ->
format("~s~s~n", [Banner, OutputSafe]);
%% too long ... squash to one
{true, long, _} ->
O = Banner ++ OutputSafe,
Format = sformat("~~~ps >~n", [config(term_width)-2]), % e.g. "~80s >~n"
format(Format, [O]);
%% short but multiline... collapse to one
{true, short, multiline} ->
format("~s~s~n", [Banner, OutputSafe]);
%% just print it
_ ->
format("~s~n~s~n~n", [Banner, Output])
end.
%%
handle_event1({What, _Gleader, {Ref, Format, Data}}, State) when is_list(Format) ->
Symbol =
case What of
error -> "!!";
warning_msg -> "**";
info_msg -> "..";
_Else -> "??"
end,
case {Format, Data} of
{?GS_TERM_FORMAT, [Ref, LastMessage, Obj, {Kind, E}]} when Kind == timeout; Kind == thrift_exception ->
ok;
{?GS_TERM_FORMAT, [Ref, LastMessage, Obj, Reason]} ->
Format1 = "** gen_server terminating in message ~p~n** State = ~s~n** Reason = ~p~n",
Message = sformat(Format1, [LastMessage, oop:inspect(Obj), Reason]),
handle_event2(Symbol, Ref, "", Message, State);
{?GS_TERM_FORMAT, _Dta} ->
Message = sformat("DATA DIDN'T MATCH: ~p~n", [Data]) ++ sformat(Format, Data),
handle_event2(Symbol, Ref, "", Message, State);
{_, _} ->
case lists:member(Format, config(omit_fmt)) of
false ->
Message = sformat(Format, Data),
handle_event2(Symbol, Ref, "", Message, State);
true ->
ok
end
end,
{ok, State};
handle_event1({What, _Gleader, {Pid, Type, Report}}, State) ->
Symbol =
case What of
error_report -> "!!";
warning_report -> "**";
info_report -> "..";
_Else -> "??"
end,
case Type of
crash_report ->
print_crash_report(Report);
progress ->
ok;
_ ->
Message = sformat("|| ~s", [oop:inspect(Report)]),
handle_event2(Symbol, Pid, Type, Message, State)
end,
{ok, State};
handle_event1(_Event, State) ->
handle_event2("??", "<?.?.?>", "", _Event, State),
{ok, State}.
handle_event(Event, State) ->
try
handle_event1(Event, State)
catch
_:E ->
format("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~n error logger error:~n ~p~n Event = ~p~n State = ~p~n ~p~n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~n",
[E, Event, State, erlang:get_stacktrace()]),
{ok, State}
end.
%%%
%%% call, info, terminate, code_change
%%%
handle_call(_Request, State) ->
Reply = ok,
{ok, Reply, State}.
handle_info(_Info, State) ->
{ok, State}.
terminate(normal, _State) ->
ok;
terminate(Reason, _State) ->
format("*****************~n~n frick, error logger terminating: ~p~n~n*****************~n~n", [Reason]),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%====================================================================
%%% Internal functions
%%====================================================================
%% how to output
format(Format, Data) ->
io:format(Format, Data).
%% convenience
sformat(Format, Data) ->
thrift_utils:sformat(Format, Data).
config(Item) ->
thrift:config(Item).
print_crash_report(Report) ->
case Report of
%% for R12B0
[[_, _, {error_info, {exit, {thrift_exception, _}, _}} | _] | _] ->
ok;
[[_, _, {error_info, {exit, {timeout, _}, _}} | _] | _] ->
ok;
%% for R11B5
[[_,_,{error_info, {thrift_exception, _}}|_] | _] ->
ok;
[[_,_,{error_info, {timeout, _}}|_] | _] ->
ok;
%% else
_ ->
io:format("~~~~ crash report: ~w~n", [Report])
end.