1-module(lager_common_test_backend).
2
3-behavior(gen_event).
4
5%% gen_event callbacks
6-export([init/1,
7         handle_call/2,
8         handle_event/2,
9         handle_info/2,
10         terminate/2,
11         code_change/3]).
12-export([get_logs/0,
13        bounce/0,
14        bounce/1]).
15
16%% holds the log messages for retreival on terminate
17-record(state, {level :: {mask, integer()},
18                formatter :: atom(),
19                format_config :: any(),
20                log = [] :: list()}).
21
22-include("lager.hrl").
23-define(TERSE_FORMAT,[time, " ", color, "[", severity,"] ", message]).
24
25%% @doc Before every test, just
26%% lager_common_test_backend:bounce(Level) with the log level of your
27%% choice. Every message will be passed along to ct:pal for your
28%% viewing in the common_test reports. Also, you can call
29%% lager_common_test_backend:get_logs/0 to get a list of all log
30%% messages this backend has received during your test. You can then
31%% search that list for expected log messages.
32
33
34-spec get_logs() -> [iolist()] | {error, term()}.
35get_logs() ->
36    gen_event:call(lager_event, ?MODULE, get_logs, infinity).
37
38bounce() ->
39    bounce(error).
40
41bounce(Level) ->
42    _ = application:stop(lager),
43    application:set_env(lager, suppress_application_start_stop, true),
44    application:set_env(lager, handlers,
45                        [
46                         {lager_common_test_backend, [Level, false]}
47                        ]),
48    ok = lager:start(),
49    %% we care more about getting all of our messages here than being
50    %% careful with the amount of memory that we're using.
51    error_logger_lager_h:set_high_water(100000),
52    ok.
53
54-spec(init(integer()|atom()|[term()]) -> {ok, #state{}} | {error, atom()}).
55%% @private
56%% @doc Initializes the event handler
57init([Level, true]) -> % for backwards compatibility
58    init([Level,{lager_default_formatter,[{eol, "\n"}]}]);
59init([Level,false]) -> % for backwards compatibility
60    init([Level,{lager_default_formatter,?TERSE_FORMAT ++ ["\n"]}]);
61init([Level,{Formatter,FormatterConfig}]) when is_atom(Formatter) ->
62    case lists:member(Level, ?LEVELS) of
63        true ->
64            {ok, #state{level=lager_util:config_to_mask(Level),
65                    formatter=Formatter,
66                    format_config=FormatterConfig}};
67        _ ->
68            {error, bad_log_level}
69    end;
70init(Level) ->
71    init([Level,{lager_default_formatter,?TERSE_FORMAT ++ ["\n"]}]).
72
73-spec(handle_event(tuple(), #state{}) -> {ok, #state{}}).
74%% @private
75handle_event({log, Message},
76    #state{level=L,formatter=Formatter,format_config=FormatConfig,log=Logs} = State) ->
77    case lager_util:is_loggable(Message,L,?MODULE) of
78        true ->
79            Log = Formatter:format(Message,FormatConfig),
80            ct:pal(Log),
81            {ok, State#state{log=[Log|Logs]}};
82        false ->
83            {ok, State}
84    end;
85handle_event(Event, State) ->
86    ct:pal(Event),
87    {ok, State#state{log = [Event|State#state.log]}}.
88
89-spec(handle_call(any(), #state{}) -> {ok, any(), #state{}}).
90%% @private
91%% @doc gets and sets loglevel. This is part of the lager backend api.
92handle_call(get_loglevel, #state{level=Level} = State) ->
93    {ok, Level, State};
94handle_call({set_loglevel, Level}, State) ->
95    case lists:member(Level, ?LEVELS) of
96        true ->
97            {ok, ok, State#state{level=lager_util:config_to_mask(Level)}};
98        _ ->
99            {ok, {error, bad_log_level}, State}
100    end;
101handle_call(get_logs, #state{log = Logs} = State) ->
102    {ok, lists:reverse(Logs), State};
103handle_call(_, State) ->
104    {ok, ok, State}.
105
106-spec(handle_info(any(), #state{}) -> {ok, #state{}}).
107%% @private
108%% @doc gen_event callback, does nothing.
109handle_info(_, State) ->
110    {ok, State}.
111
112-spec(code_change(any(), #state{}, any()) -> {ok, #state{}}).
113%% @private
114%% @doc gen_event callback, does nothing.
115code_change(_OldVsn, State, _Extra) ->
116    {ok, State}.
117
118-spec(terminate(any(), #state{}) -> {ok, list()}).
119%% @doc gen_event callback, does nothing.
120terminate(_Reason, #state{log=Logs}) ->
121    {ok, lists:reverse(Logs)}.
122