Browse Source

Add simple size based log rotation

pull/1/head
Andrew Thompson 13 years ago
parent
commit
5c11c70a31
6 changed files with 73 additions and 22 deletions
  1. +2
    -2
      src/lager.app.src
  2. +2
    -1
      src/lager_app.erl
  3. +2
    -2
      src/lager_crash_log.erl
  4. +47
    -8
      src/lager_file_backend.erl
  5. +3
    -2
      src/lager_handler_watcher.erl
  6. +17
    -7
      src/lager_util.erl

+ 2
- 2
src/lager.app.src View File

@ -16,8 +16,8 @@
{handlers, [
{lager_console_backend, info},
{lager_file_backend, [
{"log/error.log", error},
{"log/console.log", info}
{"log/error.log", error, 10485760, "", 5},
{"log/console.log", info, 10485760, "", 5}
]}
]},
%% Whether to write a crash log, and where. Undefined means no crash logger.

+ 2
- 1
src/lager_app.erl View File

@ -34,7 +34,8 @@ start(_StartType, _StartArgs) ->
Handlers = case application:get_env(lager, handlers) of
undefined ->
[{lager_console_backend, info},
{lager_file_backend, [{"log/error.log", error}, {"log/console.log", info}]}];
{lager_file_backend, [{"log/error.log", error, 10485760, "", 5},
{"log/console.log", info, 10485760, "", 5}]}];
{ok, Val} ->
Val
end,

+ 2
- 2
src/lager_crash_log.erl View File

