diff --git a/src/lager.erl b/src/lager.erl index f77c822..899087f 100644 --- a/src/lager.erl +++ b/src/lager.erl @@ -22,12 +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_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, @@ -606,3 +608,31 @@ pr_stacktrace(Stacktrace) -> pr_stacktrace(Stacktrace, {Class, Reason}) -> lists:flatten( pr_stacktrace(Stacktrace) ++ "\n" ++ io_lib:format("~s:~p", [Class, Reason])). + +rotate_sink(Sink) -> + Handlers = lager_config:global_get(handlers), + RotateHandlers = lists:filtermap( + fun({Handler,_,S}) when S == Sink -> {true, {Handler, Sink}}; + (_) -> false + end, + Handlers), + rotate_handlers(RotateHandlers). + +rotate_all() -> + rotate_handlers(lists:map(fun({H,_,S}) -> {H, S} end, + lager_config:global_get(handlers))). + + +rotate_handlers(Handlers) -> + [ rotate_handler(Handler, Sink) || {Handler, Sink} <- Handlers ]. + + +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/src/lager_file_backend.erl b/src/lager_file_backend.erl index 54b59eb..a0fd225 100644 --- a/src/lager_file_backend.erl +++ b/src/lager_file_backend.erl @@ -142,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}. @@ -637,6 +640,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..0e08e4d --- /dev/null +++ b/test/lager_rotate.erl @@ -0,0 +1,138 @@ +-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_handler({lager_file_backend, "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(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">>), + + 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). +