diff --git a/README.md b/README.md index 08d0268..0fe2a4b 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/include/eRum.hrl b/include/eRum.hrl index 1136f7d..9b41fb5 100644 --- a/include/eRum.hrl +++ b/include/eRum.hrl @@ -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). %% 默认的元数据 diff --git a/include/rumDef.hrl b/include/rumDef.hrl index 665fea0..c49b98f 100644 --- a/include/rumDef.hrl +++ b/include/rumDef.hrl @@ -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为true,则可以设置消息队列长度阈值,在该阈值处将开始丢弃消息。默认阈值为0, %% 这意味着如果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_event:which_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)). diff --git a/include/rumMsg.hrl b/include/rumMsg.hrl index 76adca4..119eb66 100644 --- a/include/rumMsg.hrl +++ b/include/rumMsg.hrl @@ -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 - }). \ No newline at end of file + }). +-endif. \ No newline at end of file diff --git a/src/backend/rumBkdConsole.erl b/src/backend/rumBkdConsole.erl index b124dc9..2413bea 100644 --- a/src/backend/rumBkdConsole.erl +++ b/src/backend/rumBkdConsole.erl @@ -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 diff --git a/src/backend/rumBkdFile.erl b/src/backend/rumBkdFile.erl index a89f6aa..7b0b462 100644 --- a/src/backend/rumBkdFile.erl +++ b/src/backend/rumBkdFile.erl @@ -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}}. diff --git a/src/crashLog/rumCrashLog.erl b/src/crashLog/rumCrashLog.erl index 1141c03..860405e 100644 --- a/src/crashLog/rumCrashLog.erl +++ b/src/crashLog/rumCrashLog.erl @@ -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} -> diff --git a/src/eRum.erl b/src/eRum.erl index 054fd17..331493c 100644 --- a/src/eRum.erl +++ b/src/eRum.erl @@ -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. diff --git a/src/eRum1.erl b/src/eRum1.erl index 8064b9e..8aa4aca 100644 --- a/src/eRum1.erl +++ b/src/eRum1.erl @@ -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 diff --git a/src/errLogger/rumErrLoggerH.erl b/src/errLogger/rumErrLoggerH.erl index fd1cad4..2f4068d 100644 --- a/src/errLogger/rumErrLoggerH.erl +++ b/src/errLogger/rumErrLoggerH.erl @@ -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}}. diff --git a/src/formatter/rumFormatter.erl b/src/formatter/rumFormatTer.erl similarity index 99% rename from src/formatter/rumFormatter.erl rename to src/formatter/rumFormatTer.erl index 94d755d..b0decab 100644 --- a/src/formatter/rumFormatter.erl +++ b/src/formatter/rumFormatTer.erl @@ -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. diff --git a/src/utils/rumStdlib.erl b/src/utils/rumStdlib.erl index 99fc028..c2833c4 100644 --- a/src/utils/rumStdlib.erl +++ b/src/utils/rumStdlib.erl @@ -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. diff --git a/src/utils/rumTruncIo.erl b/src/utils/rumTruncIo.erl deleted file mode 100644 index 58c70b6..0000000 --- a/src/utils/rumTruncIo.erl +++ /dev/null @@ -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, matthias@corelatus.se -%% -%% Various changes to this module, most notably the format/3 implementation -%% were added by Andrew Thompson `'. 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), - <> = 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. diff --git a/src/utils/rumUtil.erl b/src/utils/rumUtil.erl index cdf55ec..1898e6b 100644 --- a/src/utils/rumUtil.erl +++ b/src/utils/rumUtil.erl @@ -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};