Skip to content

Commit 7e6dc24

Browse files
committed
ct: Introduce replace mode to cth_log_redirect
In addition to the existing way of logging to both the CT HTML logs and console, allow users to completely replace the standard logging handler with the log redirect hook, effectively silencing console logging output during Common Test runs. Mostly taken from erlang#7375, with the caveat that "add" mode continues being the default, and no changes outside of the hook take place. Additionally, group cth_log_redirect test cases together to allow for easier module verification.
1 parent 71841fe commit 7e6dc24

File tree

3 files changed

+80
-38
lines changed

3 files changed

+80
-38
lines changed

lib/common_test/doc/src/ct_hooks_chapter.xml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,25 @@ results(State) ->
511511
use another level either change the <c>default</c> handler level before
512512
starting common_test, or use the <seemfa marker="kernel:logger#set_handler_config/3">
513513
<c>logger:set_handler_config/3</c></seemfa> API.</p>
514+
<p>This hook supports the following options:</p>
515+
<taglist>
516+
<tag><c>{mode, add}</c></tag>
517+
<item>
518+
<p>Add <c>cth_log_redirect</c> to the default logging handler:
519+
Logs will be emitted to both standard output via the
520+
default handler, and into the Common Test HTML logs.
521+
This is the default behaviour.</p>
522+
</item>
523+
<tag><c>{mode, replace}</c></tag>
524+
<item>
525+
<p>Replace the <c>default</c> logging handler with <c>cth_log_redirect</c>
526+
instead of logging to both the default handler and this handler.
527+
This effectively silences any logger output which would normally be printed
528+
to standard output during test runs. To enable this mode, you can pass
529+
the following options to <c>ct_run</c>:</p>
530+
<p><c>-enable_builtin_hooks false -ct_hooks cth_log_redirect [{mode,replace}]</c></p>
531+
</item>
532+
</taglist>
514533
</item>
515534
<tag><c>cth_surefire</c></tag>
516535
<item>

lib/common_test/src/cth_log_redirect.erl

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121

2222
%%% Common Test Framework functions handling test specifications.
2323
%%%
24-
%%% This module redirects sasl and error logger info to common test log.
24+
%%% This module redirects sasl, error logger, and standard logger messages to
25+
%%% the common test log.
2526

2627
%% CTH Callbacks
2728
-export([id/1, init/2,
@@ -56,9 +57,9 @@
5657
id(_Opts) ->
5758
?MODULE.
5859

59-
init(?MODULE, _Opts) ->
60+
init(?MODULE, Opts) ->
6061
ct_util:mark_process(),
61-
ok = start_log_handler(),
62+
ok = start_log_handler(Opts),
6263
{ok, tc_log_async}.
6364

6465
pre_init_per_suite(Suite, Config, State) ->
@@ -115,7 +116,7 @@ post_end_per_group(_Suite, _Group, Config, Return, State) ->
115116
set_curr_func({group,undefined}, Config),
116117
{Return, State}.
117118

118-
start_log_handler() ->
119+
start_log_handler(Options) ->
119120
case whereis(?MODULE) of
120121
undefined ->
121122
ChildSpec =
@@ -137,9 +138,15 @@ start_log_handler() ->
137138
_Else ->
138139
{{?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG},info}
139140
end,
140-
ok = logger:add_handler(?MODULE,?MODULE,
141-
#{level=>DefaultLevel,
142-
formatter=>DefaultFormatter}).
141+
HandlerConfig = #{level => DefaultLevel, formatter => DefaultFormatter},
142+
HandlerName = case proplists:get_value(mode, Options, add) of
143+
add ->
144+
?MODULE;
145+
replace ->
146+
ok = logger:remove_handler(default),
147+
default
148+
end,
149+
ok = logger:add_handler(HandlerName, ?MODULE, HandlerConfig).
143150

144151
init([]) ->
145152
{ok, #eh_state{log_func = tc_log_async}}.

lib/common_test/test/ct_hooks_SUITE.erl

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,16 @@ all(suite) ->
112112
no_init_suite_config, no_init_config, no_end_config,
113113
failed_sequence, repeat_force_stop, config_clash,
114114
callbacks_on_skip, fallback, data_dir,
115-
cth_log, cth_log_formatter, cth_log_unexpect
115+
{group, cth_log_redirect}
116116
]
117117
).
118118

119+
groups() ->
120+
[
121+
{cth_log_redirect, [], [cth_log_unexpect, cth_log_formatter,
122+
cth_log, cth_log_mode_replace]}
123+
].
124+
119125

