Bladeren bron

ft: 代码格式化

master
SisMaker 4 jaren geleden
bovenliggende
commit
26cde06218
1 gewijzigde bestanden met toevoegingen van 0 en 679 verwijderingen
  1. +0
    -679
      src/eRum1.erl

+ 0
- 679
src/eRum1.erl Bestand weergeven

@ -1,679 +0,0 @@
-module(eRum1).
-include("rumDef.hrl").
-include("rumCom.hrl").
%% API
-export([
%% start stop
start/0
, stop/0
%% log and log param
, dispatch_log/13
, do_log/16
, do_log_unsafe/16
, do_log_impl/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,
NowMs = rumTime:nowMs(),
NowStr = rumUtil:msToBinStr(NowMs),
RumMsg = #rumMsg{severity = Severity, pid = Pid, node = Node, module = Module, function = Function, line = Line, metadata = Metadata, datetime = NowStr, timestamp = NowMs, message = Msg, destinations = Destinations},
case rumConfig:get({Sink, async}, false) of
true ->
gen_emm:info_notify(SinkPid, {mWriteLog, RumMsg});
_ ->
gen_emm:call_notify(SinkPid, {mWriteLog, RumMsg})
end,
case TraceSinkPid /= undefined of
true ->
gen_emm:info_notify(TraceSinkPid, {mWriteLog, RumMsg});
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(rPrettyStrace, 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(rPrettyStrace, 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 <b>only</b> 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.

Laden…
Annuleren
Opslaan