From af3a2d79c4ff38bd72fc4e92d0900a1d925079a6 Mon Sep 17 00:00:00 2001 From: Daniil Fedotov Date: Mon, 11 Jan 2016 13:03:18 +0000 Subject: [PATCH 1/3] API to explicitly call log rotation --- src/lager.erl | 24 ++++++++++++++++++++++++ src/lager_file_backend.erl | 8 ++++++++ 2 files changed, 32 insertions(+) diff --git a/src/lager.erl b/src/lager.erl index 9e4b051..aa9e2b5 100644 --- a/src/lager.erl +++ b/src/lager.erl @@ -28,6 +28,7 @@ log/3, log/4, log/5, log_unsafe/4, md/0, md/1, + rotate_file/1, rotate_sink/1, rotate_all/0, trace/2, trace/3, trace_file/2, trace_file/3, trace_file/4, trace_console/1, trace_console/2, list_all_sinks/0, clear_all_traces/0, stop_trace/1, stop_trace/3, status/0, get_loglevel/1, get_loglevel/2, set_loglevel/2, set_loglevel/3, set_loglevel/4, get_loglevels/1, @@ -594,3 +595,26 @@ pr_stacktrace(Stacktrace) -> pr_stacktrace(Stacktrace, {Class, Reason}) -> lists:flatten( pr_stacktrace(Stacktrace) ++ "\n" ++ io_lib:format("~s:~p", [Class, Reason])). + +rotate_file(FileName) -> + Handlers = lager_config:global_get(handlers), + RotateHandlers = lists:filter( + fun ({{lager_file_backend, FN}, _, _}) -> + FN == FileName orelse + lager_util:expand_path(FN) == lager_util:expand_path(FileName); + (_) -> false + end, + Handlers), + lager_file_backend:rotate_handlers(RotateHandlers). + +rotate_sink(Sink) -> + Handlers = lager_config:global_get(handlers), + RotateHandlers = lists:filter( + fun({_,_,S}) -> S == Sink; + (_) -> false + end, + Handlers), + lager_file_backend:rotate_handlers(RotateHandlers). + +rotate_all() -> + lager_file_backend:rotate_handlers(lager_config:global_get(handlers)). \ No newline at end of file diff --git a/src/lager_file_backend.erl b/src/lager_file_backend.erl index 1c18dc4..658fc2b 100644 --- a/src/lager_file_backend.erl +++ b/src/lager_file_backend.erl @@ -42,6 +42,7 @@ -export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2, code_change/3]). +-export([rotate_handlers/1, rotate_handler/1]). -export([config_to_id/1]). @@ -401,6 +402,13 @@ schedule_rotation(Name, Date) -> erlang:send_after(lager_util:calculate_next_rotation(Date) * 1000, self(), {rotate, Name}), ok. +rotate_handlers(Handlers) -> + [ rotate_handler(Handler) || Handler <- Handlers ]. + +rotate_handler({{lager_file_backend, FileName}, Handler, _Sink}) -> + Handler ! {rotate, lager_util:expand_path(FileName)}; +rotate_handler(_) -> ok. + -ifdef(TEST). get_loglevel_test() -> From 962eb2c1d70f1bc06b909aafc411a9ded94e0146 Mon Sep 17 00:00:00 2001 From: Daniil Fedotov Date: Fri, 15 Jan 2016 13:33:08 +0000 Subject: [PATCH 2/3] Rotate as gen_event:call --- src/lager.erl | 14 +++- src/lager_file_backend.erl | 21 +++--- test/lager_rotate.erl | 135 +++++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+), 11 deletions(-) create mode 100644 test/lager_rotate.erl diff --git a/src/lager.erl b/src/lager.erl index aa9e2b5..8f5de01 100644 --- a/src/lager.erl +++ b/src/lager.erl @@ -605,7 +605,7 @@ rotate_file(FileName) -> (_) -> false end, Handlers), - lager_file_backend:rotate_handlers(RotateHandlers). + rotate_handlers(RotateHandlers). rotate_sink(Sink) -> Handlers = lager_config:global_get(handlers), @@ -614,7 +614,15 @@ rotate_sink(Sink) -> (_) -> false end, Handlers), - lager_file_backend:rotate_handlers(RotateHandlers). + rotate_handlers(RotateHandlers). rotate_all() -> - lager_file_backend:rotate_handlers(lager_config:global_get(handlers)). \ No newline at end of file + rotate_handlers(lager_config:global_get(handlers)). + + +rotate_handlers(Handlers) -> + [ rotate_handler(Handler) || Handler <- Handlers ]. + +rotate_handler({Backend, Handler, Sink}) -> + gen_event:call(Sink, Backend, rotate, infinity). + diff --git a/src/lager_file_backend.erl b/src/lager_file_backend.erl index 658fc2b..143cd6b 100644 --- a/src/lager_file_backend.erl +++ b/src/lager_file_backend.erl @@ -42,7 +42,6 @@ -export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2, code_change/3]). --export([rotate_handlers/1, rotate_handler/1]). -export([config_to_id/1]). @@ -143,6 +142,9 @@ handle_call({set_loghwm, Hwm}, #state{shaper=Shaper, name=Name} = State) -> ?INT_LOG(notice, "Changed loghwm of ~s to ~p", [Name, Hwm]), {ok, {last_loghwm, Shaper#lager_shaper.hwm}, State#state{shaper=NewShaper}} end; +handle_call(rotate, State = #state{name=File}) -> + {ok, NewState} = handle_info({rotate, File}, State), + {ok, ok, NewState}; handle_call(_Request, State) -> {ok, ok, State}. @@ -402,13 +404,6 @@ schedule_rotation(Name, Date) -> erlang:send_after(lager_util:calculate_next_rotation(Date) * 1000, self(), {rotate, Name}), ok. -rotate_handlers(Handlers) -> - [ rotate_handler(Handler) || Handler <- Handlers ]. - -rotate_handler({{lager_file_backend, FileName}, Handler, _Sink}) -> - Handler ! {rotate, lager_util:expand_path(FileName)}; -rotate_handler(_) -> ok. - -ifdef(TEST). get_loglevel_test() -> @@ -637,6 +632,16 @@ filesystem_test_() -> ?assert(filelib:is_regular("test.log.0")) end }, + {"rotation call should work", + fun() -> + gen_event:add_handler(lager_event, {lager_file_backend, "test.log"}, [{file, "test.log"}, {level, info}, {check_interval, 1000}]), + lager:log(error, self(), "Test message1"), + lager:log(error, self(), "Test message1"), + gen_event:call(lager_event, {lager_file_backend, "test.log"}, rotate, infinity), + lager:log(error, self(), "Test message1"), + ?assert(filelib:is_regular("test.log.0")) + end + }, {"sync_on option should work", fun() -> gen_event:add_handler(lager_event, lager_file_backend, [{file, "test.log"}, {level, info}, {sync_on, "=info"}, {check_interval, 5000}, {sync_interval, 5000}]), diff --git a/test/lager_rotate.erl b/test/lager_rotate.erl new file mode 100644 index 0000000..35bfd1b --- /dev/null +++ b/test/lager_rotate.erl @@ -0,0 +1,135 @@ +-module(lager_rotate). + +-compile(export_all). + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). +-endif. + + +rotate_test_() -> + {foreach, + fun() -> + file:write_file("test1.log", ""), + file:write_file("test2.log", ""), + file:write_file("test3.log", ""), + file:delete("test1.log.0"), + file:delete("test2.log.0"), + file:delete("test3.log.0"), + error_logger:tty(false), + application:load(lager), + application:set_env(lager, handlers, + [{lager_file_backend, [{file, "test1.log"}, {level, info}]}, + {lager_file_backend, [{file, "test2.log"}, {level, info}]}]), + application:set_env(lager, extra_sinks, + [{sink_event, + [{handlers, + [{lager_file_backend, [{file, "test3.log"}, {level, info}]}]} + ]}]), + application:set_env(lager, error_logger_redirect, false), + application:set_env(lager, async_threshold, undefined), + lager:start() + end, + fun(_) -> + file:delete("test1.log"), + file:delete("test2.log"), + file:delete("test3.log"), + file:delete("test1.log.0"), + file:delete("test2.log.0"), + file:delete("test3.log.0"), + application:stop(lager), + application:stop(goldrush), + error_logger:tty(true) + end, + [{"Rotate single file", + fun() -> + lager:log(error, self(), "Test message 1"), + lager:log(sink_event, error, self(), "Sink test message 1", []), + lager:rotate_file("test1.log"), + timer:sleep(1000), + true = filelib:is_regular("test1.log.0"), + lager:log(error, self(), "Test message 2"), + lager:log(sink_event, error, self(), "Sink test message 2", []), + + {ok, File1} = file:read_file("test1.log"), + {ok, File2} = file:read_file("test2.log"), + {ok, SinkFile} = file:read_file("test3.log"), + {ok, File1Old} = file:read_file("test1.log.0"), + + have_no_log(File1, <<"Test message 1">>), + have_log(File1, <<"Test message 2">>), + + have_log(File2, <<"Test message 1">>), + have_log(File2, <<"Test message 2">>), + + have_log(File1Old, <<"Test message 1">>), + have_no_log(File1Old, <<"Test message 2">>), + + have_log(SinkFile, <<"Sink test message 1">>), + have_log(SinkFile, <<"Sink test message 2">>) + end}, + {"Rotate sink", + fun() -> + lager:log(error, self(), "Test message 1"), + lager:log(sink_event, error, self(), "Sink test message 1", []), + lager:rotate_sink(sink_event), + timer:sleep(1000), + true = filelib:is_regular("test3.log.0"), + lager:log(error, self(), "Test message 2"), + lager:log(sink_event, error, self(), "Sink test message 2", []), + {ok, File1} = file:read_file("test1.log"), + {ok, File2} = file:read_file("test2.log"), + {ok, SinkFile} = file:read_file("test3.log"), + {ok, SinkFileOld} = file:read_file("test3.log.0"), + + have_log(File1, <<"Test message 1">>), + have_log(File1, <<"Test message 2">>), + + have_log(File2, <<"Test message 1">>), + have_log(File2, <<"Test message 2">>), + + have_log(SinkFileOld, <<"Sink test message 1">>), + have_no_log(SinkFileOld, <<"Sink test message 2">>), + + have_no_log(SinkFile, <<"Sink test message 1">>), + have_log(SinkFile, <<"Sink test message 2">>) + end}, + {"Rotate all", + fun() -> + lager:log(error, self(), "Test message 1"), + lager:log(sink_event, error, self(), "Sink test message 1", []), + lager:rotate_all(), + timer:sleep(1000), + true = filelib:is_regular("test3.log.0"), + lager:log(error, self(), "Test message 2"), + lager:log(sink_event, error, self(), "Sink test message 2", []), + {ok, File1} = file:read_file("test1.log"), + {ok, File2} = file:read_file("test2.log"), + {ok, SinkFile} = file:read_file("test3.log"), + {ok, File1Old} = file:read_file("test1.log.0"), + {ok, File2Old} = file:read_file("test2.log.0"), + {ok, SinkFileOld} = file:read_file("test3.log.0"), + + have_no_log(File1, <<"Test message 1">>), + have_log(File1, <<"Test message 2">>), + + have_no_log(File2, <<"Test message 1">>), + have_log(File2, <<"Test message 2">>), + + have_no_log(SinkFile, <<"Sink test message 1">>), + have_log(SinkFile, <<"Sink test message 2">>), + + have_log(File1Old, <<"Test message 1">>), + have_no_log(File1Old, <<"Test message 2">>), + + have_log(File2Old, <<"Test message 1">>), + have_no_log(File2Old, <<"Test message 2">>) + + end}]}. + +have_log(Data, Log) -> + {_,_} = binary:match(Data, Log). + +have_no_log(Data, Log) -> + nomatch = binary:match(Data, Log). + From c278368a659f5896ecac9bb6da3f3a50636e06bf Mon Sep 17 00:00:00 2001 From: Daniil Fedotov Date: Mon, 25 Jan 2016 16:39:00 +0000 Subject: [PATCH 3/3] Replace API for file rotation. Now supports rotate_handler/1,2 to rotate custom handler --- src/lager.erl | 38 ++++++++++++++++++-------------------- test/lager_rotate.erl | 5 ++++- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/lager.erl b/src/lager.erl index 8f5de01..9a4f15d 100644 --- a/src/lager.erl +++ b/src/lager.erl @@ -22,13 +22,14 @@ -define(LAGER_MD_KEY, '__lager_metadata'). -define(TRACE_SINK, '__trace_sink'). +-define(ROTATE_TIMEOUT, 100000). %% API -export([start/0, log/3, log/4, log/5, log_unsafe/4, md/0, md/1, - rotate_file/1, rotate_sink/1, rotate_all/0, + rotate_handler/1, rotate_handler/2, rotate_sink/1, rotate_all/0, trace/2, trace/3, trace_file/2, trace_file/3, trace_file/4, trace_console/1, trace_console/2, list_all_sinks/0, clear_all_traces/0, stop_trace/1, stop_trace/3, status/0, get_loglevel/1, get_loglevel/2, set_loglevel/2, set_loglevel/3, set_loglevel/4, get_loglevels/1, @@ -595,34 +596,31 @@ pr_stacktrace(Stacktrace) -> pr_stacktrace(Stacktrace, {Class, Reason}) -> lists:flatten( pr_stacktrace(Stacktrace) ++ "\n" ++ io_lib:format("~s:~p", [Class, Reason])). - -rotate_file(FileName) -> - Handlers = lager_config:global_get(handlers), - RotateHandlers = lists:filter( - fun ({{lager_file_backend, FN}, _, _}) -> - FN == FileName orelse - lager_util:expand_path(FN) == lager_util:expand_path(FileName); - (_) -> false - end, - Handlers), - rotate_handlers(RotateHandlers). - + rotate_sink(Sink) -> Handlers = lager_config:global_get(handlers), - RotateHandlers = lists:filter( - fun({_,_,S}) -> S == Sink; - (_) -> false + RotateHandlers = lists:filtermap( + fun({Handler,_,S}) when S == Sink -> {true, {Handler, Sink}}; + (_) -> false end, Handlers), rotate_handlers(RotateHandlers). rotate_all() -> - rotate_handlers(lager_config:global_get(handlers)). + rotate_handlers(lists:map(fun({H,_,S}) -> {H, S} end, + lager_config:global_get(handlers))). rotate_handlers(Handlers) -> - [ rotate_handler(Handler) || Handler <- Handlers ]. + [ rotate_handler(Handler, Sink) || {Handler, Sink} <- Handlers ]. + -rotate_handler({Backend, Handler, Sink}) -> - gen_event:call(Sink, Backend, rotate, infinity). +rotate_handler(Handler) -> + Handlers = lager_config:global_get(handlers), + case lists:keyfind(Handler, 1, Handlers) of + {Handler, _, Sink} -> rotate_handler(Handler, Sink); + false -> ok + end. +rotate_handler(Handler, Sink) -> + gen_event:call(Sink, Handler, rotate, ?ROTATE_TIMEOUT). diff --git a/test/lager_rotate.erl b/test/lager_rotate.erl index 35bfd1b..0e08e4d 100644 --- a/test/lager_rotate.erl +++ b/test/lager_rotate.erl @@ -45,7 +45,7 @@ rotate_test_() -> fun() -> lager:log(error, self(), "Test message 1"), lager:log(sink_event, error, self(), "Sink test message 1", []), - lager:rotate_file("test1.log"), + lager:rotate_handler({lager_file_backend, "test1.log"}), timer:sleep(1000), true = filelib:is_regular("test1.log.0"), lager:log(error, self(), "Test message 2"), @@ -119,6 +119,9 @@ rotate_test_() -> have_no_log(SinkFile, <<"Sink test message 1">>), have_log(SinkFile, <<"Sink test message 2">>), + have_log(SinkFileOld, <<"Sink test message 1">>), + have_no_log(SinkFileOld, <<"Sink test message 2">>), + have_log(File1Old, <<"Test message 1">>), have_no_log(File1Old, <<"Test message 2">>),