blob: 31a71b99509fcc23facb6d4c1e8dc7181378780f [file] [log] [blame]
Christopher Piro5b3a8f72007-08-01 22:27:37 +00001%%% Copyright (c) 2007- Facebook
2%%% Distributed under the Thrift Software License
Christopher Piroc2e37c72007-11-15 06:26:30 +00003%%%
Christopher Piro5b3a8f72007-08-01 22:27:37 +00004%%% 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
Christopher Piroc2e37c72007-11-15 06:26:30 +000021-export([init/1, handle_event/2, handle_call/2,
Christopher Piro5b3a8f72007-08-01 22:27:37 +000022 handle_info/2, terminate/2, code_change/3]).
23
Christopher Piro6c46f1a2007-10-23 09:47:15 +000024-export([install/0]).
25
Christopher Piroc2e37c72007-11-15 06:26:30 +000026%%
27
Christopher Piro6c46f1a2007-10-23 09:47:15 +000028-record(state, {}).
Christopher Piro5b3a8f72007-08-01 22:27:37 +000029
Christopher Piroc2e37c72007-11-15 06:26:30 +000030-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").
31
32%%
33
Christopher Piro5b3a8f72007-08-01 22:27:37 +000034%% ensure the regular logger is out and ours is in
35install() ->
Christopher Piro5b3a8f72007-08-01 22:27:37 +000036 %% remove loggers
Christopher Piro68940292007-10-02 00:35:12 +000037 io:format("starting logger~n"),
Christopher Piro5b3a8f72007-08-01 22:27:37 +000038 lists:foreach(fun(Logger) ->
39 case Logger of
40 _ -> gen_event:delete_handler(error_logger, Logger, normal)
41 end end,
42 gen_event:which_handlers(error_logger)),
43
44 %% TODO(cpiro): sasl someday?
45 %% gen_event:add_handler(error_logger, sasl_report_file_h, {LogFile, all}),
Christopher Piro5b3a8f72007-08-01 22:27:37 +000046
Christopher Piro68940292007-10-02 00:35:12 +000047 gen_event:add_handler(error_logger, ?MODULE, []),
Christopher Piro5b3a8f72007-08-01 22:27:37 +000048
Christopher Piro68940292007-10-02 00:35:12 +000049 ok.
50
Christopher Piro5b3a8f72007-08-01 22:27:37 +000051%%====================================================================
52%% gen_event callbacks
53%%====================================================================
54%%--------------------------------------------------------------------
55%% @spec init(Args) -> {ok, State}.
Christopher Piroc2e37c72007-11-15 06:26:30 +000056%%
57%% @doc
Christopher Piro5b3a8f72007-08-01 22:27:37 +000058%% Whenever a new event handler is added to an event manager,
59%% this function is called to initialize the event handler.
Christopher Piroc2e37c72007-11-15 06:26:30 +000060%% @end
Christopher Piro5b3a8f72007-08-01 22:27:37 +000061%%--------------------------------------------------------------------
62init([]) ->
Christopher Piro6c46f1a2007-10-23 09:47:15 +000063 State = #state{},
Christopher Piro5b3a8f72007-08-01 22:27:37 +000064 {ok, State}.
65
66%%--------------------------------------------------------------------
67%% @spec handle_event(Event, State) -> {ok, State} |
68%% {swap_handler, Args1, State1, Mod2, Args2} |
69%% remove_handler.
Christopher Piroc2e37c72007-11-15 06:26:30 +000070%%
71%% @doc
Christopher Piro5b3a8f72007-08-01 22:27:37 +000072%% Whenever an event manager receives an event sent using
73%% gen_event:notify/2 or gen_event:sync_notify/2, this function is called for
Christopher Piroc2e37c72007-11-15 06:26:30 +000074%% each installed event handler to handle the event.
75%% @end
Christopher Piro5b3a8f72007-08-01 22:27:37 +000076%%--------------------------------------------------------------------
77handle_event2(Symbol, Pid, Type, Message, State) -> % Message must be a string
78 {ok, MessageSafe, NL} = regexp:gsub(Message, "[\n]+", " "), % collapse whitespace to one space
79
80 Type1 =
Christopher Piroc2e37c72007-11-15 06:26:30 +000081 case Type of
82 "" -> "";
83 _ -> sformat("~p ", [Type])
84 end,
Christopher Piro5b3a8f72007-08-01 22:27:37 +000085
Christopher Piro3b63fe42007-10-19 21:34:31 +000086 Banner =
Christopher Piro6c46f1a2007-10-23 09:47:15 +000087 case config(show_pid) of
Christopher Piro3b63fe42007-10-19 21:34:31 +000088 true ->
89 sformat("~s ~s ~s", [Symbol, Pid, Type1]);
90 false ->
91 sformat("~s~i ~s", [Symbol, Pid, Type1])
92 end,
93 BannerLen = length(Banner),
94
95 %% there's no way to see if Message is a string? just try
96 Output = sformat("~s", [Message]),
97 OutputSafe = sformat("~s", [MessageSafe]),
Christopher Piro5b3a8f72007-08-01 22:27:37 +000098
99 Length =
Christopher Piroc2e37c72007-11-15 06:26:30 +0000100 case (length(OutputSafe) + BannerLen) < config(term_width) of
101 true -> short;
102 false -> long
103 end,
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000104
105 OneLine =
Christopher Piroc2e37c72007-11-15 06:26:30 +0000106 case NL == 0 of
107 true -> oneliner;
108 false -> multiline
109 end,
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000110
Christopher Piro6c46f1a2007-10-23 09:47:15 +0000111 case { config(force_one_line), Length, OneLine } of
Christopher Piroc2e37c72007-11-15 06:26:30 +0000112 %% one line and short ... print as is
113 {_, short, oneliner} ->
114 format("~s~s~n", [Banner, OutputSafe]);
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000115
Christopher Piroc2e37c72007-11-15 06:26:30 +0000116 %% too long ... squash to one
117 {true, long, _} ->
118 O = Banner ++ OutputSafe,
119 Format = sformat("~~~ps >~n", [config(term_width)-2]), % e.g. "~80s >~n"
120 format(Format, [O]);
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000121
Christopher Piroc2e37c72007-11-15 06:26:30 +0000122 %% short but multiline... collapse to one
123 {true, short, multiline} ->
124 format("~s~s~n", [Banner, OutputSafe]);
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000125
Christopher Piroc2e37c72007-11-15 06:26:30 +0000126 %% just print it
127 _ ->
128 format("~s~n~s~n~n", [Banner, Output])
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000129 end.
130
131%%
Christopher Piro524c3ec2007-10-13 05:15:33 +0000132
Christopher Piroefd5eec2007-10-02 01:33:37 +0000133handle_event1({What, _Gleader, {Ref, Format, Data}}, State) when is_list(Format) ->
Christopher Piroc2e37c72007-11-15 06:26:30 +0000134 Symbol = symbol(What),
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000135
Christopher Piro524c3ec2007-10-13 05:15:33 +0000136 case {Format, Data} of
Christopher Piroc2e37c72007-11-15 06:26:30 +0000137 {?GS_TERM_FORMAT, [Ref, LastMessage, Obj, Reason]} ->
138 %% TODO: move as much logic as possible out of thrift_logger
139 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
Christopher Piroc2e37c72007-11-15 06:26:30 +0000154 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?
160 handle_event2(Symbol, Ref, "", Message, State)
161 end;
162 {?GS_TERM_FORMAT, _Dta} ->
163 Message = sformat("DATA DIDN'T MATCH: ~p~n", [Data]) ++ sformat(Format, Data),
164 handle_event2(Symbol, Ref, "", Message, State);
165 {_, _} ->
166 case lists:member(Format, config(omit_fmt)) of
167 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) ->
Christopher Piroc2e37c72007-11-15 06:26:30 +0000177 Symbol = symbol(What),
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000178
179 case Type of
Christopher Piro3b63fe42007-10-19 21:34:31 +0000180 crash_report ->
Christopher Piroc2e37c72007-11-15 06:26:30 +0000181 print_crash_report(Report);
Christopher Piro3b63fe42007-10-19 21:34:31 +0000182 progress ->
Christopher Piroc2e37c72007-11-15 06:26:30 +0000183 ok;
184 _ ->
185 Message = sformat("|| ~s", [oop:inspect(Report)]),
186 handle_event2(Symbol, Pid, Type, Message, State)
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000187 end,
188 {ok, State};
189
190handle_event1(_Event, State) ->
191 handle_event2("??", "<?.?.?>", "", _Event, State),
192 {ok, State}.
193
194handle_event(Event, State) ->
195 try
Christopher Piroc2e37c72007-11-15 06:26:30 +0000196 handle_event1(Event, State)
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000197 catch
Christopher Piroc2e37c72007-11-15 06:26:30 +0000198 _:E ->
199 format("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~n error logger error:~n ~p~n Event = ~p~n State = ~p~n ~p~n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~n",
200 [E, Event, State, erlang:get_stacktrace()]),
201 {ok, State}
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000202 end.
203
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000204%%--------------------------------------------------------------------
205%% @spec handle_call(Request, State) -> {ok, Reply, State} |
Christopher Piro68940292007-10-02 00:35:12 +0000206%% {swap_handler, Reply, Args1, State1,
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000207%% Mod2, Args2} |
208%% {remove_handler, Reply}.
Christopher Piroc2e37c72007-11-15 06:26:30 +0000209%%
210%% @doc
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000211%% Whenever an event manager receives a request sent using
Christopher Piroc2e37c72007-11-15 06:26:30 +0000212%% gen_event:call/3,4, this function is called for the specified event
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000213%% handler to handle the request.
Christopher Piroc2e37c72007-11-15 06:26:30 +0000214%% @end
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000215%%--------------------------------------------------------------------
216handle_call(_Request, State) ->
217 Reply = ok,
218 {ok, Reply, State}.
219
220%%--------------------------------------------------------------------
221%% @spec handle_info(Info, State) -> {ok, State} |
222%% {swap_handler, Args1, State1, Mod2, Args2} |
223%% remove_handler.
Christopher Piroc2e37c72007-11-15 06:26:30 +0000224%%
225%% @doc
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000226%% This function is called for each installed event handler when
227%% an event manager receives any other message than an event or a synchronous
228%% request (or a system message).
Christopher Piroc2e37c72007-11-15 06:26:30 +0000229%% @end
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000230%%--------------------------------------------------------------------
231handle_info(_Info, State) ->
232 {ok, State}.
233
234%%--------------------------------------------------------------------
235%% @spec terminate(Reason, State) -> void().
Christopher Piroc2e37c72007-11-15 06:26:30 +0000236%%
237%% @doc
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000238%% Whenever an event handler is deleted from an event manager,
Christopher Piroc2e37c72007-11-15 06:26:30 +0000239%% this function is called. It should be the opposite of Module:init/1 and
240%% do any necessary cleaning up.
241%% @end
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000242%%--------------------------------------------------------------------
243terminate(normal, _State) ->
244 ok;
245terminate(Reason, _State) ->
246 format("*****************~n~n frick, error logger terminating: ~p~n~n*****************~n~n", [Reason]),
247 ok.
248
249%%--------------------------------------------------------------------
Christopher Piroc2e37c72007-11-15 06:26:30 +0000250%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}.
251%%
252%% @doc
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000253%% Convert process state when code is changed
Christopher Piroc2e37c72007-11-15 06:26:30 +0000254%% @end
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000255%%--------------------------------------------------------------------
256code_change(_OldVsn, State, _Extra) ->
257 {ok, State}.
258
259%%====================================================================
260%%% Internal functions
261%%====================================================================
Christopher Piro68940292007-10-02 00:35:12 +0000262
263%% how to output
264format(Format, Data) ->
265 io:format(Format, Data).
266
267%% convenience
268sformat(Format, Data) ->
269 thrift_utils:sformat(Format, Data).
270
Christopher Piro6c46f1a2007-10-23 09:47:15 +0000271config(Item) ->
272 thrift:config(Item).
Christopher Piro3b63fe42007-10-19 21:34:31 +0000273
Christopher Piroc2e37c72007-11-15 06:26:30 +0000274symbol(error_report) -> "!!";
275symbol(warning_report) -> "**";
276symbol(info_report) -> "..";
277symbol(_Else) -> "??".
278
279print_crash_report(Report) ->
Christopher Piro3b63fe42007-10-19 21:34:31 +0000280 case Report of
Christopher Piroc2e37c72007-11-15 06:26:30 +0000281 [[_,_,{error_info, XX}|_] | _] ->
282 case thrift_utils:first_item(XX) of
Christopher Piro3b63fe42007-10-19 21:34:31 +0000283 tTransportException ->
Christopher Piroc2e37c72007-11-15 06:26:30 +0000284 ok;
Christopher Piro3b63fe42007-10-19 21:34:31 +0000285 _ ->
Christopher Piroc2e37c72007-11-15 06:26:30 +0000286 io:format("~~~~ crash report: ~P~n", [XX, 2])
Christopher Piro3b63fe42007-10-19 21:34:31 +0000287 end;
288 _ ->
Christopher Piroc2e37c72007-11-15 06:26:30 +0000289 io:format("~~~~ crash report (?): ~p~n", [Report])
Christopher Piro3b63fe42007-10-19 21:34:31 +0000290 end.