From 5c11c70a3176b7f787c9e5efdd56f2070f3cd958 Mon Sep 17 00:00:00 2001 From: Andrew Thompson Date: Tue, 19 Jul 2011 11:24:20 -0400 Subject: [PATCH] Add simple size based log rotation --- src/lager.app.src | 4 +-- src/lager_app.erl | 3 +- src/lager_crash_log.erl | 4 +-- src/lager_file_backend.erl | 55 ++++++++++++++++++++++++++++++----- src/lager_handler_watcher.erl | 5 ++-- src/lager_util.erl | 24 ++++++++++----- 6 files changed, 73 insertions(+), 22 deletions(-) diff --git a/src/lager.app.src b/src/lager.app.src index c3f94bc..b7f3b21 100644 --- a/src/lager.app.src +++ b/src/lager.app.src @@ -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. diff --git a/src/lager_app.erl b/src/lager_app.erl index 3b75c0b..7e1a8ee 100644 --- a/src/lager_app.erl +++ b/src/lager_app.erl @@ -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, diff --git a/src/lager_crash_log.erl b/src/lager_crash_log.erl index 4645149..1e6c089 100644 --- a/src/lager_crash_log.erl +++ b/src/lager_crash_log.erl @@ -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), diff --git a/src/lager_file_backend.erl b/src/lager_file_backend.erl index c1e2543..e9f9a1e 100644 --- a/src/lager_file_backend.erl +++ b/src/lager_file_backend.erl @@ -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"), diff --git a/src/lager_handler_watcher.erl b/src/lager_handler_watcher.erl index 20167bd..7c00e61 100644 --- a/src/lager_handler_watcher.erl +++ b/src/lager_handler_watcher.erl @@ -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. diff --git a/src/lager_util.erl b/src/lager_util.erl index 404bd2f..eea8aea 100644 --- a/src/lager_util.erl +++ b/src/lager_util.erl @@ -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())).