-
This will have the value standard_io, standard_error,
{file,LogFileName}, or {file,LogFileName,LogFileOpts},
@@ -105,11 +105,10 @@ logger:add_handler(my_standard_h, logger_std_h,
In order to configure the default handler (that starts initially with
the kernel application) to log to file instead of standard_io,
- use the kernel configuration parameter
- logger_dest with
- value {file,FileName}. Example:
+ change the Kernel default logger to use a file. Example:
-erl -kernel logger_dest '{file,"./erl.log"}'
+erl -kernel logger '[{handler,default,logger_std_h,
+ #{ logger_std_h => #{ type => {file,"./log.log"}}}}]'
An example of how to replace the standard handler with a disk_log handler
at startup can be found in the manual of
diff --git a/lib/kernel/doc/src/ref_man.xml b/lib/kernel/doc/src/ref_man.xml
index a633ae4832..b6c2714664 100644
--- a/lib/kernel/doc/src/ref_man.xml
+++ b/lib/kernel/doc/src/ref_man.xml
@@ -32,9 +32,11 @@
+
+
@@ -67,6 +69,4 @@
-
-
diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl
index 57d8fc7a15..1d4e37196c 100644
--- a/lib/kernel/src/file.erl
+++ b/lib/kernel/src/file.erl
@@ -69,7 +69,7 @@
%% Types that can be used from other modules -- alphabetically ordered.
-export_type([date_time/0, fd/0, file_info/0, filename/0, filename_all/0,
- io_device/0, name/0, name_all/0, posix/0]).
+ io_device/0, mode/0, name/0, name_all/0, posix/0]).
%%% Includes and defines
-include("file_int.hrl").
diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src
index afffcd156e..23ac5b3444 100644
--- a/lib/kernel/src/kernel.app.src
+++ b/lib/kernel/src/kernel.app.src
@@ -140,7 +140,10 @@
inet_db,
pg2]},
{applications, []},
- {env, []},
+ {env, [{logger_level, info},
+ {logger_sasl_compatible, false},
+ {logger_log_progress, false}
+ ]},
{mod, {kernel, []}},
{runtime_dependencies, ["erts-10.0", "stdlib-3.5", "sasl-3.0"]}
]
diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl
index ae982c1741..b0e8c00bbf 100644
--- a/lib/kernel/src/kernel.erl
+++ b/lib/kernel/src/kernel.erl
@@ -30,17 +30,13 @@
%%% Callback functions for the kernel application.
%%%-----------------------------------------------------------------
start(_, []) ->
+ %% Setup the logger and configure the kernel logger environment
+ ok = logger:internal_init_logger(),
case supervisor:start_link({local, kernel_sup}, kernel, []) of
{ok, Pid} ->
ok = erl_signal_handler:start(),
- %% add error handler
- case logger:setup_standard_handler() of
- ok -> {ok, Pid, []};
- Error ->
- %% Not necessary since the node will crash anyway:
- exit(Pid, shutdown),
- Error
- end;
+ ok = logger:add_handlers(kernel),
+ {ok, Pid, []};
Error -> Error
end.
@@ -147,7 +143,7 @@ init([]) ->
case init:get_argument(mode) of
{ok, [["minimal"]]} ->
{ok, {SupFlags,
- [Code, File, StdError, User, Config, RefC, SafeSup, LoggerSup]}};
+ [Code, File, StdError, User, LoggerSup, Config, RefC, SafeSup]}};
_ ->
Rpc = #{id => rex,
start => {rpc, start_link, []},
diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl
index 98a9937111..9a6719923e 100644
--- a/lib/kernel/src/logger.erl
+++ b/lib/kernel/src/logger.erl
@@ -40,14 +40,17 @@
set_module_level/2, reset_module_level/1,
set_logger_config/1, set_logger_config/2,
set_handler_config/2, set_handler_config/3,
- get_logger_config/0, get_handler_config/1]).
+ get_logger_config/0, get_handler_config/1,
+ add_handlers/1]).
+
+%% Private configuration
+-export([internal_init_logger/0]).
%% Misc
-export([compare_levels/2]).
-export([set_process_metadata/1, update_process_metadata/1,
unset_process_metadata/0, get_process_metadata/0]).
-export([i/0, i/1]).
--export([setup_standard_handler/0, replace_simple_handler/3]).
-export([limit_term/1, get_format_depth/0, get_max_size/0, get_utc_config/0]).
%% Basic report formatting
@@ -93,8 +96,10 @@
term() => term()}.
-type timestamp() :: integer().
+-type config_handler() :: {handler, handler_id(), module(), config()}.
+
-export_type([log/0,level/0,report/0,msg_fun/0,metadata/0,config/0,handler_id/0,
- filter_id/0,filter/0,filter_arg/0,filter_return/0]).
+ filter_id/0,filter/0,filter_arg/0,filter_return/0, config_handler/0]).
%%%-----------------------------------------------------------------
%%% API
@@ -504,118 +509,184 @@ print_module_levels({Module,Level}) ->
print_module_levels(ModuleLevels) ->
lists:map(fun print_module_levels/1, ModuleLevels).
--spec setup_standard_handler() -> ok | {error,term()}.
-setup_standard_handler() ->
- case get_logger_type() of
- {ok,silent} ->
- Level = get_logger_level(),
- ok = set_logger_config(level,Level),
- remove_handler(logger_simple);
- {ok,Type} ->
- Level = get_logger_level(),
- ok = set_logger_config(level,Level),
- Filters = get_logger_filters(),
- setup_standard_handler(Type,#{level=>Level,
- filter_default=>stop,
- filters=>Filters});
- Error ->
- Error
+-spec internal_init_logger() -> ok | {error,term()}.
+%% This function is responsible for config of the logger
+%% This is done before add_handlers because we want the
+%% logger settings to take effect before the kernel supervisor
+%% tree is started.
+internal_init_logger() ->
+ try
+ ok = logger:set_logger_config(level, get_logger_level()),
+ ok = logger:set_logger_config(filter_default, get_logger_filter_default()),
+
+ [case logger:add_logger_filter(Id, Filter) of
+ ok -> ok;
+ {error, Reason} -> throw(Reason)
+ end || {Id, Filter} <- get_logger_filters()],
+
+ _ = [[case logger:set_module_level(Module, Level) of
+ ok -> ok;
+ {error, Reason} -> throw(Reason)
+ end || Module <- Modules]
+ || {module_level, Level, Modules} <- get_logger_env()],
+
+ case logger:set_handler_config(logger_simple,filters,
+ get_default_handler_filters()) of
+ ok -> ok;
+ {error,{not_found,logger_simple}} -> ok
+ end,
+
+ init_kernel_handlers()
+ catch throw:Reason ->
+ ?LOG_ERROR("Invalid logger config: ~p", [Reason]),
+ {error, {bad_config, {kernel, Reason}}}
end.
--spec setup_standard_handler(Type,Config) -> ok | {error,term()} when
- Type :: tty | standard_io | standard_error | {file,File} |
- {file,File,Modes} | {disk_log,LogOpts} | false,
- File :: file:filename(),
- Modes :: [term()], % [file:mode()], or more specific?
- Config :: config(),
- LogOpts :: map().
-setup_standard_handler(false,#{level:=Level,filters:=Filters}) ->
- case set_handler_config(logger_simple,level,Level) of
- ok ->
- set_handler_config(logger_simple,filters,Filters);
- Error ->
- Error
- end;
-setup_standard_handler(Type,Config) ->
- {Module,TypeConfig} = get_type_config(Type),
- replace_simple_handler(?STANDARD_HANDLER,
- Module,
- maps:merge(Config,TypeConfig)).
-
--spec replace_simple_handler(Id,Module,Config) -> ok | {error,term()} when
- Id :: handler_id(),
- Module :: module(),
- Config :: config().
-replace_simple_handler(Id,Module,Config) ->
- _ = code:ensure_loaded(Module),
- DoBuffer = erlang:function_exported(Module,swap_buffer,2),
- case add_handler(Id,Module,Config#{wait_for_buffer=>DoBuffer}) of
- ok ->
- if DoBuffer ->
- {ok,Buffered} = logger_simple:get_buffer(),
- _ = remove_handler(logger_simple),
- Module:swap_buffer(?STANDARD_HANDLER,Buffered);
- true ->
- _ = remove_handler(logger_simple),
- ok
- end,
- ok;
- Error ->
- Error
+-spec init_kernel_handlers() -> ok | {error,term()}.
+%% Setup the kernel environment variables to be correct
+%% The actual handlers are started by a call to add_handlers.
+init_kernel_handlers() ->
+ try
+ case get_logger_type() of
+ {ok,silent} ->
+ ok = logger:remove_handler(logger_simple);
+ {ok,false} ->
+ ok;
+ {ok,Type} ->
+ init_default_config(Type)
+ end
+ catch throw:Reason ->
+ ?LOG_ERROR("Invalid default handler config: ~p", [Reason]),
+ {error, {bad_config, {kernel, Reason}}}
end.
+-spec add_handlers(Application) -> ok | {error,term()} when
+ Application :: atom();
+ (HandlerConfig) -> ok | {error,term()} when
+ HandlerConfig :: [config_handler()].
+%% This function is responsible for resolving the handler config
+%% and then starting the correct handlers. This is done after the
+%% kernel supervisor tree has been started as it needs the logger_sup.
+add_handlers(App) when is_atom(App) ->
+ add_handlers(application:get_env(App, logger, []));
+add_handlers(HandlerConfig) ->
+ try
+ check_logger_config(HandlerConfig),
+ DefaultAdded =
+ lists:foldl(
+ fun({handler, default = Id, Module, Config}, _)
+ when not is_map_key(filters, Config) ->
+ %% The default handler should have a couple of extra filters
+ %% set on it by default.
+ DefConfig = #{ filter_default => stop,
+ filters => get_default_handler_filters()},
+ setup_handler(Id, Module, maps:merge(DefConfig,Config)),
+ true;
+ ({handler, Id, Module, Config}, Default) ->
+ setup_handler(Id, Module, Config),
+ Default orelse Id == default;
+ (_, Default) -> Default
+ end, false, HandlerConfig),
+ %% If a default handler was added we try to remove the simple_logger
+ %% If the simple logger exists it will replay its log events
+ %% to the handler(s) added in the fold above.
+ _ = [case logger:remove_handler(logger_simple) of
+ ok -> ok;
+ {error,{not_found,logger_simple}} -> ok
+ end || DefaultAdded],
+ ok
+ catch throw:Reason ->
+ ?LOG_ERROR("Invalid logger handler config: ~p", [Reason]),
+ {error, {bad_config, {handler, Reason}}}
+ end.
+
+setup_handler(Id, Module, Config) ->
+ case logger:add_handler(Id, Module, Config) of
+ ok -> ok;
+ {error, Reason} -> throw(Reason)
+ end.
+
+check_logger_config(_) ->
+ ok.
+
+-spec get_logger_type() -> {ok, standard_io | false | silent |
+ {file, file:name_all()} |
+ {file, file:name_all(), [file:mode()]}}.
get_logger_type() ->
- Type0 =
- case application:get_env(kernel, logger_dest) of
- undefined ->
- application:get_env(kernel, error_logger);
- T ->
- T
- end,
- case Type0 of
+ case application:get_env(kernel, error_logger) of
{ok, tty} ->
- {ok, tty};
+ {ok, standard_io};
{ok, {file, File}} when is_list(File) ->
{ok, {file, File}};
{ok, {file, File, Modes}} when is_list(File), is_list(Modes) ->
{ok, {file, File, Modes}};
- {ok, {disk_log, File}} when is_list(File) ->
- {ok, {disk_log, get_disk_log_config(File)}};
{ok, false} ->
{ok, false};
{ok, silent} ->
{ok, silent};
undefined ->
- {ok, tty}; % default value
+ case lists:member({handler,default,undefined}, get_logger_env()) of
+ true ->
+ {ok, false};
+ false ->
+ {ok, standard_io} % default value
+ end;
{ok, Bad} ->
- {error,{bad_config, {kernel, {logger_dest, Bad}}}}
+ throw({error_logger, Bad})
end.
-get_disk_log_config(File) ->
- Config1 =
- case application:get_env(kernel,logger_disk_log_maxfiles) of
- undefined -> #{};
- {ok,MF} -> #{max_no_files=>MF}
- end,
- Config2 =
- case application:get_env(kernel,logger_disk_log_maxbytes) of
- undefined -> Config1;
- {ok,MB} -> Config1#{max_no_bytes=>MB}
- end,
- Config3 =
- case application:get_env(kernel,logger_disk_log_type) of
- undefined -> Config2;
- {ok,T} -> Config1#{type=>T}
- end,
- Config3#{file=>File}.
-
get_logger_level() ->
- case application:get_env(kernel,logger_level) of
- undefined -> info;
- {ok,Level} when ?IS_LEVEL(Level) -> Level
+ case application:get_env(kernel,logger_level,info) of
+ Level when ?IS_LEVEL(Level) ->
+ Level;
+ Level ->
+ throw({logger_level, Level})
+ end.
+
+get_logger_filter_default() ->
+ case lists:keyfind(filters,1,get_logger_env()) of
+ {filters,Default,_} ->
+ Default;
+ false ->
+ log
end.
get_logger_filters() ->
+ lists:foldl(
+ fun({filters, _, Filters}, _Acc) ->
+ Filters;
+ (_, Acc) ->
+ Acc
+ end, [], get_logger_env()).
+
+%% This function looks at the kernel logger environment
+%% and updates it so that the correct logger is configured
+init_default_config(Type) when Type==standard_io;
+ Type==standard_error;
+ element(1,Type)==file ->
+ Env = get_logger_env(),
+ DefaultConfig = #{logger_std_h=>#{type=>Type}},
+ NewLoggerEnv =
+ case lists:keyfind(default, 2, Env) of
+ {handler, default, Module, Config} ->
+ lists:map(
+ fun({handler, default, logger_std_h, _}) ->
+ %% Only want to add the logger_std_h config
+ %% if not configured by user AND the default
+ %% handler is still the logger_std_h.
+ {handler, default, Module, maps:merge(DefaultConfig,Config)};
+ (Other) ->
+ Other
+ end, Env);
+ _ ->
+ %% Nothing has been configured, use default
+ [{handler, default, logger_std_h, DefaultConfig} | Env]
+ end,
+ application:set_env(kernel, logger, NewLoggerEnv, [{timeout,infinity}]);
+init_default_config(Type) ->
+ throw({illegal_logger_type,Type}).
+
+get_default_handler_filters() ->
case application:get_env(kernel, logger_sasl_compatible, false) of
true ->
?DEFAULT_HANDLER_FILTERS([beam,erlang,otp]);
@@ -631,18 +702,8 @@ get_logger_filters() ->
Extra ++ ?DEFAULT_HANDLER_FILTERS([beam,erlang,otp,sasl])
end.
-get_type_config({disk_log,LogOpts}) ->
- {logger_disk_log_h,#{disk_log_opts=>LogOpts}};
-get_type_config(tty) ->
- %% This is only for backwards compatibility with error_logger and
- %% old kernel and sasl environment variables
- get_type_config(standard_io);
-get_type_config(Type) when Type==standard_io;
- Type==standard_error;
- element(1,Type)==file ->
- {logger_std_h,#{logger_std_h=>#{type=>Type}}};
-get_type_config(Type) ->
- {error,{illegal_logger_type,Type}}.
+get_logger_env() ->
+ application:get_env(kernel, logger, []).
%%%-----------------------------------------------------------------
-spec limit_term(term()) -> term().
diff --git a/lib/kernel/src/logger_config.erl b/lib/kernel/src/logger_config.erl
index 799aea9617..40dc1b1e1b 100644
--- a/lib/kernel/src/logger_config.erl
+++ b/lib/kernel/src/logger_config.erl
@@ -31,7 +31,7 @@
-include("logger_internal.hrl").
new(Name) ->
- _ = ets:new(Name,[set,protected,named_table]),
+ _ = ets:new(Name,[set,protected,named_table,{write_concurrency,true}]),
ets:whereis(Name).
delete(Tid,Id) ->
diff --git a/lib/kernel/src/logger_internal.hrl b/lib/kernel/src/logger_internal.hrl
index 8c0fc2725d..f9377259f3 100644
--- a/lib/kernel/src/logger_internal.hrl
+++ b/lib/kernel/src/logger_internal.hrl
@@ -22,7 +22,7 @@
-define(LOGGER_KEY,'$logger_config$').
-define(HANDLER_KEY,'$handler_config$').
-define(LOGGER_META_KEY,'$logger_metadata$').
--define(STANDARD_HANDLER, logger_std_h).
+-define(STANDARD_HANDLER, default).
-define(DEFAULT_HANDLER_FILTERS,
?DEFAULT_HANDLER_FILTERS([beam,erlang,otp])).
-define(DEFAULT_HANDLER_FILTERS(Domain),
diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl
index a7f302ac8f..268fd4f2f3 100644
--- a/lib/kernel/src/logger_server.erl
+++ b/lib/kernel/src/logger_server.erl
@@ -122,8 +122,7 @@ init([]) ->
logger_config:create(Tid,logger,LoggerConfig),
SimpleConfig0 = maps:merge(default_config(logger_simple),
#{filter_default=>stop,
- filters=>?DEFAULT_HANDLER_FILTERS,
- logger_simple=>#{buffer=>true}}),
+ filters=>?DEFAULT_HANDLER_FILTERS}),
%% If this fails, then the node should crash
{ok,SimpleConfig} =
logger_simple:adding_handler(logger_simple,SimpleConfig0),
@@ -139,12 +138,21 @@ handle_call({add_handler,Id,Module,HConfig}, _From, #state{tid=Tid}=State) ->
%% inform the handler
case call_h(Module,adding_handler,[Id,HConfig],{ok,HConfig}) of
{ok,HConfig1} ->
- logger_config:create(Tid,Id,Module,HConfig1),
- {ok,Config} = do_get_config(Tid,logger),
- Handlers = maps:get(handlers,Config,[]),
- do_set_config(Tid,logger,
- Config#{handlers=>[Id|Handlers]}),
- ok;
+ %% We know that the call_h would have loaded the module
+ %% if it existed, so it is safe here to call function_exported
+ %% to find out if this is a valid handler
+ case erlang:function_exported(Module, log, 2) of
+ true ->
+ logger_config:create(Tid,Id,Module,HConfig1),
+ {ok,Config} = do_get_config(Tid,logger),
+ Handlers = maps:get(handlers,Config,[]),
+ do_set_config(Tid,logger,
+ Config#{handlers=>[Id|Handlers]});
+ false ->
+ {error,{invalid_handler,
+ {function_not_exported,
+ {Module,log,2}}}}
+ end;
{error,HReason} ->
{error,{handler_not_added,HReason}}
end
diff --git a/lib/kernel/src/logger_simple.erl b/lib/kernel/src/logger_simple.erl
index a1b427b96c..5272455a2d 100644
--- a/lib/kernel/src/logger_simple.erl
+++ b/lib/kernel/src/logger_simple.erl
@@ -20,29 +20,10 @@
-module(logger_simple).
-export([adding_handler/2, removing_handler/2, log/2]).
--export([get_buffer/0]).
%% This module implements a simple handler for logger. It is the
%% default used during system start.
-%%%-----------------------------------------------------------------
-%%% API
-get_buffer() ->
- case whereis(?MODULE) of
- undefined ->
- {error,noproc};
- Pid ->
- Ref = erlang:monitor(process,Pid),
- Pid ! {get_buffer,self()},
- receive
- {buffer,Buffer} ->
- erlang:demonitor(Ref,[flush]),
- {ok,Buffer};
- {'DOWN',Ref,process,Pid,Reason} ->
- {error,Reason}
- end
- end.
-
%%%-----------------------------------------------------------------
%%% Logger callback
@@ -50,7 +31,7 @@ adding_handler(?MODULE,Config) ->
Me = self(),
case whereis(?MODULE) of
undefined ->
- {Pid,Ref} = spawn_opt(fun() -> init(Me,Config) end,
+ {Pid,Ref} = spawn_opt(fun() -> init(Me) end,
[link,monitor,{message_queue_data,off_heap}]),
receive
{'DOWN',Ref,process,Pid,Reason} ->
@@ -102,48 +83,44 @@ log(_,_) ->
%%%-----------------------------------------------------------------
%%% Process
-init(Starter,Config) ->
+init(Starter) ->
register(?MODULE,self()),
Starter ! {self(),started},
- BufferSize =
- case Config of
- #{?MODULE:=#{buffer:=true}} ->
- 10;
- _ ->
- infinity
- end,
- loop(#{buffer_size=>BufferSize,dropped=>0,buffer=>[]},infinity).
+ loop(#{buffer_size=>10,dropped=>0,buffer=>[]}).
-loop(Buffer,Timeout) ->
+loop(Buffer) ->
receive
stop ->
- ok;
- {get_buffer,From} ->
- loop(Buffer#{send_to=>From},0);
+ %% We replay the logger messages of there is
+ %% a default handler when the simple handler
+ %% is removed.
+ case logger:get_handler_config(default) of
+ {ok, _} ->
+ replay_buffer(Buffer);
+ _ ->
+ ok
+ end;
{log,#{msg:=_,meta:=#{time:=_}}=Log} ->
do_log(Log),
- loop(update_buffer(Buffer,Log),Timeout);
+ loop(update_buffer(Buffer,Log));
_ ->
%% Unexpected message - flush it!
- loop(Buffer,Timeout)
- after Timeout ->
- #{dropped:=D,buffer:=B,send_to:=Pid} = Buffer,
- LogList = lists:reverse(B) ++ drop_msg(D),
- Pid ! {buffer,LogList},
- loop(Buffer#{buffer_size=>infinity,
- dropped=>0,
- buffer=>[],
- send_to=>false},
- infinity)
+ loop(Buffer)
end.
-update_buffer(#{buffer_size:=infinity}=Buffer,_Log) ->
- Buffer;
update_buffer(#{buffer_size:=0,dropped:=D}=Buffer,_Log) ->
Buffer#{dropped=>D+1};
update_buffer(#{buffer_size:=S,buffer:=B}=Buffer,Log) ->
Buffer#{buffer_size=>S-1,buffer=>[Log|B]}.
+replay_buffer(#{ dropped := D, buffer := Buffer }) ->
+ lists:foreach(
+ fun F(#{msg := {Tag, Msg}} = L) when Tag =:= string; Tag =:= report ->
+ F(L#{ msg := Msg });
+ F(#{ level := Level, msg := Msg, meta := MD}) ->
+ logger:log(Level, Msg, MD)
+ end, lists:reverse(Buffer, drop_msg(D))).
+
drop_msg(0) ->
[];
drop_msg(N) ->
diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile
index 8599a3d814..2f637ca9de 100644
--- a/lib/kernel/test/Makefile
+++ b/lib/kernel/test/Makefile
@@ -79,6 +79,7 @@ MODULES= \
logger_legacy_SUITE \
logger_simple_SUITE \
logger_std_h_SUITE \
+ logger_test_lib \
os_SUITE \
pg2_SUITE \
seq_trace_SUITE \
diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl
index f311a9c7ed..8a757f0078 100644
--- a/lib/kernel/test/logger_SUITE.erl
+++ b/lib/kernel/test/logger_SUITE.erl
@@ -40,18 +40,18 @@ suite() ->
[{timetrap,{seconds,30}}].
init_per_suite(Config) ->
- case logger:get_handler_config(logger_std_h) of
+ case logger:get_handler_config(?STANDARD_HANDLER) of
{ok,StdH} ->
- ok = logger:remove_handler(logger_std_h),
- [{logger_std_h,StdH}|Config];
+ ok = logger:remove_handler(?STANDARD_HANDLER),
+ [{default_handler,StdH}|Config];
_ ->
Config
end.
end_per_suite(Config) ->
- case ?config(logger_std_h,Config) of
+ case ?config(default_handler,Config) of
{HMod,HConfig} ->
- ok = logger:add_handler(logger_std_h,HMod,HConfig);
+ ok = logger:add_handler(?STANDARD_HANDLER,HMod,HConfig);
_ ->
ok
end.
@@ -434,7 +434,7 @@ handler_failed(_Config) ->
logger:add_handler(h1,?MODULE,#{filter_default=>true}),
{error,{invalid_formatter,[]}} =
logger:add_handler(h1,?MODULE,#{formatter=>[]}),
- ok = logger:add_handler(h1,nomodule,#{filter_default=>log}),
+ {error,{invalid_handler,_}} = logger:add_handler(h1,nomodule,#{filter_default=>log}),
logger:info(?map_rep),
check_no_log(),
#{logger:=#{handlers:=Ids1},
diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl
index 7c33c9130c..cdd1e68947 100644
--- a/lib/kernel/test/logger_disk_log_h_SUITE.erl
+++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl
@@ -31,7 +31,8 @@
end).
suite() ->
- [{timetrap,{seconds,30}}].
+ [{timetrap,{seconds,30}},
+ {ct_hooks,[logger_test_lib]}].
init_per_suite(Config) ->
timer:start(), % to avoid progress report
@@ -725,7 +726,7 @@ write_failure(Config) ->
Log = lists:concat([File,".1"]),
ct:pal("Log = ~p", [Log]),
- Node = start_h_on_new_node(Config, ?FUNCTION_NAME, File),
+ Node = start_h_on_new_node(Config, File),
false = (undefined == rpc:call(Node, ets, whereis, [?TEST_HOOKS_TAB])),
rpc:call(Node, ets, insert, [?TEST_HOOKS_TAB,{tester,self()}]),
rpc:call(Node, ?MODULE, set_internal_log, [?MODULE,internal_log]),
@@ -769,7 +770,7 @@ sync_failure(Config) ->
File = filename:join(Dir, FileName),
- Node = start_h_on_new_node(Config, ?FUNCTION_NAME, File),
+ Node = start_h_on_new_node(Config, File),
false = (undefined == rpc:call(Node, ets, whereis, [?TEST_HOOKS_TAB])),
rpc:call(Node, ets, insert, [?TEST_HOOKS_TAB,{tester,self()}]),
rpc:call(Node, ?MODULE, set_internal_log, [?MODULE,internal_log]),
@@ -809,21 +810,12 @@ sync_failure(cleanup, _Config) ->
Nodes = nodes(),
[test_server:stop_node(Node) || Node <- Nodes].
-start_h_on_new_node(_Config, Func, File) ->
- Pa = filename:dirname(code:which(?MODULE)),
- Dest =
- case os:type() of
- {win32,_} ->
- lists:concat([" {disk_log,\\\"",File,"\\\"}"]);
- _ ->
- lists:concat([" \'{disk_log,\"",File,"\"}\'"])
- end,
- Args = lists:concat([" -kernel ",logger_dest,Dest," -pa ",Pa]),
- NodeName = lists:concat([?MODULE,"_",Func]),
- ct:pal("Starting ~s with ~tp", [NodeName,Args]),
- {ok,Node} = test_server:start_node(NodeName, peer, [{args, Args}]),
- Pid = rpc:call(Node,erlang,whereis,[?STANDARD_HANDLER]),
- true = is_pid(Pid),
+start_h_on_new_node(Config, File) ->
+ {ok,_,Node} =
+ logger_test_lib:setup(
+ Config,
+ [{logger,[{handler,default,logger_disk_log_h,
+ #{ disk_log_opts => #{ file => File }}}]}]),
ok = rpc:call(Node,logger,set_handler_config,[?STANDARD_HANDLER,formatter,
{?MODULE,nl}]),
Node.
diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl
index c2d3364701..e976a10b21 100644
--- a/lib/kernel/test/logger_env_var_SUITE.erl
+++ b/lib/kernel/test/logger_env_var_SUITE.erl
@@ -1,4 +1,4 @@
-%
+%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2018. All Rights Reserved.
@@ -21,83 +21,64 @@
-compile(export_all).
--include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/logger.hrl").
-include_lib("kernel/src/logger_internal.hrl").
--define(all_vars,[{kernel,logger_dest},
- {kernel,logger_level},
- {kernel,logger_log_progress},
- {kernel,logger_sasl_compatible},
- {kernel,error_logger}]).
+-import(logger_test_lib,[setup/2,log/3,sync_and_read/3]).
suite() ->
- [{timetrap,{seconds,30}}].
+ [{timetrap,{seconds,60}},
+ {ct_hooks,[logger_test_lib]}].
init_per_suite(Config) ->
- Env = [{App,Key,application:get_env(App,Key)} || {App,Key} <- ?all_vars],
- Removed = cleanup(),
- [{env,Env},{logger,Removed}|Config].
-
-end_per_suite(Config) ->
- [application:set_env(App,Key,Val) ||
- {App,Key,Val} <- ?config(env,Config),
- Val =/= undefined],
- Hs = ?config(logger,Config),
- [ok = logger:add_handler(Id,Mod,C) || {Id,Mod,C} <- Hs],
- ok.
-
-init_per_group(_Group, Config) ->
- Config.
-
-end_per_group(_Group, _Config) ->
- ok.
-
-init_per_testcase(_TestCase, Config) ->
Config.
-end_per_testcase(Case, Config) ->
- try apply(?MODULE,Case,[cleanup,Config])
- catch error:undef -> ok
- end,
- cleanup(),
+end_per_suite(_Config) ->
ok.
groups() ->
- [].
-
-all() ->
+ [{error_logger,[],[error_logger_tty,
+ error_logger_tty_sasl_compatible,
+ error_logger_false,
+ error_logger_false_progress,
+ error_logger_false_sasl_compatible,
+ error_logger_silent,
+ error_logger_silent_sasl_compatible,
+ error_logger_file]},
+ {logger,[],[logger_file,
+ logger_file_sasl_compatible,
+ logger_file_log_progress,
+ logger_file_no_filter,
+ logger_file_no_filter_level,
+ logger_file_formatter,
+ logger_filters,
+ logger_filters_stop,
+ logger_module_level,
+ logger_disk_log,
+ logger_disk_log_formatter,
+ logger_undefined,
+ logger_many_handlers_default_first,
+ logger_many_handlers_default_last
+ ]},
+ {bad,[],[bad_error_logger,
+ bad_level,
+ bad_sasl_compatibility,
+ bad_progress]}].
+
+all() ->
[default,
default_sasl_compatible,
- dest_tty,
- dest_tty_sasl_compatible,
- dest_false,
- dest_false_progress,
- dest_false_sasl_compatible,
- dest_silent,
- dest_silent_sasl_compatible,
- dest_file_old,
- dest_file,
- dest_disk_log,
- %% disk_log_vars, % or test this in logger_disk_log_SUITE?
sasl_compatible_false,
sasl_compatible_false_no_progress,
sasl_compatible,
- bad_dest%% ,
- %% bad_level,
- %% bad_sasl_compatibility,
- %% bad_progress
+ {group,bad},
+ {group,error_logger},
+ {group,logger}
].
default(Config) ->
- {ok,{_Log,Hs}} = setup(Config,?FUNCTION_NAME,
- undefined,
- undefined, % dest
- undefined, % level
- undefined, % sasl comp (default=false)
- undefined), % progress (default=false)
- {logger_std_h,logger_std_h,StdC} = lists:keyfind(logger_std_h,1,Hs),
- true = is_pid(whereis(logger_std_h)),
+ {ok,#{handlers:=Hs},_Node} = setup(Config,[]),
+ {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs),
info = maps:get(level,StdC),
StdFilters = maps:get(filters,StdC),
{domain,{_,{log,prefix_of,[beam,erlang,otp,sasl]}}} =
@@ -105,18 +86,12 @@ default(Config) ->
true = lists:keymember(stop_progress,1,StdFilters),
false = lists:keymember(logger_simple,1,Hs),
false = lists:keymember(sasl_h,1,Hs),
- false = is_pid(whereis(sasl_h)),
ok.
default_sasl_compatible(Config) ->
- {ok,{_Log,Hs}} = setup(Config,?FUNCTION_NAME,
- undefined,
- undefined, % dest
- undefined, % level
- true, % sasl comp (default=false)
- undefined), % progress (default=false)
- {logger_std_h,logger_std_h,StdC} = lists:keyfind(logger_std_h,1,Hs),
- true = is_pid(whereis(logger_std_h)),
+ {ok,#{handlers:=Hs},_Node} = setup(Config,
+ [{logger_sasl_compatible,true}]),
+ {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs),
info = maps:get(level,StdC),
StdFilters = maps:get(filters,StdC),
{domain,{_,{log,prefix_of,[beam,erlang,otp]}}} =
@@ -124,18 +99,11 @@ default_sasl_compatible(Config) ->
false = lists:keymember(stop_progress,1,StdFilters),
false = lists:keymember(logger_simple,1,Hs),
true = lists:keymember(sasl_h,1,Hs),
- true = is_pid(whereis(sasl_h)),
ok.
-dest_tty(Config) ->
- {ok,{_Log,Hs}} = setup(Config,?FUNCTION_NAME,
- logger_dest,
- tty, % dest
- undefined, % level
- undefined, % sasl comp (default=false)
- undefined), % progress (default=false)
- {logger_std_h,logger_std_h,StdC} = lists:keyfind(logger_std_h,1,Hs),
- true = is_pid(whereis(logger_std_h)),
+error_logger_tty(Config) ->
+ {ok,#{handlers:=Hs},_Node} = setup(Config,[{error_logger,tty}]),
+ {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs),
info = maps:get(level,StdC),
StdFilters = maps:get(filters,StdC),
{domain,{_,{log,prefix_of,[beam,erlang,otp,sasl]}}} =
@@ -143,18 +111,13 @@ dest_tty(Config) ->
true = lists:keymember(stop_progress,1,StdFilters),
false = lists:keymember(logger_simple,1,Hs),
false = lists:keymember(sasl_h,1,Hs),
- false = is_pid(whereis(sasl_h)),
ok.
-dest_tty_sasl_compatible(Config) ->
- {ok,{_Log,Hs}} = setup(Config,?FUNCTION_NAME,
- logger_dest,
- tty, % dest
- undefined, % level
- true, % sasl comp (default=false)
- undefined), % progress (default=false)
- {logger_std_h,logger_std_h,StdC} = lists:keyfind(logger_std_h,1,Hs),
- true = is_pid(whereis(logger_std_h)),
+error_logger_tty_sasl_compatible(Config) ->
+ {ok,#{handlers:=Hs},_Node} = setup(Config,
+ [{error_logger,tty},
+ {logger_sasl_compatible,true}]),
+ {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs),
info = maps:get(level,StdC),
StdFilters = maps:get(filters,StdC),
{domain,{_,{log,prefix_of,[beam,erlang,otp]}}} =
@@ -162,19 +125,17 @@ dest_tty_sasl_compatible(Config) ->
false = lists:keymember(stop_progress,1,StdFilters),
false = lists:keymember(logger_simple,1,Hs),
true = lists:keymember(sasl_h,1,Hs),
- true = is_pid(whereis(sasl_h)),
ok.
-dest_false(Config) ->
- {ok,{_Log,Hs}} = setup(Config,?FUNCTION_NAME,
- logger_dest,
- false, % dest
- notice, % level
- undefined, % sasl comp (default=false)
- undefined), % progress (default=false)
- false = lists:keymember(logger_std_h,1,Hs),
+error_logger_false(Config) ->
+ {ok,#{handlers:=Hs,logger:=L},_Node} =
+ setup(Config,
+ [{error_logger,false},
+ {logger_level,notice}]),
+ false = lists:keymember(?STANDARD_HANDLER,1,Hs),
{logger_simple,logger_simple,SimpleC} = lists:keyfind(logger_simple,1,Hs),
- notice = maps:get(level,SimpleC),
+ info = maps:get(level,SimpleC),
+ notice = maps:get(level,L),
SimpleFilters = maps:get(filters,SimpleC),
{domain,{_,{log,prefix_of,[beam,erlang,otp,sasl]}}} =
lists:keyfind(domain,1,SimpleFilters),
@@ -182,16 +143,16 @@ dest_false(Config) ->
false = lists:keymember(sasl_h,1,Hs),
ok.
-dest_false_progress(Config) ->
- {ok,{_Log,Hs}} = setup(Config,?FUNCTION_NAME,
- logger_dest,
- false, % dest
- notice, % level
- undefined, % sasl comp (default=false)
- true), % progress (default=false)
- false = lists:keymember(logger_std_h,1,Hs),
+error_logger_false_progress(Config) ->
+ {ok,#{handlers:=Hs,logger:=L},_Node} =
+ setup(Config,
+ [{error_logger,false},
+ {logger_level,notice},
+ {logger_log_progress,true}]),
+ false = lists:keymember(?STANDARD_HANDLER,1,Hs),
{logger_simple,logger_simple,SimpleC} = lists:keyfind(logger_simple,1,Hs),
- notice = maps:get(level,SimpleC),
+ info = maps:get(level,SimpleC),
+ notice = maps:get(level,L),
SimpleFilters = maps:get(filters,SimpleC),
{domain,{_,{log,prefix_of,[beam,erlang,otp,sasl]}}} =
lists:keyfind(domain,1,SimpleFilters),
@@ -199,253 +160,472 @@ dest_false_progress(Config) ->
false = lists:keymember(sasl_h,1,Hs),
ok.
-dest_false_sasl_compatible(Config) ->
- {ok,{_Log,Hs}} = setup(Config,?FUNCTION_NAME,
- logger_dest,
- false, % dest
- notice, % level
- true, % sasl comp (default=false)
- undefined), % progress (default=false)
- false = lists:keymember(logger_std_h,1,Hs),
+error_logger_false_sasl_compatible(Config) ->
+ {ok,#{handlers:=Hs,logger:=L},_Node} =
+ setup(Config,
+ [{error_logger,false},
+ {logger_level,notice},
+ {logger_sasl_compatible,true}]),
+ false = lists:keymember(?STANDARD_HANDLER,1,Hs),
{logger_simple,logger_simple,SimpleC} = lists:keyfind(logger_simple,1,Hs),
- notice = maps:get(level,SimpleC),
+ info = maps:get(level,SimpleC),
+ notice = maps:get(level,L),
SimpleFilters = maps:get(filters,SimpleC),
{domain,{_,{log,prefix_of,[beam,erlang,otp]}}} =
lists:keyfind(domain,1,SimpleFilters),
false = lists:keymember(stop_progress,1,SimpleFilters),
true = lists:keymember(sasl_h,1,Hs),
- true = is_pid(whereis(sasl_h)),
ok.
-dest_silent(Config) ->
- {ok,{_Log,Hs}} = setup(Config,?FUNCTION_NAME,
- logger_dest,
- silent, % dest
- undefined, % level
- undefined, % sasl comp (default=false)
- undefined), % progress (default=false)
- false = lists:keymember(logger_std_h,1,Hs),
+error_logger_silent(Config) ->
+ {ok,#{handlers:=Hs},_Node} = setup(Config,
+ [{error_logger,silent}]),
+ false = lists:keymember(?STANDARD_HANDLER,1,Hs),
+ false = lists:keymember(logger_simple,1,Hs),
+ false = lists:keymember(sasl_h,1,Hs),
+ ok.
+
+error_logger_silent_sasl_compatible(Config) ->
+ {ok,#{handlers:=Hs},_Node} = setup(Config,
+ [{error_logger,silent},
+ {logger_sasl_compatible,true}]),
+ false = lists:keymember(?STANDARD_HANDLER,1,Hs),
+ false = lists:keymember(logger_simple,1,Hs),
+ true = lists:keymember(sasl_h,1,Hs),
+ ok.
+
+
+error_logger_file(Config) ->
+ Log = file(Config,?FUNCTION_NAME),
+ {ok,_Hs,Node} = setup(Config,
+ [{error_logger,{file,Log}}]),
+ check_default_log(Node,Log,
+ file,% dest
+ 0),% progress in std logger
+ ok.
+
+
+logger_file(Config) ->
+ Log = file(Config,?FUNCTION_NAME),
+ {ok,#{handlers:=Hs},Node}
+ = setup(Config,
+ [{logger,
+ [{handler,?STANDARD_HANDLER,logger_std_h,
+ #{logger_std_h=>#{type=>{file,Log}}}}]}]),
+ check_default_log(Node,Log,
+ file,% dest
+ 0),% progress in std logger
+
+ {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs),
+ info = maps:get(level,StdC),
+ StdFilters = maps:get(filters,StdC),
+ {domain,{_,{log,prefix_of,[beam,erlang,otp,sasl]}}} =
+ lists:keyfind(domain,1,StdFilters),
+ true = lists:keymember(stop_progress,1,StdFilters),
false = lists:keymember(logger_simple,1,Hs),
false = lists:keymember(sasl_h,1,Hs),
+
ok.
-dest_silent_sasl_compatible(Config) ->
- {ok,{_Log,Hs}} = setup(Config,?FUNCTION_NAME,
- logger_dest,
- silent, % dest
- undefined, % level
- true, % sasl comp (default=false)
- undefined), % progress (default=false)
- false = lists:keymember(logger_std_h,1,Hs),
+logger_file_sasl_compatible(Config) ->
+ Log = file(Config,?FUNCTION_NAME),
+ {ok,#{handlers:=Hs},Node}
+ = setup(Config,
+ [{logger_sasl_compatible,true},
+ {logger,
+ [{handler,?STANDARD_HANDLER,logger_std_h,
+ #{logger_std_h=>#{type=>{file,Log}}}}]}]),
+ check_default_log(Node,Log,
+ file,% dest
+ 0),% progress in std logger
+
+ {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs),
+ info = maps:get(level,StdC),
+ StdFilters = maps:get(filters,StdC),
+ {domain,{_,{log,prefix_of,[beam,erlang,otp]}}} =
+ lists:keyfind(domain,1,StdFilters),
+ false = lists:keymember(stop_progress,1,StdFilters),
false = lists:keymember(logger_simple,1,Hs),
true = lists:keymember(sasl_h,1,Hs),
- true = is_pid(whereis(sasl_h)),
+
+ ok.
+
+logger_file_log_progress(Config) ->
+ Log = file(Config,?FUNCTION_NAME),
+ {ok,#{handlers:=Hs},Node}
+ = setup(Config,
+ [{logger_log_progress,true},
+ {logger,
+ [{handler,?STANDARD_HANDLER,logger_std_h,
+ #{logger_std_h=>#{type=>{file,Log}}}}]}]),
+ check_default_log(Node,Log,
+ file,% dest
+ 6),% progress in std logger
+
+ {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs),
+ info = maps:get(level,StdC),
+ StdFilters = maps:get(filters,StdC),
+ {domain,{_,{log,prefix_of,[beam,erlang,otp,sasl]}}} =
+ lists:keyfind(domain,1,StdFilters),
+ false = lists:keymember(stop_progress,1,StdFilters),
+ false = lists:keymember(logger_simple,1,Hs),
+ false = lists:keymember(sasl_h,1,Hs),
+
ok.
+logger_file_no_filter(Config) ->
+ Log = file(Config,?FUNCTION_NAME),
+ {ok,#{handlers:=Hs},Node}
+ = setup(Config,
+ [{logger,
+ [{handler,?STANDARD_HANDLER,logger_std_h,
+ #{filter_default=>log,filters=>[],
+ logger_std_h=>#{type=>{file,Log}}}}]}]),
+ check_default_log(Node,Log,
+ file,% dest
+ 6),% progress in std logger
+
+ {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs),
+ info = maps:get(level,StdC),
+ [] = maps:get(filters,StdC),
+ false = lists:keymember(logger_simple,1,Hs),
+ false = lists:keymember(sasl_h,1,Hs),
-dest_file_old(Config) ->
- {ok,{Log,_Hs}} = setup(Config,?FUNCTION_NAME,
- error_logger,
- file, % dest
- undefined, % level
- undefined, % sasl comp (default=false)
- undefined), % progress (default=false)
- check_log(Log,
- file, % dest
- 0), % progress in std logger
ok.
-
-
-dest_file(Config) ->
- {ok,{Log,_Hs}} = setup(Config,?FUNCTION_NAME,
- logger_dest,
- file, % dest
- undefined, % level
- undefined, % sasl comp (default=false)
- undefined), % progress (default=false)
- check_log(Log,
- file, % dest
- 0), % progress in std logger
+
+logger_file_no_filter_level(Config) ->
+ Log = file(Config,?FUNCTION_NAME),
+ {ok,#{handlers:=Hs},Node}
+ = setup(Config,
+ [{logger,
+ [{handler,?STANDARD_HANDLER,logger_std_h,
+ #{filters=>[],level=>error,
+ logger_std_h=>#{type=>{file,Log}}}}]}]),
+ check_default_log(Node,Log,
+ file,% dest
+ 0,% progress in std logger
+ error),% level
+
+ {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs),
+ error = maps:get(level,StdC),
+ [] = maps:get(filters,StdC),
+ false = lists:keymember(logger_simple,1,Hs),
+ false = lists:keymember(sasl_h,1,Hs),
+
ok.
-
-
-dest_disk_log(Config) ->
- {ok,{Log,_Hs}} = setup(Config,?FUNCTION_NAME,
- logger_dest,
- disk_log, % dest
- undefined, % level
- undefined, % sasl comp (default=false)
- undefined), % progress (default=false)
- check_log(Log,
- disk_log, % dest
- 0), % progress in std logger
+
+logger_file_formatter(Config) ->
+ Log = file(Config,?FUNCTION_NAME),
+ {ok,#{handlers:=Hs},Node}
+ = setup(Config,
+ [{logger,
+ [{handler,?STANDARD_HANDLER,logger_std_h,
+ #{filters=>[],
+ formatter=>{logger_formatter,#{}},
+ logger_std_h=>#{type=>{file,Log}}}}]}]),
+ check_single_log(Node,Log,
+ file,% dest
+ 6),% progress in std logger
+
+ {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs),
+ info = maps:get(level,StdC),
+ [] = maps:get(filters,StdC),
+ false = lists:keymember(logger_simple,1,Hs),
+ false = lists:keymember(sasl_h,1,Hs),
+
+ ok.
+
+logger_filters(Config) ->
+ Log = file(Config,?FUNCTION_NAME),
+ {ok,#{handlers:=Hs,logger:=Logger},Node}
+ = setup(Config,
+ [{logger_log_progress,true},
+ {logger,
+ [{handler,?STANDARD_HANDLER,logger_std_h,
+ #{logger_std_h=>#{type=>{file,Log}}}},
+ {filters,log,[{stop_progress,{fun logger_filters:progress/2,stop}}]}
+ ]}]),
+ check_default_log(Node,Log,
+ file,% dest
+ 0),% progress in std logger
+
+ {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs),
+ info = maps:get(level,StdC),
+ StdFilters = maps:get(filters,StdC),
+ {domain,{_,{log,prefix_of,[beam,erlang,otp,sasl]}}} =
+ lists:keyfind(domain,1,StdFilters),
+ false = lists:keymember(stop_progress,1,StdFilters),
+ false = lists:keymember(logger_simple,1,Hs),
+ false = lists:keymember(sasl_h,1,Hs),
+ LoggerFilters = maps:get(filters,Logger),
+ true = lists:keymember(stop_progress,1,LoggerFilters),
+
+ ok.
+
+logger_filters_stop(Config) ->
+ Log = file(Config,?FUNCTION_NAME),
+ {ok,#{handlers:=Hs,logger:=Logger},Node}
+ = setup(Config,
+ [{logger_log_progress,true},
+ {logger,
+ [{handler,?STANDARD_HANDLER,logger_std_h,
+ #{filters=>[],
+ logger_std_h=>#{type=>{file,Log}}}},
+ {filters,stop,[{log_error,{fun logger_filters:level/2,{log,gt,info}}}]}
+ ]}]),
+ check_default_log(Node,Log,
+ file,% dest
+ 0,
+ notice),% progress in std logger
+
+ {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs),
+ info = maps:get(level,StdC),
+ [] = maps:get(filters,StdC),
+ false = lists:keymember(logger_simple,1,Hs),
+ false = lists:keymember(sasl_h,1,Hs),
+ LoggerFilters = maps:get(filters,Logger),
+ true = lists:keymember(log_error,1,LoggerFilters),
+
+ ok.
+
+logger_module_level(Config) ->
+ Log = file(Config,?FUNCTION_NAME),
+ {ok,#{handlers:=Hs,module_levels:=ModuleLevels},Node}
+ = setup(Config,
+ [{logger_log_progress,true},
+ {logger,
+ [{handler,?STANDARD_HANDLER,logger_std_h,
+ #{logger_std_h=>#{type=>{file,Log}}}},
+ {module_level,error,[supervisor]}
+ ]}]),
+ check_default_log(Node,Log,
+ file,% dest
+ 3),% progress in std logger
+
+ {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs),
+ info = maps:get(level,StdC),
+ StdFilters = maps:get(filters,StdC),
+ {domain,{_,{log,prefix_of,[beam,erlang,otp,sasl]}}} =
+ lists:keyfind(domain,1,StdFilters),
+ false = lists:keymember(stop_progress,1,StdFilters),
+ false = lists:keymember(logger_simple,1,Hs),
+ false = lists:keymember(sasl_h,1,Hs),
+ [{supervisor,error}] = ModuleLevels,
+ ok.
+
+logger_disk_log(Config) ->
+ Log = file(Config,?FUNCTION_NAME),
+ {ok,#{handlers:=Hs},Node}
+ = setup(Config,
+ [{logger,
+ [{handler,?STANDARD_HANDLER,logger_disk_log_h,
+ #{disk_log_opts=>#{file=>Log}}}]}]),
+ check_default_log(Node,Log,
+ disk_log,% dest
+ 0),% progress in std logger
+
+ {?STANDARD_HANDLER,logger_disk_log_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs),
+ info = maps:get(level,StdC),
+ StdFilters = maps:get(filters,StdC),
+ {domain,{_,{log,prefix_of,[beam,erlang,otp,sasl]}}} =
+ lists:keyfind(domain,1,StdFilters),
+ true = lists:keymember(stop_progress,1,StdFilters),
+ false = lists:keymember(logger_simple,1,Hs),
+ false = lists:keymember(sasl_h,1,Hs),
+
+ ok.
+
+logger_disk_log_formatter(Config) ->
+ Log = file(Config,?FUNCTION_NAME),
+ {ok,#{handlers:=Hs},Node}
+ = setup(Config,
+ [{logger,
+ [{handler,?STANDARD_HANDLER,logger_disk_log_h,
+ #{filters=>[],
+ formatter=>{logger_formatter,#{}},
+ disk_log_opts=>#{file=>Log}}}]}]),
+ check_single_log(Node,Log,
+ disk_log,% dest
+ 6),% progress in std logger
+
+ {?STANDARD_HANDLER,logger_disk_log_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs),
+ info = maps:get(level,StdC),
+ [] = maps:get(filters,StdC),
+ false = lists:keymember(logger_simple,1,Hs),
+ false = lists:keymember(sasl_h,1,Hs),
+
+ ok.
+
+logger_undefined(Config) ->
+ {ok,#{handlers:=Hs,logger:=L},_Node} =
+ setup(Config,[{logger,[{handler,?STANDARD_HANDLER,undefined}]}]),
+ false = lists:keymember(?STANDARD_HANDLER,1,Hs),
+ {logger_simple,logger_simple,SimpleC} = lists:keyfind(logger_simple,1,Hs),
+ info = maps:get(level,SimpleC),
+ info = maps:get(level,L),
+ SimpleFilters = maps:get(filters,SimpleC),
+ {domain,{_,{log,prefix_of,[beam,erlang,otp,sasl]}}} =
+ lists:keyfind(domain,1,SimpleFilters),
+ true = lists:keymember(stop_progress,1,SimpleFilters),
+ false = lists:keymember(sasl_h,1,Hs),
+ ok.
+
+logger_many_handlers_default_first(Config) ->
+ LogErr = file(Config,logger_many_handlers_default_first_error),
+ LogInfo = file(Config,logger_many_handlers_default_first_info),
+
+ logger_many_handlers(
+ Config,[{logger,
+ [{handler,?STANDARD_HANDLER,logger_std_h,
+ #{level=>error,
+ filters=>[],
+ formatter=>{logger_formatter,#{}},
+ logger_std_h=>#{type=>{file,LogErr}}}
+ },
+ {handler,info,logger_std_h,
+ #{level=>info,
+ filters=>[{level,{fun logger_filters:level/2,{stop,gteq,error}}}],
+ logger_std_h=>#{type=>{file,LogInfo}}}
+ }
+ ]}], LogErr, LogInfo, 6).
+
+%% Test that we can add multiple handlers with the default last
+logger_many_handlers_default_last(Config) ->
+ LogErr = file(Config,logger_many_handlers_default_last_error),
+ LogInfo = file(Config,logger_many_handlers_default_last_info),
+ logger_many_handlers(
+ Config,[{logger,
+ [{handler,info,logger_std_h,
+ #{level=>info,
+ filters=>[{level,{fun logger_filters:level/2,{stop,gteq,error}}}],
+ logger_std_h=>#{type=>{file,LogInfo}}}
+ },
+ {handler,?STANDARD_HANDLER,logger_std_h,
+ #{level=>error,
+ filters=>[],
+ formatter=>{logger_formatter,#{}},
+ logger_std_h=>#{type=>{file,LogErr}}}
+ }
+ ]}], LogErr, LogInfo, 7).
+
+logger_many_handlers(Config, Env, LogErr, LogInfo, NumProgress) ->
+ {ok,#{handlers:=Hs},Node} = setup(Config,Env),
+ check_single_log(Node,LogErr,
+ file,% dest
+ 0,% progress in std logger
+ error), % level
+ ok = rpc:call(Node,logger_std_h,filesync,[info]),
+ {ok, Bin} = file:read_file(LogInfo),
+ ct:log("Log content:~n~s",[Bin]),
+ match(Bin,<<"PROGRESS REPORT">>,NumProgress,info,info),
+ match(Bin,<<"ALERT REPORT">>,0,alert,info),
+
ok.
-
sasl_compatible_false(Config) ->
- {ok,{Log,_Hs}} = setup(Config,?FUNCTION_NAME,
- logger_dest,
- file, % dest
- undefined, % level
- false, % sasl comp
- true), % progress
- check_log(Log,
- file, % dest
- 4), % progress in std logger
+ Log = file(Config,?FUNCTION_NAME),
+ {ok,_Hs,Node} = setup(Config,
+ [{error_logger,{file,Log}},
+ {logger_sasl_compatible,false},
+ {logger_log_progress,true}]),
+ check_default_log(Node,Log,
+ file,% dest
+ 6),% progress in std logger
ok.
sasl_compatible_false_no_progress(Config) ->
- {ok,{Log,_Hs}} = setup(Config,?FUNCTION_NAME,
- logger_dest,
- file, % dest
- undefined, % level
- false, % sasl comp
- false), % progress
- check_log(Log,
- file, % dest
- 0), % progress in std logger
+ Log = file(Config,?FUNCTION_NAME),
+ {ok,_Hs,Node} = setup(Config,
+ [{error_logger,{file,Log}},
+ {logger_sasl_compatible,false},
+ {logger_log_progress,false}]),
+ check_default_log(Node,Log,
+ file,% dest
+ 0),% progress in std logger
ok.
sasl_compatible(Config) ->
- {ok,{Log,_Hs}} = setup(Config,?FUNCTION_NAME,
- logger_dest,
- file, % dest
- undefined, % level
- true, % sasl comp
- undefined), % progress
- check_log(Log,
- file, % dest
- 0), % progress in std logger
+ Log = file(Config,?FUNCTION_NAME),
+ {ok,_Hs,Node} = setup(Config,
+ [{error_logger,{file,Log}},
+ {sasl_compatible,true}]),
+ check_default_log(Node,Log,
+ file,% dest
+ 0),% progress in std logger
ok.
-bad_dest(Config) ->
- {error,{bad_config,{kernel,{logger_dest,baddest}}}} =
- setup(Config,?FUNCTION_NAME,
- logger_dest,
- baddest,
- undefined,
- undefined,
- undefined).
+bad_error_logger(Config) ->
+ error = setup(Config,[{error_logger,baddest}]).
bad_level(Config) ->
- error =
- setup(Config,?FUNCTION_NAME,
- logger_dest,
- tty,
- badlevel,
- undefined,
- undefined).
+ error = setup(Config,[{logger_level,badlevel}]).
bad_sasl_compatibility(Config) ->
- error =
- setup(Config,?FUNCTION_NAME,
- logger_dest,
- tty,
- info,
- badcomp,
- undefined).
+ error = setup(Config,[{logger_sasl_compatible,badcomp}]).
bad_progress(Config) ->
- error =
- setup(Config,?FUNCTION_NAME,
- logger_dest,
- tty,
- info,
- undefined,
- badprogress).
+ error = setup(Config,[{logger_log_progress,badprogress}]).
%%%-----------------------------------------------------------------
%%% Internal
-setup(Config,Func,DestVar,Dest,Level,SaslComp,Progress) ->
- ok = logger:add_handler(logger_simple,logger_simple,
- #{filter_default=>log,
- logger_simple=>#{buffer=>true}}),
- Dir = ?config(priv_dir,Config),
- File = lists:concat([?MODULE,"_",Func,".log"]),
- Log = filename:join(Dir,File),
- case Dest of
- undefined ->
- ok;
- F when F==file; F==disk_log ->
- application:set_env(kernel,DestVar,{Dest,Log});
- _ ->
- application:set_env(kernel,DestVar,Dest)
- end,
- case Level of
- undefined ->
- ok;
- _ ->
- application:set_env(kernel,logger_level,Level)
- end,
- case SaslComp of
- undefined ->
- ok;
- _ ->
- application:set_env(kernel,logger_sasl_compatible,SaslComp)
- end,
- case Progress of
- undefined ->
- ok;
- _ ->
- application:set_env(kernel,logger_log_progress,Progress)
- end,
- case logger:setup_standard_handler() of
- ok ->
- application:start(sasl),
- StdH = case Dest of
- NoH when NoH==false; NoH==silent -> false;
- _ -> true
- end,
- StdH = is_pid(whereis(?STANDARD_HANDLER)),
- SaslH = if SaslComp -> true;
- true -> false
- end,
- SaslH = is_pid(whereis(sasl_h)),
- {ok,{Log,maps:get(handlers,logger:i())}};
- Error ->
- Error
- end.
+file(Config,Func) ->
+ filename:join(proplists:get_value(priv_dir,Config),
+ lists:concat([Func,".log"])).
-check_log(Log,Dest,NumProgress) ->
- ok = logger:alert("dummy1"),
- ok = logger:debug("dummy1"),
+check_default_log(Node,Log,Dest,NumProgress) ->
+ check_default_log(Node,Log,Dest,NumProgress,info).
+check_default_log(Node,Log,Dest,NumProgress,Level) ->
+
+ {ok,Bin1,Bin2} = check_log(Node,Log,Dest),
+
+ match(Bin1,<<"PROGRESS REPORT">>,NumProgress,info,Level),
+ match(Bin1,<<"ALERT REPORT">>,1,alert,Level),
+ match(Bin1,<<"INFO REPORT">>,0,info,Level),
+ match(Bin1,<<"DEBUG REPORT">>,0,debug,Level),
+
+ match(Bin2,<<"INFO REPORT">>,1,info,Level),
+ match(Bin2,<<"DEBUG REPORT">>,0,debug,Level),
+ ok.
+
+check_single_log(Node,Log,Dest,NumProgress) ->
+ check_single_log(Node,Log,Dest,NumProgress,info).
+check_single_log(Node,Log,Dest,NumProgress,Level) ->
+
+ {ok,Bin1,Bin2} = check_log(Node,Log,Dest),
+
+ match(Bin1,<<"info:">>,NumProgress,info,Level),
+ match(Bin1,<<"alert:">>,1,alert,Level),
+ match(Bin1,<<"debug:">>,0,debug,Level),
+
+ match(Bin2,<<"info:">>,NumProgress+1,info,Level),
+ match(Bin2,<<"debug:">>,0,debug,Level),
+
+ ok.
+
+check_log(Node,Log,Dest) ->
+
+ ok = log(Node,alert,["dummy1"]),
+ ok = log(Node,debug,["dummy1"]),
%% Check that there are progress reports (supervisor and
%% application_controller) and an error report (the call above) in
%% the log. There should not be any info reports yet.
- {ok,Bin1} = sync_and_read(Dest,Log),
+ {ok,Bin1} = sync_and_read(Node,Dest,Log),
ct:log("Log content:~n~s",[Bin1]),
- match(Bin1,<<"PROGRESS REPORT">>,NumProgress),
- match(Bin1,<<"ALERT REPORT">>,1),
- match(Bin1,<<"INFO REPORT">>,0),
- match(Bin1,<<"DEBUG REPORT">>,0),
%% Then stop sasl and see that the info report from
%% application_controller is there
- ok = application:stop(sasl),
- {ok,Bin2} = sync_and_read(Dest,Log),
+ ok = rpc:call(Node,application,stop,[sasl]),
+ {ok,Bin2} = sync_and_read(Node,Dest,Log),
ct:log("Log content:~n~s",[Bin2]),
- match(Bin2,<<"INFO REPORT">>,1),
- match(Bin1,<<"DEBUG REPORT">>,0),
- ok.
+ {ok,Bin1,Bin2}.
-match(Bin,Pattern,0) ->
+match(Bin,Pattern,0,_,_) ->
nomatch = re:run(Bin,Pattern,[{capture,none}]);
-match(Bin,Pattern,N) ->
- {match,M} = re:run(Bin,Pattern,[{capture,all},global]),
- N = length(M).
-
-sync_and_read(disk_log,Log) ->
- logger_disk_log_h:disk_log_sync(?STANDARD_HANDLER),
- file:read_file(Log ++ ".1");
-sync_and_read(file,Log) ->
- logger_std_h:filesync(?STANDARD_HANDLER),
- file:read_file(Log).
-
-cleanup() ->
- application:stop(sasl),
- [application:unset_env(App,Key) || {App,Key} <- ?all_vars],
- #{handlers:=Hs0} = logger:i(),
- Hs = lists:keydelete(cth_log_redirect,1,Hs0),
- [ok = logger:remove_handler(Id) || {Id,_,_} <- Hs],
- Hs.
+match(Bin,Pattern,N,LogLevel,ConfLevel) ->
+ case logger:compare_levels(LogLevel,ConfLevel) of
+ lt -> match(Bin,Pattern,0,LogLevel,ConfLevel);
+ _ ->
+ {match,M} = re:run(Bin,Pattern,[{capture,all},global]),
+ N = length(M)
+ end.
diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl
index 7d1f33746d..c80e5694cf 100644
--- a/lib/kernel/test/logger_formatter_SUITE.erl
+++ b/lib/kernel/test/logger_formatter_SUITE.erl
@@ -341,7 +341,7 @@ depth(_Config) ->
{"~p",[[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0]]},
#{},
#{template=>Template}),
- application:set_env(kernel,logger_format_depth,12),
+ application:set_env(kernel,error_logger_format_depth,12),
"[1,2,3,4,5,6,7,8,9,0,1|...]" =
format(info,
{"~p",[[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0]]},
@@ -361,7 +361,7 @@ depth(_Config) ->
depth=>unlimited}),
ok.
depth(cleanup,_Config) ->
- application:unset_env(kernel,logger_format_depth),
+ application:unset_env(kernel,error_logger_format_depth),
ok.
chars_limit(_Config) ->
diff --git a/lib/kernel/test/logger_simple_SUITE.erl b/lib/kernel/test/logger_simple_SUITE.erl
index 5d8d32492d..0d505b14f5 100644
--- a/lib/kernel/test/logger_simple_SUITE.erl
+++ b/lib/kernel/test/logger_simple_SUITE.erl
@@ -25,6 +25,8 @@
-include_lib("kernel/include/logger.hrl").
-include_lib("kernel/src/logger_internal.hrl").
+-import(logger_test_lib, [setup/2, log/3, sync_and_read/3]).
+
-define(check_no_log,[] = test_server:messages_get()).
-define(check(Expected),
receive {log,Expected} ->
@@ -42,15 +44,15 @@
-define(keyval_rep,[{function,?FUNCTION_NAME}, {line,?LINE}]).
suite() ->
- [{timetrap,{seconds,30}}].
+ [{timetrap,{seconds,30}},
+ {ct_hooks, [logger_test_lib]}].
init_per_suite(Config) ->
#{handlers:=Hs0} = logger:i(),
Hs = lists:keydelete(cth_log_redirect,1,Hs0),
[ok = logger:remove_handler(Id) || {Id,_,_} <- Hs],
Env = [{App,Key,application:get_env(App,Key)} ||
- {App,Key} <- [{kernel,logger_dest},
- {kernel,logger_level}]],
+ {App,Key} <- [{kernel,logger_level}]],
[{env,Env},{logger,Hs}|Config].
end_per_suite(Config) ->
@@ -79,7 +81,7 @@ groups() ->
all() ->
[start_stop,
- get_buffer,
+ replace_default,
replace_file,
replace_disk_log
].
@@ -100,99 +102,46 @@ start_stop(_Config) ->
start_stop(cleanup,_Config) ->
logger:remove_handler(logger_simple).
-get_buffer(_Config) ->
- %% Start simple without buffer
- ok = logger:add_handler(logger_simple,logger_simple,
- #{filter_default=>log}),
- logger:emergency(?str),
- logger:alert(?str,[]),
- logger:error(?map_rep),
- logger:info(?keyval_rep),
- {ok,[]} = logger_simple:get_buffer(), % no buffer
- ok = logger:remove_handler(logger_simple),
+%% This testcase just tests that it does not crash, the default handler prints
+%% to stdout which we cannot read from in a detached slave.
+replace_default(Config) ->
- %% Start with buffer
- ok = logger:add_handler(logger_simple,logger_simple,
- #{filter_default=>log,
- logger_simple=>#{buffer=>true}}),
- logger:emergency(M1=?str),
- logger:alert(M2=?str,[]),
- logger:error(M3=?map_rep),
- logger:info(M4=?keyval_rep),
- logger:info(M41=?keyval_rep++[not_key_val]),
- error_logger:error_report(some_type,M5=?map_rep),
- error_logger:warning_report("some_type",M6=?map_rep),
- logger:critical(M7=?str,[A7=?keyval_rep]),
- logger:notice(M8=["fake",string,"line:",?LINE]),
- {ok,Buffered1} = logger_simple:get_buffer(),
- [#{level:=emergency,msg:={string,M1}},
- #{level:=alert,msg:={M2,[]}},
- #{level:=error,msg:={report,M3}},
- #{level:=info,msg:={report,M4}},
- #{level:=info,msg:={report,M41}},
- #{level:=error,msg:={report,#{label:={error_logger,error_report},
- report:=M5}}},
- #{level:=warning,msg:={report,#{label:={error_logger,warning_report},
- report:=M6}}},
- #{level:=critical,msg:={M7,[A7]}},
- #{level:=notice,msg:={string,M8}}] = Buffered1,
-
- %% Keep logging - should not buffer any more
- logger:emergency(?str),
- logger:alert(?str,[]),
- logger:error(?map_rep),
- logger:info(?keyval_rep),
- {ok,[]} = logger_simple:get_buffer(),
- ok = logger:remove_handler(logger_simple),
+ {ok, _, Node} = logger_test_lib:setup(Config, [{logger, [{handler, default, undefined}]}]),
+ log(Node, emergency, [M1=?str]),
+ log(Node, alert, [M2=?str,[]]),
+ log(Node, error, [M3=?map_rep]),
+ log(Node, info, [M4=?keyval_rep]),
+ log(Node, info, [M41=?keyval_rep++[not_key_val]]),
+ rpc:call(Node, error_logger, error_report, [some_type,M5=?map_rep]),
+ rpc:call(Node, error_logger, warning_report, ["some_type",M6=?map_rep]),
+ log(Node, critical, [M7=?str,[A7=?keyval_rep]]),
+ log(Node, notice, [M8=["fake",string,"line:",?LINE]]),
+
+ Env = rpc:call(Node, application, get_env, [kernel, logger, []]),
+ ok = rpc:call(Node, logger, add_handlers, [Env]),
- %% Fill buffer and drop
- ok = logger:add_handler(logger_simple,logger_simple,
- #{filter_default=>log,
- logger_simple=>#{buffer=>true}}),
- logger:emergency(M9=?str),
- M10=?str,
- [logger:info(M10) || _ <- lists:seq(1,8)],
- logger:error(M11=?str),
- logger:error(?str),
- logger:error(?str),
- {ok,Buffered3} = logger_simple:get_buffer(),
- 11 = length(Buffered3),
- [#{level:=emergency,msg:={string,M9}},
- #{level:=info,msg:={string,M10}},
- #{level:=info,msg:={string,M10}},
- #{level:=info,msg:={string,M10}},
- #{level:=info,msg:={string,M10}},
- #{level:=info,msg:={string,M10}},
- #{level:=info,msg:={string,M10}},
- #{level:=info,msg:={string,M10}},
- #{level:=info,msg:={string,M10}},
- #{level:=error,msg:={string,M11}},
- #{level:=info,msg:={"Simple handler buffer full, dropped ~w messages",[2]}}]
- = Buffered3,
ok.
-get_buffer(cleanup,_Config) ->
- logger:remove_handler(logger_simple).
replace_file(Config) ->
- ok = logger:add_handler(logger_simple,logger_simple,
- #{filter_default=>log,
- logger_simple=>#{buffer=>true}}),
- logger:emergency(M1=?str),
- logger:alert(M2=?str,[]),
- logger:error(?map_rep),
- logger:info(?keyval_rep),
- undefined = whereis(?STANDARD_HANDLER),
- PrivDir = ?config(priv_dir,Config),
- File = filename:join(PrivDir,atom_to_list(?FUNCTION_NAME)++".log"),
-
- application:set_env(kernel,logger_dest,{file,File}),
- application:set_env(kernel,logger_level,info),
-
- ok = logger:setup_standard_handler(),
- true = is_pid(whereis(?STANDARD_HANDLER)),
- ok = logger_std_h:filesync(?STANDARD_HANDLER),
- {ok,Bin} = file:read_file(File),
- Lines = [unicode:characters_to_list(L) ||
+
+ {ok, _, Node} = logger_test_lib:setup(Config, [{logger, [{handler, default, undefined}]}]),
+ log(Node, emergency, [M1=?str]),
+ log(Node, alert, [M2=?str,[]]),
+ log(Node, error, [M3=?map_rep]),
+ log(Node, info, [M4=?keyval_rep]),
+ log(Node, info, [M41=?keyval_rep++[not_key_val]]),
+ log(Node, critical, [M7=?str,[A7=?keyval_rep]]),
+ log(Node, notice, [M8=["fake",string,"line:",?LINE]]),
+
+ File = filename:join(proplists:get_value(priv_dir,Config),
+ atom_to_list(?FUNCTION_NAME)++".log"),
+
+ ok = rpc:call(Node, logger, add_handlers,
+ [[{handler, default, logger_std_h,
+ #{ logger_std_h => #{ type => {file, File} }}}]]),
+
+ {ok,Bin} = sync_and_read(Node, file, File),
+ Lines = [unicode:characters_to_list(L) ||
L <- binary:split(Bin,<<"\n">>,[global,trim])],
["=EMERGENCY REPORT===="++_,
M1,
@@ -203,32 +152,38 @@ replace_file(Config) ->
_,
"=INFO REPORT===="++_,
_,
- _] = Lines,
+ _,
+ "=INFO REPORT===="++_,
+ _,
+ _,
+ _,
+ "=CRITICAL REPORT===="++_,
+ _,
+ _,
+ "=NOTICE REPORT===="++_,
+ _
+ ] = Lines,
ok.
-replace_file(cleanup,_Config) ->
- logger:remove_handler(?STANDARD_HANDLER),
- logger:remove_handler(logger_simple).
-
+
replace_disk_log(Config) ->
- ok = logger:add_handler(logger_simple,logger_simple,
- #{filter_default=>log,
- logger_simple=>#{buffer=>true}}),
- logger:emergency(M1=?str),
- logger:alert(M2=?str,[]),
- logger:error(?map_rep),
- logger:info(?keyval_rep),
- undefined = whereis(?STANDARD_HANDLER),
- PrivDir = ?config(priv_dir,Config),
- File = filename:join(PrivDir,atom_to_list(?FUNCTION_NAME)),
-
- application:set_env(kernel,logger_dest,{disk_log,File}),
- application:set_env(kernel,logger_level,info),
-
- ok = logger:setup_standard_handler(),
- true = is_pid(whereis(?STANDARD_HANDLER)),
- ok = logger_disk_log_h:disk_log_sync(?STANDARD_HANDLER),
- {ok,Bin} = file:read_file(File++".1"),
- Lines = [unicode:characters_to_list(L) ||
+
+ {ok, _, Node} = logger_test_lib:setup(Config, [{logger, [{handler, default, undefined}]}]),
+ log(Node, emergency, [M1=?str]),
+ log(Node, alert, [M2=?str,[]]),
+ log(Node, error, [M3=?map_rep]),
+ log(Node, info, [M4=?keyval_rep]),
+ log(Node, info, [M41=?keyval_rep++[not_key_val]]),
+ log(Node, critical, [M7=?str,[A7=?keyval_rep]]),
+ log(Node, notice, [M8=["fake",string,"line:",?LINE]]),
+
+ File = filename:join(proplists:get_value(priv_dir,Config),
+ atom_to_list(?FUNCTION_NAME)++".log"),
+
+ ok = rpc:call(Node, logger, add_handlers,
+ [[{handler, default, logger_disk_log_h,
+ #{ disk_log_opts => #{ file => File }}}]]),
+ {ok,Bin} = sync_and_read(Node, disk_log, File),
+ Lines = [unicode:characters_to_list(L) ||
L <- binary:split(Bin,<<"\n">>,[global,trim])],
["=EMERGENCY REPORT===="++_,
M1,
@@ -239,9 +194,15 @@ replace_disk_log(Config) ->
_,
"=INFO REPORT===="++_,
_,
- _|_] = Lines, % the tail might be an info report about opening the disk log
+ _,
+ "=INFO REPORT===="++_,
+ _,
+ _,
+ _,
+ "=CRITICAL REPORT===="++_,
+ _,
+ _,
+ "=NOTICE REPORT===="++_,
+ _
+ ] = Lines,
ok.
-replace_disk_log(cleanup,_Config) ->
- logger:remove_handler(?STANDARD_HANDLER),
- logger:remove_handler(logger_simple).
-
diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl
index 34c3167960..83bc96620f 100644
--- a/lib/kernel/test/logger_std_h_SUITE.erl
+++ b/lib/kernel/test/logger_std_h_SUITE.erl
@@ -50,11 +50,12 @@
end).
suite() ->
- [{timetrap,{seconds,30}}].
+ [{timetrap,{seconds,30}},
+ {ct_hooks,[logger_test_lib]}].
init_per_suite(Config) ->
timer:start(), % to avoid progress report
- {ok,{?STANDARD_HANDLER,#{formatter:=OrigFormatter}}} =
+ {ok,{logger_std_h,#{formatter:=OrigFormatter}}} =
logger:get_handler_config(?STANDARD_HANDLER),
[{formatter,OrigFormatter}|Config].
@@ -322,29 +323,32 @@ config_fail(cleanup,_Config) ->
logger:remove_handler(?MODULE).
crash_std_h_to_file(Config) ->
- crash_std_h(Config,?FUNCTION_NAME,logger_dest,file).
+ Dir = ?config(priv_dir,Config),
+ Log = filename:join(Dir,lists:concat([?MODULE,"_",?FUNCTION_NAME,".log"])),
+ crash_std_h(Config,?FUNCTION_NAME,
+ [{handler,default,logger_std_h,
+ #{ logger_std_h => #{ type => {file, Log} }}}],
+ file, Log).
crash_std_h_to_file(cleanup,_Config) ->
crash_std_h(cleanup).
crash_std_h_to_disk_log(Config) ->
- crash_std_h(Config,?FUNCTION_NAME,logger_dest,disk_log).
+ Dir = ?config(priv_dir,Config),
+ Log = filename:join(Dir,lists:concat([?MODULE,"_",?FUNCTION_NAME,".log"])),
+ crash_std_h(Config,?FUNCTION_NAME,
+ [{handler,default,logger_disk_log_h,
+ #{ disk_log_opts => #{ file => Log }}}],
+ disk_log,Log).
crash_std_h_to_disk_log(cleanup,_Config) ->
crash_std_h(cleanup).
-crash_std_h(Config,Func,Var,Type) ->
+crash_std_h(Config,Func,Var,Type,Log) ->
Dir = ?config(priv_dir,Config),
- File = lists:concat([?MODULE,"_",Func,".log"]),
- Log = filename:join(Dir,File),
+ SysConfig = filename:join(Dir,lists:concat([?MODULE,"_",Func,".config"])),
+ ok = file:write_file(SysConfig, io_lib:format("[{kernel,[{logger,~p}]}].",[Var])),
Pa = filename:dirname(code:which(?MODULE)),
- TypeAndLog =
- case os:type() of
- {win32,_} ->
- lists:concat([" {",Type,",\\\"",Log,"\\\"}"]);
- _ ->
- lists:concat([" \'{",Type,",\"",Log,"\"}\'"])
- end,
- Args = lists:concat([" -kernel ",Var,TypeAndLog," -pa ",Pa]),
Name = lists:concat([?MODULE,"_",Func]),
+ Args = lists:concat([" -config ",filename:rootname(SysConfig)," -pa ",Pa]),
ct:pal("Starting ~p with ~tp", [Name,Args]),
%% Start a node which prints kernel logs to the destination specified by Type
{ok,Node} = test_server:start_node(Name, peer, [{args, Args}]),
@@ -585,7 +589,7 @@ write_failure(Config) ->
Dir = ?config(priv_dir, Config),
File = lists:concat([?MODULE,"_",?FUNCTION_NAME,".log"]),
Log = filename:join(Dir, File),
- Node = start_std_h_on_new_node(Config, ?FUNCTION_NAME, Log),
+ Node = start_std_h_on_new_node(Config, Log),
false = (undefined == rpc:call(Node, ets, whereis, [?TEST_HOOKS_TAB])),
rpc:call(Node, ets, insert, [?TEST_HOOKS_TAB,{tester,self()}]),
rpc:call(Node, ?MODULE, set_internal_log, [?MODULE,internal_log]),
@@ -622,7 +626,7 @@ sync_failure(Config) ->
Dir = ?config(priv_dir, Config),
File = lists:concat([?MODULE,"_",?FUNCTION_NAME,".log"]),
Log = filename:join(Dir, File),
- Node = start_std_h_on_new_node(Config, ?FUNCTION_NAME, Log),
+ Node = start_std_h_on_new_node(Config, Log),
false = (undefined == rpc:call(Node, ets, whereis, [?TEST_HOOKS_TAB])),
rpc:call(Node, ets, insert, [?TEST_HOOKS_TAB,{tester,self()}]),
rpc:call(Node, ?MODULE, set_internal_log, [?MODULE,internal_log]),
@@ -658,21 +662,12 @@ sync_failure(cleanup, _Config) ->
Nodes = nodes(),
[test_server:stop_node(Node) || Node <- Nodes].
-start_std_h_on_new_node(_Config, Func, Log) ->
- Pa = filename:dirname(code:which(?MODULE)),
- Dest =
- case os:type() of
- {win32,_} ->
- lists:concat([" {file,\\\"",Log,"\\\"}"]);
- _ ->
- lists:concat([" \'{file,\"",Log,"\"}\'"])
- end,
- Args = lists:concat([" -kernel ",logger_dest,Dest," -pa ",Pa]),
- Name = lists:concat([?MODULE,"_",Func]),
- ct:pal("Starting ~s with ~tp", [Name,Args]),
- {ok,Node} = test_server:start_node(Name, peer, [{args, Args}]),
- Pid = rpc:call(Node,erlang,whereis,[?STANDARD_HANDLER]),
- true = is_pid(Pid),
+start_std_h_on_new_node(Config, Log) ->
+ {ok,_,Node} =
+ logger_test_lib:setup(
+ Config,
+ [{logger,[{handler,default,logger_std_h,
+ #{ logger_std_h => #{ type => {file,Log}}}}]}]),
ok = rpc:call(Node,logger,set_handler_config,[?STANDARD_HANDLER,formatter,
{?MODULE,nl}]),
Node.
diff --git a/lib/kernel/test/logger_test_lib.erl b/lib/kernel/test/logger_test_lib.erl
new file mode 100644
index 0000000000..4ac05e6480
--- /dev/null
+++ b/lib/kernel/test/logger_test_lib.erl
@@ -0,0 +1,82 @@
+%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(logger_test_lib).
+
+-include_lib("kernel/src/logger_internal.hrl").
+
+-export([setup/2, log/3, sync_and_read/3]).
+
+-export([init/2,
+ pre_init_per_suite/3, pre_init_per_testcase/4,
+ post_end_per_testcase/5, post_end_per_suite/3]).
+
+setup(Config,Vars) ->
+ FuncStr = lists:concat([proplists:get_value(suite, Config), "_",
+ proplists:get_value(tc, Config)]),
+ ConfigFileName = filename:join(proplists:get_value(priv_dir, Config), FuncStr),
+ file:write_file(ConfigFileName ++ ".config", io_lib:format("[{kernel, ~p}].",[Vars])),
+ case test_server:start_node(proplists:get_value(tc, Config), slave,
+ [{args, ["-pa ",filename:dirname(code:which(?MODULE)),
+ " -boot start_sasl -kernel start_timer true "
+ "-config ",ConfigFileName]}]) of
+ {ok, Node} ->
+ L = rpc:call(Node, logger, i, []),
+ ct:log("~p",[L]),
+ {ok, L, Node};
+ {error, Reason} ->
+ ct:log("Failed to start node: ~p",[Reason]),
+ error
+ end.
+
+log(Node, F, A) ->
+ log(Node, logger, F, A).
+log(Node, M, F, A) ->
+ MD = #{ gl => rpc:call(Node, erlang, whereis, [logger]) },
+ rpc:call(Node, M, F, A ++ [MD]).
+
+sync_and_read(Node,disk_log,Log) ->
+ rpc:call(Node,logger_disk_log_h,disk_log_sync,[?STANDARD_HANDLER]),
+ file:read_file(Log ++ ".1");
+sync_and_read(Node, file,Log) ->
+ ok = rpc:call(Node,logger_std_h,filesync,[?STANDARD_HANDLER]),
+ file:read_file(Log).
+
+
+init(_, _) ->
+ {ok, []}.
+
+pre_init_per_suite(_Suite, Config, State) ->
+ {[{nodes, nodes()} | Config], State}.
+
+pre_init_per_testcase(Suite, TC, Config, State) ->
+ cleanup(Config),
+ {[{suite, Suite}, {tc, TC} | Config], State}.
+
+post_end_per_testcase(_, _TC, Config, Res, State) ->
+ cleanup(Config),
+ {Res, State}.
+
+post_end_per_suite(_, Config, State) ->
+ cleanup(Config),
+ {Config, State}.
+
+cleanup(Config) ->
+ [test_server:stop_node(N) || N <- nodes(),
+ not lists:member(N, proplists:get_value(nodes, Config))].
diff --git a/lib/sasl/test/sasl_report_SUITE.erl b/lib/sasl/test/sasl_report_SUITE.erl
index 96975aaf69..72ee2f0a10 100644
--- a/lib/sasl/test/sasl_report_SUITE.erl
+++ b/lib/sasl/test/sasl_report_SUITE.erl
@@ -54,7 +54,7 @@ gen_server_crash_unicode(Config) ->
gen_server_crash(Config, Encoding) ->
StopFilter = {fun(_,_) -> stop end, ok},
- logger:add_handler_filter(logger_std_h,stop_all,StopFilter),
+ logger:add_handler_filter(default,stop_all,StopFilter),
logger:add_handler_filter(cth_log_redirect,stop_all,StopFilter),
try
do_gen_server_crash(Config, Encoding)
@@ -62,7 +62,7 @@ gen_server_crash(Config, Encoding) ->
ok = application:unset_env(kernel, logger_sasl_compatible),
ok = application:unset_env(sasl, sasl_error_logger),
ok = application:unset_env(kernel, error_logger_format_depth),
- logger:remove_handler_filter(logger_std_h,stop_all),
+ logger:remove_handler_filter(default,stop_all),
logger:remove_handler_filter(cth_log_redirect,stop_all)
end,
ok.
@@ -83,9 +83,11 @@ do_gen_server_crash(Config, Encoding) ->
error_logger:logfile({open,KernelLog}),
application:start(sasl),
logger:i(print),
+ ct:log("error_logger handlers: ~p",[error_logger:which_report_handlers()]),
crash_me(),
+
error_logger:logfile(close),
application:stop(sasl),
--
cgit v1.2.3
From 3ff770804a27da9add51178b99946ea93d74bd32 Mon Sep 17 00:00:00 2001
From: Lukas Larsson
Date: Mon, 14 May 2018 11:39:58 +0200
Subject: kernel: Make all handler callbacks not block logger
---
lib/kernel/src/logger_server.erl | 239 ++++++++++++++++++++-----------
lib/kernel/test/logger_SUITE.erl | 68 +++++++--
lib/kernel/test/logger_env_var_SUITE.erl | 27 +++-
3 files changed, 236 insertions(+), 98 deletions(-)
(limited to 'lib')
diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl
index 268fd4f2f3..2a3723177f 100644
--- a/lib/kernel/src/logger_server.erl
+++ b/lib/kernel/src/logger_server.erl
@@ -36,8 +36,9 @@
-include("logger_internal.hrl").
-define(SERVER, logger).
+-define(LOGGER_SERVER_TAG, '$logger_cb_process').
--record(state, {tid}).
+-record(state, {tid, async_req, async_req_queue}).
%%%===================================================================
%%% API
@@ -127,96 +128,93 @@ init([]) ->
{ok,SimpleConfig} =
logger_simple:adding_handler(logger_simple,SimpleConfig0),
logger_config:create(Tid,logger_simple,logger_simple,SimpleConfig),
- {ok, #state{tid=Tid}}.
-
-handle_call({add_handler,Id,Module,HConfig}, _From, #state{tid=Tid}=State) ->
- Reply =
- case logger_config:exist(Tid,Id) of
- true ->
- {error,{already_exist,Id}};
- false ->
- %% inform the handler
- case call_h(Module,adding_handler,[Id,HConfig],{ok,HConfig}) of
- {ok,HConfig1} ->
- %% We know that the call_h would have loaded the module
- %% if it existed, so it is safe here to call function_exported
- %% to find out if this is a valid handler
- case erlang:function_exported(Module, log, 2) of
- true ->
- logger_config:create(Tid,Id,Module,HConfig1),
- {ok,Config} = do_get_config(Tid,logger),
- Handlers = maps:get(handlers,Config,[]),
- do_set_config(Tid,logger,
- Config#{handlers=>[Id|Handlers]});
- false ->
- {error,{invalid_handler,
- {function_not_exported,
- {Module,log,2}}}}
- end;
- {error,HReason} ->
- {error,{handler_not_added,HReason}}
- end
- end,
- {reply,Reply,State};
-handle_call({remove_handler,HandlerId}, _From, #state{tid=Tid}=State) ->
- Reply =
- case logger_config:get(Tid,HandlerId) of
- {ok,{Module,_}} ->
- {ok,Config} = do_get_config(Tid,logger),
- Handlers0 = maps:get(handlers,Config,[]),
- Handlers = lists:delete(HandlerId,Handlers0),
- %% inform the handler
- _ = call_h(Module,removing_handler,[HandlerId,Config],ok),
- do_set_config(Tid,logger,Config#{handlers=>Handlers}),
- logger_config:delete(Tid,HandlerId),
- ok;
- _ ->
- {error,{not_found,HandlerId}}
- end,
- {reply,Reply,State};
+ {ok, #state{tid=Tid, async_req_queue = queue:new()}}.
+
+handle_call({add_handler,Id,Module,HConfig}, From, #state{tid=Tid}=State) ->
+ case logger_config:exist(Tid,Id) of
+ true ->
+ {reply,{error,{already_exist,Id}},State};
+ false ->
+ call_h_async(
+ fun() ->
+ %% inform the handler
+ call_h(Module,adding_handler,[Id,HConfig],{ok,HConfig})
+ end,
+ fun({ok,HConfig1}) ->
+ %% We know that the call_h would have loaded the module
+ %% if it existed, so it is safe here to call function_exported
+ %% to find out if this is a valid handler
+ case erlang:function_exported(Module, log, 2) of
+ true ->
+ logger_config:create(Tid,Id,Module,HConfig1),
+ {ok,Config} = do_get_config(Tid,logger),
+ Handlers = maps:get(handlers,Config,[]),
+ do_set_config(Tid,logger,
+ Config#{handlers=>[Id|Handlers]});
+ false ->
+ {error,{invalid_handler,
+ {function_not_exported,
+ {Module,log,2}}}}
+ end;
+ ({error,HReason}) ->
+ {error,{handler_not_added,HReason}}
+ end,From,State)
+ end;
+handle_call({remove_handler,HandlerId}, From, #state{tid=Tid}=State) ->
+ case logger_config:get(Tid,HandlerId) of
+ {ok,{Module,HConfig}} ->
+ {ok,Config} = do_get_config(Tid,logger),
+ Handlers0 = maps:get(handlers,Config,[]),
+ Handlers = lists:delete(HandlerId,Handlers0),
+ call_h_async(
+ fun() ->
+ %% inform the handler
+ call_h(Module,removing_handler,[HandlerId,HConfig],ok)
+ end,
+ fun(_Res) ->
+ do_set_config(Tid,logger,Config#{handlers=>Handlers}),
+ logger_config:delete(Tid,HandlerId),
+ ok
+ end,From,State);
+ _ ->
+ {reply,{error,{not_found,HandlerId}},State}
+ end;
handle_call({add_filter,Id,Filter}, _From,#state{tid=Tid}=State) ->
Reply = do_add_filter(Tid,Id,Filter),
{reply,Reply,State};
handle_call({remove_filter,Id,FilterId}, _From, #state{tid=Tid}=State) ->
Reply = do_remove_filter(Tid,Id,FilterId),
{reply,Reply,State};
-handle_call({update_config,Id,NewConfig}, _From, #state{tid=Tid}=State) ->
- Reply =
- case logger_config:get(Tid,Id) of
- {ok,{Module,OldConfig}} ->
- Config = maps:merge(OldConfig,NewConfig),
- case call_h(Module,changing_config,[Id,OldConfig,Config],
- {ok,Config}) of
- {ok,Config1} ->
- do_set_config(Tid,Id,Config1);
- Error ->
- Error
- end;
- {ok,OldConfig} ->
- Config = maps:merge(OldConfig,NewConfig),
- do_set_config(Tid,Id,Config);
- Error ->
- Error
- end,
- {reply,Reply,State};
+handle_call({update_config,Id,NewConfig}, From, #state{tid=Tid}=State) ->
+ case logger_config:get(Tid,Id) of
+ {ok,{_Module,OldConfig}} ->
+ Config = maps:merge(OldConfig,NewConfig),
+ handle_call({set_config,Id,Config}, From, State);
+ {ok,OldConfig} ->
+ Config = maps:merge(OldConfig,NewConfig),
+ {reply,do_set_config(Tid,Id,Config),State};
+ Error ->
+ {reply,Error,State}
+ end;
handle_call({set_config,logger,Config}, _From, #state{tid=Tid}=State) ->
Reply = do_set_config(Tid,logger,Config),
{reply,Reply,State};
-handle_call({set_config,HandlerId,Config}, _From, #state{tid=Tid}=State) ->
- Reply =
- case logger_config:get(Tid,HandlerId) of
- {ok,{Module,OldConfig}} ->
- case call_h(Module,changing_config,[HandlerId,OldConfig,Config],
- {ok,Config}) of
- {ok,Config1} ->
- do_set_config(Tid,HandlerId,Config1);
- Error ->
- Error
- end;
- _ ->
- {error,{not_found,HandlerId}}
- end,
- {reply,Reply,State};
+handle_call({set_config,HandlerId,Config}, From, #state{tid=Tid}=State) ->
+ case logger_config:get(Tid,HandlerId) of
+ {ok,{Module,OldConfig}} ->
+ call_h_async(
+ fun() ->
+ call_h(Module,changing_config,[HandlerId,OldConfig,Config],
+ {ok,Config})
+ end,
+ fun({ok,Config1}) ->
+ do_set_config(Tid,HandlerId,Config1);
+ (Error) ->
+ Error
+ end,From,State);
+ _ ->
+ {reply,{error,{not_found,HandlerId}},State}
+ end;
handle_call({set_module_level,Module,Level}, _From, #state{tid=Tid}=State) ->
Reply = logger_config:set_module_level(Tid,Module,Level),
{reply,Reply,State};
@@ -224,6 +222,8 @@ handle_call({reset_module_level,Module}, _From, #state{tid=Tid}=State) ->
Reply = logger_config:reset_module_level(Tid,Module),
{reply,Reply,State}.
+handle_cast({async_req_reply,_Ref,_Reply} = Reply,State) ->
+ call_h_reply(Reply,State);
handle_cast({cache_module_level,Module}, #state{tid=Tid}=State) ->
logger_config:cache_module_level(Tid,Module),
{noreply, State}.
@@ -243,11 +243,21 @@ handle_info({log,Level,Report,Meta}, State) ->
handle_info({Ref,_Reply},State) when is_reference(Ref) ->
%% Assuming this is a timed-out gen_server reply - ignoring
{noreply, State};
-handle_info(Unexpected,State) ->
+handle_info({'DOWN',_Ref,_Proc,_Pid,_Reason} = Down,State) ->
+ call_h_reply(Down,State);
+handle_info(Unexpected,State) when element(1,Unexpected) == 'EXIT' ->
+ %% The simple logger will send an 'EXIT' message when it is replaced
+ %% We may as well ignore all 'EXIT' messages that we get
?LOG_INTERNAL(debug,
[{logger,got_unexpected_message},
{process,?SERVER},
{message,Unexpected}]),
+ {noreply,State};
+handle_info(Unexpected,State) ->
+ ?LOG_INTERNAL(info,
+ [{logger,got_unexpected_message},
+ {process,?SERVER},
+ {message,Unexpected}]),
{noreply,State}.
terminate(_Reason, _State) ->
@@ -257,8 +267,11 @@ terminate(_Reason, _State) ->
%%% Internal functions
%%%===================================================================
call(Request) ->
- case whereis(?SERVER) of
- Pid when Pid==self() ->
+ Action = element(1,Request),
+ case get(?LOGGER_SERVER_TAG) of
+ true when
+ Action == add_handler; Action == remove_handler;
+ Action == update_config; Action == set_config ->
{error,{attempting_syncronous_call_to_self,Request}};
_ ->
gen_server:call(?SERVER,Request,?DEFAULT_LOGGER_CALL_TIMEOUT)
@@ -448,7 +461,59 @@ call_h(Module, Function, Args, DefRet) ->
{error,undef,[{Module,Function,Args,_}|_]} ->
DefRet;
_ ->
- {error,{callback_crashed,
- {C,R,logger:filter_stacktrace(?MODULE,S)}}}
+ ST = logger:filter_stacktrace(?MODULE,S),
+ ?LOG_INTERNAL(error,
+ [{logger,callback_crashed},
+ {process,?SERVER},
+ {reason,{C,R,ST}}]),
+ {error,{callback_crashed,{C,R,ST}}}
end
end.
+
+%% There are all sort of API functions that can cause deadlocks if called
+%% from the handler callbacks. So we spawn a process that does the request
+%% for the logger_server. There are still APIs that will cause problems,
+%% namely logger:add_handler
+call_h_async(AsyncFun,PostFun,From,#state{ async_req = undefined } = State) ->
+ Parent = self(),
+ {Pid, Ref} = spawn_monitor(
+ fun() ->
+ put(?LOGGER_SERVER_TAG,true),
+ receive Ref -> Ref end,
+ gen_server:cast(Parent, {async_req_reply, Ref, AsyncFun()})
+ end),
+ Pid ! Ref,
+ {noreply,State#state{ async_req = {Ref,PostFun,From} }};
+call_h_async(AsyncFun,PostFun,From,#state{ async_req_queue = Q } = State) ->
+ {noreply,State#state{ async_req_queue = queue:in({AsyncFun,PostFun,From},Q) }}.
+
+call_h_reply({async_req_reply,Ref,Reply},
+ #state{ async_req = {Ref,PostFun,From}, async_req_queue = Q} = State) ->
+ erlang:demonitor(Ref,[flush]),
+ _ = gen_server:reply(From, PostFun(Reply)),
+ {Value,NewQ} = queue:out(Q),
+ NewState = State#state{ async_req = undefined,
+ async_req_queue = NewQ },
+ case Value of
+ {value,{AsyncFun,NPostFun,NFrom}} ->
+ call_h_async(AsyncFun,NPostFun,NFrom,NewState);
+ empty ->
+ {noreply,NewState}
+ end;
+call_h_reply({'DOWN',Ref,_Proc,Pid,Reason}, #state{ async_req = {Ref,_PostFun,_From}} = State) ->
+ %% This clause should only be triggered if someone explicitly sends an exit signal
+ %% to the spawned process. It is only here to make sure that the logger_server does
+ %% not deadlock if that happens.
+ ?LOG_INTERNAL(error,
+ [{logger,process_exited},
+ {process,Pid},
+ {reason,Reason}]),
+ call_h_reply(
+ {async_req_reply,Ref,{error,{logger_process_exited,Pid,Reason}}},
+ State);
+call_h_reply(Unexpected,State) ->
+ ?LOG_INTERNAL(info,
+ [{logger,got_unexpected_message},
+ {process,?SERVER},
+ {message,Unexpected}]),
+ {noreply,State}.
diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl
index 8a757f0078..8c5d4da70d 100644
--- a/lib/kernel/test/logger_SUITE.erl
+++ b/lib/kernel/test/logger_SUITE.erl
@@ -218,14 +218,17 @@ change_config(_Config) ->
{ok,{?MODULE,#{level:=info,filter_default:=stop}=C2}} =
logger:get_handler_config(h1),
false = maps:is_key(custom,C2),
- {error,fail} = logger:set_handler_config(h1,#{fail=>true}),
+ {error,fail} = logger:set_handler_config(h1,#{conf_call=>fun() -> {error,fail} end}),
{error,{attempting_syncronous_call_to_self,_}} =
logger:set_handler_config(
- h1,#{call=>fun() -> logger:set_module_level(?MODULE,debug) end}),
+ h1,#{conf_call=>fun() -> logger:set_handler_config(?MODULE,#{}) end}),
+ ok =
+ logger:set_handler_config(
+ h1,#{conf_call=>fun() -> logger:set_module_level(?MODULE,debug) end}),
{ok,{?MODULE,C2}} = logger:get_handler_config(h1),
%% Change one key only
- {error,fail} = logger:set_handler_config(h1,fail,true),
+ {error,fail} = logger:set_handler_config(h1,conf_call,fun() -> {error,fail} end),
ok = logger:set_handler_config(h1,custom,custom),
[changing_config] = test_server:messages_get(),
{ok,{?MODULE,#{custom:=custom}=C3}} = logger:get_handler_config(h1),
@@ -425,6 +428,7 @@ filter_failed(cleanup,_Config) ->
ok.
handler_failed(_Config) ->
+ register(callback_receiver,self()),
{error,{invalid_id,1}} = logger:add_handler(1,?MODULE,#{}),
{error,{invalid_module,"nomodule"}} = logger:add_handler(h1,"nomodule",#{}),
{error,{invalid_handler_config,bad}} = logger:add_handler(h1,?MODULE,bad),
@@ -443,17 +447,58 @@ handler_failed(_Config) ->
false = lists:keymember(h1,1,H1),
{error,{not_found,h1}} = logger:remove_handler(h1),
- ok = logger:add_handler(h2,?MODULE,#{filter_default=>log,crash=>true}),
+ ok = logger:add_handler(h2,?MODULE,#{filter_default=>log,log_call=>fun() -> a = b end}),
{error,{already_exist,h2}} = logger:add_handler(h2,othermodule,#{}),
+ [add] = test_server:messages_get(),
logger:info(?map_rep),
- check_no_log(),
+ [remove] = test_server:messages_get(),
+
#{logger:=#{handlers:=Ids2},
handlers:=H2} = logger:i(),
false = lists:member(h2,Ids2),
false = lists:keymember(h2,1,H2),
{error,{not_found,h2}} = logger:remove_handler(h2),
+ CallAddHandler = fun() -> logger:add_handler(h2,?MODULE,#{}) end,
+ CrashHandler = fun() -> a = b end,
+ KillHandler = fun() -> exit(self(), die) end,
+
+ {error,{handler_not_added,{attempting_syncronous_call_to_self,_}}} =
+ logger:add_handler(h1,?MODULE,#{add_call=>CallAddHandler}),
+ {error,{handler_not_added,{callback_crashed,_}}} =
+ logger:add_handler(h1,?MODULE,#{add_call=>CrashHandler}),
+ {error,{handler_not_added,{logger_process_exited,_,die}}} =
+ logger:add_handler(h1,?MODULE,#{add_call=>KillHandler}),
+
+ check_no_log(),
+ ok = logger:add_handler(h1,?MODULE,#{}),
+ {error,{attempting_syncronous_call_to_self,_}} =
+ logger:set_handler_config(h1,#{conf_call=>CallAddHandler}),
+ {error,{callback_crashed,_}} =
+ logger:set_handler_config(h1,#{conf_call=>CrashHandler}),
+ {error,{logger_process_exited,_,die}} =
+ logger:set_handler_config(h1,#{conf_call=>KillHandler}),
+
+ {error,{attempting_syncronous_call_to_self,_}} =
+ logger:set_handler_config(h1,conf_call,CallAddHandler),
+ {error,{callback_crashed,_}} =
+ logger:set_handler_config(h1,conf_call,CrashHandler),
+ {error,{logger_process_exited,_,die}} =
+ logger:set_handler_config(h1,conf_call,KillHandler),
+
+ ok = logger:remove_handler(h1),
+ [add,remove] = test_server:messages_get(),
+
+ check_no_log(),
+ ok = logger:add_handler(h1,?MODULE,#{rem_call=>CallAddHandler}),
+ ok = logger:remove_handler(h1),
+ ok = logger:add_handler(h1,?MODULE,#{rem_call=>CrashHandler}),
+ ok = logger:remove_handler(h1),
+ ok = logger:add_handler(h1,?MODULE,#{rem_call=>KillHandler}),
+ ok = logger:remove_handler(h1),
+ [add,add,add] = test_server:messages_get(),
+
ok.
handler_failed(cleanup,_Config) ->
@@ -720,16 +765,19 @@ check_maps(Expected,Got,What) ->
end.
%% Handler
+adding_handler(_Id,#{add_call:=Fun}) ->
+ Fun();
adding_handler(_Id,Config) ->
maybe_send(add),
{ok,Config}.
+
+removing_handler(_Id,#{rem_call:=Fun}) ->
+ Fun();
removing_handler(_Id,_Config) ->
maybe_send(remove),
ok.
-changing_config(_Id,_Old,#{call:=Fun}) ->
+changing_config(_Id,_Old,#{conf_call:=Fun}) ->
Fun();
-changing_config(_Id,_Old,#{fail:=true}) ->
- {error,fail};
changing_config(_Id,_Old,Config) ->
maybe_send(changing_config),
{ok,Config}.
@@ -740,8 +788,8 @@ maybe_send(Msg) ->
Pid -> Pid ! Msg
end.
-log(_Log,#{crash:=true}) ->
- a=b;
+log(_Log,#{log_call:=Fun}) ->
+ Fun();
log(Log,Config) ->
TcProc = maps:get(tc_proc,Config,self()),
TcProc ! {Log,Config},
diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl
index e976a10b21..764f443634 100644
--- a/lib/kernel/test/logger_env_var_SUITE.erl
+++ b/lib/kernel/test/logger_env_var_SUITE.erl
@@ -58,7 +58,8 @@ groups() ->
logger_disk_log_formatter,
logger_undefined,
logger_many_handlers_default_first,
- logger_many_handlers_default_last
+ logger_many_handlers_default_last,
+ logger_many_handlers_default_last_broken_filter
]},
{bad,[],[bad_error_logger,
bad_level,
@@ -470,6 +471,8 @@ logger_undefined(Config) ->
false = lists:keymember(sasl_h,1,Hs),
ok.
+
+%% Test that we can add multiple handlers with the default first
logger_many_handlers_default_first(Config) ->
LogErr = file(Config,logger_many_handlers_default_first_error),
LogInfo = file(Config,logger_many_handlers_default_first_info),
@@ -508,6 +511,28 @@ logger_many_handlers_default_last(Config) ->
}
]}], LogErr, LogInfo, 7).
+%% Check that we can handle that an added logger has a broken filter
+%% This used to cause a deadlock.
+logger_many_handlers_default_last_broken_filter(Config) ->
+ LogErr = file(Config,logger_many_handlers_default_first_broken_filter_error),
+ LogInfo = file(Config,logger_many_handlers_default_first_broken_filter_info),
+
+ logger_many_handlers(
+ Config,[{logger,
+ [{handler,info,logger_std_h,
+ #{level=>info,
+ filters=>[{broken,{fun logger_filters:level/2,broken_state}},
+ {level,{fun logger_filters:level/2,{stop,gteq,error}}}],
+ logger_std_h=>#{type=>{file,LogInfo}}}
+ },
+ {handler,?STANDARD_HANDLER,logger_std_h,
+ #{level=>error,
+ filters=>[],
+ formatter=>{logger_formatter,#{}},
+ logger_std_h=>#{type=>{file,LogErr}}}
+ }
+ ]}], LogErr, LogInfo, 7).
+
logger_many_handlers(Config, Env, LogErr, LogInfo, NumProgress) ->
{ok,#{handlers:=Hs},Node} = setup(Config,Env),
check_single_log(Node,LogErr,
--
cgit v1.2.3
From 92d93f85e88235f3fa757d300628d382d09c6226 Mon Sep 17 00:00:00 2001
From: Siri Hansen
Date: Wed, 9 May 2018 14:28:44 +0200
Subject: Fix some link errors in logger documentation
---
lib/kernel/doc/src/error_logger.xml | 18 +++++++++++-------
lib/kernel/doc/src/kernel_app.xml | 7 +++----
lib/kernel/doc/src/logger_chapter.xml | 10 +++++-----
lib/sasl/doc/src/sasl_app.xml | 12 ++++--------
4 files changed, 23 insertions(+), 24 deletions(-)
(limited to 'lib')
diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml
index cb6165c73e..c9fe9484e4 100644
--- a/lib/kernel/doc/src/error_logger.xml
+++ b/lib/kernel/doc/src/error_logger.xml
@@ -181,17 +181,21 @@ ok
Get the value of the Kernel application variable
- logger_format_depth.
+ error_logger_format_depth.
Returns max(10, Depth), where Depth is the
- value of
-
- logger_format_depth
+ value of error_logger_format_depth
in the Kernel application, if Depth is an integer. Otherwise,
unlimited is returned.
- For backwards compatibility, the value
- of error_logger_format_depth is used
- if logger_format_depth is not set.
+
+ The error_logger_format_depth variable
+ is
+ deprecated since
+ the Logger API was
+ introduced in OTP-21. The variable, and this function, are
+ kept for backwards compatibility since they still might be
+ used by legacy report handlers.
+
diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml
index ebc7962f29..7cd05dab14 100644
--- a/lib/kernel/doc/src/kernel_app.xml
+++ b/lib/kernel/doc/src/kernel_app.xml
@@ -181,11 +181,10 @@
This parameter specifies which log levels to log. The
specified level, and all levels that are more severe, will
be logged.
- It is possible to change this variable at run-time
- using
- logger:set_logger_config(#{ level => error }).
- .
The default value is info.
+ To change the global log level at run-time, use
+
+ logger:set_logger_config(level, error).