blob: 0d0b876c3c87d401406bd2df67eeedec9a7643c9 [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 Piro10522a72007-11-15 06:26:31 +0000134 Symbol =
135 case What of
136 error -> "!!";
137 warning_msg -> "**";
138 info_msg -> "..";
139 _Else -> "??"
140 end,
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000141
Christopher Piro524c3ec2007-10-13 05:15:33 +0000142 case {Format, Data} of
Christopher Piroc2e37c72007-11-15 06:26:30 +0000143 {?GS_TERM_FORMAT, [Ref, LastMessage, Obj, Reason]} ->
144 %% TODO: move as much logic as possible out of thrift_logger
145 Ignore =
146 begin
147 is_tuple(Reason) andalso
148 size(Reason) >= 1 andalso element(1, Reason) == timeout
149 end
150 orelse
151 begin
152 case thrift_utils:unnest_record(Reason, tTransportException) of
153 error -> false;
154 {ok, TTE} ->
155 oop:get(TTE, type) == ?tTransportException_NOT_OPEN andalso
156 oop:get(TTE, message) == "in tSocket:read/2: gen_tcp:recv"
157 end
158 end,
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000159
Christopher Piroc2e37c72007-11-15 06:26:30 +0000160 case Ignore of
161 true ->
162 ok;
163 false ->
164 Format1 = "** gen_server terminating in message ~p~n** State = ~s~n** Reason = ~s~n",
165 Message = sformat(Format1, [LastMessage, oop:inspect(Obj), oop:inspect(Reason)]), %% TODO(cpiro): hope Reason is an object?
166 handle_event2(Symbol, Ref, "", Message, State)
167 end;
168 {?GS_TERM_FORMAT, _Dta} ->
169 Message = sformat("DATA DIDN'T MATCH: ~p~n", [Data]) ++ sformat(Format, Data),
170 handle_event2(Symbol, Ref, "", Message, State);
171 {_, _} ->
172 case lists:member(Format, config(omit_fmt)) of
173 false ->
174 Message = sformat(Format, Data),
175 handle_event2(Symbol, Ref, "", Message, State);
176 true ->
177 ok
178 end
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000179 end,
180 {ok, State};
181
182handle_event1({What, _Gleader, {Pid, Type, Report}}, State) ->
Christopher Piro10522a72007-11-15 06:26:31 +0000183 Symbol =
184 case What of
185 error_report -> "!!";
186 warning_report -> "**";
187 info_report -> "..";
188 _Else -> "??"
189 end,
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000190
191 case Type of
Christopher Piro3b63fe42007-10-19 21:34:31 +0000192 crash_report ->
Christopher Piroc2e37c72007-11-15 06:26:30 +0000193 print_crash_report(Report);
Christopher Piro3b63fe42007-10-19 21:34:31 +0000194 progress ->
Christopher Piroc2e37c72007-11-15 06:26:30 +0000195 ok;
196 _ ->
197 Message = sformat("|| ~s", [oop:inspect(Report)]),
198 handle_event2(Symbol, Pid, Type, Message, State)
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000199 end,
200 {ok, State};
201
202handle_event1(_Event, State) ->
203 handle_event2("??", "<?.?.?>", "", _Event, State),
204 {ok, State}.
205
206handle_event(Event, State) ->
207 try
Christopher Piroc2e37c72007-11-15 06:26:30 +0000208 handle_event1(Event, State)
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000209 catch
Christopher Piroc2e37c72007-11-15 06:26:30 +0000210 _:E ->
211 format("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~n error logger error:~n ~p~n Event = ~p~n State = ~p~n ~p~n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~n",
212 [E, Event, State, erlang:get_stacktrace()]),
213 {ok, State}
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000214 end.
215
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000216%%--------------------------------------------------------------------
217%% @spec handle_call(Request, State) -> {ok, Reply, State} |
Christopher Piro68940292007-10-02 00:35:12 +0000218%% {swap_handler, Reply, Args1, State1,
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000219%% Mod2, Args2} |
220%% {remove_handler, Reply}.
Christopher Piroc2e37c72007-11-15 06:26:30 +0000221%%
222%% @doc
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000223%% Whenever an event manager receives a request sent using
Christopher Piroc2e37c72007-11-15 06:26:30 +0000224%% gen_event:call/3,4, this function is called for the specified event
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000225%% handler to handle the request.
Christopher Piroc2e37c72007-11-15 06:26:30 +0000226%% @end
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000227%%--------------------------------------------------------------------
228handle_call(_Request, State) ->
229 Reply = ok,
230 {ok, Reply, State}.
231
232%%--------------------------------------------------------------------
233%% @spec handle_info(Info, State) -> {ok, State} |
234%% {swap_handler, Args1, State1, Mod2, Args2} |
235%% remove_handler.
Christopher Piroc2e37c72007-11-15 06:26:30 +0000236%%
237%% @doc
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000238%% This function is called for each installed event handler when
239%% an event manager receives any other message than an event or a synchronous
240%% request (or a system message).
Christopher Piroc2e37c72007-11-15 06:26:30 +0000241%% @end
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000242%%--------------------------------------------------------------------
243handle_info(_Info, State) ->
244 {ok, State}.
245
246%%--------------------------------------------------------------------
247%% @spec terminate(Reason, State) -> void().
Christopher Piroc2e37c72007-11-15 06:26:30 +0000248%%
249%% @doc
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000250%% Whenever an event handler is deleted from an event manager,
Christopher Piroc2e37c72007-11-15 06:26:30 +0000251%% this function is called. It should be the opposite of Module:init/1 and
252%% do any necessary cleaning up.
253%% @end
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000254%%--------------------------------------------------------------------
255terminate(normal, _State) ->
256 ok;
257terminate(Reason, _State) ->
258 format("*****************~n~n frick, error logger terminating: ~p~n~n*****************~n~n", [Reason]),
259 ok.
260
261%%--------------------------------------------------------------------
Christopher Piroc2e37c72007-11-15 06:26:30 +0000262%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}.
263%%
264%% @doc
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000265%% Convert process state when code is changed
Christopher Piroc2e37c72007-11-15 06:26:30 +0000266%% @end
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000267%%--------------------------------------------------------------------
268code_change(_OldVsn, State, _Extra) ->
269 {ok, State}.
270
271%%====================================================================
272%%% Internal functions
273%%====================================================================
Christopher Piro68940292007-10-02 00:35:12 +0000274
275%% how to output
276format(Format, Data) ->
277 io:format(Format, Data).
278
279%% convenience
280sformat(Format, Data) ->
281 thrift_utils:sformat(Format, Data).
282
Christopher Piro6c46f1a2007-10-23 09:47:15 +0000283config(Item) ->
284 thrift:config(Item).
Christopher Piro3b63fe42007-10-19 21:34:31 +0000285
Christopher Piroc2e37c72007-11-15 06:26:30 +0000286print_crash_report(Report) ->
Christopher Piro3b63fe42007-10-19 21:34:31 +0000287 case Report of
Christopher Piroc2e37c72007-11-15 06:26:30 +0000288 [[_,_,{error_info, XX}|_] | _] ->
289 case thrift_utils:first_item(XX) of
Christopher Piro3b63fe42007-10-19 21:34:31 +0000290 tTransportException ->
Christopher Piroc2e37c72007-11-15 06:26:30 +0000291 ok;
Christopher Piro3b63fe42007-10-19 21:34:31 +0000292 _ ->
Christopher Piro10522a72007-11-15 06:26:31 +0000293 io:format("~~~~ crash report: ~P~n", [XX, 3])
Christopher Piro3b63fe42007-10-19 21:34:31 +0000294 end;
295 _ ->
Christopher Piroc2e37c72007-11-15 06:26:30 +0000296 io:format("~~~~ crash report (?): ~p~n", [Report])
Christopher Piro3b63fe42007-10-19 21:34:31 +0000297 end.