aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel')
-rw-r--r--lib/kernel/doc/src/config.xml4
-rw-r--r--lib/kernel/doc/src/kernel_app.xml141
-rw-r--r--lib/kernel/doc/src/logger.xml103
-rw-r--r--lib/kernel/doc/src/logger_chapter.xml101
-rw-r--r--lib/kernel/doc/src/logger_disk_log_h.xml8
-rw-r--r--lib/kernel/doc/src/logger_formatter.xml2
-rw-r--r--lib/kernel/doc/src/logger_std_h.xml11
-rw-r--r--lib/kernel/doc/src/ref_man.xml4
-rw-r--r--lib/kernel/src/file.erl2
-rw-r--r--lib/kernel/src/kernel.app.src5
-rw-r--r--lib/kernel/src/kernel.erl14
-rw-r--r--lib/kernel/src/logger.erl271
-rw-r--r--lib/kernel/src/logger_config.erl2
-rw-r--r--lib/kernel/src/logger_internal.hrl2
-rw-r--r--lib/kernel/src/logger_server.erl24
-rw-r--r--lib/kernel/src/logger_simple.erl69
-rw-r--r--lib/kernel/test/Makefile1
-rw-r--r--lib/kernel/test/logger_SUITE.erl12
-rw-r--r--lib/kernel/test/logger_disk_log_h_SUITE.erl28
-rw-r--r--lib/kernel/test/logger_env_var_SUITE.erl790
-rw-r--r--lib/kernel/test/logger_formatter_SUITE.erl4
-rw-r--r--lib/kernel/test/logger_simple_SUITE.erl203
-rw-r--r--lib/kernel/test/logger_std_h_SUITE.erl59
-rw-r--r--lib/kernel/test/logger_test_lib.erl82
24 files changed, 1155 insertions, 787 deletions
diff --git a/lib/kernel/doc/src/config.xml b/lib/kernel/doc/src/config.xml
index fdb2d29f63..8850c1736b 100644
--- a/lib/kernel/doc/src/config.xml
+++ b/lib/kernel/doc/src/config.xml
@@ -37,10 +37,10 @@
data in the system configuration file <c>Name.config</c>.</p>
<p>Configuration parameter values in the configuration file
override the values in the application resource files (see
- <seealso marker="app"><c>app(4)</c></seealso>.
+ <seealso marker="app"><c>app(4)</c></seealso>).
The values in the configuration file can be
overridden by command-line flags (see
- <seealso marker="erts:erl"><c>erts:erl(1)</c></seealso>.</p>
+ <seealso marker="erts:erl"><c>erts:erl(1)</c></seealso>).</p>
<p>The value of a configuration parameter is retrieved by calling
<c>application:get_env/1,2</c>.</p>
</description>
diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml
index 7894600c21..ebc7962f29 100644
--- a/lib/kernel/doc/src/kernel_app.xml
+++ b/lib/kernel/doc/src/kernel_app.xml
@@ -165,78 +165,34 @@
<p>Permissions are described in
<seealso marker="application#permit/2"><c>application:permit/2</c></seealso>.</p>
</item>
- <tag><c>logger_dest = Value</c></tag>
+ <tag><marker id="logger"/><c>logger = [Config]</c></tag>
<item>
- <p><c>Value</c> is one of:</p>
- <taglist>
- <tag><c>tty</c></tag>
- <item><p>Installs the standard handler, <seealso marker="logger_std_h">
- <c>logger_std_h(3)</c></seealso>, with <c>type</c> set
- to <c>standard_io</c>. This is the default
- option.</p></item>
- <tag><c>{file, FileName}</c></tag>
- <item><p>Installs the standard handler, <seealso marker="logger_std_h">
- <c>logger_std_h(3)</c></seealso>, with <c>type</c> set
- to <c>{file, FileName}</c>, where <c>FileName</c>
- is a string. The file is opened with encoding UTF-8.</p></item>
- <tag><c>{disk_log, FileName}</c></tag>
- <item><p>Installs the disk_log handler, <seealso marker="logger_disk_log_h">
- <c>logger_disk_log_h(3)</c></seealso>, with <c>file</c> set
- to <c>FileName</c> (a string), and possibly other disk_log
- parameters set by the environment variables
- <c>logger_disk_log_type</c>, <c>logger_disk_log_maxfiles</c> and
- <c>logger_disk_log_maxbytes</c>,
- see <seealso marker="#disk_log_vars">below</seealso>. The
- file is opened with encoding UTF-8.</p></item>
- <tag><c>false</c></tag>
- <item>
- <p>No standard handler is installed, but
- the initial, primitive handler is kept, printing
- raw event messages to <c>tty</c>.</p>
- </item>
- <tag><c>silent</c></tag>
- <item>
- <p>No standard handler is started, and the initial,
- primitive handler is removed.</p>
- </item>
- </taglist>
+ <p>Specifies how <seealso marker="logger"><c>logger</c></seealso> should be
+ configured.</p>
+ <p>For more details and examples, see the <seealso marker="logger_chapter#logger">
+ Configuration</seealso> section in the <seealso marker="logger_chapter">
+ Logger User's Guide</seealso>.
+ </p>
</item>
- <tag><c>logger_level = Level</c></tag>
+ <tag><marker id="logger_level"/><c>logger_level = Level</c></tag>
<item>
- <p><c>Value = emergency | alert | critical | error | warning |
+ <p><c>Level = emergency | alert | critical | error | warning |
notice | info | debug</c></p>
<p>This parameter specifies which log levels to log. The
specified level, and all levels that are more severe, will
be logged.</p>
- <p>This configuration parameter is used both for the global
- logger level, and for the standard handler started by
- the Kernel application (see <c>logger_dest</c> variable above).</p>
+ <p>It is possible to change this variable at run-time
+ using <seealso marker="logger:set_logger_config/1">
+ <c>logger:set_logger_config(#{ level => error })</c></seealso>.
+ .</p>
<p>The default value is <c>info</c>.</p>
</item>
- <tag><marker id="disk_log_vars"/>
- <c>logger_disk_log_type = halt | wrap</c></tag>
- <item/>
- <tag><c>logger_disk_log_maxfiles = integer()</c></tag>
- <item/>
- <tag><c>logger_disk_log_maxbytes = integer()</c></tag>
- <item>
- <p>If <c>logger_dest</c> is set to {disk_log,File}, then these
- parameters specify the configuration to use when opening the
- disk log file. They specify the type of disk log, the
- maximum number of files (if the type is wrap) and the
- maximum size of each file, respectively.</p>
- <p>The default values are:</p>
- <code>
-logger_disk_log_type = wrap
-logger_disk_log_maxfiles = 10
-logger_disk_log_maxbytes = 1048576</code>
- </item>
<tag><marker id="logger_sasl_compatible"/>
<c>logger_sasl_compatible = boolean()</c></tag>
<item>
- <p>If this parameter is set to true, then the logger handler
- started by kernel will not log any progress-, crash-, or
- supervisor reports. If the SASL application is started,
+ <p>If this parameter is set to true, then the <c>default</c> logger handler
+ will not log any progress-, crash-, or supervisor reports.
+ If the SASL application is started,
these log events will be sent to a second handler instance
named <c>sasl_h</c>, according to values of the SASL
environment variables <c>sasl_error_logger</c>
@@ -247,6 +203,8 @@ logger_disk_log_maxbytes = 1048576</code>
<p>See chapter <seealso marker="logger_chapter#compatibility">Backwards
compatibility with error_logger</seealso> for more
information about handling of the so called SASL reports.</p>
+ <note><p>This configuration option only effects the <c>default</c>
+ and <c>sasl</c> handler. Any other handlers are uneffected.</p></note>
</item>
<tag><marker id="logger_log_progress"/>
<c>logger_log_progress = boolean()</c></tag>
@@ -254,51 +212,13 @@ logger_disk_log_maxbytes = 1048576</code>
<p>If <c>logger_sasl_compatible = false</c>,
then <c>logger_log_progress</c> specifies if progress
reports from <c>supervisor</c>
- and <c>application_controller</c> shall be logged or
- not.</p>
+ and <c>application_controller</c> shall be logged by the
+ default logger.</p>
<p>If <c>logger_sasl_compatible = true</c>,
then <c>logger_log_progress</c> is ignored.</p>
- </item>
- <tag><marker id="logger_format_depth"/>
- <c>logger_format_depth = Depth</c></tag>
- <item>
- <p>Can be used to limit the size of the
- formatted output from the logger handlers.</p>
-
- <p><c>Depth</c> is a positive integer representing the maximum
- depth to which terms are printed by the logger
- handlers included in OTP. This
- configuration parameter is used by the default formatter,
- <seealso marker="logger_formatter"><c>logger_formatter(3)</c></seealso>,
- unless the formatter's <c>depth</c> parameter is explicitly set.
- (If you have implemented your own formatter, this configuration
- parameter has no effect on that.)</p>
-
- <p><c>Depth</c> is used as follows: Format strings
- received by the formatter are rewritten.
- The format controls <c>~p</c> and <c>~w</c> are replaced with
- <c>~P</c> and <c>~W</c>, respectively, and <c>Depth</c> is
- used as the depth parameter. For details, see
- <seealso marker="stdlib:io#format/2"><c>io:format/2</c></seealso>
- in STDLIB.</p>
-
- <note><p>A reasonable starting value for <c>Depth</c> is
- <c>30</c>. We recommend to test crashing various processes in your
- application, examine the logs from the crashes, and then
- increase or decrease the value.</p></note>
- </item>
- <tag><c>logger_max_size = integer() | unlimited</c></tag>
- <item>
- <p>This parameter specifies a hard maximum size limit (number
- of characters) each log event can have when printed by the
- default logger formatter. If the resulting string after
- formatting an event is bigger than this, it will be
- truncated before printed to the handler's destination.</p>
- </item>
- <tag><c>logger_utc = boolean()</c></tag>
- <item>
- <p>If set to <c>true</c>, the default formatter will display
- all dates in Universal Coordinated Time.</p>
+ <p>The default value is <c>false</c></p>
+ <note><p>This configuration option only effects the <c>default</c>
+ and <c>sasl</c> handler. Any other handlers are uneffected.</p></note>
</item>
<tag><c>global_groups = [GroupTuple]</c></tag>
<item>
@@ -572,9 +492,20 @@ MaxT = TickTime + TickTime / 4</code>
variables are not set.</p>
<taglist>
<tag><c>error_logger</c></tag>
- <item>Replaced by <c>logger_dest</c></item>
+ <item>Replaced by setting the type of the default
+ <seealso marker="logger_std_h#type"><c>logger_std_h</c></seealso>
+ to the same value. Example:
+ <code type="none">
+erl -kernel logger '[{handler,default,logger_std_h,#{logger_std_h=>#{type=>{file,"/tmp/erlang.log"}}}}]'
+ </code>
+ </item>
<tag><c>error_logger_format_depth</c></tag>
- <item>Replaced by <c>logger_format_depth</c></item>
+ <item>Replaced by setting the <seealso marker="logger_formatter#depth"><c>depth</c></seealso>
+ parameter of the default handlers formatter. Example:
+ <code type="none">
+erl -kernel logger '[{handler,default,logger_std_h,#{formatter=>{logger_formatter,#{legacy_header=>true,template=>[{logger_formatter,header},"\n",msg,"\n"],depth=>10}}}]'
+ </code>
+ </item>
</taglist>
<p>See <seealso marker="logger_chapter#compatibility">Backwards
compatibility with error_logger</seealso> for more
diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml
index d901454e62..239b9553b1 100644
--- a/lib/kernel/doc/src/logger.xml
+++ b/lib/kernel/doc/src/logger.xml
@@ -33,10 +33,49 @@
<file>logger.xml</file>
</header>
<module>logger</module>
- <modulesummary>API module for the logger application.</modulesummary>
+ <modulesummary>API module for the logger.</modulesummary>
<description>
-
+ <p>
+ This module is the main logger API. It contains functions that allow
+ application to use a single log API and the system to manage those log
+ events independently. To log events the logger
+ <seealso marker="#macros">macros</seealso> should be used. For instance,
+ to log a new error log event:</p>
+ <code>
+?LOG_ERROR("error happened because: ~p",[Reason]). %% With macro
+logger:error("error happened because: ~p",[Reason]). %% Without macro
+ </code>
+ <p>This log event will then be sent to the configured log handlers which
+ by default means that it will be printed to the console. If you want
+ your systems logs to be printed to a file instead of the console you
+ have to configure the default handler to do so. The simplest way is
+ to include the following in your <seealso marker="config"><c>sys.config</c></seealso>.</p>
+ <code>
+[{kernel,
+ [{logger,
+ [{handler,default,logger_std_h,
+ #{logger_std_h=>#{type=>{file,"path/to/file.log"}}}}]}]}].
+ </code>
+ <p>
+ For more information about:
+ </p>
+ <list type="bulleted">
+ <item>how to use the API,
+ see <seealso marker="logger_chapter">the User's Guide</seealso>.</item>
+ <item>how to configure logger,
+ see the <seealso marker="logger_chapter#configuration">Configuration</seealso>
+ section in the User's Guide.</item>
+ <item>the convinience macros in logger.hrl,
+ see <seealso marker="#macros">the macro section</seealso>.</item>
+ <item>what the builtin formatter can do,
+ see <seealso marker="logger_formatter">logger_formatter</seealso>.</item>
+ <item>what the builtin handlers can do,
+ see <seealso marker="logger_std_h">logger_std_h</seealso> and
+ <seealso marker="logger_disk_log_h">logger_disk_log_h</seealso>.</item>
+ <item>what builtin filters are available,
+ see <seealso marker="logger_filters">logger_filters</seealso>.</item>
+ </list>
</description>
<datatypes>
@@ -348,16 +387,16 @@
<code><![CDATA[1> logger:i(print).
Current logger configuration:
Level: info
- FilterDefault: log
+ Filter Default: log
Filters:
Handlers:
- Id: logger_std_h
+ Id: default
Module: logger_std_h
Level: info
Formatter:
Module: logger_formatter
- Config: #{template => [{logger_formatter,header},"\n",msg,"\n"],
- legacy_header => true}
+ Config: #{legacy_header => true,single_line => false,
+ template => [{logger_formatter,header},"\n",msg,"\n"]}
Filter Default: stop
Filters:
Id: stop_progress
@@ -550,6 +589,56 @@ Current logger configuration:
</func>
<func>
+ <name name="add_handlers" arity="1" clause_i="1"/>
+ <fsummary>Setup logger handlers from the applications configuration parameters.</fsummary>
+ <desc>
+ <p>Reads the application configuration parameter <c>logger</c> and
+ calls <c>logger:add_handlers/1</c> with it contents.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="add_handlers" arity="1" clause_i="2"/>
+ <fsummary>Setup logger handlers.</fsummary>
+ <type name="config_handler"/>
+ <desc>
+ <p>This function should be used by custom logger handlers to make
+ configuration consistent no matter which handler the system uses.
+ Normal usage to to add a call to <c>logger:add_handlers/1</c>
+ just after the processes that the handler needs are started
+ and pass the applications logger config as an argument. Eg.</p>
+ <code>
+-behaviour(application).
+start(_, []) ->
+ case supervisor:start_link({local, my_sup}, my_sup, []) of
+ {ok, Pid} ->
+ ok = logger:add_handlers(my_app),
+ {ok, Pid, []};
+ Error -> Error
+ end.</code>
+ <p>This will read the <c>logger</c> configuration parameter from
+ the handler application and start the configured handlers. The contents
+ of the configuration use the same rules as the
+ <seealso marker="logger_chapter#handler-configuration">logger handler configuration</seealso>.
+ </p>
+ <p>If the handler is meant to replace the default handler the kernels
+ default handlers have to be disabled before the new handler is added.
+ A <c>sys.config</c> file that disables the kernel handler and adds
+ a custom handler could looks like this:</p>
+ <code>
+[{kernel,
+ [{logger,
+ %% Disable the default kernel handler
+ [{handler,default,undefined}]}]},
+ {my_app,
+ [{logger,
+ %% Enable this handler as the default
+ [{handler,default,my_handler,#{}}]}]}].
+ </code>
+ </desc>
+ </func>
+
+ <func>
<name name="set_logger_config" arity="1"/>
<fsummary>Set configuration data for the logger.</fsummary>
<desc>
@@ -650,7 +739,7 @@ Current logger configuration:
<p>If process metadata exists for the current process, this
function behaves as if it was implemented as follows:</p>
<code type="erl">
-logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta))
+logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)).
</code>
<p>If no process metadata exists, the function behaves as
<seealso marker="#set_process_metadata-1">
diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml
index 3150c5adb4..484358f392 100644
--- a/lib/kernel/doc/src/logger_chapter.xml
+++ b/lib/kernel/doc/src/logger_chapter.xml
@@ -248,11 +248,97 @@
<section>
<title>Configuration</title>
+ <p>Logger can be configured either when the system starts through
+ <seealso marker="config">configuration parameters</seealso>,
+ or at run-time by using the <seealso marker="logger">logger</seealso>
+ API. The recommended approach is to do the initial configuration in
+ the <c>sys.config</c> file and then use the API when some configuration
+ has to be changed at run-time, such as the logging level.</p>
+
<section>
- <title>Application environment variables</title>
- <p>See <seealso marker="kernel_app#configuration">Kernel(6)</seealso> for
- information about the application environment variables that can
- be used for configuring logger.</p>
+ <title>Application configuration parameters</title>
+ <p>Logger is best configured by using the configuration parameters
+ of kernel. There are three possible configuration parameters:
+ <seealso marker="#logger"><c>logger</c></seealso>,
+ <seealso marker="kernel_app#logger_level"><c>logger_level</c></seealso>,
+ <seealso marker="kernel_app#logger_sasl_compatible"><c>logger_sasl_compatible</c></seealso> and
+ <seealso marker="kernel_app#logger_log_progress"><c>logger_log_progress</c></seealso>.
+ logger_level, logger_sasl_compatible and logger_log_progress are described in the
+ <seealso marker="kernel_app#configuration">Kernel Configuration</seealso>,
+ while <c>logger</c> is described below.</p>
+ <section>
+ <marker id="logger"/>
+ <title>logger</title>
+ <p>The <c>logger</c> application configuration parameter is used to configure
+ three different logger aspects; handlers, logger filters and module levels.
+ The configuration is a list containing tagged tuples that look like this:</p>
+ <taglist>
+ <tag><c>DisableHandler = {handler,default,undefined}</c></tag>
+ <item>Disable the default handler. This will allow another application
+ to add its own default handler. See <seealso marker="logger#add_handlers/1">
+ <c>logger:add_handlers/1</c></seealso> for more details.</item>
+ <tag><c>AddHandler = {handler,HandlerId,Module,HandlerConfig}</c></tag>
+ <item>Add a handler as if <seealso marker="logger:add_handler/3">
+ <c>logger:add_handler(HandlerId,Module,HandlerConfig)</c></seealso> had been
+ called.</item>
+ <tag><c>Filters = {filters, FilterDefault, [Filter]}</c><br/>
+ <c>FilterDefault = log | stop</c><br/>
+ <c>Filter = {FilterId, {FilterFun, FilterConfig}}</c></tag>
+ <item>Add the specified <seealso marker="logger#add_logger_filter/2">
+ logger filters</seealso>. Only one entry is allowed of this option.</item>
+ <tag><c>ModuleLevel</c></tag>
+ <item><c>{module_level, Level, [Module]}</c>,
+ this option configures the <seealso marker="logger#set_module_level/2">
+ module log level</seealso> to be used. It is possible to have multiple
+ <c>module_level</c> entries.</item>
+ </taglist>
+ <p>Examples:</p>
+ <list>
+ <item>
+ <p>Output logs into a the file &quot;logs/erlang.log&quot;</p>
+ <code>
+[{kernel,
+ [{logger,
+ [{handler, default, logger_std_h,
+ #{ logger_std_h => #{ type => {file,"log/erlang.log"}}}}]}]}].
+ </code>
+ </item>
+ <item>
+ <p>Output logs in single line format</p>
+ <code>
+[{kernel,
+ [{logger,
+ [{handler, default, logger_std_h,
+ #{ formatter => { logger_formatter,#{ single_line => true}}}}]}]}].
+ </code>
+ </item>
+ <item>
+ <p>Add the pid to each log event</p>
+ <code>
+[{kernel,
+ [{logger,
+ [{handler, default, logger_std_h,
+ #{ formatter => { logger_formatter,
+ #{ template => [time," ",pid," ",msg,"\n"]}}
+ }}]}]}].
+ </code>
+ </item>
+ <item>
+ <p>Use a different file for debug logging</p>
+ <code>
+[{kernel,
+ [{logger,
+ [{handler, default, logger_std_h,
+ #{ level => error,
+ logger_std_h => #{ type => {file, "log/erlang.log"}}}},
+ {handler, info, logger_std_h,
+ #{ level => debug,
+ logger_std_h => #{ type => {file, "log/debug.log"}}}}
+ ]}]}].
+ </code>
+ </item>
+ </list>
+ </section>
</section>
<section>
@@ -330,6 +416,13 @@
<c>logger_formatter</c></seealso>, and <c>Extra</c> is
it's configuration map.</p>
</item>
+ <tag>HandlerConfig, <c>term() = term()</c></tag>
+ <item>
+ Any keys not listed above are considered to be handler specific
+ configuration. The configuration of the Kernel handlers can be found in
+ <seealso marker="logger_std_h"><c>logger_std_h</c></seealso> and
+ <seealso marker="logger_disk_log_h"><c>logger_disk_log_h</c></seealso>.
+ </item>
</taglist>
<p>Note that <c>level</c> and <c>filters</c> are obeyed by
diff --git a/lib/kernel/doc/src/logger_disk_log_h.xml b/lib/kernel/doc/src/logger_disk_log_h.xml
index 90cc4fec30..440ae28e5d 100644
--- a/lib/kernel/doc/src/logger_disk_log_h.xml
+++ b/lib/kernel/doc/src/logger_disk_log_h.xml
@@ -121,11 +121,11 @@ logger:add_handler(my_disk_log_h, logger_disk_log_h,
#{filesync_repeat_interval => 1000}}).
</code>
<p>In order to use the disk_log handler instead of the default standard
- handler when starting en Erlang node, use the kernel configuration parameter
- <seealso marker="kernel_app#configuration"><c>logger_dest</c></seealso> with
- value <c>{disk_log,FileName}</c>. Example:</p>
+ handler when starting en Erlang node, change the Kernel default logger to
+ use disk_log. Example:</p>
<code type="none">
-erl -kernel logger_dest '{disk_log,"./system_disk_log"}'
+erl -kernel logger '[{handler,default,logger_disk_log_h,
+ #{ disk_log_opts => #{ file => "./system_disk_log"}}}]'
</code>
</description>
diff --git a/lib/kernel/doc/src/logger_formatter.xml b/lib/kernel/doc/src/logger_formatter.xml
index 7df4c88f40..5f13e54365 100644
--- a/lib/kernel/doc/src/logger_formatter.xml
+++ b/lib/kernel/doc/src/logger_formatter.xml
@@ -66,7 +66,7 @@
be truncated by the <c>max_size</c> parameter.</p>
</note>
</item>
- <tag><c>depth = pos_integer() | unlimited</c></tag>
+ <tag><marker id="depth"/><c>depth = pos_integer() | unlimited</c></tag>
<item>
<p>A positive integer representing the maximum depth to
which terms shall be printed by this formatter. Format
diff --git a/lib/kernel/doc/src/logger_std_h.xml b/lib/kernel/doc/src/logger_std_h.xml
index fe9b9ca5a9..bf23d874c8 100644
--- a/lib/kernel/doc/src/logger_std_h.xml
+++ b/lib/kernel/doc/src/logger_std_h.xml
@@ -40,7 +40,7 @@
application. Multiple instances of this handler can be added to
logger, and each instance will print logs to <c>standard_io</c>,
<c>standard_error</c> or to file. The default instance that starts
- with kernel is named <c>logger_std_h</c> - which is the name to be used
+ with kernel is named <c>default</c> - which is the name to be used
for reconfiguration.</p>
<p>The handler has an overload protection mechanism that will keep the handler
process and the kernel application alive during a high load of log
@@ -57,7 +57,7 @@
are stored in a sub map with the key <c>logger_std_h</c>. The following
keys and values may be specified:</p>
<taglist>
- <tag><c>type</c></tag>
+ <tag><marker id="type"/><c>type</c></tag>
<item>
<p>This will have the value <c>standard_io</c>, <c>standard_error</c>,
<c>{file,LogFileName}</c>, or <c>{file,LogFileName,LogFileOpts}</c>,
@@ -105,11 +105,10 @@ logger:add_handler(my_standard_h, logger_std_h,
</code>
<p>In order to configure the default handler (that starts initially with
the kernel application) to log to file instead of <c>standard_io</c>,
- use the kernel configuration parameter
- <seealso marker="kernel_app#configuration"><c>logger_dest</c></seealso> with
- value <c>{file,FileName}</c>. Example:</p>
+ change the Kernel default logger to use a file. Example:</p>
<code type="none">
-erl -kernel logger_dest '{file,"./erl.log"}'
+erl -kernel logger '[{handler,default,logger_std_h,
+ #{ logger_std_h => #{ type => {file,"./log.log"}}}}]'
</code>
<p>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 @@
</description>
<xi:include href="kernel_app.xml"/>
+ <xi:include href="app.xml"/>
<xi:include href="application.xml"/>
<xi:include href="auth.xml"/>
<xi:include href="code.xml"/>
+ <xi:include href="config.xml"/>
<xi:include href="disk_log.xml"/>
<xi:include href="erl_boot_server.xml"/>
<xi:include href="erl_ddll.xml"/>
@@ -67,6 +69,4 @@
<xi:include href="user.xml"/>
<xi:include href="wrap_log_reader.xml"/>
<xi:include href="zlib_stub.xml"/>
- <xi:include href="app.xml"/>
- <xi:include href="config.xml"/>
</application>
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,37 +20,18 @@
-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
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))].