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