error_logger_lager_h.erl 10 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
%% Copyright (c) 2011 Basho Technologies, Inc.  All Rights Reserved.
%%
%% This file is provided to you under the Apache License,
%% Version 2.0 (the "License"); you may not use this file
%% except in compliance with the License.  You may obtain
%% a copy of the License at
%%
%%   http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing,
%% software distributed under the License is distributed on an
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
%% KIND, either express or implied.  See the License for the
%% specific language governing permissions and limitations
%% under the License.

Andrew Thompson's avatar
Andrew Thompson committed
17
18
19
20
21
22
23
%% @doc A error_logger backend for redirecting events into lager.
%% Error messages and crash logs are also optionally written to a crash log.

%% @see lager_crash_log

%% @private

24
25
-module(error_logger_lager_h).

26
27
-include("lager.hrl").

28
29
30
31
32
-behaviour(gen_event).

-export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2,
        code_change/3]).

33
34
-export([format_reason/1]).

35
-define(LOG(Level, Pid, Msg),
36
    case ?SHOULD_LOG(Level) of
37
38
39
40
41
42
        true ->
            lager:log(Level, Pid, Msg);
        _ -> ok
    end).

-define(LOG(Level, Pid, Fmt, Args),
43
    case ?SHOULD_LOG(Level) of
44
45
46
47
48
        true ->
            lager:log(Level, Pid, Fmt, Args);
        _ -> ok
    end).

49
50
51
-define(CRASH_LOG(Event),
    gen_server:cast(lager_crash_log, {log, Event})).

Andrew Thompson's avatar
Andrew Thompson committed
52
-spec init(any()) -> {ok, {}}.
53
54
55
56
57
58
59
60
61
init(_) ->
    {ok, {}}.

handle_call(_Request, State) ->
    {ok, ok, State}.

handle_event(Event, State) ->
    case Event of
        {error, _GL, {Pid, Fmt, Args}} ->
62
63
64
65
            case Fmt of
                "** Generic server "++_ ->
                    %% gen_server terminate
                    [Name, _Msg, _State, Reason] = Args,
66
                    ?CRASH_LOG(Event),
67
                    ?LOG(error, Pid, "gen_server ~w terminated with reason: ~s",
68
69
70
71
                        [Name, format_reason(Reason)]);
                "** State machine "++_ ->
                    %% gen_fsm terminate
                    [Name, _Msg, StateName, _StateData, Reason] = Args,
72
                    ?CRASH_LOG(Event),
73
                    ?LOG(error, Pid, "gen_fsm ~w in state ~w terminated with reason: ~s",
74
75
76
77
                        [Name, StateName, format_reason(Reason)]);
                "** gen_event handler"++_ ->
                    %% gen_event handler terminate
                    [ID, Name, _Msg, _State, Reason] = Args,
78
                    ?CRASH_LOG(Event),
79
                    ?LOG(error, Pid, "gen_event ~w installed in ~w terminated with reason: ~s",
80
81
                        [ID, Name, format_reason(Reason)]);
                _ ->
82
                    ?CRASH_LOG(Event),
83
                    ?LOG(error, Pid, lager:safe_format(Fmt, Args, 4096))
84
            end;
85
        {error_report, _GL, {Pid, std_error, D}} ->
86
            ?CRASH_LOG(Event),
87
            ?LOG(error, Pid, print_silly_list(D));
88
        {error_report, _GL, {Pid, supervisor_report, D}} ->
89
            ?CRASH_LOG(Event),
90
91
92
            case lists:sort(D) of
                [{errorContext, Ctx}, {offender, Off}, {reason, Reason}, {supervisor, Name}] ->
                    Offender = format_offender(Off),
Andrew Thompson's avatar
Andrew Thompson committed
93
94
95
                    ?LOG(error, Pid,
                        "Supervisor ~w had child ~s exit with reason ~s in context ~w",
                        [element(2, Name), Offender, format_reason(Reason), Ctx]);
96
                _ ->
97
                    ?LOG(error, Pid, ["SUPERVISOR REPORT ", print_silly_list(D)])
98
            end;
99
        {error_report, _GL, {Pid, crash_report, [Self, Neighbours]}} ->
100
            ?CRASH_LOG(Event),
101
            ?LOG(error, Pid, ["CRASH REPORT ", format_crash_report(Self, Neighbours)]);
