From deafa022b623dd84dc3bb10d7a879d110b2df1cf Mon Sep 17 00:00:00 2001 From: SisMaker <1713699517@qq.com> Date: Sun, 6 Jun 2021 00:27:01 +0800 Subject: [PATCH] =?UTF-8?q?ft:=20=E4=BB=A3=E7=A0=81=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- c_src/tracer/erl_tracer_nif.c1 | 257 --------------------------------- include/eTpf.hrl | 10 +- rebar.config | 1 + src/tracer/tpTracerFile.erl | 80 +++++----- src/tracer/tpTracerLog.erl | 112 ++++++++++++++ src/utils/tpTermCut.erl | 5 +- test/lg_SUITE.erl | 2 +- 8 files changed, 170 insertions(+), 299 deletions(-) delete mode 100644 c_src/tracer/erl_tracer_nif.c1 create mode 100644 src/tracer/tpTracerLog.erl diff --git a/README.md b/README.md index f42af41..62869b2 100644 --- a/README.md +++ b/README.md @@ -236,7 +236,7 @@ end. 对于长时间运行的会话,eTpf可以旋转跟踪文件。这是一项有助于避免磁盘空间用完的功能,并且不用于保留较小的文件(eTpf可以处理非常大的文件就可以了)。 eTpf:trace/3,4可以提供一个映射,而不是将文件名前缀作为第三个参数传递 给。当前有三个选项,包括filename_prefix。其他选项是最大文件大小(以字节为单位)fMaxSize,以及将在文件中每LZ4帧存储的事件数 -fEventFrame。这两个选项使您可以控制文件写入或旋转的频率。 +fMaxLog。这两个选项使您可以控制文件写入或旋转的频率。 以下示例将文件大小限制为100MB: diff --git a/c_src/tracer/erl_tracer_nif.c1 b/c_src/tracer/erl_tracer_nif.c1 deleted file mode 100644 index 65b54a3..0000000 --- a/c_src/tracer/erl_tracer_nif.c1 +++ /dev/null @@ -1,257 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson 2015-2016. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -/* - * Purpose: NIF library for process/port tracer - * - */ - - -#define STATIC_ERLANG_NIF 1 - -#include "erl_nif.h" -#include "config.h" -#include "sys.h" - -#ifdef VALGRIND -# include -#endif - -/* NIF interface declarations */ -static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); -static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info); -static void unload(ErlNifEnv* env, void* priv_data); - -/* The NIFs: */ -static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); - -static ErlNifFunc nif_funcs[] = { - {"enabled", 3, enabled}, - {"trace", 5, trace} -}; - - -ERL_NIF_INIT(erl_tracer, nif_funcs, load, NULL, upgrade, unload) - -#define ATOMS \ - ATOM_DECL(call); \ - ATOM_DECL(command); \ - ATOM_DECL(cpu_timestamp); \ - ATOM_DECL(discard); \ - ATOM_DECL(exception_from); \ - ATOM_DECL(extra); \ - ATOM_DECL(match_spec_result); \ - ATOM_DECL(monotonic); \ - ATOM_DECL(ok); \ - ATOM_DECL(remove); \ - ATOM_DECL(return_from); \ - ATOM_DECL(scheduler_id); \ - ATOM_DECL(send); \ - ATOM_DECL(send_to_non_existing_process); \ - ATOM_DECL(seq_trace); \ - ATOM_DECL(spawn); \ - ATOM_DECL(strict_monotonic); \ - ATOM_DECL(timestamp); \ - ATOM_DECL(trace); \ - ATOM_DECL(trace_status); \ - ATOM_DECL(trace_ts); \ - ATOM_DECL(true); \ - ATOM_DECL(gc_minor_start); \ - ATOM_DECL(gc_minor_end); \ - ATOM_DECL(gc_major_start); \ - ATOM_DECL(gc_major_end); - -#define ATOM_DECL(A) static ERL_NIF_TERM atom_##A -ATOMS -#undef ATOM_DECL - -static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) -{ - -#define ATOM_DECL(A) atom_##A = enif_make_atom(env, #A) -ATOMS -#undef ATOM_DECL - - *priv_data = NULL; - - return 0; -} - -static void unload(ErlNifEnv* env, void* priv_data) -{ - -} - -static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, - ERL_NIF_TERM load_info) -{ - if (*old_priv_data != NULL) { - return -1; /* Don't know how to do that */ - } - if (*priv_data != NULL) { - return -1; /* Don't know how to do that */ - } - if (load(env, priv_data, load_info)) { - return -1; - } - return 0; -} - -static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - ErlNifPid to_pid; - ErlNifPort to_port; - ERL_NIF_TERM ret = enif_is_identical(argv[0], atom_trace_status) ? - atom_remove : atom_discard; - - ASSERT(argc == 3); - - if (enif_get_local_pid(env, argv[1], &to_pid)) { - if (!enif_is_process_alive(env, &to_pid)) - /* tracer is dead so we should remove this trace point */ - return ret; - } else if (enif_get_local_port(env, argv[1], &to_port)) { - if (!enif_is_port_alive(env, &to_port)) - /* tracer is dead so we should remove this trace point */ - return ret; - } else { - /* The state was not a pid or a port */ - return ret; - } - - /* Only generate trace for when tracer != tracee */ - if (enif_is_identical(argv[1], argv[2])) { - return atom_discard; - } - - return atom_trace; -} - -/* - -spec trace(seq_trace, TracerState :: pid() | port(), - Label :: non_neg_integer(), - Msg :: term(), - Opts :: map()) -> ignored(); - trace(Tag :: atom(), TracerState :: pid() | port(), - Tracee :: pid() || port() || undefined, - Msg :: term(), - Opts :: map()) -> ignored(). -*/ -static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{ - ERL_NIF_TERM value, msg, tt[8], opts; - ErlNifPid to_pid; - ErlNifPort to_port; - size_t tt_sz = 0; - int is_port = 0; - size_t opts_sz = 0; - ASSERT(argc == 5); - - if (!enif_get_local_pid(env, argv[1], &to_pid)) { - if (!enif_get_local_port(env, argv[1], &to_port)) { - /* This only fails if argv[1] is a not a local port/pid - which should not happen as it is checked in enabled */ - ASSERT(0); - return atom_ok; - } - is_port = 1; - } - - opts = argv[4]; - - if (!enif_get_map_size(env, opts, &opts_sz)) - opts_sz = 0; - - if (opts_sz && enif_get_map_value(env, opts, atom_extra, &value)) { - tt[tt_sz++] = atom_trace; - tt[tt_sz++] = argv[2]; - tt[tt_sz++] = argv[0]; - tt[tt_sz++] = argv[3]; - tt[tt_sz++] = value; - } else { - if (enif_is_identical(argv[0], atom_seq_trace)) { - tt[tt_sz++] = atom_seq_trace; - tt[tt_sz++] = argv[2]; - tt[tt_sz++] = argv[3]; - } else { - tt[tt_sz++] = atom_trace; - tt[tt_sz++] = argv[2]; - tt[tt_sz++] = argv[0]; - tt[tt_sz++] = argv[3]; - } - } - - - if (opts_sz && enif_get_map_value(env, opts, atom_match_spec_result, &value)) { - tt[tt_sz++] = value; - } - - if (opts_sz && enif_get_map_value(env, opts, atom_scheduler_id, &value)) { - tt[tt_sz++] = value; - } - - if (opts_sz && enif_get_map_value(env, opts, atom_timestamp, &value)) { - ERL_NIF_TERM ts; - if (enif_is_identical(value, atom_monotonic)) { - ErlNifTime mon = enif_monotonic_time(ERL_NIF_NSEC); - ts = enif_make_int64(env, mon); - } else if (enif_is_identical(value, atom_strict_monotonic)) { - ErlNifTime mon = enif_monotonic_time(ERL_NIF_NSEC); - ERL_NIF_TERM unique = enif_make_unique_integer( - env, ERL_NIF_UNIQUE_MONOTONIC); - ts = enif_make_tuple2(env, enif_make_int64(env, mon), unique); - } else if (enif_is_identical(value, atom_timestamp)) { - ts = enif_now_time(env); - } else if (enif_is_identical(value, atom_cpu_timestamp)) { - ts = enif_cpu_time(env); - } else { - ASSERT(0); - goto error; - } - tt[tt_sz++] = ts; - if (tt[0] == atom_trace) - tt[0] = atom_trace_ts; - } - - msg = enif_make_tuple_from_array(env, tt, tt_sz); - - if (is_port) { - ErlNifBinary bin; - - if (!enif_term_to_binary(env, msg, &bin)) - goto error; - - msg = enif_make_binary(env, &bin); - - (void) enif_port_command(env, &to_port, NULL, msg); - /* if failure: port has probably died, enabled will clean up */ - - enif_release_binary(&bin); - } else { - - (void) enif_send(env, &to_pid, NULL, msg); - /* if failure: process has probably died, enabled will clean up */ - } - -error: - - return atom_ok; -} diff --git a/include/eTpf.hrl b/include/eTpf.hrl index 7bf745d..9fe010c 100644 --- a/include/eTpf.hrl +++ b/include/eTpf.hrl @@ -38,14 +38,16 @@ port => pos_integer() %% tracer for file - , fBaseName => string() - , fMaxSize => pos_integer() - , fEventFrame => pos_integer() + , fDir => string() %% 文件目录 + , fBaseName => string() %% 文件base file name + , fMaxSize => pos_integer() %% 最大的文件字节数 + , fMaxLog => pos_integer() %% 最大日志数量 + , fMaxMsg => pos_integer() %% 最大消息数量 %% tracer for console %% trace msg term cut cfg - , tcmIsCut => boolean() %% 是否cut trace msg term + , tcmIsCut => boolean() %% 是否cut trace msg term , tcmDepth => pos_integer() %% term cut时保留的最大深度 , tcmListSize => pos_integer() %% List term cut时保留的最大数量 , tcmMapSize => pos_integer() %% Map term cut时保留的最大数量 diff --git a/rebar.config b/rebar.config index 28a19f4..eb09672 100644 --- a/rebar.config +++ b/rebar.config @@ -1,6 +1,7 @@ {erl_opts, [debug_info]}. {deps, [ {eFmt, ".*", {git, "http://192.168.0.88:53000/SisMaker/eFmt.git", {branch, "master"}}}, + {eSync, ".*", {git, "http://192.168.0.88:53000/SisMaker/eSync.git", {branch, "master"}}}, {eGbh, ".*", {git, "http://192.168.0.88:53000/SisMaker/eGbh.git", {branch, "master"}}} ]}. diff --git a/src/tracer/tpTracerFile.erl b/src/tracer/tpTracerFile.erl index 95ec3a6..76a9aaa 100644 --- a/src/tracer/tpTracerFile.erl +++ b/src/tracer/tpTracerFile.erl @@ -15,15 +15,15 @@ -record(state, { parent :: pid() - , fBaseName :: file:filename_all() + , fDir :: file:filename_all() %% file base name + , fBaseName :: file:filename_all() %% file base name , size = 0 :: non_neg_integer() , fMaxSize :: infinity | non_neg_integer() , ioDevice :: file:io_device() - , fEventFrame :: pos_integer() - , eventsThisFrame = 0 :: non_neg_integer() - , buffer = <<>> :: binary() - , tcmIsCut = false + , fMaxMsg = 0 :: non_neg_integer() + , msgCnt = 0 + , msgList = [] }). start_link(Opts) -> @@ -34,7 +34,9 @@ init(Parent, TracerOpts) -> process_flag(message_queue_data, off_heap), process_flag(trap_exit, true), %% No need to close the file, it'll be closed when the process exits. - Filename = maps:get(fBaseName, TracerOpts, "traces.lz4"), + FDir = maps:get(fDir, TracerOpts, <<"./">>), + FBaseName = maps:get(fBaseName, TracerOpts, <<"traces.zip">>), + Filename = fileName(FDir, FBaseName), {ok, IoDevice} = file:open(Filename, [write, raw]), TcmIsCut = maps:get(tcmIsCut, TracerOpts, maps:get(tcmDepth, ?defTcmMap)), @@ -53,15 +55,16 @@ init(Parent, TracerOpts) -> State = #state{ parent = Parent - , fBaseName = Filename + , fDir = FDir + , fBaseName = FBaseName , ioDevice = IoDevice - , fMaxSize = maps:get(fMaxSize, TracerOpts, infinity) - , fEventFrame = maps:get(fEventFrame, TracerOpts, 100000) + , fMaxSize = maps:get(fMaxSize, TracerOpts, 104857600) + , fMaxMsg = maps:get(fMaxMsg, TracerOpts, 150) , tcmIsCut = TcmIsCut }, loop(State). -loop(State = #state{parent = Parent, size = Size, ioDevice = IoDevice, fEventFrame = MaxEvents, eventsThisFrame = NumEvents0, buffer = Buffer0, tcmIsCut = TcmIsCut}) -> +loop(#state{parent = Parent, size = Size, fDir = FDir, fBaseName = FBaseName, ioDevice = IoDevice, fMaxSize = MaxSize, tcmIsCut = TcmIsCut, fMaxMsg = FMaxMsg, msgCnt = MsgCnt, msgList = MsgList} = State) -> receive {system, From, Request} -> sys:handle_system_msg(Request, From, Parent, ?MODULE, [], State); @@ -69,31 +72,30 @@ loop(State = #state{parent = Parent, size = Size, ioDevice = IoDevice, fEventFra terminate(Reason, State); RMsg -> Msg = case TcmIsCut of true -> tpTermCut:cut(RMsg); _ -> RMsg end, - Bin = term_to_binary(Msg), - BinSize = byte_size(Bin), - Buffer = <>, - NumEvents = NumEvents0 + 1, - if - MaxEvents =:= NumEvents -> - Frame = lz4f:compress_frame(Buffer), - ok = file:write(IoDevice, Frame), - maybe_rotate(State#state{size = Size + byte_size(Frame), - eventsThisFrame = 0, buffer = <<>>}); + NewMsgList = [Msg | MsgList], + NewMsgCnt = MsgCnt + 1, + + case NewMsgCnt >= FMaxMsg of true -> - loop(State#state{eventsThisFrame = NumEvents, buffer = Buffer}) + MsgListBin = zlib:compress(term_to_binary(NewMsgList)), + MsgListSize = byte_size(MsgListBin), + NewSize = Size + MsgListSize, + ok = file:write(IoDevice, [<>, MsgListBin]), + + case NewSize >= MaxSize of + true -> + ok = file:close(IoDevice), + Filename = fileName(FDir, FBaseName), + {ok, NewIoDevice} = file:open(Filename, [write, raw]), + loop(State#state{ioDevice = NewIoDevice, size = 0, msgCnt = 0, msgList = []}); + _ -> + loop(State#state{size = NewSize, msgCnt = 0, msgList = []}) + end; + _ -> + loop(State#state{msgCnt = NewMsgCnt, msgList = NewMsgList}) end end. -%%IMY-todo 干掉这个函数 同时对于旋转的文件名 使用时间 年月日时分 -maybe_rotate(State = #state{fBaseName = Filename, size = Size, fMaxSize = MaxSize, - ioDevice = OldIoDevice}) when Size > MaxSize -> - ok = file:close(OldIoDevice), - ok = file:rename(Filename, Filename ++ ".bak"), - {ok, NewIoDevice} = file:open(Filename, [write, raw]), - loop(State#state{size = 0, ioDevice = NewIoDevice}); -maybe_rotate(State) -> - loop(State). - system_continue(_, _, State) -> loop(State). @@ -105,6 +107,18 @@ system_code_change(Misc, _, _, _) -> {ok, Misc}. -spec terminate(any(), #state{}) -> no_return(). -terminate(Reason, #state{ioDevice = IoDevice, buffer = Buffer}) -> - _ = file:write(IoDevice, lz4f:compress_frame(Buffer)), +terminate(Reason, #state{ioDevice = IoDevice, size = Size, msgList = MsgList}) -> + MsgListBin = zlib:compress(term_to_binary(MsgList)), + MsgListSize = byte_size(MsgListBin), + NewSize = Size + MsgListSize, + ok = file:write(IoDevice, [<>, MsgListBin]), + ok = file:close(IoDevice), exit(Reason). + +fileName(Dir, FBaseName) -> + {{Year, Month, Day}, {Hour, Minute, _Second}} = erlang:localtime(), + TimeStr = eFmt:formatBin("~B~2.10.0B~2.10.0B~B~2.10.0B_", [Year, Month, Day, Hour, Minute]), + FileName = <>, + WholeName = filename:absname(filename:join(Dir, FileName)), + ok = filelib:ensure_dir(WholeName), + WholeName. \ No newline at end of file diff --git a/src/tracer/tpTracerLog.erl b/src/tracer/tpTracerLog.erl new file mode 100644 index 0000000..6ffb306 --- /dev/null +++ b/src/tracer/tpTracerLog.erl @@ -0,0 +1,112 @@ +-module(tpTracerLog). +-include("eTpf.hrl"). + +-export([ + start_link/1 + , init/2 +]). + +%% sys callbacks +-export([ + system_continue/3 + , system_terminate/4 + , system_code_change/4 +]). + +-record(state, { + parent :: pid() + , fDir :: file:filename_all() %% file base name + , fBaseName :: file:filename_all() %% file base name + , size = 0 :: non_neg_integer() + , fMaxSize :: infinity | non_neg_integer() + , ioDevice :: file:io_device() + , tcmIsCut = false + , fMaxLog :: pos_integer() + , fLogIndex = 0 :: non_neg_integer() +}). + +start_link(Opts) -> + Pid = proc_lib:spawn_link(?MODULE, init, [self(), Opts]), + {ok, Pid}. + +init(Parent, TracerOpts) -> + process_flag(message_queue_data, off_heap), + process_flag(trap_exit, true), + %% No need to close the file, it'll be closed when the process exits. + FDir = maps:get(fDir, TracerOpts, <<"./">>), + FBaseName = maps:get(fBaseName, TracerOpts, <<"traces.log">>), + Filename = fileName(FDir, FBaseName), + {ok, IoDevice} = file:open(Filename, [write, raw]), + + TcmIsCut = maps:get(tcmIsCut, TracerOpts, maps:get(tcmDepth, ?defTcmMap)), + case TcmIsCut of + true -> + erlang:put(tcmDepth, maps:get(tcmDepth, TracerOpts, maps:get(tcmDepth, ?defTcmMap))), + erlang:put(tcmListSize, maps:get(tcmListSize, TracerOpts, maps:get(tcmListSize, ?defTcmMap))), + erlang:put(tcmMapSize, maps:get(tcmMapSize, TracerOpts, maps:get(tcmMapSize, ?defTcmMap))), + erlang:put(tcmTupleSize, maps:get(tcmTupleSize, TracerOpts, maps:get(tcmTupleSize, ?defTcmMap))), + erlang:put(tcmBinSize, maps:get(tcmBinSize, TracerOpts, maps:get(tcmBinSize, ?defTcmMap))), + erlang:put(tcmBitSize, maps:get(tcmBitSize, TracerOpts, maps:get(tcmBitSize, ?defTcmMap))), + erlang:put(tcmNestStruct, maps:get(tcmNestStruct, TracerOpts, maps:get(tcmNestStruct, ?defTcmMap))); + _ -> + ignore + end, + + State = #state{ + parent = Parent + , fDir = FDir + , fBaseName = FBaseName + , ioDevice = IoDevice + , fMaxSize = maps:get(fMaxSize, TracerOpts, 61644800) + , fMaxLog = maps:get(fMaxLog, TracerOpts, 50000) + , tcmIsCut = TcmIsCut + }, + loop(State). + +loop(#state{parent = Parent, fDir = FDir, fBaseName = FBaseName, size = Size, ioDevice = IoDevice, fMaxSize = MaxSize, fMaxLog = FMaxLog, fLogIndex = FLogIndex, tcmIsCut = TcmIsCut} = State) -> + receive + {system, From, Request} -> + sys:handle_system_msg(Request, From, Parent, ?MODULE, [], State); + {'EXIT', Parent, Reason} -> + terminate(Reason, State); + RMsg -> + Msg = case TcmIsCut of true -> tpTermCut:cut(RMsg); _ -> RMsg end, + MsgBin = eFmt:format("~w", [Msg]), + ok = file:write(IoDevice, MsgBin), + NewSize = Size + byte_size(MsgBin), + NewFLogIndex = FLogIndex + 1, + case NewFLogIndex >= FMaxLog orelse NewSize >= MaxSize of + true -> + ok = file:close(IoDevice), + Filename = fileName(FDir, FBaseName), + {ok, NewIoDevice} = file:open(Filename, [write, raw]), + loop(State#state{size = 0, fLogIndex = 0, ioDevice = NewIoDevice}); + _ -> + loop(State#state{size = NewSize, fLogIndex = NewFLogIndex}) + end + end. + +system_continue(_, _, State) -> + loop(State). + +-spec system_terminate(any(), _, _, #state{}) -> no_return(). +system_terminate(Reason, _, _, State) -> + terminate(Reason, State). + +system_code_change(Misc, _, _, _) -> + {ok, Misc}. + +-spec terminate(any(), #state{}) -> no_return(). +terminate(Reason, #state{ioDevice = IoDevice}) -> + ok = file:close(IoDevice), + exit(Reason). + +fileName(Dir, FBaseName) -> + {{Year, Month, Day}, {Hour, Minute, _Second}} = erlang:localtime(), + TimeStr = eFmt:formatBin("~B~2.10.0B~2.10.0B~B~2.10.0B_", [Year, Month, Day, Hour, Minute]), + FileName = <>, + WholeName = filename:absname(filename:join(Dir, FileName)), + ok = filelib:ensure_dir(WholeName), + WholeName. + + diff --git a/src/utils/tpTermCut.erl b/src/utils/tpTermCut.erl index 2009633..dd4a961 100644 --- a/src/utils/tpTermCut.erl +++ b/src/utils/tpTermCut.erl @@ -21,7 +21,7 @@ cut(Term, Depth) -> TcmDepth = getTcmValue(tcmDepth), case Depth > TcmDepth of true -> - '$truncated11'; + '$truncated'; _ -> if is_bitstring(Term) -> @@ -53,10 +53,9 @@ cutList([], _, _, _, _, _, Acc) -> cutList([Term | Tail], Depth, Len, TcmTupleSize, TcmNestStruct, NumStructs, Acc) -> case Len >= TcmTupleSize orelse NumStructs >= TcmNestStruct of true -> - lists:reverse(['$truncated33' | Acc]); + lists:reverse(['$truncated' | Acc]); _ -> %% if List was a cons, Tail can be anything - io:format("IMY********************1111 ~p~n", [{Len, NumStructs, Term}]), cutList(Tail, Depth, Len + 1, TcmTupleSize, TcmNestStruct, NumStructs + isStruct(Term), [cut(Term, Depth + 1) | Acc]) end; cutList(Term, Depth, _, _, _, _, Acc) -> %% if List was a cons [[[a,a,a|b],1,2,3]|bbbb] diff --git a/test/lg_SUITE.erl b/test/lg_SUITE.erl index 8dac618..7db0d05 100644 --- a/test/lg_SUITE.erl +++ b/test/lg_SUITE.erl @@ -137,7 +137,7 @@ file_tracer_rotation(Config) -> eTpf:trace(lists, tpTracerFile, #{ fBaseName => Prefix, fMaxSize => 100, %% Intentionally low. - fEventFrame => 10 %% Needed to trigger the rotation, default is too high. + fMaxLog => 10 %% Needed to trigger the rotation, default is too high. }), lists:seq(1, 1000), eTpf:stop(),