diff --git a/README.md b/README.md index e11b621..4e45196 100644 --- a/README.md +++ b/README.md @@ -374,20 +374,20 @@ The module should provide the following callbacks as `lager_rotator_behaviour` ```erlang %% @doc Create a log file --callback(create_logfile(Name :: list(), Buffer :: {integer(), integer()} | any()) -> +-callback(createLogfile(Name :: list(), Buffer :: {integer(), integer()} | any()) -> {ok, {FD :: file:io_device(), Inode :: integer(), Size :: integer()}} | {error, any()}). %% @doc Open a log file --callback(open_logfile(Name :: list(), Buffer :: {integer(), integer()} | any()) -> +-callback(openLogfile(Name :: list(), Buffer :: {integer(), integer()} | any()) -> {ok, {FD :: file:io_device(), Inode :: integer(), Size :: integer()}} | {error, any()}). %% @doc Ensure reference to current target, could be rotated --callback(ensure_logfile(Name :: list(), FD :: file:io_device(), Inode :: integer(), +-callback(ensureLogfile(Name :: list(), FD :: file:io_device(), Inode :: integer(), Buffer :: {integer(), integer()} | any()) -> {ok, {FD :: file:io_device(), Inode :: integer(), Size :: integer()}} | {error, any()}). %% @doc Rotate the log file --callback(rotate_logfile(Name :: list(), Count :: integer()) -> +-callback(rotateLogfile(Name :: list(), Count :: integer()) -> ok). ``` diff --git a/src/backend/rumBackendFile.erl b/src/backend/rumBackendFile.erl index 8cf4535..33da295 100644 --- a/src/backend/rumBackendFile.erl +++ b/src/backend/rumBackendFile.erl @@ -104,7 +104,7 @@ init(LogFileConfig) when is_list(LogFileConfig) -> State0 = #state{name = Name, level = Level, size = Size, date = Date, count = Count, rotator = Rotator, shaper = Shaper, formatter = Formatter, formatter_config = FormatterConfig, sync_on = SyncOn, sync_interval = SyncInterval, sync_size = SyncSize, check_interval = CheckInterval}, - State = case Rotator:create_logfile(Name, {SyncSize, SyncInterval}) of + State = case Rotator:createLogfile(Name, {SyncSize, SyncInterval}) of {ok, {FD, Inode, Ctime, _Size}} -> State0#state{fd = FD, inode = Inode, ctime = Ctime}; {error, Reason} -> @@ -173,7 +173,7 @@ handle_event(_Event, State) -> %% @private handle_info({rotate, File}, #state{name = File, count = Count, date = Date, rotator = Rotator} = State0) -> State1 = close_file(State0), - _ = Rotator:rotate_logfile(File, Count), + _ = Rotator:rotateLogfile(File, Count), schedule_rotation(File, Date), {ok, State1}; handle_info({shaper_expired, Name}, #state{shaper = Shaper, name = Name, formatter = Formatter, formatter_config = FormatConfig} = State) -> @@ -227,12 +227,12 @@ write(#state{name = Name, fd = FD, true -> %% need to check for rotation Buffer = {State0#state.sync_size, State0#state.sync_interval}, - case Rotator:ensure_logfile(Name, FD, Inode, Ctime, Buffer) of + case Rotator:ensureLogfile(Name, FD, Inode, Ctime, Buffer) of {ok, {_FD, _Inode, _Ctime, Size}} when RotSize > 0, Size > RotSize -> State1 = close_file(State0), - case Rotator:rotate_logfile(Name, Count) of + case Rotator:rotateLogfile(Name, Count) of ok -> - %% go around the loop again, we'll do another rotation check and hit the next clause of ensure_logfile + %% go around the loop again, we'll do another rotation check and hit the next clause of ensureLogfile write(State1, Timestamp, Level, Msg); {error, Reason} -> case Flap of @@ -271,7 +271,7 @@ write_should_check(#state{last_check = LastCheck0, check_interval = CheckInterva _ -> % We need to know if the file has changed "out from under lager" so we don't % write to an invalid FD - {Result, _FInfo} = rumUtil:has_file_changed(Name, Inode0, Ctime0), + {Result, _FInfo} = rumUtil:isFileChanged(Name, Inode0, Ctime0), Result end. @@ -501,7 +501,7 @@ rotation_test_() -> % while a process has an open file handle referencing it ok; _ -> - {ok, {FD, Inode, Ctime, _Size}} = Rotator:open_logfile(TestLog, {SyncSize, SyncInterval}), + {ok, {FD, Inode, Ctime, _Size}} = Rotator:openLogfile(TestLog, {SyncSize, SyncInterval}), State0 = DefaultState#state{fd = FD, inode = Inode, ctime = Ctime}, State1 = write(State0, os:timestamp(), ?DEBUG, "hello world"), ?assertMatch(#state{name = TestLog, level = ?DEBUG, fd = FD, inode = Inode, ctime = Ctime}, State1), @@ -528,7 +528,7 @@ rotation_test_() -> RotationSize = 15, PreviousCheck = os:timestamp(), - {ok, {FD, Inode, Ctime, _Size}} = Rotator:open_logfile(TestLog, {SyncSize, SyncInterval}), + {ok, {FD, Inode, Ctime, _Size}} = Rotator:openLogfile(TestLog, {SyncSize, SyncInterval}), State0 = DefaultState#state{ fd = FD, inode = Inode, ctime = Ctime, size = RotationSize, check_interval = CheckInterval, last_check = PreviousCheck}, diff --git a/src/crashLog/rumCrashLog.erl b/src/crashLog/rumCrashLog.erl index 8f44d59..ff05a22 100644 --- a/src/crashLog/rumCrashLog.erl +++ b/src/crashLog/rumCrashLog.erl @@ -80,7 +80,7 @@ handleCast(_Msg, _State) -> kpS. handleInfo(rotate, #state{fileName = Name, count = Count, date = Date, rotator = Rotator}) -> - _ = Rotator:rotate_logfile(Name, Count), + _ = Rotator:rotateLogfile(Name, Count), schedule_rotation(Date), kpS; handleInfo(_Msg, _State) -> @@ -184,9 +184,9 @@ do_log({log, Event}, #state{fileName = Name, fd = FD, inode = Inode, ctime = Cti if ReportStr == ignore -> {ok, State}; true -> - case Rotator:ensure_logfile(Name, FD, Inode, Ctime, false) of + case Rotator:ensureLogfile(Name, FD, Inode, Ctime, false) of {ok, {_FD, _Inode, _Ctime, Size}} when RotSize /= 0, Size > RotSize -> - _ = Rotator:rotate_logfile(Name, Count), + _ = Rotator:rotateLogfile(Name, Count), handle_cast({log, Event}, State); {ok, {NewFD, NewInode, NewCtime, _Size}} -> TimeBinStr = rumUtil:msToBinStr(), diff --git a/src/rotator/rumRotatorExm.erl b/src/rotator/rumRotatorExm.erl index 9808cf2..24c7d49 100644 --- a/src/rotator/rumRotatorExm.erl +++ b/src/rotator/rumRotatorExm.erl @@ -2,17 +2,17 @@ %% Create a log file -callback createLogFile(Name :: list(), Buffer :: {integer(), integer()} | any()) -> - {ok, {file:io_device(), integer(), file:date_time(), integer()}} | + {ok, Fd :: file:io_device(), Inode :: integer(), DateTime :: file:date_time(), FileSize :: integer()} | {error, any()}. %% Open a log file -callback openLogFile(Name :: list(), Buffer :: {integer(), integer()} | any()) -> - {ok, {file:io_device(), integer(), file:date_time(), integer()}} | + {ok, Fd :: file:io_device(), Inode :: integer(), DateTime :: file:date_time(), FileSize :: integer()} | {error, any()}. %% Ensure reference to current target, could be rotated -callback ensureLogFile(Name :: list(), FD :: file:io_device(), Inode :: integer(), CTime :: file:date_time(), Buffer :: {integer(), integer()} | any()) -> - {ok, {file:io_device(), integer(), file:date_time(), integer()}} | + {ok, Fd :: file:io_device(), Inode :: integer(), DateTime :: file:date_time(), FileSize :: integer()} | {error, any()}. %% Rotate the log file diff --git a/src/rotator/rumRotatorIns.erl b/src/rotator/rumRotatorIns.erl index 00cb017..02ad417 100644 --- a/src/rotator/rumRotatorIns.erl +++ b/src/rotator/rumRotatorIns.erl @@ -15,66 +15,62 @@ -include_lib("eunit/include/eunit.hrl"). -endif. -createLogFile(Name, Buffer) -> - openLogFile(Name, Buffer). +createLogFile(FileName, Buffer) -> + openLogFile(FileName, Buffer). -openLogFile(Name, Buffer) -> - case filelib:ensure_dir(Name) of +openLogFile(FileName, Buffer) -> + case filelib:ensure_dir(FileName) of ok -> - Options = [append, raw] ++ + Options = case Buffer of - {Size0, Interval} when is_integer(Interval), Interval >= 0, is_integer(Size0), Size0 >= 0 -> - [{delayed_write, Size0, Interval}]; - _ -> [] + {Size, Interval} -> + [append, raw, {delayed_write, Size, Interval}]; + _ -> + [append, raw] end, - case file:open(Name, Options) of - {ok, FD} -> - case file:read_file_info(Name, [raw]) of - {ok, FInfo0} -> - Inode = FInfo0#file_info.inode, - {ok, CTime} = maybe_update_ctime(Name, FInfo0), - Size1 = FInfo0#file_info.size, - {ok, {FD, Inode, CTime, Size1}}; - X -> X + case file:open(FileName, Options) of + {ok, Fd} -> + case file:read_file_info(FileName, [raw]) of + {ok, FileInfo} -> + #file_info{size = FileSize, inode = Inode} = FileInfo, + CTime = tryUpdateCTime(FileName, FileInfo), + {ok, Fd, Inode, CTime, FileSize}; + RfiErr -> RfiErr end; - Y -> Y + OpErr -> OpErr end; - Z -> Z + EnDirErr -> EnDirErr end. -ensureLogFile(Name, undefined, _Inode, _CTime, Buffer) -> - openLogFile(Name, Buffer); -ensureLogFile(Name, FD, Inode0, CTime0, Buffer) -> - case rumUtil:has_file_changed(Name, Inode0, CTime0) of - {true, _FInfo} -> - reopen_logfile(Name, FD, Buffer); - {_, FInfo} -> - {ok, {FD, Inode0, CTime0, FInfo#file_info.size}} +ensureLogFile(FileName, Fd, Inode, CTime, Buffer) -> + case Fd of + undefined -> + openLogFile(FileName, Buffer); + _ -> + case rumUtil:isFileChanged(FileName, Inode, CTime) of + {true, _FInfo} -> + reopenLogfile(FileName, Fd, Buffer); + {_, FInfo} -> + {ok, Fd, Inode, CTime, FInfo#file_info.size} + end end. -reopen_logfile(Name, FD0, Buffer) -> - %% Flush and close any file handles. - %% delayed write can cause file:close not to do a close - _ = file:datasync(FD0), - _ = file:close(FD0), - _ = file:close(FD0), - case openLogFile(Name, Buffer) of - {ok, {_FD1, _Inode, _Size, _CTime} = FileInfo} -> - %% inode changed, file was probably moved and - %% recreated - {ok, FileInfo}; - Error -> - Error - end. +reopenLogfile(FileName, Fd, Buffer) -> + %% Flush and close any file handles. delayed write can cause file:close not to do a close + _ = file:datasync(Fd), + _ = file:close(Fd), + _ = file:close(Fd), + openLogFile(FileName, Buffer). %% renames failing are OK +%% IMY-TODO 文件名格式修改调整 rotateLogFile(File, 0) -> %% open the file in write-only mode to truncate/create it case file:open(File, [write]) of {ok, FD} -> _ = file:close(FD), _ = file:close(FD), - {ok, _CTime} = maybe_update_ctime(File), + tryUpdateCTime(File), ok; Error -> Error @@ -89,28 +85,25 @@ rotateLogFile(File0, Count) -> _ = file:rename(File1, File2), rotateLogFile(File0, Count - 1). -maybe_update_ctime(Name) -> +tryUpdateCTime(Name) -> case file:read_file_info(Name, [raw]) of {ok, FInfo} -> - maybe_update_ctime(Name, FInfo); + tryUpdateCTime(Name, FInfo); _ -> - {ok, calendar:local_time()} + erlang:localtime() end. -maybe_update_ctime(Name, FInfo) -> - {OsType, _} = os:type(), - do_update_ctime(OsType, Name, FInfo). - -do_update_ctime(win32, Name, FInfo0) -> - % Note: we force the creation time to be the current time. - % On win32 this may prevent the ctime from being updated: - % https://stackoverflow.com/q/8804342/1466825 - NewCtime = calendar:local_time(), - FInfo1 = FInfo0#file_info{ctime = NewCtime}, - ok = file:write_file_info(Name, FInfo1, [raw]), - {ok, NewCtime}; -do_update_ctime(_, _Name, FInfo) -> - {ok, FInfo#file_info.ctime}. +tryUpdateCTime(Name, FileInfo) -> + case os:type() of + {win32, _} -> + %注意:我们将创建时间强制为当前时间。在win32上,这可能会阻止ctime的更新:https://stackoverflow.com/q/8804342/1466825 + NewCtime = erlang:localtime(), + NewFileInfo = FileInfo#file_info{ctime = NewCtime}, + ok = file:write_file_info(Name, NewFileInfo, [raw]), + NewCtime; + _ -> + element(#file_info.ctime, FileInfo) + end. -ifdef(TEST). diff --git a/src/utils/rumUtil.erl b/src/utils/rumUtil.erl index ad0ea75..c642650 100644 --- a/src/utils/rumUtil.erl +++ b/src/utils/rumUtil.erl @@ -28,7 +28,7 @@ , makeInnerSinkName/1 , otp_version/0 , maybe_flush/2 - , has_file_changed/3 + , isFileChanged/3 , get_env/2 ]). @@ -616,23 +616,18 @@ maybe_flush(undefined, #rumShaper{} = S) -> maybe_flush(Flag, #rumShaper{} = S) when is_boolean(Flag) -> S#rumShaper{flushQueue = Flag}. --spec has_file_changed(Name :: file:name_all(), - Inode0 :: pos_integer(), - Ctime0 :: file:date_time()) -> {boolean(), file:file_info() | undefined}. -has_file_changed(Name, Inode0, Ctime0) -> - {OsType, _} = os:type(), - F = file:read_file_info(Name, [raw]), - case {OsType, F} of - {win32, {ok, #file_info{ctime = Ctime1} = FInfo}} -> - % Note: on win32, Inode is always zero - % So check the file's ctime to see if it - % needs to be re-opened - Changed = Ctime0 =/= Ctime1, - {Changed, FInfo}; - {_, {ok, #file_info{inode = Inode1} = FInfo}} -> - Changed = Inode0 =/= Inode1, - {Changed, FInfo}; - {_, _} -> +-spec isFileChanged(FileName :: file:name_all(), Inode :: pos_integer(), Ctime :: file:date_time()) -> {boolean(), file:file_info() | undefined}. +isFileChanged(FileName, Inode, Ctime) -> + case file:read_file_info(FileName, [raw]) of + {ok, FileInfo} -> + case os:type() of + {win32, _} -> + % Note: on win32, Inode is always zero So check the file's ctime to see if it needs to be re-opened + {Ctime =/= FileInfo#file_info.ctime, FileInfo}; + _ -> + {Inode =/= FileInfo#file_info.inode, FileInfo} + end; + _ -> {true, undefined} end.