blob: e266ef4c1c66fa783890bb9f1afef74a97116dac [file] [log] [blame]
Christopher Piro5b3a8f72007-08-01 22:27:37 +00001%%% Copyright (c) 2007- Facebook
2%%% Distributed under the Thrift Software License
3%%%
4%%% See accompanying file LICENSE or visit the Thrift site at:
5%%% http://developers.facebook.com/thrift/
6
7-module(thrift_logger).
8
9-behaviour(gen_event).
10
Christopher Pirofa0c8572007-08-11 01:15:57 +000011%% TODO(cpiro): either
12%% make exceptions know whether they need to be displayed
13%% or not exit with tExecptions for non-errors
14%% or "register" tExceptions with the logger (I LIKE!)
15%% ... we shouldn't need to build any specifics in here
16-include("thrift.hrl").
17-include("oop.hrl").
18-include("transport/tTransportException.hrl").
19
Christopher Piro5b3a8f72007-08-01 22:27:37 +000020%% gen_event callbacks
21-export([init/1, handle_event/2, handle_call/2,
22 handle_info/2, terminate/2, code_change/3]).
23
Christopher Piro6c46f1a2007-10-23 09:47:15 +000024-export([install/0]).
25
26-record(state, {}).
Christopher Piro5b3a8f72007-08-01 22:27:37 +000027
28%% ensure the regular logger is out and ours is in
29install() ->
Christopher Piro5b3a8f72007-08-01 22:27:37 +000030 %% remove loggers
Christopher Piro68940292007-10-02 00:35:12 +000031 io:format("starting logger~n"),
Christopher Piro5b3a8f72007-08-01 22:27:37 +000032 lists:foreach(fun(Logger) ->
33 case Logger of
34 _ -> gen_event:delete_handler(error_logger, Logger, normal)
35 end end,
36 gen_event:which_handlers(error_logger)),
37
38 %% TODO(cpiro): sasl someday?
39 %% gen_event:add_handler(error_logger, sasl_report_file_h, {LogFile, all}),
Christopher Piro5b3a8f72007-08-01 22:27:37 +000040
Christopher Piro68940292007-10-02 00:35:12 +000041 gen_event:add_handler(error_logger, ?MODULE, []),
Christopher Piro5b3a8f72007-08-01 22:27:37 +000042
Christopher Piro68940292007-10-02 00:35:12 +000043 ok.
44
Christopher Piro5b3a8f72007-08-01 22:27:37 +000045%%====================================================================
46%% gen_event callbacks
47%%====================================================================
48%%--------------------------------------------------------------------
49%% @spec init(Args) -> {ok, State}.
50%%
51%% @doc
52%% Whenever a new event handler is added to an event manager,
53%% this function is called to initialize the event handler.
54%% @end
55%%--------------------------------------------------------------------
56init([]) ->
Christopher Piro6c46f1a2007-10-23 09:47:15 +000057 State = #state{},
Christopher Piro5b3a8f72007-08-01 22:27:37 +000058 {ok, State}.
59
60%%--------------------------------------------------------------------
61%% @spec handle_event(Event, State) -> {ok, State} |
62%% {swap_handler, Args1, State1, Mod2, Args2} |
63%% remove_handler.
64%%
65%% @doc
66%% Whenever an event manager receives an event sent using
67%% gen_event:notify/2 or gen_event:sync_notify/2, this function is called for
68%% each installed event handler to handle the event.
69%% @end
70%%--------------------------------------------------------------------
71handle_event2(Symbol, Pid, Type, Message, State) -> % Message must be a string
72 {ok, MessageSafe, NL} = regexp:gsub(Message, "[\n]+", " "), % collapse whitespace to one space
73
74 Type1 =
75 case Type of
76 "" -> "";
77 _ -> sformat("~p ", [Type])
78 end,
79
Christopher Piro3b63fe42007-10-19 21:34:31 +000080 Banner =
Christopher Piro6c46f1a2007-10-23 09:47:15 +000081 case config(show_pid) of
Christopher Piro3b63fe42007-10-19 21:34:31 +000082 true ->
83 sformat("~s ~s ~s", [Symbol, Pid, Type1]);
84 false ->
85 sformat("~s~i ~s", [Symbol, Pid, Type1])
86 end,
87 BannerLen = length(Banner),
88
89 %% there's no way to see if Message is a string? just try
90 Output = sformat("~s", [Message]),
91 OutputSafe = sformat("~s", [MessageSafe]),
Christopher Piro5b3a8f72007-08-01 22:27:37 +000092
93 Length =
Christopher Piro6c46f1a2007-10-23 09:47:15 +000094 case (length(OutputSafe) + BannerLen) < config(term_width) of
Christopher Piro5b3a8f72007-08-01 22:27:37 +000095 true -> short;
96 false -> long
97 end,
98
99 OneLine =
100 case NL == 0 of
101 true -> oneliner;
102 false -> multiline
103 end,
104
Christopher Piro6c46f1a2007-10-23 09:47:15 +0000105 case { config(force_one_line), Length, OneLine } of
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000106 %% one line and short ... print as is
107 {_, short, oneliner} ->
108 format("~s~s~n", [Banner, OutputSafe]);
109
110 %% too long ... squash to one
111 {true, long, _} ->
112 O = Banner ++ OutputSafe,
Christopher Piro6c46f1a2007-10-23 09:47:15 +0000113 Format = sformat("~~~ps >~n", [config(term_width)-2]), % e.g. "~80s >~n"
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000114 format(Format, [O]);
115
116 %% short but multiline... collapse to one
117 {true, short, multiline} ->
118 format("~s~s~n", [Banner, OutputSafe]);
119
120 %% just print it
121 _ ->
122 format("~s~n~s~n~n", [Banner, Output])
123 end.
124
125%%
Christopher Piro524c3ec2007-10-13 05:15:33 +0000126-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").
127
Christopher Piroefd5eec2007-10-02 01:33:37 +0000128handle_event1({What, _Gleader, {Ref, Format, Data}}, State) when is_list(Format) ->
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000129 Symbol = case What of
130 error -> "!!";
131 warning_msg -> "**";
132 info_msg -> "..";
133 _Else -> "??"
134 end,
135
Christopher Piro524c3ec2007-10-13 05:15:33 +0000136 case {Format, Data} of
137 {?GS_TERM_FORMAT, [Ref, LastMessage, Obj, Reason]} ->
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000138 %% TODO: move as much logic as possible out of thrift_logger
Christopher Pirofa0c8572007-08-11 01:15:57 +0000139 Ignore =
140 begin
141 is_tuple(Reason) andalso
142 size(Reason) >= 1 andalso element(1, Reason) == timeout
143 end
144 orelse
145 begin
146 case thrift_utils:unnest_record(Reason, tTransportException) of
147 error -> false;
148 {ok, TTE} ->
149 oop:get(TTE, type) == ?tTransportException_NOT_OPEN andalso
150 oop:get(TTE, message) == "in tSocket:read/2: gen_tcp:recv"
151 end
152 end,
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000153
154 case Ignore of
155 true ->
156 ok;
157 false ->
158 Format1 = "** gen_server terminating in message ~p~n** State = ~s~n** Reason = ~s~n",
159 Message = sformat(Format1, [LastMessage, oop:inspect(Obj), oop:inspect(Reason)]), %% TODO(cpiro): hope Reason is an object?
Christopher Piro68940292007-10-02 00:35:12 +0000160 handle_event2(Symbol, Ref, "", Message, State)
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000161 end;
Christopher Piro524c3ec2007-10-13 05:15:33 +0000162 {?GS_TERM_FORMAT, _Dta} ->
163 Message = sformat("DATA DIDN'T MATCH: ~p~n", [Data]) ++ sformat(Format, Data),
164 handle_event2(Symbol, Ref, "", Message, State);
Christopher Piro3b63fe42007-10-19 21:34:31 +0000165 {_, _} ->
Christopher Piro6c46f1a2007-10-23 09:47:15 +0000166 case lists:member(Format, config(omit_fmt)) of
Christopher Piro3b63fe42007-10-19 21:34:31 +0000167 false ->
168 Message = sformat(Format, Data),
169 handle_event2(Symbol, Ref, "", Message, State);
170 true ->
171 ok
172 end
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000173 end,
174 {ok, State};
175
176handle_event1({What, _Gleader, {Pid, Type, Report}}, State) ->
177 Symbol = case What of
178 error_report -> "!!";
179 warning_report -> "**";
180 info_report -> "..";
181 _Else -> "??"
182 end,
183
184 case Type of
Christopher Piro3b63fe42007-10-19 21:34:31 +0000185 crash_report ->
186 case do_print_crash_report(Report) of
187 true ->
188 io:format("~~~~ crash report: '~p'~n", [Report]);
189 false ->
190 ok
191 end;
192%% crash_report ->
193%% [Cruft|_] = Report,
194%% {value, {_, Reason}} = lists:keysearch(error_info, 1, Cruft),
195%% {value, {_, {_, _, [_,_,_,_, Obj, []]}}} = lists:keysearch(initial_call, 1, Cruft),
196%% sformat("state == ~s~nreason ==~s", [oop:inspect(Obj), oop:inspect(Reason)]),
197%% ok;
198 progress ->
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000199 ok;
200
201 _ ->
202 Message = sformat("|| ~s", [oop:inspect(Report)]),
203 handle_event2(Symbol, Pid, Type, Message, State)
204 end,
205 {ok, State};
206
207handle_event1(_Event, State) ->
208 handle_event2("??", "<?.?.?>", "", _Event, State),
209 {ok, State}.
210
211handle_event(Event, State) ->
212 try
213 handle_event1(Event, State)
214 catch
215 _:E ->
216 format("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~n error logger error:~n ~p~n Event = ~p~n State = ~p~n ~p~n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~n",
217 [E, Event, State, erlang:get_stacktrace()]),
218 {ok, State}
219 end.
220
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000221%%--------------------------------------------------------------------
222%% @spec handle_call(Request, State) -> {ok, Reply, State} |
Christopher Piro68940292007-10-02 00:35:12 +0000223%% {swap_handler, Reply, Args1, State1,
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000224%% Mod2, Args2} |
225%% {remove_handler, Reply}.
226%%
227%% @doc
228%% Whenever an event manager receives a request sent using
229%% gen_event:call/3,4, this function is called for the specified event
230%% handler to handle the request.
231%% @end
232%%--------------------------------------------------------------------
233handle_call(_Request, State) ->
234 Reply = ok,
235 {ok, Reply, State}.
236
237%%--------------------------------------------------------------------
238%% @spec handle_info(Info, State) -> {ok, State} |
239%% {swap_handler, Args1, State1, Mod2, Args2} |
240%% remove_handler.
241%%
242%% @doc
243%% This function is called for each installed event handler when
244%% an event manager receives any other message than an event or a synchronous
245%% request (or a system message).
246%% @end
247%%--------------------------------------------------------------------
248handle_info(_Info, State) ->
249 {ok, State}.
250
251%%--------------------------------------------------------------------
252%% @spec terminate(Reason, State) -> void().
253%%
254%% @doc
255%% Whenever an event handler is deleted from an event manager,
256%% this function is called. It should be the opposite of Module:init/1 and
257%% do any necessary cleaning up.
258%% @end
259%%--------------------------------------------------------------------
260terminate(normal, _State) ->
261 ok;
262terminate(Reason, _State) ->
263 format("*****************~n~n frick, error logger terminating: ~p~n~n*****************~n~n", [Reason]),
264 ok.
265
266%%--------------------------------------------------------------------
267%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}.
268%%
269%% @doc
270%% Convert process state when code is changed
271%% @end
272%%--------------------------------------------------------------------
273code_change(_OldVsn, State, _Extra) ->
274 {ok, State}.
275
276%%====================================================================
277%%% Internal functions
278%%====================================================================
Christopher Piro68940292007-10-02 00:35:12 +0000279
280%% how to output
281format(Format, Data) ->
282 io:format(Format, Data).
283
284%% convenience
285sformat(Format, Data) ->
286 thrift_utils:sformat(Format, Data).
287
Christopher Piro6c46f1a2007-10-23 09:47:15 +0000288config(Item) ->
289 thrift:config(Item).
Christopher Piro3b63fe42007-10-19 21:34:31 +0000290
291do_print_crash_report(Report) ->
292 case Report of
293 [[_,_,{error_info, XXX}|_] | _] ->
294 case thrift_utils:first_item(XXX) of
295 tTransportException ->
296 false;
297 _ ->
298 true
299 end;
300 _ ->
301 true
302 end.