lager_test_backend.erl 17.9 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
%% 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.

-module(lager_test_backend).

-behaviour(gen_event).

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

Andrew Thompson's avatar
Andrew Thompson committed
24
-record(state, {level, buffer, ignored}).
25
26
27
28
29
30
31
-compile([{parse_transform, lager_transform}]).

-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.

init([Level]) ->
Andrew Thompson's avatar
Andrew Thompson committed
32
    {ok, #state{level=lager_util:level_to_num(Level), buffer=[], ignored=[]}}.
33
34
35

handle_call(count, #state{buffer=Buffer} = State) ->
    {ok, length(Buffer), State};
Andrew Thompson's avatar
Andrew Thompson committed
36
37
handle_call(count_ignored, #state{ignored=Ignored} = State) ->
    {ok, length(Ignored), State};
38
39
handle_call(flush, State) ->
    {ok, ok, State#state{buffer=[], ignored=[]}};
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
handle_call(pop, #state{buffer=Buffer} = State) ->
    case Buffer of
        [] ->
            {ok, undefined, State};
        [H|T] ->
            {ok, H, State#state{buffer=T}}
    end;
handle_call(get_loglevel, #state{level=Level} = State) ->
    {ok, Level, State};
handle_call({set_loglevel, Level}, State) ->
    {ok, ok, State#state{level=lager_util:level_to_num(Level)}};
handle_call(_Request, State) ->
    {ok, ok, State}.

handle_event({log, Level, Time, Message}, #state{level=LogLevel,
        buffer=Buffer} = State) when Level >= LogLevel ->
    {ok, State#state{buffer=Buffer ++ [{Level, Time, Message}]}};
Andrew Thompson's avatar
Andrew Thompson committed
57
58
handle_event({log, Level, Time, Message}, #state{ignored=Ignored} = State) ->
    {ok, State#state{ignored=Ignored ++ [ignored]}};
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
handle_event(_Event, State) ->
    {ok, State}.

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

terminate(_Reason, _State) ->
    ok.

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

-ifdef(TEST).

pop() ->
Andrew Thompson's avatar
Andrew Thompson committed
74
    gen_event:call(lager_event, ?MODULE, pop).
75
76

count() ->
Andrew Thompson's avatar
Andrew Thompson committed
77
    gen_event:call(lager_event, ?MODULE, count).
78

Andrew Thompson's avatar
Andrew Thompson committed
79
count_ignored() ->
Andrew Thompson's avatar
Andrew Thompson committed
80
    gen_event:call(lager_event, ?MODULE, count_ignored).
Andrew Thompson's avatar
Andrew Thompson committed
81

82
83
84
85
86
87
88
89
90
91
92
lager_test_() ->
    {foreach,
        fun setup/0,
        fun cleanup/1,
        [
            {"observe that there is nothing up my sleeve",
                fun() ->
                        ?assertEqual(undefined, pop()),
                        ?assertEqual(0, count())
                end
            },
Andrew Thompson's avatar
Andrew Thompson committed
93
            {"logging works",
94
95
96
97
98
99
100
101
102
103
104
                fun() ->
                        lager:warning("test message"),
                        ?assertEqual(1, count()),
                        {Level, Time, Message}  = pop(),
                        ?assertMatch(Level, lager_util:level_to_num(warning)),
                        [LevelStr, LocStr, MsgStr] = re:split(Message, " ", [{return, list}, {parts, 3}]),
                        ?assertEqual("[warning]", LevelStr),
                        ?assertEqual("test message", MsgStr),
                        ok
                end
            },
Andrew Thompson's avatar
Andrew Thompson committed
105
            {"logging with arguments works",
106
107
108
109
110
111
112
113
114
115
116
                fun() ->
                        lager:warning("test message ~p", [self()]),
                        ?assertEqual(1, count()),
                        {Level, Time, Message}  = pop(),
                        ?assertMatch(Level, lager_util:level_to_num(warning)),
                        [LevelStr, LocStr, MsgStr] = re:split(Message, " ", [{return, list}, {parts, 3}]),
                        ?assertEqual("[warning]", LevelStr),
                        ?assertEqual(lists:flatten(io_lib:format("test message ~p", [self()])), MsgStr),
                        ok
                end
            },
Andrew Thompson's avatar
Andrew Thompson committed
117
            {"logging works from inside a begin/end block",
118
119
120
121
122
123
124
125
126
                fun() ->
                        ?assertEqual(0, count()),
                        begin
                                lager:warning("test message 2")
                        end,
                        ?assertEqual(1, count()),
                        ok
                end
            },
Andrew Thompson's avatar
Andrew Thompson committed
127
            {"logging works from inside a list comprehension",
128
129
130
131
132
133
134
                fun() ->
                        ?assertEqual(0, count()),
                        [lager:warning("test message") || N <- lists:seq(1, 10)],
                        ?assertEqual(10, count()),
                        ok
                end
            },
Andrew Thompson's avatar
Andrew Thompson committed
135
            {"logging works from a begin/end block inside a list comprehension",
136
137
138
139
140
141
142
                fun() ->
                        ?assertEqual(0, count()),
                        [ begin lager:warning("test message") end || N <- lists:seq(1, 10)],
                        ?assertEqual(10, count()),
                        ok
                end
            },
Andrew Thompson's avatar
Andrew Thompson committed
143
            {"logging works from a nested list comprehension",
144
145
146
147
148
149
150
                fun() ->
                        ?assertEqual(0, count()),
                        [ [lager:warning("test message") || N <- lists:seq(1, 10)] ||
                            I <- lists:seq(1, 10)],
                        ?assertEqual(100, count()),
                        ok
                end
Andrew Thompson's avatar
Andrew Thompson committed
151
152
153
154
155
156
157
158
159
160
161
            },
            {"log messages below the threshold are ignored",
                fun() ->
                        ?assertEqual(0, count()),
                        lager:debug("this message will be ignored"),
                        ?assertEqual(0, count()),
                        ?assertEqual(0, count_ignored()),
                        lager_mochiglobal:put(loglevel, 0),
                        lager:debug("this message should be ignored"),
                        ?assertEqual(0, count()),
                        ?assertEqual(1, count_ignored()),
Andrew Thompson's avatar
Andrew Thompson committed
162
                        lager:set_loglevel(?MODULE, debug),
Andrew Thompson's avatar
Andrew Thompson committed
163
164
165
                        lager:debug("this message should be logged"),
                        ?assertEqual(1, count()),
                        ?assertEqual(1, count_ignored()),
Andrew Thompson's avatar
Andrew Thompson committed
166
                        ?assertEqual(debug, lager:get_loglevel(?MODULE)),
Andrew Thompson's avatar
Andrew Thompson committed
167
168
                        ok
                end
169
170
171
172
173
174
            }
        ]
    }.

setup() ->
    application:load(lager),
Andrew Thompson's avatar
Andrew Thompson committed
175
    application:set_env(lager, handlers, [{?MODULE, [info]}]),
176
177
178
179
180
181
    application:start(lager).

cleanup(_) ->
    application:stop(lager),
    application:unload(lager).

182
183
184
185
186

crash(Type) ->
    spawn(fun() -> gen_server:call(crash, Type) end),
    timer:sleep(100).

187
error_logger_redirect_crash_test_() ->
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
    {foreach,
        fun() ->
                application:load(lager),
                application:set_env(lager, error_logger_redirect, true),
                application:set_env(lager, handlers, [{?MODULE, [error]}]),
                application:start(lager),
                crash:start()
        end,

        fun(_) ->
                application:stop(lager),
                application:unload(lager),
                case whereis(crash) of
                    undefined -> ok;
                    Pid -> exit(Pid, kill)
                end
        end,
        [
            {"again, there is nothing up my sleeve",
                fun() ->
                        ?assertEqual(undefined, pop()),
                        ?assertEqual(0, count())
                end
            },
            {"bad return value",
                fun() ->
                        Pid = whereis(crash),
                        crash(bad_return),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: bad return value: bleh", [Pid])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
            },
            {"case clause",
                fun() ->
                        Pid = whereis(crash),
                        crash(case_clause),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: no case clause matching {} in crash:handle_call/3", [Pid])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
            },
            {"function clause",
                fun() ->
                        Pid = whereis(crash),
                        crash(function_clause),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: no function clause matching crash:function({})", [Pid])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
            },
            {"if clause",
                fun() ->
                        Pid = whereis(crash),
                        crash(if_clause),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: no true branch found while evaluating if expression in crash:handle_call/3", [Pid])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
            },
            {"try clause",
                fun() ->
                        Pid = whereis(crash),
                        crash(try_clause),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: no try clause matching [] in crash:handle_call/3", [Pid])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
Andrew Thompson's avatar
Andrew Thompson committed
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
            },
            {"undefined function",
                fun() ->
                        Pid = whereis(crash),
                        crash(undef),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: call to undefined function crash:booger/0 from crash:handle_call/3", [Pid])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
            },
            {"bad math",
                fun() ->
                        Pid = whereis(crash),
                        crash(badarith),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: bad arithmetic expression in crash:handle_call/3", [Pid])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
            },
            {"bad match",
                fun() ->
                        Pid = whereis(crash),
                        crash(badmatch),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: no match of right hand value {} in crash:handle_call/3", [Pid])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
            },
            {"bad arity",
                fun() ->
                        Pid = whereis(crash),
                        crash(badarity),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: fun called with wrong arity of 1 instead of 3 in crash:handle_call/3", [Pid])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
            },
            {"bad arg1",
                fun() ->
                        Pid = whereis(crash),
                        crash(badarg1),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: bad argument in crash:handle_call/3", [Pid])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
            },
            {"bad arg2",
                fun() ->
                        Pid = whereis(crash),
                        crash(badarg2),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: bad argument in call to erlang:iolist_to_binary([[102,111,111],bar]) in crash:handle_call/3", [Pid])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
            },
            {"noproc",
                fun() ->
                        Pid = whereis(crash),
                        crash(noproc),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[error] ~w gen_server crash terminated with reason: no such process or port in call to gen_event:call(foo, bar, baz)", [Pid])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
319
320
321
322
            }
        ]
    }.

323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
error_logger_redirect_test_() ->
    {foreach,
        fun() ->
                application:load(lager),
                application:set_env(lager, error_logger_redirect, true),
                application:set_env(lager, handlers, [{?MODULE, [info]}]),
                application:start(lager),
                timer:sleep(100),
                gen_event:call(lager_event, ?MODULE, flush)
        end,

        fun(_) ->
                application:stop(lager),
                application:unload(lager)
        end,
        [
            {"error reports are printed",
                fun() ->
                        error_logger:error_report([{this, is}, a, {silly, format}]),
                        timer:sleep(100),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[error] ~w this: is a silly: format", [self()])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
            },
            {"string error reports are printed",
                fun() ->
                        error_logger:error_report("this is less silly"),
                        timer:sleep(100),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[error] ~w this is less silly", [self()])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
            },
            {"error messages are printed",
                fun() ->
                        error_logger:error_msg("doom, doom has come upon you all"),
                        timer:sleep(100),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[error] ~w doom, doom has come upon you all", [self()])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
            },
            {"info reports are printed",
                fun() ->
                        error_logger:info_report([{this, is}, a, {silly, format}]),
                        timer:sleep(100),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[info] ~w this: is a silly: format", [self()])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
            },
            {"string info reports are printed",
                fun() ->
                        error_logger:info_report("this is less silly"),
                        timer:sleep(100),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[info] ~w this is less silly", [self()])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
            },
            {"error messages are printed",
                fun() ->
                        error_logger:info_msg("doom, doom has come upon you all"),
                        timer:sleep(100),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[info] ~w doom, doom has come upon you all", [self()])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
            },
            {"application stop reports",
                fun() ->
                        error_logger:info_report([{application, foo}, {exited, quittin_time}, {type, lazy}]),
                        timer:sleep(100),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[info] ~w Application foo exited with reason: quittin_time", [self()])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
            },
            {"supervisor reports",
                fun() ->
                        error_logger:error_report(supervisor_report, [{errorContext, france}, {offender, [{name, mini_steve}, {mfargs, {a, b, [c]}}, {pid, bleh}]}, {reason, fired}, {supervisor, {local, steve}}]),
                        timer:sleep(100),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[error] ~w Supervisor steve had child mini_steve started with a:b(c) at bleh exit with reason fired in context france", [self()])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
            },
            {"supervisor_bridge reports",
                fun() ->
                        error_logger:error_report(supervisor_report, [{errorContext, france}, {offender, [{mod, mini_steve}, {pid, bleh}]}, {reason, fired}, {supervisor, {local, steve}}]),
                        timer:sleep(100),
                        {_, _, Msg} = pop(),
                        Expected = lists:flatten(io_lib:format("[error] ~w Supervisor steve had child at module mini_steve at bleh exit with reason fired in context france", [self()])),
                        ?assertEqual(Expected, lists:flatten(Msg))
                end
            }
            %% TODO progress reports
        ]
    }.

424
425
426
-endif.