summaryrefslogtreecommitdiff
path: root/lib/kernel/src/disk_log_1.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel/src/disk_log_1.erl')
-rw-r--r--lib/kernel/src/disk_log_1.erl300
1 files changed, 251 insertions, 49 deletions
diff --git a/lib/kernel/src/disk_log_1.erl b/lib/kernel/src/disk_log_1.erl
index a488a44dcc..5f585a4c39 100644
--- a/lib/kernel/src/disk_log_1.erl
+++ b/lib/kernel/src/disk_log_1.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2021. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2023. 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.
@@ -23,10 +23,14 @@
-export([int_open/4, ext_open/4, logl/1, close/3, truncate/3, chunk/5,
sync/2, write_cache/2]).
--export([mf_int_open/7, mf_int_log/3, mf_int_close/2, mf_int_inc/2,
- mf_ext_inc/2, mf_int_chunk/4, mf_int_chunk_step/3,
+-export([mf_int_open/7, mf_int_log/3, mf_int_close/2, mf_int_inc/2,
+ mf_ext_inc/2, mf_int_chunk/4, mf_int_chunk_step/3,
mf_sync/1, mf_write_cache/1]).
-export([mf_ext_open/7, mf_ext_log/3, mf_ext_close/2]).
+-export([open_rotate_log_file/3, do_rotate/2,
+ rotate_ext_log/3, rotate_write_cache/1, rotate_ext_close/1,
+ change_size_rotate/2, get_rotate_size/1,
+ rotate_files/2]).
-export([print_index_file/1]).
-export([read_index_file/1]).
@@ -38,6 +42,7 @@
-export([is_head/1]).
-export([position/3, truncate_at/3, fwrite/4, fclose/2]).
-export([set_quiet/1, is_quiet/0]).
+-export([remove_files/4]).
-compile({inline,[{scan_f2,7}]}).
@@ -455,7 +460,7 @@ new_ext_file(FName, Head) ->
%% -> {FdC, {NoItemsWritten, NoBytesWritten}} | throw(Error)
ext_log_head(Fd, Head) ->
case lh(Head, external) of
- {ok, BinHead} ->
+ {ok, BinHead} ->
Size = byte_size(BinHead),
{ok, FdC} = fwrite_header(Fd, BinHead, Size),
{FdC, {1, Size}};
@@ -678,7 +683,7 @@ mf_int_open(FName, MaxB, MaxF, Repair, Mode, Head, Version) ->
end.
%% -> {ok, handle(), Lost} | {error, Error, handle()}
-mf_int_inc(Handle, Head) ->
+mf_int_inc(Handle, Head) ->
#handle{filename = FName, cur_cnt = CurCnt, acc_cnt = AccCnt,
cur_name = FileName, curF = CurF, maxF = MaxF,
cur_fdc = CurFdC, noFull = NoFull} = Handle,
@@ -732,7 +737,7 @@ mf_int_log(Handle, Bins, Head, No0, Wraps) ->
noFull = NoFull + 1},
case catch close(CurFdC, FileName, read_write) of
ok ->
- mf_int_log(Handle1, Bins, Head, No0 + Nh,
+ mf_int_log(Handle1, Bins, Head, No0 + Nh,
[Lost | Wraps]);
Error ->
Lost1 = Lost + sum(Wraps),
@@ -845,7 +850,10 @@ mf_write_cache(#handle{filename = FName, cur_fdc = FdC} = Handle) ->
%% -> {Reply, handle()}; Reply = ok | Error
mf_sync(#handle{filename = FName, cur_fdc = FdC} = Handle) ->
{Reply, NewFdC} = fsync(FdC, FName),
- {Reply, Handle#handle{cur_fdc = NewFdC}}.
+ {Reply, Handle#handle{cur_fdc = NewFdC}};
+mf_sync(#rotate_handle{file = FName, cur_fdc = FdC} = Handle) ->
+ {Reply, NewFdC} = fsync(FdC, FName),
+ {Reply, Handle#rotate_handle{cur_fdc = NewFdC}}.
%% -> ok | throw(FileError)
mf_int_close(#handle{filename = FName, curF = CurF, cur_name = FileName,
@@ -855,7 +863,7 @@ mf_int_close(#handle{filename = FName, curF = CurF, cur_name = FileName,
ok.
%% -> {ok, handle(), Cnt} | throw(FileError)
-mf_ext_open(FName, MaxB, MaxF, Repair, Mode, Head, Version) ->
+mf_ext_open(FName, MaxB, MaxF, Repair, Mode, Head, Version) ->
{First, Sz, TotSz, NFiles} = read_index_file(Repair, FName, MaxF),
write_size_file(Mode, FName, MaxB, MaxF, Version),
NewMaxF = if
@@ -865,37 +873,37 @@ mf_ext_open(FName, MaxB, MaxF, Repair, Mode, Head, Version) ->
MaxF
end,
{ok, FdC, FileName, Lost, {NoItems, NoBytes}, CurB} =
- ext_file_open(FName, First, 0, 0, Head, Repair, Mode),
+ ext_file_open(FName, First, 0, 0, Head, Repair, Mode),
CurCnt = Sz + NoItems - Lost,
{ok, #handle{filename = FName, maxB = MaxB, cur_name = FileName,
- maxF = NewMaxF, cur_cnt = CurCnt, acc_cnt = -Sz,
- curF = First, cur_fdc = FdC, firstPos = NoBytes,
- curB = CurB, noFull = 0, accFull = 0},
+ maxF = NewMaxF, cur_cnt = CurCnt, acc_cnt = -Sz,
+ curF = First, cur_fdc = FdC, firstPos = NoBytes,
+ curB = CurB, noFull = 0, accFull = 0},
TotSz + CurCnt}.
%% -> {ok, handle(), Lost}
%% | {error, Error, handle()}
%% | throw(FatalError)
%% Fatal errors should always terminate the log.
-mf_ext_inc(Handle, Head) ->
- #handle{filename = FName, cur_cnt = CurCnt, cur_name = FileName,
- acc_cnt = AccCnt, curF = CurF, maxF = MaxF, cur_fdc = CurFdC,
- noFull = NoFull} = Handle,
+mf_ext_inc(Handle, Head) ->
+ #handle{filename = FName, cur_cnt = CurCnt, cur_name = FileName,
+ acc_cnt = AccCnt, curF = CurF, maxF = MaxF, cur_fdc = CurFdC,
+ noFull = NoFull} = Handle,
case catch wrap_ext_log(FName, CurF, MaxF, CurCnt, Head) of
- {NewF, NewMaxF, NewFdC, NewFileName, Nh, FirstPos, Lost} ->
- Handle1 = Handle#handle{cur_fdc = NewFdC, curF = NewF,
- cur_name = NewFileName,
- cur_cnt = Nh, acc_cnt = AccCnt + CurCnt,
- maxF = NewMaxF, firstPos = FirstPos,
- curB = FirstPos, noFull = NoFull + 1},
- case catch fclose(CurFdC, FileName) of
- ok ->
- {ok, Handle1, Lost};
- Error -> % Error in the last file, new file opened.
- {error, Error, Handle1}
- end;
- Error ->
- {error, Error, Handle}
+ {NewF, NewMaxF, NewFdC, NewFileName, Nh, FirstPos, Lost} ->
+ Handle1 = Handle#handle{cur_fdc = NewFdC, curF = NewF,
+ cur_name = NewFileName,
+ cur_cnt = Nh, acc_cnt = AccCnt + CurCnt,
+ maxF = NewMaxF, firstPos = FirstPos,
+ curB = FirstPos, noFull = NoFull + 1},
+ case catch fclose(CurFdC, FileName) of
+ ok ->
+ {ok, Handle1, Lost};
+ Error -> % Error in the last file, new file opened.
+ {error, Error, Handle1}
+ end;
+ Error ->
+ {error, Error, Handle}
end.
%% -> {ok, handle(), Logged, Lost, NoWraps} | {ok, handle(), Logged}
@@ -968,18 +976,201 @@ mf_ext_close(#handle{filename = FName, curF = CurF,
Res.
%% -> {ok, handle()} | throw(FileError)
-change_size_wrap(Handle, {NewMaxB, NewMaxF}, Version) ->
- FName = Handle#handle.filename,
+change_size_wrap(#handle{filename = FName} = Handle, {NewMaxB, NewMaxF}, Version) ->
{_MaxB, MaxF} = get_wrap_size(Handle),
write_size_file(read_write, FName, NewMaxB, NewMaxF, Version),
if
- NewMaxF > MaxF ->
- remove_files(FName, MaxF + 1, NewMaxF),
- {ok, Handle#handle{maxB = NewMaxB, maxF = NewMaxF}};
- NewMaxF < MaxF ->
- {ok, Handle#handle{maxB = NewMaxB, maxF = {NewMaxF, MaxF}}};
- true ->
- {ok, Handle#handle{maxB = NewMaxB, maxF = NewMaxF}}
+ NewMaxF > MaxF ->
+ remove_files(wrap, FName, MaxF + 1, NewMaxF),
+ {ok, Handle#handle{maxB = NewMaxB, maxF = NewMaxF}};
+ NewMaxF < MaxF ->
+ {ok, Handle#handle{maxB = NewMaxB, maxF = {NewMaxF, MaxF}}};
+ true ->
+ {ok, Handle#handle{maxB = NewMaxB, maxF = NewMaxF}}
+ end.
+
+change_size_rotate(#rotate_handle{maxB = MaxB, maxF = MaxF, file = FName} = Handle, {NewMaxB, NewMaxF}) ->
+ {MaxB, MaxF1} = get_size(MaxB, MaxF),
+ if
+ NewMaxF > MaxF1 ->
+ remove_files(rotate, FName, MaxF1, NewMaxF),
+ {ok, Handle#rotate_handle{maxB = NewMaxB, maxF = NewMaxF}};
+ NewMaxF < MaxF1 ->
+ remove_files(rotate, FName, NewMaxF, MaxF1),
+ {ok, Handle#rotate_handle{maxB = NewMaxB, maxF = NewMaxF}};
+ true ->
+ {ok, Handle#rotate_handle{maxB = NewMaxB, maxF = NewMaxF}}
+ end.
+
+open_rotate_log_file(FileName, Size, Head) ->
+ try
+ case filelib:ensure_dir(FileName) of
+ ok ->
+ case file:open(FileName, [raw, binary, read, append]) of
+ {ok, Fd} ->
+ {FdC1, _HeadSize} = ext_log_head(Fd, Head),
+ {FdC, FileSize} = position_close(FdC1, FileName, cur),
+ {ok,#file_info{inode=INode}} =
+ file:read_file_info(FileName,[raw]),
+ {MaxB, MaxF} = Size,
+ RotHandle = #rotate_handle{file = FileName,
+ cur_fdc = FdC,
+ maxB = MaxB,
+ maxF = MaxF,
+ curB = FileSize,
+ firstPos = FileSize,
+ inode = INode},
+ update_rotation(RotHandle),
+ {ok, RotHandle};
+ Error ->
+ Error
+ end;
+ Error ->
+ Error
+ end
+ catch
+ _:Reason -> {error,Reason}
+ end.
+
+update_rotation(#rotate_handle{file = FName, maxF = MaxF}) ->
+ maybe_remove_archives(MaxF, FName),
+ maybe_update_compress(0, MaxF,FName).
+
+maybe_remove_archives(Count, FName) ->
+ Archive = rotate_file_name(FName, Count),
+ case file:read_file_info(Archive,[raw]) of
+ {error,enoent} ->
+ ok;
+ _ ->
+ _ = file:delete(Archive),
+ maybe_remove_archives(Count+1, FName)
+ end.
+
+maybe_update_compress(MaxF, MaxF, _FName) ->
+ ok;
+maybe_update_compress(N, MaxF, FName) ->
+ FileName = add_ext(FName, N),
+ case file:read_file_info(FileName,[raw]) of
+ {ok,_} ->
+ compress_file(FileName);
+ _ ->
+ ok
+ end,
+ maybe_update_compress(N+1, MaxF, FName).
+
+do_rotate(#rotate_handle{file = FName, maxF = MaxF, cur_fdc = FdC} = RotHandle, Head) ->
+ #cache{fd = Fd, c = C} = FdC,
+ {_, _C1} = write_cache(Fd, FName, C),
+ _ = delayed_write_close(Fd),
+ rotate_files(FName, MaxF),
+ {ok, NewFdC, FileSize} = ensure_open(FName, Head),
+ {ok,#file_info{inode=INode}} = file:read_file_info(FName,[raw]),
+ RotHandle#rotate_handle{cur_fdc = NewFdC, inode = INode, curB = FileSize, firstPos = FileSize}.
+
+rotate_files(FileName,0) ->
+ _ = file:delete(FileName),
+ ok;
+rotate_files(FileName, 1) ->
+ FileName0 = FileName ++".0",
+ Rename = file:rename(FileName, FileName0),
+ %% Rename may fail if file has been deleted. If it has, then
+ %% we do not need to compress it...
+ if Rename =:= ok -> compress_file(FileName0);
+ true -> ok
+ end,
+ ok;
+rotate_files(FileName, Count) ->
+ _ = file:rename(rotate_file_name(FileName, Count-2), rotate_file_name(FileName, Count-1)),
+ rotate_files(FileName, Count-1).
+
+rotate_file_name(FileName, Count) ->
+ concat([FileName, ".", Count, ".gz"]).
+
+compress_file(FileName) ->
+ {ok,In} = file:open(FileName,[read,binary]),
+ {ok,Out} = file:open(FileName++".gz",[write]),
+ Z = zlib:open(),
+ zlib:deflateInit(Z, default, deflated, 31, 8, default),
+ compress_data(Z,In,Out),
+ zlib:deflateEnd(Z),
+ zlib:close(Z),
+ _ = file:close(In),
+ _ = file:close(Out),
+ _ = file:delete(FileName),
+ ok.
+
+compress_data(Z,In,Out) ->
+ case file:read(In,100000) of
+ {ok,Data} ->
+ Compressed = zlib:deflate(Z, Data),
+ _ = file:write(Out,Compressed),
+ compress_data(Z,In,Out);
+ eof ->
+ Compressed = zlib:deflate(Z, <<>>, finish),
+ _ = file:write(Out,Compressed),
+ ok
+ end.
+
+rotate_ext_log(Handle, Bin, Head) ->
+ rotate_ext_log(Handle, Bin, Head, 0).
+
+rotate_ext_log(Handle, [], _Head, N) ->
+ {ok, Handle, N};
+rotate_ext_log(Handle, Bins, Head, N0) ->
+ #rotate_handle{file = FileName, maxB = MaxB, cur_fdc = CurFdC,
+ curB = CurB, firstPos = FirstPos} = Handle,
+ {FirstBins, LastBins, NoBytes, N} =
+ ext_split_bins(CurB, MaxB, FirstPos, Bins),
+ case FirstBins of
+ [] ->
+ Handle1 = do_rotate(Handle, Head),
+ rotate_ext_log(Handle1, Bins, Head, N0);
+ _ ->
+ case fwrite(CurFdC, FileName, FirstBins, NoBytes) of
+ {ok, NewCurFdC} ->
+ Handle1 = Handle#rotate_handle{cur_fdc = NewCurFdC,
+ curB = CurB + NoBytes},
+ rotate_ext_log(Handle1, LastBins, Head, N0 + N);
+ {Error, NewCurFdC} ->
+ Handle1 = Handle#rotate_handle{cur_fdc = NewCurFdC},
+ {error, Error, Handle1}
+ end
+ end.
+
+%% -> {Reply, handle()}; Reply = ok | Error
+rotate_write_cache(#rotate_handle{file = FName, cur_fdc = FdC} = Handle) ->
+ erase(write_cache_timer_is_running),
+ #cache{fd = Fd, c = C} = FdC,
+ {Reply, NewFdC} = write_cache(Fd, FName, C),
+ {Reply, Handle#rotate_handle{cur_fdc = NewFdC}}.
+
+rotate_ext_close(#rotate_handle{file = FName, cur_fdc = CurFdC}) ->
+ (catch fclose(CurFdC, FName)).
+
+ensure_open(Filename, Head) ->
+ case filelib:ensure_dir(Filename) of
+ ok ->
+ case open_update(Filename) of
+ {ok, Fd} ->
+ {FdC1, _HeadSize} = ext_log_head(Fd, Head),
+ {FdC, FileSize} = position_close(FdC1, Filename, cur),
+ {ok, FdC, FileSize};
+ Error ->
+ exit({could_not_reopen_file,Error})
+ end;
+ Error ->
+ exit({could_not_create_dir_for_file,Error})
+ end.
+
+%% A special close that closes the FD properly when the delayed write close failed
+delayed_write_close(Fd) ->
+ case file:close(Fd) of
+ %% We got an error while closing, could be a delayed write failing
+ %% So we close again in order to make sure the file is closed.
+ {error, _} ->
+ file:close(Fd);
+ Res ->
+ Res
end.
%%-----------------------------------------------------------------
@@ -1037,7 +1228,7 @@ ext_file_open(FName, NewFile, OldFile, OldCnt, Head, Repair, Mode) ->
-define(index_file_name(F), add_ext(F, "idx")).
read_index_file(truncate, FName, MaxF) ->
- remove_files(FName, 2, MaxF),
+ remove_files(wrap, FName, 2, MaxF),
_ = file:delete(?index_file_name(FName)),
{1, 0, 0, 0};
read_index_file(_, FName, _MaxF) ->
@@ -1139,7 +1330,7 @@ write_index_file(read_write, FName, NewFile, OldFile, OldCnt) ->
_ = file:close(Fd),
case R of
{ok, <<Lost:SzSz/unit:8>>} -> Lost;
- {ok, _} ->
+ {ok, _} ->
throw({error, {invalid_index_file, FileName}});
eof -> 0;
Error2 -> file_error(FileName, Error2)
@@ -1342,7 +1533,7 @@ inc_wrap(FName, CurF, MaxF) ->
if
CurF >= NewMaxF ->
%% We are at or above the new number of files
- remove_files(FName, CurF + 1, OldMaxF),
+ remove_files(wrap, FName, CurF + 1, OldMaxF),
if
CurF > NewMaxF ->
%% The change was done while the current file was
@@ -1389,24 +1580,34 @@ file_size(Fname) ->
%% -> ok | throw(FileError)
%% Tries to remove each file with name FName.I, N<=I<=Max.
-remove_files(FName, N, Max) ->
- remove_files(FName, N, Max, ok).
+remove_files(Type, FName, N, Max) ->
+ remove_files(Type, FName, N, Max, ok).
-remove_files(_FName, N, Max, ok) when N > Max ->
+remove_files(_Type, _FName, N, Max, ok) when N > Max ->
ok;
-remove_files(_FName, N, Max, {FileName, Error}) when N > Max ->
+remove_files(_Type, _FName, N, Max, {FileName, Error}) when N > Max ->
file_error(FileName, Error);
-remove_files(FName, N, Max, Reply) ->
- FileName = add_ext(FName, N),
+remove_files(Type, FName, N, Max, Reply) ->
+ FileName =
+ case Type of
+ wrap -> add_ext(FName, N);
+ rotate -> rotate_file_name(FName, N)
+ end,
NewReply = case file:delete(FileName) of
ok -> Reply;
{error, enoent} -> Reply;
Error -> {FileName, Error}
end,
- remove_files(FName, N + 1, Max, NewReply).
+ remove_files(Type, FName, N + 1, Max, NewReply).
%% -> {MaxBytes, MaxFiles}
get_wrap_size(#handle{maxB = MaxB, maxF = MaxF}) ->
+ get_size(MaxB, MaxF).
+
+get_rotate_size(#rotate_handle{maxB = MaxB, maxF = MaxF}) ->
+ get_size(MaxB, MaxF).
+
+get_size(MaxB, MaxF) ->
case MaxF of
{NewMaxF,_} -> {MaxB, NewMaxF};
MaxF -> {MaxB, MaxF}
@@ -1586,6 +1787,7 @@ write_cache_close(Fd, FileName, C) ->
-spec file_error(file:filename(), {'error', file:posix()}) -> no_return().
+
file_error(FileName, {error, Error}) ->
throw({error, {file_error, FileName, Error}}).