Sfoglia il codice sorgente

ft: 命令修改整理

master
SisMaker 4 anni fa
parent
commit
0c109f50e1
14 ha cambiato i file con 135 aggiunte e 1019 eliminazioni
  1. +7
    -7
      README.md
  2. +7
    -9
      include/eRum.hrl
  3. +40
    -52
      include/rumDef.hrl
  4. +5
    -1
      include/rumMsg.hrl
  5. +10
    -10
      src/backend/rumBkdConsole.erl
  6. +44
    -44
      src/backend/rumBkdFile.erl
  7. +8
    -8
      src/crashLog/rumCrashLog.erl
  8. +5
    -5
      src/eRum.erl
  9. +2
    -4
      src/eRum1.erl
  10. +1
    -1
      src/errLogger/rumErrLoggerH.erl
  11. +2
    -2
      src/formatter/rumFormatTer.erl
  12. +3
    -6
      src/utils/rumStdlib.erl
  13. +0
    -869
      src/utils/rumTruncIo.erl
  14. +1
    -1
      src/utils/rumUtil.erl

+ 7
- 7
README.md Vedi File

@ -121,16 +121,16 @@ below). If async values are not configured, no overload protection will be appli
自定义格式
-----------------
All loggers have a default formatting that can be overriden. A formatter is any module that
All loggers have a default formatting that can be overriden. A formatTer is any module that
exports `format(#lager_log_message{},Config#any())`. It is specified as part of the configuration for the backend:
```erlang
{eRum, [
{handlers, [
{lager_console_backend, [{level, info}, {formatter, lager_default_formatter},
{formatter_config, [time, " [",severity, "] ", message, "\n"]}]},
{lager_file_backend, [{file, "error.log"}, {level, error}, {formatter, lager_default_formatter},
{formatter_config, [date, " ", time, " [", severity, "] ",pid, " ", message, "\n"]}]},
{lager_console_backend, [{level, info}, {formatTer, lager_default_formatter},
{formatCfg, [time, " [",severity, "] ", message, "\n"]}]},
{lager_file_backend, [{file, "error.log"}, {level, error}, {formatTer, lager_default_formatter},
{formatCfg, [date, " ", time, " [", severity, "] ",pid, " ", message, "\n"]}]},
{lager_file_backend, [{file, "console.log"}, {level, info}]}
]}
]}.
@ -438,8 +438,8 @@ If you don't like the default colors, they are also configurable; see the `.app.
The output will be colored from the first occurrence of the atom color in the formatting configuration. For example:
```erlang
{lager_console_backend, [{level, info}, {formatter, lager_default_formatter},
{formatter_config, [time, color, " [", severity, "] ", message, "\e[0m\r\n"]}]]}
{lager_console_backend, [{level, info}, {formatTer, lager_default_formatter},
{formatCfg, [time, color, " [", severity, "] ", message, "\e[0m\r\n"]}]]}
```
This will make the entire log message, except time, colored. The escape sequence before the line break is needed in

+ 7
- 9
include/eRum.hrl Vedi File

@ -3,15 +3,13 @@
%% eRumCfg模块初始化了 使eRum的App启动后请调用一次
-define(eRumInit(), ?eRumInit(?RumDefSink)).
-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
case ets:info(?eRumEts) of
undefined ->
ets:new(?eRumEts, [named_table]),
ets:insert(?eRumEts, {Sink, ?none}),
rumKvsToBeam:load(?eRumCfg, [{Sink, ?none}]);
_ ->
ignore
end).
%%

+ 40
- 52
include/rumDef.hrl Vedi File

@ -1,4 +1,5 @@
-include("rumCom.hrl").
-include("rumMsg.hrl").
%%
-define(RumAppName, eRum).
@ -19,16 +20,16 @@
-define(RumRotateTimeout, 100000).
%%
-define(RumDefTracer, lager_default_tracer).
-define(RumErrLogSink, error_logger_lager_event).
-define(RumDefTracer, rumDefTracer).
-define(RumErrLogSink, errLoggerEvent).
%% rumBkdConsole的选项
-type rumConsoleOpt() ::
{id, atom() | {atom(), atom()}} |
{use_stderr, boolean()} |
{group_leader, false | pid() | atom()} |
{formatter, atom()} |
{formatter_config, list()}.
{formatTer, atom()} |
{formatCfg, list()}.
%% rumBkdFile的选项
-type rumFileOpt() ::
@ -38,18 +39,18 @@
{date, string()} |
{count, non_neg_integer()} |
{rotator, atom()} |
{high_water_mark, non_neg_integer()} |
{hWM, non_neg_integer()} |
%% 使
{flush_queue, boolean()} |
{flushQueue, boolean()} |
%% flush_queue为true0
%% flush_queue为true
{flush_threshold, non_neg_integer()} |
{sync_interval, non_neg_integer()} |
{sync_size, non_neg_integer()} |
{sync_on, rumAtomLevel()} |
{check_interval, non_neg_integer()} |
{formatter, atom()} |
{formatter_config, term()}.
{flushThr, non_neg_integer()} |
{syncInt, non_neg_integer()} |
{syncSize, non_neg_integer()} |
{syncOn, rumAtomLevel()} |
{checkInt, non_neg_integer()} |
{formatTer, atom()} |
{formatCfg, term()}.
%% BkdFile选项默认值
-define(RumDefLogLevel, info).
@ -58,13 +59,13 @@
-define(RumDefRotateCnt, 5).
-define(RumDefRotateMod, rumRotatorIns).
-define(RumDefSyncLevel, error).
-define(RumDefSyncInterval, 1000).
-define(RumDefSyncInt, 1000).
-define(RumDefSyncSize, 1024 * 64). %% 64kb
-define(RumDefCheckInterval, 1000).
-define(RumDefCheckInt, 1000).
-define(RumDefCheckHWM, 1000). %% IMY-todo修正该默认值
-define(RumDefFlushQueue, true).
-define(RumDefFlushThreshold, 0). %% IMY-todo修正该默认值
-define(RumDefFormatter, rumFormatter).
-define(RumDefFlushThr, 0). %% IMY-todo修正该默认值
-define(RumDefFormatTer, rumFormatTer).
-define(RumDefFormatterCfg, []).
%%
@ -88,7 +89,7 @@
%% If true, flush notify messages from msg queue at overload
%% true
, flushQueue = true :: boolean()
, flushThreshold = 0 :: integer()
, flushThr = 0 :: integer()
%% timer
, timer = make_ref() :: reference()
%% optional filter fun to avoid counting suppressed messages against HWM totals
@ -101,51 +102,38 @@
-type rumAtomLevel() :: none | debug | info | notice | warning | error | critical | alert | emergency.
-type rumMaskLevel() :: 0..256.
%%
-define(RumLevels, [debug, info, notice, warning, error, critical, alert, emergency, none]).
%% 使
-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(RunShouldLog(Sink, Level), (rumUtil:levelToNum(Level) band ?eRumCfg:get(Sink)) /= 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, {?none, []}))) /= 0).
-define(RunShouldLog(Level), (rumUtil:levelToNum(Level) band rumConfig:get(?RumDefSink)) /= 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}], [])})).
LagerMsg = ?newMsg(Level, Pid, node(), ?MODULE, ?FUNCTION_NAME, ?LINE, [], [], rumUtil:nowMs(), eFmt:formatBin(Format, Args)),
gen_emm:info_notify(?RumDefSink, {mWriteLog, LagerMsg})).
%%使
-ifdef(TEST).
-define(INT_LOG(Level, Format, Args),
case ?RunShouldLog(Level) of
true ->
?RunNotify(Level, self(), Format, Args);
_ ->
ok
end).
-else.
-define(INT_LOG(Level, Format, Args),
Self = self(),
%%gen_event处理程序调用gen_eventwhich_handlers的死锁
spawn(fun() ->
case catch (gen_event:which_handlers(rumEvent)) of
X when X == []; X == {'EXIT', noproc}; X == [rumBkdThrottle] ->
%% there's no handlers yet or lager isn't running, try again
%% in half a second.
timer:sleep(500),
?RunNotify(Level, Self, Format, Args);
_ ->;
case ?RunShouldLog(Level) of
true ->
?RunNotify(Level, Self, Format, Args);
_ ->;
ok
end
end
end)).
-endif.
spawn(
fun() ->
case catch (gen_emm:which_epm(?RumDefSink)) of
X when X == []; X == {'EXIT', noproc}; X == [rumBkdThrottle] ->
%% there's no handlers yet or lager isn't running, try again
%% in half a second.
timer:sleep(500),
?RunNotify(Level, Self, Format, Args);
_ ->
case ?RunShouldLog(Level) of
true ->;
?RunNotify(Level, Self, Format, Args);
_ ->
ok
end
end
end)).

+ 5
- 1
include/rumMsg.hrl Vedi File

@ -1,3 +1,6 @@
-ifndef(__RumMsg__).
-define(__RumMsg__, 1).
-record(rumMsg, {
severity :: rumAtomLevel()
, pid :: pid()
@ -26,4 +29,5 @@
, timestamp = TimeMs
, message = Msg
, destinations = Destinations
}).
}).
-endif.

+ 10
- 10
src/backend/rumBkdConsole.erl Vedi File