102
        {warning_msg, _GL, {Pid, Fmt, Args}} ->
103
            ?LOG(warning, Pid, lager:safe_format(Fmt, Args, 4096));
104
105
        {warning_report, _GL, {Pid, std_warning, Report}} ->
            ?LOG(warning, Pid, print_silly_list(Report));
106
        {info_msg, _GL, {Pid, Fmt, Args}} ->
107
            ?LOG(info, Pid, lager:safe_format(Fmt, Args, 4096));
108
        {info_report, _GL, {Pid, std_info, D}} when is_list(D) ->
109
110
111
            Details = lists:sort(D),
            case Details of
                [{application, App}, {exited, Reason}, {type, _Type}] ->
Andrew Thompson's avatar
Andrew Thompson committed
112
113
                    ?LOG(info, Pid, "Application ~w exited with reason: ~w",
                        [App, Reason]);
114
                _ ->
115
                    ?LOG(info, Pid, print_silly_list(D))
116
            end;
117
118
        {info_report, _GL, {Pid, std_info, D}} ->
            ?LOG(info, Pid, "~w", [D]);
119
120
121
122
        {info_report, _GL, {P, progress, D}} ->
            Details = lists:sort(D),
            case Details of
                [{application, App}, {started_at, Node}] ->
123
                    ?LOG(info, P, "Application ~w started on node ~w",
124
125
126
127
                        [App, Node]);
                [{started, Started}, {supervisor, Name}] ->
                    MFA = format_mfa(proplists:get_value(mfargs, Started)),
                    Pid = proplists:get_value(pid, Started),
Andrew Thompson's avatar
Andrew Thompson committed
128
129
                    ?LOG(debug, P, "Supervisor ~w started ~s at pid ~w",
                        [element(2, Name), MFA, Pid]);
130
                _ ->
131
                    ?LOG(info, P, ["PROGRESS REPORT ", print_silly_list(D)])
132
133
            end;
        _ ->
134
            ?LOG(warning, self(), "Unexpected error_logger event ~w", [Event])
135
136
137
138
139
140
141
142
143
144
145
146
147
148
    end,
    {ok, State}.

