diff --git a/README.md b/README.md index 5f92d7a99..8f257520d 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ To compile ejabberd you need: - GCC. - Libexpat ≥ 1.95. - Libyaml ≥ 0.1.4. - - Erlang/OTP ≥ 21.0. + - Erlang/OTP ≥ 19.3. - OpenSSL ≥ 1.0.0. - Zlib ≥ 1.2.3, for Stream Compression support (XEP-0138). Optional. - PAM library. Optional. For Pluggable Authentication Modules (PAM). diff --git a/configure.ac b/configure.ac index 4fc8882b0..2d98f62f1 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ(2.53) AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 0.0` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) -REQUIRE_ERLANG_MIN="10.0 (Erlang/OTP 21.0)" +REQUIRE_ERLANG_MIN="8.3 (Erlang/OTP 19.3)" REQUIRE_ERLANG_MAX="100.0.0 (No Max)" AC_CONFIG_MACRO_DIR([m4]) diff --git a/ejabberdctl.template b/ejabberdctl.template index a3b60a893..a940c3df7 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -83,8 +83,10 @@ ERL_INETRC="$ETC_DIR"/inetrc # define ejabberd parameters EJABBERD_OPTS="$EJABBERD_OPTS\ +$(sed '/^log_rate_limit/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\ $(sed '/^log_rotate_size/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\ -$(sed '/^log_rotate_count/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")" +$(sed '/^log_rotate_count/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\ +$(sed '/^log_rotate_date/!d;s/:[ \t]*\(.[^ ]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")" [ -n "$EJABBERD_OPTS" ] && EJABBERD_OPTS="-ejabberd $EJABBERD_OPTS" EJABBERD_OPTS="-mnesia dir \"$SPOOL_DIR\" $MNESIA_OPTIONS $EJABBERD_OPTS -s ejabberd" diff --git a/include/logger.hrl b/include/logger.hrl index b0d1bd361..eace910dd 100644 --- a/include/logger.hrl +++ b/include/logger.hrl @@ -17,10 +17,28 @@ %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- --include_lib("kernel/include/logger.hrl"). - -define(PRINT(Format, Args), io:format(Format, Args)). +-ifdef(LAGER). +-compile([{parse_transform, lager_transform}]). + +-define(DEBUG(Format, Args), + begin lager:debug(Format, Args), ok end). + +-define(INFO_MSG(Format, Args), + begin lager:info(Format, Args), ok end). + +-define(WARNING_MSG(Format, Args), + begin lager:warning(Format, Args), ok end). + +-define(ERROR_MSG(Format, Args), + begin lager:error(Format, Args), ok end). + +-define(CRITICAL_MSG(Format, Args), + begin lager:critical(Format, Args), ok end). +-else. +-include_lib("kernel/include/logger.hrl"). + -define(DEBUG(Format, Args), begin ?LOG_DEBUG(Format, Args), ok end). @@ -35,6 +53,7 @@ -define(CRITICAL_MSG(Format, Args), begin ?LOG_CRITICAL(Format, Args), ok end). +-endif. %% Use only when trying to troubleshoot test problem with ExUnit -define(EXUNIT_LOG(Format, Args), diff --git a/rebar.config b/rebar.config index 2b5482a81..703d66860 100644 --- a/rebar.config +++ b/rebar.config @@ -18,7 +18,8 @@ %%% %%%---------------------------------------------------------------------- -{deps, [{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.16"}}}, +{deps, [{lager, ".*", {git, "https://github.com/erlang-lager/lager", "3.6.10"}}, + {p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.16"}}}, {cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.20"}}}, {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.1.2"}}}, {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.17"}}}, @@ -93,6 +94,7 @@ {if_var_true, sip, {d, 'SIP'}}, {if_var_true, stun, {d, 'STUN'}}, {if_version_above, "20", {d, 'DEPRECATED_GET_STACKTRACE'}}, + {if_version_below, "22", {d, 'LAGER'}}, {if_var_true, roster_gateway_workaround, {d, 'ROSTER_GATWAY_WORKAROUND'}}, {if_var_match, db_type, mssql, {d, 'mssql'}}, {if_var_true, elixir, {d, 'ELIXIR_ENABLED'}}, diff --git a/src/ejabberd.app.src.in b/src/ejabberd.app.src.in index 7908ec101..f1d08b8c7 100644 --- a/src/ejabberd.app.src.in +++ b/src/ejabberd.app.src.in @@ -6,7 +6,7 @@ {modules, []}, {registered, []}, {applications, [kernel, stdlib, sasl, ssl]}, - {included_applications, [os_mon, mnesia, inets, p1_utils, fast_yaml, fast_tls, pkix, xmpp, cache_tab, eimp]}, + {included_applications, [os_mon, lager, mnesia, inets, p1_utils, fast_yaml, fast_tls, pkix, xmpp, cache_tab, eimp]}, {env, [{enabled_backends, [@enabled_backends@]}]}, {mod, {ejabberd_app, []}}]}. diff --git a/src/ejabberd.erl b/src/ejabberd.erl index 4758fd2f9..861714d26 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -49,6 +49,7 @@ stop() -> application:stop(ejabberd). halt() -> + application:stop(sasl), ejabberd_logger:flush(), erlang:halt(1, [{flush, true}]). diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl index d777fd86e..7988077c0 100644 --- a/src/ejabberd_app.erl +++ b/src/ejabberd_app.erl @@ -84,6 +84,8 @@ start_included_apps() -> lists:foreach( fun(mnesia) -> ok; + (lager) -> + ok; (os_mon)-> ok; (App) -> diff --git a/src/ejabberd_logger.erl b/src/ejabberd_logger.erl index 916ca6fea..211099720 100644 --- a/src/ejabberd_logger.erl +++ b/src/ejabberd_logger.erl @@ -25,25 +25,33 @@ -module(ejabberd_logger). -compile({no_auto_import, [get/0]}). --include_lib("kernel/include/logger.hrl"). - %% API -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}]). --type loglevel() :: none | logger:level(). +-type loglevel() :: none | emergency | alert | critical | + error | warning | notice | info | debug. -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 restart() -> ok. +-spec reopen_log() -> ok. +-spec rotate_log() -> ok. +-spec get() -> loglevel(). +-spec set(0..5 | loglevel()) -> ok. +-spec flush() -> ok. + %%%=================================================================== %%% API %%%=================================================================== @@ -61,20 +69,6 @@ get_log_path() -> end end. --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 -> - I; - undefined -> - Default; - {ok, Junk} -> - ?LOG_ERROR("Wrong value for ~ts: ~p; " - "using ~p as a fallback", - [Name, Junk, Default]), - Default - end. - -spec loglevels() -> [loglevel(), ...]. loglevels() -> [none, emergency, alert, critical, error, warning, notice, info, debug]. @@ -87,11 +81,174 @@ 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 -> + I; + undefined -> + Default; + {ok, Junk} -> + 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) -> + L; + undefined -> + Default; + {ok, Junk} -> + error_logger:error_msg("wrong value for ~ts: ~p; " + "using ~p as a fallback~n", + [Name, Junk, Default]), + Default + end. + -spec start() -> ok. start() -> start(info). --spec start(loglevel()) -> ok. +start(Level) -> + StartedApps = application:which_applications(5000), + case lists:keyfind(logger, 1, StartedApps) of + %% Elixir logger is started. We assume everything is in place + %% to use lager to Elixir logger bridge. + {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(Level); + _ -> + do_start(Level) + end. + +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, Level}]}]), + ejabberd:start_app(lager), + ok. + +do_start(Level) -> + application:load(sasl), + application:set_env(sasl, sasl_error_logger, false), + application:load(lager), + ConsoleLog = get_log_path(), + Dir = filename:dirname(ConsoleLog), + ErrorLog = filename:join([Dir, "error.log"]), + CrashLog = filename:join([Dir, "crash.log"]), + LogRotateDate = get_string_env(log_rotate_date, ""), + 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, 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}]}]), + application:set_env(lager, crash_log, CrashLog), + application:set_env(lager, crash_log_date, LogRotateDate), + application:set_env(lager, crash_log_size, LogRotateSize), + application:set_env(lager, crash_log_count, LogRotateCount), + ejabberd:start_app(lager), + 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). + +reopen_log() -> + ok. + +rotate_log() -> + catch lager_crash_log ! rotate, + lists:foreach( + fun({lager_file_backend, File}) -> + whereis(lager_event) ! {rotate, File}; + (_) -> + ok + end, gen_event:which_handlers(lager_event)). + +get() -> + 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). + +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, Level); + (lager_console_backend = H) -> + lager:set_loglevel(H, Level); + (elixir_logger_backend = H) -> + lager:set_loglevel(H, Level); + (_) -> + ok + end, get_lager_handlers()) + end, + 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 + {'EXIT',noproc} -> + []; + 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). + +-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), @@ -120,14 +277,14 @@ start(Level) -> end, case logger:add_handler(ejabberd_log, logger_std_h, #{level => all, - config => Config#{type => {file, EjabberdLog}}, + 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#{type => {file, ErrorLog}}, + config => Config#{file => ErrorLog}, formatter => {logger_formatter, FileFmtConfig}}) of ok -> ok; {error, {already_exist, _}} -> ok @@ -167,12 +324,10 @@ reopen_log() -> rotate_log() -> ok. --spec get() -> loglevel(). get() -> #{level := Level} = logger:get_primary_config(), Level. --spec set(0..5 | loglevel()) -> ok. set(N) when is_integer(N), N>=0, N=<5 -> set(convert_loglevel(N)); set(Level) when ?is_loglevel(Level) -> @@ -188,7 +343,6 @@ set(Level) when ?is_loglevel(Level) -> end end. --spec flush() -> ok. flush() -> lists:foreach( fun(#{id := HandlerId, module := logger_std_h}) -> @@ -198,3 +352,5 @@ flush() -> (_) -> ok end, logger:get_handler_config()). + +-endif.