@ -2,8 +2,8 @@
%% Configuration is a proplist with the following keys:
%%`level' - log level to use
%%`use_stderr' - either `true' or `false', defaults to false. If set to true, use standard error to output console log messages
%%`formatter' - the module to use when formatting log messages. Defaults to `rumFormatter'
%%`formatter_config' - the format configuration string. Defaults to `time [ severity ] message'
%%`formatTer' - the module to use when formatting log messages. Defaults to `rumFormatTer'
%%`formatCfg' - the format configuration string. Defaults to `time [ severity ] message'
-behaviour(gen_emm).
@ -14,7 +14,7 @@
-define(TERSE_FORMAT, [time, " ", color, "[", severity, "] ", message]).
-define(RumDefConsoleFmtCfg, ?TERSE_FORMAT ++ [eol()]).
-define(RumDefConsoleOpts, [{use_stderr, false}, {group_leader, false}, {id, ?MODULE}, {formatter, rumFormatter}, {formatter_config, ?RumDefConsoleFmtCfg}]).
-define(RumDefConsoleOpts, [{use_stderr, false}, {group_leader, false}, {id, ?MODULE}, {formatTer, ?RumDefFormatTer}, {formatCfg, ?RumDefConsoleFmtCfg}]).
-export([
init/1
@ -29,7 +29,7 @@
id :: atom() | {atom(), any()}
, level :: rumMaskLevel()
, out = user :: user | standard_error | pid()
, formatter :: atom()
, formatTer :: atom()
, formatCfg :: any()
, colors = [] :: list()
}).
@ -52,9 +52,9 @@ init(Opts) ->
Colors = ?IIF(rumUtil:get_env(colored, false), rumUtil:get_env(colors, []), []),
Level = rumUtil:get_opt(level, Opts, undefined),
LevelMask = rumUtil:configToMask(Level),
[UseErr, GroupLeader, Id, Formatter, Config] = [rumUtil:get_opt(Key, Opts, Def) || {Key, Def} <- ?RumDefConsoleOpts],
[UseErr, GroupLeader, Id, FormatTer, FormatCfg] = [rumUtil:get_opt(Key, Opts, Def) || {Key, Def} <- ?RumDefConsoleOpts],
Out = ?IIF(UseErr, standard_error, ?IIF(GroupLeader == false, user, begin erlang:monitor(process, GroupLeader), GroupLeader end)),
{ok, #state{level = LevelMask, id = Id, out = Out, formatter = Formatter, formatCfg = Config, colors = Colors}}
{ok, #state{level = LevelMask, id = Id, out = Out, formatTer = FormatTer, formatCfg = FormatCfg, colors = Colors}}
end.
checkOpts([]) -> true;
@ -64,9 +64,9 @@ checkOpts([{level, Level} | T]) ->
?IIF(rumUtil:validateLogLevel(Level) /= false, checkOpts(T), {error, {bad_level, Level}});
checkOpts([{use_stderr, Flag} | T]) when is_boolean(Flag) ->
checkOpts(T);
checkOpts([{formatter, M} | T]) when is_atom(M) ->
checkOpts([{formatTer, M} | T]) when is_atom(M) ->
checkOpts(T);
checkOpts([{formatter_config, C} | T]) when is_list(C) ->
checkOpts([{formatCfg, C} | T]) when is_list(C) ->
checkOpts(T);
checkOpts([{group_leader, L} | T]) when is_pid(L) ->
checkOpts(T);
@ -86,10 +86,10 @@ handleCall(_Msg, State) ->
?ERR("~p call receive unexpect msg ~p ~n ", [?MODULE, _Msg]),
{reply, ok, State}.
handleEvent({mWriteLog, Message}, #state{level = Level, out = Out, formatter = Formatter, formatCfg = FormatConfig, colors = Colors, id = ID}) ->
handleEvent({mWriteLog, Message}, #state{level = Level, out = Out, formatTer = FormatTer, formatCfg = FormatCfg, colors = Colors, id = ID}) ->
case rumUtil:isLoggAble(Message, Level, ID) of
true ->
io:put_chars(Out, Formatter:format(Message, FormatConfig, Colors)),
io:put_chars(Out, FormatTer:format(Message, FormatCfg, Colors)),
kpS;
_ ->
kpS

+ 44
- 44
src/backend/rumBkdFile.erl Vedi File

@ -6,7 +6,7 @@
%% This backend supports external and internal log rotation and will re-open handles to files if the inode changes.
%% It will also rotate the files itself if the size of the file exceeds the `size' and keep `count' rotated files.
%% `date' is an alternate rotation trigger, based on time. See the README for documentation.
%% For performance, the file backend does delayed writes, although it will sync at specific log levels, configured via the `sync_on' option.
%% For performance, the file backend does delayed writes, although it will sync at specific log levels, configured via the `syncOn' option.
%% By default the error level or above will trigger a sync.
-behaviour(gen_emm).
@ -33,18 +33,18 @@
level :: rumMaskLevel(),
fd :: file:io_device() | undefined,
inode :: integer() | undefined,
ctime :: file:date_time() | undefined,
cTime :: file:date_time() | undefined,
flap = false :: boolean(),
size = 0 :: integer(),
date :: undefined | string(),
count = 10 :: integer(),
rotator = lager_util :: atom(),
shaper :: rumShaper(),
formatter :: atom(),
formatterConfig :: any(),
formatTer :: atom(),
formatCfg :: any(),
syncOn :: integer(),
checkInterval = ?RumDefCheckInterval :: non_neg_integer(), %%
syncInterval = ?RumDefSyncInterval :: non_neg_integer(),
checkInt = ?RumDefCheckInt :: non_neg_integer(), %%
syncInt = ?RumDefSyncInt :: non_neg_integer(),
syncSize = ?RumDefSyncSize :: non_neg_integer(),
lastCheck = rumTime:nowMs() :: erlang:timestamp(), %%
osType :: atom()
@ -59,35 +59,35 @@ init(Opts) ->
Size = rumUtil:get_opt(size, Opts, ?RumDefRotateSize),
Count = rumUtil:get_opt(count, Opts, ?RumDefRotateCnt),
Rotator = rumUtil:get_opt(rotator, Opts, ?RumDefRotateMod),
HighWaterMark = rumUtil:get_opt(high_water_mark, Opts, ?RumDefCheckHWM),
Flush = rumUtil:get_opt(flush_queue, Opts, ?RumDefFlushQueue),
FlushThr = rumUtil:get_opt(flush_threshold, Opts, ?RumDefFlushThreshold),
SyncInterval = rumUtil:get_opt(sync_interval, Opts, ?RumDefSyncInterval),
CfgCheckInterval = rumUtil:get_opt(check_interval, Opts, ?RumDefCheckInterval),
SyncSize = rumUtil:get_opt(sync_size, Opts, ?RumDefSyncSize),
CfgSyncOn = rumUtil:get_opt(sync_on, Opts, ?RumDefSyncLevel),
Formatter = rumUtil:get_opt(formatter, Opts, ?RumDefFormatter),
FormatterConfig = rumUtil:get_opt(formatter_config, Opts, ?RumDefFormatterCfg),
HWM = rumUtil:get_opt(hWM, Opts, ?RumDefCheckHWM),
Flush = rumUtil:get_opt(flushQueue, Opts, ?RumDefFlushQueue),
FlushThr = rumUtil:get_opt(flushThr, Opts, ?RumDefFlushThr),
SyncInt = rumUtil:get_opt(syncInt, Opts, ?RumDefSyncInt),
CfgCheckInterval = rumUtil:get_opt(checkInt, Opts, ?RumDefCheckInt),
SyncSize = rumUtil:get_opt(syncSize, Opts, ?RumDefSyncSize),
CfgSyncOn = rumUtil:get_opt(syncOn, Opts, ?RumDefSyncLevel),
FormatTer = rumUtil:get_opt(formatTer, Opts, ?RumDefFormatTer),
FormatCfg = rumUtil:get_opt(formatCfg, Opts, ?RumDefFormatterCfg),
%%
Level = rumUtil:configToMask(CfgLevel),
SyncOn = rumUtil:configToMask(CfgSyncOn),
CheckInterval = ?IIF(CfgCheckInterval == always, 0, CfgCheckInterval),
CheckInt = ?IIF(CfgCheckInterval == always, 0, CfgCheckInterval),
{ok, Date} = rumUtil:parseRotateSpec(CfgDate),
FileName = rumUtil:parsePath(RelName),
scheduleRotation(Date, FileName),
Shaper = rumUtil:maybeFlush(Flush, #rumShaper{hwm = HighWaterMark, flushThreshold = FlushThr, id = FileName}),
Shaper = rumUtil:maybeFlush(Flush, #rumShaper{hwm = HWM, flushThr = FlushThr, id = FileName}),
TemState = #state{
fileName = FileName, level = Level, size = Size, date = Date
, count = Count, rotator = Rotator, shaper = Shaper
, formatter = Formatter, formatterConfig = FormatterConfig
, syncOn = SyncOn, syncInterval = SyncInterval
, syncSize = SyncSize, checkInterval = CheckInterval
, formatTer = FormatTer, formatCfg = FormatCfg
, syncOn = SyncOn, syncInt = SyncInt
, syncSize = SyncSize, checkInt = CheckInt
},
case Rotator:createLogFile(FileName, {SyncSize, SyncInterval}) of
case Rotator:createLogFile(FileName, {SyncSize, SyncInt}) of
{ok, Fd, Inode, CTime, _Size} ->
{ok, TemState#state{fd = Fd, inode = Inode, ctime = CTime}};
{ok, TemState#state{fd = Fd, inode = Inode, cTime = CTime}};
{error, Reason} ->
?INT_LOG(error, "Failed to open log file ~ts with error ~s", [FileName, file:format_error(Reason)]),
{ok, TemState#state{flap = true}}
@ -95,17 +95,17 @@ init(Opts) ->
handleCall(mGetLogLevel, #state{level = Level} = State) ->
{reply, Level, State};
handleCall({mSetLogLevel, Level}, #state{fileName = Ident} = State) ->
handleCall({mSetLogLevel, Level}, #state{fileName = FileName} = State) ->
case rumUtil:validateLogLevel(Level) of
false ->
{reply, {error, bad_loglevel}, State};
LevelMask ->
?INT_LOG(notice, "Changed loglevel of ~s to ~p", [Ident, Level]),
?INT_LOG(notice, "Changed loglevel of ~s to ~p", [FileName, Level]),
{reply, ok, State#state{level = LevelMask}}
end;
handleCall({mSetLogHwm, Hwm}, #state{shaper = Shaper, fileName = FileName} = State) ->
case checkOpts([{high_water_mark, Hwm}], true) of
case checkOpts([{hWM, Hwm}], true) of
false ->
{reply, {error, bad_log_hwm}, State};
_ ->
@ -120,12 +120,12 @@ handleCall(_Msg, State) ->
?ERR("~p call receive unexpect msg ~p ~n ", [?MODULE, _Msg]),
{reply, ok, State}.
handleEvent({mWriteLog, Message}, #state{fileName = FileName, level = Level, shaper = Shaper, formatter = Formatter, formatterConfig = FormatConfig} = State) ->
handleEvent({mWriteLog, Message}, #state{fileName = FileName, level = Level, shaper = Shaper, formatTer = FormatTer, formatCfg = FormatCfg} = State) ->
case rumUtil:isLoggAble(Message, Level, {rumBkdFile, FileName}) of
true ->
case rumUtil:checkHwm(Shaper) of
{true, _Drop, NewShaper} ->
{ok, writeLog(State#state{shaper = NewShaper}, rumMsg:timestamp(Message), rumMsg:severity_as_int(Message), Formatter:format(Message, FormatConfig))};
{ok, writeLog(State#state{shaper = NewShaper}, rumMsg:timestamp(Message), rumMsg:severity_as_int(Message), FormatTer:format(Message, FormatCfg))};
{drop, Drop, NewShaper} ->
TemState =
case Drop =< 0 of
@ -134,9 +134,9 @@ handleEvent({mWriteLog, Message}, #state{fileName = FileName, level = Level, sha
_ ->
Report = eFmt:format(<<"rumBkdFile dropped ~p messages in the last second that exceeded the limit of ~p messages/sec">>, [Drop, NewShaper#rumShaper.hwm]),
ReportMsg = rumMsg:new(Report, warning, [], []),
writeLog(State, rumMsg:timestamp(ReportMsg), rumMsg:severity_as_int(ReportMsg), Formatter:format(ReportMsg, FormatConfig))
writeLog(State, rumMsg:timestamp(ReportMsg), rumMsg:severity_as_int(ReportMsg), FormatTer:format(ReportMsg, FormatCfg))
end,
{ok, writeLog(TemState#state{shaper = NewShaper}, rumMsg:timestamp(Message), rumMsg:severity_as_int(Message), Formatter:format(Message, FormatConfig))};
{ok, writeLog(TemState#state{shaper = NewShaper}, rumMsg:timestamp(Message), rumMsg:severity_as_int(Message), FormatTer:format(Message, FormatCfg))};
{false, _, NewShaper} ->
{ok, State#state{shaper = NewShaper}}
end;
@ -152,14 +152,14 @@ handleInfo({mRotate, File}, #state{fileName = File, count = Count, date = Date,
_ = Rotator:rotateLogFile(File, Count),
scheduleRotation(File, Date),
{ok, NewState};
handleInfo({mShaperExpired, Name}, #state{shaper = Shaper, fileName = Name, formatter = Formatter, formatterConfig = FormatConfig} = State) ->
handleInfo({mShaperExpired, Name}, #state{shaper = Shaper, fileName = Name, formatTer = FormatTer, formatCfg = FormatCfg} = State) ->
case Shaper#rumShaper.dropped of
0 ->
ignore;
Dropped ->
Report = eFmt:format(<<"rumBkdFile dropped ~p messages in the last second that exceeded the limit of ~p messages/sec">>, [Dropped, Shaper#rumShaper.hwm]),
ReportMsg = rumMsg:new(Report, warning, [], []),
writeLog(State, rumMsg:timestamp(ReportMsg), rumMsg:severity_as_int(ReportMsg), Formatter:format(ReportMsg, FormatConfig))
writeLog(State, rumMsg:timestamp(ReportMsg), rumMsg:severity_as_int(ReportMsg), FormatTer:format(ReportMsg, FormatCfg))
end,
{ok, State#state{shaper = Shaper#rumShaper{dropped = 0, mps = 0, lastTime = rumTime:now()}}};
handleInfo(_Msg, _State) ->
@ -174,11 +174,11 @@ terminate(_Reason, State) ->
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
writeLog(#state{fileName = FileName, fd = Fd, inode = Inode, ctime = CTime, flap = Flap, size = RotSize, count = Count, rotator = Rotator, lastCheck = LastCheck, checkInterval = CheckInterval, syncSize = SyncSize, syncInterval = SyncInterval} = State, Timestamp, Level, Msg) ->
case isWriteCheck(Fd, LastCheck, CheckInterval, FileName, Inode, CTime, Timestamp) of
writeLog(#state{fileName = FileName, fd = Fd, inode = Inode, cTime = CTime, flap = Flap, size = RotSize, count = Count, rotator = Rotator, lastCheck = LastCheck, checkInt = CheckInt, syncSize = SyncSize, syncInt = SyncInt} = State, Timestamp, Level, Msg) ->
case isWriteCheck(Fd, LastCheck, CheckInt, FileName, Inode, CTime, Timestamp) of
true ->
%% need to check for rotation
case Rotator:ensureLogFile(FileName, Fd, Inode, CTime, {SyncSize, SyncInterval}) of
case Rotator:ensureLogFile(FileName, Fd, Inode, CTime, {SyncSize, SyncInt}) of
{ok, NewFD, NewInode, NewCTime, FileSize} ->
case RotSize > 0 andalso FileSize > RotSize of
true ->
@ -192,7 +192,7 @@ writeLog(#state{fileName = FileName, fd = Fd, inode = Inode, ctime = CTime, flap
end;
_ ->
%% update our last check and try again
TemState = State#state{lastCheck = Timestamp, fd = NewFD, inode = NewInode, ctime = NewCTime},
TemState = State#state{lastCheck = Timestamp, fd = NewFD, inode = NewInode, cTime = NewCTime},
writeFile(TemState, Level, Msg)
end;
{error, Reason} ->
@ -255,25 +255,25 @@ checkOpts([{count, Count} | Tail], IsFile) when is_integer(Count), Count >= 0 ->
checkOpts(Tail, IsFile);
checkOpts([{rotator, Rotator} | Tail], IsFile) when is_atom(Rotator) ->
checkOpts(Tail, IsFile);
checkOpts([{high_water_mark, HighWaterMark} | Tail], IsFile) when is_integer(HighWaterMark), HighWaterMark >= 0 ->
checkOpts([{hWM, HighWaterMark} | Tail], IsFile) when is_integer(HighWaterMark), HighWaterMark >= 0 ->
checkOpts(Tail, IsFile);
checkOpts([{date, _Date} | Tail], IsFile) ->
checkOpts(Tail, IsFile);
checkOpts([{sync_interval, SyncInt} | Tail], IsFile) when is_integer(SyncInt), SyncInt >= 0 ->
checkOpts([{syncInt, SyncInt} | Tail], IsFile) when is_integer(SyncInt), SyncInt >= 0 ->
checkOpts(Tail, IsFile);
checkOpts([{sync_size, SyncSize} | Tail], IsFile) when is_integer(SyncSize), SyncSize >= 0 ->
checkOpts([{syncSize, SyncSize} | Tail], IsFile) when is_integer(SyncSize), SyncSize >= 0 ->
checkOpts(Tail, IsFile);
checkOpts([{check_interval, CheckInt} | Tail], IsFile) when is_integer(CheckInt), CheckInt >= 0; CheckInt == always ->
checkOpts([{checkInt, CheckInt} | Tail], IsFile) when is_integer(CheckInt), CheckInt >= 0; CheckInt == always ->
checkOpts(Tail, IsFile);
checkOpts([{sync_on, Level} | Tail], IsFile) ->
checkOpts([{syncOn, Level} | Tail], IsFile) ->
?IIF(rumUtil:validateLogLevel(Level) /= false, checkOpts(Tail, IsFile), {error, {invalid_sync_on, Level}});
checkOpts([{formatter, Fmt} | Tail], IsFile) when is_atom(Fmt) ->
checkOpts([{formatTer, Fmt} | Tail], IsFile) when is_atom(Fmt) ->
checkOpts(Tail, IsFile);
checkOpts([{formatter_config, FmtCfg} | Tail], IsFile) when is_list(FmtCfg) ->
checkOpts([{formatCfg, FmtCfg} | Tail], IsFile) when is_list(FmtCfg) ->
checkOpts(Tail, IsFile);
checkOpts([{flush_queue, FlushCfg} | Tail], IsFile) when is_boolean(FlushCfg) ->
checkOpts([{flushQueue, FlushCfg} | Tail], IsFile) when is_boolean(FlushCfg) ->
checkOpts(Tail, IsFile);
checkOpts([{flush_threshold, Thr} | Tail], IsFile) when is_integer(Thr), Thr >= 0 ->
checkOpts([{flushThr, Thr} | Tail], IsFile) when is_integer(Thr), Thr >= 0 ->
checkOpts(Tail, IsFile);
checkOpts([Other | _Tail], _IsFile) ->
{error, {invalid_opt, Other}}.

+ 8
- 8
src/crashLog/rumCrashLog.erl Vedi File

@ -34,7 +34,7 @@
fileName :: string() %%
, fd :: pid() | undefined %%
, inode :: integer() | undefined %% inode信息
, ctime :: file:date_time() | undefined %%
, cTime :: file:date_time() | undefined %%
, maxFmtSize :: integer() %%
, maxFileSize :: integer() %%
, date :: undefined | string() %%
@ -55,7 +55,7 @@ init({RelFilename, MaxFmtSize, MaxFileSize, CfgDate, Count, Rotator}) ->
case Rotator:openLogFile(Filename, false) of
{ok, Fd, Inode, CTime, _Size} ->
scheduleRotation(Date),
{ok, #state{fileName = Filename, fd = Fd, inode = Inode, ctime = CTime, maxFmtSize = MaxFmtSize, maxFileSize = MaxFileSize, date = Date, count = Count, rotator = Rotator}};
{ok, #state{fileName = Filename, fd = Fd, inode = Inode, cTime = CTime, maxFmtSize = MaxFmtSize, maxFileSize = MaxFileSize, date = Date, count = Count, rotator = Rotator}};
{error, Reason} ->
?INT_LOG(error, "Failed to open crash log file ~ts with error: ~s", [Filename, file:format_error(Reason)]),
{ok, #state{fileName = Filename, maxFmtSize = MaxFmtSize, maxFileSize = MaxFileSize, date = Date, count = Count, flap = true, rotator = Rotator}}
@ -146,7 +146,7 @@ saslLimitedStr(progress, Report, FmtMaxBytes) ->
saslLimitedStr(crash_report, Report, FmtMaxBytes) ->
rumStdlib:proc_lib_format(Report, FmtMaxBytes).
writeLog(Event, #state{fileName = FileName, fd = FD, inode = Inode, ctime = Ctime, flap = Flap, maxFmtSize = FmtMaxBytes, maxFileSize = RotSize, count = Count, rotator = Rotator} = State) ->
writeLog(Event, #state{fileName = FileName, fd = FD, inode = Inode, cTime = CTime, flap = Flap, maxFmtSize = FmtMaxBytes, maxFileSize = RotSize, count = Count, rotator = Rotator} = State) ->
%% borrowed from riak_err
{ReportStr, Pid, MsgStr, _ErrorP} =
case Event of
@ -163,8 +163,8 @@ writeLog(Event, #state{fileName = FileName, fd = FD, inode = Inode, ctime = Ctim
ignore ->
{ok, State};
_ ->
case Rotator:ensureLogFile(FileName, FD, Inode, Ctime, false) of
{ok, NewFD, NewInode, NewCtime, FileSize} ->
case Rotator:ensureLogFile(FileName, FD, Inode, CTime, false) of
{ok, NewFD, NewInode, NewCTime, FileSize} ->
case RotSize > 0 andalso FileSize > RotSize of
true ->
_ = Rotator:rotateLogFile(FileName, Count),
@ -177,11 +177,11 @@ writeLog(Event, #state{fileName = FileName, fd = FD, inode = Inode, ctime = Ctim
case file:write(NewFD, unicode:characters_to_binary(Msg)) of
{error, Reason} when Flap == false ->
?INT_LOG(error, "Failed to write log message to file ~ts: ~s", [FileName, file:format_error(Reason)]),
{ok, State#state{fd = NewFD, inode = NewInode, ctime = NewCtime, flap = true}};
{ok, State#state{fd = NewFD, inode = NewInode, cTime = NewCTime, flap = true}};
ok ->
{ok, State#state{fd = NewFD, inode = NewInode, ctime = NewCtime, flap = false}};
{ok, State#state{fd = NewFD, inode = NewInode, cTime = NewCTime, flap = false}};
_ ->
{ok, State#state{fd = NewFD, inode = NewInode, ctime = NewCtime}}
{ok, State#state{fd = NewFD, inode = NewInode, cTime = NewCTime}}
end
end;
{error, Reason} ->

+ 5
- 5
src/eRum.erl Vedi File

@ -532,17 +532,17 @@ minLogLevel(Levels) ->
%% arguments. The caller is NOT crashed.
safe_format(Fmt, Args, Limit) ->
safe_format(Fmt, Args, Limit, []).
safe_format_2(Fmt, Args, Limit).
safe_format(Fmt, Args, Limit, Options) ->
try rumTruncIo:format(Fmt, Args, Limit, Options)
safe_format_2(Fmt, Args, Limit) ->
try eFmt:formatBin(Fmt, Args, [{charsLimit, Limit}])
catch
_:_ -> rumTruncIo:format("FORMAT ERROR: ~p ~p", [Fmt, Args], Limit)
_:_ -> eFmt:formatBin(<<"FORMAT ERROR: ~p ~p">>, [Fmt, Args], [{charsLimit, Limit}])
end.
%% @private
safe_format_chop(Fmt, Args, Limit) ->
safe_format(Fmt, Args, Limit, [{chomp, true}]).
safe_format_2(Fmt, Args, Limit).
%% @private Print the format string `Fmt' with `Args' without a size limit.
%% This is unsafe because the output of this function is unbounded.

+ 2
- 4
src/eRum1.erl Vedi File

@ -4,9 +4,6 @@
-include("rumMsg.hrl").
-include("rumCom.hrl").
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.
%% API
-export([
@ -18,6 +15,7 @@
, dispatch_log/13
, do_log/16
, do_log_unsafe/16
, do_log_impl/16
, safe_format/3
, safe_format_chop/3
, unsafe_format/2
@ -125,7 +123,7 @@ do_log_impl(Severity, Pid, Node, Module, Function, _File, Line, Metadata, Format
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

+ 1
- 1
src/errLogger/rumErrLoggerH.erl Vedi File

@ -67,7 +67,7 @@ setHighWater(N) ->
init([HighWaterMark, GlStrategy]) ->
Flush = rumUtil:get_env(errLoggerFlushQueue, true),
FlushThr = rumUtil:get_env(errLoggerFlushThreshold, 0),
Shaper = #rumShaper{hwm = HighWaterMark, flushQueue = Flush, flushThreshold = FlushThr, filter = shaperFun(), id = ?MODULE},
Shaper = #rumShaper{hwm = HighWaterMark, flushQueue = Flush, flushThr = FlushThr, filter = shaperFun(), id = ?MODULE},
Raw = rumUtil:get_env(errLoggerFormatRaw, false),
Sink = configSink(),
{ok, #state{sink = Sink, shaper = Shaper, groupleader_strategy = GlStrategy, raw = Raw}}.

src/formatter/rumFormatter.erl → src/formatter/rumFormatTer.erl Vedi File

@ -1,4 +1,4 @@
-module(rumFormatter).
-module(rumFormatTer).
-include("rumDef.hrl").
@ -18,7 +18,7 @@
%% elements in the configuration are printed verbatim. Atoms in the configuration are treated as metadata properties
%% and extracted from the log message. Optionally, a tuple of {atom(),semi-iolist()} can be used. The atom will look
%% up the property, but if not found it will use the semi-iolist() instead. These fallbacks can be similarly nested
%% or refer to other properties, if desired. You can also use a {atom, semi-iolist(), semi-iolist()} formatter, which
%% or refer to other properties, if desired. You can also use a {atom, semi-iolist(), semi-iolist()} formatTer, which
%% acts like a ternary operator's true/false branches.
%%
%% The metadata properties date,time, message, severity, and sev will always exist.

+ 3
- 6
src/utils/rumStdlib.erl Vedi File

@ -42,8 +42,7 @@ proc_lib_format([OwnReport, LinkReport], FmtMaxBytes) ->
format_report(Rep, FmtMaxBytes) when is_list(Rep) ->
format_rep(Rep, FmtMaxBytes);
format_report(Rep, FmtMaxBytes) ->
{Str, _} = rumTruncIo:print(Rep, FmtMaxBytes),
io_lib:format("~p~n", [Str]).
eFmt:formatBin(<<"~p~n">>, [Rep], [{charsLimit, FmtMaxBytes}]).
format_rep([{initial_call, InitialCall} | Rep], FmtMaxBytes) ->
[format_mfa(InitialCall, FmtMaxBytes) | format_rep(Rep, FmtMaxBytes)];
@ -74,13 +73,11 @@ format_mfa({M, F, Args} = StartF, FmtMaxBytes) ->
pp_fun(FmtMaxBytes) ->
fun(Term, _I) ->
{Str, _} = rumTruncIo:print(Term, FmtMaxBytes),
io_lib:format("~s", [Str])
eFmt:formatBin(<<"~p">>, [Term], [{charsLimit ,FmtMaxBytes}])
end.
format_tag(Tag, Data, FmtMaxBytes) ->
{Str, _} = rumTruncIo:print(Data, FmtMaxBytes),
io_lib:format(" ~p: ~s~n", [Tag, Str]).
eFmt:formatBin(<<"~p: ~p~n">>, [Tag, Data], [{charsLimit ,FmtMaxBytes}]).
%% From OTP stdlib's lib.erl ... These functions aren't exported.

+ 0
- 869
src/utils/rumTruncIo.erl Vedi File

@ -1,869 +0,0 @@
%%
%% @doc Module to print out terms for logging. Limits by length rather than depth.
%%
%% The resulting string may be slightly larger than the limit; the intention
%% is to provide predictable CPU and memory consumption for formatting
%% terms, not produce precise string lengths.
%%
%% Typical use:
%%
%% trunc_io:print(Term, 500).
%%
%% Source license: Erlang Public License.
%% Original author: Matthias Lang, <tt>matthias@corelatus.se</tt>
%%
%% Various changes to this module, most notably the format/3 implementation
%% were added by Andrew Thompson `<andrew@basho.com>'. The module has been renamed
%% to avoid conflicts with the vanilla module.
-module(rumTruncIo).
-author('matthias@corelatus.se').
%% And thanks to Chris Newcombe for a bug fix
-export([format/3, format/4, print/2, print/3, fprint/2, fprint/3, safe/2]). % interface functions
-version("$Id: trunc_io.erl,v 1.11 2009-02-23 12:01:06 matthias Exp $").
-ifdef(TEST).
-export([perf/0, perf/3, perf1/0, test/0, test/2]). % testing functions
-include_lib("eunit/include/eunit.hrl").
-endif.
-type option() :: {'depth', integer()}
| {'lists_as_strings', boolean()}
| {'force_strings', boolean()}.
-type options() :: [option()].
-record(print_options, {
%% negative depth means no depth limiting
depth = -1 :: integer(),
%% whether to print lists as strings, if possible
lists_as_strings = true :: boolean(),
%% force strings, or binaries to be printed as a string,
%% even if they're not printable
force_strings = false :: boolean()
}).
format(Fmt, Args, Max) ->
format(Fmt, Args, Max, []).
format(Fmt, Args, Max, Options) ->
try rumFormat:format(Fmt, Args, Max, Options)
catch
_What:_Why ->
erlang:error(badarg, [Fmt, Args])
end.
%% @doc Returns an flattened list containing the ASCII representation of the given
%% term.
-spec fprint(term(), pos_integer()) -> string().
fprint(Term, Max) ->
fprint(Term, Max, []).
%% @doc Returns an flattened list containing the ASCII representation of the given
%% term.
-spec fprint(term(), pos_integer(), options()) -> string().
fprint(T, Max, Options) ->
{L, _} = print(T, Max, prepare_options(Options, #print_options{})),
lists:flatten(L).
%% @doc Same as print, but never crashes.
%%
%% This is a tradeoff. Print might conceivably crash if it's asked to
%% print something it doesn't understand, for example some new data
%% type in a future version of Erlang. If print crashes, we fall back
%% to io_lib to format the term, but then the formatting is
%% depth-limited instead of length limited, so you might run out
%% memory printing it. Out of the frying pan and into the fire.
%%
-spec safe(term(), pos_integer()) -> {string(), pos_integer()} | {string()}.
safe(What, Len) ->
case catch print(What, Len) of
{L, Used} when is_list(L) -> {L, Used};
_ -> {"unable to print" ++ io_lib:write(What, 99)}
end.
%% @doc Returns {List, Length}
-spec print(term(), pos_integer()) -> {iolist(), pos_integer()}.
print(Term, Max) ->
print(Term, Max, []).
%% @doc Returns {List, Length}
-spec print(term(), pos_integer(), options() | #print_options{}) -> {iolist(), pos_integer()}.
print(Term, Max, Options) when is_list(Options) ->
%% need to convert the proplist to a record
print(Term, Max, prepare_options(Options, #print_options{}));
print(Term, _Max, #print_options{force_strings = true}) when not is_list(Term), not is_binary(Term), not is_atom(Term) ->
erlang:error(badarg);
print(_, Max, _Options) when Max < 0 -> {"...", 3};
print(_, _, #print_options{depth = 0}) -> {"...", 3};
%% @doc We assume atoms, floats, funs, integers, PIDs, ports and refs never need
%% to be truncated. This isn't strictly true, someone could make an
%% arbitrarily long bignum. Let's assume that won't happen unless someone
%% is being malicious.
%%
print(Atom, _Max, #print_options{force_strings = NoQuote}) when is_atom(Atom) ->
L = atom_to_list(Atom),
R = case atom_needs_quoting_start(L) andalso not NoQuote of
true -> lists:flatten([$', L, $']);
false -> L
end,
{R, length(R)};
print(<<>>, _Max, #print_options{depth = 1}) ->
{"<<>>", 4};
print(Bin, _Max, #print_options{depth = 1}) when is_binary(Bin) ->
{"<<...>>", 7};
print(<<>>, _Max, Options) ->
case Options#print_options.force_strings of
true ->
{"", 0};
false ->
{"<<>>", 4}
end;
print(Binary, 0, _Options) when is_bitstring(Binary) ->
{"<<..>>", 6};
print(Bin, Max, _Options) when is_binary(Bin), Max < 2 ->
{"<<...>>", 7};
print(Binary, Max, Options) when is_binary(Binary) ->
B = binary_to_list(Binary, 1, lists:min([Max, byte_size(Binary)])),
{Res, Length} =
case Options#print_options.lists_as_strings orelse Options#print_options.force_strings of
true ->
Depth = Options#print_options.depth,
MaxSize = (Depth - 1) * 4,
%% check if we need to truncate based on depth
In =
case Depth > -1 andalso MaxSize < length(B) andalso
not Options#print_options.force_strings of
true ->
string:substr(B, 1, MaxSize);
false -> B
end,
MaxLen =
case Options#print_options.force_strings of
true ->
Max;
false ->
%% make room for the leading doublequote
Max - 1
end,
try alist(In, MaxLen, Options) of
{L0, Len0} ->
case Options#print_options.force_strings of
false ->
case B /= In of
true ->
{[$", L0, "..."], Len0 + 4};
false ->
{[$" | L0], Len0 + 1}
end;
true ->
{L0, Len0}
end
catch
throw:{unprintable, C} ->
Index = string:chr(In, C),
case Index > 1 andalso Options#print_options.depth =< Index andalso
Options#print_options.depth > -1 andalso
not Options#print_options.force_strings of
true ->
%% print first Index-1 characters followed by ...
{L0, Len0} = alist_start(string:substr(In, 1, Index - 1), Max - 1, Options),
{L0 ++ "...", Len0 + 3};
false ->
list_body(In, Max - 4, dec_depth(Options), true)
end
end;
_ ->
list_body(B, Max - 4, dec_depth(Options), true)
end,
case Options#print_options.force_strings of
true ->
{Res, Length};
_ ->
{["<<", Res, ">>"], Length + 4}
end;
%% bitstrings are binary's evil brother who doesn't end on an 8 bit boundary.
%% This makes printing them extremely annoying, so list_body/list_bodyc has
%% some magic for dealing with the output of bitstring_to_list, which returns
%% a list of integers (as expected) but with a trailing binary that represents
%% the remaining bits.
print({inline_bitstring, B}, _Max, _Options) when is_bitstring(B) ->
Size = bit_size(B),
<<Value:Size>> = B,
ValueStr = integer_to_list(Value),
SizeStr = integer_to_list(Size),
{[ValueStr, $:, SizeStr], length(ValueStr) + length(SizeStr) + 1};
print(BitString, Max, Options) when is_bitstring(BitString) ->
BL =
case byte_size(BitString) > Max of
true ->
binary_to_list(BitString, 1, Max);
_ ->
R = erlang:bitstring_to_list(BitString),
{Bytes, [Bits]} = lists:splitwith(fun erlang:is_integer/1, R),
%% tag the trailing bits with a special tuple we catch when
%% list_body calls print again
Bytes ++ [{inline_bitstring, Bits}]
end,
{X, Len0} = list_body(BL, Max - 4, dec_depth(Options), true),
{["<<", X, ">>"], Len0 + 4};
print(Float, _Max, _Options) when is_float(Float) ->
%% use the same function io_lib:format uses to print floats
%% float_to_list is way too verbose.
L = io_lib_format:fwrite_g(Float),
{L, length(L)};
print(Fun, Max, _Options) when is_function(Fun) ->
L = erlang:fun_to_list(Fun),
case length(L) > Max of
true ->
S = erlang:max(5, Max),
Res = string:substr(L, 1, S) ++ "..>",
{Res, length(Res)};
_ ->
{L, length(L)}
end;
print(Integer, _Max, _Options) when is_integer(Integer) ->
L = integer_to_list(Integer),
{L, length(L)};
print(Pid, _Max, _Options) when is_pid(Pid) ->
L = pid_to_list(Pid),
{L, length(L)};
print(Ref, _Max, _Options) when is_reference(Ref) ->
L = erlang:ref_to_list(Ref),
{L, length(L)};
print(Port, _Max, _Options) when is_port(Port) ->
L = erlang:port_to_list(Port),
{L, length(L)};
print({'$lager_record', Name, Fields}, Max, Options) ->
Leader = "#" ++ atom_to_list(Name) ++ "{",
{RC, Len} = record_fields(Fields, Max - length(Leader) + 1, dec_depth(Options)),
{[Leader, RC, "}"], Len + length(Leader) + 1};
print(Tuple, Max, Options) when is_tuple(Tuple) ->
{TC, Len} = tuple_contents(Tuple, Max - 2, Options),
{[${, TC, $}], Len + 2};
print(List, Max, Options) when is_list(List) ->
case Options#print_options.lists_as_strings orelse
Options#print_options.force_strings of
true ->
alist_start(List, Max, dec_depth(Options));
_ ->
{R, Len} = list_body(List, Max - 2, dec_depth(Options), false),
{[$[, R, $]], Len + 2}
end;
print(Map, Max, Options) when is_map(Map) ->
{MapBody, Len} = map_body(Map, Max - 3, dec_depth(Options)),
{[$#, ${, MapBody, $}], Len + 3};
print(Term, Max, Options) ->
error(badarg, [Term, Max, Options]).
%% Returns {List, Length}
tuple_contents(Tuple, Max, Options) ->
L = tuple_to_list(Tuple),
list_body(L, Max, dec_depth(Options), true).
%% Format the inside of a list, i.e. do not add a leading [ or trailing ].
%% Returns {List, Length}
list_body([], _Max, _Options, _Tuple) -> {[], 0};
list_body(_, Max, _Options, _Tuple) when Max < 4 -> {"...", 3};
list_body(_, _Max, #print_options{depth = 0}, _Tuple) -> {"...", 3};
list_body([H], Max, Options = #print_options{depth = 1}, _Tuple) ->
print(H, Max, Options);
list_body([H | _], Max, Options = #print_options{depth = 1}, Tuple) ->
{List, Len} = print(H, Max - 4, Options),
Sep =
case Tuple of
true -> $,;
false -> $|
end,
{[List ++ [Sep | "..."]], Len + 4};
list_body([H | T], Max, Options, Tuple) ->
{List, Len} = print(H, Max, Options),
{Final, FLen} = list_bodyc(T, Max - Len, Options, Tuple),
{[List | Final], FLen + Len};
list_body(X, Max, Options, _Tuple) -> %% improper list
{List, Len} = print(X, Max - 1, Options),
{[$|, List], Len + 1}.
list_bodyc([], _Max, _Options, _Tuple) -> {[], 0};
list_bodyc(_, Max, _Options, _Tuple) when Max < 5 -> {",...", 4};
list_bodyc(_, _Max, #print_options{depth = 1}, true) -> {",...", 4};
list_bodyc(_, _Max, #print_options{depth = 1}, false) -> {"|...", 4};
list_bodyc([H | T], Max, #print_options{depth = Depth} = Options, Tuple) ->
{List, Len} = print(H, Max, dec_depth(Options)),
{Final, FLen} = list_bodyc(T, Max - Len - 1, dec_depth(Options), Tuple),
Sep =
case Depth == 1 andalso not Tuple of
true -> $|;
_ -> $,
end,
{[Sep, List | Final], FLen + Len + 1};
list_bodyc(X, Max, Options, _Tuple) -> %% improper list
{List, Len} = print(X, Max - 1, Options),
{[$|, List], Len + 1}.
map_body(Map, Max, #print_options{depth = Depth}) when Max < 4; Depth =:= 0 ->
case erlang:map_size(Map) of
0 -> {[], 0};
_ -> {"...", 3}
end;
map_body(Map, Max, Options) ->
case maps:to_list(Map) of
[] ->
{[], 0};
[{Key, Value} | Rest] ->
{KeyStr, KeyLen} = print(Key, Max - 4, Options),
DiffLen = KeyLen + 4,
{ValueStr, ValueLen} = print(Value, Max - DiffLen, Options),
DiffLen2 = DiffLen + ValueLen,
{Final, FLen} = map_bodyc(Rest, Max - DiffLen2, dec_depth(Options)),
{[KeyStr, " => ", ValueStr | Final], DiffLen2 + FLen}
end.
map_bodyc([], _Max, _Options) ->
{[], 0};
map_bodyc(_Rest, Max, #print_options{depth = Depth}) when Max < 5; Depth =:= 0 ->
{",...", 4};
map_bodyc([{Key, Value} | Rest], Max, Options) ->
{KeyStr, KeyLen} = print(Key, Max - 5, Options),
DiffLen = KeyLen + 5,
{ValueStr, ValueLen} = print(Value, Max - DiffLen, Options),
DiffLen2 = DiffLen + ValueLen,
{Final, FLen} = map_bodyc(Rest, Max - DiffLen2, dec_depth(Options)),
{[$,, KeyStr, " => ", ValueStr | Final], DiffLen2 + FLen}.
%% The head of a list we hope is ascii. Examples:
%%
%% [65,66,67] -> "ABC"
%% [65,0,67] -> "A"[0,67]
%% [0,65,66] -> [0,65,66]
%% [65,b,66] -> "A"[b,66]
%%
alist_start([], _Max, #print_options{force_strings = true}) -> {"", 0};
alist_start([], _Max, _Options) -> {"[]", 2};
alist_start(_, Max, _Options) when Max < 4 -> {"...", 3};
alist_start(_, _Max, #print_options{depth = 0}) -> {"[...]", 5};
alist_start(L, Max, #print_options{force_strings = true} = Options) ->
alist(L, Max, Options);
%alist_start([H|_T], _Max, #print_options{depth=1}) when is_integer(H) -> {[$[, H, $|, $., $., $., $]], 7};
alist_start([H | T], Max, Options) when is_integer(H), H >= 16#20, H =< 16#7e -> % definitely printable
try alist([H | T], Max - 1, Options) of
{L, Len} ->
{[$" | L], Len + 1}
catch
throw:{unprintable, _} ->
{R, Len} = list_body([H | T], Max - 2, Options, false),
{[$[, R, $]], Len + 2}
end;
alist_start([H | T], Max, Options) when is_integer(H), H >= 16#a0, H =< 16#ff -> % definitely printable
try alist([H | T], Max - 1, Options) of
{L, Len} ->
{[$" | L], Len + 1}
catch
throw:{unprintable, _} ->
{R, Len} = list_body([H | T], Max - 2, Options, false),
{[$[, R, $]], Len + 2}
end;
alist_start([H | T], Max, Options) when H =:= $\t; H =:= $\n; H =:= $\r; H =:= $\v; H =:= $\e; H =:= $\f; H =:= $\b ->
try alist([H | T], Max - 1, Options) of
{L, Len} ->
{[$" | L], Len + 1}
catch
throw:{unprintable, _} ->
{R, Len} = list_body([H | T], Max - 2, Options, false),
{[$[, R, $]], Len + 2}
end;
alist_start(L, Max, Options) ->
{R, Len} = list_body(L, Max - 2, Options, false),
{[$[, R, $]], Len + 2}.
alist([], _Max, #print_options{force_strings = true}) -> {"", 0};
alist([], _Max, _Options) -> {"\"", 1};
alist(_, Max, #print_options{force_strings = true}) when Max < 4 -> {"...", 3};
alist(_, Max, #print_options{force_strings = false}) when Max < 5 -> {"...\"", 4};
alist([H | T], Max, Options = #print_options{force_strings = false, lists_as_strings = true}) when H =:= $"; H =:= $\\ ->
%% preserve escaping around quotes
{L, Len} = alist(T, Max - 1, Options),
{[$\\, H | L], Len + 2};
alist([H | T], Max, Options) when is_integer(H), H >= 16#20, H =< 16#7e -> % definitely printable
{L, Len} = alist(T, Max - 1, Options),
{[H | L], Len + 1};
alist([H | T], Max, Options) when is_integer(H), H >= 16#a0, H =< 16#ff -> % definitely printable
{L, Len} = alist(T, Max - 1, Options),
{[H | L], Len + 1};
alist([H | T], Max, Options) when H =:= $\t; H =:= $\n; H =:= $\r; H =:= $\v; H =:= $\e; H =:= $\f; H =:= $\b ->
{L, Len} = alist(T, Max - 1, Options),
case Options#print_options.force_strings of
true ->
{[H | L], Len + 1};
_ ->
{[escape(H) | L], Len + 1}
end;
alist([H | T], Max, #print_options{force_strings = true} = Options) when is_integer(H) ->
{L, Len} = alist(T, Max - 1, Options),
{[H | L], Len + 1};
alist([H | T], Max, Options = #print_options{force_strings = true}) when is_binary(H); is_list(H) ->
{List, Len} = print(H, Max, Options),
case (Max - Len) =< 0 of
true ->
%% no more room to print anything
{List, Len};
false ->
%% no need to decrement depth, as we're in printable string mode
{Final, FLen} = alist(T, Max - Len, Options),
{[List | Final], FLen + Len}
end;
alist(_, _, #print_options{force_strings = true}) ->
erlang:error(badarg);
alist([H | _L], _Max, _Options) ->
throw({unprintable, H});
alist(H, _Max, _Options) ->
%% improper list
throw({unprintable, H}).
%% is the first character in the atom alphabetic & lowercase?
atom_needs_quoting_start([H | T]) when H >= $a, H =< $z ->
atom_needs_quoting(T);
atom_needs_quoting_start(_) ->
true.
atom_needs_quoting([]) ->
false;
atom_needs_quoting([H | T]) when (H >= $a andalso H =< $z);
(H >= $A andalso H =< $Z);
(H >= $0 andalso H =< $9);
H == $@; H == $_ ->
atom_needs_quoting(T);
atom_needs_quoting(_) ->
true.
-spec prepare_options(options(), #print_options{}) -> #print_options{}.
prepare_options([], Options) ->
Options;
prepare_options([{depth, Depth} | T], Options) when is_integer(Depth) ->
prepare_options(T, Options#print_options{depth = Depth});
prepare_options([{lists_as_strings, Bool} | T], Options) when is_boolean(Bool) ->
prepare_options(T, Options#print_options{lists_as_strings = Bool});
prepare_options([{force_strings, Bool} | T], Options) when is_boolean(Bool) ->
prepare_options(T, Options#print_options{force_strings = Bool}).
dec_depth(#print_options{depth = Depth} = Options) when Depth > 0 ->
Options#print_options{depth = Depth - 1};
dec_depth(Options) ->
Options.
escape($\t) -> "\\t";
escape($\n) -> "\\n";
escape($\r) -> "\\r";
escape($\e) -> "\\e";
escape($\f) -> "\\f";
escape($\b) -> "\\b";
escape($\v) -> "\\v".
record_fields([], _, _) ->
{"", 0};
record_fields(_, Max, #print_options{depth = D}) when Max < 4; D == 0 ->
{"...", 3};
record_fields([{Field, Value} | T], Max, Options) ->
{ExtraChars, Terminator} = case T of
[] ->
{1, []};
_ ->
{2, ","}
end,
{FieldStr, FieldLen} = print(Field, Max - ExtraChars, Options),
{ValueStr, ValueLen} = print(Value, Max - (FieldLen + ExtraChars), Options),
{Final, FLen} = record_fields(T, Max - (FieldLen + ValueLen + ExtraChars), dec_depth(Options)),
{[FieldStr ++ "=" ++ ValueStr ++ Terminator | Final], FLen + FieldLen + ValueLen + ExtraChars}.
-ifdef(TEST).
%%--------------------
%% The start of a test suite. So far, it only checks for not crashing.
-spec test() -> ok.
test() ->
test(trunc_io, print).
-spec test(atom(), atom()) -> ok.
test(Mod, Func) ->
Simple_items = [atom, 1234, 1234.0, {tuple}, [], [list], "string", self(),
<<1, 2, 3>>, make_ref(), fun() -> ok end],
F = fun(A) ->
Mod:Func(A, 100),
Mod:Func(A, 2),
Mod:Func(A, 20)
end,
G = fun(A) ->
case catch F(A) of
{'EXIT', _} -> exit({failed, A});
_ -> ok
end
end,
lists:foreach(G, Simple_items),
Tuples = [{1, 2, 3, a, b, c}, {"abc", def, 1234},
{{{{a}, b, c, {d}, e}}, f}],
Lists = [[1, 2, 3, 4, 5, 6, 7], lists:seq(1, 1000),
[{a}, {a, b}, {a, [b, c]}, "def"], [a | b], [$a | $b]],
lists:foreach(G, Tuples),
lists:foreach(G, Lists).
-spec perf() -> ok.
perf() ->
{New, _} = timer:tc(trunc_io, perf, [trunc_io, print, 1000]),
{Old, _} = timer:tc(trunc_io, perf, [io_lib, write, 1000]),
io:fwrite("New code took ~p us, old code ~p\n", [New, Old]).
-spec perf(atom(), atom(), integer()) -> done.
perf(M, F, Reps) when Reps > 0 ->
test(M, F),
perf(M, F, Reps - 1);
perf(_, _, _) ->
done.
%% Performance test. Needs a particularly large term I saved as a binary...
-spec perf1() -> {non_neg_integer(), non_neg_integer()}.
perf1() ->
{ok, Bin} = file:read_file("bin"),
A = binary_to_term(Bin),
{N, _} = timer:tc(trunc_io, print, [A, 1500]),
{M, _} = timer:tc(io_lib, write, [A]),
{N, M}.
format_test() ->
%% simple format strings
?assertEqual("foobar", lists:flatten(format("~s", [["foo", $b, $a, $r]], 50))),
?assertEqual("[\"foo\",98,97,114]", lists:flatten(format("~p", [["foo", $b, $a, $r]], 50))),
?assertEqual("[\"foo\",98,97,114]", lists:flatten(format("~P", [["foo", $b, $a, $r], 10], 50))),
?assertEqual("[[102,111,111],98,97,114]", lists:flatten(format("~w", [["foo", $b, $a, $r]], 50))),
%% complex ones
?assertEqual(" foobar", lists:flatten(format("~10s", [["foo", $b, $a, $r]], 50))),
?assertEqual("f", lists:flatten(format("~1s", [["foo", $b, $a, $r]], 50))),
?assertEqual("[\"foo\",98,97,114]", lists:flatten(format("~22p", [["foo", $b, $a, $r]], 50))),
?assertEqual("[\"foo\",98,97,114]", lists:flatten(format("~22P", [["foo", $b, $a, $r], 10], 50))),
?assertEqual("**********", lists:flatten(format("~10W", [["foo", $b, $a, $r], 10], 50))),
?assertEqual("[[102,111,111],98,97,114]", lists:flatten(format("~25W", [["foo", $b, $a, $r], 10], 50))),
% Note these next two diverge from io_lib:format; the field width is
% ignored, when it should be used as max line length.
?assertEqual("[\"foo\",98,97,114]", lists:flatten(format("~10p", [["foo", $b, $a, $r]], 50))),
?assertEqual("[\"foo\",98,97,114]", lists:flatten(format("~10P", [["foo", $b, $a, $r], 10], 50))),
ok.
atom_quoting_test() ->
?assertEqual("hello", lists:flatten(format("~p", [hello], 50))),
?assertEqual("'hello world'", lists:flatten(format("~p", ['hello world'], 50))),
?assertEqual("'Hello world'", lists:flatten(format("~p", ['Hello world'], 50))),
?assertEqual("hello_world", lists:flatten(format("~p", ['hello_world'], 50))),
?assertEqual("'node@127.0.0.1'", lists:flatten(format("~p", ['node@127.0.0.1'], 50))),
?assertEqual("node@nohost", lists:flatten(format("~p", [node@nohost], 50))),
?assertEqual("abc123", lists:flatten(format("~p", [abc123], 50))),
ok.
sane_float_printing_test() ->
?assertEqual("1.0", lists:flatten(format("~p", [1.0], 50))),
?assertEqual("1.23456789", lists:flatten(format("~p", [1.23456789], 50))),
?assertEqual("1.23456789", lists:flatten(format("~p", [1.234567890], 50))),
?assertEqual("0.3333333333333333", lists:flatten(format("~p", [1 / 3], 50))),
?assertEqual("0.1234567", lists:flatten(format("~p", [0.1234567], 50))),
ok.
float_inside_list_test() ->
?assertEqual("[97,38.233913133184835,99]", lists:flatten(format("~p", [[$a, 38.233913133184835, $c]], 50))),
?assertError(badarg, lists:flatten(format("~s", [[$a, 38.233913133184835, $c]], 50))),
ok.
quote_strip_test() ->
?assertEqual("\"hello\"", lists:flatten(format("~p", ["hello"], 50))),
?assertEqual("hello", lists:flatten(format("~s", ["hello"], 50))),
?assertEqual("hello", lists:flatten(format("~s", [hello], 50))),
?assertEqual("hello", lists:flatten(format("~p", [hello], 50))),
?assertEqual("'hello world'", lists:flatten(format("~p", ['hello world'], 50))),
?assertEqual("hello world", lists:flatten(format("~s", ['hello world'], 50))),
ok.
binary_printing_test() ->
?assertEqual("<<>>", lists:flatten(format("~p", [<<>>], 50))),
?assertEqual("", lists:flatten(format("~s", [<<>>], 50))),
?assertEqual("<<..>>", lists:flatten(format("~p", [<<"hi">>], 0))),
?assertEqual("<<...>>", lists:flatten(format("~p", [<<"hi">>], 1))),
?assertEqual("<<\"hello\">>", lists:flatten(format("~p", [<<$h, $e, $l, $l, $o>>], 50))),
?assertEqual("<<\"hello\">>", lists:flatten(format("~p", [<<"hello">>], 50))),
?assertEqual("<<104,101,108,108,111>>", lists:flatten(format("~w", [<<"hello">>], 50))),
?assertEqual("<<1,2,3,4>>", lists:flatten(format("~p", [<<1, 2, 3, 4>>], 50))),
?assertEqual([1, 2, 3, 4], lists:flatten(format("~s", [<<1, 2, 3, 4>>], 50))),
?assertEqual("hello", lists:flatten(format("~s", [<<"hello">>], 50))),
?assertEqual("hello\nworld", lists:flatten(format("~s", [<<"hello\nworld">>], 50))),
?assertEqual("<<\"hello\\nworld\">>", lists:flatten(format("~p", [<<"hello\nworld">>], 50))),
?assertEqual("<<\"\\\"hello world\\\"\">>", lists:flatten(format("~p", [<<"\"hello world\"">>], 50))),
?assertEqual("<<\"hello\\\\world\">>", lists:flatten(format("~p", [<<"hello\\world">>], 50))),
?assertEqual("<<\"hello\\\\\world\">>", lists:flatten(format("~p", [<<"hello\\\world">>], 50))),
?assertEqual("<<\"hello\\\\\\\\world\">>", lists:flatten(format("~p", [<<"hello\\\\world">>], 50))),
?assertEqual("<<\"hello\\bworld\">>", lists:flatten(format("~p", [<<"hello\bworld">>], 50))),
?assertEqual("<<\"hello\\tworld\">>", lists:flatten(format("~p", [<<"hello\tworld">>], 50))),
?assertEqual("<<\"hello\\nworld\">>", lists:flatten(format("~p", [<<"hello\nworld">>], 50))),
?assertEqual("<<\"hello\\rworld\">>", lists:flatten(format("~p", [<<"hello\rworld">>], 50))),
?assertEqual("<<\"hello\\eworld\">>", lists:flatten(format("~p", [<<"hello\eworld">>], 50))),
?assertEqual("<<\"hello\\fworld\">>", lists:flatten(format("~p", [<<"hello\fworld">>], 50))),
?assertEqual("<<\"hello\\vworld\">>", lists:flatten(format("~p", [<<"hello\vworld">>], 50))),
?assertEqual(" hello", lists:flatten(format("~10s", [<<"hello">>], 50))),
?assertEqual("[a]", lists:flatten(format("~s", [<<"[a]">>], 50))),
?assertEqual("[a]", lists:flatten(format("~s", [[<<"[a]">>]], 50))),
ok.
bitstring_printing_test() ->
?assertEqual("<<1,2,3,1:7>>", lists:flatten(format("~p",
[<<1, 2, 3, 1:7>>], 100))),
?assertEqual("<<1:7>>", lists:flatten(format("~p",
[<<1:7>>], 100))),
?assertEqual("<<1,2,3,...>>", lists:flatten(format("~p",
[<<1, 2, 3, 1:7>>], 12))),
?assertEqual("<<1,2,3,...>>", lists:flatten(format("~p",
[<<1, 2, 3, 1:7>>], 13))),
?assertEqual("<<1,2,3,1:7>>", lists:flatten(format("~p",
[<<1, 2, 3, 1:7>>], 14))),
?assertEqual("<<..>>", lists:flatten(format("~p", [<<1:7>>], 0))),
?assertEqual("<<...>>", lists:flatten(format("~p", [<<1:7>>], 1))),
?assertEqual("[<<1>>,<<2>>]", lists:flatten(format("~p", [[<<1>>, <<2>>]],
100))),
?assertEqual("{<<1:7>>}", lists:flatten(format("~p", [{<<1:7>>}], 50))),
ok.
list_printing_test() ->
?assertEqual("[]", lists:flatten(format("~p", [[]], 50))),
?assertEqual("[]", lists:flatten(format("~w", [[]], 50))),
?assertEqual("", lists:flatten(format("~s", [[]], 50))),
?assertEqual("...", lists:flatten(format("~s", [[]], -1))),
?assertEqual("[[]]", lists:flatten(format("~p", [[[]]], 50))),
?assertEqual("[13,11,10,8,5,4]", lists:flatten(format("~p", [[13, 11, 10, 8, 5, 4]], 50))),
?assertEqual("\"\\rabc\"", lists:flatten(format("~p", [[13, $a, $b, $c]], 50))),
?assertEqual("[1,2,3|4]", lists:flatten(format("~p", [[1, 2, 3 | 4]], 50))),
?assertEqual("[...]", lists:flatten(format("~p", [[1, 2, 3, 4]], 4))),
?assertEqual("[1,...]", lists:flatten(format("~p", [[1, 2, 3, 4]], 6))),
?assertEqual("[1,...]", lists:flatten(format("~p", [[1, 2, 3, 4]], 7))),
?assertEqual("[1,2,...]", lists:flatten(format("~p", [[1, 2, 3, 4]], 8))),
?assertEqual("[1|4]", lists:flatten(format("~p", [[1 | 4]], 50))),
?assertEqual("[1]", lists:flatten(format("~p", [[1]], 50))),
?assertError(badarg, lists:flatten(format("~s", [[1 | 4]], 50))),
?assertEqual("\"hello...\"", lists:flatten(format("~p", ["hello world"], 10))),
?assertEqual("hello w...", lists:flatten(format("~s", ["hello world"], 10))),
?assertEqual("hello world\r\n", lists:flatten(format("~s", ["hello world\r\n"], 50))),
?assertEqual("\rhello world\r\n", lists:flatten(format("~s", ["\rhello world\r\n"], 50))),
?assertEqual("\"\\rhello world\\r\\n\"", lists:flatten(format("~p", ["\rhello world\r\n"], 50))),
?assertEqual("[13,104,101,108,108,111,32,119,111,114,108,100,13,10]", lists:flatten(format("~w", ["\rhello world\r\n"], 60))),
?assertEqual("...", lists:flatten(format("~s", ["\rhello world\r\n"], 3))),
?assertEqual("[22835963083295358096932575511191922182123945984,...]",
lists:flatten(format("~p", [
[22835963083295358096932575511191922182123945984,
22835963083295358096932575511191922182123945984]], 9))),
?assertEqual("[22835963083295358096932575511191922182123945984,...]",
lists:flatten(format("~p", [
[22835963083295358096932575511191922182123945984,
22835963083295358096932575511191922182123945984]], 53))),
%%improper list
?assertEqual("[1,2,3|4]", lists:flatten(format("~P", [[1 | [2 | [3 | 4]]], 5], 50))),
?assertEqual("[1|1]", lists:flatten(format("~P", [[1 | 1], 5], 50))),
?assertEqual("[9|9]", lists:flatten(format("~p", [[9 | 9]], 50))),
ok.
iolist_printing_test() ->
?assertEqual("iolist: HelloIamaniolist",
lists:flatten(format("iolist: ~s", [[$H, $e, $l, $l, $o, "I", ["am", [<<"an">>], [$i, $o, $l, $i, $s, $t]]]], 1000))),
?assertEqual("123...",
lists:flatten(format("~s", [[<<"123456789">>, "HellIamaniolist"]], 6))),
?assertEqual("123456...",
lists:flatten(format("~s", [[<<"123456789">>, "HellIamaniolist"]], 9))),
?assertEqual("123456789H...",
lists:flatten(format("~s", [[<<"123456789">>, "HellIamaniolist"]], 13))),
?assertEqual("123456789HellIamaniolist",
lists:flatten(format("~s", [[<<"123456789">>, "HellIamaniolist"]], 30))),
ok.
tuple_printing_test() ->
?assertEqual("{}", lists:flatten(format("~p", [{}], 50))),
?assertEqual("{}", lists:flatten(format("~w", [{}], 50))),
?assertError(badarg, lists:flatten(format("~s", [{}], 50))),
?assertEqual("{...}", lists:flatten(format("~p", [{foo}], 1))),
?assertEqual("{...}", lists:flatten(format("~p", [{foo}], 2))),
?assertEqual("{...}", lists:flatten(format("~p", [{foo}], 3))),
?assertEqual("{...}", lists:flatten(format("~p", [{foo}], 4))),
?assertEqual("{...}", lists:flatten(format("~p", [{foo}], 5))),
?assertEqual("{foo,...}", lists:flatten(format("~p", [{foo, bar}], 6))),
?assertEqual("{foo,...}", lists:flatten(format("~p", [{foo, bar}], 7))),
?assertEqual("{foo,...}", lists:flatten(format("~p", [{foo, bar}], 9))),
?assertEqual("{foo,bar}", lists:flatten(format("~p", [{foo, bar}], 10))),
?assertEqual("{22835963083295358096932575511191922182123945984,...}",
lists:flatten(format("~w", [
{22835963083295358096932575511191922182123945984,
22835963083295358096932575511191922182123945984}], 10))),
?assertEqual("{22835963083295358096932575511191922182123945984,...}",
lists:flatten(format("~w", [
{22835963083295358096932575511191922182123945984,
bar}], 10))),
?assertEqual("{22835963083295358096932575511191922182123945984,...}",
lists:flatten(format("~w", [
{22835963083295358096932575511191922182123945984,
22835963083295358096932575511191922182123945984}], 53))),
ok.
map_printing_test() ->
?assertEqual("#{}", lists:flatten(format("~p", [maps:new()], 50))),
?assertEqual("#{}", lists:flatten(format("~p", [maps:new()], 3))),
?assertEqual("#{}", lists:flatten(format("~w", [maps:new()], 50))),
?assertError(badarg, lists:flatten(format("~s", [maps:new()], 50))),
?assertEqual("#{...}", lists:flatten(format("~p", [maps:from_list([{bar, foo}])], 1))),
?assertEqual("#{...}", lists:flatten(format("~p", [maps:from_list([{bar, foo}])], 6))),
?assertEqual("#{bar => ...}", lists:flatten(format("~p", [maps:from_list([{bar, foo}])], 7))),
?assertEqual("#{bar => ...}", lists:flatten(format("~p", [maps:from_list([{bar, foo}])], 9))),
?assertEqual("#{bar => foo}", lists:flatten(format("~p", [maps:from_list([{bar, foo}])], 10))),
?assertEqual("#{bar => ...,...}", lists:flatten(format("~p", [maps:from_list([{bar, foo}, {foo, bar}])], 9))),
?assertEqual("#{bar => foo,...}", lists:flatten(format("~p", [maps:from_list([{bar, foo}, {foo, bar}])], 10))),
?assertEqual("#{bar => foo,...}", lists:flatten(format("~p", [maps:from_list([{bar, foo}, {foo, bar}])], 17))),
?assertEqual("#{bar => foo,foo => ...}", lists:flatten(format("~p", [maps:from_list([{bar, foo}, {foo, bar}])], 18))),
?assertEqual("#{bar => foo,foo => ...}", lists:flatten(format("~p", [maps:from_list([{bar, foo}, {foo, bar}])], 19))),
?assertEqual("#{bar => foo,foo => ...}", lists:flatten(format("~p", [maps:from_list([{bar, foo}, {foo, bar}])], 20))),
?assertEqual("#{bar => foo,foo => bar}", lists:flatten(format("~p", [maps:from_list([{bar, foo}, {foo, bar}])], 21))),
?assertEqual("#{22835963083295358096932575511191922182123945984 => ...}",
lists:flatten(format("~w", [
maps:from_list([{22835963083295358096932575511191922182123945984,
22835963083295358096932575511191922182123945984}])], 10))),
?assertEqual("#{22835963083295358096932575511191922182123945984 => ...}",
lists:flatten(format("~w", [
maps:from_list([{22835963083295358096932575511191922182123945984,
bar}])], 10))),
?assertEqual("#{22835963083295358096932575511191922182123945984 => ...}",
lists:flatten(format("~w", [
maps:from_list([{22835963083295358096932575511191922182123945984,
bar}])], 53))),
?assertEqual("#{22835963083295358096932575511191922182123945984 => bar}",
lists:flatten(format("~w", [
maps:from_list([{22835963083295358096932575511191922182123945984,
bar}])], 54))),
ok.
unicode_test() ->
?assertEqual([231, 167, 129], lists:flatten(format("~s", [<<231, 167, 129>>], 50))),
?assertEqual([31169], lists:flatten(format("~ts", [<<231, 167, 129>>], 50))),
ok.
depth_limit_test() ->
?assertEqual("{...}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 1], 50))),
?assertEqual("{a,...}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 2], 50))),
?assertEqual("{a,[...]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 3], 50))),
?assertEqual("{a,[b|...]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 4], 50))),
?assertEqual("{a,[b,[...]]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 5], 50))),
?assertEqual("{a,[b,[c|...]]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 6], 50))),
?assertEqual("{a,[b,[c,[...]]]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 7], 50))),
?assertEqual("{a,[b,[c,[d]]]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 8], 50))),
?assertEqual("{a,[b,[c,[d]]]}", lists:flatten(format("~P", [{a, [b, [c, [d]]]}, 9], 50))),
?assertEqual("{a,{...}}", lists:flatten(format("~P", [{a, {b, {c, {d}}}}, 3], 50))),
?assertEqual("{a,{b,...}}", lists:flatten(format("~P", [{a, {b, {c, {d}}}}, 4], 50))),
?assertEqual("{a,{b,{...}}}", lists:flatten(format("~P", [{a, {b, {c, {d}}}}, 5], 50))),
?assertEqual("{a,{b,{c,...}}}", lists:flatten(format("~P", [{a, {b, {c, {d}}}}, 6], 50))),
?assertEqual("{a,{b,{c,{...}}}}", lists:flatten(format("~P", [{a, {b, {c, {d}}}}, 7], 50))),
?assertEqual("{a,{b,{c,{d}}}}", lists:flatten(format("~P", [{a, {b, {c, {d}}}}, 8], 50))),
?assertEqual("#{a => #{...}}",
lists:flatten(format("~P",
[maps:from_list([{a, maps:from_list([{b, maps:from_list([{c, d}])}])}]), 2], 50))),
?assertEqual("#{a => #{b => #{...}}}",
lists:flatten(format("~P",
[maps:from_list([{a, maps:from_list([{b, maps:from_list([{c, d}])}])}]), 3], 50))),
?assertEqual("#{a => #{b => #{c => d}}}",
lists:flatten(format("~P",
[maps:from_list([{a, maps:from_list([{b, maps:from_list([{c, d}])}])}]), 4], 50))),
?assertEqual("#{}", lists:flatten(format("~P", [maps:new(), 1], 50))),
?assertEqual("#{...}", lists:flatten(format("~P", [maps:from_list([{1, 1}, {2, 2}, {3, 3}]), 1], 50))),
?assertEqual("#{1 => 1,...}", lists:flatten(format("~P", [maps:from_list([{1, 1}, {2, 2}, {3, 3}]), 2], 50))),
?assertEqual("#{1 => 1,2 => 2,...}", lists:flatten(format("~P", [maps:from_list([{1, 1}, {2, 2}, {3, 3}]), 3], 50))),
?assertEqual("#{1 => 1,2 => 2,3 => 3}", lists:flatten(format("~P", [maps:from_list([{1, 1}, {2, 2}, {3, 3}]), 4], 50))),
?assertEqual("{\"a\",[...]}", lists:flatten(format("~P", [{"a", ["b", ["c", ["d"]]]}, 3], 50))),
?assertEqual("{\"a\",[\"b\",[[...]|...]]}", lists:flatten(format("~P", [{"a", ["b", ["c", ["d"]]]}, 6], 50))),
?assertEqual("{\"a\",[\"b\",[\"c\",[\"d\"]]]}", lists:flatten(format("~P", [{"a", ["b", ["c", ["d"]]]}, 9], 50))),
?assertEqual("[...]", lists:flatten(format("~P", [[1, 2, 3], 1], 50))),
?assertEqual("[1|...]", lists:flatten(format("~P", [[1, 2, 3], 2], 50))),
?assertEqual("[1,2|...]", lists:flatten(format("~P", [[1, 2, 3], 3], 50))),
?assertEqual("[1,2,3]", lists:flatten(format("~P", [[1, 2, 3], 4], 50))),
?assertEqual("{1,...}", lists:flatten(format("~P", [{1, 2, 3}, 2], 50))),
?assertEqual("{1,2,...}", lists:flatten(format("~P", [{1, 2, 3}, 3], 50))),
?assertEqual("{1,2,3}", lists:flatten(format("~P", [{1, 2, 3}, 4], 50))),
?assertEqual("{1,...}", lists:flatten(format("~P", [{1, 2, 3}, 2], 50))),
?assertEqual("[1,2|...]", lists:flatten(format("~P", [[1, 2, <<3>>], 3], 50))),
?assertEqual("[1,2,<<...>>]", lists:flatten(format("~P", [[1, 2, <<3>>], 4], 50))),
?assertEqual("[1,2,<<3>>]", lists:flatten(format("~P", [[1, 2, <<3>>], 5], 50))),
?assertEqual("<<...>>", lists:flatten(format("~P", [<<0, 0, 0, 0>>, 1], 50))),
?assertEqual("<<0,...>>", lists:flatten(format("~P", [<<0, 0, 0, 0>>, 2], 50))),
?assertEqual("<<0,0,...>>", lists:flatten(format("~P", [<<0, 0, 0, 0>>, 3], 50))),
?assertEqual("<<0,0,0,...>>", lists:flatten(format("~P", [<<0, 0, 0, 0>>, 4], 50))),
?assertEqual("<<0,0,0,0>>", lists:flatten(format("~P", [<<0, 0, 0, 0>>, 5], 50))),
%% this is a seriously weird edge case
?assertEqual("<<\" \"...>>", lists:flatten(format("~P", [<<32, 32, 32, 0>>, 2], 50))),
?assertEqual("<<\" \"...>>", lists:flatten(format("~P", [<<32, 32, 32, 0>>, 3], 50))),
?assertEqual("<<\" \"...>>", lists:flatten(format("~P", [<<32, 32, 32, 0>>, 4], 50))),
?assertEqual("<<32,32,32,0>>", lists:flatten(format("~P", [<<32, 32, 32, 0>>, 5], 50))),
?assertEqual("<<32,32,32,0>>", lists:flatten(format("~p", [<<32, 32, 32, 0>>], 50))),
%% depth limiting for some reason works in 4 byte chunks on printable binaries?
?assertEqual("<<\"hell\"...>>", lists:flatten(format("~P", [<<"hello world">>, 2], 50))),
?assertEqual("<<\"abcd\"...>>", lists:flatten(format("~P", [<<$a, $b, $c, $d, $e, 0>>, 2], 50))),
%% I don't even know...
?assertEqual("<<>>", lists:flatten(format("~P", [<<>>, 1], 50))),
?assertEqual("<<>>", lists:flatten(format("~W", [<<>>, 1], 50))),
?assertEqual("{abc,<<\"abc\\\"\">>}", lists:flatten(format("~P", [{abc, <<"abc\"">>}, 4], 50))),
ok.
print_terms_without_format_string_test() ->
?assertError(badarg, format({hello, world}, [], 50)),
?assertError(badarg, format([{google, bomb}], [], 50)),
?assertEqual([$h, $e, $l, $l, $o, 3594], format([$h, $e, $l, $l, $o, 3594], [], 50)),
?assertError(badarg, format([$h, $e, $l, $l, $o, 65535], [], 50)),
?assertEqual("helloworld", lists:flatten(format([$h, $e, $l, $l, $o, "world"], [], 50))),
?assertEqual("hello", lists:flatten(format(<<"hello">>, [], 50))),
?assertEqual("hello", lists:flatten(format('hello', [], 50))),
?assertError(badarg, format(<<1, 2, 3, 1:7>>, [], 100)),
?assertError(badarg, format(65535, [], 50)),
ok.
improper_io_list_test() ->
?assertEqual(">hello", lists:flatten(format('~s', [[$> | <<"hello">>]], 50))),
?assertEqual(">hello", lists:flatten(format('~ts', [[$> | <<"hello">>]], 50))),
?assertEqual("helloworld", lists:flatten(format('~ts', [[<<"hello">> | <<"world">>]], 50))),
ok.
-endif.

+ 1
- 1
src/utils/rumUtil.erl Vedi File

@ -526,7 +526,7 @@ checkHwm(Shaper = #rumShaper{filter = Filter}, Event) ->
end.
%% S i.e.
checkHwm(#rumShaper{id = Id, hwm = Hwm, mps = Mps, lastTime = LastTime, dropped = Drop, flushQueue = FlushQueue, flushThreshold = FlushThreshold, timer = Timer, filter = Filter} = Shaper) ->
checkHwm(#rumShaper{id = Id, hwm = Hwm, mps = Mps, lastTime = LastTime, dropped = Drop, flushQueue = FlushQueue, flushThr = FlushThreshold, timer = Timer, filter = Filter} = Shaper) ->
if
Hwm == undefined ->
{true, 0, Shaper};

Caricamento…
Annulla
Salva