|
|
@ -1,6 +1,6 @@ |
|
|
|
%% ------------------------------------------------------------------- |
|
|
|
%% |
|
|
|
%% Copyright (c) 2011-2015 Basho Technologies, Inc. |
|
|
|
%% Copyright (c) 2011-2017 Basho Technologies, Inc. |
|
|
|
%% |
|
|
|
%% This file is provided to you under the Apache License, |
|
|
|
%% Version 2.0 (the "License"); you may not use this file |
|
|
@ -20,23 +20,38 @@ |
|
|
|
|
|
|
|
-module(lager_test_backend). |
|
|
|
|
|
|
|
-include("lager.hrl"). |
|
|
|
|
|
|
|
-behaviour(gen_event). |
|
|
|
|
|
|
|
-export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2, |
|
|
|
code_change/3]). |
|
|
|
|
|
|
|
-include("lager.hrl"). |
|
|
|
|
|
|
|
-define(TEST_SINK_NAME, '__lager_test_sink'). %% <-- used by parse transform |
|
|
|
-define(TEST_SINK_EVENT, '__lager_test_sink_lager_event'). %% <-- used by lager API calls and internals for gen_event |
|
|
|
|
|
|
|
-record(state, {level :: list(), buffer :: list(), ignored :: term()}). |
|
|
|
-record(state, { |
|
|
|
level :: list(), |
|
|
|
buffer :: list(), |
|
|
|
ignored :: term() |
|
|
|
}). |
|
|
|
-compile({parse_transform, lager_transform}). |
|
|
|
|
|
|
|
-ifdef(TEST). |
|
|
|
-include_lib("eunit/include/eunit.hrl"). |
|
|
|
-record(test, {attrs :: list(), format :: list(), args :: list()}). |
|
|
|
-export([pop/0, count/0, count_ignored/0, flush/0, print_state/0]). |
|
|
|
-record(test, { |
|
|
|
attrs :: list(), |
|
|
|
format :: list(), |
|
|
|
args :: list() |
|
|
|
}). |
|
|
|
-export([ |
|
|
|
count/0, |
|
|
|
count_ignored/0, |
|
|
|
flush/0, |
|
|
|
message_stuffer/3, |
|
|
|
pop/0, |
|
|
|
print_state/0 |
|
|
|
]). |
|
|
|
-endif. |
|
|
|
|
|
|
|
init(Level) -> |
|
|
@ -137,23 +152,13 @@ has_line_numbers() -> |
|
|
|
% this gets called a LOT - cache the answer |
|
|
|
case erlang:get({?MODULE, has_line_numbers}) of |
|
|
|
undefined -> |
|
|
|
R = otp_version() >= 15, |
|
|
|
R = lager_util:otp_version() >= 15, |
|
|
|
erlang:put({?MODULE, has_line_numbers}, R), |
|
|
|
R; |
|
|
|
Bool -> |
|
|
|
Bool |
|
|
|
end. |
|
|
|
|
|
|
|
otp_version() -> |
|
|
|
otp_version(erlang:system_info(otp_release)). |
|
|
|
|
|
|
|
otp_version([$R | Rel]) -> |
|
|
|
{Ver, _} = string:to_integer(Rel), |
|
|
|
Ver; |
|
|
|
otp_version(Rel) -> |
|
|
|
{Ver, _} = string:to_integer(Rel), |
|
|
|
Ver. |
|
|
|
|
|
|
|
not_running_test() -> |
|
|
|
?assertEqual({error, lager_not_running}, lager:log(info, self(), "not running")). |
|
|
|
|
|
|
@ -909,7 +914,7 @@ error_logger_redirect_crash_cleanup(_Sink) -> |
|
|
|
error_logger:tty(true). |
|
|
|
|
|
|
|
error_logger_redirect_crash_test_() -> |
|
|
|
TestBody=fun(Name,CrashReason,Expected) -> |
|
|
|
TestBody=fun(Name,CrashReason,Expected) -> |
|
|
|
fun(Sink) -> |
|
|
|
{Name, |
|
|
|
fun() -> |
|
|
@ -924,7 +929,7 @@ error_logger_redirect_crash_test_() -> |
|
|
|
end |
|
|
|
end, |
|
|
|
Tests = [ |
|
|
|
fun(Sink) -> |
|
|
|
fun(Sink) -> |
|
|
|
{"again, there is nothing up my sleeve", |
|
|
|
fun() -> |
|
|
|
?assertEqual(undefined, pop(Sink)), |
|
|
@ -952,7 +957,7 @@ error_logger_redirect_crash_test_() -> |
|
|
|
TestBody("badfun",badfun,"gen_server crash terminated with reason: bad function booger in crash:handle_call/3") |
|
|
|
], |
|
|
|
{"Error logger redirect crash", [ |
|
|
|
{"Redirect to default sink", |
|
|
|
{"Redirect to default sink", |
|
|
|
{foreach, |
|
|
|
fun error_logger_redirect_crash_setup/0, |
|
|
|
fun error_logger_redirect_crash_cleanup/1, |
|
|
@ -1529,7 +1534,7 @@ error_logger_redirect_test_() -> |
|
|
|
end, |
|
|
|
Tests), |
|
|
|
{"Error logger redirect", [ |
|
|
|
{"Redirect to default sink", |
|
|
|
{"Redirect to default sink", |
|
|
|
{foreach, |
|
|
|
fun error_logger_redirect_setup/0, |
|
|
|
fun error_logger_redirect_cleanup/1, |
|
|
@ -1552,77 +1557,107 @@ unsafe_format_test() -> |
|
|
|
ok. |
|
|
|
|
|
|
|
async_threshold_test_() -> |
|
|
|
{foreach, |
|
|
|
Cleanup = fun(Reset) -> |
|
|
|
_ = error_logger:tty(false), |
|
|
|
_ = application:stop(lager), |
|
|
|
_ = application:stop(goldrush), |
|
|
|
_ = application:unset_env(lager, async_threshold), |
|
|
|
if |
|
|
|
Reset -> |
|
|
|
true = ets:delete(async_threshold_test), |
|
|
|
error_logger:tty(true); |
|
|
|
true -> |
|
|
|
_ = (catch ets:delete(async_threshold_test)), |
|
|
|
ok |
|
|
|
end |
|
|
|
end, |
|
|
|
Setup = fun() -> |
|
|
|
% Evidence suggests that previous tests somewhere are leaving some of this stuff |
|
|
|
% loaded, and cleaning it out forcefully to allows the test to succeed. |
|
|
|
_ = Cleanup(false), |
|
|
|
_ = ets:new(async_threshold_test, [set, named_table, public]), |
|
|
|
?assertEqual(true, ets:insert_new(async_threshold_test, {sync_toggled, 0})), |
|
|
|
?assertEqual(true, ets:insert_new(async_threshold_test, {async_toggled, 0})), |
|
|
|
_ = application:load(lager), |
|
|
|
ok = application:set_env(lager, error_logger_redirect, false), |
|
|
|
ok = application:set_env(lager, async_threshold, 2), |
|
|
|
ok = application:set_env(lager, async_threshold_window, 1), |
|
|
|
ok = application:set_env(lager, handlers, [{?MODULE, info}]), |
|
|
|
ok = lager:start(), |
|
|
|
true |
|
|
|
end, |
|
|
|
{foreach, Setup, Cleanup, [ |
|
|
|
{"async threshold works", |
|
|
|
fun() -> |
|
|
|
error_logger:tty(false), |
|
|
|
ets:new(async_threshold_test, [set, named_table, public]), |
|
|
|
ets:insert_new(async_threshold_test, {sync_toggled, 0}), |
|
|
|
ets:insert_new(async_threshold_test, {async_toggled, 0}), |
|
|
|
application:load(lager), |
|
|
|
application:set_env(lager, error_logger_redirect, false), |
|
|
|
application:set_env(lager, async_threshold, 2), |
|
|
|
application:set_env(lager, async_threshold_window, 1), |
|
|
|
application:set_env(lager, handlers, [{?MODULE, info}]), |
|
|
|
lager:start() |
|
|
|
end, |
|
|
|
fun(_) -> |
|
|
|
application:unset_env(lager, async_threshold), |
|
|
|
application:stop(lager), |
|
|
|
application:stop(goldrush), |
|
|
|
ets:delete(async_threshold_test), |
|
|
|
error_logger:tty(true) |
|
|
|
end, |
|
|
|
[ |
|
|
|
{"async threshold works", |
|
|
|
fun() -> |
|
|
|
%% we start out async |
|
|
|
?assertEqual(true, lager_config:get(async)), |
|
|
|
|
|
|
|
%% put a ton of things in the queue |
|
|
|
Workers = [spawn_monitor(fun() -> [lager:info("hello world") || _ <- lists:seq(1, 100)] end) |
|
|
|
|| _ <- lists:seq(1, 10)], |
|
|
|
|
|
|
|
%% serialize on mailbox |
|
|
|
_ = gen_event:which_handlers(lager_event), |
|
|
|
timer:sleep(500), |
|
|
|
|
|
|
|
%% By now the flood of messages will have |
|
|
|
%% forced the backend throttle to turn off |
|
|
|
%% async mode, but it's possible all |
|
|
|
%% outstanding requests have been processed, |
|
|
|
%% so checking the current status (sync or |
|
|
|
%% async) is an exercise in race control. |
|
|
|
|
|
|
|
%% Instead, we'll see whether the backend |
|
|
|
%% throttle has toggled into sync mode at any |
|
|
|
%% point in the past |
|
|
|
?assertMatch([{sync_toggled, N}] when N > 0, |
|
|
|
ets:lookup(async_threshold_test, sync_toggled)), |
|
|
|
%% wait for all the workers to return, meaning that all |
|
|
|
%% the messages have been logged (since we're |
|
|
|
%% definitely in sync mode at the end of the run) |
|
|
|
collect_workers(Workers), |
|
|
|
%% serialize on the mailbox again |
|
|
|
_ = gen_event:which_handlers(lager_event), |
|
|
|
%% just in case... |
|
|
|
timer:sleep(1000), |
|
|
|
lager:info("hello world"), |
|
|
|
_ = gen_event:which_handlers(lager_event), |
|
|
|
%% we start out async |
|
|
|
?assertEqual(true, lager_config:get(async)), |
|
|
|
?assertEqual([{sync_toggled, 0}], |
|
|
|
ets:lookup(async_threshold_test, sync_toggled)), |
|
|
|
|
|
|
|
%% put a ton of things in the queue |
|
|
|
WorkCnt = erlang:max(10, (erlang:system_info(schedulers) * 2)), |
|
|
|
OtpVsn = lager_util:otp_version(), |
|
|
|
% newer OTPs *may* handle the messages faster, so we'll send more |
|
|
|
MsgCnt = ((OtpVsn * OtpVsn) div 2), |
|
|
|
Workers = spawn_stuffers(WorkCnt, [MsgCnt, info, "hello world"], []), |
|
|
|
|
|
|
|
%% serialize on mailbox |
|
|
|
_ = gen_event:which_handlers(lager_event), |
|
|
|
timer:sleep(500), |
|
|
|
|
|
|
|
%% By now the flood of messages should have forced the backend throttle |
|
|
|
%% to turn off async mode, but it's possible all outstanding requests |
|
|
|
%% have been processed, so checking the current status (sync or async) |
|
|
|
%% is an exercise in race control. |
|
|
|
%% Instead, we'll see whether the backend throttle has toggled into sync |
|
|
|
%% mode at any point in the past. |
|
|
|
?assertMatch([{sync_toggled, N}] when N > 0, |
|
|
|
ets:lookup(async_threshold_test, sync_toggled)), |
|
|
|
|
|
|
|
%% Wait for all the workers to return, meaning that all the messages have |
|
|
|
%% been logged (since we're definitely in sync mode at the end of the run). |
|
|
|
collect_workers(Workers), |
|
|
|
|
|
|
|
%% serialize on the mailbox again |
|
|
|
_ = gen_event:which_handlers(lager_event), |
|
|
|
|
|
|
|
%% just in case... |
|
|
|
timer:sleep(1000), |
|
|
|
lager:info("hello world"), |
|
|
|
_ = gen_event:which_handlers(lager_event), |
|
|
|
|
|
|
|
%% async is true again now that the mailbox has drained |
|
|
|
?assertEqual(true, lager_config:get(async)), |
|
|
|
ok |
|
|
|
end} |
|
|
|
]}. |
|
|
|
|
|
|
|
%% async is true again now that the mailbox has drained |
|
|
|
?assertEqual(true, lager_config:get(async)), |
|
|
|
ok |
|
|
|
end |
|
|
|
} |
|
|
|
] |
|
|
|
}. |
|
|
|
% Fire off the stuffers with minimal resource overhead - speed is of the essence. |
|
|
|
spawn_stuffers(0, _, Refs) -> |
|
|
|
% Attempt to return them in about the order that they'll finish. |
|
|
|
lists:reverse(Refs); |
|
|
|
spawn_stuffers(N, Args, Refs) -> |
|
|
|
{_Pid, Ref} = erlang:spawn_monitor(?MODULE, message_stuffer, Args), |
|
|
|
spawn_stuffers((N - 1), Args, [Ref | Refs]). |
|
|
|
|
|
|
|
% Spawned process to stuff N copies of Message into lager's message queue as fast as possible. |
|
|
|
% Skip using a list function for speed and low memory footprint - don't want to take the |
|
|
|
% resources to create a sequence (or pass one in). |
|
|
|
message_stuffer(N, Level, Message) -> |
|
|
|
message_stuffer_(N, Level, [{pid, erlang:self()}], Message). |
|
|
|
|
|
|
|
message_stuffer_(0, _, _, _) -> |
|
|
|
ok; |
|
|
|
message_stuffer_(N, Level, Meta, Message) -> |
|
|
|
lager:log(Level, Meta, Message), |
|
|
|
message_stuffer_((N - 1), Level, Meta, Message). |
|
|
|
|
|
|
|
collect_workers([]) -> |
|
|
|
ok; |
|
|
|
collect_workers(Workers) -> |
|
|
|
collect_workers([Ref | Refs]) -> |
|
|
|
receive |
|
|
|
{'DOWN', Ref, _, _, _} -> |
|
|
|
collect_workers(lists:keydelete(Ref, 2, Workers)) |
|
|
|
collect_workers(Refs) |
|
|
|
end. |
|
|
|
|
|
|
|
produce_n_error_logger_msgs(N) -> |
|
|
|