diff options
Diffstat (limited to 'src/ejabberd_logger.erl')
-rw-r--r-- | src/ejabberd_logger.erl | 311 |
1 files changed, 218 insertions, 93 deletions
diff --git a/src/ejabberd_logger.erl b/src/ejabberd_logger.erl index 795d4f390..a63aca5cb 100644 --- a/src/ejabberd_logger.erl +++ b/src/ejabberd_logger.erl @@ -1,11 +1,11 @@ %%%------------------------------------------------------------------- -%%% @author Evgeniy Khramtsov <ekhramtsov@process-one.net> -%%% @doc -%%% -%%% @end +%%% File : ejabberd_logger.erl +%%% Author : Evgeniy Khramtsov <ekhramtsov@process-one.net> +%%% Purpose : ejabberd logger wrapper %%% Created : 12 May 2013 by Evgeniy Khramtsov <ekhramtsov@process-one.net> %%% -%%% ejabberd, Copyright (C) 2013-2016 ProcessOne +%%% +%%% ejabberd, Copyright (C) 2013-2019 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -23,34 +23,41 @@ %%%------------------------------------------------------------------- -module(ejabberd_logger). - --behaviour(ejabberd_config). +-compile({no_auto_import, [get/0]}). %% API --export([start/0, reopen_log/0, rotate_log/0, get/0, set/1, get_log_path/0, opt_type/1]). +-export([start/0, get/0, set/1, get_log_path/0, flush/0]). +-export([convert_loglevel/1, loglevels/0]). +-ifndef(LAGER). +-export([progress_filter/2]). +-endif. +%% Deprecated functions +-export([restart/0, reopen_log/0, rotate_log/0]). +-deprecated([{restart, 0}, + {reopen_log, 0}, + {rotate_log, 0}]). --include("ejabberd.hrl"). +-type loglevel() :: none | emergency | alert | critical | + error | warning | notice | info | debug. --type loglevel() :: 0 | 1 | 2 | 3 | 4 | 5. +-define(is_loglevel(L), + ((L == none) or (L == emergency) or (L == alert) + or (L == critical) or (L == error) or (L == warning) + or (L == notice) or (L == info) or (L == debug))). --spec start() -> ok. --spec get_log_path() -> string(). +-export_type([loglevel/0]). + +-spec restart() -> ok. -spec reopen_log() -> ok. -spec rotate_log() -> ok. --spec get() -> {loglevel(), atom(), string()}. --spec set(loglevel() | {loglevel(), list()}) -> {module, module()}. +-spec get() -> loglevel(). +-spec set(0..5 | loglevel()) -> ok. +-spec flush() -> ok. %%%=================================================================== %%% API %%%=================================================================== -%% @doc Returns the full path to the ejabberd log file. -%% It first checks for application configuration parameter 'log_path'. -%% If not defined it checks the environment variable EJABBERD_LOG_PATH. -%% And if that one is neither defined, returns the default value: -%% "ejabberd.log" in current directory. -%% Note: If the directory where to place the ejabberd log file to not exist, -%% it is not created and no log file will be generated. -%% @spec () -> string() +-spec get_log_path() -> string(). get_log_path() -> case ejabberd_config:env_binary_to_list(ejabberd, log_path) of {ok, Path} -> @@ -58,23 +65,25 @@ get_log_path() -> undefined -> case os:getenv("EJABBERD_LOG_PATH") of false -> - ?LOG_PATH; + "ejabberd.log"; Path -> Path end end. -opt_type(log_rotate_date) -> - fun(S) -> binary_to_list(iolist_to_binary(S)) end; -opt_type(log_rotate_size) -> - fun(I) when is_integer(I), I >= 0 -> I end; -opt_type(log_rotate_count) -> - fun(I) when is_integer(I), I >= 0 -> I end; -opt_type(log_rate_limit) -> - fun(I) when is_integer(I), I >= 0 -> I end; -opt_type(_) -> - [log_rotate_date, log_rotate_size, log_rotate_count, log_rate_limit]. +-spec loglevels() -> [loglevel(), ...]. +loglevels() -> + [none, emergency, alert, critical, error, warning, notice, info, debug]. +-spec convert_loglevel(0..5) -> loglevel(). +convert_loglevel(0) -> none; +convert_loglevel(1) -> critical; +convert_loglevel(2) -> error; +convert_loglevel(3) -> warning; +convert_loglevel(4) -> info; +convert_loglevel(5) -> debug. + +-spec get_integer_env(atom(), T) -> T. get_integer_env(Name, Default) -> case application:get_env(ejabberd, Name) of {ok, I} when is_integer(I), I>=0 -> @@ -82,11 +91,14 @@ get_integer_env(Name, Default) -> undefined -> Default; {ok, Junk} -> - error_logger:error_msg("wrong value for ~s: ~p; " + error_logger:error_msg("wrong value for ~ts: ~p; " "using ~p as a fallback~n", [Name, Junk, Default]), Default end. + +-ifdef(LAGER). +-spec get_string_env(atom(), T) -> T. get_string_env(Name, Default) -> case application:get_env(ejabberd, Name) of {ok, L} when is_list(L) -> @@ -94,14 +106,17 @@ get_string_env(Name, Default) -> undefined -> Default; {ok, Junk} -> - error_logger:error_msg("wrong value for ~s: ~p; " + error_logger:error_msg("wrong value for ~ts: ~p; " "using ~p as a fallback~n", [Name, Junk, Default]), Default end. -%% @spec () -> ok +-spec start() -> ok. start() -> + start(info). + +start(Level) -> StartedApps = application:which_applications(5000), case lists:keyfind(logger, 1, StartedApps) of %% Elixir logger is started. We assume everything is in place @@ -109,24 +124,23 @@ start() -> {logger, _, _} -> error_logger:info_msg("Ignoring ejabberd logger options, using Elixir Logger.", []), %% Do not start lager, we rely on Elixir Logger - do_start_for_logger(); + do_start_for_logger(Level); _ -> - do_start() + do_start(Level) end. -do_start_for_logger() -> +do_start_for_logger(Level) -> application:load(sasl), application:set_env(sasl, sasl_error_logger, false), application:load(lager), application:set_env(lager, error_logger_redirect, false), application:set_env(lager, error_logger_whitelist, ['Elixir.Logger.ErrorHandler']), application:set_env(lager, crash_log, false), - application:set_env(lager, handlers, [{elixir_logger_backend, [{level, info}]}]), + application:set_env(lager, handlers, [{elixir_logger_backend, [{level, Level}]}]), ejabberd:start_app(lager), ok. -%% Start lager -do_start() -> +do_start(Level) -> application:load(sasl), application:set_env(sasl, sasl_error_logger, false), application:load(lager), @@ -138,11 +152,15 @@ do_start() -> LogRotateSize = get_integer_env(log_rotate_size, 10*1024*1024), LogRotateCount = get_integer_env(log_rotate_count, 1), LogRateLimit = get_integer_env(log_rate_limit, 100), + ConsoleLevel = case get_lager_version() >= "3.6.0" of + true -> [{level, Level}]; + false -> Level + end, application:set_env(lager, error_logger_hwm, LogRateLimit), application:set_env( lager, handlers, - [{lager_console_backend, info}, - {lager_file_backend, [{file, ConsoleLog}, {level, info}, {date, LogRotateDate}, + [{lager_console_backend, ConsoleLevel}, + {lager_file_backend, [{file, ConsoleLog}, {level, Level}, {date, LogRotateDate}, {count, LogRotateCount}, {size, LogRotateSize}]}, {lager_file_backend, [{file, ErrorLog}, {level, error}, {date, LogRotateDate}, {count, LogRotateCount}, {size, LogRotateSize}]}]), @@ -151,16 +169,20 @@ do_start() -> application:set_env(lager, crash_log_size, LogRotateSize), application:set_env(lager, crash_log_count, LogRotateCount), ejabberd:start_app(lager), - ok. + lists:foreach(fun(Handler) -> + lager:set_loghwm(Handler, LogRateLimit) + end, gen_event:which_handlers(lager_event)). + +restart() -> + Level = ejabberd_option:loglevel(), + application:stop(lager), + start(Level). -%% @spec () -> ok reopen_log() -> - %% Lager detects external log rotation automatically. ok. -%% @spec () -> ok rotate_log() -> - lager_crash_log ! rotate, + catch lager_crash_log ! rotate, lists:foreach( fun({lager_file_backend, File}) -> whereis(lager_event) ! {rotate, File}; @@ -168,62 +190,40 @@ rotate_log() -> ok end, gen_event:which_handlers(lager_event)). -%% @spec () -> {loglevel(), atom(), string()} get() -> - case get_lager_loglevel() of - none -> {0, no_log, "No log"}; - emergency -> {1, critical, "Critical"}; - alert -> {1, critical, "Critical"}; - critical -> {1, critical, "Critical"}; - error -> {2, error, "Error"}; - warning -> {3, warning, "Warning"}; - notice -> {3, warning, "Warning"}; - info -> {4, info, "Info"}; - debug -> {5, debug, "Debug"} - end. + Handlers = get_lager_handlers(), + lists:foldl(fun(lager_console_backend, _Acc) -> + lager:get_loglevel(lager_console_backend); + (elixir_logger_backend, _Acc) -> + lager:get_loglevel(elixir_logger_backend); + (_, Acc) -> + Acc + end, + none, Handlers). -%% @spec (loglevel() | {loglevel(), list()}) -> {module, module()} -set(LogLevel) when is_integer(LogLevel) -> - LagerLogLevel = case LogLevel of - 0 -> none; - 1 -> critical; - 2 -> error; - 3 -> warning; - 4 -> info; - 5 -> debug; - E -> throw({wrong_loglevel, E}) - end, - case get_lager_loglevel() of - LagerLogLevel -> +set(N) when is_integer(N), N>=0, N=<5 -> + set(convert_loglevel(N)); +set(Level) when ?is_loglevel(Level) -> + case get() of + Level -> ok; _ -> ConsoleLog = get_log_path(), lists:foreach( fun({lager_file_backend, File} = H) when File == ConsoleLog -> - lager:set_loglevel(H, LagerLogLevel); + lager:set_loglevel(H, Level); (lager_console_backend = H) -> - lager:set_loglevel(H, LagerLogLevel); + lager:set_loglevel(H, Level); (elixir_logger_backend = H) -> - lager:set_loglevel(H, LagerLogLevel); + lager:set_loglevel(H, Level); (_) -> ok - end, gen_event:which_handlers(lager_event)) + end, get_lager_handlers()) end, - {module, lager}; -set({_LogLevel, _}) -> - error_logger:error_msg("custom loglevels are not supported for 'lager'"), - {module, lager}. - -get_lager_loglevel() -> - Handlers = get_lager_handlers(), - lists:foldl(fun(lager_console_backend, _Acc) -> - lager:get_loglevel(lager_console_backend); - (elixir_logger_backend, _Acc) -> - lager:get_loglevel(elixir_logger_backend); - (_, Acc) -> - Acc - end, - none, Handlers). + case Level of + debug -> xmpp:set_config([{debug, true}]); + _ -> xmpp:set_config([{debug, false}]) + end. get_lager_handlers() -> case catch gen_event:which_handlers(lager_event) of @@ -232,3 +232,128 @@ get_lager_handlers() -> Result -> Result end. + +-spec get_lager_version() -> string(). +get_lager_version() -> + Apps = application:loaded_applications(), + case lists:keyfind(lager, 1, Apps) of + {_, _, Vsn} -> Vsn; + false -> "0.0.0" + end. + +flush() -> + application:stop(lager), + application:stop(sasl). + +-else. +-include_lib("kernel/include/logger.hrl"). + +-spec start() -> ok | {error, term()}. +start() -> + start(info). + +start(Level) -> + EjabberdLog = get_log_path(), + Dir = filename:dirname(EjabberdLog), + ErrorLog = filename:join([Dir, "error.log"]), + LogRotateSize = get_integer_env(log_rotate_size, 10*1024*1024), + LogRotateCount = get_integer_env(log_rotate_count, 1), + Config = #{max_no_bytes => LogRotateSize, + max_no_files => LogRotateCount, + filesync_repeat_interval => no_repeat, + sync_mode_qlen => 1000, + drop_mode_qlen => 1000, + flush_qlen => 5000}, + FmtConfig = #{legacy_header => false, + time_designator => $ , + max_size => 100*1024, + single_line => false}, + FileFmtConfig = FmtConfig#{template => file_template()}, + ConsoleFmtConfig = FmtConfig#{template => console_template()}, + try + ok = logger:set_primary_config(level, Level), + ok = logger:update_formatter_config(default, ConsoleFmtConfig), + case logger:add_primary_filter(progress_report, + {fun ?MODULE:progress_filter/2, stop}) of + ok -> ok; + {error, {already_exist, _}} -> ok + end, + case logger:add_handler(ejabberd_log, logger_std_h, + #{level => all, + config => Config#{file => EjabberdLog}, + formatter => {logger_formatter, FileFmtConfig}}) of + ok -> ok; + {error, {already_exist, _}} -> ok + end, + case logger:add_handler(error_log, logger_std_h, + #{level => error, + config => Config#{file => ErrorLog}, + formatter => {logger_formatter, FileFmtConfig}}) of + ok -> ok; + {error, {already_exist, _}} -> ok + end + catch _:{Tag, Err} when Tag == badmatch; Tag == case_clause -> + ?LOG_CRITICAL("Failed to set logging: ~p", [Err]), + Err + end. + +restart() -> + ok. + +progress_filter(#{level:=info,msg:={report,#{label:={_,progress}}}} = Event, _) -> + case get() of + debug -> + logger_filters:progress(Event#{level => debug}, log); + _ -> + stop + end; +progress_filter(Event, _) -> + Event. + +console_template() -> + [time, " [", level, "] " | msg()]. + +file_template() -> + [time, " [", level, "] ", pid, + {mfa, ["@", mfa, {line, [":", line], []}], []}, " " | msg()]. + +msg() -> + [{logger_formatter, [[logger_formatter, title], ":", io_lib:nl()], []}, + msg, io_lib:nl()]. + +reopen_log() -> + ok. + +rotate_log() -> + ok. + +get() -> + #{level := Level} = logger:get_primary_config(), + Level. + +set(N) when is_integer(N), N>=0, N=<5 -> + set(convert_loglevel(N)); +set(Level) when ?is_loglevel(Level) -> + case get() of + Level -> ok; + PrevLevel -> + ?LOG_NOTICE("Changing loglevel from '~s' to '~s'", + [PrevLevel, Level]), + logger:set_primary_config(level, Level), + case Level of + debug -> xmpp:set_config([{debug, true}]); + _ -> xmpp:set_config([{debug, false}]) + end + end. + +flush() -> + lists:foreach( + fun(#{id := HandlerId, module := logger_std_h}) -> + logger_std_h:filesync(HandlerId); + (#{id := HandlerId, module := logger_disk_log_h}) -> + logger_disk_log_h:filesync(HandlerId); + (_) -> + ok + end, logger:get_handler_config()). + +-endif. |