@ -51,7 +51,7 @@ start(Filename, MaxBytes) ->
%% @private
init([Filename, MaxBytes]) ->
case lager_util:open_logfile(Filename, false) of
{ok, {FD, Inode}} ->
{ok, {FD, Inode, _}} ->
{ok, #state{name=Filename, fd=FD, inode=Inode, fmtmaxbytes=MaxBytes}};
Error ->
Error
@ -78,7 +78,7 @@ handle_cast({log, Event}, #state{name=Name, fd=FD, inode=Inode, flap=Flap, fmtma
{noreply, State};
true ->
case lager_util:ensure_logfile(Name, FD, Inode, false) of
{ok, {NewFD, NewInode}} ->
{ok, {NewFD, NewInode, _Size}} ->
{Date, TS} = lager_util:format_time(lager_stdlib:maybe_utc(erlang:localtime())),
Time = [Date, " ", TS," =", ReportStr, "====\n"],
NodeSuffix = other_node_suffix(Pid),

+ 47
- 8
src/lager_file_backend.erl View File

@ -42,7 +42,10 @@
level :: integer(),
fd :: file:io_device(),
inode :: integer(),
flap=false :: boolean()
flap=false :: boolean(),
size = 0 :: integer(),
date,
count = 10
}).
%% @private
@ -50,15 +53,17 @@
init(LogFiles) ->
Files = [begin
case lager_util:open_logfile(Name, true) of
{ok, {FD, Inode}} ->
#file{name=Name, level=lager_util:level_to_num(Level), fd=FD, inode=Inode};
{ok, {FD, Inode, _}} ->
#file{name=Name, level=lager_util:level_to_num(Level), fd=FD,
inode=Inode, size=Size, date=Date, count=Count};
{error, Reason} ->
?INT_LOG(error, "Failed to open log file ~s with error ~s",
[Name, file:format_error(Reason)]),
#file{name=Name, level=lager_util:level_to_num(Level), flap=true}
#file{name=Name, level=lager_util:level_to_num(Level),
flap=true, size=Size, date=Date, count=Count}
end
end ||
{Name, Level} <- LogFiles],
{Name, Level, Size, Date, Count} <- validate_logfiles(LogFiles)],
{ok, #state{files=Files}}.
%% @private
@ -114,9 +119,13 @@ terminate(_Reason, State) ->
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
write(#file{name=Name, fd=FD, inode=Inode, flap=Flap} = File, Level, Msg) ->
write(#file{name=Name, fd=FD, inode=Inode, flap=Flap, size=RotSize,
count=Count} = File, Level, Msg) ->
case lager_util:ensure_logfile(Name, FD, Inode, true) of
{ok, {NewFD, NewInode}} ->
{ok, {_, _, Size}} when RotSize /= 0, Size > RotSize ->
lager_util:rotate_logfile(Name, Count),
write(File, Level, Msg);
{ok, {NewFD, NewInode, _}} ->
file:write(NewFD, Msg),
case Level of
_ when Level =< ?ERROR ->
@ -144,6 +153,36 @@ write(#file{name=Name, fd=FD, inode=Inode, flap=Flap} = File, Level, Msg) ->
end
end.
validate_logfiles([]) ->
[];
validate_logfiles([{Name, Level, Size, Date, Count}|T]) ->
case lists:member(Level, ?LEVELS) of
true ->
case (is_integer(Size) andalso Size >= 0) of
true ->
case (is_integer(Count) andalso Count >= 0) of
true ->
[{Name, Level, Size, Date,
Count}|validate_logfiles(T)];
_ ->
?INT_LOG(error, "Invalid rotation count of ~p for ~s.",
[Name, Count]),
validate_logfiles(T)
end;
_ ->
?INT_LOG(error, "Invalid rotation size of ~p for ~s.",
[Name, Size]),
validate_logfiles(T)
end;
_ ->
?INT_LOG(error, "Invalid log level of ~p for ~s.",
[Name, Level]),
validate_logfiles(T)
end;
validate_logfiles([H|T]) ->
?INT_LOG(error, "Invalid logfile config ~p.", [H]),
validate_logfiles(T).
-ifdef(TEST).
get_loglevel_test() ->
@ -160,7 +199,7 @@ get_loglevel_test() ->
?assertEqual(Level2, lager_util:level_to_num(warning)).
rotation_test() ->
{ok, {FD, Inode}} = lager_util:open_logfile("test.log", true),
{ok, {FD, Inode, _}} = lager_util:open_logfile("test.log", true),
?assertMatch(#file{name="test.log", level=?DEBUG, fd=FD, inode=Inode},
write(#file{name="test.log", level=?DEBUG, fd=FD, inode=Inode}, 0, "hello world")),
file:delete("test.log"),

+ 3
- 2
src/lager_handler_watcher.erl View File

@ -80,9 +80,10 @@ install_handler(Event, Module, Config) ->
ok ->
lager:log(debug, self(), "Lager installed handler ~p into ~p", [Module, Event]),
ok;
_ ->
Error ->
%% try to reinstall it later
lager:log(error, self(), "Lager failed to install handler ~p into ~p, retrying later", [Module, Event]),
lager:log(error, self(), "Lager failed to install handler ~p into"
" ~p, retrying later : ~p", [Module, Event, Error]),
erlang:send_after(5000, self(), reinstall_handler)
end.

+ 17
- 7
src/lager_util.erl View File

@ -19,7 +19,7 @@
-include_lib("kernel/include/file.hrl").
-export([levels/0, level_to_num/1, num_to_level/1, open_logfile/2,
ensure_logfile/4, format_time/0, format_time/1, localtime_ms/0, maybe_utc/1]).
ensure_logfile/4, rotate_logfile/2, format_time/0, format_time/1, localtime_ms/0, maybe_utc/1]).
levels() ->
[debug, info, notice, warning, error, critical, alert, emergency].
@ -54,7 +54,7 @@ open_logfile(Name, Buffer) ->
case file:read_file_info(Name) of
{ok, FInfo} ->
Inode = FInfo#file_info.inode,
{ok, {FD, Inode}};
{ok, {FD, Inode, FInfo#file_info.size}};
X -> X
end;
Y -> Y
@ -68,16 +68,16 @@ ensure_logfile(Name, FD, Inode, Buffer) ->
Inode2 = FInfo#file_info.inode,
case Inode == Inode2 of
true ->
{ok, {FD, Inode}};
{ok, {FD, Inode, FInfo#file_info.size}};
false ->
%% delayed write can cause file:close not to do a close
file:close(FD),
file:close(FD),
case open_logfile(Name, Buffer) of
{ok, {FD2, Inode3}} ->
{ok, {FD2, Inode3, Size}} ->
%% inode changed, file was probably moved and
%% recreated
{ok, {FD2, Inode3}};
{ok, {FD2, Inode3, Size}};
Error ->
Error
end
@ -87,9 +87,9 @@ ensure_logfile(Name, FD, Inode, Buffer) ->
file:close(FD),
file:close(FD),
case open_logfile(Name, Buffer) of
{ok, {FD2, Inode3}} ->
{ok, {FD2, Inode3, Size}} ->
%% file was removed
{ok, {FD2, Inode3}};
{ok, {FD2, Inode3, Size}};
Error ->
Error
end
@ -109,6 +109,16 @@ maybe_utc({Date, {H, M, S, Ms}}) ->
{Date1, {H1, M1, S1, Ms}}
end.
rotate_logfile(File, 0) ->
file:delete(File);
rotate_logfile(File, 1) ->
file:rename(File, File++".0"),
rotate_logfile(File, 0);
rotate_logfile(File, Count) ->
file:rename(File ++ "." ++ integer_to_list(Count - 2), File ++ "." ++
integer_to_list(Count - 1)),
rotate_logfile(File, Count - 1).
format_time() ->
format_time(maybe_utc(localtime_ms())).

Loading…
Cancel
Save