120126
%%--------------------------------------------------------------------
121127
%% TEST CASES
@@ -300,36 +306,7 @@ data_dir(Config) when is_list(Config) ->
300306
cth_log(Config) when is_list(Config) ->
301307
%% test that cth_log_redirect writes properly to
302308
%% html I/O log
303-
ct:timetrap({minutes,10}),
304-
StartOpts = do_test(cth_log, "cth_log_SUITE.erl", [], Config),
305-
Logdir = proplists:get_value(logdir, StartOpts),
306-
TCLogs =
307-
filelib:wildcard(
308-
filename:join(Logdir,
309-
"ct_run*/cth.tests*/run*/cth_log_suite.tc*.html")),
310-
lists:foreach(
311-
fun(TCLog) ->
312-
{ok,Bin} = file:read_file(TCLog),
313-
Ts = string:lexemes(binary_to_list(Bin),[$\n]),
314-
Matches = lists:foldl(fun("=ERROR"++_, {E,I,N,L}) ->
315-
{E+1,I,N,L};
316-
("=INFO"++_, {E,I,N,L}) ->
317-
{E,I+1,N,L};
318-
("=NOTICE"++_, {E,I,N,L}) ->
319-
{E,I,N+1,L};
320-
("Logger"++_, {E,I,N,L}) ->
321-
{E,I,N,L+1};
322-
(_, N) -> N
323-
end, {0,0,0,0}, Ts),
324-
ct:pal("~p ({Error,Info,Notice,Log}) matches in ~tp",
325-
[Matches,TCLog]),
326-
MatchList = tuple_to_list(Matches),
327-
case [N || N <- MatchList, N<1] of
328-
[] -> ok;
329-
_ -> exit({missing_io,TCLog})
330-
end
331-
end, TCLogs),
332-
ok.
309+
verify_cth_log_output(Config, [], []).
333310

334311
cth_log_formatter(Config) when is_list(Config) ->
335312
%% test that cth_log_redirect writes properly to
@@ -398,6 +375,12 @@ cth_log_unexpect(Config) when is_list(Config) ->
398375
end, UnexpIoLogs),
399376
ok.
400377

378+
cth_log_mode_replace(Config) when is_list(Config) ->
379+
%% test that cth_log_redirect writes properly to
380+
%% html I/O log when replace mode is used
381+
verify_cth_log_output(Config, [{cth_log_redirect, [{mode, replace}]}],
382+
[{enable_builtin_hooks, false}]).
383+
401384
%% OTP-10599 adds the Suite argument as first argument to all hook
402385
%% callbacks that did not have a Suite argument from before. This test
403386
%% checks that ct_hooks will fall back to old versions of callbacks if
@@ -542,6 +525,39 @@ gen_config(Name,KeyVals,Config) ->
542525
|| {Key,Value} <- KeyVals]),
543526
File.
544527

528+
verify_cth_log_output(Config, CTHooks, ExtraOpts) ->
529+
ct:timetrap({minutes,10}),
530+
StartOpts = do_test(cth_log, "cth_log_SUITE.erl", CTHooks, Config, ok, 2, ExtraOpts),
531+
Logdir = proplists:get_value(logdir, StartOpts),
532+
TCLogs =
533+
filelib:wildcard(
534+
filename:join(Logdir,
535+
"ct_run*/cth.tests*/run*/cth_log_suite.tc*.html")),
536+
lists:foreach(
537+
fun(TCLog) ->
538+
{ok,Bin} = file:read_file(TCLog),
539+
Ts = string:lexemes(binary_to_list(Bin),[$\n]),
540+
Matches = lists:foldl(fun("=ERROR"++_, {E,I,N,L}) ->
541+
{E+1,I,N,L};
542+
("=INFO"++_, {E,I,N,L}) ->
543+
{E,I+1,N,L};
544+
("=NOTICE"++_, {E,I,N,L}) ->
545+
{E,I,N+1,L};
546+
("Logger"++_, {E,I,N,L}) ->
547+
{E,I,N,L+1};
548+
(_, N) -> N
549+
end, {0,0,0,0}, Ts),
550+
ct:pal("~p ({Error,Info,Notice,Log}) matches in ~tp",
551+
[Matches,TCLog]),
552+
MatchList = tuple_to_list(Matches),
553+
case [N || N <- MatchList, N<1] of
554+
[] -> ok;
555+
_ -> exit({missing_io,TCLog})
556+
end
557+
end, TCLogs),
558+
ok.
559+
560+
545561
%%%-----------------------------------------------------------------
546562
%%% TEST EVENTS
547563
%%%-----------------------------------------------------------------

0 commit comments

Comments
 (0)