1: -module(mongooseim_loglevel_SUITE).
    2: 
    3: -export([all/0, init_per_suite/1, end_per_suite/1]).
    4: 
    5: -export([
    6:          something_is_logged/1,
    7:          set_get_loglevel/1,
    8:          set_custom_loglevel/1,
    9:          log_at_every_level/1,
   10:          log_at_custom_level/1
   11:         ]).
   12: 
   13: -import(logger_helper, ['contains?'/2, filter_out_non_matching/2, get_at_least_n_log_lines/3,
   14:                         get_log/1, levels/0, levels_less_than_or_equal_to/1]).
   15: 
   16: -include_lib("common_test/include/ct.hrl").
   17: -include_lib("eunit/include/eunit.hrl").
   18: 
   19: -define(LOGFILE, "log/mongooseim.log.1").
   20: 
   21: all() ->
   22:     [
   23:      something_is_logged,
   24:      set_get_loglevel,
   25:      set_custom_loglevel,
   26:      log_at_every_level,
   27:      log_at_custom_level
   28:     ].
   29: 
   30: init_per_suite(Config) ->
   31:     LoggerConfig = logger:get_primary_config(),
   32:     logger:set_primary_config(level, info),
   33:     {ok, Backend} = mongoose_logger_running(),
   34:     [{logger_primary_config, LoggerConfig},
   35:      {logger_backend, Backend}
   36:      | Config].
   37: 
   38: end_per_suite(Config) ->
   39:     logger:remove_handler(?config(logger_handler, Config)),
   40:     logger:set_primary_config(?config(logger_primary_config, Config)),
   41:     ok.
   42: 
   43: %%
   44: %% Tests
   45: %%
   46: something_is_logged(Config) ->
   47:     {_, File} = ?config(logger_backend, Config),
   48:     Before = get_log(File),
   49:     logger:log(error, "Something"),
   50:     ?assertNotEqual(timeout, get_at_least_n_log_lines(File, length(Before) + 1, timer:seconds(5))).
   51: 
   52: set_get_loglevel(_Config) ->
   53:     LevelsToTest = ([ {Keyword, Number} || {Number, Keyword} <- levels() ] ++
   54:                     [ {Keyword, Keyword} || {_, Keyword} <- levels() ]),
   55:     Assertion = fun(Expected, Level) ->
   56:                         ?assertEqual(ok, mongoose_logs:set_global_loglevel(Level)),
   57:                         ?assertEqual(Expected, mongoose_logs:get_global_loglevel())
   58:             end,
   59:     [ Assertion(Expected, Level) || {Expected, Level} <- LevelsToTest ].
   60: 
   61: set_custom_loglevel(_Config) ->
   62:     ExampleMod = mongooseim_loglevel_SUITE_helper,
   63:     ExampleLvl = info,
   64:     ?assertEqual(ok, mongoose_logs:set_module_loglevel(ExampleMod, ExampleLvl)).
   65: 
   66: log_at_every_level(_Config) ->
   67:     %% given
   68:     [ begin
   69:           %% when
   70:           mongoose_logs:set_global_loglevel(LName),
   71:           %% then
   72:           log_at_level(LName)
   73:       end || {_, LName} <- levels() ].
   74: 
   75: log_at_level(none) ->
   76:     %% When log level `none` is set and we log on each possible level...
   77:     Before = get_log(?LOGFILE),
   78:     [ logger:log(LevelName, "", []) || {_, LevelName} <- levels(), LevelName /= none ],
   79:     %% ...then nothing ends up in the log file.
   80:     Fun = fun() -> get_log(?LOGFILE) -- Before end,
   81:     async_helper:wait_until(Fun, []);
   82: log_at_level(LName) ->
   83:     %% When current log level is L and we log on each possible level...
   84:     Before = get_log(?LOGFILE),
   85:     [ logger:log(LevelName, "match-this ~s", [LevelName]) || {_, LevelName} <- levels(), LevelName /= none ],
   86:     %% ...then for each sensible level (i.e. less or equal to current level)
   87:     %% we get a line in the log file.
   88:     ExpectedContents = levels_less_than_or_equal_to(LName),
   89:     After = case get_at_least_n_log_lines(?LOGFILE,
   90:                                           length(Before) + length(ExpectedContents),
   91:                                           timer:seconds(5)) of
   92:                 timeout -> ct:fail("timed out waiting for messages to reach the log file");
   93:                 Res -> Res
   94:             end,
   95:     LinesDiff = filter_out_non_matching(After -- Before, <<"match-this">>),
   96:     LinesWithExpectedContents = lists:zip(LinesDiff, ExpectedContents),
   97:     [ ?assert('contains?'(Line, ExpectedLevel))
   98:       || {Line, ExpectedLevel} <- LinesWithExpectedContents ].
   99: 
  100: log_at_custom_level(_Config) ->
  101:     %% given logging on custom log level for the helper module
  102:     mongoose_logs:set_global_loglevel(critical),
  103:     mongoose_logs:set_module_loglevel(mongooseim_loglevel_SUITE_helper, debug),
  104:     %% when we log from here and from the helper module
  105:     Before = get_log(?LOGFILE),
  106:     NotSupposedToBeLogged = "This should not be in the logs!",
  107:     ShouldBeSuccessfullyLogged = "This MUST appear in the logs successfully",
  108:     logger:log(debug, NotSupposedToBeLogged),
  109:     mongooseim_loglevel_SUITE_helper:log(debug, ShouldBeSuccessfullyLogged),
  110:     %% then
  111:     After = case get_at_least_n_log_lines(?LOGFILE, length(Before) + 1, timer:seconds(5)) of
  112:                 timeout -> ct:fail("timeout waiting for messages to reach the log file");
  113:                 Res -> Res
  114:             end,
  115:     Diff = After -- Before,
  116:     %% ...nothing logged from the suite reaches the log file
  117:     ?assertEqual([], filter_out_non_matching(Diff, list_to_binary(NotSupposedToBeLogged))),
  118:     %% ...logs from the helper module are found in the log file
  119:     [LogLine] = filter_out_non_matching(Diff, list_to_binary(ShouldBeSuccessfullyLogged)),
  120:     ?assert('contains?'(LogLine, <<"debug">>)).
  121: 
  122: %%
  123: %% Helpers
  124: %%
  125: 
  126: mongoose_logger_running() ->
  127:     File = ?LOGFILE,
  128:     HandlerID = disk_log,
  129:     HandlerModule = logger_disk_log_h,
  130:     HandlerConfig = #{config => #{
  131:                         file => "log/mongooseim.log",
  132:                         type => wrap,
  133:                         max_no_files => 5,
  134:                         max_no_bytes => 2097152
  135:                        }
  136:                      },
  137:     ok = logger:add_handler(HandlerID, HandlerModule, HandlerConfig),
  138:     FileBackend = {HandlerID, File},
  139:     {ok, FileBackend}.