-module(eRum_app).
|
|
|
|
-behaviour(application).
|
|
|
|
-include("rumCom.hrl").
|
|
-include("rumDef.hrl").
|
|
-include("eRum.hrl").
|
|
|
|
-export([
|
|
start/2
|
|
, stop/1
|
|
, doStart/0
|
|
, startSink/1
|
|
, startSink/2
|
|
, startHandler/3
|
|
]).
|
|
|
|
start(_StartType, _StartArgs) ->
|
|
{ok, Pid} = eRum_sup:start_link(),
|
|
SavedHandlers = doStart(),
|
|
doStartExtraSink(),
|
|
doStartTraces(),
|
|
?eRumInit(),
|
|
{ok, Pid, SavedHandlers}.
|
|
|
|
%% 启动默认的接收器(sink)
|
|
doStart() ->
|
|
%% 尝试起送异步管理者
|
|
tryStartAsyncMgr(rumUtil:get_env(asyncThreshold, undefined), rumUtil:get_env(asyncThresholdWindow, undefined), ?RumDefSink),
|
|
|
|
%%尝试安装killer
|
|
tryInstallKiller(rumUtil:get_env(killerHwm, undefined), rumUtil:get_env(killerReTime, undefined), ?RumDefSink),
|
|
|
|
%%尝试启动各个handler
|
|
tryStartHandlers(rumUtil:get_env(handlers, ?RumDefHandler), ?RumDefSink),
|
|
|
|
%% 尝试替换error logger
|
|
SavedHandlers = tryStartErrLoggerHandler(rumUtil:get_env(errLoggerRedirect, true), rumUtil:get_env(errLoggerHwm, 0), rumUtil:get_env(errLoggerWhitelist, [])),
|
|
eRum:updateLogevelCfg(?RumDefSink),
|
|
SavedHandlers.
|
|
|
|
startSink(?RumDefSink) -> doStart();
|
|
startSink(Sink) ->
|
|
AllSinksDef = rumUtil:get_env(extraSinks, []),
|
|
SinkValue = lists:keyfind(Sink, 1, AllSinksDef),
|
|
SinkOpts = ?IIF(SinkValue == false, [], element(2, SinkValue)),
|
|
startSink(Sink, SinkOpts).
|
|
|
|
startSink(Sink, Opts) ->
|
|
rumConfig:initSink(Sink),
|
|
ChildId = rumUtil:makeInnerSinkName(Sink),
|
|
SinkSpec = #{
|
|
id => ChildId,
|
|
start => {gen_event, start_link, [{local, Sink}]},
|
|
restart => permanent,
|
|
shutdown => 5000,
|
|
type => worker,
|
|
modules => [dynamic]},
|
|
_ = supervisor:start_child(eRum_sup, SinkSpec),
|
|
tryStartAsyncMgr(proplists:get_value(asyncThreshold, Opts, undefined), proplists:get_value(asyncThresholdWindow, Opts, undefined), Sink),
|
|
tryInstallKiller(proplists:get_value(killerHwm, Opts, undefined), proplists:get_value(killerReTime, Opts, undefined), Sink),
|
|
tryStartHandlers(proplists:get_value(handlers, Opts, []), Sink),
|
|
eRum:updateLogevelCfg(Sink).
|
|
|
|
doStartExtraSink() ->
|
|
doStartExtraSinks(rumUtil:get_env(extraSinks, [])).
|
|
|
|
doStartExtraSinks(Sinks) ->
|
|
[startSink(Sink, Opts) || {Sink, Opts} <- Sinks],
|
|
ok.
|
|
|
|
doStartTraces() ->
|
|
_ = rumUtil:trace_filter(none),
|
|
ok = addTraces().
|
|
|
|
addTraces() ->
|
|
Traces = rumUtil:get_env(traces, []),
|
|
[startTrace(One) || One <- Traces],
|
|
ok.
|
|
|
|
startTrace({Handler, Filter}) ->
|
|
{ok, _} = eRum:trace(Handler, Filter);
|
|
startTrace({Handler, Filter, Level}) when is_atom(Level) ->
|
|
{ok, _} = eRum:trace(Handler, Filter, Level).
|
|
|
|
stop(Handlers) ->
|
|
[error_logger:add_report_handler(Handler) || Handler <- Handlers],
|
|
rumConfig:cleanup().
|
|
|
|
tryStartAsyncMgr(undefined, _Window, _Sink) ->
|
|
ignore;
|
|
tryStartAsyncMgr(Threshold, Window, Sink) ->
|
|
case Window of
|
|
undefined ->
|
|
supervisor:start_child(rumHWatcherSup, [Sink, rumBkdThrottle, [Threshold, erlang:trunc(Threshold * 0.2)]]);
|
|
_ ->
|
|
supervisor:start_child(rumHWatcherSup, [Sink, rumBkdThrottle, [Threshold, Window]])
|
|
end,
|
|
ok.
|
|
|
|
tryInstallKiller(undefined, _ReTime, _Sink) -> ok;
|
|
tryInstallKiller(HWM, ReTime, Sink) ->
|
|
case ReTime of
|
|
undefined ->
|
|
_ = supervisor:start_child(rumHWatcherSup, [Sink, rumMgrKiller, [HWM, 5000]]);
|
|
_ ->
|
|
_ = supervisor:start_child(rumHWatcherSup, [Sink, rumMgrKiller, [HWM, ReTime]])
|
|
end,
|
|
ok.
|
|
|
|
tryStartHandlers(undefined, _Sink) -> ok;
|
|
tryStartHandlers(Handlers, Sink) ->
|
|
%% 启动失败的处理程序将在handler_watcher中处理
|
|
NewHandler = doStartHandlers(Handlers, Sink, [], []),
|
|
rumConfig:global_set(handlers, rumConfig:global_get(handlers, []) ++ NewHandler),
|
|
ok.
|
|
|
|
doStartHandlers([], _Sink, _NameAcc, HandlerAcc) ->
|
|
HandlerAcc;
|
|
doStartHandlers([OneHandler | Handlers], Sink, NameAcc, HandlerAcc) ->
|
|
{Module, Options} = parseHandlers(OneHandler),
|
|
NewNameAcc =
|
|
case Module of
|
|
{rumBkFile, F} ->
|
|
case lists:member(F, NameAcc) of
|
|
true ->
|
|
error_logger:error_msg("Cannot have same file (~p) in multiple file backends~n", [F]),
|
|
throw({error, bad_config});
|
|
_ ->
|
|
[F | NameAcc]
|
|
end;
|
|
_ ->
|
|
NameAcc
|
|
end,
|
|
|
|
HandlerRet = startHandler(Sink, Module, Options),
|
|
doStartHandlers(Handlers, Sink, NewNameAcc, [HandlerRet | HandlerAcc]).
|
|
|
|
startHandler(Sink, Module, Config) ->
|
|
{ok, Watcher} = supervisor:start_child(rumHWatcherSup, [Sink, Module, Config]),
|
|
{Module, Watcher, Sink}.
|
|
|
|
-spec tryStartErrLoggerHandler(boolean(), pos_integer(), list()) -> list().
|
|
tryStartErrLoggerHandler(false, _HWM, _Whitelist) -> [];
|
|
tryStartErrLoggerHandler(_ErrLoggerRedirect, HWM, WhiteList) ->
|
|
case whereis(error_logger) of
|
|
undefined ->
|
|
%% 在OTP 21及以上版本中,error_logger已弃用,而改用 'logger'
|
|
%% 作为一个修补, 启动error_logger并将其安装为 logger handler
|
|
%% 我们不能使用 error_logger:add_report_handler 因为我们想要监视这个handler
|
|
%% 因此,我们必须手动添加这个 logger handler
|
|
%%
|
|
%% 从长远来看,我们应该安装一个日志处理程序, but this will bridge the gap for now.
|
|
_ = error_logger:start(),
|
|
_ = logger:add_handler(error_logger, error_logger, #{level => info, filter_default => log}),
|
|
ok = tryRemoveLoggerHandler();
|
|
_ ->
|
|
ok
|
|
end,
|
|
|
|
%% capture which handlers we removed from error_logger so we can restore them when lager stops
|
|
%% 捕获从error_logger中删除的处理程序,以便在lager停止时恢复它们
|
|
OldHandlers =
|
|
case supervisor:start_child(rumHWatcherSup, [error_logger, rumErrLoggerH, [HWM, rumUtil:get_env(errLoggerGroupLeaderStrategy, handle)]]) of
|
|
{ok, _} ->
|
|
[begin error_logger:delete_report_handler(X), X end || X <- gen_event:which_handlers(error_logger) -- [rumErrLoggerH | WhiteList]];
|
|
{error, _} ->
|
|
[]
|
|
end,
|
|
OldHandlers.
|
|
|
|
%% 在OTP 21.1及更高版本上,我们需要删除`default' handler。但是它可能不存在,因此我们将其包装在try-catch块中
|
|
tryRemoveLoggerHandler() ->
|
|
try
|
|
ok = logger:remove_handler(default)
|
|
catch
|
|
error:undef -> ok;
|
|
Err:Reason ->
|
|
error_logger:error_msg("calling logger:remove_handler(default) failed: ~p ~p", [Err, Reason])
|
|
end.
|
|
|
|
parseHandlers([]) ->
|
|
[];
|
|
parseHandlers({rumBkdFile, Config}) ->
|
|
%% this is definitely a new-style config, no expansion needed
|
|
maybe_make_handler_id(rumBkdFile, Config);
|
|
parseHandlers({Mod, Config}) ->
|
|
maybe_make_handler_id(Mod, Config).
|
|
|
|
maybe_make_handler_id(Mod, Config) ->
|
|
%% Allow the backend to generate a gen_event handler id, if it wants to.
|
|
%% We don't use erlang:function_exported here because that requires the module
|
|
%% already be loaded, which is unlikely at this phase of startup. Using code:load
|
|
%% caused undesirable side-effects with generating code-coverage reports.
|
|
|
|
%%允许后端生成gen_event处理程序id,如果它愿意的话。
|
|
%%这里我们没有使用erlang:function_exported,因为这需要用到模块
|
|
%%已经加载,这在启动阶段是不太可能的。使用代码:负载
|
|
%%会在生成代码覆盖率报告时产生不良的副作用。
|
|
|
|
try Mod:configToId(Config) of
|
|
Id ->
|
|
{Id, Config}
|
|
catch
|
|
error:undef ->
|
|
{Mod, Config}
|
|
end.
|