handle_info(_Info, State) ->
    {ok, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%% internal functions

149
format_crash_report(Report, Neighbours) ->
150
151
    Name = proplists:get_value(registered_name, Report, proplists:get_value(pid, Report)),
    {_Class, Reason, _Trace} = proplists:get_value(error_info, Report),
Andrew Thompson's avatar
Andrew Thompson committed
152
153
    io_lib:format("Process ~w with ~w neighbours crashed with reason: ~s",
        [Name, length(Neighbours), format_reason(Reason)]).
154
155
156
157
158

format_offender(Off) ->
    case proplists:get_value(name, Off) of
        undefined ->
            %% supervisor_bridge
Andrew Thompson's avatar
Andrew Thompson committed
159
160
            io_lib:format("at module ~w at ~w",
                [proplists:get_value(mod, Off), proplists:get_value(pid, Off)]);
161
162
163
        Name ->
            %% regular supervisor
            MFA = format_mfa(proplists:get_value(mfargs, Off)),
Andrew Thompson's avatar
Andrew Thompson committed
164
165
            io_lib:format("~w started with ~s at ~w",
                [Name, MFA, proplists:get_value(pid, Off)])
166
167
    end.

Andrew Thompson's avatar
Andrew Thompson committed
168
format_reason({'function not exported', [{M, F, A},MFA|_]}) ->
Andrew Thompson's avatar
Andrew Thompson committed
169
170
    ["call to undefined function ", format_mfa({M, F, length(A)}),
        " from ", format_mfa(MFA)];
171
format_reason({undef, [MFA|_]}) ->
172
    ["call to undefined function ", format_mfa(MFA)];
173
174
format_reason({bad_return_value, Val}) ->
    io_lib:format("bad return value: ~w", [Val]);
175
format_reason({{case_clause, Val}, [MFA|_]}) ->
176
    [io_lib:format("no case clause matching ~w in ", [Val]), format_mfa(MFA)];
177
format_reason({function_clause, [MFA|_]}) ->
178
    ["no function clause matching ", format_mfa(MFA)];
179
format_reason({if_clause, [MFA|_]}) ->
180
    ["no true branch found while evaluating if expression in ", format_mfa(MFA)];
181
format_reason({{try_clause, Val}, [MFA|_]}) ->
182
    [io_lib:format("no try clause matching ~w in ", [Val]), format_mfa(MFA)]; 
183
format_reason({badarith, [MFA|_]}) ->
184
    ["bad arithmetic expression in ", format_mfa(MFA)];
185
format_reason({{badmatch, Val}, [MFA|_]}) ->
186
    [io_lib:format("no match of right hand value ~w in ", [Val]), format_mfa(MFA)];
187
188
189
190
191
192
193
194
195
196
197
198
format_reason({emfile, _Trace}) ->
    "maximum number of file descriptors exhausted, check ulimit -n";
format_reason({system_limit, [{M, F, _}|_] = Trace}) ->
    Limit = case {M, F} of
        {erlang, open_port} ->
            "maximum number of ports exceeded";
        {erlang, spawn} ->
            "maximum number of processes exceeded";
        {erlang, spawn_opt} ->
            "maximum number of processes exceeded";
        {erlang, list_to_atom} ->
            "tried to create an atom larger than 255, or maximum atom count exceeded";
199
200
        {ets, new} ->
            "maximum number of ETS tables exceeded";
201
        _ ->
202
            {Str, _} = lager_trunc_io:print(Trace, 500),
203
204
205
            Str
    end,
    ["system limit: ", Limit];
Andrew Thompson's avatar
Andrew Thompson committed
206
format_reason({badarg, [MFA,MFA2|_]}) ->
207
    case MFA of
Andrew Thompson's avatar
Andrew Thompson committed
208
        {_M, _F, A} when is_list(A) ->
209
            ["bad argument in call to ", format_mfa(MFA), " in ", format_mfa(MFA2)];
210
211
        _ ->
            %% seems to be generated by a bad call to a BIF
212
            ["bad argument in ", format_mfa(MFA)]
213
214
215
    end;
format_reason({{badarity, {Fun, Args}}, [MFA|_]}) ->
    {arity, Arity} = lists:keyfind(arity, 1, erlang:fun_info(Fun)),
Andrew Thompson's avatar
Andrew Thompson committed
216
217
    [io_lib:format("fun called with wrong arity of ~w instead of ~w in ",
            [length(Args), Arity]), format_mfa(MFA)];
218
format_reason({noproc, MFA}) ->
219
    ["no such process or port in call to ", format_mfa(MFA)];
220
format_reason({{badfun, Term}, [MFA|_]}) ->
221
    [io_lib:format("bad function ~w in ", [Term]), format_mfa(MFA)];
222
format_reason(Reason) ->
223
    {Str, _} = lager_trunc_io:print(Reason, 500),
224
225
226
    Str.

format_mfa({M, F, A}) when is_list(A) ->
227
    io_lib:format("~w:~w("++format_args(A, [])++")", [M, F | A]);
228
229
format_mfa({M, F, A}) when is_integer(A) ->
    io_lib:format("~w:~w/~w", [M, F, A]);
230
format_mfa(Other) ->
231
232
    io_lib:format("~w", [Other]).

233
234
235
236
237
238
239
240
241
242
243
244
format_args([], Acc) ->
    string:join(lists:reverse(Acc), ", ");
format_args([H|T], Acc) when is_list(H) ->
    case lager_stdlib:string_p(H) of
        true ->
            format_args(T, ["\"~s\""|Acc]);
        _ ->
            format_args(T, ["~w"|Acc])
    end;
format_args([_|T], Acc) ->
    format_args(T, ["~w"|Acc]).

245
print_silly_list(L) when is_list(L) ->
246
    case lager_stdlib:string_p(L) of
247
248
249
250
        true ->
            lager_trunc_io:format("~s", [L], 4096);
        _ ->
            print_silly_list(L, [], [])
251
252
    end;
print_silly_list(L) ->
253
254
    {Str, _} = lager_trunc_io:print(L, 4096),
    Str.
255
256

print_silly_list([], Fmt, Acc) ->
257
258
    lager_trunc_io:format(string:join(lists:reverse(Fmt), ", "),
        lists:reverse(Acc), 4096);
259
print_silly_list([{K,V}|T], Fmt, Acc) ->
260
    print_silly_list(T, ["~w: ~w" | Fmt], [V, K | Acc]);
261
print_silly_list([H|T], Fmt, Acc) ->
262
    print_silly_list(T, ["~w" | Fmt], [H | Acc]).