File 3791-feat-kernel-allow-reconfiguration-of-logger.patch of Package erlang
From f7916862465ad18eb1b71a17a714449a20f29733 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Niemier?= <lukasz@niemier.pl>
Date: Tue, 23 Mar 2021 12:07:48 +0100
Subject: [PATCH] feat(kernel): allow reconfiguration of logger
This adds function `logger:reconfigure/0` that allows to reset logger
configuration to what is configured in `kernel` application
configuration. This function is meant to be used by build/run tools like
Rebar3 or Mix to provide "production-like" way to configure logging in
reliable way.
This function is obviously not safe to be called during application
runtime, as it may cause losing some log messages, but it is by far
better solution than what we have now, where Rebar3 tries to reimplement
such functionality on their own (risking incompatibilities if `logger`
configuration will be expanded one day) or just ignore it, and requires
users to configure logging on their own (like Mix does, that just relies
that users will use Elixir's Logger). Lack of such functionality
prevents Elixir from further integration with Erlang's Logger.
Additionally this hides `logger:internal_init_logger/0` call, as
`logger:reconfigure/0` already does the same thing, just with additional
step to cleanup state before running initialization step, which in clean
startup is just no-op.
Co-authored-by: Mikko Ahlroth <mikko@ahlroth.fi>
Co-authored-by: Lukas Larsson <garazdawi@gmail.com>
---
lib/kernel/doc/src/logger.xml | 12 +++++++
lib/kernel/src/logger.erl | 30 ++++++++++++++--
lib/kernel/test/logger_SUITE.erl | 59 ++++++++++++--------------------
3 files changed, 62 insertions(+), 39 deletions(-)
diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml
index 1c17004aa0..68d1984c7f 100644
--- a/lib/kernel/doc/src/logger.xml
+++ b/lib/kernel/doc/src/logger.xml
@@ -1189,6 +1189,18 @@ logger:set_proxy_config(maps:merge(Old, Config)).
</desc>
</func>
+ <func>
+ <name name="reconfigure" arity="0" since=""/>
+ <fsummary>Reconfigure Logger.</fsummary>
+ <desc>
+ <p>Reconfigure Logger using updated <c>kernel</c> configuration
+ that was set after <c>kernel</c> application was loaded.</p>
+ <p>Beware, that this is meant to be run only by the build tools,
+ not manually during application lifetime, as this may cause
+ missing log entries.</p>
+ </desc>
+ </func>
+
</funcs>
diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl
index ea75c8d720..9107bf0e61 100644
--- a/lib/kernel/src/logger.erl
+++ b/lib/kernel/src/logger.erl
@@ -51,9 +51,9 @@
get_primary_config/0, get_handler_config/1,
get_handler_config/0, get_handler_ids/0, get_config/0,
get_proxy_config/0,
- add_handlers/1]).
+ add_handlers/1,
+ reconfigure/0]).
-%% Private configuration
-export([internal_init_logger/0]).
%% Misc
@@ -794,6 +794,31 @@ print_module_levels(Modules,M) ->
[print_module_levels(Module,M) || Module <- Modules],
ok.
+-spec reconfigure() -> ok | {error,term()}.
+%% This function is meant to be used by the build tools like Rebar3 or Mix
+%% to ensure that the logger configuration is reset to the expected state
+%% before running main application.
+reconfigure() ->
+ try
+ [case logger:remove_handler(Id) of
+ ok -> ok;
+ {error, Reason} -> throw({remove, Id, Reason})
+ end || #{id := Id} <- logger:get_handler_config()],
+ ok=logger:add_handler(simple,logger_simple_h,
+ #{filter_default=>stop,
+ filters=>?DEFAULT_HANDLER_FILTERS}),
+ logger:unset_module_level(),
+ internal_init_logger()
+ of
+ ok ->
+ logger:add_handlers(kernel);
+ Error ->
+ Error
+ catch
+ throw:Reason ->
+ {error, Reason}
+ end.
+
-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
@@ -807,6 +832,7 @@ internal_init_logger() ->
ok = logger:set_primary_config(metadata, get_primary_metadata()),
ok = logger:set_primary_config(filter_default,
get_primary_filter_default(Env)),
+ ok = logger:set_primary_config(filters, []),
[case logger:add_primary_filter(Id, Filter) of
ok -> ok;
diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl
index 82533ab572..5545759b5d 100644
--- a/lib/kernel/test/logger_SUITE.erl
+++ b/lib/kernel/test/logger_SUITE.erl
@@ -24,6 +24,7 @@
-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/logger.hrl").
-include_lib("kernel/src/logger_internal.hrl").
+-include_lib("stdlib/include/assert.hrl").
-define(str,"Log from "++atom_to_list(?FUNCTION_NAME)++
":"++integer_to_list(?LINE)).
@@ -1009,15 +1010,14 @@ app_config(Config) ->
%% start, it is not possible to see code coverage in that test.
kernel_config(Config) ->
%% Start a node with simple handler only, then simulate kernel
- %% start by calling internally exported
- %% internal_init_logger(). This is to test all variants of kernel
- %% config, including bad config, and see the code coverage.
+ %% start by calling logger:reconfigure(). This is to test all
+ %% variants of kernel config, including bad config, and see
+ %% the code coverage.
{ok,#{handlers:=[#{id:=simple,filters:=DF}]}=LC,Node} =
logger_test_lib:setup(Config,[{error_logger,false}]),
%% Same once more, to get coverage
ok = rpc:call(Node,logger,internal_init_logger,[]),
- ok = rpc:call(Node,logger,add_handlers,[kernel]),
LC = rpc:call(Node,logger,get_config,[]),
%% This shall mean the same as above, but using 'logger' parameter
@@ -1025,15 +1025,13 @@ kernel_config(Config) ->
ok = rpc:call(Node,application,unset_env,[kernel,error_logger]),
ok = rpc:call(Node,application,set_env,
[kernel,logger,[{handler,default,undefined}]]),
- ok = rpc:call(Node,logger,internal_init_logger,[]),
- ok = rpc:call(Node,logger,add_handlers,[kernel]),
- LC = rpc:call(Node,logger,get_config,[]),
+ ok = rpc:call(Node,logger,reconfigure,[]),
+ ?assertEqual(LC, rpc:call(Node,logger,get_config,[])),
%% Silent
ok = rpc:call(Node,application,unset_env,[kernel,logger]),
ok = rpc:call(Node,application,set_env,[kernel,error_logger,silent]),
- ok = rpc:call(Node,logger,internal_init_logger,[]),
- ok = rpc:call(Node,logger,add_handlers,[kernel]),
+ ok = rpc:call(Node,logger,reconfigure,[]),
#{primary:=#{filter_default:=log,filters:=[]},
handlers:=[],
module_levels:=[]} = rpc:call(Node,logger,get_config,[]),
@@ -1041,30 +1039,25 @@ kernel_config(Config) ->
%% Default
ok = rpc:call(Node,application,unset_env,[kernel,error_logger]),
ok = rpc:call(Node,application,unset_env,[kernel,logger]),
- ok = rpc:call(Node,logger,internal_init_logger,[]),
- ok = rpc:call(Node,logger,add_handlers,[kernel]),
+ ok = rpc:call(Node,logger,reconfigure,[]),
#{primary:=#{filter_default:=log,filters:=[]},
handlers:=[#{id:=default,filters:=DF,config:=#{type:=standard_io}}],
module_levels:=[]} = rpc:call(Node,logger,get_config,[]),
%% error_logger=tty (same as default)
- ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again
ok = rpc:call(Node,application,set_env,[kernel,error_logger,tty]),
ok = rpc:call(Node,application,unset_env,[kernel,logger]),
- ok = rpc:call(Node,logger,internal_init_logger,[]),
- ok = rpc:call(Node,logger,add_handlers,[kernel]),
+ ok = rpc:call(Node,logger,reconfigure,[]),
#{primary:=#{filter_default:=log,filters:=[]},
handlers:=[#{id:=default,filters:=DF,config:=#{type:=standard_io}}],
module_levels:=[]} = rpc:call(Node,logger,get_config,[]),
%% error_logger={file,File}
- ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again
F = filename:join(?config(priv_dir,Config),
atom_to_list(?FUNCTION_NAME)++".log"),
ok = rpc:call(Node,application,set_env,[kernel,error_logger,{file,F}]),
ok = rpc:call(Node,application,unset_env,[kernel,logger]),
- ok = rpc:call(Node,logger,internal_init_logger,[]),
- ok = rpc:call(Node,logger,add_handlers,[kernel]),
+ ok = rpc:call(Node,logger,reconfigure,[]),
#{primary:=#{filter_default:=log,filters:=[]},
handlers:=[#{id:=default,filters:=DF,
config:=#{type:=file,file:=F,modes:=Modes}}],
@@ -1073,55 +1066,47 @@ kernel_config(Config) ->
%% Same, but using 'logger' parameter instead of 'error_logger'
- ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again
ok = rpc:call(Node,application,unset_env,[kernel,error_logger]),
ok = rpc:call(Node,application,set_env,[kernel,logger,
[{handler,default,logger_std_h,
#{config=>#{type=>{file,F}}}}]]),
- ok = rpc:call(Node,logger,internal_init_logger,[]),
- ok = rpc:call(Node,logger,add_handlers,[kernel]),
+ ok = rpc:call(Node,logger,reconfigure,[]),
#{primary:=#{filter_default:=log,filters:=[]},
handlers:=[#{id:=default,filters:=DF,
config:=#{type:=file,file:=F,modes:=Modes}}],
module_levels:=[]} = rpc:call(Node,logger,get_config,[]),
%% Same, but with type={file,File,Modes}
- ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again
ok = rpc:call(Node,application,unset_env,[kernel,error_logger]),
M = [raw,write],
ok = rpc:call(Node,application,set_env,[kernel,logger,
[{handler,default,logger_std_h,
#{config=>#{type=>{file,F,M}}}}]]),
- ok = rpc:call(Node,logger,internal_init_logger,[]),
- ok = rpc:call(Node,logger,add_handlers,[kernel]),
+ ok = rpc:call(Node,logger,reconfigure,[]),
#{primary:=#{filter_default:=log,filters:=[]},
handlers:=[#{id:=default,filters:=DF,
config:=#{type:=file,file:=F,modes:=[delayed_write|M]}}],
module_levels:=[]} = rpc:call(Node,logger,get_config,[]),
%% Same, but with disk_log handler
- ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again
ok = rpc:call(Node,application,unset_env,[kernel,error_logger]),
ok = rpc:call(Node,application,set_env,[kernel,logger,
[{handler,default,logger_disk_log_h,
#{config=>#{file=>F}}}]]),
- ok = rpc:call(Node,logger,internal_init_logger,[]),
- ok = rpc:call(Node,logger,add_handlers,[kernel]),
+ ok = rpc:call(Node,logger,reconfigure,[]),
#{primary:=#{filter_default:=log,filters:=[]},
handlers:=[#{id:=default,filters:=DF,config:=#{file:=F}}],
module_levels:=[]} = rpc:call(Node,logger,get_config,[]),
%% Set primary filters and module level. No default handler.
- ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again
ok = rpc:call(Node,application,unset_env,[kernel,error_logger]),
ok = rpc:call(Node,application,set_env,
[kernel,logger,[{handler,default,undefined},
{filters,stop,[{f1,{fun(_,_) -> log end,ok}}]},
{module_level,debug,[?MODULE]}]]),
- ok = rpc:call(Node,logger,internal_init_logger,[]),
- ok = rpc:call(Node,logger,add_handlers,[kernel]),
+ ok = rpc:call(Node,logger,reconfigure,[]),
#{primary:=#{filter_default:=stop,filters:=[_]},
- handlers:=[],
+ handlers:=[#{id:=simple}],
module_levels:=[{?MODULE,debug}]} = rpc:call(Node,logger,get_config,[]),
%% Bad config
@@ -1129,38 +1114,38 @@ kernel_config(Config) ->
ok = rpc:call(Node,application,set_env,[kernel,error_logger,bad]),
{error,{bad_config,{kernel,{error_logger,bad}}}} =
- rpc:call(Node,logger,internal_init_logger,[]),
+ rpc:call(Node,logger,reconfigure,[]),
ok = rpc:call(Node,application,unset_env,[kernel,error_logger]),
ok = rpc:call(Node,application,set_env,[kernel,logger_level,bad]),
{error,{bad_config,{kernel,{logger_level,bad}}}} =
- rpc:call(Node,logger,internal_init_logger,[]),
+ rpc:call(Node,logger,reconfigure,[]),
ok = rpc:call(Node,application,unset_env,[kernel,logger_level]),
ok = rpc:call(Node,application,set_env,
[kernel,logger,[{filters,stop,[bad]}]]),
{error,{bad_config,{kernel,{invalid_filters,[bad]}}}} =
- rpc:call(Node,logger,internal_init_logger,[]),
+ rpc:call(Node,logger,reconfigure,[]),
ok = rpc:call(Node,application,set_env,
[kernel,logger,[{filters,stop,[bad]}]]),
{error,{bad_config,{kernel,{invalid_filters,[bad]}}}} =
- rpc:call(Node,logger,internal_init_logger,[]),
+ rpc:call(Node,logger,reconfigure,[]),
ok = rpc:call(Node,application,set_env,
[kernel,logger,[{filters,stop,[{f1,bad}]}]]),
{error,{bad_config,{kernel,{invalid_filter,{f1,bad}}}}} =
- rpc:call(Node,logger,internal_init_logger,[]),
+ rpc:call(Node,logger,reconfigure,[]),
ok = rpc:call(Node,application,set_env,
[kernel,logger,MF=[{filters,stop,[]},{filters,log,[]}]]),
{error,{bad_config,{kernel,{multiple_filters,MF}}}} =
- rpc:call(Node,logger,internal_init_logger,[]),
+ rpc:call(Node,logger,reconfigure,[]),
ok = rpc:call(Node,application,set_env,
[kernel,logger,[{module_level,bad,[?MODULE]}]]),
{error,{bad_config,{kernel,{invalid_level,bad}}}} =
- rpc:call(Node,logger,internal_init_logger,[]),
+ rpc:call(Node,logger,reconfigure,[]),
ok.
--
2.31.1