-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.