diff options
Diffstat (limited to 'lib')
33 files changed, 938 insertions, 370 deletions
diff --git a/lib/common_test/src/Makefile b/lib/common_test/src/Makefile index 2a2a9cb5bc..9adcf2f13b 100644 --- a/lib/common_test/src/Makefile +++ b/lib/common_test/src/Makefile @@ -166,4 +166,4 @@ release_tests_spec: opt release_docs_spec: docs # Include dependencies -- list below added by Kostis Sagonas -$(EBIN)/cth_log_redirect.beam: ../../kernel/include/logger.hrl +$(EBIN)/cth_log_redirect.beam: ../../kernel/include/logger.hrl ../../kernel/src/logger_internal.hrl diff --git a/lib/compiler/src/cerl_trees.erl b/lib/compiler/src/cerl_trees.erl index c7a129b42c..533c984221 100644 --- a/lib/compiler/src/cerl_trees.erl +++ b/lib/compiler/src/cerl_trees.erl @@ -351,10 +351,9 @@ mapfold(F, S0, T) -> mapfold(fun(T0, A) -> {T0, A} end, F, S0, T). -%% @spec mapfold(Pre, Post, Initial::term(), Tree::cerl()) -> -%% {cerl(), term()} -%% -%% Pre = Post = (cerl(), term()) -> {cerl(), term()} +%% @spec mapfold(Pre, Post, Initial::term(), Tree::cerl()) -> {cerl(), term()} +%% Pre = (cerl(), term()) -> {cerl(), term()} +%% Post = (cerl(), term()) -> {cerl(), term()} %% %% @doc Does a combined map/fold operation on the nodes of the %% tree. It begins by calling <code>Pre</code> on the tree, using the diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index dbb6bf8135..df4e2245f4 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -60,7 +60,6 @@ #include <openssl/rand.h> #include <openssl/evp.h> #include <openssl/hmac.h> -#include <openssl/engine.h> #include <openssl/err.h> /* Helper macro to construct a OPENSSL_VERSION_NUMBER. @@ -102,8 +101,10 @@ # undef FIPS_SUPPORT # endif +# if LIBRESSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(2,7,0) /* LibreSSL wants the 1.0.1 API */ # define NEED_EVP_COMPATIBILITY_FUNCTIONS +# endif #endif @@ -112,8 +113,10 @@ #endif -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) -# define HAS_EVP_PKEY_CTX +#ifndef HAS_LIBRESSL +# if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0) +# define HAS_EVP_PKEY_CTX +# endif #endif @@ -121,10 +124,6 @@ #include <openssl/modes.h> #endif -#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,8,'h') -#define HAS_ENGINE_SUPPORT -#endif - #include "crypto_callback.h" #if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(0,9,8) \ @@ -185,6 +184,19 @@ # undef HAVE_RSA_SSLV23_PADDING #endif +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,8,'h') \ + && defined(HAVE_EC) +/* If OPENSSL_NO_EC is set, there will be an error in ec.h included from engine.h + So if EC is disabled, you can't use Engine either.... +*/ +# define HAS_ENGINE_SUPPORT +#endif + + +#if defined(HAS_ENGINE_SUPPORT) +# include <openssl/engine.h> +#endif + #if defined(HAVE_CMAC) #include <openssl/cmac.h> #endif @@ -500,7 +512,6 @@ static ERL_NIF_TERM aes_gcm_decrypt_NO_EVP(ErlNifEnv* env, int argc, const ERL_N static ERL_NIF_TERM chacha20_poly1305_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM chacha20_poly1305_decrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, char **cmds, int i); static ERL_NIF_TERM engine_by_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM engine_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM engine_finish_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -528,6 +539,7 @@ static int term2point(ErlNifEnv* env, ERL_NIF_TERM term, static ERL_NIF_TERM bin_from_bn(ErlNifEnv* env, const BIGNUM *bn); #ifdef HAS_ENGINE_SUPPORT +static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, char **cmds, int i); static int zero_terminate(ErlNifBinary bin, char **buf); #endif @@ -5407,9 +5419,9 @@ static ERL_NIF_TERM engine_get_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TE #endif } +#ifdef HAS_ENGINE_SUPPORT static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, char **cmds, int i) { -#ifdef HAS_ENGINE_SUPPORT ERL_NIF_TERM head, tail; const ERL_NIF_TERM *tmp_tuple; ErlNifBinary tmpbin; @@ -5454,10 +5466,8 @@ static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, cha cmds[i] = NULL; return 0; } -#else - return atom_notsup; -#endif } +#endif static ERL_NIF_TERM engine_get_all_methods_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* () */ diff --git a/lib/crypto/c_src/otp_test_engine.c b/lib/crypto/c_src/otp_test_engine.c index 5c6122c06a..d0e23a2a3e 100644 --- a/lib/crypto/c_src/otp_test_engine.c +++ b/lib/crypto/c_src/otp_test_engine.c @@ -24,10 +24,8 @@ #include <stdio.h> #include <string.h> -#include <openssl/engine.h> #include <openssl/md5.h> #include <openssl/rsa.h> -#include <openssl/pem.h> #define PACKED_OPENSSL_VERSION(MAJ, MIN, FIX, P) \ ((((((((MAJ << 8) | MIN) << 8 ) | FIX) << 8) | (P-'a'+1)) << 4) | 0xf) @@ -40,6 +38,21 @@ #define OLD #endif +#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION(0,9,8,'o') \ + && !defined(OPENSSL_NO_EC) \ + && !defined(OPENSSL_NO_ECDH) \ + && !defined(OPENSSL_NO_ECDSA) +# define HAVE_EC +#endif + +#if defined(HAVE_EC) +/* If OPENSSL_NO_EC is set, there will be an error in ec.h included from engine.h + So if EC is disabled, you can't use Engine either.... +*/ +#include <openssl/engine.h> +#include <openssl/pem.h> + + static const char *test_engine_id = "MD5"; static const char *test_engine_name = "MD5 test engine"; @@ -262,3 +275,5 @@ int pem_passwd_cb_fun(char *buf, int size, int rwflag, void *password) return 0; } } + +#endif diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml index 1f788a4e35..66619c9e11 100644 --- a/lib/crypto/doc/src/notes.xml +++ b/lib/crypto/doc/src/notes.xml @@ -31,6 +31,28 @@ </header> <p>This document describes the changes made to the Crypto application.</p> +<section><title>Crypto 4.2.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + If OPENSSL_NO_EC was set, the compilation of the crypto + nifs failed.</p> + <p> + Own Id: OTP-15073</p> + </item> + <item> + <p> + C-compile errors for LibreSSL 2.7.0 - 2.7.2 fixed</p> + <p> + Own Id: OTP-15074 Aux Id: ERL-618 </p> + </item> + </list> + </section> + +</section> + <section><title>Crypto 4.2.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk index 3432f00836..778aff9d13 100644 --- a/lib/crypto/vsn.mk +++ b/lib/crypto/vsn.mk @@ -1 +1 @@ -CRYPTO_VSN = 4.2.1 +CRYPTO_VSN = 4.2.2 diff --git a/lib/edoc/src/edoc_doclet.erl b/lib/edoc/src/edoc_doclet.erl index f55cffe158..6cb3095507 100644 --- a/lib/edoc/src/edoc_doclet.erl +++ b/lib/edoc/src/edoc_doclet.erl @@ -40,7 +40,7 @@ -import(edoc_report, [report/2, warning/2]). -%% @headerfile "edoc_doclet.hrl" +%% @headerfile "../include/edoc_doclet.hrl" -include("../include/edoc_doclet.hrl"). -define(EDOC_APP, edoc). diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index 554d675383..f96d946a5d 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -226,7 +226,7 @@ <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>The default value is <c>info</c></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> @@ -251,14 +251,14 @@ logger_disk_log_maxbytes = 1048576</code> <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 starated, + supervisor reports. If the SASL application is started, these log events will be sent to a second handler instance - named sasl_h, according to values of the SASL environment - variables <c>sasl_error_logger</c> + named <c>sasl_h</c>, according to values of the SASL + environment variables <c>sasl_error_logger</c> and <c>sasl_errlog_type</c>, see <seealso marker="sasl:sasl_app#configuration">SASL(6) </seealso></p> - <p>The default value is <c>false</c></p> + <p>The default value is <c>false</c>.</p> <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> @@ -271,7 +271,7 @@ logger_disk_log_maxbytes = 1048576</code> reports from <c>supervisor</c> and <c>application_controller</c> shall be logged or not.</p> - <p>If <c>logger_sasl_compatible = false</c>, + <p>If <c>logger_sasl_compatible = true</c>, then <c>logger_log_progress</c> is ignored.</p> </item> <tag><marker id="logger_format_depth"/> @@ -280,14 +280,6 @@ logger_disk_log_maxbytes = 1048576</code> <p>Can be used to limit the size of the formatted output from the logger handlers.</p> - <note><p>This configuration parameter was introduced in OTP 18.1 - and is experimental. Based on user feedback, it - can be changed or improved in future releases, for example, - to gain better control over how to limit the size of the - formatted output. We have no plans to remove this - new feature entirely, unless it turns out to be - useless.</p></note> - <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 @@ -312,11 +304,11 @@ logger_disk_log_maxbytes = 1048576</code> </item> <tag><c>logger_max_size = integer() | unlimited</c></tag> <item> - <p>This parameter specifies the maximum size (bytes) each - log event can have when printed by the standard logger - handler. If the resulting string after formatting an event - is bigger than this, it will be truncated before printed - to the handler's destination.</p> + <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> diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index 66e6e5c689..d901454e62 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -67,37 +67,86 @@ <datatype> <name name="metadata"/> <desc> - <p>Metadata associated with the message to be logged.</p> + <p>Metadata for the log event.</p> + <p>Logger adds the following metadata to each log event:</p> + <list> + <item><c>pid => self()</c></item> + <item><c>gl => group_leader()</c></item> + <item><c>time => erlang:monotonic_time(microsecond)</c></item> + </list> + <p>When a log macro is used, Logger also inserts location + information:</p> + <list> + <item><c>mfa => {?MODULE,?FUNCTION_NAME,?FUNCTION_ARITY}</c></item> + <item><c>file => ?FILE</c></item> + <item><c>line => ?LINE</c></item> + </list> + <p>You can add custom metadata, either by specifying a map as + the last parameter to any of the log macros or the API + functions, or by setting process metadata + with <seealso marker="#set_process_metadata-1"> + <c>set_process_metadata/1</c></seealso> + or <seealso marker="#update_process_metadata-1"> + <c>update_process_metadata/1</c></seealso>.</p> + <p>Logger merges all the metadata maps before forwarding the + log event to the handlers. If the same keys occur, values + from the log call overwrites process metadata, which in turn + overwrites values set by Logger.</p> </desc> </datatype> <datatype> <name name="config"/> <desc> - <p></p> + <p>Configuration data for the logger part of Logger, or for a handler.</p> + <p>The following default values apply:</p> + <list> + <item><c>level => info</c></item> + <item><c>filter_default => log</c></item> + <item><c>filters => []</c></item> + <item><c>formatter => {logger_formatter,DefaultFormatterConfig</c>}</item> + </list> + <p>See the <seealso marker="logger_formatter#configuration"> + <c>logger_formatter(3)</c></seealso> manual page for + information about the default configuration for this + formatter.</p> </desc> </datatype> <datatype> <name name="handler_id"/> <desc> - <p></p> + <p>A unique identifier for a handler instance.</p> </desc> </datatype> <datatype> <name name="filter_id"/> <desc> - <p></p> + <p>A unique identifier for a filter.</p> </desc> </datatype> <datatype> <name name="filter"/> <desc> - <p></p> + <p>A filter which can be installed for logger or for a handler.</p> + </desc> + </datatype> + <datatype> + <name name="filter_arg"/> + <desc> + <p>The second argument to the filter fun.</p> </desc> </datatype> <datatype> <name name="filter_return"/> <desc> - <p></p> + <p>The return value from the filter fun.</p> + </desc> + </datatype> + <datatype> + <name name="timestamp"/> + <desc> + <p>A timestamp produced + with <seealso marker="erts:erlang#monotonic_time-1"> + <c>erlang:monotonic_time(microsecond)</c></seealso>.</p> </desc> </datatype> </datatypes> @@ -126,14 +175,10 @@ </list> <p>All macros expand to a call to logger, where <c>Level</c> is - taken from the macro name, and the following metadata is added, - or merged with the given <c>Metadata</c>:</p> - - <code> -#{mfa=>{?MODULE,?FUNCTION_NAME,?FUNCTION_ARITY}, - file=>?FILE, - line=>?LINE} - </code> + taken from the macro name, and location data is added. See the + description of + the <seealso marker="#type-metadata"><c>metadata()</c></seealso> + type for more information about the location data.</p> <p>The call is wrapped in a case statement and will be evaluated only if <c>Level</c> is equal to or below the configured log @@ -267,7 +312,7 @@ <func> <name name="i" arity="0"/> - <fsummary>Get information about all logger configurations</fsummary> + <fsummary>Get all logger configurations</fsummary> <desc> <p>Same as <seealso marker="#i/1"><c>logger:i(term)</c></seealso></p> </desc> @@ -277,27 +322,30 @@ <name name="i" arity="1" clause_i="1"/> <name name="i" arity="1" clause_i="2"/> <name name="i" arity="1" clause_i="3"/> - <fsummary>Get information about all logger configurations</fsummary> + <fsummary>Get all logger configurations</fsummary> <desc> - <p>The <c>logger:i/1</c> function can be used to get all - current logger configuration. The way that the information - is returned depends on the <c><anno>Action</anno></c></p> + <p>Display or return all current logger configuration.</p> <taglist> - <tag>string</tag> - <item>Return the pretty printed current logger configuration - as iodata.</item> - <tag>term</tag> - <item>Return the current logger configuration as a term. The - format of this term may change inbetween releases. For a - stable format use <seealso marker="#get_handler_config/1"> + <tag><c><anno>Action</anno> = string</c></tag> + <item> + <p>Return the pretty printed current logger configuration + as iodata.</p> + </item> + <tag><c><anno>Action</anno> = term</c></tag> + <item> + <p>Return the current logger configuration as a term. The + format of this term may change inbetween releases. For a + stable format use <seealso marker="#get_handler_config/1"> <c>logger:get_handler_config/1</c></seealso> - and <seealso marker="#get_logger_config/0"> + and <seealso marker="#get_logger_config/0"> <c>logger:get_logger_config/0</c></seealso>. - The same as calling <c>logger:i()</c>.</item> - <tag>print</tag> - <item>Pretty print all the current logger configuration to - standard out. Example: - <code><![CDATA[1> logger:i(). + The same as calling <c>logger:i()</c>.</p> + </item> + <tag><c><anno>Action</anno> = print</c></tag> + <item> + <p>Pretty print all the current logger configuration to + standard out. Example:</p> + <code><![CDATA[1> logger:i(print). Current logger configuration: Level: info FilterDefault: log @@ -339,6 +387,39 @@ Current logger configuration: <fsummary>Add a filter to the logger.</fsummary> <desc> <p>Add a filter to the logger.</p> + <p>The filter fun is called with the log event as the first + parameter, and the specified <c>filter_args()</c> as the + second parameter.</p> + <p>The return value of the fun specifies if a log event is to + be discarded or forwarded to the handlers:</p> + <taglist> + <tag><c>log()</c></tag> + <item> + <p>The filter <em>passed</em>. The next logger filter, if + any, is applied. If no more logger filters exist, the + log event is forwarded to the handler part of the + logger, where handler filters are applied.</p> + </item> + <tag><c>stop</c></tag> + <item> + <p>The filter <em>did not pass</em>, and the log event is + immediately discarded.</p> + </item> + <tag><c>ignore</c></tag> + <item> + <p>The filter has no knowledge of the log event. The next + logger filter, if any, is applied. If no more logger + filters exist, the value of the <c>filter_default</c> + configuration parameter for the logger specifies if the + log event shall be discarded or forwarded to the handler + part.</p> + </item> + </taglist> + <p>See section <seealso marker="logger_chapter#Filter"> + Filter</seealso> in the User's Guide for more information + about filters.</p> + <p>Some built-in filters exist. These are defined + in <seealso marker="logger_filters"><c>logger_filters</c></seealso>.</p> </desc> </func> @@ -347,6 +428,39 @@ Current logger configuration: <fsummary>Add a filter to the specified handler.</fsummary> <desc> <p>Add a filter to the specified handler.</p> + <p>The filter fun is called with the log event as the first + parameter, and the specified <c>filter_args()</c> as the + second parameter.</p> + <p>The return value of the fun specifies if a log event is to + be discarded or forwarded to the handler callback:</p> + <taglist> + <tag><c>log()</c></tag> + <item> + <p>The filter <em>passed</em>. The next handler filter, if + any, is applied. If no more filters exist for this + handler, the log event is forwarded to the handler + callback.</p> + </item> + <tag><c>stop</c></tag> + <item> + <p>The filter <em>did not pass</em>, and the log event is + immediately discarded.</p> + </item> + <tag><c>ignore</c></tag> + <item> + <p>The filter has no knowledge of the log event. The next + handler filter, if any, is applied. If no more filters + exist for this handler, the value of + the <c>filter_default</c> configuration parameter for + the handler specifies if the log event shall be + discarded or forwarded to the handler callback.</p> + </item> + </taglist> + <p>See + section <seealso marker="logger_chapter#Filter">Filter</seealso> + in the User's Guide for more information about filters.</p> + <p>Some built-in filters exist. These are defined in + <seealso marker="logger_filters"><c>logger_filters</c></seealso>.</p> </desc> </func> @@ -354,7 +468,8 @@ Current logger configuration: <name name="remove_logger_filter" arity="1"/> <fsummary>Remove a filter from the logger.</fsummary> <desc> - <p>Remove the filter with the specified identity from the logger.</p> + <p>Remove the filter identified + by <c><anno>FilterId</anno></c> from the logger.</p> </desc> </func> @@ -362,7 +477,9 @@ Current logger configuration: <name name="remove_handler_filter" arity="2"/> <fsummary>Remove a filter from the specified handler.</fsummary> <desc> - <p>Remove the filter with the specified identity from the given handler.</p> + <p>Remove the filter identified + by <c><anno>FilterId</anno></c> from the handler identified + by <c><anno>HandlerId</anno></c>.</p> </desc> </func> @@ -371,6 +488,9 @@ Current logger configuration: <fsummary>Add a handler with the given configuration.</fsummary> <desc> <p>Add a handler with the given configuration.</p> + <p><c><anno>HandlerId</anno></c> is a unique identifier which + must be used in all subsequent calls reffering to this + handler.</p> </desc> </func> @@ -378,7 +498,7 @@ Current logger configuration: <name name="remove_handler" arity="1"/> <fsummary>Remove the handler with the specified identity.</fsummary> <desc> - <p>Remove the handler with the specified identity.</p> + <p>Remove the handler identified by <c><anno>HandlerId</anno></c>.</p> </desc> </func> @@ -386,10 +506,37 @@ Current logger configuration: <name name="set_module_level" arity="2"/> <fsummary>Set the log level for the specified module.</fsummary> <desc> - <p>Set the log level for the specified module.</p> - <p>To change the logging level globally, use - <seealso marker="#set_logger_config/2"><c>logger:set_logger_config(level, Level)</c></seealso>. - </p> + <p>Set the log level for the + specified <c><anno>Module</anno></c>.</p> + <p>The log level for a module overrides the global log level + of the logger for log event originating from the module in + question. Notice, however, that it does not override the + level configuration for any handler.</p> + <p>For example: Assume that the global log level for the + logger is <c>info</c>, and there is one handler, <c>h1</c>, + with level <c>info</c> and one handler, <c>h2</c>, with + level <c>debug</c>.</p> + <p>With this configuration, no debug messages will be logged, + since they are all stopped by the global log level.</p> + <p>If the level for <c>mymodule</c> is set now set + to <c>debug</c>, then debug events from this module will be + logged by the handler <c>h2</c>, but not by + handler <c>h1</c>.</p> + <p>Debug events from other modules are still not logged.</p> + <p>To change the global log level for the logger, use + <seealso marker="#set_logger_config/2"> + <c>logger:set_logger_config(level,Level)</c></seealso>.</p> + <p>To change the log level for a handler, use + <seealso marker="#set_handler_config/3"> + <c>logger:set_handler_config(HandlerId,level,Level)</c></seealso>.</p> + <note> + <p>The originating module for a log event is only detected + if <c>mfa=>{Module,Function,Arity}</c> exists in the + metadata. When log macros are used, this association is + automatically added to all log events. If the logger API + is called directly, without using a macro, the logging + client must explicitly add this information.</p> + </note> </desc> </func> @@ -404,21 +551,62 @@ Current logger configuration: <func> <name name="set_logger_config" arity="1"/> + <fsummary>Set configuration data for the logger.</fsummary> + <desc> + <p>Set configuration data for the logger. This overwrites the + current logger configuration.</p> + <p>To modify the existing configuration, + use <seealso marker="#set_logger_config-2"><c>set_logger_config/2</c> + </seealso>, or read the current configuration + with <seealso marker="#get_logger_config-0"><c>get_logger_config/0</c> + </seealso>, then merge in your added or updated + associations before writing it back.</p> + <p>If a key is removed compared to the current configuration, + the default value is used.</p> + </desc> + </func> + + <func> <name name="set_logger_config" arity="2"/> <fsummary>Add or update configuration data for the logger.</fsummary> <desc> - <p>Add or update configuration data for the logger.</p> + <p>Add or update configuration data for the logger. If the + given <c><anno>Key</anno></c> already exists, its associated + value will be changed to <c><anno>Value</anno></c>. If it + doesn't exist, it will be added.</p> </desc> </func> <func> <name name="set_handler_config" arity="2"/> + <fsummary>Set configuration data for the specified handler.</fsummary> + <desc> + <p>Set configuration data for the specified handler. This + overwrites the current handler configuration.</p> + <p>To modify the existing configuration, + use <seealso marker="#set_handler_config-3"><c>set_handler_config/3</c> + </seealso>, or read the current configuration + with <seealso marker="#get_handler_config-1"><c>get_handler_config/1</c> + </seealso>, then merge in your added or updated + associations before writing it back.</p> + <p>If a key is removed compared to the current configuration, + and the key is know by Logger, the default value is used. If + it is a custom key, then it is up to the handler + implementation if the value is removed or a default value is + inserted.</p> + </desc> + </func> + + <func> <name name="set_handler_config" arity="3"/> <fsummary>Add or update configuration data for the specified handler.</fsummary> <desc> <p>Add or update configuration data for the specified - handler.</p> + handler. If the given <c><anno>Key</anno></c> already + exists, its associated value will be changed + to <c><anno>Value</anno></c>. If it doesn't exist, it will + be added.</p> </desc> </func> @@ -437,17 +625,37 @@ Current logger configuration: <name name="set_process_metadata" arity="1"/> <fsummary>Set metadata to use when logging from current process.</fsummary> <desc> - <p>Set metadata which <c>logger</c> automatically inserts it - in all log events produced on the current - process. Subsequent calls will overwrite previous data set - by this function.</p> - <p>When logging, location data produced by the log macros, - and/or metadata given as argument to the log call (API - function or macro), will be merged with the process - metadata. If the same keys occur, values from the metadata - argument to the log call will overwrite values in the - process metadata, which in turn will overwrite values from - the location data.</p> + <p>Set metadata which Logger shall automatically insert in + all log events produced on the current process.</p> + <p>Location data produced by the log macros, and/or metadata + given as argument to the log call (API function or macro), + are merged with the process metadata. If the same keys + occur, values from the metadata argument to the log call + overwrite values from the process metadata, which in turn + overwrite values from the location data.</p> + <p>Subsequent calls to this function overwrites previous data + set. To update existing data instead of overwriting it, + see <seealso marker="#update_process_metadata-1"> + <c>update_process_metadata/1</c></seealso>.</p> + </desc> + </func> + + <func> + <name name="update_process_metadata" arity="1"/> + <fsummary>Set or update metadata to use when logging from + current process.</fsummary> + <desc> + <p>Set or update metadata to use when logging from current + process</p> + <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)) + </code> + <p>If no process metadata exists, the function behaves as + <seealso marker="#set_process_metadata-1"> + <c>set_process_metadata/1</c> + </seealso>.</p> </desc> </func> @@ -457,7 +665,9 @@ Current logger configuration: <desc> <p>Retrieve data set with <seealso marker="#set_process_metadata-1"> - <c>set_process_metadata/1</c></seealso>.</p> + <c>set_process_metadata/1</c></seealso> or + <seealso marker="#update_process_metadata-1"> + <c>update_process_metadata/1</c></seealso>.</p> </desc> </func> @@ -467,12 +677,103 @@ Current logger configuration: <desc> <p>Delete data set with <seealso marker="#set_process_metadata-1"> - <c>set_process_metadata/1</c></seealso>.</p> + <c>set_process_metadata/1</c></seealso> or + <seealso marker="#update_process_metadata-1"> + <c>update_process_metadata/1</c></seealso>.</p> + </desc> + </func> + + <func> + <name name="format_report" arity="1"/> + <fsummary>Convert a log message on report form to {Format,Args}.</fsummary> + <desc> + <p>Convert a log message on report form to <c>{Format,Args}</c>.</p> + <p>This is the default report callback used + by <seealso marker="logger_formatter"> + <c>logger_formatter</c></seealso> when no custom report + callback is found.</p> + <p>The function produces lines of <c>Key: Value</c> from + key-value lists. Strings are printed with <c>~ts</c> and + other terms with <c>~tp</c>.</p> + <p>If the <c><anno>Report</anno></c> is a map, it is + converted to a key-value list before formatting as such.</p> </desc> </func> </funcs> + <section> + <title>Callback Functions</title> + <p>The following functions are to be exported from a handler + callback module.</p> + </section> + + <funcs> + <func> + <name>Module:adding_handler(HandlerId,Config1) -> {ok,Config2} | {error,Reason}</name> + <fsummary>An instance of this handler is about to be added.</fsummary> + <type> + <v>HandlerId = + <seealso marker="#type-handler_id">handler_id()</seealso></v> + <v>Config1 = Config2 = + <seealso marker="#type-config">config()</seealso></v> + <v>Reason = term()</v> + </type> + <desc> + <p>This callback function is optional.</p> + <p>The function is called when an new handler is about to be + added, and the purpose is to verify the configuration and + initiate all resourced needed by the handler.</p> + <p>If everything succeeds, the callback function can add + possible default values or internal state values to the + configuration, and return the adjusted map + in <c>{ok,Config2}</c>.</p> + <p>If the configuration is faulty, or if the initiation fails, + the callback function must return <c>{error,Reason}</c>.</p> + </desc> + </func> + + <func> + <name>Module:removing_handler(HandlerId,Config) -> ok</name> + <fsummary>The given handler is about to be removed.</fsummary> + <type> + <v>HandlerId = + <seealso marker="#type-handler_id">handler_id()</seealso></v> + <v>Config = + <seealso marker="#type-config">config()</seealso></v> + </type> + <desc> + <p>This callback function is optional.</p> + <p>The function is called when a handler is about to be + removed, and the purpose is to release all resources used by + the handler. The return value is ignored by Logger.</p> + </desc> + </func> + + <func> + <name>Module:changing_config(HandlerId,Config1,Config2) -> {ok,Config3} | {error,Reason}</name> + <fsummary>The configuration for this handler is about to change.</fsummary> + <type> + <v>HandlerId = + <seealso marker="#type-handler_id">handler_id()</seealso></v> + <v>Config1 = Config2 = Config3 = + <seealso marker="#type-config">config()</seealso></v> + <v>Reason = term()</v> + </type> + <desc> + <p>This callback function is optional.</p> + <p>The function is called when the configuration for a handler + is about to change, and the purpose is to verify and act on + the new configuration.</p> + <p><c>Config1</c> is the existing configuration + and <c>Config2</c> is the new configuration.</p> + <p>If everything succeeds, the callback function must return a + possibly adjusted configuration in <c>{ok,Config3}</c>.</p> + <p>If the configuration is faulty, the callback function must + return <c>{error,Reason}</c>.</p> + </desc> + </func> + </funcs> </erlref> diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 88dcfbe8d9..519df2ba48 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -157,7 +157,7 @@ <p>A formatter is defined as a module exporting the following function:</p> - <code>format(Log,Extra) -> string()</code> + <code>format(Log,Extra) -> unicode:chardata()</code> <p>The formatter plugin is called by each handler, and the returned string can be printed to the handler's destination @@ -322,19 +322,6 @@ return <c>ignore</c>.</p> <p>Default is <c>log</c>.</p> </item> - <tag><c>depth = pos_integer() | unlimited</c></tag> - <item> - <p>Specifies if the depth of terms in the log events shall - be limited by using control characters <c>~P</c> - and <c>~W</c> instead of <c>~p</c> and <c>~w</c>, - respectively. See - <seealso marker="stdlib:io#format-1"><c>io:format</c></seealso>.</p> - </item> - <tag><c>max_size = pos_integer() | unlimited</c></tag> - <item> - <p>Specifies if the size of a log event shall be limited by - truncating the formatted string.</p> - </item> <tag><c>formatter = {Module::module(),Extra::term()}</c></tag> <item> <p>See <seealso marker="#Formatter">Formatter</seealso> for more @@ -347,10 +334,9 @@ <p>Note that <c>level</c> and <c>filters</c> are obeyed by Logger itself before forwarding the log events to each - handler, while <c>depth</c>, <c>max_size</c> - and <c>formatter</c> are left to the handler - implementation. All Logger's built-in handlers do apply these - configuration parameters before printing.</p> + handler, while <c>formatter</c> is left to the handler + implementation. All Logger's built-in handlers will call the + given formatter before printing.</p> </section> </section> @@ -488,8 +474,9 @@ error_logger:add_report_handler/1,2. level => debug} 2> <input>logger:add_handler(debug_handler,logger_std_h,Config).</input> ok</pre> - <p>By default, the handler receives all events, so we need to add a filter - to stop all non-debug events:</p> + <p>By default, the handler receives all events + (<c>filter_defalt=log</c>), so we need to add a filter to stop + all non-debug events:</p> <pre> 3> <input>Fun = fun(#{level:=debug}=Log,_) -> Log; (_,_) -> stop end.</input> #Fun<erl_eval.12.98642416> @@ -516,7 +503,7 @@ ok</pre> <p>It may also implement the following callbacks:</p> <code> adding_handler(logger:handler_id(),logger:config()) -> {ok,logger:config()} | {error,term()} -removing_handler(logger:handler_id()) -> ok +removing_handler(logger:handler_id(),logger:config()) -> ok changing_config(logger:handler_id(),logger:config(),logger:config()) -> {ok,logger:config()} | {error,term()} </code> <p>When logger:add_handler(Id,Module,Config) is called, logger @@ -526,7 +513,7 @@ changing_config(logger:handler_id(),logger:config(),logger:config()) -> {ok,logg events as calls to Module:log/2.</p> <p>A handler can be removed by calling logger:remove_handler(Id). logger will call - Module:removing_handler(Id), and then remove the handler's + Module:removing_handler(Id,Config), and then remove the handler's configuration from the configuration database.</p> <p>When logger:set_handler_config is called, logger calls Module:changing_config(Id,OldConfig,NewConfig). If this function @@ -547,7 +534,7 @@ log(Log,#{formatter:={FModule,FConfig}) -> this:</p> <code> -module(myhandler). --export([adding_handler/2, removing_handler/1, log/2]). +-export([adding_handler/2, removing_handler/2, log/2]). -export([init/1, handle_call/3, handle_cast/2, terminate/2]). adding_handler(Id,Config) -> @@ -576,7 +563,7 @@ log(Log,#{myhandler_fd:=Fd,formatter:={FModule,FConfig}}) -> single process.</p> <code> -module(myhandler). --export([adding_handler/2, removing_handler/1, log/2]). +-export([adding_handler/2, removing_handler/2, log/2]). -export([init/1, handle_call/3, handle_cast/2, terminate/2]). adding_handler(Id,Config) -> diff --git a/lib/kernel/doc/src/logger_filters.xml b/lib/kernel/doc/src/logger_filters.xml index d742391e35..c34ec7d14c 100644 --- a/lib/kernel/doc/src/logger_filters.xml +++ b/lib/kernel/doc/src/logger_filters.xml @@ -33,16 +33,20 @@ <file>logger_filters.xml</file> </header> <module>logger_filters</module> - <modulesummary>Filters to use with logger.</modulesummary> + <modulesummary>Filters to use with Logger.</modulesummary> <description> - <p>Filters to use with logger. All functions exported from this - module can be used as logger or handler + <p>All functions exported from this module can be used as logger + or handler filters. See <seealso marker="logger#add_logger_filter-2"> <c>logger:add_logger_filter/2</c></seealso> and <seealso marker="logger#add_handler_filter-3"> - <c>logger:add_handler_filter/3</c></seealso> - for more information about how filters are added.</p> + <c>logger:add_handler_filter/3</c></seealso> for more information + about how filters are added.</p> + <p>Filters are removed with <seealso marker="logger#remove_logger_filter-1"> + <c>logger:remove_logger_filter/1</c></seealso> + and <seealso marker="logger#remove_handler_filter-2"> + <c>logger:remove_handler_filter/2</c></seealso>.</p> </description> <funcs> diff --git a/lib/kernel/doc/src/logger_formatter.xml b/lib/kernel/doc/src/logger_formatter.xml index 6a17e3641f..7df4c88f40 100644 --- a/lib/kernel/doc/src/logger_formatter.xml +++ b/lib/kernel/doc/src/logger_formatter.xml @@ -33,12 +33,187 @@ <file>logger_formatter.xml</file> </header> <module>logger_formatter</module> - <modulesummary>Default formatter for the Logger application.</modulesummary> + <modulesummary>Default formatter for Logger.</modulesummary> <description> - <p>Default formatter for the Logger application.</p> + <p>Each log handler has a configured formatter specified as a + module and a configuration term. The purpose of the formatter is + to translate the log events to a final printable string + (<c>unicode:chardata()</c>) which can be written to the output + device of the handler.</p> + <p><c>logger_formatter</c> is the default formatter used by + Logger.</p> </description> + <section> + <title>Configuration</title> + <p>The configuration term for <c>logger_formatter</c> is a map, + and the following keys can be set as configuration + parameters:</p> + <taglist> + <tag><c>chars_limit = pos_integer() | unlimited</c></tag> + <item> + <p>A positive integer representing the value of the option + with the same name to be used when calling + <seealso marker="stdlib:io_lib#format-3">io_lib:format/3</seealso>. + This value limits the total number of characters printed + for each log event. Notice that this is a soft limit. For a + hard truncation limit, see option <c>max_size</c>.</p> + <p>Default is <c>unlimited</c>.</p> + <note> + <p><c>chars_limit</c> has no effect on log messages on + string form. These are expected to be short, but can still + be truncated by the <c>max_size</c> parameter.</p> + </note> + </item> + <tag><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 + strings passed to this formatter are rewritten. The format + controls ~p and ~w are replaced with ~P and ~W, + respectively, and the value is used as the depth + parameter. For details, see + <seealso marker="stdlib:io#format-2">io:format/2,3</seealso> + in STDLIB.</p> + <p>Default is <c>unlimited</c>.</p> + <note> + <p><c>depth</c> has no effect on log messages on string + form. These are expected to be short, but can still be + truncated by the <c>max_size</c> parameter.</p> + </note> + </item> + <tag><c>max_size = pos_integer() | unlimited</c></tag> + <item> + <p>A positive integer representing the absolute maximum size a + string returned from this formatter can have. If the + formatted string is longer, after possibly being limited + by <c>chars_limit</c> or <c>depth</c>, it is truncated.</p> + <p>Default is <c>unlimited</c>.</p> + </item> + <tag><c>single_line = boolean()</c></tag> + <item> + <p>If set to <c>true</c>, all newlines in the message are + replaced with <c>", "</c>, and whitespaces following + directly after newlines are removed. Note that newlines + added by the <c>template</c> parameter are not replaced.</p> + <p>Default is <c>true</c>.</p> + </item> + <tag><c>legacy_header = boolen()</c></tag> + <item> + <p>If set to <c>true</c> a header field is added to + logger_formatter's part of <c>Metadata</c>. The value of + this field is a string similar to the header created by the + old <c>error_logger</c> event handlers. It can be included + in the log event by adding the + tuple <c>{logger_formatter,header}</c> to the template. See + section <seealso marker="#default_templates">Default + Templates</seealso> for more information.</p> + <p>Default is <c>false</c>.</p> + </item> + <tag><c>report_cb = fun((</c><seealso marker="logger#type-report"><c>logger:report()</c></seealso><c>) -> {</c><seealso marker="stdlib:io#type-format"><c>io:format()</c></seealso><c>,[term()]})</c></tag> + <item> + <p>A report callback is used by the formatter to transform log + messages on report form to a format string and + arguments. The report callback can be specified in the + metadata for the log event. If no report callback exist in + metadata, <c>logger_formatter</c> will + use <seealso marker="logger#format_report-1"> + <c>logger:format_report/1</c></seealso> as default + callback.</p> + <p>If this configuration parameter is set, it replaces both + the default report callback, and any report callback found + in metadata. That is, all reports are converted by this + configured function.</p> + <p>The value must be a function with arity 1, + returning <c>{Format,Args}</c>, and it will be called with a + report as only argument.</p> + </item> + <tag><c>template = </c><seealso marker="#type-template"><c>template()</c></seealso></tag> + <item> + <p>The template is a list of atoms, tuples and strings. The + atoms <c>level</c> or <c>msg</c>, are treated as + placeholders for the severity level and the log message, + repectively. Other atoms or tuples are interpreted as + placeholders for metadata, where atoms are expected to match + top level keys, and tuples represent paths to sub keys when + the metadata is a nested map. For example the + tuple <c>{key1,key2}</c> is replaced by the value of + the <c>key2</c> field in the nested map below. The + atom <c>key1</c> on its own is replaced by the complete + value of the <c>key1</c> field. The values are converted to + strings.</p> + +<code> +#{key1=>#{key2=>my_value, + ...} + ...}</code> + + <p>Strings in the template are printed literally.</p> + <p>The default template differs depending on the values + of <c>legacy_header</c> + and <c>single_line</c>. See <seealso marker="#default_templates">Default + Templates</seealso> for more information</p> + </item> + <tag><c>utc = boolean()</c></tag> + <item> + <p>If set to <c>true</c>, all dates are displayed in Universal + Coordinated Time.</p> + <p>Default is <c>false</c>.</p> + </item> + </taglist> + </section> + + <section> + <marker id="default_templates"/> + <title>Default templates</title> + + <p>The default value for the <c>template</c> configuration + parameter depends on the value of <c>single_line</c> + and <c>legacy_header</c> as follows.</p> + + <p>The log event used in the examples is:</p> + <code> +?LOG_ERROR("name: ~p~nexit_reason: ~p",[my_reg_name,"It crashed"])</code> + + <taglist> + <tag><c>legacy_header=true</c></tag> + <item> + <p>Default template: <c>[{logger_formatter,header},"\n",msg,"\n"]</c></p> + + <p>Example log entry:</p> + <code> +=ERROR REPORT==== 29-Dec-2017::13:30:51.245123 === +name: my_reg_name +exit_reason: "It crashed"</code> + + <p>Notice that all eight levels might occur in the heading, + not only <c>ERROR</c>, <c>WARNING</c> or <c>INFO</c> as the + old <c>error_logger</c> produced. And microseconds are + added at the end of the timestamp.</p> + </item> + + <tag><c>single_line=true</c></tag> + <item> + <p>Default template: <c>[time," ",level,": ",msg,"\n"]</c></p> + + <p>Example log entry:</p> + <code>2017-12-29 13:31:49.640317 error: name: my_reg_name, exit_reason: "It crashed"</code> + </item> + + <tag><c>legacy_header=false, single_line=false</c></tag> + <item> + <p>Default template: <c>[time," ",level,":\n",msg,"\n"]</c></p> + + <p>Example log entry:</p> + <code> +2017-12-29 13:32:25.191925 error: +name: my_reg_name +exit_reason: "It crashed"</code> + </item> + </taglist> + </section> + <datatypes> <datatype> <name name="template"/> @@ -52,101 +227,22 @@ <name name="format" arity="2"/> <fsummary>Formats the given message.</fsummary> <desc> - <p>Formats the given message.</p> - <p>The template is a list of atoms, tuples and strings. Atoms - can be <c>level</c> or <c>msg</c>, which are placeholders - for the severity level and the log message, - repectively. Tuples are interpreted as placeholders for - metadata. Each element in the tuple must be an atom which - matches a key in the nested metadata map, e.g. the - tuple <c>{key1,key2}</c> will be replaced by the value of - the key2 field in this nested map (the value vill be - converted to a string):</p> - -<code> -#{key1=>#{key2=>my_value, - ...}, - ...}</code> - - - <p> Strings are printed literally.</p> - - <p><c>depth</c> is a positive integer representing the maximum - depth to which terms shall be printed by this - formatter. Format strings passed to this formatter are - rewritten. The format controls ~p and ~w are replaced with - ~P and ~W, respectively, and the value is used as the depth - parameter. For details, see - <seealso marker="stdlib:io#format-2">io:format/2,3</seealso> - in STDLIB.</p> - - <p><c>chars_limit</c> is a positive integer representing the - value of the option with the same name to be used when calling - <seealso marker="stdlib:io#format-3">io:format/3</seealso>. This - value limits the total number of characters printed bu the - formatter. Notes that this is a soft limit. For a hard - truncation limit, see option <c>max_size</c>.</p> - - <p><c>max_size</c> is a positive integer representing the - maximum size a string returned from this formatter can - have. If the formatted string is longer, after possibly - being limited by <c>depth</c> and/or <c>chars_limit</c>, it - will be truncated.</p> - - <p><c>utc</c> is a boolean. If set to true, all dates are - displayed in Universal Coordinated Time. Default - is <c>false</c>.</p> - - <p><c>report_cb</c> must be a function with arity 1, - returning <c>{Format,Args}</c>. This function will replace - any <c>report_cb</c> found in metadata.</p> - - <p>If <c>single_line=true</c>, all newlines in the message are - replaced with <c>", "</c>, and whitespaces following directly - after newlines are removed. Note that newlines added by the - formatter template are not replaced.</p> - - <p>If <c>legacy_header=true</c> a header field is added to - logger_formatter's part of <c>Metadata</c>. The value of - this field is a string similar to the header created by the - old <c>error_logger</c> event handlers. It can be included - in the log event by adding the - tuple <c>{logger_formatter,header}</c> to the template.</p> - - <p>The default template when <c>legacy_header=true</c> is</p> - - <code>[{logger_formatter,header},"\n",msg,"\n"]</code> - - <p>which will cause log entries like this:</p> - - <code>=ERROR REPORT==== 29-Dec-2017::13:30:51.245123 === - process: <0.74.0> - exit_reason: "Something went wrong"</code> - - <p>Note that all eight levels might occur here, not - only <c>ERROR</c>, <c>WARNING</c> or <c>INFO</c>. And also - that micro seconds are added at the end of the - timestamp.</p> - - <p>The default template when <c>single_line=true</c> is</p> - - <code>[time," ",level,": ",msg,"\n"]</code> - - <p>which will cause log entries like this:</p> - - <code>2017-12-29 13:31:49.640317 error: process: <0.74.0>, exit_reason: "Something went wrong"</code> - - <p>The default template when both <c>legacy_header</c> and - <c>single_line</c> are set to false is:</p> - - <code>[time," ",level,":\n",msg,"\n"]</code> - - <p>which will cause log entries like this:</p> - - <code>2017-12-29 13:32:25.191925 error: - process: <0.74.0> - exit_reason: "Something went wrong"</code> - + <p>This the formatter callback function to be called from + handlers. The log event is processed as follows:</p> + <list> + <item>If the message is on report form, it is converted to + <c>{Format,Args}</c> by calling the report + callback.</item> + <item>The size is limited according to the values of + configuration parameters <c>chars_limit</c> + and <c>depth</c>. Notice that this does not apply to + messages on string form.</item> + <item>The full log entry is composed according to + the <c>template</c>.</item> + <item>If the final string is too long, it is truncated + according to the value of configuration + parameter <c>max_size</c>.</item> + </list> </desc> </func> diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index 0706220a94..47d0ca5ea3 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -32,7 +32,7 @@ which_report_handlers/0]). %% logger callbacks --export([adding_handler/2, removing_handler/1, log/2]). +-export([adding_handler/2, removing_handler/2, log/2]). -export([get_format_depth/0, limit_term/1]). @@ -111,8 +111,8 @@ adding_handler(?MODULE,Config) -> Error end. --spec removing_handler(logger:handler_id()) -> ok. -removing_handler(?MODULE) -> +-spec removing_handler(logger:handler_id(),logger:config()) -> ok. +removing_handler(?MODULE,_Config) -> stop(), ok. diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl index fd06f0f7d8..5704cc79c2 100644 --- a/lib/kernel/src/hipe_unified_loader.erl +++ b/lib/kernel/src/hipe_unified_loader.erl @@ -453,7 +453,7 @@ make_beam_stub(Mod, LoaderState, MD5, Beam, FunDefs, ClosuresToPatch) -> %%======================================================================== %% Patching %% @spec patch(refs(), BaseAddress::integer(), ConstAndZone::term(), -%% FunDefs::term(), TrampolineMap::term()) -> 'ok'. +%% FunDefs::term(), TrampolineMap::term()) -> 'ok' %% @type refs()=[{RefType::integer(), Reflist::reflist()} | refs()] %% %% @type reflist()= [{Data::term(), Offsets::offests()}|reflist()] diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 943ef8c2d1..98a9937111 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -44,8 +44,8 @@ %% Misc -export([compare_levels/2]). --export([set_process_metadata/1, unset_process_metadata/0, - get_process_metadata/0]). +-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]). @@ -60,27 +60,41 @@ %%%----------------------------------------------------------------- %%% Types --type log() :: #{level=>level(), - msg=>{io:format(),[term()]} | +-type log() :: #{level:=level(), + msg:={io:format(),[term()]} | {report,report()} | {string,unicode:chardata()}, - meta=>metadata()}. + meta:=metadata()}. -type level() :: emergency | alert | critical | error | warning | notice | info | debug. -type report() :: map() | [{atom(),term()}]. -type msg_fun() :: fun((term()) -> {io:format(),[term()]} | report() | unicode:chardata()). --type metadata() :: map(). - +-type metadata() :: #{pid => pid(), + gl => pid(), + time => timestamp(), + mfa => {module(),atom(),non_neg_integer()}, + file => file:filename(), + line => non_neg_integer(), + term() => term()}. +-type location() :: #{mfa := {module(),atom(),non_neg_integer()}, + file := file:filename(), + line := non_neg_integer()}. -type handler_id() :: atom(). -type filter_id() :: atom(). --type filter() :: {fun((log(),term()) -> filter_return()),term()}. +-type filter() :: {fun((log(),filter_arg()) -> filter_return()),filter_arg()}. +-type filter_arg() :: term(). -type filter_return() :: stop | ignore | log(). --type config() :: map(). +-type config() :: #{level => level(), + filter_default => log | stop, + filters => [{filter_id(),filter()}], + formatter => {module(),term()}, + term() => term()}. +-type timestamp() :: integer(). -export_type([log/0,level/0,report/0,msg_fun/0,metadata/0,config/0,handler_id/0, - filter_id/0,filter/0,filter_return/0]). + filter_id/0,filter/0,filter_arg/0,filter_return/0]). %%%----------------------------------------------------------------- %%% API @@ -185,24 +199,24 @@ allow(Level,Module) when ?IS_LEVEL(Level), is_atom(Module) -> -spec macro_log(Location,Level,StringOrReport) -> ok when - Location :: map(), + Location :: location(), Level :: level(), StringOrReport :: unicode:chardata() | report(). macro_log(Location,Level,StringOrReport) -> log_allowed(Location,Level,StringOrReport,#{}). -spec macro_log(Location,Level,StringOrReport,Meta) -> ok when - Location :: map(), + Location :: location(), Level :: level(), StringOrReport :: unicode:chardata() | report(), Meta :: metadata(); (Location,Level,Format,Args) -> ok when - Location :: map(), + Location :: location(), Level :: level(), Format :: io:format(), Args ::[term()]; (Location,Level,Fun,FunArgs) -> ok when - Location :: map(), + Location :: location(), Level :: level(), Fun :: msg_fun(), FunArgs :: term(). @@ -213,13 +227,13 @@ macro_log(Location,Level,FunOrFormat,Args) -> log_allowed(Location,Level,{FunOrFormat,Args},#{}). -spec macro_log(Location,Level,Format,Args,Meta) -> ok when - Location :: map(), + Location :: location(), Level :: level(), Format :: io:format(), Args ::[term()], Meta :: metadata(); (Location,Level,Fun,FunArgs,Meta) -> ok when - Location :: map(), + Location :: location(), Level :: level(), Fun :: msg_fun(), FunArgs :: term(), @@ -390,6 +404,19 @@ set_process_metadata(Meta) when is_map(Meta) -> set_process_metadata(Meta) -> erlang:error(badarg,[Meta]). +-spec update_process_metadata(Meta) -> ok when + Meta :: metadata(). +update_process_metadata(Meta) when is_map(Meta) -> + case get_process_metadata() of + undefined -> + set_process_metadata(Meta); + Meta0 when is_map(Meta0) -> + set_process_metadata(maps:merge(Meta0,Meta)), + ok + end; +update_process_metadata(Meta) -> + erlang:error(badarg,[Meta]). + -spec get_process_metadata() -> Meta | undefined when Meta :: metadata(). get_process_metadata() -> @@ -699,7 +726,7 @@ do_log_1(Level,Msg,Meta) -> end. -spec log_allowed(Location,Level,Msg,Meta) -> ok when - Location :: map(), + Location :: location() | #{}, Level :: level(), Msg :: {msg_fun(),term()} | {io:format(),[term()]} | diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index 3b71f936d8..0150fa781a 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -34,7 +34,7 @@ %% logger callbacks -export([log/2, - adding_handler/2, removing_handler/1, + adding_handler/2, removing_handler/2, changing_config/3, swap_buffer/2]). %%%=================================================================== @@ -223,7 +223,7 @@ check_my_config([]) -> %%%----------------------------------------------------------------- %%% Handler being removed -removing_handler(Name) -> +removing_handler(Name, _Config) -> stop(Name). %%%----------------------------------------------------------------- diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl index 386e7832e2..8e954f8d98 100644 --- a/lib/kernel/src/logger_formatter.erl +++ b/lib/kernel/src/logger_formatter.erl @@ -29,7 +29,7 @@ %%%----------------------------------------------------------------- %%% API --spec format(Log,Config) -> String when +-spec format(Log,Config) -> unicode:chardata() when Log :: logger:log(), Config :: #{single_line=>boolean(), legacy_header=>boolean(), @@ -38,8 +38,7 @@ max_size=>pos_integer() | unlimited, depth=>pos_integer() | unlimited, template=>template(), - utc=>boolean()}, - String :: string(). + utc=>boolean()}. format(#{level:=Level,msg:=Msg0,meta:=Meta},Config0) when is_map(Config0) -> Config = add_default_config(Config0), @@ -263,7 +262,7 @@ utcstr(_) -> "". add_default_config(#{utc:=_}=Config0) -> Default = #{legacy_header=>false, - single_line=>false, + single_line=>true, chars_limit=>unlimited}, MaxSize = get_max_size(maps:get(max_size,Config0,false)), Depth = get_depth(maps:get(depth,Config0,false)), diff --git a/lib/kernel/src/logger_internal.hrl b/lib/kernel/src/logger_internal.hrl index 82df499c2b..8c0fc2725d 100644 --- a/lib/kernel/src/logger_internal.hrl +++ b/lib/kernel/src/logger_internal.hrl @@ -31,6 +31,7 @@ {no_domain,{fun logger_filters:domain/2,{log,no_domain,[]}}}]). -define(DEFAULT_FORMATTER,logger_formatter). -define(DEFAULT_FORMAT_CONFIG,#{legacy_header=>true, + single_line=>false, template=>?DEFAULT_FORMAT_TEMPLATE_HEADER}). -define(DEFAULT_FORMAT_TEMPLATE_HEADER, [{logger_formatter,header},"\n",msg,"\n"]). diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index 6ef3b8582a..a7f302ac8f 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -158,7 +158,7 @@ handle_call({remove_handler,HandlerId}, _From, #state{tid=Tid}=State) -> Handlers0 = maps:get(handlers,Config,[]), Handlers = lists:delete(HandlerId,Handlers0), %% inform the handler - _ = call_h(Module,removing_handler,[HandlerId],ok), + _ = call_h(Module,removing_handler,[HandlerId,Config],ok), do_set_config(Tid,logger,Config#{handlers=>Handlers}), logger_config:delete(Tid,HandlerId), ok; @@ -234,7 +234,13 @@ handle_info({log,Level,Report,Meta}, State) -> {noreply, State}; handle_info({Ref,_Reply},State) when is_reference(Ref) -> %% Assuming this is a timed-out gen_server reply - ignoring - {noreply, State}. + {noreply, State}; +handle_info(Unexpected,State) -> + ?LOG_INTERNAL(debug, + [{logger,got_unexpected_message}, + {process,?SERVER}, + {message,Unexpected}]), + {noreply,State}. terminate(_Reason, _State) -> ok. diff --git a/lib/kernel/src/logger_simple.erl b/lib/kernel/src/logger_simple.erl index 23ff6ccd2e..a1b427b96c 100644 --- a/lib/kernel/src/logger_simple.erl +++ b/lib/kernel/src/logger_simple.erl @@ -19,7 +19,7 @@ %% -module(logger_simple). --export([adding_handler/2, removing_handler/1, log/2]). +-export([adding_handler/2, removing_handler/2, log/2]). -export([get_buffer/0]). %% This module implements a simple handler for logger. It is the @@ -63,7 +63,7 @@ adding_handler(?MODULE,Config) -> {error,{handler_process_name_already_exists,?MODULE}} end. -removing_handler(?MODULE) -> +removing_handler(?MODULE,_Config) -> case whereis(?MODULE) of undefined -> ok; diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index cbc9db372c..31edcfea8b 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -35,7 +35,7 @@ terminate/2, code_change/3]). %% logger callbacks --export([log/2, adding_handler/2, removing_handler/1, +-export([log/2, adding_handler/2, removing_handler/2, changing_config/3, swap_buffer/2]). %%%=================================================================== @@ -207,7 +207,7 @@ check_my_config([]) -> %%%----------------------------------------------------------------- %%% Handler being removed -removing_handler(Name) -> +removing_handler(Name,_Config) -> stop(Name). %%%----------------------------------------------------------------- diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index 0edce3e34c..f311a9c7ed 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -666,6 +666,9 @@ process_metadata(_Config) -> check_logged(info,S3,#{time=>Time,line=>0,custom=>func}), ProcMeta = logger:get_process_metadata(), + ok = logger:update_process_metadata(#{custom=>changed,custom2=>added}), + Expected = ProcMeta#{custom:=changed,custom2=>added}, + Expected = logger:get_process_metadata(), ok = logger:unset_process_metadata(), undefined = logger:get_process_metadata(), @@ -720,7 +723,7 @@ check_maps(Expected,Got,What) -> adding_handler(_Id,Config) -> maybe_send(add), {ok,Config}. -removing_handler(_Id) -> +removing_handler(_Id,_Config) -> maybe_send(remove), ok. changing_config(_Id,_Old,#{call:=Fun}) -> diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index c7c6137380..63e5b56021 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -497,24 +497,19 @@ disk_log_sync(Config) -> filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,nl}}), - start_tracer([{?MODULE,format,2}, - {disk_log,blog,2}, + start_tracer([{disk_log,blog,2}, {disk_log,sync,1}], - [{formatter,"first"}, - {disk_log,blog}, + [{disk_log,blog,<<"first\n">>}, {disk_log,sync}]), logger:info("first", ?domain), %% wait for automatic disk_log_sync check_tracer(?FILESYNC_REPEAT_INTERVAL*2), - start_tracer([{?MODULE,format,2}, - {disk_log,blog,2}, + start_tracer([{disk_log,blog,2}, {disk_log,sync,1}], - [{formatter,"second"}, - {formatter,"third"}, - {disk_log,blog}, - {disk_log,blog}, + [{disk_log,blog,<<"second\n">>}, + {disk_log,blog,<<"third\n">>}, {disk_log,sync}]), %% two log requests in fast succession will make the handler skip %% an automatic disk log sync @@ -531,13 +526,10 @@ disk_log_sync(Config) -> no_repeat = maps:get(filesync_repeat_interval, logger_disk_log_h:info(?MODULE)), - start_tracer([{?MODULE,format,2}, - {disk_log,blog,2}, + start_tracer([{disk_log,blog,2}, {disk_log,sync,1}], - [{formatter,"fourth"}, - {disk_log,blog}, - {formatter,"fifth"}, - {disk_log,blog}, + [{disk_log,blog,<<"fourth\n">>}, + {disk_log,blog,<<"fifth\n">>}, {disk_log,sync}]), logger:info("fourth", ?domain), @@ -566,6 +558,7 @@ disk_log_sync(Config) -> check_tracer(100), ok. disk_log_sync(cleanup,_Config) -> + dbg:stop_clear(), logger:remove_handler(?MODULE). disk_log_wrap(Config) -> @@ -623,6 +616,7 @@ disk_log_wrap(Config) -> ok. disk_log_wrap(cleanup,_Config) -> + dbg:stop_clear(), logger:remove_handler(?MODULE). disk_log_full(Config) -> @@ -668,6 +662,7 @@ disk_log_full(Config) -> {trace,{error_status,{error,{full,_}}}}] = Received, ok. disk_log_full(cleanup, _Config) -> + dbg:stop_clear(), logger:remove_handler(?MODULE). disk_log_events(Config) -> @@ -713,6 +708,7 @@ disk_log_events(Config) -> end, Received), ok. disk_log_events(cleanup, _Config) -> + dbg:stop_clear(), logger:remove_handler(?MODULE). write_failure(Config) -> @@ -763,7 +759,7 @@ sync_failure(Config) -> Dir = ?config(priv_dir, Config), FileName = lists:concat([?MODULE,"_",?FUNCTION_NAME]), File = filename:join(Dir, FileName), - Log = lists:concat([File,".1"]), + Node = start_h_on_new_node(Config, ?FUNCTION_NAME, File), false = (undefined == rpc:call(Node, ets, whereis, [?TEST_HOOKS_TAB])), @@ -832,10 +828,10 @@ log_on_remote_node(Node,Msg) -> ok. %% functions for test hook macros to be called by rpc -set_internal_log(Mod, Func) -> - ?set_internal_log({Mod,Func}). -set_result(Op, Result) -> - ?set_result(Op, Result). +set_internal_log(_Mod, _Func) -> + ?set_internal_log({_Mod,_Func}). +set_result(_Op, _Result) -> + ?set_result(_Op, _Result). set_defaults() -> ?set_defaults(). @@ -885,7 +881,7 @@ op_switch_to_drop(cleanup, _Config) -> ok = stop_handler(?MODULE). op_switch_to_flush() -> - [{timetrap,{seconds,60}}]. + [{timetrap,{minutes,3}}]. op_switch_to_flush(Config) -> {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), @@ -988,7 +984,7 @@ kill_disabled(cleanup, _Config) -> ok = stop_handler(?MODULE). qlen_kill_new(Config) -> - {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + {_Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), Pid0 = whereis(?MODULE), {_,Mem0} = process_info(Pid0, memory), RestartAfter = 2000, @@ -1025,7 +1021,7 @@ qlen_kill_new(cleanup, _Config) -> ok = stop_handler(?MODULE). mem_kill_new(Config) -> - {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + {_Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), Pid0 = whereis(?MODULE), {_,Mem0} = process_info(Pid0, memory), RestartAfter = 2000, @@ -1111,7 +1107,7 @@ restart_after(cleanup, _Config) -> %% during high load to verify that sync, dropping and flushing is %% handled correctly. handler_requests_under_load() -> - [{timetrap,{seconds,60}}]. + [{timetrap,{minutes,3}}]. handler_requests_under_load(Config) -> {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NewHConfig = @@ -1140,7 +1136,7 @@ handler_requests_under_load(Config) -> NoOfReqs = lists:foldl(fun({_,Res}, N) -> N + length(Res) end, 0, ReqResult), ct:pal("~w requests made. Errors: ~n~p", [NoOfReqs,Errors]), ok = file:delete(Log). -handler_requests_under_load(cleanup, Config) -> +handler_requests_under_load(cleanup, _Config) -> ok = stop_handler(?MODULE). send_requests(HName, TO, Reqs = [{Req,Res}|Rs]) -> @@ -1369,7 +1365,6 @@ start_tracer(Trace,Expected) -> Pid = self(), dbg:tracer(process,{fun tracer/2,{Pid,Expected}}), dbg:p(whereis(?MODULE),[c]), - dbg:p(Pid,[c]), tpl(Trace), ok. @@ -1387,13 +1382,14 @@ tpl([{M,F,A}|Trace]) -> tpl([]) -> ok. -tracer({trace,_,call,{?MODULE,format,[#{msg:={string,Msg}}|_]}}, {Pid,[{formatter,Msg}|Expected]}) -> - maybe_tracer_done(Pid,Expected,{formatter,Msg}); tracer({trace,_,call,{logger_disk_log_h,handle_cast,[{Op,_}|_]}}, {Pid,[{Mod,Func,Op}|Expected]}) -> maybe_tracer_done(Pid,Expected,{Mod,Func,Op}); +tracer({trace,_,call,{Mod=disk_log,Func=blog,[_,Data]}}, {Pid,[{Mod,Func,Data}|Expected]}) -> + maybe_tracer_done(Pid,Expected,{Mod,Func,Data}); tracer({trace,_,call,{Mod,Func,_}}, {Pid,[{Mod,Func}|Expected]}) -> maybe_tracer_done(Pid,Expected,{Mod,Func}); tracer({trace,_,call,Call}, {Pid,Expected}) -> + ct:log("Tracer got unexpected: ~p~nExpected: ~p~n",[Call,Expected]), Pid ! {tracer_got_unexpected,Call,Expected}, {Pid,Expected}. @@ -1413,5 +1409,6 @@ check_tracer(T) -> dbg:stop_clear(), ct:fail({tracer_got_unexpected,Got,Expected}) after T -> + dbg:stop_clear(), ct:fail({timeout,tracer}) end. diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl index ac1abba629..7d1f33746d 100644 --- a/lib/kernel/test/logger_formatter_SUITE.erl +++ b/lib/kernel/test/logger_formatter_SUITE.erl @@ -73,18 +73,19 @@ all() -> default(_Config) -> String1 = format(info,{"~p",[term]},#{},#{}), ct:log(String1), - [_Date,_Time,"info:\nterm\n"] = string:lexemes(String1," "), + [_Date,_Time,"info:","term\n"] = string:lexemes(String1," "), Time = timestamp(), ExpectedTimestamp = default_time_format(Time), String2 = format(info,{"~p",[term]},#{time=>Time},#{}), ct:log(String2), - " info:\nterm\n" = string:prefix(String2,ExpectedTimestamp), + " info: term\n" = string:prefix(String2,ExpectedTimestamp), ok. legacy_header(_Config) -> Time = timestamp(), - String1 = format(info,{"~p",[term]},#{time=>Time},#{legacy_header=>true}), + String1 = format(info,{"~p",[term]},#{time=>Time},#{legacy_header=>true, + single_line=>false}), ct:log(String1), "=INFO REPORT==== "++Rest = String1, [Timestamp,"\nterm\n"] = string:lexemes(Rest," ="), @@ -98,12 +99,14 @@ legacy_header(_Config) -> true = lists:member(M,["Jan","Feb","Mar","Apr","May","Jun", "Jul","Aug","Sep","Oct","Nov","Dec"]), - String2 = format(info,{"~p",[term]},#{time=>Time},#{legacy_header=>false}), + String2 = format(info,{"~p",[term]},#{time=>Time},#{legacy_header=>false, + single_line=>false}), ct:log(String2), ExpectedTimestamp = default_time_format(Time), " info:\nterm\n" = string:prefix(String2,ExpectedTimestamp), - String3 = format(info,{"~p",[term]},#{time=>Time},#{legacy_header=>bad}), + String3 = format(info,{"~p",[term]},#{time=>Time},#{legacy_header=>bad, + single_line=>false}), ct:log(String3), String3 = String2, @@ -114,7 +117,8 @@ legacy_header(_Config) -> String4 = String1, String5 = format(info,{"~p",[term]},#{}, % <--- no time - #{legacy_header=>true}), + #{legacy_header=>true, + single_line=>false}), ct:log(String5), "=INFO REPORT==== "++_ = String5, ok. @@ -289,38 +293,36 @@ report_cb(_Config) -> ok. max_size(_Config) -> - Template = [msg], + Cfg = #{template=>[msg], + single_line=>false}, "12345678901234567890" = - format(info,{"12345678901234567890",[]},#{},#{template=>Template}), + format(info,{"12345678901234567890",[]},#{},Cfg), application:set_env(kernel,logger_max_size,11), "12345678901234567890" = % min value is 50, so this is not limited - format(info,{"12345678901234567890",[]},#{},#{template=>Template}), + format(info,{"12345678901234567890",[]},#{},Cfg), "12345678901234567890123456789012345678901234567..." = % 50 format(info, {"123456789012345678901234567890123456789012345678901234567890", []}, #{}, - #{template=>Template}), + Cfg), application:set_env(kernel,logger_max_size,53), "12345678901234567890123456789012345678901234567890..." = %53 format(info, {"123456789012345678901234567890123456789012345678901234567890", []}, #{}, - #{template=>Template}), + Cfg), "123456789012..." = - format(info,{"12345678901234567890",[]},#{},#{template=>Template, - max_size=>15}), + format(info,{"12345678901234567890",[]},#{},Cfg#{max_size=>15}), "12345678901234567890" = - format(info,{"12345678901234567890",[]},#{},#{template=>Template, - max_size=>unlimited}), + format(info,{"12345678901234567890",[]},#{},Cfg#{max_size=>unlimited}), %% Check that one newline at the end of the line is kept (if it exists) "12345678901...\n" = - format(info,{"12345678901234567890\n",[]},#{},#{template=>Template, - max_size=>15}), + format(info,{"12345678901234567890\n",[]},#{},Cfg#{max_size=>15}), "12345678901...\n" = - format(info,{"12345678901234567890",[]},#{},#{template=>[msg,"\n"], - max_size=>15}), + format(info,{"12345678901234567890",[]},#{},Cfg#{template=>[msg,"\n"], + max_size=>15}), ok. max_size(cleanup,_Config) -> application:unset_env(kernel,logger_max_size), @@ -441,20 +443,20 @@ format_time(_Config) -> ExpectedTimestamp1 = default_time_format(Time1), String1 = format(info,{"~p",[term]},#{time=>Time1},#{}), ct:log(String1), - " info:\nterm\n" = string:prefix(String1,ExpectedTimestamp1), + " info: term\n" = string:prefix(String1,ExpectedTimestamp1), Time2 = timestamp(), ExpectedTimestamp2 = default_time_format(Time2,true), String2 = format(info,{"~p",[term]},#{time=>Time2},#{utc=>true}), ct:log(String2), - " info:\nterm\n" = string:prefix(String2,ExpectedTimestamp2), + " info: term\n" = string:prefix(String2,ExpectedTimestamp2), application:set_env(kernel,logger_utc,true), Time3 = timestamp(), ExpectedTimestamp3 = default_time_format(Time3,true), String3 = format(info,{"~p",[term]},#{time=>Time3},#{}), ct:log(String3), - " info:\nterm\n" = string:prefix(String3,ExpectedTimestamp3), + " info: term\n" = string:prefix(String3,ExpectedTimestamp3), ok. diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index e940e0a026..7c8d63cbbd 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -499,86 +499,79 @@ filesync(Config) -> #{logger_std_h => #{type => Type}, filter_default=>log, filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), - formatter=>{?MODULE,self()}}), - Tester = self(), - TraceFun = fun({trace,_,call,{Mod,Func,Details}}, Pid) -> - Pid ! {trace,Mod,Func,Details}, - Pid; - ({trace,TPid,'receive',Received}, Pid) -> - Pid ! {trace,TPid,Received}, - Pid - end, - {ok,_} = dbg:tracer(process, {TraceFun, Tester}), - FileCtrlPid = maps:get(file_ctrl_pid , logger_std_h:info(?MODULE)), - {ok,_} = dbg:p(FileCtrlPid, [c]), - {ok,_} = dbg:tpl(logger_std_h, write_to_dev, 5, []), - {ok,_} = dbg:tpl(logger_std_h, sync_dev, 4, []), - {ok,_} = dbg:tp(file, datasync, 1, []), + formatter=>{?MODULE,nl}}), + + %% check repeated filesync happens + start_tracer([{logger_std_h, write_to_dev, 5}, + {logger_std_h, sync_dev, 4}, + {file, datasync, 1}], + [{logger_std_h, write_to_dev, <<"first\n">>}, + {logger_std_h, sync_dev}, + {file,datasync}]), logger:info("first", ?domain), %% wait for automatic filesync - timer:sleep(?FILESYNC_REP_INT), - Expected1 = [{log,"first"}, {trace,logger_std_h,write_to_dev}, - {trace,logger_std_h,sync_dev}, {trace,file,datasync}], - + check_tracer(?FILESYNC_REP_INT*2), + + %% check that explicit filesync is only done once + start_tracer([{logger_std_h, write_to_dev, 5}, + {logger_std_h, sync_dev, 4}, + {file, datasync, 1}], + [{logger_std_h, write_to_dev, <<"second\n">>}, + {logger_std_h, sync_dev}, + {file,datasync}, + {no_more,500} + ]), logger:info("second", ?domain), %% do explicit filesync logger_std_h:filesync(?MODULE), %% a second filesync should be ignored logger_std_h:filesync(?MODULE), - Expected2 = [{log,"second"}, {trace,logger_std_h,write_to_dev}, - {trace,logger_std_h,sync_dev}, {trace,file,datasync}], + check_tracer(100), %% check that if there's no repeated filesync active, %% a filesync is still performed when handler goes idle logger:set_handler_config(?MODULE, logger_std_h, #{filesync_repeat_interval => no_repeat}), no_repeat = maps:get(filesync_repeat_interval, logger_std_h:info(?MODULE)), + %% The following timer is to make sure the time from last log + %% ("second") to next ("third") is long enough, so the a flush is + %% triggered by the idle timeout between "thrid" and "fourth". + timer:sleep(?IDLE_DETECT_TIME_MSEC*2), + start_tracer([{logger_std_h, write_to_dev, 5}, + {logger_std_h, sync_dev, 4}, + {file, datasync, 1}], + [{logger_std_h, write_to_dev, <<"third\n">>}, + {logger_std_h, sync_dev}, + {file,datasync}, + {logger_std_h, write_to_dev, <<"fourth\n">>}, + {logger_std_h, sync_dev}, + {file,datasync}]), logger:info("third", ?domain), + %% wait for automatic filesync timer:sleep(?IDLE_DETECT_TIME_MSEC*2), logger:info("fourth", ?domain), %% wait for automatic filesync - timer:sleep(?IDLE_DETECT_TIME_MSEC*2), - Expected3 = [{log,"third"}, {trace,logger_std_h,write_to_dev}, - {log,"fourth"}, {trace,logger_std_h,write_to_dev}, - {trace,logger_std_h,sync_dev}, {trace,file,datasync}], - - dbg:stop_clear(), - - %% verify that filesync has been performed as expected - Received1 = lists:map(fun({trace,M,F,_}) -> {trace,M,F}; - (Other) -> Other - end, test_server:messages_get()), - ct:pal("Trace #1 =~n~p", [Received1]), - Received1 = Expected1 ++ Expected2 ++ Expected3, - - try_read_file(Log, {ok,<<"first\nsecond\nthird\nfourth\n">>}, 1000), - - {ok,_} = dbg:tracer(process, {TraceFun, Tester}), - {ok,_} = dbg:p(whereis(?MODULE), [c]), - {ok,_} = dbg:tpl(logger_std_h, handle_cast, 2, []), + check_tracer(?IDLE_DETECT_TIME_MSEC*2), %% switch repeated filesync on and verify that the looping works SyncInt = 1000, WaitT = 4500, + OneSync = {logger_std_h,handle_cast,repeated_filesync}, + %% receive 1 initial repeated_filesync, then 1 per sec + start_tracer([{logger_std_h,handle_cast,2}], + [OneSync || _ <- lists:seq(1, 1 + trunc(WaitT/SyncInt))]), + logger:set_handler_config(?MODULE, logger_std_h, #{filesync_repeat_interval => SyncInt}), SyncInt = maps:get(filesync_repeat_interval, logger_std_h:info(?MODULE)), timer:sleep(WaitT), logger:set_handler_config(?MODULE, logger_std_h, - #{filesync_repeat_interval => no_repeat}), - dbg:stop_clear(), - - Received2 = lists:map(fun({trace,_M,handle_cast,[{Op,_},_]}) -> {trace,Op}; - (Other) -> Other - end, test_server:messages_get()), - ct:pal("Trace #2 =~n~p", [Received2]), - OneSync = [{trace,repeated_filesync}], - %% receive 1 initial repeated_filesync, then 1 per sec - Received2 = - lists:flatten([OneSync || _ <- lists:seq(1, 1 + trunc(WaitT/SyncInt))]), + #{filesync_repeat_interval => no_repeat}), + check_tracer(100), ok. filesync(cleanup, _Config) -> + dbg:stop_clear(), logger:remove_handler(?MODULE). write_failure(Config) -> @@ -785,7 +778,7 @@ op_switch_to_drop_tty(cleanup, _Config) -> ok = stop_handler(?MODULE). op_switch_to_flush_file() -> - [{timetrap,{seconds,60}}]. + [{timetrap,{minutes,3}}]. op_switch_to_flush_file(Config) -> {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), @@ -1052,7 +1045,7 @@ restart_after(cleanup, _Config) -> %% during high load to verify that sync, dropping and flushing is %% handled correctly. handler_requests_under_load() -> - [{timetrap,{seconds,60}}]. + [{timetrap,{minutes,3}}]. handler_requests_under_load(Config) -> {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), @@ -1394,3 +1387,67 @@ analyse(Msgs) -> From ! {result,self(),TestFun(Msgs)}, analyse(Msgs) end. + +start_tracer(Trace,Expected) -> + Pid = self(), + FileCtrlPid = maps:get(file_ctrl_pid, logger_std_h:info(?MODULE)), + dbg:tracer(process,{fun tracer/2,{Pid,Expected}}), + dbg:p(whereis(?MODULE),[c]), + dbg:p(FileCtrlPid,[c]), + tpl(Trace), + ok. + +tpl([{M,F,A}|Trace]) -> + {ok,Match} = dbg:tpl(M,F,A,[]), + case lists:keyfind(matched,1,Match) of + {_,_,1} -> + ok; + _ -> + dbg:stop_clear(), + throw({skip,"Can't trace "++atom_to_list(M)++":"++ + atom_to_list(F)++"/"++integer_to_list(A)}) + end, + tpl(Trace); +tpl([]) -> + ok. + +tracer({trace,_,call,{logger_std_h,handle_cast,[{Op,_}|_]}}, + {Pid,[{Mod,Func,Op}|Expected]}) -> + maybe_tracer_done(Pid,Expected,{Mod,Func,Op}); +tracer({trace,_,call,{Mod=logger_std_h,Func=write_to_dev,[_,Data,_,_,_]}}, + {Pid,[{Mod,Func,Data}|Expected]}) -> + maybe_tracer_done(Pid,Expected,{Mod,Func,Data}); +tracer({trace,_,call,{Mod,Func,_}}, {Pid,[{Mod,Func}|Expected]}) -> + maybe_tracer_done(Pid,Expected,{Mod,Func}); +tracer({trace,_,call,Call}, {Pid,Expected}) -> + ct:log("Tracer got unexpected: ~p~nExpected: ~p~n",[Call,Expected]), + Pid ! {tracer_got_unexpected,Call,Expected}, + {Pid,Expected}. + +maybe_tracer_done(Pid,[]=Expected,Got) -> + ct:log("Tracer got: ~p~n",[Got]), + Pid ! {tracer_done,0}, + {Pid,Expected}; +maybe_tracer_done(Pid,[{no_more,T}]=Expected,Got) -> + ct:log("Tracer got: ~p~n",[Got]), + Pid ! {tracer_done,T}, + {Pid,Expected}; +maybe_tracer_done(Pid,Expected,Got) -> + ct:log("Tracer got: ~p~n",[Got]), + {Pid,Expected}. + +check_tracer(T) -> + check_tracer(T,fun() -> ct:fail({timeout,tracer}) end). +check_tracer(T,TimeoutFun) -> + receive + {tracer_done,Delay} -> + %% Possibly wait Delay ms to check that no unexpected + %% traces are received + check_tracer(Delay,fun() -> ok end); + {tracer_got_unexpected,Got,Expected} -> + dbg:stop_clear(), + ct:fail({tracer_got_unexpected,Got,Expected}) + after T -> + dbg:stop_clear(), + TimeoutFun() + end. diff --git a/lib/sasl/src/sasl.erl b/lib/sasl/src/sasl.erl index 657eb6688a..2bf11bdcdf 100644 --- a/lib/sasl/src/sasl.erl +++ b/lib/sasl/src/sasl.erl @@ -130,6 +130,7 @@ add_sasl_logger(undefined, _Level) -> ok; add_sasl_logger(std, undefined) -> ok; add_sasl_logger(Dest, Level) -> FC0 = #{legacy_header=>true, + single_line=>false, template=>[{logger_formatter,header},"\n",msg,"\n"]}, FC = case application:get_env(sasl,utc_log) of {ok,Bool} when is_boolean(Bool) -> diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index d78309167a..d0ed674eee 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -30,6 +30,28 @@ <file>notes.xml</file> </header> +<section><title>Ssh 4.6.9</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Host key hash erroneously calculated for clients + following draft-00 of RFC 4419, for example PuTTY</p> + <p> + Own Id: OTP-15064</p> + </item> + <item> + <p> + Renegotiation could fail in some states</p> + <p> + Own Id: OTP-15066</p> + </item> + </list> + </section> + +</section> + <section><title>Ssh 4.6.8</title> <section><title>Fixed Bugs and Malfunctions</title> <list> @@ -487,6 +509,34 @@ </section> +<section><title>Ssh 4.4.2.4</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix rare spurios shutdowns of ssh servers when receiveing + <c>{'EXIT',_,normal}</c> messages.</p> + <p> + Own Id: OTP-15018</p> + </item> + <item> + <p> + Host key hash erroneously calculated for clients + following draft-00 of RFC 4419, for example PuTTY</p> + <p> + Own Id: OTP-15064</p> + </item> + <item> + <p> + Renegotiation could fail in some states</p> + <p> + Own Id: OTP-15066</p> + </item> + </list> + </section> + +</section> <section><title>Ssh 4.4.2.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index b21c0337ad..dfdad769ed 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -545,7 +545,7 @@ role({_,Role}) -> Role; role({_,Role,_}) -> Role. -spec renegotiation(state_name()) -> boolean(). -renegotiation({_,_,ReNeg}) -> ReNeg == renegotiation; +renegotiation({_,_,ReNeg}) -> ReNeg == renegotiate; renegotiation(_) -> false. diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index f5bba9f824..631c4d0213 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -1808,9 +1808,10 @@ kex_alg_dependent({E, F, K}) -> %% diffie-hellman and ec diffie-hellman (with E = Q_c, F = Q_s) <<?Empint(E), ?Empint(F), ?Empint(K)>>; -kex_alg_dependent({-1, _, -1, _, _, E, F, K}) -> +kex_alg_dependent({-1, NBits, -1, Prime, Gen, E, F, K}) -> %% ssh_msg_kex_dh_gex_request_old - <<?Empint(E), ?Empint(F), ?Empint(K)>>; + <<?Euint32(NBits), + ?Empint(Prime), ?Empint(Gen), ?Empint(E), ?Empint(F), ?Empint(K)>>; kex_alg_dependent({Min, NBits, Max, Prime, Gen, E, F, K}) -> %% diffie-hellman group exchange @@ -1849,9 +1850,6 @@ public_algo({#'ECPoint'{},{namedCurve,OID}}) -> Curve = public_key:oid2ssh_curvename(OID), list_to_atom("ecdsa-sha2-" ++ binary_to_list(Curve)). - - - sha('ssh-rsa') -> sha; sha('rsa-sha2-256') -> sha256; sha('rsa-sha2-384') -> sha384; diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index f327d2ec11..f10e7aa96a 100644 --- a/lib/ssh/vsn.mk +++ b/lib/ssh/vsn.mk @@ -1,4 +1,5 @@ #-*-makefile-*- ; force emacs to enter makefile-mode -SSH_VSN = 4.6.8 +SSH_VSN = 4.6.9 + APP_VSN = "ssh-$(SSH_VSN)" diff --git a/lib/stdlib/doc/src/string.xml b/lib/stdlib/doc/src/string.xml index c7772d63a3..4a3d37dcb6 100644 --- a/lib/stdlib/doc/src/string.xml +++ b/lib/stdlib/doc/src/string.xml @@ -641,7 +641,7 @@ ÖÄÅ</pre> <note><p> The functions are kept for backward compatibility, but are not recommended. - They will be deprecated in Erlang/OTP 21. + They will be deprecated in a future release. </p> <p>Any undocumented functions in <c>string</c> are not to be used.</p> </note> diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl index 53042251cc..3ee2031d02 100644 --- a/lib/stdlib/src/gen_event.erl +++ b/lib/stdlib/src/gen_event.erl @@ -122,7 +122,7 @@ -type add_handler_ret() :: ok | term() | {'EXIT',term()}. -type del_handler_ret() :: ok | term() | {'EXIT',term()}. --type emgr_name() :: {'local', atom()} | {'global', atom()} +-type emgr_name() :: {'local', atom()} | {'global', term()} | {'via', atom(), term()}. -type debug_flag() :: 'trace' | 'log' | 'statistics' | 'debug' | {'logfile', string()}. @@ -130,7 +130,7 @@ | {'debug', [debug_flag()]} | {'spawn_opt', [proc_lib:spawn_option()]} | {'hibernate_after', timeout()}. --type emgr_ref() :: atom() | {atom(), atom()} | {'global', atom()} +-type emgr_ref() :: atom() | {atom(), atom()} | {'global', term()} | {'via', atom(), term()} | pid(). -type start_ret() :: {'ok', pid()} | {'error', term()}. @@ -146,7 +146,7 @@ %% start_link() %% start_link(MgrName | Options) %% start_link(MgrName, Options) -%% MgrName ::= {local, atom()} | {global, atom()} | {via, atom(), term()} +%% MgrName ::= {local, atom()} | {global, term()} | {via, atom(), term()} %% Options ::= [{timeout, Timeout} | {debug, [Flag]} | {spawn_opt,SOpts}] %% Flag ::= trace | log | {logfile, File} | statistics | debug %% (debug == log && statistics) diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl index f65ef78636..035dd871ff 100644 --- a/lib/stdlib/src/gen_server.erl +++ b/lib/stdlib/src/gen_server.erl @@ -166,7 +166,7 @@ %%% start(Name, Mod, Args, Options) %%% start_link(Mod, Args, Options) %%% start_link(Name, Mod, Args, Options) where: -%%% Name ::= {local, atom()} | {global, atom()} | {via, atom(), term()} +%%% Name ::= {local, atom()} | {global, term()} | {via, atom(), term()} %%% Mod ::= atom(), callback module implementing the 'real' server %%% Args ::= term(), init arguments (to Mod:init/1) %%% Options ::= [{timeout, Timeout} | {debug, [Flag]}] |