aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMickael Remond <mremond@process-one.net>2016-03-09 19:12:56 +0100
committerMickael Remond <mremond@process-one.net>2016-03-09 19:12:56 +0100
commitf4ee8a25051a51b4fdd1318cb6918e05404a466b (patch)
tree03a4f77d5a9b82f376c9f83b7070dc21f41384ec
parentFix escaping of argument in iexlive and iexdebug (diff)
Add Elixir Logger Backend to bridge logs from lager
We will need to support loglevel bridging. It should help with #966
-rw-r--r--src/ejabberd_logger.erl44
-rw-r--r--src/elixir_logger_backend.erl115
2 files changed, 157 insertions, 2 deletions
diff --git a/src/ejabberd_logger.erl b/src/ejabberd_logger.erl
index 605b1d633..a13ac814a 100644
--- a/src/ejabberd_logger.erl
+++ b/src/ejabberd_logger.erl
@@ -102,6 +102,29 @@ get_string_env(Name, Default) ->
%% @spec () -> ok
start() ->
+ StartedApps = application:which_applications(5000),
+ case lists:keyfind(logger, 1, StartedApps) of
+ {logger, _, _} ->
+ error_logger:info_msg("Ignoring logger options, using Elixir Logger.", []),
+ %% Do not start lager, we rely on Elixir Logger
+ do_start_for_logger();
+ _ ->
+ do_start()
+ end.
+
+do_start_for_logger() ->
+ 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, debug}]}]),
+ ejabberd:start_app(lager),
+ ok.
+
+%% Start lager
+do_start() ->
application:load(sasl),
application:set_env(sasl, sasl_error_logger, false),
application:load(lager),
@@ -145,7 +168,7 @@ rotate_log() ->
%% @spec () -> {loglevel(), atom(), string()}
get() ->
- case lager:get_loglevel(lager_console_backend) of
+ case get_lager_loglevel() of
none -> {0, no_log, "No log"};
emergency -> {1, critical, "Critical"};
alert -> {1, critical, "Critical"};
@@ -168,7 +191,7 @@ set(LogLevel) when is_integer(LogLevel) ->
5 -> debug;
E -> throw({wrong_loglevel, E})
end,
- case lager:get_loglevel(lager_console_backend) of
+ case get_lager_loglevel() of
LagerLogLevel ->
ok;
_ ->
@@ -186,3 +209,20 @@ set(LogLevel) when is_integer(LogLevel) ->
set({_LogLevel, _}) ->
error_logger:error_msg("custom loglevels are not supported for 'lager'"),
{module, lager}.
+
+get_lager_loglevel() ->
+ R = case get_lager_handlers() of
+ [] -> none;
+ [elixir_logger_backend] -> debug;
+ [FirstHandler|_] ->
+ lager:get_loglevel(FirstHandler)
+ end,
+ R.
+
+get_lager_handlers() ->
+ case catch gen_event:which_handlers(lager_event) of
+ {'EXIT',noproc} ->
+ [];
+ Result ->
+ Result
+ end.
diff --git a/src/elixir_logger_backend.erl b/src/elixir_logger_backend.erl
new file mode 100644
index 000000000..6bd5b638e
--- /dev/null
+++ b/src/elixir_logger_backend.erl
@@ -0,0 +1,115 @@
+%%%-------------------------------------------------------------------
+%%% @author Mickael Remond <mremond@process-one.net>
+%%% @doc
+%%% This module bridges lager logs to Elixir Logger.
+%%% @end
+%%% Created : 9 March 2016 by Mickael Remond <mremond@process-one.net>
+%%%
+%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
+%%%
+%%% This program is free software; you can redistribute it and/or
+%%% modify it under the terms of the GNU General Public License as
+%%% published by the Free Software Foundation; either version 2 of the
+%%% License, or (at your option) any later version.
+%%%
+%%% This program is distributed in the hope that it will be useful,
+%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%%% General Public License for more details.
+%%%
+%%% You should have received a copy of the GNU General Public License along
+%%% with this program; if not, write to the Free Software Foundation, Inc.,
+%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+%%%
+%%%-------------------------------------------------------------------
+
+-module(elixir_logger_backend).
+
+-behaviour(gen_event).
+
+-export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2,
+ code_change/3]).
+
+init(_Opts) ->
+ State = [],
+ {ok, State}.
+
+%% @private
+handle_event({log, LagerMsg}, State) ->
+ #{mode := Mode, truncate := Truncate, level := MinLevel, utc_log := UTCLog} = 'Elixir.Logger.Config':'__data__'(),
+ MsgLevel = severity_to_level(lager_msg:severity(LagerMsg)),
+ case {lager_util:is_loggable(LagerMsg, debug, ?MODULE), 'Elixir.Logger':compare_levels(MsgLevel, MinLevel)} of
+ {_, lt}->
+ {ok, State};
+ {true, _} ->
+ Metadata = normalize_pid(lager_msg:metadata(LagerMsg)),
+ Message = 'Elixir.Logger.Utils':truncate(lager_msg:message(LagerMsg), Truncate),
+ Timestamp = timestamp(lager_msg:timestamp(LagerMsg), UTCLog),
+ GroupLeader = case proplists:get_value(pid, Metadata, self()) of
+ Pid when is_pid(Pid) ->
+ erlang:process_info(self(), group_leader);
+ _ -> {group_leader, self()}
+ end,
+ notify(Mode, {MsgLevel, GroupLeader, {'Elixir.Logger', Message, Timestamp, Metadata}}),
+ {ok, State};
+ _ ->
+ {ok, State}
+ end;
+handle_event(_, State) ->
+ {ok, State}.
+
+%% @private
+%% TODO Handle loglevels
+handle_call(_Msg, State) ->
+ {ok, ok, State}.
+
+%% @private
+handle_info(_Msg, State) ->
+ {ok, State}.
+
+%% @private
+terminate(_Reason, _State) ->
+ ok.
+
+%% @private
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+notify(sync, Msg) ->
+ gen_event:sync_notify('Elixir.Logger', Msg);
+notify(async, Msg) -> gen_event:notify('Elixir.Logger', Msg).
+
+normalize_pid(Metadata) ->
+ case proplists:get_value(pid, Metadata) of
+ Pid when is_pid(Pid) -> Metadata;
+ Pid when is_list(Pid) ->
+ M1 = proplists:delete(pid, Metadata),
+ case catch erlang:list_to_pid(Pid) of
+ {'EXIT', _} ->
+ M1;
+ PidAsPid ->
+ [{pid, PidAsPid}|M1]
+ end;
+ _ ->
+ proplists:delete(pid, Metadata)
+ end.
+
+%% Return timestamp with milliseconds
+timestamp(Time, UTCLog) ->
+ {_, _, Micro} = erlang:timestamp(),
+ {Date, {Hours, Minutes, Seconds}} =
+ case UTCLog of
+ true -> calendar:now_to_universal_time(Time);
+ false -> calendar:now_to_local_time(Time)
+ end,
+ {Date, {Hours, Minutes, Seconds, Micro div 1000}}.
+
+
+severity_to_level(debug) -> debug;
+severity_to_level(info) -> info;
+severity_to_level(notice) -> info;
+severity_to_level(warning) -> warn;
+severity_to_level(error) -> error;
+severity_to_level(critical) -> error;
+severity_to_level(alert) -> error;
+severity_to_level(emergency) -> error.