浏览代码

Added Hourly Rotation Option, Added Rotator Option

pull/420/head
Wilson Li 7 年前
父节点
当前提交
b3fc8fd785
共有 2 个文件被更改,包括 71 次插入20 次删除
  1. +23
    -12
      src/lager_file_backend.erl
  2. +48
    -8
      src/lager_util.erl

+ 23
- 12
src/lager_file_backend.erl 查看文件

@ -53,6 +53,7 @@
-define(DEFAULT_ROTATION_SIZE, 10485760). %% 10mb
-define(DEFAULT_ROTATION_DATE, "$D0"). %% midnight
-define(DEFAULT_ROTATION_COUNT, 5).
-define(DEFAULT_ROTATION_MOD, lager_util).
-define(DEFAULT_SYNC_LEVEL, error).
-define(DEFAULT_SYNC_INTERVAL, 1000).
-define(DEFAULT_SYNC_SIZE, 1024*64). %% 64kb
@ -67,6 +68,7 @@
size = 0 :: integer(),
date :: undefined | string(),
count = 10 :: integer(),
rotator :: atom(),
shaper :: lager_shaper(),
formatter :: atom(),
formatter_config :: any(),
@ -79,7 +81,8 @@
-type option() :: {file, string()} | {level, lager:log_level()} |
{size, non_neg_integer()} | {date, string()} |
{count, non_neg_integer()} | {high_water_mark, non_neg_integer()} |
{count, non_neg_integer()} | {rotator, atom()} |
{high_water_mark, non_neg_integer()} |
{sync_interval, non_neg_integer()} |
{sync_size, non_neg_integer()} | {sync_on, lager:log_level()} |
{check_interval, non_neg_integer()} | {formatter, atom()} |
@ -108,16 +111,16 @@ init(LogFileConfig) when is_list(LogFileConfig) ->
{error, {fatal, bad_config}};
Config ->
%% probabably a better way to do this, but whatever
[RelName, Level, Date, Size, Count, HighWaterMark, Flush, SyncInterval, SyncSize, SyncOn, CheckInterval, Formatter, FormatterConfig] =
[proplists:get_value(Key, Config) || Key <- [file, level, date, size, count, high_water_mark, flush_queue, sync_interval, sync_size, sync_on, check_interval, formatter, formatter_config]],
[RelName, Level, Date, Size, Count, Rotator, HighWaterMark, SyncInterval, SyncSize, SyncOn, CheckInterval, Formatter, FormatterConfig] =
[proplists:get_value(Key, Config) || Key <- [file, level, date, size, count, rotator, high_water_mark, sync_interval, sync_size, sync_on, check_interval, formatter, formatter_config]],
FlushThr = proplists:get_value(flush_threshold, Config, 0),
Name = lager_util:expand_path(RelName),
schedule_rotation(Name, Date),
Shaper = lager_util:maybe_flush(Flush, #lager_shaper{hwm=HighWaterMark, flush_threshold = FlushThr, id=Name}),
State0 = #state{name=Name, level=Level, size=Size, date=Date, count=Count, shaper=Shaper, formatter=Formatter,
formatter_config=FormatterConfig, sync_on=SyncOn, sync_interval=SyncInterval, sync_size=SyncSize,
check_interval=CheckInterval},
State = case lager_util:open_logfile(Name, {SyncSize, SyncInterval}) of
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
{ok, {FD, Inode, _}} ->
State0#state{fd=FD, inode=Inode};
{error, Reason} ->
@ -184,8 +187,8 @@ handle_event(_Event, State) ->
{ok, State}.
%% @private
handle_info({rotate, File}, #state{name=File,count=Count,date=Date} = State) ->
_ = lager_util:rotate_logfile(File, Count),
handle_info({rotate, File}, #state{name=File,count=Count,date=Date,rotator=Rotator} = State) ->
_ = Rotator:rotate_logfile(File, Count),
State1 = close_file(State),
schedule_rotation(File, Date),
{ok, State1};
@ -229,14 +232,14 @@ config_to_id(Config) ->
write(#state{name=Name, fd=FD, inode=Inode, flap=Flap, size=RotSize,
count=Count} = State, Timestamp, Level, Msg) ->
count=Count, rotator=Rotator} = State, Timestamp, Level, Msg) ->
LastCheck = timer:now_diff(Timestamp, State#state.last_check) div 1000,
case LastCheck >= State#state.check_interval orelse FD == undefined of
true ->
%% need to check for rotation
case lager_util:ensure_logfile(Name, FD, Inode, {State#state.sync_size, State#state.sync_interval}) of
case Rotator:ensure_logfile(Name, FD, Inode, {State#state.sync_size, State#state.sync_interval}) of
{ok, {_, _, Size}} when RotSize /= 0, Size > RotSize ->
case lager_util:rotate_logfile(Name, Count) of
case Rotator:rotate_logfile(Name, Count) of
ok ->
%% go around the loop again, we'll do another rotation check and hit the next clause of ensure_logfile
write(State, Timestamp, Level, Msg);
@ -309,6 +312,7 @@ validate_logfile_proplist(List) ->
lists:keymerge(1, lists:sort(Res), lists:sort([
{level, validate_loglevel(?DEFAULT_LOG_LEVEL)}, {date, DefaultRotationDate},
{size, ?DEFAULT_ROTATION_SIZE}, {count, ?DEFAULT_ROTATION_COUNT},
{rotator, ?DEFAULT_ROTATION_MOD},
{sync_on, validate_loglevel(?DEFAULT_SYNC_LEVEL)}, {sync_interval, ?DEFAULT_SYNC_INTERVAL},
{sync_size, ?DEFAULT_SYNC_SIZE}, {check_interval, ?DEFAULT_CHECK_INTERVAL},
{formatter, lager_default_formatter}, {formatter_config, []}
@ -347,6 +351,13 @@ validate_logfile_proplist([{count, Count}|Tail], Acc) ->
_ ->
throw({bad_config, "Invalid rotation count", Count})
end;
validate_logfile_proplist([{rotator, Rotator}|Tail], Acc) ->
case is_atom(Rotator) of
true ->
validate_logfile_proplist(Tail, [{rotator, Rotator}|Acc]);
false ->
throw({bad_config, "Invalid rotation module", Rotator})
end;
validate_logfile_proplist([{high_water_mark, HighWaterMark}|Tail], Acc) ->
case HighWaterMark of
Hwm when is_integer(Hwm), Hwm >= 0 ->

+ 48
- 8
src/lager_util.erl 查看文件

@ -25,7 +25,7 @@
-export([
levels/0, level_to_num/1, level_to_chr/1,
num_to_level/1, config_to_mask/1, config_to_levels/1, mask_to_levels/1,
open_logfile/2, ensure_logfile/4, rotate_logfile/2, format_time/0, format_time/1,
create_logfile/2, open_logfile/2, ensure_logfile/4, rotate_logfile/2, format_time/0, format_time/1,
localtime_ms/0, localtime_ms/1, maybe_utc/1, parse_rotation_date_spec/1,
calculate_next_rotation/1, validate_trace/1, check_traces/4, is_loggable/3,
trace_filter/1, trace_filter/2, expand_path/1, find_file/2, check_hwm/1, check_hwm/2,
@ -140,6 +140,9 @@ level_to_atom(String) ->
erlang:error(badarg)
end.
create_logfile(Name, Buffer) ->
open_logfile(Name, Buffer).
open_logfile(Name, Buffer) ->
case filelib:ensure_dir(Name) of
ok ->
@ -244,19 +247,34 @@ format_time({{Y, M, D}, {H, Mi, S}}) ->
{[integer_to_list(Y), $-, i2l(M), $-, i2l(D)],
[i2l(H), $:, i2l(Mi), $:, i2l(S)]}.
parse_rotation_hour_spec([], Res) ->
{ok, Res};
parse_rotation_hour_spec([$H, M1, M2], Res) ->
case list_to_integer([M1, M2]) of
X when X >= 0, X =< 59 ->
{ok, Res ++ [{minute, X}]};
_ ->
{error, invalid_date_spec}
end;
parse_rotation_hour_spec([$H, M], Res) when M >= $0, M =< $9 ->
{ok, Res ++ [{minute, M - 48}]};
parse_rotation_hour_spec(_,_) ->
{error, invalid_date_spec}.
%% Default to 00:00:00 rotation
parse_rotation_day_spec([], Res) ->
{ok, Res ++ [{hour, 0}]};
parse_rotation_day_spec([$D, D1, D2], Res) ->
{ok, Res ++ [{hour ,0}]};
parse_rotation_day_spec([$D, D1, D2|T], Res) ->
case list_to_integer([D1, D2]) of
X when X >= 0, X =< 23 ->
{ok, Res ++ [{hour, X}]};
parse_rotation_hour_spec(T, Res ++ [{hour, X}]);
_ ->
{error, invalid_date_spec}
end;
parse_rotation_day_spec([$D, D], Res) when D >= $0, D =< $9 ->
{ok, Res ++ [{hour, D - 48}]};
parse_rotation_day_spec(_, _) ->
{error, invalid_date_spec}.
parse_rotation_day_spec([$D, D|T], Res) when D >= $0, D =< $9 ->
parse_rotation_hour_spec(T, Res ++ [{hour, D - 48 }]);
parse_rotation_day_spec(X, Res) ->
parse_rotation_hour_spec(X, Res).
parse_rotation_date_spec([$$, $W, W|T]) when W >= $0, W =< $6 ->
Week = W - 48,
@ -295,6 +313,17 @@ calculate_next_rotation(Spec) ->
calculate_next_rotation([], Now) ->
Now;
calculate_next_rotation([{minute, X}|T], {{_, _, _}, {Hour, Minute, _}} = Now) when Minute < X ->
%% rotation is this hour
NewNow = setelement(2, Now, {Hour, X, 0}),
calculate_next_rotation(T, NewNow);
calculate_next_rotation([{minute, X}|T], Now) ->
%% rotation is next hour
Seconds = calendar:datetime_to_gregorian_seconds(Now) + 3600,
DateTime = calendar:gregorian_seconds_to_datetime(Seconds),
{_, {NewHour, _, _}} = DateTime,
NewNow = setelement(2, DateTime, {NewHour, X, 0}),
calculate_next_rotation(T, NewNow);
calculate_next_rotation([{hour, X}|T], {{_, _, _}, {Hour, _, _}} = Now) when Hour < X ->
%% rotation is today, sometime
NewNow = setelement(2, Now, {X, 0, 0}),
@ -630,10 +659,13 @@ maybe_flush(Flag, #lager_shaper{} = S) when is_boolean(Flag) ->
-ifdef(TEST).
parse_test() ->
?assertEqual({ok, [{minute, 0}]}, parse_rotation_date_spec("$H0")),
?assertEqual({ok, [{minute, 59}]}, parse_rotation_date_spec("$H59")),
?assertEqual({ok, [{hour, 0}]}, parse_rotation_date_spec("$D0")),
?assertEqual({ok, [{hour, 23}]}, parse_rotation_date_spec("$D23")),
?assertEqual({ok, [{day, 0}, {hour, 23}]}, parse_rotation_date_spec("$W0D23")),
?assertEqual({ok, [{day, 5}, {hour, 16}]}, parse_rotation_date_spec("$W5D16")),
?assertEqual({ok, [{day, 0}, {hour, 12}, {minute, 30}]}, parse_rotation_date_spec("$W0D12H30")),
?assertEqual({ok, [{date, 1}, {hour, 0}]}, parse_rotation_date_spec("$M1D0")),
?assertEqual({ok, [{date, 5}, {hour, 6}]}, parse_rotation_date_spec("$M5D6")),
?assertEqual({ok, [{date, 5}, {hour, 0}]}, parse_rotation_date_spec("$M5")),
@ -645,6 +677,8 @@ parse_test() ->
ok.
parse_fail_test() ->
?assertEqual({error, invalid_date_spec}, parse_rotation_date_spec("$H")),
?assertEqual({error, invalid_date_spec}, parse_rotation_date_spec("$H60")),
?assertEqual({error, invalid_date_spec}, parse_rotation_date_spec("$D")),
?assertEqual({error, invalid_date_spec}, parse_rotation_date_spec("$D24")),
?assertEqual({error, invalid_date_spec}, parse_rotation_date_spec("$W7")),
@ -658,6 +692,12 @@ parse_fail_test() ->
ok.
rotation_calculation_test() ->
?assertMatch({{2000, 1, 1}, {13, 0, 0}},
calculate_next_rotation([{minute, 0}], {{2000, 1, 1}, {12, 34, 43}})),
?assertMatch({{2000, 1, 1}, {12, 45, 0}},
calculate_next_rotation([{minute, 45}], {{2000, 1, 1}, {12, 34, 43}})),
?assertMatch({{2000, 1, 2}, {0, 0, 0}},
calculate_next_rotation([{minute, 0}], {{2000, 1, 1}, {23, 45, 43}})),
?assertMatch({{2000, 1, 2}, {0, 0, 0}},
calculate_next_rotation([{hour, 0}], {{2000, 1, 1}, {12, 34, 43}})),
?assertMatch({{2000, 1, 1}, {16, 0, 0}},

正在加载...
取消
保存