From 6aaf2476e6ef08429386f305c7a0b4d81089cded Mon Sep 17 00:00:00 2001 From: SisMaker <1713699517@qq.com> Date: Tue, 16 Mar 2021 00:43:19 +0800 Subject: [PATCH] =?UTF-8?q?ft:=20=E4=BB=A3=E7=A0=81=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/eRum.hrl | 59 ++- include/rumCom.hrl | 21 +- include/rumDef.hrl | 15 +- include/rumMsg.hrl | 12 +- src/backend/rumBkdThrottle.erl | 2 +- src/eRum.erl | 104 ++--- src/eRum1.erl | 679 +++++++++++++++++++++++++++++++++ src/mgrKiller/rumMgrKiller.erl | 2 +- src/utils/rumUtil.erl | 53 +-- 9 files changed, 794 insertions(+), 153 deletions(-) create mode 100644 src/eRum1.erl diff --git a/include/eRum.hrl b/include/eRum.hrl index 8b7aea1..fb3ea80 100644 --- a/include/eRum.hrl +++ b/include/eRum.hrl @@ -1,5 +1,19 @@ -include("rumCom.hrl"). +%% 该宏用于确保eRumCfg模块初始化了 任何使用eRum的App启动后请调用一次 +-define(eRumInit(), eRumInit(Sink)). +-define(eRumInit(Sink), + begin + case ets:info(?eRumEts) of + undefined -> + ets:new(?eRumEts, [named_table]), + ets:insert(?eRumEts, {Sink, ?none}), + rumKvsToBeam:load(?eRumCfg, [{Sink, ?none}]); + _ -> + ignore + end + end). + %% 默认的元数据 %% Level, Pid, Node, Module, Function, FunctionArity, File, Line, Other @@ -22,32 +36,39 @@ ?rumLog(?RumDefSink, Severity, self(), node(), ?MODULE, ?FUNCTION_NAME, ?FILE, ?LINE, Metadata ++ eRum:md(), Format, Args, ?RumDefTruncation, Safety)). -define(rumLog(Sink, Severity, Metadata, Format, Args, Size, Safety), - eRum:dispatch_log(Sink, Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, Size, Safety)). + case ?eRumCfg:get(Sink) band Severity /= 0 of + true -> + do_log_impl(Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, Severity, Size, Sink, Safety); + _ -> + ok + end). + + %%eRum:dispatch_log(Sink, Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, Size, Safety)). --define(rumNone(Format, Args), ?rumLog(none, Format, Args, safe)). --define(rumNone(Metadata, Format, Args), ?rumLog(none, Metadata, Format, Args, safe)). +-define(rumNone(Format, Args), ?rumLog(?none, Format, Args, safe)). +-define(rumNone(Metadata, Format, Args), ?rumLog(?none, Metadata, Format, Args, safe)). --define(rumDebug(Format, Args), ?rumLog(debug, Format, Args, safe)). --define(rumDebug(Metadata, Format, Args), ?rumLog(debug, Metadata, Format, Args, safe)). +-define(rumDebug(Format, Args), ?rumLog(?debug, Format, Args, safe)). +-define(rumDebug(Metadata, Format, Args), ?rumLog(?debug, Metadata, Format, Args, safe)). --define(rumInfo(Format, Args), ?rumLog(info, Format, Args, safe)). --define(rumInfo(Metadata, Format, Args), ?rumLog(info, Metadata, Format, Args, safe)). +-define(rumInfo(Format, Args), ?rumLog(?info, Format, Args, safe)). +-define(rumInfo(Metadata, Format, Args), ?rumLog(?info, Metadata, Format, Args, safe)). --define(rumNotice(Format, Args), ?rumLog(notice, Format, Args, safe)). --define(rumNotice(Metadata, Format, Args), ?rumLog(notice, Metadata, Format, Args, safe)). +-define(rumNotice(Format, Args), ?rumLog(?notice, Format, Args, safe)). +-define(rumNotice(Metadata, Format, Args), ?rumLog(?notice, Metadata, Format, Args, safe)). --define(rumWarning(Format, Args), ?rumLog(warning, Format, Args, safe)). --define(rumWarning(Metadata, Format, Args), ?rumLog(warning, Metadata, Format, Args, safe)). +-define(rumWarning(Format, Args), ?rumLog(?warning, Format, Args, safe)). +-define(rumWarning(Metadata, Format, Args), ?rumLog(?warning, Metadata, Format, Args, safe)). --define(rumError(Format, Args), ?rumLog(error, Format, Args, safe)). --define(rumError(Metadata, Format, Args), ?rumLog(error, Metadata, Format, Args, safe)). +-define(rumError(Format, Args), ?rumLog(?error, Format, Args, safe)). +-define(rumError(Metadata, Format, Args), ?rumLog(?error, Metadata, Format, Args, safe)). --define(rumCritical(Format, Args), ?rumLog(critical, Format, Args, safe)). --define(rumCritical(Metadata, Format, Args), ?rumLog(critical, Metadata, Format, Args, safe)). +-define(rumCritical(Format, Args), ?rumLog(?critical, Format, Args, safe)). +-define(rumCritical(Metadata, Format, Args), ?rumLog(?critical, Metadata, Format, Args, safe)). --define(rumAlert(Format, Args), ?rumLog(alert, Format, Args, safe)). --define(rumAlert(Metadata, Format, Args), ?rumLog(alert, Metadata, Format, Args, safe)). +-define(rumAlert(Format, Args), ?rumLog(?alert, Format, Args, safe)). +-define(rumAlert(Metadata, Format, Args), ?rumLog(?alert, Metadata, Format, Args, safe)). --define(rumEmergency(Format, Args), ?rumLog(emergency, Format, Args, safe)). --define(rumEmergency(Metadata, Format, Args), ?rumLog(emergency, Metadata, Format, Args, safe)). +-define(rumEmergency(Format, Args), ?rumLog(?emergency, Format, Args, safe)). +-define(rumEmergency(Metadata, Format, Args), ?rumLog(?emergency, Metadata, Format, Args, safe)). diff --git a/include/rumCom.hrl b/include/rumCom.hrl index f740683..1302143 100644 --- a/include/rumCom.hrl +++ b/include/rumCom.hrl @@ -1,2 +1,21 @@ +-ifndef(__RumCom__). +-define(__RumCom__, 1). + +%% 配置模块 +-define(eRumCfg, eRumCfg). +-define(eRumEts, eRumEts). + -define(RumDefSink, rumEvent). --define(RumDefTruncation, 4096). \ No newline at end of file +-define(RumDefTruncation, 4096). + +%% 日志等级定义 +-define(debug, 128). +-define(info, 64). +-define(notice, 32). +-define(warning, 16). +-define(error, 8). +-define(critical, 4). +-define(alert, 2). +-define(emergency, 1). +-define(none, 0). +-endif. \ No newline at end of file diff --git a/include/rumDef.hrl b/include/rumDef.hrl index 252c251..75fc40c 100644 --- a/include/rumDef.hrl +++ b/include/rumDef.hrl @@ -1,3 +1,4 @@ +-include("rumCom.hrl"). %% 应用名字 -define(RumAppName, eRum). @@ -104,20 +105,10 @@ %% 使用这些“功能”意味着,出于安全考虑,参数列表不会被截断 -define(RumLevelsUnsafe, [{debug_unsafe, debug}, {info_unsafe, info}, {notice_unsafe, notice}, {warning_unsafe, warning}, {error_unsafe, error}, {critical_unsafe, critical}, {alert_unsafe, alert}, {emergency_unsafe, emergency}]). -%% 日志等级定义 --define(DEBUG, 128). --define(INFO, 64). --define(NOTICE, 32). --define(WARNING, 16). --define(ERROR, 8). --define(CRITICAL, 4). --define(ALERT, 2). --define(EMERGENCY, 1). --define(LOG_NONE, 0). --define(RunShouldLog(Sink, Level), (rumUtil:levelToNum(Level) band element(1, rumConfig:get({Sink, loglevel}, {?LOG_NONE, []}))) /= 0). +-define(RunShouldLog(Sink, Level), (rumUtil:levelToNum(Level) band element(1, rumConfig:get({Sink, loglevel}, {?none, []}))) /= 0). --define(RunShouldLog(Level), (rumUtil:levelToNum(Level) band element(1, rumConfig:get(loglevel, {?LOG_NONE, []}))) /= 0). +-define(RunShouldLog(Level), (rumUtil:levelToNum(Level) band element(1, rumConfig:get(loglevel, {?none, []}))) /= 0). -define(RunNotify(Level, Pid, Format, Args), gen_emm:info_notify(rumEvent, {mWriteLog, rumMsg:new(io_lib:format(Format, Args), Level, [{pid, Pid}, {line, ?LINE}, {file, ?FILE}, {module, ?MODULE}], [])})). diff --git a/include/rumMsg.hrl b/include/rumMsg.hrl index 8065b39..bc52fc7 100644 --- a/include/rumMsg.hrl +++ b/include/rumMsg.hrl @@ -1,11 +1,11 @@ -record(rumMsg, { severity :: rumAtomLevel() - , pid - , node - , module - , function - , file - , line + , pid :: pid() + , node :: node() + , module :: module() + , function :: atom() + , file :: atom() + , line :: integer() , metadata :: [tuple()] , datetime :: binary() , timestamp :: non_neg_integer() diff --git a/src/backend/rumBkdThrottle.erl b/src/backend/rumBkdThrottle.erl index 7dfbe8d..ee8e44f 100644 --- a/src/backend/rumBkdThrottle.erl +++ b/src/backend/rumBkdThrottle.erl @@ -29,7 +29,7 @@ init([{sink, Sink}, Hwm, Window]) -> {ok, #state{sink = Sink, hwm = Hwm, window = Hwm - Window}}. handleCall(mGetLogLevel, State) -> - {reply, ?LOG_NONE, State}; + {reply, ?none, State}; handleCall({mSetLogLevel, _Level}, State) -> {reply, ok, State}; handleCall(_Msg, State) -> diff --git a/src/eRum.erl b/src/eRum.erl index 69789ce..2c5df11 100644 --- a/src/eRum.erl +++ b/src/eRum.erl @@ -1,5 +1,6 @@ -module(eRum). + -include("rumDef.hrl"). -include("rumMsg.hrl"). -include("rumCom.hrl"). @@ -15,13 +16,6 @@ , stop/0 %% log and log param - , log/4 - , log/5 - , log_unsafe/4 - , do_log/15 - , do_log/16 - , do_log_unsafe/16 - , dispatch_log/11 , dispatch_log/13 , safe_format/3 , safe_format_chop/3 @@ -86,92 +80,42 @@ start() -> stop() -> application:stop(eRum). -%% @doc Manually log a message into eRum without using the parse transform. --spec log(rumAtomLevel(), pid() | atom() | [tuple(), ...], string(), list()) -> ok | {error, lager_not_running}. -log(Level, Pid, Format, Args) when is_pid(Pid); is_atom(Pid) -> - dispatch_log(Level, [{pid, Pid}], Format, Args, ?RumDefTruncation); -log(Level, Metadata, Format, Args) when is_list(Metadata) -> - dispatch_log(Level, Metadata, Format, Args, ?RumDefTruncation). - -%% @doc Manually log a message into eRum without using the parse transform. --spec log(atom(), rumAtomLevel(), pid() | atom() | [tuple(), ...], string(), list()) -> ok | {error, lager_not_running}. -log(Sink, Level, Pid, Format, Args) when is_pid(Pid); is_atom(Pid) -> - dispatch_log(Sink, Level, [{pid, Pid}], Format, Args, ?RumDefTruncation, safe); -log(Sink, Level, Metadata, Format, Args) when is_list(Metadata) -> - dispatch_log(Sink, Level, Metadata, Format, Args, ?RumDefTruncation, safe). - -log_unsafe(Level, Metadata, Format, Args) when is_list(Metadata) -> - dispatch_log(?RumDefSink, Level, Metadata, Format, Args, ?RumDefTruncation, unsafe). - --spec dispatch_log(atom(), rumAtomLevel(), list(), string(), list() | none, pos_integer(), safe | unsafe) -> ok | {error, lager_not_running} | {error, {sink_not_configured, atom()}}. -%% this is the same check that the parse transform bakes into the module at compile time -%% see rumTransform (lines 173-216) -dispatch_log(Sink, Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, Size, Safety) when is_atom(Severity) -> - SeverityAsInt = rumUtil:levelToNum(Severity), - case {whereis(Sink), whereis(?RumDefSink), rumConfig:get({Sink, loglevel}, {?LOG_NONE, []})} of - {undefined, undefined, _} -> {error, lager_not_running}; - {undefined, _LagerEventPid0, _} -> {error, {sink_not_configured, Sink}}; - {SinkPid, _LagerEventPid1, {Level, Traces}} when Safety =:= safe andalso ((Level band SeverityAsInt) /= 0 orelse Traces /= []) -> - do_log(Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, Size, SeverityAsInt, Level, Traces, Sink, SinkPid); - {SinkPid, _LagerEventPid1, {Level, Traces}} when Safety =:= unsafe andalso ((Level band SeverityAsInt) /= 0 orelse Traces /= []) -> - do_log_unsafe(Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, Size, SeverityAsInt, Level, Traces, Sink, SinkPid); - _ -> ok +-spec dispatch_log(atom(), rumAtomLevel(), pid(), node(), atom(), atom(), atom(), integer(), list(), string(), list() | none, pos_integer(), safe | unsafe) -> ok | {error, lager_not_running} | {error, {sink_not_configured, atom()}}. +%% this is the same check that the parse transform bakes into the module at compile time see rumTransform (lines 173-216) +dispatch_log(Sink, Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, Size, Safety) -> + case ?eRumCfg:get(Sink) band Severity /= 0 of + true -> + do_log_impl(Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, Severity, Size, Sink, Safety); + _ -> + ok end. -%% 向后兼容使用eRum 2.x编译的梁 -do_log(Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, Size, SeverityAsInt, LevelThreshold, TraceFilters, SinkPid) -> - do_log(Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, Size, SeverityAsInt, LevelThreshold, TraceFilters, ?RumDefSink, SinkPid). - -%% @private Should only be called externally from code generated from the parse transform -do_log(Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, Size, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid) when is_atom(Severity) -> - FormatFun = fun() -> safe_format_chop(Format, Args, Size) end, - do_log_impl(Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid, FormatFun). - -do_log_impl(Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid, FormatFun) -> +do_log_impl(Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, Severity, Size, Sink, Safety) -> + %% IMY-todo TraceFilters = ? + TraceFilters = [], {Destinations, TraceSinkPid} = case TraceFilters of [] -> {[], undefined}; _ -> - {rumUtil:check_traces(Metadata, SeverityAsInt, TraceFilters, []), whereis(?RumTrackSink)} + {rumUtil:check_traces(Metadata, Severity, TraceFilters, []), whereis(?RumTrackSink)} end, - case (LevelThreshold band SeverityAsInt) /= 0 orelse Destinations /= [] of + + Msg = ?IIF(Args /= [], ?IIF(Safety == safe, eFmt:formatBin(Format, [Args], [{charsLimit, Size}]), eFmt:formatBin(Format, [Args])), Format), + LagerMsg = ?newMsg(Severity, Pid, Node, Module, Function, File, Line, Metadata, Destinations, rumUtil:nowMs(), Msg), + case rumConfig:get({Sink, async}, false) of true -> - Msg = - case Args of - A when is_list(A) -> - FormatFun(); - _ -> - Format - end, - LagerMsg = ?newMsg(Severity, Pid, Node, Module, Function, File, Line, Metadata, Destinations, rumUtil:nowMs(), Msg), - case rumConfig:get({Sink, async}, false) of - true -> - gen_emm:info_notify(SinkPid, {mWriteLog, LagerMsg}); - false -> - gen_emm:call_notify(SinkPid, {mWriteLog, LagerMsg}) - end, - case TraceSinkPid /= undefined of - true -> - gen_emm:info_notify(TraceSinkPid, {mWriteLog, LagerMsg}); - false -> - ok - end; + gen_emm:info_notify(Sink, {mWriteLog, LagerMsg}); + false -> + gen_emm:call_notify(Sink, {mWriteLog, LagerMsg}) + end, + case TraceSinkPid /= undefined of + true -> + gen_emm:info_notify(TraceSinkPid, {mWriteLog, LagerMsg}); false -> ok end. -%% @private Should only be called externally from code generated from the parse transform -%% Specifically, it would be level ++ `_unsafe' as in `info_unsafe'. -do_log_unsafe(Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, _Size, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid) when is_atom(Severity) -> - FormatFun = fun() -> unsafe_format(Format, Args) end, - do_log_impl(Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid, FormatFun). - - -%% backwards compatible with beams compiled with lager 2.x -dispatch_log(Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, Size) -> - dispatch_log(?RumDefSink, Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, Size, safe). - %% @doc Get lager metadata for current process -spec md() -> [{atom(), any()}]. md() -> diff --git a/src/eRum1.erl b/src/eRum1.erl new file mode 100644 index 0000000..bc6d1be --- /dev/null +++ b/src/eRum1.erl @@ -0,0 +1,679 @@ +-module(eRum1). + +-include("rumDef.hrl"). +-include("rumMsg.hrl"). +-include("rumCom.hrl"). + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). +-endif. + +%% API +-export([ + %% start stop + start/0 + , stop/0 + + %% log and log param + , dispatch_log/13 + , do_log/16 + , do_log_unsafe/16 + , safe_format/3 + , safe_format_chop/3 + , unsafe_format/2 + , md/0 + , md/1 + , getLogLevel/1 + , getLogLevel/2 + , setLogLevel/2 + , setLogLevel/3 + , setLogLevel/4 + , getLogLevels/1 + , updateLogevelCfg/1 + , setLogHwm/2 + , setLogHwm/3 + , setLogHwm/4 + , rotate_handler/1 + , rotate_handler/2 + , rotate_sink/1 + , rotate_all/0 + , posix_error/1 + + %% stack + , pr/2 + , pr/3 + , pr_stacktrace/1 + , pr_stacktrace/2 + + %% trace + , trace/2 + , trace/3 + , trace_file/2 + , trace_file/3 + , trace_file/4 + , trace_console/1 + , trace_console/2 + , install_trace/2 + , install_trace/3 + , remove_trace/1 + , trace_state/3 + , trace_func/3 + , list_all_sinks/0 + , clear_all_traces/0 + , clear_trace_by_destination/1 + , stop_trace/1 + , stop_trace/3 + , status/0 +]). + +-record(trace_func_state_v1, { + pid :: undefined | pid(), + level :: rumAtomLevel(), + count :: infinity | pos_integer(), + format_string :: string(), + timeout :: infinity | pos_integer(), + started = os:timestamp() :: erlang:timestamp() %% use os:timestamp for compatability +}). + +start() -> + application:ensure_all_started(eRum). + +stop() -> + application:stop(eRum). + +-spec dispatch_log(atom(), rumAtomLevel(), pid(), node(), atom(), atom(), atom(), integer(), list(), string(), list() | none, pos_integer(), safe | unsafe) -> ok | {error, lager_not_running} | {error, {sink_not_configured, atom()}}. +%% this is the same check that the parse transform bakes into the module at compile time see rumTransform (lines 173-216) +dispatch_log(Sink, Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, Size, Safety) -> + SeverityAsInt = rumUtil:levelToNum(Severity), + case {whereis(Sink), rumConfig:get({Sink, loglevel}, {?none, []})} of + {undefined, _} -> {error, lager_not_running}; + {SinkPid, {Level, Traces}} when Safety =:= safe andalso ((Level band SeverityAsInt) /= 0 orelse Traces /= []) -> + do_log(Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, Size, SeverityAsInt, Level, Traces, Sink, SinkPid); + {SinkPid, {Level, Traces}} when Safety =:= unsafe andalso ((Level band SeverityAsInt) /= 0 orelse Traces /= []) -> + do_log_unsafe(Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, Size, SeverityAsInt, Level, Traces, Sink, SinkPid); + _ -> ok + end. + +%% Should only be called externally from code generated from the parse transform +do_log(Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, Size, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid) -> + FormatFun = fun() -> safe_format_chop(Format, Args, Size) end, + do_log_impl(Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid, FormatFun). + +%% Should only be called externally from code generated from the parse transform Specifically, it would be level ++ `_unsafe' as in `info_unsafe'. +do_log_unsafe(Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, _Size, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid) -> + FormatFun = fun() -> unsafe_format(Format, Args) end, + do_log_impl(Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid, FormatFun). + +do_log_impl(Severity, Pid, Node, Module, Function, File, Line, Metadata, Format, Args, SeverityAsInt, LevelThreshold, TraceFilters, Sink, SinkPid, FormatFun) -> + {Destinations, TraceSinkPid} = + case TraceFilters of + [] -> + {[], undefined}; + _ -> + {rumUtil:check_traces(Metadata, SeverityAsInt, TraceFilters, []), whereis(?RumTrackSink)} + end, + case (LevelThreshold band SeverityAsInt) /= 0 orelse Destinations /= [] of + true -> + Msg = + case Args of + A when is_list(A) -> + FormatFun(); + _ -> + Format + end, + LagerMsg = ?newMsg(Severity, Pid, Node, Module, Function, File, Line, Metadata, Destinations, rumUtil:nowMs(), Msg), + case rumConfig:get({Sink, async}, false) of + true -> + gen_emm:info_notify(SinkPid, {mWriteLog, LagerMsg}); + false -> + gen_emm:call_notify(SinkPid, {mWriteLog, LagerMsg}) + end, + case TraceSinkPid /= undefined of + true -> + gen_emm:info_notify(TraceSinkPid, {mWriteLog, LagerMsg}); + false -> + ok + end; + false -> + ok + end. + +%% @doc Get lager metadata for current process +-spec md() -> [{atom(), any()}]. +md() -> + case erlang:get(?PdMdKey) of + undefined -> []; + MD -> MD + end. + +%% @doc Set lager metadata for current process. +%% Will badarg if you don't supply a list of {key, value} tuples keyed by atoms. +-spec md([{atom(), any()}, ...]) -> ok. +md(NewMD) when is_list(NewMD) -> + %% make sure its actually a real proplist + case lists:all( + fun({Key, _Value}) when is_atom(Key) -> true; + (_) -> false + end, NewMD) of + true -> + erlang:put(?PdMdKey, NewMD), + ok; + false -> + erlang:error(badarg) + end; +md(_) -> + erlang:error(badarg). + + +validate_trace_filters(Filters, Level, Backend) -> + Sink = proplists:get_value(sink, Filters, ?RumDefSink), + {Sink, + rumUtil:validate_trace({ + proplists:delete(sink, Filters), + Level, + Backend + }) + }. + +%% @doc Set the loglevel for a particular backend. +setLogLevel(Handler, Level) when is_atom(Level) -> + setLogLevel(?RumDefSink, Handler, undefined, Level). + +%% @doc Set the loglevel for a particular backend that has multiple identifiers +%% (eg. the file backend). +setLogLevel(Handler, Ident, Level) when is_atom(Level) -> + setLogLevel(?RumDefSink, Handler, Ident, Level). + +%% @doc Set the loglevel for a particular sink's backend that potentially has +%% multiple identifiers. (Use `undefined' if it doesn't have any.) +setLogLevel(Sink, Handler, Ident, Level) when is_atom(Level) -> + HandlerArg = + case Ident of + undefined -> Handler; + _ -> {Handler, Ident} + end, + Reply = gen_emm:call(Sink, HandlerArg, {mSetLogLevel, Level}, infinity), + updateLogevelCfg(Sink), + Reply. + + +%% @doc Get the loglevel for a particular backend on the default sink. In the case that the backend has multiple identifiers, the lowest is returned. +getLogLevel(Handler) -> + getLogLevel(?RumDefSink, Handler). + +%% @doc Get the loglevel for a particular sink's backend. In the case that the backend +%% has multiple identifiers, the lowest is returned. +getLogLevel(Sink, Handler) -> + case gen_emm:call(Sink, Handler, mGetLogLevel, infinity) of + Mask when is_integer(Mask) -> + case rumUtil:maskToLevels(Mask) of + [] -> none; + Levels -> hd(Levels) + end; + Y -> Y + end. + +getLogLevels(Sink) -> + [gen_emm:call(Sink, Handler, mGetLogLevel, infinity) || Handler <- gen_emm:which_epm(Sink)]. + +%% @doc Set the loghwm for the default sink. +setLogHwm(Handler, Hwm) when is_integer(Hwm) -> + setLogHwm(?RumDefSink, Handler, Hwm). + +%% @doc Set the loghwm for a particular backend. +setLogHwm(Sink, Handler, Hwm) when is_integer(Hwm) -> + gen_emm:call(Sink, Handler, {mSetLogHwm, Hwm}, infinity). + +%% @doc Set the loghwm (log high water mark) for file backends with multiple identifiers +setLogHwm(Sink, Handler, Ident, Hwm) when is_integer(Hwm) -> + gen_emm:call(Sink, {Handler, Ident}, {mSetLogHwm, Hwm}, infinity). + +%% @doc Print stacktrace in human readable form +pr_stacktrace(Stacktrace) -> + Stacktrace1 = + case rumUtil:get_env(reverse_pretty_stacktrace, true) of + true -> + lists:reverse(Stacktrace); + _ -> + Stacktrace + end, + pr_stacktrace_(Stacktrace1). + +pr_stacktrace_(Stacktrace) -> + Indent = "\n ", + lists:foldl( + fun(Entry, Acc) -> + Acc ++ Indent ++ binary_to_list(rumErrLoggerH:formatMfa(Entry)) + end, + [], + Stacktrace). + +pr_stacktrace(Stacktrace, {Class, Reason}) -> + case rumUtil:get_env(reverse_pretty_stacktrace, true) of + true -> + lists:flatten( + pr_stacktrace_(lists:reverse(Stacktrace)) ++ "\n" ++ io_lib:format("~s:~p", [Class, Reason])); + _ -> + lists:flatten( + io_lib:format("~s:~p", [Class, Reason]) ++ pr_stacktrace_(Stacktrace)) + end. + +rotate_sink(Sink) -> + Handlers = rumConfig:global_get(handlers), + RotateHandlers = lists:filtermap( + fun({Handler, _, S}) when S == Sink -> {true, {Handler, Sink}}; + (_) -> false + end, + Handlers), + rotate_handlers(RotateHandlers). + +rotate_all() -> + rotate_handlers(lists:map(fun({H, _, S}) -> {H, S} end, + rumConfig:global_get(handlers))). + + +rotate_handlers(Handlers) -> + [rotate_handler(Handler, Sink) || {Handler, Sink} <- Handlers]. + + +rotate_handler(Handler) -> + Handlers = rumConfig:global_get(handlers), + case lists:keyfind(Handler, 1, Handlers) of + {Handler, _, Sink} -> rotate_handler(Handler, Sink); + false -> ok + end. + +rotate_handler(Handler, Sink) -> + gen_emm:call(Sink, Handler, mRotate, ?RumRotateTimeout). + + +trace_file(File, Filter) -> + trace_file(File, Filter, debug, []). + +trace_file(File, Filter, Level) when is_atom(Level) -> + trace_file(File, Filter, Level, []); + +trace_file(File, Filter, Options) when is_list(Options) -> + trace_file(File, Filter, debug, Options). + +trace_file(File, Filter, Level, Options) -> + FileName = rumUtil:parsePath(File), + case validate_trace_filters(Filter, Level, {rumBkdFile, FileName}) of + {Sink, {ok, Trace}} -> + Handlers = rumConfig:global_get(handlers, []), + %% check if this file backend is already installed + Res = + case rumUtil:find_file(FileName, Handlers) of + false -> + %% install the handler + LogFileConfig = + lists:keystore(level, 1, + lists:keystore(file, 1, + Options, + {file, FileName}), + {level, none}), + HandlerInfo = + eRum_app:startHandler(Sink, {rumBkdFile, FileName}, LogFileConfig), + rumConfig:global_set(handlers, [HandlerInfo | Handlers]), + {ok, installed}; + {_Watcher, _Handler, Sink} -> + {ok, exists}; + {_Watcher, _Handler, _OtherSink} -> + {error, file_in_use} + end, + case Res of + {ok, _} -> + add_trace_to_loglevel_config(Trace, Sink), + {ok, {{rumBkdFile, FileName}, Filter, Level}}; + {error, _} = E -> + E + end; + {_Sink, Error} -> + Error + end. + +trace_console(Filter) -> + trace_console(Filter, debug). + +trace_console(Filter, Level) -> + trace(rumBkdConsole, Filter, Level). + +trace(Backend, Filter) -> + trace(Backend, Filter, debug). + +trace({rumBkdFile, File}, Filter, Level) -> + trace_file(File, Filter, Level); + +trace(Backend, Filter, Level) -> + case validate_trace_filters(Filter, Level, Backend) of + {Sink, {ok, Trace}} -> + add_trace_to_loglevel_config(Trace, Sink), + {ok, {Backend, Filter, Level}}; + {_Sink, Error} -> + Error + end. + +stop_trace(Backend, Filter, Level) -> + case validate_trace_filters(Filter, Level, Backend) of + {Sink, {ok, Trace}} -> + stop_trace_int(Trace, Sink); + {_Sink, Error} -> + Error + end. + +stop_trace({Backend, Filter, Level}) -> + stop_trace(Backend, Filter, Level). + +%% Important: validate_trace_filters orders the arguments of +%% trace tuples differently than the way outside callers have +%% the trace tuple. +%% +%% That is to say, outside they are represented as +%% `{Backend, Filter, Level}' +%% +%% and when they come back from validation, they're +%% `{Filter, Level, Backend}' +stop_trace_int({_Filter, _Level, Backend} = Trace, Sink) -> + {Level, Traces} = rumConfig:get({Sink, loglevel}), + NewTraces = lists:delete(Trace, Traces), + _ = rumUtil:trace_filter([element(1, T) || T <- NewTraces]), + %MinLevel = minimum_loglevel(get_loglevels() ++ get_trace_levels(NewTraces)), + rumConfig:set({Sink, loglevel}, {Level, NewTraces}), + case getLogLevel(Sink, Backend) of + none -> + %% check no other traces point here + case lists:keyfind(Backend, 3, NewTraces) of + false -> + gen_emm:delEpm(Sink, Backend, []), + rumConfig:global_set(handlers, + lists:keydelete(Backend, 1, + rumConfig:global_get(handlers))); + _ -> + ok + end; + _ -> + ok + end, + ok. + +%% @doc installs a lager trace handler into the target process (using sys:install) at the specified level. +-spec install_trace(pid(), rumAtomLevel()) -> ok. +install_trace(Pid, Level) -> + install_trace(Pid, Level, []). + +-spec install_trace(pid(), rumAtomLevel(), [{count, infinity | pos_integer()} | {format_string, string()} | {timeout, timeout()}]) -> ok. +install_trace(Pid, Level, Options) -> + sys:install(Pid, {fun ?MODULE:trace_func/3, trace_state(Pid, Level, Options)}). + +%% @doc remove a previously installed lager trace handler from the target process. +-spec remove_trace(pid()) -> ok. +remove_trace(Pid) -> + sys:remove(Pid, fun ?MODULE:trace_func/3). + +list_all_sinks() -> + sets:to_list( + lists:foldl(fun({_Watcher, _Handler, Sink}, Set) -> + sets:add_element(Sink, Set) + end, + sets:new(), + rumConfig:global_get(handlers, []))). + +clear_traces_by_sink(Sinks) -> + lists:foreach(fun(S) -> + {Level, _Traces} = + rumConfig:get({S, loglevel}), + rumConfig:set({S, loglevel}, + {Level, []}) + end, + Sinks). + +clear_trace_by_destination(ID) -> + Sinks = lists:sort(list_all_sinks()), + Traces = find_traces(Sinks), + [stop_trace_int({Filter, Level, Destination}, Sink) || {Sink, {Filter, Level, Destination}} <- Traces, Destination == ID]. + + +clear_all_traces() -> + Handlers = rumConfig:global_get(handlers, []), + clear_traces_by_sink(list_all_sinks()), + _ = rumUtil:trace_filter(none), + rumConfig:global_set(handlers, + lists:filter( + fun({Handler, _Watcher, Sink}) -> + case getLogLevel(Sink, Handler) of + none -> + gen_emm:delEpm(Sink, Handler, []), + false; + _ -> + true + end + end, Handlers)). + +find_traces(Sinks) -> + lists:foldl(fun(S, Acc) -> + {_Level, Traces} = rumConfig:get({S, loglevel}), + Acc ++ lists:map(fun(T) -> {S, T} end, Traces) + end, + [], + Sinks). + +status() -> + Handlers = rumConfig:global_get(handlers, []), + Sinks = lists:sort(list_all_sinks()), + Traces = find_traces(Sinks), + TraceCount = case length(Traces) of + 0 -> 1; + N -> N + end, + Status = ["Lager status:\n", + [begin + Level = getLogLevel(Sink, Handler), + get_sink_handler_status(Sink, Handler, Level) + end || {Handler, _Watcher, Sink} <- lists:sort(fun({_, _, S1}, + {_, _, S2}) -> S1 =< S2 end, + Handlers)], + "Active Traces:\n", + [begin + LevelName = + case rumUtil:maskToLevels(Level) of + [] -> none; + Levels -> hd(Levels) + end, + + io_lib:format("Tracing messages matching ~p (sink ~s) at level ~p to ~p\n", + [Filter, Sink, LevelName, Destination]) + end || {Sink, {Filter, Level, Destination}} <- Traces], + [ + "Tracing Reductions:\n", + case ?RumDefTracer:info('query') of + {null, false} -> ""; + Query -> io_lib:format("~p~n", [Query]) + end + ], + [ + "Tracing Statistics:\n ", + [begin + [" ", atom_to_list(Table), ": ", + integer_to_list(?RumDefTracer:info(Table) div TraceCount), + "\n"] + end || Table <- [input, output, filter]] + ]], + io:put_chars(Status). + +get_sink_handler_status(Sink, Handler, Level) -> + case Handler of + {rumBkdFile, File} -> + io_lib:format("File ~ts (~s) at level ~p\n", [File, Sink, Level]); + rumBkdConsole -> + io_lib:format("Console (~s) at level ~p\n", [Sink, Level]); + _ -> + [] + end. + +%% @doc Try to convert an atom to a posix error, but fall back on printing the +%% term if its not a valid posix error code. +posix_error(Error) when is_atom(Error) -> + case erl_posix_msg:message(Error) of + "unknown POSIX error" -> atom_to_list(Error); + Message -> Message + end; +posix_error(Error) -> + safe_format_chop("~p", [Error], ?RumDefTruncation). + +%% @private +add_trace_to_loglevel_config(Trace, Sink) -> + {MinLevel, Traces} = rumConfig:get({Sink, loglevel}), + case lists:member(Trace, Traces) of + false -> + NewTraces = [Trace | Traces], + _ = rumUtil:trace_filter([element(1, T) || T <- NewTraces]), + rumConfig:set({Sink, loglevel}, {MinLevel, [Trace | Traces]}); + _ -> + ok + end. + +%% @doc recalculate min log level +updateLogevelCfg(error_logger) -> + %% Not a sink under our control, part of the Erlang logging + %% utility that error_logger_lager_h attaches to + true; +updateLogevelCfg(Sink) -> + {_, Traces} = rumConfig:get({Sink, loglevel}, {ignore_me, []}), + MinLog = minLogLevel(getLogLevels(Sink)), + rumConfig:set({Sink, loglevel}, {MinLog, Traces}). + +minLogLevel(Levels) -> + lists:foldl( + fun(Mask, Acc) -> + Mask bor Acc + end, 0, Levels). + +%% @doc Print the format string `Fmt' with `Args' safely with a size +%% limit of `Limit'. If the format string is invalid, or not enough +%% arguments are supplied 'FORMAT ERROR' is printed with the offending +%% arguments. The caller is NOT crashed. + +safe_format(Fmt, Args, Limit) -> + safe_format(Fmt, Args, Limit, []). + +safe_format(Fmt, Args, Limit, Options) -> + try rumTruncIo:format(Fmt, Args, Limit, Options) + catch + _:_ -> rumTruncIo:format("FORMAT ERROR: ~p ~p", [Fmt, Args], Limit) + end. + +%% @private +safe_format_chop(Fmt, Args, Limit) -> + safe_format(Fmt, Args, Limit, [{chomp, true}]). + +%% @private Print the format string `Fmt' with `Args' without a size limit. +%% This is unsafe because the output of this function is unbounded. +%% +%% Log messages with unbounded size will kill your application dead as +%% OTP mechanisms stuggle to cope with them. So this function is +%% intended only for messages which have a reasonable bounded +%% size before they're formatted. +%% +%% If the format string is invalid or not enough arguments are +%% supplied a 'FORMAT ERROR' message is printed instead with the +%% offending arguments. The caller is NOT crashed. +unsafe_format(Fmt, Args) -> + try io_lib:format(Fmt, Args) + catch + _:_ -> io_lib:format("FORMAT ERROR: ~p ~p", [Fmt, Args]) + end. + +%% @doc Print a record or a list of records lager found during parse transform +pr(Record, Module) when is_tuple(Record), is_atom(element(1, Record)) -> + pr(Record, Module, []); +pr(List, Module) when is_list(List) -> + pr(List, Module, []); +pr(Record, _) -> + Record. + +%% @doc Print a record or a list of records lager found during parse transform +pr(Record, Module, Options) when is_tuple(Record), is_atom(element(1, Record)), is_list(Options) -> + try + case is_record_known(Record, Module) of + false -> + Record; + {RecordName, RecordFields} -> + {'$lager_record', RecordName, + zip(RecordFields, tl(tuple_to_list(Record)), Module, Options, [])} + end + catch + error:undef -> + Record + end; +pr([Head | Tail], Module, Options) when is_list(Options) -> + [pr(Head, Module, Options) | pr(Tail, Module, Options)]; +pr(Record, _, _) -> + Record. + +zip([FieldName | RecordFields], [FieldValue | Record], Module, Options, ToReturn) when is_list(FieldValue) -> + zip(RecordFields, Record, Module, Options, + [{FieldName, pr(FieldValue, Module, Options)} | ToReturn]); +zip([FieldName | RecordFields], [FieldValue | Record], Module, Options, ToReturn) -> + Compress = lists:member(compress, Options), + case is_tuple(FieldValue) andalso + tuple_size(FieldValue) > 0 andalso + is_atom(element(1, FieldValue)) andalso + is_record_known(FieldValue, Module) of + false when Compress andalso FieldValue =:= undefined -> + zip(RecordFields, Record, Module, Options, ToReturn); + false -> + zip(RecordFields, Record, Module, Options, [{FieldName, FieldValue} | ToReturn]); + _Else -> + F = {FieldName, pr(FieldValue, Module, Options)}, + zip(RecordFields, Record, Module, Options, [F | ToReturn]) + end; +zip([], [], _Module, _Compress, ToReturn) -> + lists:reverse(ToReturn). + +is_record_known(Record, Module) -> + Name = element(1, Record), + Attrs = Module:module_info(attributes), + case lists:keyfind(lager_records, 1, Attrs) of + false -> false; + {lager_records, Records} -> + case lists:keyfind(Name, 1, Records) of + false -> false; + {Name, RecordFields} -> + case (tuple_size(Record) - 1) =:= length(RecordFields) of + false -> false; + true -> {Name, RecordFields} + end + end + end. + + +%% @private +trace_func(#trace_func_state_v1{pid = Pid, level = Level, format_string = Fmt} = FuncState, Event, ProcState) -> + _ = eRum:log(Level, Pid, Fmt, [Event, ProcState]), + check_timeout(decrement_count(FuncState)). + +%% @private +trace_state(Pid, Level, Options) -> + #trace_func_state_v1{pid = Pid, + level = Level, + count = proplists:get_value(count, Options, infinity), + timeout = proplists:get_value(timeout, Options, infinity), + format_string = proplists:get_value(format_string, Options, "TRACE ~p ~p")}. + +decrement_count(#trace_func_state_v1{count = infinity} = FuncState) -> + FuncState; +decrement_count(#trace_func_state_v1{count = 1}) -> + %% hit the counter limit + done; +decrement_count(#trace_func_state_v1{count = Count} = FuncState) -> + FuncState#trace_func_state_v1{count = Count - 1}. + +check_timeout(#trace_func_state_v1{timeout = infinity} = FuncState) -> + FuncState; +check_timeout(#trace_func_state_v1{timeout = Timeout, started = Started} = FuncState) -> + case (timer:now_diff(os:timestamp(), Started) / 1000) > Timeout of + true -> + done; + false -> + FuncState + end. \ No newline at end of file diff --git a/src/mgrKiller/rumMgrKiller.erl b/src/mgrKiller/rumMgrKiller.erl index d40763b..7a61dac 100644 --- a/src/mgrKiller/rumMgrKiller.erl +++ b/src/mgrKiller/rumMgrKiller.erl @@ -27,7 +27,7 @@ init([KillerHWM, KillerReinstallAfter]) -> {ok, #state{killerHwm = KillerHWM, killerReTime = KillerReinstallAfter}}. handleCall(mGetLogLevel, State) -> - {replay, ?LOG_NONE, State}; + {replay, ?none, State}; handleCall({mSetLogLevel, _Level}, State) -> {replay, ok, State}; handleCall(mGetSettings, State = #state{killerHwm = KillerHWM, killerReTime = KillerReinstallAfter}) -> diff --git a/src/utils/rumUtil.erl b/src/utils/rumUtil.erl index 1b56b74..640e760 100644 --- a/src/utils/rumUtil.erl +++ b/src/utils/rumUtil.erl @@ -1,5 +1,6 @@ -module(rumUtil). +-include("rumCom.hrl"). -include("rumDef.hrl"). -include_lib("kernel/include/file.hrl"). @@ -35,42 +36,28 @@ , get_opt/3 ]). --ifdef(TEST). --export([ - create_test_dir/0 - , get_test_dir/0 - , delete_test_dir/0 - , set_dir_permissions/2 - , safe_application_load/1 - , safe_write_file/2 -]). --include_lib("eunit/include/eunit.hrl"). --endif. - - - levels() -> [debug, info, notice, warning, error, critical, alert, emergency, none]. -levelToNum(debug) -> ?DEBUG; -levelToNum(info) -> ?INFO; -levelToNum(notice) -> ?NOTICE; -levelToNum(warning) -> ?WARNING; -levelToNum(error) -> ?ERROR; -levelToNum(critical) -> ?CRITICAL; -levelToNum(alert) -> ?ALERT; -levelToNum(emergency) -> ?EMERGENCY; -levelToNum(none) -> ?LOG_NONE. - -numToLevel(?DEBUG) -> debug; -numToLevel(?INFO) -> info; -numToLevel(?NOTICE) -> notice; -numToLevel(?WARNING) -> warning; -numToLevel(?ERROR) -> error; -numToLevel(?CRITICAL) -> critical; -numToLevel(?ALERT) -> alert; -numToLevel(?EMERGENCY) -> emergency; -numToLevel(?LOG_NONE) -> none. +levelToNum(debug) -> ?debug; +levelToNum(info) -> ?info; +levelToNum(notice) -> ?notice; +levelToNum(warning) -> ?warning; +levelToNum(error) -> ?error; +levelToNum(critical) -> ?critical; +levelToNum(alert) -> ?alert; +levelToNum(emergency) -> ?emergency; +levelToNum(none) -> ?none. + +numToLevel(?debug) -> debug; +numToLevel(?info) -> info; +numToLevel(?notice) -> notice; +numToLevel(?warning) -> warning; +numToLevel(?error) -> error; +numToLevel(?critical) -> critical; +numToLevel(?alert) -> alert; +numToLevel(?emergency) -> emergency; +numToLevel(?none) -> none. levelToChr(debug) -> $D; levelToChr(info) -> $I;