blob: 12aa05964ddb88a4096edba2264b8beabd2ee7cc [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 Piro42fe4b92007-10-02 00:24:07 +000011-include("thrift_logger.hrl").
Christopher Piro5b3a8f72007-08-01 22:27:37 +000012
Christopher Pirofa0c8572007-08-11 01:15:57 +000013%% TODO(cpiro): either
14%% make exceptions know whether they need to be displayed
15%% or not exit with tExecptions for non-errors
16%% or "register" tExceptions with the logger (I LIKE!)
17%% ... we shouldn't need to build any specifics in here
18-include("thrift.hrl").
19-include("oop.hrl").
20-include("transport/tTransportException.hrl").
21
Christopher Piro5b3a8f72007-08-01 22:27:37 +000022%% gen_event callbacks
23-export([init/1, handle_event/2, handle_call/2,
24 handle_info/2, terminate/2, code_change/3]).
25
Christopher Piro42fe4b92007-10-02 00:24:07 +000026-export([install/0, install/1]).
Christopher Piro5b3a8f72007-08-01 22:27:37 +000027
28%% ensure the regular logger is out and ours is in
29install() ->
30 install([]).
Christopher Piro42fe4b92007-10-02 00:24:07 +000031install(Args) ->
Christopher Piro5b3a8f72007-08-01 22:27:37 +000032 %% remove loggers
33 lists:foreach(fun(Logger) ->
34 case Logger of
35 _ -> gen_event:delete_handler(error_logger, Logger, normal)
36 end end,
37 gen_event:which_handlers(error_logger)),
38
39 %% TODO(cpiro): sasl someday?
40 %% gen_event:add_handler(error_logger, sasl_report_file_h, {LogFile, all}),
Christopher Piro42fe4b92007-10-02 00:24:07 +000041 gen_event:add_handler(error_logger, ?MODULE, Args).
Christopher Piro5b3a8f72007-08-01 22:27:37 +000042
Christopher Piro42fe4b92007-10-02 00:24:07 +000043%% how to output
44format(Format, Data) ->
45 io:format(Format, Data).
Christopher Piro5b3a8f72007-08-01 22:27:37 +000046
Christopher Piro42fe4b92007-10-02 00:24:07 +000047%% convenience
48sformat(Format, Data) ->
49 thrift_utils:sformat(Format, Data).
Christopher Piro5b3a8f72007-08-01 22:27:37 +000050
51%%====================================================================
52%% gen_event callbacks
53%%====================================================================
54%%--------------------------------------------------------------------
55%% @spec init(Args) -> {ok, State}.
56%%
57%% @doc
58%% Whenever a new event handler is added to an event manager,
59%% this function is called to initialize the event handler.
60%% @end
61%%--------------------------------------------------------------------
62init([]) ->
Christopher Piro42fe4b92007-10-02 00:24:07 +000063 {ok, #thrift_logger_state{
64 term_width = 110,
65 force_one_line = true,
66 omit = [oop_new], % req_processed
67 gen_server_messages = false,
68 lookup = true
69 }};
Christopher Piro5b3a8f72007-08-01 22:27:37 +000070
Christopher Piro42fe4b92007-10-02 00:24:07 +000071init([State]) ->
Christopher Piro5b3a8f72007-08-01 22:27:37 +000072 {ok, State}.
73
74%%--------------------------------------------------------------------
75%% @spec handle_event(Event, State) -> {ok, State} |
76%% {swap_handler, Args1, State1, Mod2, Args2} |
77%% remove_handler.
78%%
79%% @doc
80%% Whenever an event manager receives an event sent using
81%% gen_event:notify/2 or gen_event:sync_notify/2, this function is called for
82%% each installed event handler to handle the event.
83%% @end
84%%--------------------------------------------------------------------
85handle_event2(Symbol, Pid, Type, Message, State) -> % Message must be a string
86 {ok, MessageSafe, NL} = regexp:gsub(Message, "[\n]+", " "), % collapse whitespace to one space
87
88 Type1 =
89 case Type of
90 "" -> "";
91 _ -> sformat("~p ", [Type])
92 end,
93
94 Banner = sformat("~s ~p ~s", [Symbol, Pid, Type1]),
95 BannerLen = length(Banner),
96 {Output, OutputSafe} =
97 try %% there's no way to see if Message is a string? just try
98 {sformat("~s", [Message]),
99 sformat("~s", [MessageSafe])}
100 catch X -> why_doesnt_this_work
101 end,
102
103 Length =
Christopher Piro42fe4b92007-10-02 00:24:07 +0000104 case (length(OutputSafe) + BannerLen) < State#thrift_logger_state.term_width of
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000105 true -> short;
106 false -> long
107 end,
108
109 OneLine =
110 case NL == 0 of
111 true -> oneliner;
112 false -> multiline
113 end,
114
Christopher Piro42fe4b92007-10-02 00:24:07 +0000115 case { State#thrift_logger_state.force_one_line, Length, OneLine } of
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000116 %% one line and short ... print as is
117 {_, short, oneliner} ->
118 format("~s~s~n", [Banner, OutputSafe]);
119
120 %% too long ... squash to one
121 {true, long, _} ->
122 O = Banner ++ OutputSafe,
Christopher Piro42fe4b92007-10-02 00:24:07 +0000123 Format = sformat("~~~ps >~n", [State#thrift_logger_state.term_width-2]), % e.g. "~80s >~n"
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000124 format(Format, [O]);
125
126 %% short but multiline... collapse to one
127 {true, short, multiline} ->
128 format("~s~s~n", [Banner, OutputSafe]);
129
130 %% just print it
131 _ ->
132 format("~s~n~s~n~n", [Banner, Output])
133 end.
134
135%%
136handle_event1({What, _Gleader, {Pid, Format, Data}}, State) when is_list(Format) ->
137 Symbol = case What of
138 error -> "!!";
139 warning_msg -> "**";
140 info_msg -> "..";
141 _Else -> "??"
142 end,
143
144 case Format of
145 "** Generic server ~p terminating \n** Last message in was ~p~n** When Server state == ~p~n** Reason for termination == ~n** ~p~n" ->
146 %% v- Pid is a pattern match, not a bind
Christopher Piro42fe4b92007-10-02 00:24:07 +0000147 [Pid, LastMessage, Obj, Reason] = Data,
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000148
149 %% TODO: move as much logic as possible out of thrift_logger
Christopher Pirofa0c8572007-08-11 01:15:57 +0000150 Ignore =
151 begin
152 is_tuple(Reason) andalso
153 size(Reason) >= 1 andalso element(1, Reason) == timeout
154 end
155 orelse
156 begin
157 case thrift_utils:unnest_record(Reason, tTransportException) of
158 error -> false;
159 {ok, TTE} ->
160 oop:get(TTE, type) == ?tTransportException_NOT_OPEN andalso
161 oop:get(TTE, message) == "in tSocket:read/2: gen_tcp:recv"
162 end
163 end,
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000164
165 case Ignore of
166 true ->
167 ok;
168 false ->
169 Format1 = "** gen_server terminating in message ~p~n** State = ~s~n** Reason = ~s~n",
170 Message = sformat(Format1, [LastMessage, oop:inspect(Obj), oop:inspect(Reason)]), %% TODO(cpiro): hope Reason is an object?
Christopher Piro42fe4b92007-10-02 00:24:07 +0000171 handle_event2(Symbol, Pid, "", Message, State)
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000172 end;
173 _ ->
174 Message = sformat(Format, Data),
Christopher Piro42fe4b92007-10-02 00:24:07 +0000175 handle_event2(Symbol, Pid, "", Message, State)
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000176 end,
177 {ok, State};
178
179handle_event1({What, _Gleader, {Pid, Type, Report}}, State) ->
180 Symbol = case What of
181 error_report -> "!!";
182 warning_report -> "**";
183 info_report -> "..";
184 _Else -> "??"
185 end,
186
187 case Type of
188 {thrift_info, TI} ->
189 %% should we show it?
Christopher Piro42fe4b92007-10-02 00:24:07 +0000190 case not lists:member(TI, State#thrift_logger_state.omit) of
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000191 true ->
192 Message = handle_thrift_info(TI, Report, State),
193 handle_event2(Symbol, Pid, "", Message, State);
194 false ->
195 ok
196 end;
197 crash_report ->
198 %% [Cruft|_] = Report, %%
199 %% {value, {_, Reason}} = lists:keysearch(error_info, 1, Cruft), %%
200 %% {value, {_, {_, _, [_,_,_,_, Obj, []]}}} = lists:keysearch(initial_call, 1, Cruft), %%
201 %% sformat("state == ~s~nreason ==~s", [oop:inspect(Obj), oop:inspect(Reason)]), %%
202 ok;
203 progress ->
204 ok;
205
206 _ ->
207 Message = sformat("|| ~s", [oop:inspect(Report)]),
208 handle_event2(Symbol, Pid, Type, Message, State)
209 end,
210 {ok, State};
211
212handle_event1(_Event, State) ->
213 handle_event2("??", "<?.?.?>", "", _Event, State),
214 {ok, State}.
215
216handle_event(Event, State) ->
217 try
218 handle_event1(Event, State)
219 catch
220 _:E ->
221 format("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~n error logger error:~n ~p~n Event = ~p~n State = ~p~n ~p~n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~n",
222 [E, Event, State, erlang:get_stacktrace()]),
223 {ok, State}
224 end.
225
226%% thrift info handlers
227handle_thrift_info(oop_new, {Args, Class, Object}, State) ->
228 %% arg Class can't come first! Then it'd look like a Class object
229 L = io_lib:format("~p:new(~s) = ~s", [Class, thrift_utils:unbrack(Args), oop:inspect(Object)]),
230 lists:flatten(L);
231
232handle_thrift_info(server_listening, {Port}, State) ->
233 sformat("server listening on port ~p", [Port]);
234
235handle_thrift_info(req_processed, {Value}, State) ->
236 sformat("request: ~p", [Value]);
237
238handle_thrift_info(conn_accepted, {AddrString}, State) ->
239 sformat("connection accepted from ~s", [AddrString]);
240
Christopher Piroee598842007-08-03 23:34:55 +0000241handle_thrift_info(conn_timeout, {AddrString}, State) ->
242 sformat("connection timed out from ~s", [AddrString]);
243
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000244handle_thrift_info(conn_closed, {AddrString}, State) ->
245 sformat("connection closed from ~s", [AddrString]);
246
247handle_thrift_info(Else, Report, State) ->
248 sformat("~p ~s", [Else, oop:inspect(Report)]).
249
250%%--------------------------------------------------------------------
251%% @spec handle_call(Request, State) -> {ok, Reply, State} |
Christopher Piro42fe4b92007-10-02 00:24:07 +0000252%% {swap_handler, Reply, Args1, State1,
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000253%% Mod2, Args2} |
254%% {remove_handler, Reply}.
255%%
256%% @doc
257%% Whenever an event manager receives a request sent using
258%% gen_event:call/3,4, this function is called for the specified event
259%% handler to handle the request.
260%% @end
261%%--------------------------------------------------------------------
262handle_call(_Request, State) ->
263 Reply = ok,
264 {ok, Reply, State}.
265
266%%--------------------------------------------------------------------
267%% @spec handle_info(Info, State) -> {ok, State} |
268%% {swap_handler, Args1, State1, Mod2, Args2} |
269%% remove_handler.
270%%
271%% @doc
272%% This function is called for each installed event handler when
273%% an event manager receives any other message than an event or a synchronous
274%% request (or a system message).
275%% @end
276%%--------------------------------------------------------------------
277handle_info(_Info, State) ->
278 {ok, State}.
279
280%%--------------------------------------------------------------------
281%% @spec terminate(Reason, State) -> void().
282%%
283%% @doc
284%% Whenever an event handler is deleted from an event manager,
285%% this function is called. It should be the opposite of Module:init/1 and
286%% do any necessary cleaning up.
287%% @end
288%%--------------------------------------------------------------------
289terminate(normal, _State) ->
290 ok;
291terminate(Reason, _State) ->
292 format("*****************~n~n frick, error logger terminating: ~p~n~n*****************~n~n", [Reason]),
293 ok.
294
295%%--------------------------------------------------------------------
296%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}.
297%%
298%% @doc
299%% Convert process state when code is changed
300%% @end
301%%--------------------------------------------------------------------
302code_change(_OldVsn, State, _Extra) ->
303 {ok, State}.
304
305%%====================================================================
306%%% Internal functions
307%%====================================================================