From f508378290a8b1cac4f54d9a9d6f1ce61dcad800 Mon Sep 17 00:00:00 2001 From: Guilherme Andrade Date: Tue, 9 Aug 2016 21:37:16 +0100 Subject: zlib: Add test case for inflateGetDictionary --- lib/kernel/test/zlib_SUITE.erl | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl index f8257bd20c..4720a7f5ea 100644 --- a/lib/kernel/test/zlib_SUITE.erl +++ b/lib/kernel/test/zlib_SUITE.erl @@ -83,7 +83,7 @@ groups() -> [api_open_close, api_deflateInit, api_deflateSetDictionary, api_deflateReset, api_deflateParams, api_deflate, api_deflateEnd, - api_inflateInit, api_inflateSetDictionary, + api_inflateInit, api_inflateSetDictionary, api_inflateGetDictionary, api_inflateSync, api_inflateReset, api_inflate, api_inflateChunk, api_inflateEnd, api_setBufsz, api_getBufsz, api_crc32, api_adler32, api_getQSize, api_un_compress, api_un_zip, @@ -288,6 +288,30 @@ api_inflateSetDictionary(Config) when is_list(Config) -> ?m({'EXIT',{stream_error,_}}, zlib:inflateSetDictionary(Z1,Dict)), ?m(ok, zlib:close(Z1)). +%% Test inflateGetDictionary. +api_inflateGetDictionary(Config) when is_list(Config) -> + % Compress payload using custom dictionary + Z1 = zlib:open(), + ?m(ok, zlib:deflateInit(Z1)), + Dict = <<"foobar barfoo foo bar far boo">>, + ?m(_, zlib:deflateSetDictionary(Z1, Dict)), + Payload = <<"foobarbarbar">>, + Compressed = zlib:deflate(Z1, Payload, finish), + ?m(ok, zlib:close(Z1)), + + % Decompress and test dictionary extraction + Z2 = zlib:open(), + ?m(ok, zlib:inflateInit(Z2)), + ?m(<<>>, iolist_to_binary(zlib:inflateGetDictionary(Z2))), + ?m({'EXIT',{stream_error,_}}, zlib:inflateSetDictionary(Z2, Dict)), + ?m({'EXIT',{{need_dictionary,_},_}}, zlib:inflate(Z2, Compressed)), + ?m(ok, zlib:inflateSetDictionary(Z2, Dict)), + ?m(Dict, iolist_to_binary(zlib:inflateGetDictionary(Z2))), + ?m(Payload, iolist_to_binary(zlib:inflate(Z2, Compressed))), + ?m(ok, zlib:close(Z2)), + ?m(?BARG, zlib:inflateSetDictionary(Z2, Dict)), + ok. + %% Test inflateSync. api_inflateSync(Config) when is_list(Config) -> {skip,"inflateSync/1 sucks"}. -- cgit v1.2.3 From dc322963be1e20c153413fe964a59cffe21971b9 Mon Sep 17 00:00:00 2001 From: Guilherme Andrade Date: Sat, 20 Aug 2016 23:26:07 +0100 Subject: Skip inflateGetDictionary test case if unsupported --- lib/kernel/test/zlib_SUITE.erl | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl index 4720a7f5ea..4b67fce9a8 100644 --- a/lib/kernel/test/zlib_SUITE.erl +++ b/lib/kernel/test/zlib_SUITE.erl @@ -290,6 +290,18 @@ api_inflateSetDictionary(Config) when is_list(Config) -> %% Test inflateGetDictionary. api_inflateGetDictionary(Config) when is_list(Config) -> + Z1 = zlib:open(), + IsOperationSupported = + case catch zlib:inflateGetDictionary(Z1) of + {'EXIT',{einval,_}} -> true; + {'EXIT',{enotsup,_}} -> false + end, + _ = zlib:close(Z1), + api_inflateGetDictionary_if_supported(IsOperationSupported). + +api_inflateGetDictionary_if_supported(false) -> + {skip, "inflateGetDictionary/1 unsupported in current setup"}; +api_inflateGetDictionary_if_supported(true) -> % Compress payload using custom dictionary Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1)), -- cgit v1.2.3 From 5e57b3faccba1ae66ebd40fed23f5770eee71b04 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 24 Aug 2016 15:57:53 +0200 Subject: Replace usage of deprecated time units --- lib/kernel/doc/src/seq_trace.xml | 4 ++-- lib/kernel/src/inet_db.erl | 3 ++- lib/kernel/src/kernel.app.src | 2 +- lib/kernel/test/erl_distribution_SUITE.erl | 4 ++-- lib/kernel/test/file_SUITE.erl | 2 +- lib/kernel/test/gen_tcp_misc_SUITE.erl | 2 +- lib/kernel/test/interactive_shell_SUITE.erl | 2 +- lib/kernel/test/os_SUITE.erl | 2 +- 8 files changed, 11 insertions(+), 10 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/seq_trace.xml b/lib/kernel/doc/src/seq_trace.xml index 5ac199b6a7..ba7259219d 100644 --- a/lib/kernel/doc/src/seq_trace.xml +++ b/lib/kernel/doc/src/seq_trace.xml @@ -129,7 +129,7 @@ seq_trace:set_token(OldToken), % activate the trace token again Erlang monotonic time and a monotonically increasing integer. The time-stamp has the same format and value - as produced by {erlang:monotonic_time(nano_seconds), + as produced by {erlang:monotonic_time(nanosecond), erlang:unique_integer([monotonic])}.

set_token(monotonic_timestamp, Bool) @@ -141,7 +141,7 @@ seq_trace:set_token(OldToken), % activate the trace token again Erlang monotonic time. The time-stamp has the same format and value as produced by - erlang:monotonic_time(nano_seconds).

+ erlang:monotonic_time(nanosecond).

If multiple timestamp flags are passed, timestamp has diff --git a/lib/kernel/src/inet_db.erl b/lib/kernel/src/inet_db.erl index 465cec1b45..6cbb6ac2da 100644 --- a/lib/kernel/src/inet_db.erl +++ b/lib/kernel/src/inet_db.erl @@ -1379,7 +1379,8 @@ cache_rr(_Db, Cache, RR) -> ets:insert(Cache, RR). times() -> - erlang:convert_time_unit(erlang:monotonic_time() - erlang:system_info(start_time),native,seconds). + erlang:convert_time_unit(erlang:monotonic_time() - erlang:system_info(start_time), + native, second). %% lookup and remove old entries diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 56d1699656..4d08a55c7c 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -118,6 +118,6 @@ {applications, []}, {env, [{error_logger, tty}]}, {mod, {kernel, []}}, - {runtime_dependencies, ["erts-8.0", "stdlib-3.0", "sasl-3.0"]} + {runtime_dependencies, ["erts-9.0", "stdlib-3.0", "sasl-3.0"]} ] }. diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl index eb58e92224..bd62096684 100644 --- a/lib/kernel/test/erl_distribution_SUITE.erl +++ b/lib/kernel/test/erl_distribution_SUITE.erl @@ -226,7 +226,7 @@ time_ping(Node) -> T0 = erlang:monotonic_time(), pang = net_adm:ping(Node), T1 = erlang:monotonic_time(), - erlang:convert_time_unit(T1 - T0, native, milli_seconds). + erlang:convert_time_unit(T1 - T0, native, millisecond). %% Keep the connection with the client node up. %% This is neccessary as the client node runs with much shorter @@ -270,7 +270,7 @@ tick_cli_test1(Node) -> receive {whats_the_result, From} -> Diff = erlang:convert_time_unit(T2-T1, native, - milli_seconds), + millisecond), case Diff of T when T > 8000, T < 16000 -> From ! {tick_test, T}; diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 5f049c6f99..375899987e 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -3730,7 +3730,7 @@ response_analysis(Module, Function, Arguments) -> {Result, Comment}. micro_ts() -> - erlang:monotonic_time(micro_seconds). + erlang:monotonic_time(microsecond). response_stat(init, Ts) -> {0, 0, Ts, 0, 0}; diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index 6b64c83fc2..929f66d400 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -1914,7 +1914,7 @@ so_priority(Config) when is_list(Config) -> %% Accept test utilities (suites are below) millis() -> - erlang:monotonic_time(milli_seconds). + erlang:monotonic_time(millisecond). collect_accepts(0,_) -> []; collect_accepts(N,Tmo) -> diff --git a/lib/kernel/test/interactive_shell_SUITE.erl b/lib/kernel/test/interactive_shell_SUITE.erl index fc3706ba1e..298a364a91 100644 --- a/lib/kernel/test/interactive_shell_SUITE.erl +++ b/lib/kernel/test/interactive_shell_SUITE.erl @@ -711,7 +711,7 @@ toerl_loop(Port,Acc) -> end. millistamp() -> - erlang:monotonic_time(milli_seconds). + erlang:monotonic_time(millisecond). get_data_within(Port, X, Acc) when X =< 0 -> ?dbg({get_data_within, X, Acc, ?LINE}), diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl index 19ab3713a1..b9ddc3bf48 100644 --- a/lib/kernel/test/os_SUITE.erl +++ b/lib/kernel/test/os_SUITE.erl @@ -311,7 +311,7 @@ perf_counter_api(_Config) -> T1 = os:perf_counter(), timer:sleep(100), T2 = os:perf_counter(), - TsDiff = erlang:convert_time_unit(T2 - T1, perf_counter, nano_seconds), + TsDiff = erlang:convert_time_unit(T2 - T1, perf_counter, nanosecond), ct:pal("T1: ~p~n" "T2: ~p~n" "TsDiff: ~p~n", -- cgit v1.2.3 From 6da22ec1d08090c038340e4acf4d96390cd74e9d Mon Sep 17 00:00:00 2001 From: Tuncer Ayaz Date: Wed, 31 Aug 2016 19:35:12 +0200 Subject: Print on_load error in a more useful style When printing the error term from on_load, insert a newline because the term is never short and will produce hard to read/copy output that is just hanging off to the right. --- lib/kernel/src/code_server.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index 48541ec500..3f220f4a6c 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -1414,7 +1414,7 @@ finish_on_load_report(Mod, Term) -> %% from the code_server process. spawn(fun() -> F = "The on_load function for module " - "~s returned ~P\n", + "~s returned:~n~P\n", %% Express the call as an apply to simplify %% the ext_mod_dep/1 test case. -- cgit v1.2.3 From db12deff975c013a0ac02cfb49a339f8e6af5938 Mon Sep 17 00:00:00 2001 From: Luis Rascao Date: Sat, 10 Sep 2016 21:02:47 +0200 Subject: Allow for easier enabling of distribution debug Also update already deprecated calls. --- lib/kernel/include/dist_util.hrl | 2 +- lib/kernel/src/global.erl | 16 ++++++++++------ lib/kernel/src/net_kernel.erl | 4 +--- 3 files changed, 12 insertions(+), 10 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/include/dist_util.hrl b/lib/kernel/include/dist_util.hrl index 320e916c04..e3d2fe0eb6 100644 --- a/lib/kernel/include/dist_util.hrl +++ b/lib/kernel/include/dist_util.hrl @@ -29,7 +29,7 @@ -endif. -ifdef(dist_trace). --define(trace(Fmt,Args), io:format("~p ~p:~s",[erlang:now(),node(),lists:flatten(io_lib:format(Fmt, Args))])). +-define(trace(Fmt,Args), io:format("~p ~p:~s",[erlang:timestamp(),node(),lists:flatten(io_lib:format(Fmt, Args))])). % Use the one below for config-file (early boot) connection tracing %-define(trace(Fmt,Args), erlang:display([erlang:now(),node(),lists:flatten(io_lib:format(Fmt, Args))])). -define(trace_factor,8). diff --git a/lib/kernel/src/global.erl b/lib/kernel/src/global.erl index 0c73ead7c5..5e8bc2ba5d 100644 --- a/lib/kernel/src/global.erl +++ b/lib/kernel/src/global.erl @@ -58,14 +58,18 @@ %%% In certain places in the server, calling io:format hangs everything, %%% so we'd better use erlang:display/1. %%% my_tracer is used in testsuites --define(trace(_), ok). +%% uncomment this if tracing is wanted +%%-define(DEBUG, true). +-ifdef(DEBUG). +-define(trace(T), erlang:display({format, node(), cs(), T})). + cs() -> + {_Big, Small, Tiny} = erlang:timestamp(), + (Small rem 100) * 100 + (Tiny div 10000). %-define(trace(T), (catch my_tracer ! {node(), {line,?LINE}, T})). - -%-define(trace(T), erlang:display({format, node(), cs(), T})). -%cs() -> -% {_Big, Small, Tiny} = now(), -% (Small rem 100) * 100 + (Tiny div 10000). +-else. +-define(trace(_), ok). +-endif. %% These are the protocol versions: %% Vsn 1 is the original protocol. diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index 0c679e7349..0a9f9316b0 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -26,15 +26,13 @@ %%-define(dist_debug, true). -%-define(DBG,erlang:display([?MODULE,?LINE])). - -ifdef(dist_debug). -define(debug(Term), erlang:display(Term)). -else. -define(debug(Term), ok). -endif. --ifdef(DEBUG). +-ifdef(dist_debug). -define(connect_failure(Node,Term), io:format("Net Kernel 2: Failed connection to node ~p, reason ~p~n", [Node,Term])). -- cgit v1.2.3 From 3489b9b689073f428a23f7fc7a67774b7dda07be Mon Sep 17 00:00:00 2001 From: Tuncer Ayaz Date: Sun, 18 Sep 2016 12:33:08 +0200 Subject: Use more correct delimiters for erl_nif.h include Anywhere but the beam sources we shouldn't #include "erl_nif.h", because what "erl_nif.h" does is: (1) fail to find it outside of -I dirs, (2) then treat it as if it was written like . Using skips (1). More information can be found in 6.10.2 of the C standard. Because the examples use "erl_nif.h", NIF projects in the Erlang ecosystem copy this verbatim and make the same mistake. --- lib/kernel/test/gen_tcp_api_SUITE_data/gen_tcp_api_SUITE.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/gen_tcp_api_SUITE_data/gen_tcp_api_SUITE.c b/lib/kernel/test/gen_tcp_api_SUITE_data/gen_tcp_api_SUITE.c index 2990ddda41..b91dca61d4 100644 --- a/lib/kernel/test/gen_tcp_api_SUITE_data/gen_tcp_api_SUITE.c +++ b/lib/kernel/test/gen_tcp_api_SUITE_data/gen_tcp_api_SUITE.c @@ -17,7 +17,7 @@ * * %CopyrightEnd% */ -#include "erl_nif.h" +#include #include #include -- cgit v1.2.3 From 3f3f25b23379b1afb15cc97462cf5d385690f5a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 22 Sep 2016 12:19:40 +0200 Subject: file_SUITE: Test file:write_file/3 Extend file_SUITE:read_write_file/1 to test file:write_file/3 which was not tested at all. While we are it, remove the superfluous roundtrips tests of term_to_binary/1 and binary_to_term/1. Those BIFs are tested in detail in other test suites (for example, binary_SUITE in emulator_test). --- lib/kernel/test/file_SUITE.erl | 54 ++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 23 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 5f049c6f99..c37d114a58 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -493,22 +493,13 @@ read_write_file(Config) when is_list(Config) -> %% Try writing and reading back some term SomeTerm = {"This term",{will,be},[written,$t,$o],1,file,[]}, - ok = ?FILE_MODULE:write_file(Name,term_to_binary(SomeTerm)), - {ok,Bin1} = ?FILE_MODULE:read_file(Name), - SomeTerm = binary_to_term(Bin1), + Bin1 = term_to_binary(SomeTerm), + ok = do_read_write_file(Name, Bin1), %% Try a "null" term NullTerm = [], - ok = ?FILE_MODULE:write_file(Name,term_to_binary(NullTerm)), - {ok,Bin2} = ?FILE_MODULE:read_file(Name), - NullTerm = binary_to_term(Bin2), - - %% Try some "complicated" types - BigNum = 123456789012345678901234567890, - ComplTerm = {self(),make_ref(),BigNum,3.14159}, - ok = ?FILE_MODULE:write_file(Name,term_to_binary(ComplTerm)), - {ok,Bin3} = ?FILE_MODULE:read_file(Name), - ComplTerm = binary_to_term(Bin3), + Bin2 = term_to_binary(NullTerm), + ok = do_read_write_file(Name, Bin2), %% Try reading a nonexistent file Name2 = filename:join(RootDir, @@ -519,25 +510,42 @@ read_write_file(Config) when is_list(Config) -> {error, enoent} = ?FILE_MODULE:read_file(''), %% Try writing to a bad filename - {error, enoent} = - ?FILE_MODULE:write_file("",term_to_binary(NullTerm)), + {error, enoent} = do_read_write_file("", Bin2), %% Try writing something else than a binary - {error, badarg} = ?FILE_MODULE:write_file(Name,{1,2,3}), - {error, badarg} = ?FILE_MODULE:write_file(Name,self()), + {error, badarg} = do_read_write_file(Name, {1,2,3}), + {error, badarg} = do_read_write_file(Name, self()), %% Some non-term binaries - ok = ?FILE_MODULE:write_file(Name,[]), - {ok,Bin4} = ?FILE_MODULE:read_file(Name), - 0 = byte_size(Bin4), + ok = do_read_write_file(Name, []), - ok = ?FILE_MODULE:write_file(Name,[Bin1,[],[[Bin2]]]), - {ok,Bin5} = ?FILE_MODULE:read_file(Name), - {Bin1,Bin2} = split_binary(Bin5,byte_size(Bin1)), + %% Write some iolists + ok = do_read_write_file(Name, [Bin1,[],[[Bin2]]]), + ok = do_read_write_file(Name, ["string",<<"binary">>]), + ok = do_read_write_file(Name, "pure string"), [] = flush(), ok. +do_read_write_file(Name, Data) -> + case ?FILE_MODULE:write_file(Name, Data) of + ok -> + BinData = iolist_to_binary(Data), + {ok,BinData} = ?FILE_MODULE:read_file(Name), + + ok = ?FILE_MODULE:write_file(Name, Data, []), + {ok,BinData} = ?FILE_MODULE:read_file(Name), + + ok = ?FILE_MODULE:write_file(Name, Data, [raw]), + {ok,BinData} = ?FILE_MODULE:read_file(Name), + + ok; + {error,_}=Res -> + Res = ?FILE_MODULE:write_file(Name, Data, []), + Res = ?FILE_MODULE:write_file(Name, Data, [raw]), + Res + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -- cgit v1.2.3 From 07b0f4315b079cce7aeef7babdba1bbb686de611 Mon Sep 17 00:00:00 2001 From: Nathan Long Date: Tue, 13 Sep 2016 08:36:42 -0400 Subject: Let file:write_file/3 use writev Previously, this function would turn any input into a single binary before writing. This meant it could not take advantage of the `writev` system call if it was given a list of binaries and told to write with `raw` mode. To see this, start an erlang shell on the parent commit, and also start this dtrace script: https://github.com/evanmiller/tracewrite like this: sudo dtrace -s tracewrite.d -p $(pgrep beam) In the erlang shell, run the following: file:write_file("/tmp/tmp.txt", [<<97,98>>, <<98,97>>], [raw]). In the dtrace output, you will see that the system call used is `write`. Now repeat with this commit, and you will see that `writev` is used. --- lib/kernel/src/file.erl | 49 ++++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 19 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index 58b601e456..1971df9038 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -397,25 +397,36 @@ write_file(Name, Bin) -> Modes :: [mode()], Reason :: posix() | badarg | terminated | system_limit. -write_file(Name, Bin, ModeList) when is_list(ModeList) -> - case make_binary(Bin) of - B when is_binary(B) -> - case open(Name, [binary, write | - lists:delete(binary, - lists:delete(write, ModeList))]) of - {ok, Handle} -> - case write(Handle, B) of - ok -> - close(Handle); - E1 -> - _ = close(Handle), - E1 - end; - E2 -> - E2 - end; - E3 -> - E3 +write_file(Name, IOData, ModeList) when is_list(ModeList) -> + case lists:member(raw, ModeList) of + true -> + %% For backwards compatibility of error messages + try iolist_size(IOData) of + _Size -> do_write_file(Name, IOData, ModeList) + catch + error:Error -> {error, Error} + end; + false -> + case make_binary(IOData) of + Bin when is_binary(Bin) -> + do_write_file(Name, Bin, ModeList); + Error -> + Error + end + end. + +do_write_file(Name, IOData, ModeList) -> + case open(Name, [binary, write | ModeList]) of + {ok, Handle} -> + case write(Handle, IOData) of + ok -> + close(Handle); + E1 -> + _ = close(Handle), + E1 + end; + E2 -> + E2 end. %% Obsolete, undocumented, local node only, don't use!. -- cgit v1.2.3 From 2e232b0449ca9aa0fd1854672cb87f902323ccdf Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 26 Sep 2016 16:58:03 +0200 Subject: Update appups in kernel and stdlib for OTP versions > 19.1 --- lib/kernel/src/kernel.appup.src | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src index d16e200cb3..82cf73cbda 100644 --- a/lib/kernel/src/kernel.appup.src +++ b/lib/kernel/src/kernel.appup.src @@ -18,9 +18,9 @@ %% %CopyrightEnd% {"%VSN%", %% Up from - max one major revision back - [{<<"5\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.* + [{<<"5\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.* {<<"4\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-18.* %% Down to - max one major revision back - [{<<"5\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.* + [{<<"5\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.* {<<"4\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-18.* }. -- cgit v1.2.3 From 024092849b5feab979018c05cfdab640c3f2cecf Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 27 Sep 2016 11:13:41 +0200 Subject: Update appups in kernel, stdlib and sasl for OTP-20 --- lib/kernel/src/kernel.appup.src | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src index 82cf73cbda..b505524471 100644 --- a/lib/kernel/src/kernel.appup.src +++ b/lib/kernel/src/kernel.appup.src @@ -18,9 +18,7 @@ %% %CopyrightEnd% {"%VSN%", %% Up from - max one major revision back - [{<<"5\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.* - {<<"4\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-18.* + [{<<"5\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-19.* %% Down to - max one major revision back - [{<<"5\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.* - {<<"4\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-18.* + [{<<"5\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-19.* }. -- cgit v1.2.3 From 714570912d678c10f4aa4b6a6692d958e0fc7fe7 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 28 Sep 2016 16:40:19 +0200 Subject: Document the order of directories added with code:add_pathsa/1 code:add_pathsa/1 reverts the list of directories when adding it at the beginning of the code path. The command line option '-pa' behaves in the same way. This is now documented. --- lib/kernel/doc/src/code.xml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml index 4db377bcde..3143cdc825 100644 --- a/lib/kernel/doc/src/code.xml +++ b/lib/kernel/doc/src/code.xml @@ -382,9 +382,14 @@ zip:create("mnesia-4.4.7.ez", Add directories to the beginning of the code path. -

Adds the directories in Dirs to the beginning of - the code path. If a Dir exists, it is removed - from the old position in the code path.

+

Traverses Dirs and adds + each Dir to the beginning of the code + path. This means that the order of Dirs + is reversed in the resulting code path. For example, if you + add [Dir1,Dir2], the resulting path will + be [Dir2,Dir1|OldCodePath].

+

If a Dir already exists in the code + path, it is removed from the old position.

Always returns ok, regardless of the validity of each individual Dir.

-- cgit v1.2.3 From 16d295c98f46e468ab1f7f4b3e6bfeb8f0f5749e Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 15 Sep 2016 21:18:28 +0200 Subject: hipe_unified_loader: Refactor rename all Addresses to FunDefs as they are lists of #fundef records --- lib/kernel/src/hipe_unified_loader.erl | 120 ++++++++++++++++----------------- 1 file changed, 60 insertions(+), 60 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl index 087cceb5d8..7b1e6c2591 100644 --- a/lib/kernel/src/hipe_unified_loader.erl +++ b/lib/kernel/src/hipe_unified_loader.erl @@ -229,15 +229,15 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch, Architecture) -> %% Patch references to code labels in data seg. ok = patch_consts(LabelMap, ConstAddr, CodeAddress, WriteWord), %% Find out which functions are being loaded (and where). - %% Note: Addresses are sorted descending. - {MFAs,Addresses} = exports(ExportMap, CodeAddress), + %% Note: FunDefs are sorted descending. + {MFAs,FunDefs} = exports(ExportMap, CodeAddress), %% Remove references to old versions of the module. ReferencesToPatch = get_refs_from(MFAs, []), %% io:format("References to patch: ~w~n", [ReferencesToPatch]), ok = remove_refs_from(MFAs), %% Patch all dynamic references in the code. %% Function calls, Atoms, Constants, System calls - ok = patch(Refs, CodeAddress, ConstMap2, Addresses, TrampolineMap), + ok = patch(Refs, CodeAddress, ConstMap2, FunDefs, TrampolineMap), %% Tell the system where the loaded funs are. %% (patches the BEAM code to redirect to native.) @@ -250,23 +250,23 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch, Architecture) -> lists:foreach(fun({FE, DestAddress}) -> hipe_bifs:set_native_address_in_fe(FE, DestAddress) end, erase(closures_to_patch)), - export_funs(Addresses), + export_funs(FunDefs), ok; BeamBinary when is_binary(BeamBinary) -> %% Find all closures in the code. [] = erase(closures_to_patch), %Clean up, assertion. ClosurePatches = find_closure_patches(Refs), AddressesOfClosuresToPatch = - calculate_addresses(ClosurePatches, CodeAddress, Addresses), - export_funs(Addresses), + calculate_addresses(ClosurePatches, CodeAddress, FunDefs), + export_funs(FunDefs), export_funs(Mod, MD5, BeamBinary, - Addresses, AddressesOfClosuresToPatch) + FunDefs, AddressesOfClosuresToPatch) end, %% Redirect references to the old module to the new module's BEAM stub. patch_to_emu_step2(OldReferencesToPatch), %% Patch referring functions to call the new function %% The call to export_funs/1 above updated the native addresses - %% for the targets, so passing 'Addresses' is not needed. + %% for the targets, so passing 'FunDefs' is not needed. redirect(ReferencesToPatch), %% Final clean up. _ = erase(hipe_patch_closures), @@ -373,29 +373,29 @@ trampoline_map_lookup(Primop, Map) -> exports(ExportMap, BaseAddress) -> exports(ExportMap, BaseAddress, [], []). -exports([Offset,M,F,A,IsClosure,IsExported|Rest], BaseAddress, MFAs, Addresses) -> +exports([Offset,M,F,A,IsClosure,IsExported|Rest], BaseAddress, MFAs, FunDefs) -> case IsExported andalso erlang:is_builtin(M, F, A) of true -> - exports(Rest, BaseAddress, MFAs, Addresses); + exports(Rest, BaseAddress, MFAs, FunDefs); _false -> MFA = {M,F,A}, Address = BaseAddress + Offset, FunDef = #fundef{address=Address, mfa=MFA, is_closure=IsClosure, is_exported=IsExported}, - exports(Rest, BaseAddress, [MFA|MFAs], [FunDef|Addresses]) + exports(Rest, BaseAddress, [MFA|MFAs], [FunDef|FunDefs]) end; -exports([], _, MFAs, Addresses) -> - {MFAs, Addresses}. +exports([], _, MFAs, FunDefs) -> + {MFAs, FunDefs}. mod({M,_F,_A}) -> M. %%------------------------------------------------------------------------ -calculate_addresses(PatchOffsets, Base, Addresses) -> +calculate_addresses(PatchOffsets, Base, FunDefs) -> RemoteOrLocal = local, % closure code refs are local [{Data, offsets_to_addresses(Offsets, Base), - get_native_address(DestMFA, Addresses, RemoteOrLocal)} || + get_native_address(DestMFA, FunDefs, RemoteOrLocal)} || {{DestMFA,_,_}=Data,Offsets} <- PatchOffsets]. offsets_to_addresses(Os, Base) -> @@ -424,7 +424,7 @@ find_closure_refs([], Refs) -> %%------------------------------------------------------------------------ -export_funs([FunDef | Addresses]) -> +export_funs([FunDef | FunDefs]) -> #fundef{address=Address, mfa=MFA, is_closure=IsClosure, is_exported=IsExported} = FunDef, ?IF_DEBUG({M,F,A} = MFA, no_debug), @@ -439,19 +439,19 @@ export_funs([FunDef | Addresses]) -> end, no_debug), hipe_bifs:set_funinfo_native_address(MFA, Address, IsExported), hipe_bifs:set_native_address(MFA, Address, IsClosure), - export_funs(Addresses); + export_funs(FunDefs); export_funs([]) -> ok. -export_funs(Mod, MD5, Beam, Addresses, ClosuresToPatch) -> - Fs = [{F,A,Address} || #fundef{address=Address, mfa={_M,F,A}} <- Addresses], +export_funs(Mod, MD5, Beam, FunDefs, ClosuresToPatch) -> + Fs = [{F,A,Address} || #fundef{address=Address, mfa={_M,F,A}} <- FunDefs], Mod = code:make_stub_module(Mod, Beam, {Fs,ClosuresToPatch,MD5}), ok. %%======================================================================== %% Patching %% @spec patch(refs(), BaseAddress::integer(), ConstAndZone::term(), -%% Addresses::term(), TrampolineMap::term()) -> 'ok'. +%% FunDefs::term(), TrampolineMap::term()) -> 'ok'. %% @type refs()=[{RefType::integer(), Reflist::reflist()} | refs()] %% %% @type reflist()= [{Data::term(), Offsets::offests()}|reflist()] @@ -463,39 +463,39 @@ export_funs(Mod, MD5, Beam, Addresses, ClosuresToPatch) -> %% (we use this to look up the address of a referred function only once). %% -patch([{Type,SortedRefs}|Rest], CodeAddress, ConstMap2, Addresses, TrampolineMap) -> +patch([{Type,SortedRefs}|Rest], CodeAddress, ConstMap2, FunDefs, TrampolineMap) -> ?debug_msg("Patching ~w at [~w+offset] with ~w\n", [Type,CodeAddress,SortedRefs]), case ?EXT2PATCH_TYPE(Type) of call_local -> - patch_call(SortedRefs, CodeAddress, Addresses, 'local', TrampolineMap); + patch_call(SortedRefs, CodeAddress, FunDefs, 'local', TrampolineMap); call_remote -> - patch_call(SortedRefs, CodeAddress, Addresses, 'remote', TrampolineMap); + patch_call(SortedRefs, CodeAddress, FunDefs, 'remote', TrampolineMap); Other -> - patch_all(Other, SortedRefs, CodeAddress, {ConstMap2,CodeAddress}, Addresses) + patch_all(Other, SortedRefs, CodeAddress, {ConstMap2,CodeAddress}, FunDefs) end, - patch(Rest, CodeAddress, ConstMap2, Addresses, TrampolineMap); + patch(Rest, CodeAddress, ConstMap2, FunDefs, TrampolineMap); patch([], _, _, _, _) -> ok. %%---------------------------------------------------------------- %% Handle a 'call_local' or 'call_remote' patch. %% -patch_call([{DestMFA,Offsets}|SortedRefs], BaseAddress, Addresses, RemoteOrLocal, TrampolineMap) -> +patch_call([{DestMFA,Offsets}|SortedRefs], BaseAddress, FunDefs, RemoteOrLocal, TrampolineMap) -> case bif_address(DestMFA) of false -> - %% Previous code used mfa_to_address(DestMFA, Addresses) + %% Previous code used mfa_to_address(DestMFA, FunDefs) %% here for local calls. That is wrong because even local - %% destinations may not be present in Addresses: they may + %% destinations may not be present in FunDefs: they may %% not have been compiled yet, or they may be BEAM-only %% functions (e.g. module_info). - DestAddress = get_native_address(DestMFA, Addresses, RemoteOrLocal), + DestAddress = get_native_address(DestMFA, FunDefs, RemoteOrLocal), Trampoline = trampoline_map_get(DestMFA, TrampolineMap), - patch_mfa_call_list(Offsets, BaseAddress, DestMFA, DestAddress, Addresses, RemoteOrLocal, Trampoline); + patch_mfa_call_list(Offsets, BaseAddress, DestMFA, DestAddress, FunDefs, RemoteOrLocal, Trampoline); BifAddress when is_integer(BifAddress) -> Trampoline = trampoline_map_lookup(DestMFA, TrampolineMap), patch_bif_call_list(Offsets, BaseAddress, BifAddress, Trampoline) end, - patch_call(SortedRefs, BaseAddress, Addresses, RemoteOrLocal, TrampolineMap); + patch_call(SortedRefs, BaseAddress, FunDefs, RemoteOrLocal, TrampolineMap); patch_call([], _, _, _, _) -> ok. @@ -506,12 +506,12 @@ patch_bif_call_list([Offset|Offsets], BaseAddress, BifAddress, Trampoline) -> patch_bif_call_list(Offsets, BaseAddress, BifAddress, Trampoline); patch_bif_call_list([], _, _, _) -> ok. -patch_mfa_call_list([Offset|Offsets], BaseAddress, DestMFA, DestAddress, Addresses, RemoteOrLocal, Trampoline) -> +patch_mfa_call_list([Offset|Offsets], BaseAddress, DestMFA, DestAddress, FunDefs, RemoteOrLocal, Trampoline) -> CallAddress = BaseAddress+Offset, - add_ref(DestMFA, CallAddress, Addresses, 'call', Trampoline, RemoteOrLocal), + add_ref(DestMFA, CallAddress, FunDefs, 'call', Trampoline, RemoteOrLocal), ?ASSERT(assert_local_patch(CallAddress)), patch_call_insn(CallAddress, DestAddress, Trampoline), - patch_mfa_call_list(Offsets, BaseAddress, DestMFA, DestAddress, Addresses, RemoteOrLocal, Trampoline); + patch_mfa_call_list(Offsets, BaseAddress, DestMFA, DestAddress, FunDefs, RemoteOrLocal, Trampoline); patch_mfa_call_list([], _, _, _, _, _, _) -> ok. patch_call_insn(CallAddress, DestAddress, Trampoline) -> @@ -522,33 +522,33 @@ patch_call_insn(CallAddress, DestAddress, Trampoline) -> %% ____________________________________________________________________ %% -patch_all(Type, [{Dest,Offsets}|Rest], BaseAddress, ConstAndZone, Addresses)-> - patch_all_offsets(Type, Dest, Offsets, BaseAddress, ConstAndZone, Addresses), - patch_all(Type, Rest, BaseAddress, ConstAndZone, Addresses); +patch_all(Type, [{Dest,Offsets}|Rest], BaseAddress, ConstAndZone, FunDefs)-> + patch_all_offsets(Type, Dest, Offsets, BaseAddress, ConstAndZone, FunDefs), + patch_all(Type, Rest, BaseAddress, ConstAndZone, FunDefs); patch_all(_, [], _, _, _) -> ok. patch_all_offsets(Type, Data, [Offset|Offsets], BaseAddress, - ConstAndZone, Addresses) -> + ConstAndZone, FunDefs) -> ?debug_msg("Patching ~w at [~w+~w] with ~w\n", [Type,BaseAddress,Offset, Data]), Address = BaseAddress + Offset, - patch_offset(Type, Data, Address, ConstAndZone, Addresses), + patch_offset(Type, Data, Address, ConstAndZone, FunDefs), ?debug_msg("Patching done\n",[]), - patch_all_offsets(Type, Data, Offsets, BaseAddress, ConstAndZone, Addresses); + patch_all_offsets(Type, Data, Offsets, BaseAddress, ConstAndZone, FunDefs); patch_all_offsets(_, _, [], _, _, _) -> ok. %%---------------------------------------------------------------- %% Handle any patch type except 'call_local' or 'call_remote'. %% -patch_offset(Type, Data, Address, ConstAndZone, Addresses) -> +patch_offset(Type, Data, Address, ConstAndZone, FunDefs) -> case Type of load_address -> - patch_load_address(Data, Address, ConstAndZone, Addresses); + patch_load_address(Data, Address, ConstAndZone, FunDefs); load_atom -> Atom = Data, patch_atom(Address, Atom); sdesc -> - patch_sdesc(Data, Address, ConstAndZone, Addresses); + patch_sdesc(Data, Address, ConstAndZone, FunDefs); x86_abs_pcrel -> patch_instr(Address, Data, x86_abs_pcrel) %% _ -> @@ -561,37 +561,37 @@ patch_atom(Address, Atom) -> patch_instr(Address, hipe_bifs:atom_to_word(Atom), atom). patch_sdesc(?STACK_DESC(SymExnRA, FSize, Arity, Live), - Address, {_ConstMap2,CodeAddress}, _Addresses) -> + Address, {_ConstMap2,CodeAddress}, _FunDefs) -> ExnRA = case SymExnRA of [] -> 0; % No catch LabelOffset -> CodeAddress + LabelOffset end, ?ASSERT(assert_local_patch(Address)), - DBG_MFA = ?IF_DEBUG(address_to_mfa_lth(Address, _Addresses), {undefined,undefined,0}), + DBG_MFA = ?IF_DEBUG(address_to_mfa_lth(Address, _FunDefs), {undefined,undefined,0}), hipe_bifs:enter_sdesc({Address, ExnRA, FSize, Arity, Live, DBG_MFA}). %%---------------------------------------------------------------- %% Handle a 'load_address'-type patch. %% -patch_load_address(Data, Address, ConstAndZone, Addresses) -> +patch_load_address(Data, Address, ConstAndZone, FunDefs) -> case Data of {local_function,DestMFA} -> - patch_load_mfa(Address, DestMFA, Addresses, 'local'); + patch_load_mfa(Address, DestMFA, FunDefs, 'local'); {remote_function,DestMFA} -> - patch_load_mfa(Address, DestMFA, Addresses, 'remote'); + patch_load_mfa(Address, DestMFA, FunDefs, 'remote'); {constant,Name} -> {ConstMap2,_CodeAddress} = ConstAndZone, ConstAddress = find_const(Name, ConstMap2), patch_instr(Address, ConstAddress, constant); {closure,{DestMFA,Uniq,Index}} -> - patch_closure(DestMFA, Uniq, Index, Address, Addresses); + patch_closure(DestMFA, Uniq, Index, Address, FunDefs); {c_const,CConst} -> patch_instr(Address, bif_address(CConst), c_const) end. -patch_closure(DestMFA, Uniq, Index, Address, Addresses) -> +patch_closure(DestMFA, Uniq, Index, Address, FunDefs) -> case get(hipe_patch_closures) of false -> []; % This is taken care of when registering the module. @@ -602,7 +602,7 @@ patch_closure(DestMFA, Uniq, Index, Address, Addresses) -> %% address into the fun entry to ensure that the native code cannot %% be called until it has been completely fixed up. RemoteOrLocal = local, % closure code refs are local - DestAddress = get_native_address(DestMFA, Addresses, RemoteOrLocal), + DestAddress = get_native_address(DestMFA, FunDefs, RemoteOrLocal), BEAMAddress = hipe_bifs:fun_to_address(DestMFA), FE = hipe_bifs:get_fe(mod(DestMFA), {Uniq, Index, BEAMAddress}), put(closures_to_patch, [{FE,DestAddress}|get(closures_to_patch)]), @@ -616,12 +616,12 @@ patch_closure(DestMFA, Uniq, Index, Address, Addresses) -> %% Patch an instruction loading the address of an MFA. %% RemoteOrLocal ::= 'remote' | 'local' %% -patch_load_mfa(CodeAddress, DestMFA, Addresses, RemoteOrLocal) -> +patch_load_mfa(CodeAddress, DestMFA, FunDefs, RemoteOrLocal) -> DestAddress = case bif_address(DestMFA) of false -> - NativeAddress = get_native_address(DestMFA, Addresses, RemoteOrLocal), - add_ref(DestMFA, CodeAddress, Addresses, 'load_mfa', [], RemoteOrLocal), + NativeAddress = get_native_address(DestMFA, FunDefs, RemoteOrLocal), + add_ref(DestMFA, CodeAddress, FunDefs, 'load_mfa', [], RemoteOrLocal), NativeAddress; BifAddress when is_integer(BifAddress) -> BifAddress @@ -772,7 +772,7 @@ find_const(ConstNo, []) -> %%---------------------------------------------------------------- %% Record that the code at address 'Address' has a reference %% of type 'RefType' ('call' or 'load_mfa') to 'CalleeMFA'. -%% 'Addresses' must be an address-descending list from exports/2. +%% 'FunDefs' must be an address-descending list from exports/2. %% %% If 'RefType' is 'call', then 'Trampoline' may be the address %% of a stub branching to 'CalleeMFA', where the stub is reachable @@ -785,8 +785,8 @@ find_const(ConstNo, []) -> %% -record(ref, {caller_mfa, address, ref_type, trampoline, remote_or_local}). %% -add_ref(CalleeMFA, Address, Addresses, RefType, Trampoline, RemoteOrLocal) -> - CallerMFA = address_to_mfa_lth(Address, Addresses), +add_ref(CalleeMFA, Address, FunDefs, RefType, Trampoline, RemoteOrLocal) -> + CallerMFA = address_to_mfa_lth(Address, FunDefs), %% just a sanity assertion below true = case RemoteOrLocal of local -> @@ -904,13 +904,13 @@ remove_refs_from(MFAs) -> %%-------------------------------------------------------------------- %% To find the native code of an MFA we need to look in 3 places: -%% 1. If it is compiled now look in the Addresses data structure. +%% 1. If it is compiled now look in the FunDefs data structure. %% 2. Then look in native_addresses from module info. %% 3. Then (the function might have been singled compiled) look in %% hipe_funinfo %% If all else fails create a native stub for the MFA -get_native_address(MFA, Addresses, RemoteOrLocal) -> - case mfa_to_address(MFA, Addresses, RemoteOrLocal) of +get_native_address(MFA, FunDefs, RemoteOrLocal) -> + case mfa_to_address(MFA, FunDefs, RemoteOrLocal) of Adr when is_integer(Adr) -> Adr; false -> IsRemote = -- cgit v1.2.3 From 099c60de4033d7b397d4b3fb47f183b52fcba855 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 15 Sep 2016 21:21:10 +0200 Subject: erts: Improve hipe load/upgrade/purge machinery A step toward better integration of hipe load and purge Highlights: * code_server no longer needs to call hipe_unified_loader:post_beam_load/1 Instead new internal function hipe_redirect_to_module() is called by loading BIFs to patch native call sites if needed. * hipe_purge_module() is called by erts_internal:purge_module/2 to purge any native code. * struct hipe_mfa_info redesigned and only used for exported functions that are called from or implemented by native code. A list of native call sites (struct hipe_ref) are kept for each hipe_mfa_info. * struct hipe_sdesc used by hipe_find_mfa_from_ra() to build native stack traces. --- lib/kernel/src/code_server.erl | 24 +-- lib/kernel/src/hipe_unified_loader.erl | 233 ++++++--------------- lib/kernel/test/code_SUITE.erl | 30 +-- lib/kernel/test/code_SUITE_data/upgrade_client.erl | 88 +++++++- lib/kernel/test/code_SUITE_data/upgradee.erl | 12 ++ 5 files changed, 187 insertions(+), 200 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index 835b7eb588..90ecb22074 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -1130,7 +1130,7 @@ try_load_module_2(File, Mod, Bin, From, Architecture, #state{moddb=Db}=St) -> case catch hipe_unified_loader:load_native_code(Mod, Bin, Architecture) of {module,Mod} = Module -> - ets:insert(Db, [{{native,Mod},true},{Mod,File}]), + ets:insert(Db, {Mod,File}), {reply,Module,St}; no_native -> try_load_module_3(File, Mod, Bin, From, Architecture, St); @@ -1139,10 +1139,9 @@ try_load_module_2(File, Mod, Bin, From, Architecture, {reply,ok,St} end. -try_load_module_3(File, Mod, Bin, From, Architecture, St0) -> +try_load_module_3(File, Mod, Bin, From, _Architecture, St0) -> Action = fun({module,_}=Module, #state{moddb=Db}=S) -> ets:insert(Db, {Mod,File}), - post_beam_load([Mod], Architecture, S), {reply,Module,S}; ({error,on_load_failure}=Error, S) -> {reply,Error,S}; @@ -1153,24 +1152,14 @@ try_load_module_3(File, Mod, Bin, From, Architecture, St0) -> Res = erlang:load_module(Mod, Bin), handle_on_load(Res, Action, Mod, From, St0). -hipe_result_to_status(Result, #state{moddb=Db}) -> +hipe_result_to_status(Result, #state{}) -> case Result of - {module,Mod} -> - ets:insert(Db, [{{native,Mod},true}]), + {module,_} -> Result; _ -> {error,Result} end. -post_beam_load(_, undefined, _) -> - %% HiPE is disabled. - ok; -post_beam_load(Mods0, _Architecture, #state{moddb=Db}) -> - %% post_beam_load/2 can potentially be very expensive because it - %% blocks multi-scheduling. Therefore, we only want to call - %% it with modules that are known to have native code loaded. - Mods = [M || M <- Mods0, ets:member(Db, {native,M})], - hipe_unified_loader:post_beam_load(Mods). int_list([H|T]) when is_integer(H) -> int_list(T); int_list([_|_]) -> false; @@ -1313,15 +1302,12 @@ abort_if_sticky(L, Db) -> [_|_] -> {error,Sticky} end. -do_finish_loading(Prepared, #state{moddb=Db}=St) -> +do_finish_loading(Prepared, #state{moddb=Db}) -> MagicBins = [B || {_,{B,_}} <- Prepared], case erlang:finish_loading(MagicBins) of ok -> MFs = [{M,F} || {M,{_,F}} <- Prepared], true = ets:insert(Db, MFs), - Ms = [M || {M,_} <- MFs], - Architecture = erlang:system_info(hipe_architecture), - post_beam_load(Ms, Architecture, St), ok; {Reason,Ms} -> {error,[{M,Reason} || M <- Ms]} diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl index 7b1e6c2591..75a5995695 100644 --- a/lib/kernel/src/hipe_unified_loader.erl +++ b/lib/kernel/src/hipe_unified_loader.erl @@ -41,10 +41,11 @@ % I think the real solution would be to let BIF erlang:load_module/2 redirect all % hipe calls to the module and thereby remove post_beam_load. +% SVERK: Can we remove -compile(no_native) now when post_beam_load is gone? + -export([chunk_name/1, %% Only the code and code_server modules may call the entries below! load_native_code/3, - post_beam_load/1, load_module/4, load/3]). @@ -101,15 +102,13 @@ word_size(Architecture) -> load_native_code(_Mod, _Bin, undefined) -> no_native; load_native_code(Mod, Bin, Architecture) when is_atom(Mod), is_binary(Bin) -> - %% patch_to_emu(Mod), case code:get_chunk(Bin, chunk_name(Architecture)) of undefined -> no_native; NativeCode when is_binary(NativeCode) -> erlang:system_flag(multi_scheduling, block_normal), try - OldReferencesToPatch = patch_to_emu_step1(Mod), - case load_module(Mod, NativeCode, Bin, OldReferencesToPatch, - Architecture) of + put(hipe_patch_closures, false), + case load_common(Mod, NativeCode, Bin, Architecture) of bad_crc -> no_native; Result -> Result end @@ -120,22 +119,6 @@ load_native_code(Mod, Bin, Architecture) when is_atom(Mod), is_binary(Bin) -> %%======================================================================== --spec post_beam_load([module()]) -> 'ok'. - -post_beam_load([])-> - ok; -post_beam_load([_|_]=Mods) -> - erlang:system_flag(multi_scheduling, block_normal), - try - _ = [patch_to_emu(Mod) || Mod <- Mods], - ok - after - erlang:system_flag(multi_scheduling, unblock_normal) - end, - ok. - -%%======================================================================== - version_check(Version, Mod) when is_atom(Mod) -> Ver = ?VERSION_STRING(), case Version < Ver of @@ -153,19 +136,12 @@ version_check(Version, Mod) when is_atom(Mod) -> load_module(Mod, Bin, Beam, Architecture) -> erlang:system_flag(multi_scheduling, block_normal), try - load_module_nosmp(Mod, Bin, Beam, Architecture) + put(hipe_patch_closures, false), + load_common(Mod, Bin, Beam, Architecture) after erlang:system_flag(multi_scheduling, unblock_normal) end. -load_module_nosmp(Mod, Bin, Beam, Architecture) -> - load_module(Mod, Bin, Beam, [], Architecture). - -load_module(Mod, Bin, Beam, OldReferencesToPatch, Architecture) -> - ?debug_msg("************ Loading Module ~w ************\n",[Mod]), - %% Loading a whole module, let the BEAM loader patch closures. - put(hipe_patch_closures, false), - load_common(Mod, Bin, Beam, OldReferencesToPatch, Architecture). %%======================================================================== @@ -175,20 +151,17 @@ load_module(Mod, Bin, Beam, OldReferencesToPatch, Architecture) -> load(Mod, Bin, Architecture) -> erlang:system_flag(multi_scheduling, block_normal), try - load_nosmp(Mod, Bin, Architecture) + ?debug_msg("********* Loading funs in module ~w *********\n",[Mod]), + %% Loading just some functions in a module; patch closures separately. + put(hipe_patch_closures, true), + load_common(Mod, Bin, [], Architecture) after erlang:system_flag(multi_scheduling, unblock_normal) end. -load_nosmp(Mod, Bin, Architecture) -> - ?debug_msg("********* Loading funs in module ~w *********\n",[Mod]), - %% Loading just some functions in a module; patch closures separately. - put(hipe_patch_closures, true), - load_common(Mod, Bin, [], [], Architecture). - %%------------------------------------------------------------------------ -load_common(Mod, Bin, Beam, OldReferencesToPatch, Architecture) -> +load_common(Mod, Bin, Beam, Architecture) -> %% Unpack the binary. [{Version, CheckSum}, ConstAlign, ConstSize, ConstMap, LabelMap, ExportMap, @@ -228,15 +201,14 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch, Architecture) -> Architecture), %% Patch references to code labels in data seg. ok = patch_consts(LabelMap, ConstAddr, CodeAddress, WriteWord), + %% Find out which functions are being loaded (and where). - %% Note: FunDefs are sorted descending. - {MFAs,FunDefs} = exports(ExportMap, CodeAddress), - %% Remove references to old versions of the module. - ReferencesToPatch = get_refs_from(MFAs, []), - %% io:format("References to patch: ~w~n", [ReferencesToPatch]), - ok = remove_refs_from(MFAs), + %% Note: FunDefs are sorted descending address order. + FunDefs = exports(ExportMap, CodeAddress), + %% Patch all dynamic references in the code. %% Function calls, Atoms, Constants, System calls + ok = patch(Refs, CodeAddress, ConstMap2, FunDefs, TrampolineMap), %% Tell the system where the loaded funs are. @@ -250,7 +222,7 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch, Architecture) -> lists:foreach(fun({FE, DestAddress}) -> hipe_bifs:set_native_address_in_fe(FE, DestAddress) end, erase(closures_to_patch)), - export_funs(FunDefs), + set_beam_call_traps(FunDefs), ok; BeamBinary when is_binary(BeamBinary) -> %% Find all closures in the code. @@ -259,15 +231,10 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch, Architecture) -> AddressesOfClosuresToPatch = calculate_addresses(ClosurePatches, CodeAddress, FunDefs), export_funs(FunDefs), - export_funs(Mod, MD5, BeamBinary, - FunDefs, AddressesOfClosuresToPatch) + make_beam_stub(Mod, MD5, BeamBinary, FunDefs, AddressesOfClosuresToPatch, + CodeAddress, byte_size(CodeBinary)) end, - %% Redirect references to the old module to the new module's BEAM stub. - patch_to_emu_step2(OldReferencesToPatch), - %% Patch referring functions to call the new function - %% The call to export_funs/1 above updated the native addresses - %% for the targets, so passing 'FunDefs' is not needed. - redirect(ReferencesToPatch), + %% Final clean up. _ = erase(hipe_patch_closures), _ = erase(hipe_assert_code_area), @@ -371,21 +338,21 @@ trampoline_map_lookup(Primop, Map) -> is_exported :: boolean()}). exports(ExportMap, BaseAddress) -> - exports(ExportMap, BaseAddress, [], []). + exports(ExportMap, BaseAddress, []). -exports([Offset,M,F,A,IsClosure,IsExported|Rest], BaseAddress, MFAs, FunDefs) -> +exports([Offset,M,F,A,IsClosure,IsExported|Rest], BaseAddress, FunDefs) -> case IsExported andalso erlang:is_builtin(M, F, A) of true -> - exports(Rest, BaseAddress, MFAs, FunDefs); + exports(Rest, BaseAddress, FunDefs); _false -> MFA = {M,F,A}, Address = BaseAddress + Offset, FunDef = #fundef{address=Address, mfa=MFA, is_closure=IsClosure, is_exported=IsExported}, - exports(Rest, BaseAddress, [MFA|MFAs], [FunDef|FunDefs]) + exports(Rest, BaseAddress, [FunDef|FunDefs]) end; -exports([], _, MFAs, FunDefs) -> - {MFAs, FunDefs}. +exports([], _, FunDefs) -> + FunDefs. mod({M,_F,_A}) -> M. @@ -424,9 +391,9 @@ find_closure_refs([], Refs) -> %%------------------------------------------------------------------------ -export_funs([FunDef | FunDefs]) -> +set_beam_call_traps([FunDef | FunDefs]) -> #fundef{address=Address, mfa=MFA, is_closure=IsClosure, - is_exported=IsExported} = FunDef, + is_exported=_IsExported} = FunDef, ?IF_DEBUG({M,F,A} = MFA, no_debug), ?IF_DEBUG( case IsClosure of @@ -437,15 +404,32 @@ export_funs([FunDef | FunDefs]) -> ?debug_msg("LINKING: ~w:~w/~w to closure (0x~.16b)\n", [M,F,A, Address]) end, no_debug), - hipe_bifs:set_funinfo_native_address(MFA, Address, IsExported), hipe_bifs:set_native_address(MFA, Address, IsClosure), + set_beam_call_traps(FunDefs); +set_beam_call_traps([]) -> + ok. + +export_funs([FunDef | FunDefs]) -> + #fundef{address=Address, mfa=MFA, is_closure=_IsClosure, + is_exported=IsExported} = FunDef, + ?IF_DEBUG({M,F,A} = MFA, no_debug), + ?IF_DEBUG( + case _IsClosure of + false -> + ?debug_msg("LINKING: ~w:~w/~w to (0x~.16b)\n", + [M,F,A, Address]); + true -> + ?debug_msg("LINKING: ~w:~w/~w to closure (0x~.16b)\n", + [M,F,A, Address]) + end, no_debug), + hipe_bifs:set_funinfo_native_address(MFA, Address, IsExported), export_funs(FunDefs); export_funs([]) -> ok. -export_funs(Mod, MD5, Beam, FunDefs, ClosuresToPatch) -> +make_beam_stub(Mod, MD5, Beam, FunDefs, ClosuresToPatch, CodeAddress, CodeSize) -> Fs = [{F,A,Address} || #fundef{address=Address, mfa={_M,F,A}} <- FunDefs], - Mod = code:make_stub_module(Mod, Beam, {Fs,ClosuresToPatch,MD5}), + Mod = code:make_stub_module(Mod, Beam, {Fs,ClosuresToPatch,MD5,CodeAddress,CodeSize}), ok. %%======================================================================== @@ -515,8 +499,7 @@ patch_mfa_call_list([Offset|Offsets], BaseAddress, DestMFA, DestAddress, FunDefs patch_mfa_call_list([], _, _, _, _, _, _) -> ok. patch_call_insn(CallAddress, DestAddress, Trampoline) -> - %% This assertion is false when we're called from redirect/2. - %% ?ASSERT(assert_local_patch(CallAddress)), + ?ASSERT(assert_local_patch(CallAddress)), hipe_bifs:patch_call(CallAddress, DestAddress, Trampoline). %% ____________________________________________________________________ @@ -561,15 +544,15 @@ patch_atom(Address, Atom) -> patch_instr(Address, hipe_bifs:atom_to_word(Atom), atom). patch_sdesc(?STACK_DESC(SymExnRA, FSize, Arity, Live), - Address, {_ConstMap2,CodeAddress}, _FunDefs) -> + Address, {_ConstMap2,CodeAddress}, FunDefs) -> ExnRA = case SymExnRA of [] -> 0; % No catch LabelOffset -> CodeAddress + LabelOffset end, ?ASSERT(assert_local_patch(Address)), - DBG_MFA = ?IF_DEBUG(address_to_mfa_lth(Address, _FunDefs), {undefined,undefined,0}), - hipe_bifs:enter_sdesc({Address, ExnRA, FSize, Arity, Live, DBG_MFA}). + MFA = address_to_mfa_lth(Address, FunDefs), + hipe_bifs:enter_sdesc({Address, ExnRA, FSize, Arity, Live, MFA, get(hipe_patch_closures)}). %%---------------------------------------------------------------- @@ -617,6 +600,7 @@ patch_closure(DestMFA, Uniq, Index, Address, FunDefs) -> %% RemoteOrLocal ::= 'remote' | 'local' %% patch_load_mfa(CodeAddress, DestMFA, FunDefs, RemoteOrLocal) -> + ?ASSERT(assert_local_patch(CodeAddress)), DestAddress = case bif_address(DestMFA) of false -> @@ -626,7 +610,6 @@ patch_load_mfa(CodeAddress, DestMFA, FunDefs, RemoteOrLocal) -> BifAddress when is_integer(BifAddress) -> BifAddress end, - ?ASSERT(assert_local_patch(CodeAddress)), patch_instr(CodeAddress, DestAddress, 'load_mfa'). %%---------------------------------------------------------------- @@ -781,23 +764,17 @@ find_const(ConstNo, []) -> %% RemoteOrLocal ::= 'remote' | 'local'. %% -%% -%% -record(ref, {caller_mfa, address, ref_type, trampoline, remote_or_local}). -%% - add_ref(CalleeMFA, Address, FunDefs, RefType, Trampoline, RemoteOrLocal) -> CallerMFA = address_to_mfa_lth(Address, FunDefs), - %% just a sanity assertion below - true = case RemoteOrLocal of - local -> - {M1,_,_} = CalleeMFA, - {M2,_,_} = CallerMFA, - M1 =:= M2; - remote -> - true - end, - %% io:format("Adding ref ~w\n",[{CallerMFA, CalleeMFA, Address, RefType}]), - hipe_bifs:add_ref(CalleeMFA, {CallerMFA,Address,RefType,Trampoline,RemoteOrLocal}). + case RemoteOrLocal of + local -> + %% just a sanity assertion + {M,_,_} = CalleeMFA, + {M,_,_} = CallerMFA; + remote -> + hipe_bifs:add_ref(CalleeMFA, {CallerMFA,Address,RefType,Trampoline,get(hipe_patch_closures)}) + end, + ok. % For FunDefs sorted from low to high addresses address_to_mfa_lth(Address, FunDefs) -> @@ -825,81 +802,6 @@ address_to_mfa_lth(_Address, [], Prev) -> %% ?error_msg("Local adddress not found ~w\n",[Address]), %% exit({?MODULE, local_address_not_found}). -%%---------------------------------------------------------------- -%% Change callers of the given module to instead trap to BEAM. -%% load_native_code/3 calls this just before loading native code. -%% -patch_to_emu(Mod) -> - patch_to_emu_step2(patch_to_emu_step1(Mod)). - -%% Step 1 must occur before the loading of native code updates -%% references information or creates a new BEAM stub module. -patch_to_emu_step1(Mod) -> - case is_loaded(Mod) of - true -> - %% Get exported functions - MFAs = [{Mod,Fun,Arity} || {Fun,Arity} <- Mod:module_info(exports)], - %% get_refs_from/2 only finds references from compiled static - %% call sites to the module, but some native address entries - %% were added as the result of dynamic apply calls. We must - %% purge them too, but we have no explicit record of them. - %% Therefore invalidate all native addresses for the module. - hipe_bifs:invalidate_funinfo_native_addresses(MFAs), - %% Find all call sites that call these MFAs. As a side-effect, - %% create native stubs for any MFAs that are referred. - ReferencesToPatch = get_refs_from(MFAs, []), - ok = remove_refs_from(MFAs), - ReferencesToPatch; - false -> - %% The first time we load the module, no redirection needs to be done. - [] - end. - -%% Step 2 must occur after the new BEAM stub module is created. -patch_to_emu_step2(ReferencesToPatch) -> - redirect(ReferencesToPatch). - --spec is_loaded(Module::atom()) -> boolean(). -%% @doc Checks whether a module is loaded or not. -is_loaded(M) when is_atom(M) -> - try hipe_bifs:fun_to_address({M,module_info,0}) of - I when is_integer(I) -> true - catch _:_ -> false - end. - -%%-------------------------------------------------------------------- -%% Given a list of MFAs, tag them with their referred_from references. -%% The resulting {MFA,Refs} list is later passed to redirect/1, once -%% the MFAs have been bound to (possibly new) native-code addresses. -%% -get_refs_from(MFAs, []) -> - mark_referred_from(MFAs), - MFAs. - -mark_referred_from(MFAs) -> - lists:foreach(fun(MFA) -> hipe_bifs:mark_referred_from(MFA) end, MFAs). - -%%-------------------------------------------------------------------- -%% Given a list of MFAs with referred_from references, update their -%% callers to refer to their new native-code addresses. -%% -%% The {MFA,Refs} list must come from get_refs_from/2. -%% -redirect(MFAs) -> - lists:foreach(fun(MFA) -> hipe_bifs:redirect_referred_from(MFA) end, MFAs). - -%%-------------------------------------------------------------------- -%% Given a list of MFAs, remove all referred_from references having -%% any of them as CallerMFA. -%% -%% This is the only place using refers_to. Whenever a reference is -%% added from CallerMFA to CalleeMFA, CallerMFA is added to CalleeMFA's -%% referred_from list, and CalleeMFA is added to CallerMFA's refers_to -%% list. The refers_to list is used here to find the CalleeMFAs whose -%% referred_from lists should be updated. -%% -remove_refs_from(MFAs) -> - lists:foreach(fun(MFA) -> hipe_bifs:remove_refs_from(MFA) end, MFAs). %%-------------------------------------------------------------------- @@ -913,12 +815,13 @@ get_native_address(MFA, FunDefs, RemoteOrLocal) -> case mfa_to_address(MFA, FunDefs, RemoteOrLocal) of Adr when is_integer(Adr) -> Adr; false -> - IsRemote = case RemoteOrLocal of - remote -> true; - local -> false - end, - hipe_bifs:find_na_or_make_stub(MFA, IsRemote) + remote -> + hipe_bifs:find_na_or_make_stub(MFA); + local -> + ?error_msg("Local function ~p not found\n",[MFA]), + exit({function_not_found,MFA}) + end end. mfa_to_address(MFA, [#fundef{address=Adr, mfa=MFA, diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index c5167efa56..1be4c364d5 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -483,23 +483,25 @@ load_binary(Config) when is_list(Config) -> code:delete(code_b_test), ok. + upgrade(Config) -> DataDir = proplists:get_value(data_dir, Config), - %%T = [beam, hipe], - T = [beam], - - [upgrade_do(DataDir, Client, U1, U2, O1, O2) - || Client<-T, U1<-T, U2<-T, O1<-T, O2<-T], + T = [beam, hipe], + %%T = [beam], + %%T = [hipe], + [upgrade_do(DataDir, Client, T) || Client <- T], ok. -upgrade_do(DataDir, Client, U1, U2, O1, O2) -> +upgrade_do(DataDir, Client, T) -> compile_load(upgrade_client, DataDir, undefined, Client), - upgrade_client:run(DataDir, U1, U2, O1, O2), + [upgrade_client:run(DataDir, U1, U2, O1, O2) + || U1<-T, U2<-T, O1<-T, O2<-T], ok. compile_load(Mod, Dir, Ver, CodeType) -> + erlang:display({"{{{{{{{{{{{{{{{{Loading",Mod,Ver,CodeType}), Version = case Ver of undefined -> io:format("Compiling '~p' as ~p\n", [Mod, CodeType]), @@ -516,9 +518,15 @@ compile_load(Mod, Dir, Ver, CodeType) -> CompOpts = [binary, report] ++ Target ++ Version, Src = filename:join(Dir, atom_to_list(Mod) ++ ".erl"), + T1 = erlang:now(), {ok,Mod,Code} = compile:file(Src, CompOpts), + T2 = erlang:now(), ObjFile = filename:basename(Src,".erl") ++ ".beam", {module,Mod} = code:load_binary(Mod, ObjFile, Code), + T3 = erlang:now(), + io:format("Compile time ~p ms, Load time ~p ms\n", + [timer:now_diff(T2,T1) div 1000, timer:now_diff(T3,T2) div 1000]), + erlang:display({"}}}}}}}}}}}}}}}Loaded",Mod,Ver,CodeType}), ok. dir_req(Config) when is_list(Config) -> @@ -810,8 +818,6 @@ check_funs({'$M_EXPR','$F_EXPR',_}, {code_server,start_link,1}]) -> 0; check_funs({'$M_EXPR','$F_EXPR',_}, [{erlang,spawn_link,1},{code_server,start_link,1}]) -> 0; -check_funs({'$M_EXPR',module_info,1}, - [{hipe_unified_loader,patch_to_emu_step1,1} | _]) -> 0; check_funs({'$M_EXPR','$F_EXPR',2}, [{hipe_unified_loader,write_words,3} | _]) -> 0; check_funs({'$M_EXPR','$F_EXPR',2}, @@ -823,11 +829,7 @@ check_funs({'$M_EXPR','$F_EXPR',2}, {hipe_unified_loader,sort_and_write,5} | _]) -> 0; check_funs({'$M_EXPR','$F_EXPR',1}, [{lists,foreach,2}, - {hipe_unified_loader,patch_consts,3} | _]) -> 0; -check_funs({'$M_EXPR','$F_EXPR',1}, - [{lists,foreach,2}, - {hipe_unified_loader,mark_referred_from,1}, - {hipe_unified_loader,get_refs_from,2}| _]) -> 0; + {hipe_unified_loader,patch_consts,4} | _]) -> 0; check_funs({'$M_EXPR',warning_msg,2}, [{code_server,finish_on_load_report,2} | _]) -> 0; check_funs({'$M_EXPR','$F_EXPR',1}, diff --git a/lib/kernel/test/code_SUITE_data/upgrade_client.erl b/lib/kernel/test/code_SUITE_data/upgrade_client.erl index bb655e01d3..7ca0df7f5c 100644 --- a/lib/kernel/test/code_SUITE_data/upgrade_client.erl +++ b/lib/kernel/test/code_SUITE_data/upgrade_client.erl @@ -9,6 +9,8 @@ run(Dir, Upgradee1, Upgradee2, Other1, Other2) -> %% Load version 1 of upgradee code_SUITE:compile_load(upgradee, Dir, 1, Upgradee1), + Tracer = start_tracing(), + ?line 1 = upgradee:exp1(), ?line 1 = upgradee:exp1exp2(), ?line 1 = upgradee:exp1loc2(), @@ -56,6 +58,16 @@ run(Dir, Upgradee1, Upgradee2, Other1, Other2) -> ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1), ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2), + Env1 = "Env1", + put(loc1_fun, upgradee:get_local_fun(Env1)), + erlang:display(sverk_break), + ?line {1,Env1} = (get(loc1_fun))(), + + put(exp1exp2_fun, upgradee:get_exp1exp2_fun()), + ?line 1 = (get(exp1exp2_fun))(), + + ?line 13 = check_tracing(Tracer), + %% %% Load version 1 of other %% @@ -78,6 +90,8 @@ run(Dir, Upgradee1, Upgradee2, Other1, Other2) -> ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp2), ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2), + ?line 5 = check_tracing(Tracer), + %% %% Load version 2 of upgradee %% @@ -130,6 +144,15 @@ run(Dir, Upgradee1, Upgradee2, Other1, Other2) -> ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp2), ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2), + ?line {1,Env1} = (get(loc1_fun))(), + Env2 = "Env2", + put(loc2_fun, upgradee:get_local_fun(Env2)), + ?line {2,Env2} = (get(loc2_fun))(), + + ?line 2 = (get(exp1exp2_fun))(), + + ?line 10 = check_tracing(Tracer), + %% %% Load version 2 of other %% @@ -182,17 +205,26 @@ run(Dir, Upgradee1, Upgradee2, Other1, Other2) -> ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1loc2), ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2), + ?line {1,Env1} = (get(loc1_fun))(), + ?line {2,Env2} = (get(loc2_fun))(), + ?line 2 = (get(exp1exp2_fun))(), + + ?line 10 = check_tracing(Tracer), %% %% Upgrade proxy to version 2 %% P ! upgrade_order, - %% - io:format("Delete version 2 of 'upgradee'\n",[]), + io:format("Purge version 1 of 'upgradee'\n",[]), %% + put(loc1_fun,undefined), code:purge(upgradee), + + %% + io:format("Delete version 2 of 'upgradee'\n",[]), + %% code:delete(upgradee), ?line {'EXIT',{undef,_}} = (catch upgradee:exp2()), @@ -239,17 +271,24 @@ run(Dir, Upgradee1, Upgradee2, Other1, Other2) -> ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1loc2), ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1loc2), ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2), + + ?line {'EXIT',{undef,_}} = (catch (get(exp1exp2_fun))()), + ?line 14 = check_tracing(Tracer), + unlink(P), exit(P, die_please), io:format("Purge 'upgradee'\n",[]), + put(loc2_fun,undefined), code:purge(upgradee), io:format("Delete and purge 'other'\n",[]), code:purge(other), code:delete(other), code:purge(other), + + stop_tracing(Tracer), ok. proxy_call(Pid, CallType, Func) -> @@ -257,3 +296,48 @@ proxy_call(Pid, CallType, Func) -> receive {Pid, call_result, Func, Ret} -> Ret end. + + +start_tracing() -> + Self = self(), + {Tracer,_} = spawn_opt(fun() -> tracer_loop(Self) end, [link,monitor]), + ?line 1 = erlang:trace_pattern({error_handler,undefined_function,3}, + true, [global]), + ?line 1 = erlang:trace(Self, true, [call,{tracer,Tracer}]), + Tracer. + + +tracer_loop(Receiver) -> + receive + die_please -> + ok; + {do_trace_delivered, Tracee} -> + _ = erlang:trace_delivered(Tracee), + tracer_loop(Receiver); + + Msg -> + Receiver ! Msg, + tracer_loop(Receiver) + end. + +check_tracing(Tracer) -> + Tracer ! {do_trace_delivered, self()}, + check_tracing_loop(0). + +check_tracing_loop(N) -> + Self = self(), + receive + {trace, _Pid, call, {_M, _F, _Args}} = Msg -> + io:format("Trace: ~p\n",[Msg]), + check_tracing_loop(N+1); + {trace_delivered, Self, _} -> + N + end. + + +stop_tracing(Tracer) -> + erlang:trace(self(), false, [call]), + Tracer ! die_please, + receive + {'DOWN', _, process, Tracer, _} -> ok + end. diff --git a/lib/kernel/test/code_SUITE_data/upgradee.erl b/lib/kernel/test/code_SUITE_data/upgradee.erl index 62b1d95e30..8ca660c19c 100644 --- a/lib/kernel/test/code_SUITE_data/upgradee.erl +++ b/lib/kernel/test/code_SUITE_data/upgradee.erl @@ -8,6 +8,9 @@ -export([exp1/0]). % only exported in v1 -export([exp1loc2/0]). % exported in v1, local in v2 -export([exp1exp2/0]). % exported in v1 and v2 +-export([get_local_fun/1]). +-export([get_exp1exp2_fun/0]). +-export([exp1exp2_fun/0]). exp1() -> ?VERSION. loc1() -> ?VERSION. @@ -20,6 +23,9 @@ loc1() -> ?VERSION. -export([exp2/0]). -export([loc1exp2/0]). -export([exp1exp2/0]). +-export([get_local_fun/1]). +-export([get_exp1exp2_fun/0]). +-export([exp1exp2_fun/0]). exp2() -> ?VERSION. loc2() -> ?VERSION. @@ -31,6 +37,12 @@ exp1loc2() -> ?VERSION. loc1exp2() -> ?VERSION. loc1loc2() -> ?VERSION. +get_local_fun(Env) -> fun() -> {?VERSION,Env} end. +get_exp1exp2_fun() -> fun ?MODULE:exp1exp2_fun/0. + +exp1exp2_fun() -> + ?VERSION. + dispatch_loop() -> receive upgrade_order -> -- cgit v1.2.3 From 4d96d297044274c46deda1adfc567297449a9ba9 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 15 Sep 2016 19:23:27 +0200 Subject: erts: Remove unused hipe_bifs:code_size and hipe_bifs:update_code_size --- lib/kernel/src/hipe_unified_loader.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl index 75a5995695..b3acd9ea74 100644 --- a/lib/kernel/src/hipe_unified_loader.erl +++ b/lib/kernel/src/hipe_unified_loader.erl @@ -195,7 +195,7 @@ load_common(Mod, Bin, Beam, Architecture) -> CalleeMFAs = find_callee_mfas(Refs, Architecture), %% Write the code to memory. {CodeAddress,Trampolines} = - enter_code(CodeSize, CodeBinary, CalleeMFAs, Mod, Beam), + enter_code(CodeSize, CodeBinary, CalleeMFAs), %% Construct CalleeMFA-to-trampoline mapping. TrampolineMap = mk_trampoline_map(CalleeMFAs, Trampolines, Architecture), @@ -863,9 +863,8 @@ assert_local_patch(Address) when is_integer(Address) -> %% Beam: nil() | binary() (used as a flag) -enter_code(CodeSize, CodeBinary, CalleeMFAs, Mod, Beam) -> +enter_code(CodeSize, CodeBinary, CalleeMFAs) -> true = byte_size(CodeBinary) =:= CodeSize, - hipe_bifs:update_code_size(Mod, Beam, CodeSize), {CodeAddress,Trampolines} = hipe_bifs:enter_code(CodeBinary, CalleeMFAs), ?init_assert_patch(CodeAddress, byte_size(CodeBinary)), {CodeAddress,Trampolines}. -- cgit v1.2.3 From 81cf5bc05e502809398116eaa0b78deb9336d68b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 10 Oct 2016 16:39:31 +0200 Subject: kernel: Add test pdict_SUITE:literals to get some coverage in beam_load.c:hash_internal_genop_arg(). --- lib/kernel/test/pdict_SUITE.erl | 60 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/test/pdict_SUITE.erl b/lib/kernel/test/pdict_SUITE.erl index 638d99176e..d105952df9 100644 --- a/lib/kernel/test/pdict_SUITE.erl +++ b/lib/kernel/test/pdict_SUITE.erl @@ -32,10 +32,13 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, mixed/1, + literals/1, simple/1, complicated/1, heavy/1, simple_all_keys/1, info/1]). -export([init_per_testcase/2, end_per_testcase/2]). -export([other_process/2]). +-export([put_do/2, get_do/1, erase_do/1]). + init_per_testcase(_Case, Config) -> Config. @@ -48,6 +51,7 @@ suite() -> all() -> [simple, complicated, heavy, simple_all_keys, info, + literals, mixed]. groups() -> @@ -418,3 +422,59 @@ do_mixed([GoalN | _]=Goals, CurrN, Array0, C, Rand0) -> Array2 = array:resize(CurrN-1, Array1), do_mixed(Goals, CurrN-1, Array2, C+1, Rand2) end. + +%% Test hash precalculation of literal keys +literals(_Config) -> + %% Put literal -> get variable + put(1742, "1742"), + "1742" = ?MODULE:get_do(1742), + "1742" = ?MODULE:erase_do(1742), + + put(-1742, "-1742"), + "-1742" = ?MODULE:get_do(-1742), + "-1742" = ?MODULE:erase_do(-1742), + + put([], "NIL"), + "NIL" = ?MODULE:get_do([]), + "NIL" = ?MODULE:erase_do([]), + + put(<<"binary">>, "binary"), + "binary" = ?MODULE:get_do(<<"binary">>), + "binary" = ?MODULE:erase_do(<<"binary">>), + + BigBin = <<"A large binary with a lot of bytes to make it go off heap as shared and reference counted">>, + put(BigBin, "bigbin"), + "bigbin" = ?MODULE:get_do(BigBin), + "bigbin" = ?MODULE:erase_do(BigBin), + + %% Put variable -> get literal + ?MODULE:put_do(4217, "4217"), + "4217" = get(4217), + "4217" = erase(4217), + + ?MODULE:put_do(-4217, "-4217"), + "-4217" = get(-4217), + "-4217" = erase(-4217), + + ?MODULE:put_do([], "NIL"), + "NIL" = get([]), + "NIL" = erase([]), + + ?MODULE:put_do(<<"bytes">>, "bytes"), + "bytes" = get(<<"bytes">>), + "bytes" = erase(<<"bytes">>), + + ?MODULE:put_do(BigBin, "BigBin"), + "BigBin" = get(BigBin), + "BigBin" = erase(BigBin), + + ok. + +put_do(K, V) -> + put(K, V). + +get_do(K) -> + get(K). + +erase_do(K) -> + erase(K). -- cgit v1.2.3 From 39072836944d00c288beebfd98b14593f9609006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20L=C3=A5ng?= Date: Thu, 19 May 2016 17:38:54 +0200 Subject: Add a loader state for HiPE code loading Just like the BEAM loader state (as returned by erlang:prepare_loading/2), the HiPE loader state is contained in a magic binary. Eventually, we will separate HiPE loading into a prepare and a finalise phase, like the BEAM loader, where the prepare phase will be implemented by hipe_unified_loader and the finalise phase be implemented in C by hipe_load.c and beam_load.c, making prepare side-effect free and finalise atomic. The finalise phase will be exposed through the erlang:finish_loading/1 API, just like the BEAM loader, as this will allow HiPE and BEAM modules to be mixed in the same atomic "commit". The usage of a loader state makes it easier to keep track of all resources allocated during loading, and will not only make it easy to prevent leaks when hipe_unified_loader crashes, but also paves the way for proper, leak-free, unloading of HiPE modules. --- lib/kernel/src/hipe_unified_loader.erl | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl index b3acd9ea74..6245646263 100644 --- a/lib/kernel/src/hipe_unified_loader.erl +++ b/lib/kernel/src/hipe_unified_loader.erl @@ -188,14 +188,16 @@ load_common(Mod, Bin, Beam, Architecture) -> put(closures_to_patch, []), WordSize = word_size(Architecture), WriteWord = write_word_fun(WordSize), + LoaderState = hipe_bifs:alloc_loader_state(Mod), %% Create data segment {ConstAddr,ConstMap2} = - create_data_segment(ConstAlign, ConstSize, ConstMap, WriteWord), + create_data_segment(ConstAlign, ConstSize, ConstMap, WriteWord, + LoaderState), %% Find callees for which we may need trampolines. CalleeMFAs = find_callee_mfas(Refs, Architecture), %% Write the code to memory. {CodeAddress,Trampolines} = - enter_code(CodeSize, CodeBinary, CalleeMFAs), + enter_code(CodeSize, CodeBinary, CalleeMFAs, LoaderState), %% Construct CalleeMFA-to-trampoline mapping. TrampolineMap = mk_trampoline_map(CalleeMFAs, Trampolines, Architecture), @@ -231,8 +233,8 @@ load_common(Mod, Bin, Beam, Architecture) -> AddressesOfClosuresToPatch = calculate_addresses(ClosurePatches, CodeAddress, FunDefs), export_funs(FunDefs), - make_beam_stub(Mod, MD5, BeamBinary, FunDefs, AddressesOfClosuresToPatch, - CodeAddress, byte_size(CodeBinary)) + make_beam_stub(Mod, LoaderState, MD5, BeamBinary, FunDefs, + AddressesOfClosuresToPatch) end, %% Final clean up. @@ -427,9 +429,9 @@ export_funs([FunDef | FunDefs]) -> export_funs([]) -> ok. -make_beam_stub(Mod, MD5, Beam, FunDefs, ClosuresToPatch, CodeAddress, CodeSize) -> +make_beam_stub(Mod, LoaderState, MD5, Beam, FunDefs, ClosuresToPatch) -> Fs = [{F,A,Address} || #fundef{address=Address, mfa={_M,F,A}} <- FunDefs], - Mod = code:make_stub_module(Mod, Beam, {Fs,ClosuresToPatch,MD5,CodeAddress,CodeSize}), + Mod = code:make_stub_module(LoaderState, Beam, {Fs,ClosuresToPatch,MD5}), ok. %%======================================================================== @@ -685,9 +687,9 @@ bif_address(Name) when is_atom(Name) -> %% memory, and produces a ConstMap2 mapping each constant's ConstNo to %% its runtime address, tagged if the constant is a term. %% -create_data_segment(DataAlign, DataSize, DataList, WriteWord) -> +create_data_segment(DataAlign, DataSize, DataList, WriteWord, LoaderState) -> %%io:format("create_data_segment: \nDataAlign: ~p\nDataSize: ~p\nDataList: ~p\n",[DataAlign,DataSize,DataList]), - DataAddress = hipe_bifs:alloc_data(DataAlign, DataSize), + DataAddress = hipe_bifs:alloc_data(DataAlign, DataSize, LoaderState), enter_data(DataList, [], DataAddress, DataSize, WriteWord). enter_data(List, ConstMap2, DataAddress, DataSize, WriteWord) -> @@ -863,9 +865,10 @@ assert_local_patch(Address) when is_integer(Address) -> %% Beam: nil() | binary() (used as a flag) -enter_code(CodeSize, CodeBinary, CalleeMFAs) -> +enter_code(CodeSize, CodeBinary, CalleeMFAs, LoaderState) -> true = byte_size(CodeBinary) =:= CodeSize, - {CodeAddress,Trampolines} = hipe_bifs:enter_code(CodeBinary, CalleeMFAs), + {CodeAddress,Trampolines} = hipe_bifs:enter_code(CodeBinary, CalleeMFAs, + LoaderState), ?init_assert_patch(CodeAddress, byte_size(CodeBinary)), {CodeAddress,Trampolines}. -- cgit v1.2.3 From 966098ceb9dd9d18e9bcd37cd06b96045903e320 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 20 Sep 2016 16:16:50 +0200 Subject: erts: Move new hipe ref and sdesc lists to loader state --- lib/kernel/src/hipe_unified_loader.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl index 6245646263..cde2eb059a 100644 --- a/lib/kernel/src/hipe_unified_loader.erl +++ b/lib/kernel/src/hipe_unified_loader.erl @@ -189,6 +189,7 @@ load_common(Mod, Bin, Beam, Architecture) -> WordSize = word_size(Architecture), WriteWord = write_word_fun(WordSize), LoaderState = hipe_bifs:alloc_loader_state(Mod), + put(hipe_loader_state, LoaderState), %% Create data segment {ConstAddr,ConstMap2} = create_data_segment(ConstAlign, ConstSize, ConstMap, WriteWord, @@ -238,6 +239,7 @@ load_common(Mod, Bin, Beam, Architecture) -> end, %% Final clean up. + _ = erase(hipe_loader_state), _ = erase(hipe_patch_closures), _ = erase(hipe_assert_code_area), ?debug_msg("****************Loader Finished****************\n", []), @@ -554,7 +556,8 @@ patch_sdesc(?STACK_DESC(SymExnRA, FSize, Arity, Live), end, ?ASSERT(assert_local_patch(Address)), MFA = address_to_mfa_lth(Address, FunDefs), - hipe_bifs:enter_sdesc({Address, ExnRA, FSize, Arity, Live, MFA, get(hipe_patch_closures)}). + hipe_bifs:enter_sdesc({Address, ExnRA, FSize, Arity, Live, MFA, get(hipe_patch_closures)}, + get(hipe_loader_state)). %%---------------------------------------------------------------- @@ -774,7 +777,9 @@ add_ref(CalleeMFA, Address, FunDefs, RefType, Trampoline, RemoteOrLocal) -> {M,_,_} = CalleeMFA, {M,_,_} = CallerMFA; remote -> - hipe_bifs:add_ref(CalleeMFA, {CallerMFA,Address,RefType,Trampoline,get(hipe_patch_closures)}) + hipe_bifs:add_ref(CalleeMFA, {CallerMFA,Address,RefType,Trampoline, + get(hipe_patch_closures), + get(hipe_loader_state)}) end, ok. -- cgit v1.2.3 From e77d7a8417368617c4c228af9556a7f6a8f3e84c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 28 Sep 2016 20:55:40 +0200 Subject: erts: Fix early hipe patch loading by introducing hipe_bifs:commit_patch_load/1 that creates the HipeModule. --- lib/kernel/src/hipe_unified_loader.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl index cde2eb059a..03116758de 100644 --- a/lib/kernel/src/hipe_unified_loader.erl +++ b/lib/kernel/src/hipe_unified_loader.erl @@ -225,6 +225,7 @@ load_common(Mod, Bin, Beam, Architecture) -> lists:foreach(fun({FE, DestAddress}) -> hipe_bifs:set_native_address_in_fe(FE, DestAddress) end, erase(closures_to_patch)), + ok = hipe_bifs:commit_patch_load(LoaderState), set_beam_call_traps(FunDefs), ok; BeamBinary when is_binary(BeamBinary) -> @@ -556,7 +557,7 @@ patch_sdesc(?STACK_DESC(SymExnRA, FSize, Arity, Live), end, ?ASSERT(assert_local_patch(Address)), MFA = address_to_mfa_lth(Address, FunDefs), - hipe_bifs:enter_sdesc({Address, ExnRA, FSize, Arity, Live, MFA, get(hipe_patch_closures)}, + hipe_bifs:enter_sdesc({Address, ExnRA, FSize, Arity, Live, MFA}, get(hipe_loader_state)). @@ -778,7 +779,6 @@ add_ref(CalleeMFA, Address, FunDefs, RefType, Trampoline, RemoteOrLocal) -> {M,_,_} = CallerMFA; remote -> hipe_bifs:add_ref(CalleeMFA, {CallerMFA,Address,RefType,Trampoline, - get(hipe_patch_closures), get(hipe_loader_state)}) end, ok. -- cgit v1.2.3 From cb987678ff56142029758e0e84fa97fa90003b4a Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 30 Sep 2016 18:05:25 +0200 Subject: kernel: Fix strange 'ok' return value from code_server when failing to load native code. --- lib/kernel/src/code_server.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index 90ecb22074..418b0c50e1 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -1136,7 +1136,7 @@ try_load_module_2(File, Mod, Bin, From, Architecture, try_load_module_3(File, Mod, Bin, From, Architecture, St); Error -> error_msg("Native loading of ~ts failed: ~p\n", [File,Error]), - {reply,ok,St} + {reply,{error,Error},St} end. try_load_module_3(File, Mod, Bin, From, _Architecture, St0) -> -- cgit v1.2.3 From ee0531308b0cc57f0312e1d0676635969d8a8029 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 7 Oct 2016 16:55:08 +0200 Subject: kernel: Fix code_SUITE:upgrade for non-hipe --- lib/kernel/test/code_SUITE.erl | 7 ++++--- lib/kernel/test/code_SUITE_data/upgrade_client.erl | 1 - 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 1be4c364d5..96f8ff2c5f 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -487,9 +487,10 @@ load_binary(Config) when is_list(Config) -> upgrade(Config) -> DataDir = proplists:get_value(data_dir, Config), - T = [beam, hipe], - %%T = [beam], - %%T = [hipe], + T = case erlang:system_info(hipe_architecture) of + undefined -> [beam]; + _ -> [beam,hipe] + end, [upgrade_do(DataDir, Client, T) || Client <- T], ok. diff --git a/lib/kernel/test/code_SUITE_data/upgrade_client.erl b/lib/kernel/test/code_SUITE_data/upgrade_client.erl index 7ca0df7f5c..98d36f6014 100644 --- a/lib/kernel/test/code_SUITE_data/upgrade_client.erl +++ b/lib/kernel/test/code_SUITE_data/upgrade_client.erl @@ -60,7 +60,6 @@ run(Dir, Upgradee1, Upgradee2, Other1, Other2) -> Env1 = "Env1", put(loc1_fun, upgradee:get_local_fun(Env1)), - erlang:display(sverk_break), ?line {1,Env1} = (get(loc1_fun))(), put(exp1exp2_fun, upgradee:get_exp1exp2_fun()), -- cgit v1.2.3 From 2b2de6173d4ba5506ae9b1692a19e01cf0d299c8 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 17 Oct 2016 11:32:38 +0200 Subject: kernel,hipe: Fix dialyzer warnings --- lib/kernel/src/code.erl | 4 ++-- lib/kernel/src/hipe_unified_loader.erl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index 8d0a2fbf66..622b27080c 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -116,8 +116,8 @@ get_chunk(_, _) -> is_module_native(_) -> erlang:nif_error(undef). --spec make_stub_module(Module, Beam, Info) -> Module when - Module :: module(), +-spec make_stub_module(LoaderState, Beam, Info) -> module() when + LoaderState :: binary(), Beam :: binary(), Info :: {list(), list(), binary()}. diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl index 03116758de..a7e30a048f 100644 --- a/lib/kernel/src/hipe_unified_loader.erl +++ b/lib/kernel/src/hipe_unified_loader.erl @@ -772,7 +772,7 @@ find_const(ConstNo, []) -> add_ref(CalleeMFA, Address, FunDefs, RefType, Trampoline, RemoteOrLocal) -> CallerMFA = address_to_mfa_lth(Address, FunDefs), - case RemoteOrLocal of + _ = case RemoteOrLocal of local -> %% just a sanity assertion {M,_,_} = CalleeMFA, -- cgit v1.2.3 From 8fcdf80aba9115bc6f3b345b83e53be1bbf69778 Mon Sep 17 00:00:00 2001 From: Magnus Henoch Date: Mon, 17 Oct 2016 11:05:43 +0100 Subject: Clarify permission bits in file.xml In commit 3d70cee4034e, this text was changed to suggest that a list of Unix-specific permission bits would follow, but no such list was added. Let's revert back to the previous wording, so people won't look for a list that isn't there. --- lib/kernel/doc/src/file.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index 09497482cf..b674b3ca93 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -1477,8 +1477,8 @@ f.txt: {person, "kalle", 25}. 16#400

set group id on execution

-

On Unix platforms, the following bits - can also be set:

+

On Unix platforms, other bits than those listed above + may be set.

links = integer() >= 0 @@ -2042,8 +2042,8 @@ f.txt: {person, "kalle", 25}. 16#400

Set group id on execution

-

On Unix platforms, the following bits - can also be set.

+

On Unix platforms, other bits than those listed above + may be set.

uid = integer() >= 0 -- cgit v1.2.3 From 0c7e4330ddced978934dd63520795554b29244a2 Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Tue, 18 Oct 2016 13:08:38 +0200 Subject: Code cleanup to remove confusing assertion The code of add_ref/6 contained an assertion that somehow appeared twice. Also, a recent commit (2b2de61) suppressed an unmatched return warning in a non-optimal way. By changing the return type of hipe_bifs:add_ref/2 to be 'ok' instead of [] the code can be cleaned up and simplified. While at it, fixed the wrong indentation of some nearby code. --- lib/kernel/src/hipe_unified_loader.erl | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl index a7e30a048f..ce7408a3cc 100644 --- a/lib/kernel/src/hipe_unified_loader.erl +++ b/lib/kernel/src/hipe_unified_loader.erl @@ -772,27 +772,26 @@ find_const(ConstNo, []) -> add_ref(CalleeMFA, Address, FunDefs, RefType, Trampoline, RemoteOrLocal) -> CallerMFA = address_to_mfa_lth(Address, FunDefs), - _ = case RemoteOrLocal of + case RemoteOrLocal of local -> %% just a sanity assertion - {M,_,_} = CalleeMFA, - {M,_,_} = CallerMFA; + {_M,_,_} = CalleeMFA, + ok; remote -> hipe_bifs:add_ref(CalleeMFA, {CallerMFA,Address,RefType,Trampoline, get(hipe_loader_state)}) - end, - ok. + end. -% For FunDefs sorted from low to high addresses +%% For FunDefs sorted from low to high addresses address_to_mfa_lth(Address, FunDefs) -> - case address_to_mfa_lth(Address, FunDefs, false) of - false -> - ?error_msg("Local adddress not found ~w\n",[Address]), - exit({?MODULE, local_address_not_found}); - MFA -> - MFA - end. - + case address_to_mfa_lth(Address, FunDefs, false) of + false -> + ?error_msg("Local adddress not found ~w\n",[Address]), + exit({?MODULE, local_address_not_found}); + MFA -> + MFA + end. + address_to_mfa_lth(Address, [#fundef{address=Adr, mfa=MFA}|Rest], Prev) -> if Address < Adr -> Prev; @@ -802,7 +801,7 @@ address_to_mfa_lth(Address, [#fundef{address=Adr, mfa=MFA}|Rest], Prev) -> address_to_mfa_lth(_Address, [], Prev) -> Prev. -% For FunDefs sorted from high to low addresses +%% For FunDefs sorted from high to low addresses %% address_to_mfa_htl(Address, [#fundef{address=Adr, mfa=MFA}|_Rest]) when Address >= Adr -> MFA; %% address_to_mfa_htl(Address, [_ | Rest]) -> address_to_mfa_htl(Address, Rest); %% address_to_mfa_htl(Address, []) -> @@ -868,8 +867,6 @@ assert_local_patch(Address) when is_integer(Address) -> %% ____________________________________________________________________ %% -%% Beam: nil() | binary() (used as a flag) - enter_code(CodeSize, CodeBinary, CalleeMFAs, LoaderState) -> true = byte_size(CodeBinary) =:= CodeSize, {CodeAddress,Trampolines} = hipe_bifs:enter_code(CodeBinary, CalleeMFAs, -- cgit v1.2.3 From a79e2569f5a6d6a622bd78b04326a6ad976c0a4c Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Tue, 18 Oct 2016 14:03:41 +0200 Subject: Restore the erroneously taken out assertion --- lib/kernel/src/hipe_unified_loader.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl index ce7408a3cc..04ec1479cb 100644 --- a/lib/kernel/src/hipe_unified_loader.erl +++ b/lib/kernel/src/hipe_unified_loader.erl @@ -774,8 +774,9 @@ add_ref(CalleeMFA, Address, FunDefs, RefType, Trampoline, RemoteOrLocal) -> CallerMFA = address_to_mfa_lth(Address, FunDefs), case RemoteOrLocal of local -> - %% just a sanity assertion - {_M,_,_} = CalleeMFA, + %% assert that the callee and caller are from the same module + {M,_,_} = CalleeMFA, + {M,_,_} = CallerMFA, ok; remote -> hipe_bifs:add_ref(CalleeMFA, {CallerMFA,Address,RefType,Trampoline, -- cgit v1.2.3 From c8f3ec52716064e0835e2e84329b2257be22acb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20L=C3=A5ng?= Date: Tue, 18 Oct 2016 15:54:40 +0200 Subject: hipe_llvm: Use different symbols for remote calls --- lib/kernel/test/code_SUITE.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 96f8ff2c5f..237f1c46b6 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -489,7 +489,11 @@ upgrade(Config) -> T = case erlang:system_info(hipe_architecture) of undefined -> [beam]; - _ -> [beam,hipe] + _ -> + case hipe:llvm_support_available() of + false -> [beam,hipe]; + true -> [beam,hipe,hipe_llvm] + end end, [upgrade_do(DataDir, Client, T) || Client <- T], @@ -514,7 +518,8 @@ compile_load(Mod, Dir, Ver, CodeType) -> end, Target = case CodeType of beam -> []; - hipe -> [native] + hipe -> [native]; + hipe_llvm -> [native,{hipe,to_llvm}] end, CompOpts = [binary, report] ++ Target ++ Version, -- cgit v1.2.3 From 72026389d89a2e9530d3594df742588bace7079a Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 18 Oct 2016 17:59:06 +0200 Subject: kernel: Reduce test logging from code_SUITE:upgrade --- lib/kernel/test/code_SUITE.erl | 4 ++-- lib/kernel/test/code_SUITE_data/upgrade_client.erl | 28 +++++++++++++--------- 2 files changed, 19 insertions(+), 13 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 237f1c46b6..8d517d144e 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -506,7 +506,7 @@ upgrade_do(DataDir, Client, T) -> ok. compile_load(Mod, Dir, Ver, CodeType) -> - erlang:display({"{{{{{{{{{{{{{{{{Loading",Mod,Ver,CodeType}), + %%erlang:display({"{{{{{{{{{{{{{{{{Loading",Mod,Ver,CodeType}), Version = case Ver of undefined -> io:format("Compiling '~p' as ~p\n", [Mod, CodeType]), @@ -532,7 +532,7 @@ compile_load(Mod, Dir, Ver, CodeType) -> T3 = erlang:now(), io:format("Compile time ~p ms, Load time ~p ms\n", [timer:now_diff(T2,T1) div 1000, timer:now_diff(T3,T2) div 1000]), - erlang:display({"}}}}}}}}}}}}}}}Loaded",Mod,Ver,CodeType}), + %%erlang:display({"}}}}}}}}}}}}}}}Loaded",Mod,Ver,CodeType}), ok. dir_req(Config) when is_list(Config) -> diff --git a/lib/kernel/test/code_SUITE_data/upgrade_client.erl b/lib/kernel/test/code_SUITE_data/upgrade_client.erl index 98d36f6014..faa18e1410 100644 --- a/lib/kernel/test/code_SUITE_data/upgrade_client.erl +++ b/lib/kernel/test/code_SUITE_data/upgrade_client.erl @@ -65,7 +65,7 @@ run(Dir, Upgradee1, Upgradee2, Other1, Other2) -> put(exp1exp2_fun, upgradee:get_exp1exp2_fun()), ?line 1 = (get(exp1exp2_fun))(), - ?line 13 = check_tracing(Tracer), + ok = check_tracing(Tracer, 13), %% %% Load version 1 of other @@ -89,7 +89,7 @@ run(Dir, Upgradee1, Upgradee2, Other1, Other2) -> ?line {'EXIT',{undef,_}} = proxy_call(P, other, exp2), ?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2), - ?line 5 = check_tracing(Tracer), + ok = check_tracing(Tracer, 5), %% %% Load version 2 of upgradee @@ -150,7 +150,7 @@ run(Dir, Upgradee1, Upgradee2, Other1, Other2) -> ?line 2 = (get(exp1exp2_fun))(), - ?line 10 = check_tracing(Tracer), + ok = check_tracing(Tracer, 10), %% %% Load version 2 of other @@ -208,7 +208,7 @@ run(Dir, Upgradee1, Upgradee2, Other1, Other2) -> ?line {2,Env2} = (get(loc2_fun))(), ?line 2 = (get(exp1exp2_fun))(), - ?line 10 = check_tracing(Tracer), + ok = check_tracing(Tracer, 10), %% %% Upgrade proxy to version 2 @@ -273,7 +273,7 @@ run(Dir, Upgradee1, Upgradee2, Other1, Other2) -> ?line {'EXIT',{undef,_}} = (catch (get(exp1exp2_fun))()), - ?line 14 = check_tracing(Tracer), + ok = check_tracing(Tracer, 14), unlink(P), exit(P, die_please), @@ -319,18 +319,24 @@ tracer_loop(Receiver) -> tracer_loop(Receiver) end. -check_tracing(Tracer) -> +check_tracing(Tracer, Expected) -> Tracer ! {do_trace_delivered, self()}, - check_tracing_loop(0). + case check_tracing_loop(0,[]) of + {Expected,_} -> + ok; + {Got, MsgList} -> + io:format("Expected ~p trace msg, got ~p:\n~p\n", + [Expected, Got, lists:reverse(MsgList)]), + "Trace msg mismatch" + end. -check_tracing_loop(N) -> +check_tracing_loop(N, MsgList) -> Self = self(), receive {trace, _Pid, call, {_M, _F, _Args}} = Msg -> - io:format("Trace: ~p\n",[Msg]), - check_tracing_loop(N+1); + check_tracing_loop(N+1, [Msg | MsgList]); {trace_delivered, Self, _} -> - N + {N, MsgList} end. -- cgit v1.2.3 From 7a48251e07d662261de68798de6a91ab8ad5ffe7 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 20 Oct 2016 15:16:47 +0200 Subject: kernel: Test hipe and hipe_llvm separate in code_SUITE:upgrade. Combining both takes time. --- lib/kernel/test/code_SUITE.erl | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 8d517d144e..342f9ad5c3 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -487,16 +487,21 @@ load_binary(Config) when is_list(Config) -> upgrade(Config) -> DataDir = proplists:get_value(data_dir, Config), - T = case erlang:system_info(hipe_architecture) of - undefined -> [beam]; - _ -> - case hipe:llvm_support_available() of - false -> [beam,hipe]; - true -> [beam,hipe,hipe_llvm] - end - end, - - [upgrade_do(DataDir, Client, T) || Client <- T], + case erlang:system_info(hipe_architecture) of + undefined -> + upgrade_do(DataDir, beam, [beam]); + + _ -> + T = [beam, hipe], + [upgrade_do(DataDir, Client, T) || Client <- T], + + case hipe:llvm_support_available() of + false -> ok; + true -> + T2 = [beam, hipe_llvm], + [upgrade_do(DataDir, Client, T2) || Client <- T2] + end + end, ok. upgrade_do(DataDir, Client, T) -> -- cgit v1.2.3 From f96c3f2e8c02ec3e50708fed6e58227bd234318e Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 20 Oct 2016 15:17:37 +0200 Subject: kernel: Fix hipe compiler option to_llvm --- lib/kernel/test/code_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 342f9ad5c3..29b3f7caaf 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -524,7 +524,7 @@ compile_load(Mod, Dir, Ver, CodeType) -> Target = case CodeType of beam -> []; hipe -> [native]; - hipe_llvm -> [native,{hipe,to_llvm}] + hipe_llvm -> [native,{hipe,[to_llvm]}] end, CompOpts = [binary, report] ++ Target ++ Version, -- cgit v1.2.3 From 70526ab4e8b5a0d84c5cfd27557a481707420ce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20G=C3=B6m=C3=B6ri?= Date: Thu, 20 Oct 2016 01:05:45 +0200 Subject: controlling_process can return {error, badarg} For gen_tcp, gen_udp and gen_sctp controlling_process/2 can return badarg if erlang:port_connect/2 fails with badarg. This can easily happen if the new owner is not alive but in some race condition also when the socket is closed right before port_connect/2 (and after the previous socket function) This commit documents this behaviour. --- lib/kernel/doc/src/gen_tcp.xml | 6 +++++- lib/kernel/doc/src/gen_udp.xml | 6 +++++- lib/kernel/src/gen_sctp.erl | 2 +- lib/kernel/src/gen_tcp.erl | 2 +- lib/kernel/src/gen_udp.erl | 2 +- 5 files changed, 13 insertions(+), 5 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index 08454b9832..e97db20062 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -231,7 +231,11 @@ do_recv(Sock, Bs) -> Socket. The controlling process is the process that receives messages from the socket. If called by any other process than the current controlling process, - {error, not_owner} is returned.

+ {error, not_owner} is returned. If the process identified + by Pid is not an existing local pid, + {error, badarg} is returned. {error, badarg} may also + be returned in some cases when Socket is closed + during the execution of this function.

If the socket is set in active mode, this function will transfer any messages in the mailbox of the caller to the new controlling process. diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml index 3f88a0272d..f79566ef71 100644 --- a/lib/kernel/doc/src/gen_udp.xml +++ b/lib/kernel/doc/src/gen_udp.xml @@ -68,7 +68,11 @@ Socket. The controlling process is the process that receives messages from the socket. If called by any other process than the current controlling process, - {error, not_owner} is returned.

+ {error, not_owner} is returned. If the process identified + by Pid is not an existing local pid, + {error, badarg} is returned. {error, badarg} may also + be returned in some cases when Socket is closed + during the execution of this function.

diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl index b133e6fed4..a6aa0edd15 100644 --- a/lib/kernel/src/gen_sctp.erl +++ b/lib/kernel/src/gen_sctp.erl @@ -439,7 +439,7 @@ error_string(X) -> -spec controlling_process(Socket, Pid) -> ok | {error, Reason} when Socket :: sctp_socket(), Pid :: pid(), - Reason :: closed | not_owner | inet:posix(). + Reason :: closed | not_owner | badarg | inet:posix(). controlling_process(S, Pid) when is_port(S), is_pid(Pid) -> inet:udp_controlling_process(S, Pid); diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl index 1a21541b7c..ac61dbc792 100644 --- a/lib/kernel/src/gen_tcp.erl +++ b/lib/kernel/src/gen_tcp.erl @@ -320,7 +320,7 @@ unrecv(S, Data) when is_port(S) -> -spec controlling_process(Socket, Pid) -> ok | {error, Reason} when Socket :: socket(), Pid :: pid(), - Reason :: closed | not_owner | inet:posix(). + Reason :: closed | not_owner | badarg | inet:posix(). controlling_process(S, NewOwner) -> case inet_db:lookup_socket(S) of diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl index 98d2f0bcfb..3121544719 100644 --- a/lib/kernel/src/gen_udp.erl +++ b/lib/kernel/src/gen_udp.erl @@ -195,7 +195,7 @@ connect(S, Address, Port) when is_port(S) -> -spec controlling_process(Socket, Pid) -> ok | {error, Reason} when Socket :: socket(), Pid :: pid(), - Reason :: closed | not_owner | inet:posix(). + Reason :: closed | not_owner | badarg | inet:posix(). controlling_process(S, NewOwner) -> inet:udp_controlling_process(S, NewOwner). -- cgit v1.2.3 From 1c3dc406736e13a38d6a70fc82027d3f3f846b13 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 31 Oct 2016 17:20:54 +0100 Subject: kernel: Add lost dependency to erts-8.1 In OTP-19.1 (c70ca686fe269db6079a2ca1c7e09cdfc0cfa903) erts_code_purger:finish_after_on_load/2 was added and called by code_server.erl. --- lib/kernel/src/kernel.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 56d1699656..d184223524 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -118,6 +118,6 @@ {applications, []}, {env, [{error_logger, tty}]}, {mod, {kernel, []}}, - {runtime_dependencies, ["erts-8.0", "stdlib-3.0", "sasl-3.0"]} + {runtime_dependencies, ["erts-8.1", "stdlib-3.0", "sasl-3.0"]} ] }. -- cgit v1.2.3 From 3097a53cc6bc3728f72c3873713f42d9c591ba0c Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Tue, 15 Nov 2016 10:40:30 +0100 Subject: Minor documentation cleanup --- lib/kernel/doc/src/disk_log.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/disk_log.xml b/lib/kernel/doc/src/disk_log.xml index 0b6ee1e6a5..3c9bc7f6e8 100644 --- a/lib/kernel/doc/src/disk_log.xml +++ b/lib/kernel/doc/src/disk_log.xml @@ -43,7 +43,7 @@ halt logs

Appends items to a single file, which size can - be limited by the disk log module.

+ be limited by the disk_log module.

wrap logs

Uses a sequence of wrap log files of limited size. As a wrap log file is filled up, further items are logged on to the next @@ -62,8 +62,8 @@ An item logged to an internally formatted log must not occupy more than 4 GB of disk space (the size must fit in 4 bytes).

external format -

Leaves it up to the user to read the logged deep byte lists. - The disk log module cannot repair externally formatted logs.

+

Leaves it up to the user to read and interpret the logged data. + The disk_log module cannot repair externally formatted logs.

For each open disk log, one process handles requests -- cgit v1.2.3 From a59be645027272e96acdddf5cf9710aeda1e11de Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Tue, 15 Nov 2016 11:53:07 +0100 Subject: Rename internal function for clarity --- lib/kernel/src/disk_log.erl | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index 9b44021872..515caf98a7 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.erl @@ -133,7 +133,7 @@ log(Log, Term) -> Log :: log(), Bytes :: bytes(). blog(Log, Bytes) -> - req(Log, {blog, check_bytes(Bytes)}). + req(Log, {blog, ensure_binary(Bytes)}). -spec log_terms(Log, TermList) -> ok | {error, Resaon :: log_error_rsn()} when Log :: log(), @@ -147,7 +147,7 @@ log_terms(Log, Terms) -> Log :: log(), BytesList :: [bytes()]. blog_terms(Log, Bytess) -> - Bs = check_bytes_list(Bytess, Bytess), + Bs = ensure_binary_list(Bytess), req(Log, {blog, Bs}). -type notify_ret() :: 'ok' | {'error', 'no_such_log'}. @@ -169,13 +169,13 @@ alog_terms(Log, Terms) -> Log :: log(), Bytes :: bytes(). balog(Log, Bytes) -> - notify(Log, {balog, check_bytes(Bytes)}). + notify(Log, {balog, ensure_binary(Bytes)}). -spec balog_terms(Log, ByteList) -> notify_ret() when Log :: log(), ByteList :: [bytes()]. balog_terms(Log, Bytess) -> - Bs = check_bytes_list(Bytess, Bytess), + Bs = ensure_binary_list(Bytess), notify(Log, {balog, Bs}). -type close_error_rsn() ::'no_such_log' | 'nonode' @@ -221,7 +221,7 @@ truncate(Log, Head) -> Log :: log(), BHead :: bytes(). btruncate(Log, Head) -> - req(Log, {truncate, {ok, check_bytes(Head)}, btruncate, 2}). + req(Log, {truncate, {ok, ensure_binary(Head)}, btruncate, 2}). -type reopen_error_rsn() :: no_such_log | nonode @@ -250,7 +250,7 @@ reopen(Log, NewFile, NewHead) -> File :: file:filename(), BHead :: bytes(). breopen(Log, NewFile, NewHead) -> - req(Log, {reopen, NewFile, {ok, check_bytes(NewHead)}, breopen, 3}). + req(Log, {reopen, NewFile, {ok, ensure_binary(NewHead)}, breopen, 3}). -type inc_wrap_error_rsn() :: 'no_such_log' | 'nonode' | {'read_only_mode', log()} @@ -1326,7 +1326,7 @@ do_open(A) -> end. mk_head({head, Term}, internal) -> {ok, term_to_binary(Term)}; -mk_head({head, Bytes}, external) -> {ok, check_bytes(Bytes)}; +mk_head({head, Bytes}, external) -> {ok, ensure_binary(Bytes)}; mk_head(H, _) -> H. terms2bins([T | Ts]) -> @@ -1334,23 +1334,24 @@ terms2bins([T | Ts]) -> terms2bins([]) -> []. -check_bytes_list([B | Bs], Bs0) when is_binary(B) -> - check_bytes_list(Bs, Bs0); -check_bytes_list([], Bs0) -> +ensure_binary_list(Bs) -> + ensure_binary_list(Bs, Bs). + +ensure_binary_list([B | Bs], Bs0) when is_binary(B) -> + ensure_binary_list(Bs, Bs0); +ensure_binary_list([], Bs0) -> Bs0; -check_bytes_list(_, Bs0) -> - check_bytes_list(Bs0). - -check_bytes_list([B | Bs]) when is_binary(B) -> - [B | check_bytes_list(Bs)]; -check_bytes_list([B | Bs]) -> - [list_to_binary(B) | check_bytes_list(Bs)]; -check_bytes_list([]) -> +ensure_binary_list(_, Bs0) -> + make_binary_list(Bs0). + +make_binary_list([B | Bs]) -> + [ensure_binary(B) | make_binary_list(Bs)]; +make_binary_list([]) -> []. -check_bytes(Binary) when is_binary(Binary) -> +ensure_binary(Binary) when is_binary(Binary) -> Binary; -check_bytes(Bytes) -> +ensure_binary(Bytes) -> list_to_binary(Bytes). %%----------------------------------------------------------------- @@ -1388,7 +1389,7 @@ check_head({head_func, {M, F, A}}, _Format) when is_atom(M), is_list(A) -> {ok, {M, F, A}}; check_head({head, Head}, external) -> - case catch check_bytes(Head) of + case catch ensure_binary(Head) of {'EXIT', _} -> {error, {badarg, head}}; _ -> -- cgit v1.2.3 From a46e95598f6c587277671980115a4c61ff08c9cd Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Tue, 15 Nov 2016 14:21:29 +0100 Subject: Eliminate some code duplication --- lib/kernel/src/disk_log.erl | 63 +++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 33 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index 515caf98a7..312d07515b 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.erl @@ -686,7 +686,7 @@ handle({From, write_cache}, S) when From =:= self() -> Error -> loop(S#state{cache_error = Error}) end; -handle({From, {log, B}}, S) -> +handle({From, {log, B}}=Message, S) -> case get(log) of L when L#log.mode =:= read_only -> reply(From, {error, {read_only_mode, L#log.name}}, S); @@ -699,9 +699,9 @@ handle({From, {log, B}}, S) -> L when L#log.blocked_by =:= From -> reply(From, {error, {blocked_log, L#log.name}}, S); _ -> - loop(S#state{queue = [{From, {log, B}} | S#state.queue]}) - end; -handle({From, {blog, B}}, S) -> + enqueue(Message, S) + end; +handle({From, {blog, B}}=Message, S) -> case get(log) of L when L#log.mode =:= read_only -> reply(From, {error, {read_only_mode, L#log.name}}, S); @@ -712,9 +712,9 @@ handle({From, {blog, B}}, S) -> L when L#log.blocked_by =:= From -> reply(From, {error, {blocked_log, L#log.name}}, S); _ -> - loop(S#state{queue = [{From, {blog, B}} | S#state.queue]}) + enqueue(Message, S) end; -handle({alog, B}, S) -> +handle({alog, B}=Message, S) -> case get(log) of L when L#log.mode =:= read_only -> notify_owners({read_only,B}), @@ -728,9 +728,9 @@ handle({alog, B}, S) -> notify_owners({blocked_log, B}), loop(S); _ -> - loop(S#state{queue = [{alog, B} | S#state.queue]}) + enqueue(Message, S) end; -handle({balog, B}, S) -> +handle({balog, B}=Message, S) -> case get(log) of L when L#log.mode =:= read_only -> notify_owners({read_only,B}), @@ -741,9 +741,9 @@ handle({balog, B}, S) -> notify_owners({blocked_log, B}), loop(S); _ -> - loop(S#state{queue = [{balog, B} | S#state.queue]}) + enqueue(Message, S) end; -handle({From, {block, QueueLogRecs}}, S) -> +handle({From, {block, QueueLogRecs}}=Message, S) -> case get(log) of L when L#log.status =:= ok -> do_block(From, QueueLogRecs, L), @@ -753,8 +753,7 @@ handle({From, {block, QueueLogRecs}}, S) -> L when L#log.blocked_by =:= From -> reply(From, {error, {blocked_log, L#log.name}}, S); _ -> - loop(S#state{queue = [{From, {block, QueueLogRecs}} | - S#state.queue]}) + enqueue(Message, S) end; handle({From, unblock}, S) -> case get(log) of @@ -766,7 +765,7 @@ handle({From, unblock}, S) -> L -> reply(From, {error, {not_blocked_by_pid, L#log.name}}, S) end; -handle({From, sync}, S) -> +handle({From, sync}=Message, S) -> case get(log) of L when L#log.mode =:= read_only -> reply(From, {error, {read_only_mode, L#log.name}}, S); @@ -777,9 +776,9 @@ handle({From, sync}, S) -> L when L#log.blocked_by =:= From -> reply(From, {error, {blocked_log, L#log.name}}, S); _ -> - loop(S#state{queue = [{From, sync} | S#state.queue]}) + enqueue(Message, S) end; -handle({From, {truncate, Head, F, A}}, S) -> +handle({From, {truncate, Head, F, A}}=Message, S) -> case get(log) of L when L#log.mode =:= read_only -> reply(From, {error, {read_only_mode, L#log.name}}, S); @@ -801,10 +800,9 @@ handle({From, {truncate, Head, F, A}}, S) -> L when L#log.blocked_by =:= From -> reply(From, {error, {blocked_log, L#log.name}}, S); _ -> - loop(S#state{queue = [{From, {truncate, Head, F, A}} - | S#state.queue]}) + enqueue(Message, S) end; -handle({From, {chunk, Pos, B, N}}, S) -> +handle({From, {chunk, Pos, B, N}}=Message, S) -> case get(log) of L when L#log.status =:= ok, S#state.cache_error =/= ok -> loop(cache_error(S, [From])); @@ -817,9 +815,9 @@ handle({From, {chunk, Pos, B, N}}, S) -> L when L#log.status =:= {blocked, false} -> reply(From, {error, {blocked_log, L#log.name}}, S); _L -> - loop(S#state{queue = [{From, {chunk, Pos, B, N}} | S#state.queue]}) + enqueue(Message, S) end; -handle({From, {chunk_step, Pos, N}}, S) -> +handle({From, {chunk_step, Pos, N}}=Message, S) -> case get(log) of L when L#log.status =:= ok, S#state.cache_error =/= ok -> loop(cache_error(S, [From])); @@ -832,10 +830,9 @@ handle({From, {chunk_step, Pos, N}}, S) -> L when L#log.status =:= {blocked, false} -> reply(From, {error, {blocked_log, L#log.name}}, S); _ -> - loop(S#state{queue = [{From, {chunk_step, Pos, N}} - | S#state.queue]}) + enqueue(Message, S) end; -handle({From, {change_notify, Pid, NewNotify}}, S) -> +handle({From, {change_notify, Pid, NewNotify}}=Message, S) -> case get(log) of L when L#log.status =:= ok -> case do_change_notify(L, Pid, NewNotify) of @@ -850,10 +847,9 @@ handle({From, {change_notify, Pid, NewNotify}}, S) -> L when L#log.blocked_by =:= From -> reply(From, {error, {blocked_log, L#log.name}}, S); _ -> - loop(S#state{queue = [{From, {change_notify, Pid, NewNotify}} - | S#state.queue]}) + enqueue(Message, S) end; -handle({From, {change_header, NewHead}}, S) -> +handle({From, {change_header, NewHead}}=Message, S) -> case get(log) of L when L#log.mode =:= read_only -> reply(From, {error, {read_only_mode, L#log.name}}, S); @@ -870,10 +866,9 @@ handle({From, {change_header, NewHead}}, S) -> L when L#log.blocked_by =:= From -> reply(From, {error, {blocked_log, L#log.name}}, S); _ -> - loop(S#state{queue = [{From, {change_header, NewHead}} - | S#state.queue]}) + enqueue(Message, S) end; -handle({From, {change_size, NewSize}}, S) -> +handle({From, {change_size, NewSize}}=Message, S) -> case get(log) of L when L#log.mode =:= read_only -> reply(From, {error, {read_only_mode, L#log.name}}, S); @@ -899,10 +894,9 @@ handle({From, {change_size, NewSize}}, S) -> L when L#log.blocked_by =:= From -> reply(From, {error, {blocked_log, L#log.name}}, S); _ -> - loop(S#state{queue = [{From, {change_size, NewSize}} - | S#state.queue]}) + enqueue(Message, S) end; -handle({From, inc_wrap_file}, S) -> +handle({From, inc_wrap_file}=Message, S) -> case get(log) of L when L#log.mode =:= read_only -> reply(From, {error, {read_only_mode, L#log.name}}, S); @@ -925,7 +919,7 @@ handle({From, inc_wrap_file}, S) -> L when L#log.blocked_by =:= From -> reply(From, {error, {blocked_log, L#log.name}}, S); _ -> - loop(S#state{queue = [{From, inc_wrap_file} | S#state.queue]}) + enqueue(Message, S) end; handle({From, {reopen, NewFile, Head, F, A}}, S) -> case get(log) of @@ -1034,6 +1028,9 @@ handle({system, From, Req}, S) -> handle(_, S) -> loop(S). +enqueue(Message, S) -> + loop(S#state{queue = [Message | S#state.queue]}). + sync_loop(From, S) -> log_loop(S, [], [], From, 0). -- cgit v1.2.3 From b653dc18fa91e68021b28bef37e942f7fa7f3809 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Tue, 15 Nov 2016 15:56:24 +0100 Subject: Only read log format once in collect loop --- lib/kernel/src/disk_log.erl | 54 +++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 26 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index 312d07515b..d06429d629 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.erl @@ -67,7 +67,7 @@ %%-define(PROFILE(C), C). -define(PROFILE(C), void). --compile({inline,[{log_loop,5},{log_end_sync,2},{replies,2},{rflat,1}]}). +-compile({inline,[{log_loop,6},{log_end_sync,2},{replies,2},{rflat,1}]}). %%%---------------------------------------------------------------------- %%% Contract type specifications @@ -691,7 +691,7 @@ handle({From, {log, B}}=Message, S) -> L when L#log.mode =:= read_only -> reply(From, {error, {read_only_mode, L#log.name}}, S); L when L#log.status =:= ok, L#log.format =:= internal -> - log_loop(S, From, [B], [], iolist_size(B)); + log_loop(S, From, [B], []); L when L#log.status =:= ok, L#log.format =:= external -> reply(From, {error, {format_external, L#log.name}}, S); L when L#log.status =:= {blocked, false} -> @@ -706,7 +706,7 @@ handle({From, {blog, B}}=Message, S) -> L when L#log.mode =:= read_only -> reply(From, {error, {read_only_mode, L#log.name}}, S); L when L#log.status =:= ok -> - log_loop(S, From, [B], [], iolist_size(B)); + log_loop(S, From, [B], []); L when L#log.status =:= {blocked, false} -> reply(From, {error, {blocked_log, L#log.name}}, S); L when L#log.blocked_by =:= From -> @@ -720,7 +720,7 @@ handle({alog, B}=Message, S) -> notify_owners({read_only,B}), loop(S); L when L#log.status =:= ok, L#log.format =:= internal -> - log_loop(S, [], [B], [], iolist_size(B)); + log_loop(S, [], [B], []); L when L#log.status =:= ok -> notify_owners({format_external, B}), loop(S); @@ -736,7 +736,7 @@ handle({balog, B}=Message, S) -> notify_owners({read_only,B}), loop(S); L when L#log.status =:= ok -> - log_loop(S, [], [B], [], iolist_size(B)); + log_loop(S, [], [B], []); L when L#log.status =:= {blocked, false} -> notify_owners({blocked_log, B}), loop(S); @@ -770,7 +770,7 @@ handle({From, sync}=Message, S) -> L when L#log.mode =:= read_only -> reply(From, {error, {read_only_mode, L#log.name}}, S); L when L#log.status =:= ok -> - sync_loop([From], S); + log_loop(S, [], [], [From]); L when L#log.status =:= {blocked, false} -> reply(From, {error, {blocked_log, L#log.name}}, S); L when L#log.blocked_by =:= From -> @@ -1031,41 +1031,43 @@ handle(_, S) -> enqueue(Message, S) -> loop(S#state{queue = [Message | S#state.queue]}). -sync_loop(From, S) -> - log_loop(S, [], [], From, 0). +%% Collect further log and sync requests already in the mailbox or queued -define(MAX_LOOK_AHEAD, 64*1024). +log_loop(S, Pids, Bins, Sync) -> + log_loop(S, Pids, Bins, Sync, iolist_size(Bins), (get(log))#log.format). + %% Inlined. -log_loop(#state{cache_error = CE}=S, Pids, _Bins, _Sync, _Sz) when CE =/= ok -> +log_loop(#state{cache_error = CE}=S, Pids, _Bins, _Sync, _Sz, _F) when CE =/= ok -> loop(cache_error(S, Pids)); -log_loop(#state{}=S, Pids, Bins, Sync, Sz) when Sz > ?MAX_LOOK_AHEAD -> +log_loop(#state{}=S, Pids, Bins, Sync, Sz, _F) when Sz > ?MAX_LOOK_AHEAD -> loop(log_end(S, Pids, Bins, Sync)); -log_loop(#state{messages = []}=S, Pids, Bins, Sync, Sz) -> - receive +log_loop(#state{messages = []}=S, Pids, Bins, Sync, Sz, F) -> + receive Message -> - log_loop(Message, Pids, Bins, Sync, Sz, S, get(log)) + log_loop(Message, Pids, Bins, Sync, Sz, F, S) after 0 -> loop(log_end(S, Pids, Bins, Sync)) end; -log_loop(#state{messages = [M | Ms]}=S, Pids, Bins, Sync, Sz) -> +log_loop(#state{messages = [M | Ms]}=S, Pids, Bins, Sync, Sz, F) -> S1 = S#state{messages = Ms}, - log_loop(M, Pids, Bins, Sync, Sz, S1, get(log)). + log_loop(M, Pids, Bins, Sync, Sz, F, S1). %% Items logged after the last sync request found are sync:ed as well. -log_loop({alog,B}, Pids, Bins, Sync, Sz, S, #log{format = internal}) -> +log_loop({alog,B}, Pids, Bins, Sync, Sz, internal=F, S) -> %% {alog, _} allowed for the internal format only. - log_loop(S, Pids, [B | Bins], Sync, Sz+iolist_size(B)); -log_loop({balog, B}, Pids, Bins, Sync, Sz, S, _L) -> - log_loop(S, Pids, [B | Bins], Sync, Sz+iolist_size(B)); -log_loop({From, {log, B}}, Pids, Bins, Sync, Sz, S, #log{format = internal}) -> + log_loop(S, Pids, [B | Bins], Sync, Sz+iolist_size(B), F); +log_loop({balog, B}, Pids, Bins, Sync, Sz, F, S) -> + log_loop(S, Pids, [B | Bins], Sync, Sz+iolist_size(B), F); +log_loop({From, {log, B}}, Pids, Bins, Sync, Sz, internal=F, S) -> %% {log, _} allowed for the internal format only. - log_loop(S, [From | Pids], [B | Bins], Sync, Sz+iolist_size(B)); -log_loop({From, {blog, B}}, Pids, Bins, Sync, Sz, S, _L) -> - log_loop(S, [From | Pids], [B | Bins], Sync, Sz+iolist_size(B)); -log_loop({From, sync}, Pids, Bins, Sync, Sz, S, _L) -> - log_loop(S, Pids, Bins, [From | Sync], Sz); -log_loop(Message, Pids, Bins, Sync, _Sz, S, _L) -> + log_loop(S, [From | Pids], [B | Bins], Sync, Sz+iolist_size(B), F); +log_loop({From, {blog, B}}, Pids, Bins, Sync, Sz, F, S) -> + log_loop(S, [From | Pids], [B | Bins], Sync, Sz+iolist_size(B), F); +log_loop({From, sync}, Pids, Bins, Sync, Sz, F, S) -> + log_loop(S, Pids, Bins, [From | Sync], Sz, F); +log_loop(Message, Pids, Bins, Sync, _Sz, _F, S) -> NS = log_end(S, Pids, Bins, Sync), handle(Message, NS). -- cgit v1.2.3 From 6abc37b32ac6965ab780cd6fda5667585caaa711 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Tue, 15 Nov 2016 16:53:38 +0100 Subject: Use iolist_size instead of local function --- lib/kernel/src/disk_log.erl | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index d06429d629..8dd9975b23 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.erl @@ -1695,7 +1695,7 @@ do_unblock(L, S) -> do_log(L, B) when L#log.type =:= halt -> #log{format = Format, extra = Halt} = L, #halt{curB = CurSize, size = Sz} = Halt, - {Bs, BSize} = bsize(B, Format), + {Bs, BSize} = logl(B, Format), case get(is_full) of true -> {error, {error, {full, L#log.name}}, 0}; @@ -1731,17 +1731,14 @@ do_log(L, B) when L#log.format_type =:= wrap_ext -> {error, Error, Logged - Lost} end. -bsize(B, external) -> - {B, xsz(B, 0)}; -bsize(B, internal) -> +logl(B, external) -> + {B, iolist_size(B)}; +logl(B, internal) -> disk_log_1:logl(B). -xsz([B|T], Sz) -> xsz(T, byte_size(B) + Sz); -xsz([], Sz) -> Sz. - halt_write_full(L, [Bin | Bins], Format, N) -> B = [Bin], - {Bs, BSize} = bsize(B, Format), + {Bs, BSize} = logl(B, Format), Halt = L#log.extra, #halt{curB = CurSize, size = Sz} = Halt, if -- cgit v1.2.3 From 08d22e7658542279711753f3363299105bd669bb Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Tue, 15 Nov 2016 17:21:49 +0100 Subject: Pass through known size instead of recomputing --- lib/kernel/src/disk_log.erl | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index 8dd9975b23..1b28d05691 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.erl @@ -1042,13 +1042,13 @@ log_loop(S, Pids, Bins, Sync) -> log_loop(#state{cache_error = CE}=S, Pids, _Bins, _Sync, _Sz, _F) when CE =/= ok -> loop(cache_error(S, Pids)); log_loop(#state{}=S, Pids, Bins, Sync, Sz, _F) when Sz > ?MAX_LOOK_AHEAD -> - loop(log_end(S, Pids, Bins, Sync)); + loop(log_end(S, Pids, Bins, Sync, Sz)); log_loop(#state{messages = []}=S, Pids, Bins, Sync, Sz, F) -> receive Message -> log_loop(Message, Pids, Bins, Sync, Sz, F, S) after 0 -> - loop(log_end(S, Pids, Bins, Sync)) + loop(log_end(S, Pids, Bins, Sync, Sz)) end; log_loop(#state{messages = [M | Ms]}=S, Pids, Bins, Sync, Sz, F) -> S1 = S#state{messages = Ms}, @@ -1067,14 +1067,14 @@ log_loop({From, {blog, B}}, Pids, Bins, Sync, Sz, F, S) -> log_loop(S, [From | Pids], [B | Bins], Sync, Sz+iolist_size(B), F); log_loop({From, sync}, Pids, Bins, Sync, Sz, F, S) -> log_loop(S, Pids, Bins, [From | Sync], Sz, F); -log_loop(Message, Pids, Bins, Sync, _Sz, _F, S) -> - NS = log_end(S, Pids, Bins, Sync), +log_loop(Message, Pids, Bins, Sync, Sz, _F, S) -> + NS = log_end(S, Pids, Bins, Sync, Sz), handle(Message, NS). -log_end(S, [], [], Sync) -> +log_end(S, [], [], Sync, _Sz) -> log_end_sync(S, Sync); -log_end(S, Pids, Bins, Sync) -> - case do_log(get(log), rflat(Bins)) of +log_end(S, Pids, Bins, Sync, Sz) -> + case do_log(get(log), rflat(Bins), Sz) of N when is_integer(N) -> ok = replies(Pids, ok), S1 = (state_ok(S))#state{cnt = S#state.cnt+N}, @@ -1692,10 +1692,13 @@ do_unblock(L, S) -> -spec do_log(#log{}, [binary()]) -> integer() | {'error', _, integer()}. -do_log(L, B) when L#log.type =:= halt -> +do_log(L, B) -> + do_log(L, B, iolist_size(B)). + +do_log(L, B, BSz) when L#log.type =:= halt -> #log{format = Format, extra = Halt} = L, #halt{curB = CurSize, size = Sz} = Halt, - {Bs, BSize} = logl(B, Format), + {Bs, BSize} = logl(B, Format, BSz), case get(is_full) of true -> {error, {error, {full, L#log.name}}, 0}; @@ -1704,7 +1707,7 @@ do_log(L, B) when L#log.type =:= halt -> undefined -> halt_write_full(L, B, Format, 0) end; -do_log(L, B) when L#log.format_type =:= wrap_int -> +do_log(L, B, _BSz) when L#log.format_type =:= wrap_int -> case disk_log_1:mf_int_log(L#log.extra, B, L#log.head) of {ok, Handle, Logged, Lost, Wraps} -> notify_owners_wrap(Wraps), @@ -1717,7 +1720,7 @@ do_log(L, B) when L#log.format_type =:= wrap_int -> put(log, L#log{extra = Handle}), {error, Error, Logged - Lost} end; -do_log(L, B) when L#log.format_type =:= wrap_ext -> +do_log(L, B, _BSz) when L#log.format_type =:= wrap_ext -> case disk_log_1:mf_ext_log(L#log.extra, B, L#log.head) of {ok, Handle, Logged, Lost, Wraps} -> notify_owners_wrap(Wraps), @@ -1731,14 +1734,16 @@ do_log(L, B) when L#log.format_type =:= wrap_ext -> {error, Error, Logged - Lost} end. -logl(B, external) -> +logl(B, external, undefined) -> {B, iolist_size(B)}; -logl(B, internal) -> +logl(B, external, Sz) -> + {B, Sz}; +logl(B, internal, _Sz) -> disk_log_1:logl(B). halt_write_full(L, [Bin | Bins], Format, N) -> B = [Bin], - {Bs, BSize} = logl(B, Format), + {Bs, BSize} = logl(B, Format, undefined), Halt = L#log.extra, #halt{curB = CurSize, size = Sz} = Halt, if -- cgit v1.2.3 From 0d8670a9359d6a895b003ad1cfd7a523983ba775 Mon Sep 17 00:00:00 2001 From: Alexander Clouter Date: Fri, 18 Nov 2016 18:50:24 +0000 Subject: document {yield/nb_yield}() limitation --- lib/kernel/doc/src/rpc.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/rpc.xml b/lib/kernel/doc/src/rpc.xml index 5944e9321a..adec2d9520 100644 --- a/lib/kernel/doc/src/rpc.xml +++ b/lib/kernel/doc/src/rpc.xml @@ -88,6 +88,12 @@ to retrieve the value of evaluating apply(Module, Function, Args) on node Node.

+ +

yield/1 and + nb_yield/1,2 + must be called by the same process from which this function + was made otherwise they will never yield correctly.

+
@@ -299,6 +305,11 @@ the tuple {value, Val} when the computation is finished, or timeout when Timeout milliseconds has elapsed.

+ +

This function must be called by the same process from which + async_call/4 + was made otherwise it will only return timeout.

+
@@ -407,6 +418,11 @@ If the answer is available, it is returned immediately. Otherwise, the calling process is suspended until the answer arrives from Node.

+ +

This function must be called by the same process from which + async_call/4 + was made otherwise it will never return.

+
-- cgit v1.2.3 From 5b9265ad19d6596a8b599eccc64accb67e3c664e Mon Sep 17 00:00:00 2001 From: Magnus Henoch Date: Mon, 21 Nov 2016 11:48:54 +0000 Subject: Remove vestiges of watchdog support in heart Hardware watchdog support was removed from heart in R13A, but there were still some vestiges in the code and the documentation. - Remove mentions of the HW_WD_DISABLE variable, as it's no longer used. - Remove the HEART_BEAT_BOOT_DELAY variable, as it was only used for the hardware watchdog. --- lib/kernel/doc/src/heart.xml | 17 +++++------------ lib/kernel/src/heart.erl | 15 +++++---------- 2 files changed, 10 insertions(+), 22 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/heart.xml b/lib/kernel/doc/src/heart.xml index 59a046bf4d..5b5b71e521 100644 --- a/lib/kernel/doc/src/heart.xml +++ b/lib/kernel/doc/src/heart.xml @@ -37,10 +37,7 @@ the heart port program is to check that the Erlang runtime system it is supervising is still running. If the port program has not received any heartbeats within HEART_BEAT_TIMEOUT seconds - (defaults to 60 seconds), the system can be rebooted. Also, if - the system is equipped with a hardware watchdog timer and is - running Solaris, the watchdog can be used to supervise the entire - system.

+ (defaults to 60 seconds), the system can be rebooted.

An Erlang runtime system to be monitored by a heart program is to be started with command-line flag -heart (see also erl(1)). @@ -51,17 +48,13 @@ or a terminated Erlang runtime system, environment variable HEART_COMMAND must be set before the system is started. If this variable is not set, a warning text is printed but - the system does not reboot. However, if the hardware watchdog is - used, it still triggers a reboot HEART_BEAT_BOOT_DELAY - seconds later (defaults to 60 seconds).

+ the system does not reboot.

To reboot on Windows, HEART_COMMAND can be set to heart -shutdown (included in the Erlang delivery) or to any other suitable program that can activate a reboot.

-

The hardware watchdog is not started under Solaris if - environment variable HW_WD_DISABLE is set.

-

The environment variables HEART_BEAT_TIMEOUT and - HEART_BEAT_BOOT_DELAY can be used to configure the heart - time-outs; they can be set in the operating system shell before Erlang +

The environment variable HEART_BEAT_TIMEOUT + can be used to configure the heart + time-outs; it can be set in the operating system shell before Erlang is started or be specified at the command line:

 % erl -heart -env HEART_BEAT_TIMEOUT 30 ...
diff --git a/lib/kernel/src/heart.erl b/lib/kernel/src/heart.erl index eea78aabdf..8fa48d56fb 100644 --- a/lib/kernel/src/heart.erl +++ b/lib/kernel/src/heart.erl @@ -198,16 +198,11 @@ start_portprogram() -> end. get_heart_timeouts() -> - HeartOpts = case os:getenv("HEART_BEAT_TIMEOUT") of - false -> ""; - H when is_list(H) -> - "-ht " ++ H - end, - HeartOpts ++ case os:getenv("HEART_BEAT_BOOT_DELAY") of - false -> ""; - W when is_list(W) -> - " -wt " ++ W - end. + case os:getenv("HEART_BEAT_TIMEOUT") of + false -> ""; + H when is_list(H) -> + "-ht " ++ H + end. check_start_heart() -> case init:get_argument(heart) of -- cgit v1.2.3 From 65c4401c1cd1094da5d8f35137532a22054a8517 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Mon, 21 Nov 2016 15:31:16 +0100 Subject: Remove remnants of module package support --- lib/kernel/src/code.erl | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index 622b27080c..1b149bbb4e 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -719,37 +719,28 @@ start_get_mode() -> which(Module) when is_atom(Module) -> case is_loaded(Module) of false -> - which2(Module); + File = atom_to_list(Module) ++ objfile_extension(), + which(File, get_path()); {file, File} -> File end. -which2(Module) -> - Base = atom_to_list(Module), - File = filename:basename(Base) ++ objfile_extension(), - Path = get_path(), - which(File, filename:dirname(Base), Path). - --spec which(file:filename(), file:filename(), [file:filename()]) -> +-spec which(file:filename(), [file:filename()]) -> 'non_existing' | file:filename(). -which(_, _, []) -> +which(_, []) -> non_existing; -which(File, Base, [Directory|Tail]) -> - Path = if - Base =:= "." -> Directory; - true -> filename:join(Directory, Base) - end, +which(File, [Path|Tail]) -> case erl_prim_loader:list_dir(Path) of {ok,Files} -> case lists:member(File,Files) of true -> filename:append(Path, File); false -> - which(File, Base, Tail) + which(File, Tail) end; _Error -> - which(File, Base, Tail) + which(File, Tail) end. %% Search the code path for a specific file. Try to locate @@ -760,13 +751,13 @@ which(File, Base, [Directory|Tail]) -> Absname :: file:filename(). where_is_file(File) when is_list(File) -> Path = get_path(), - which(File, ".", Path). + which(File, Path). -spec where_is_file(Path :: file:filename(), Filename :: file:filename()) -> file:filename() | 'non_existing'. where_is_file(Path, File) when is_list(Path), is_list(File) -> - which(File, ".", Path). + which(File, Path). -spec set_primary_archive(ArchiveFile :: file:filename(), ArchiveBin :: binary(), -- cgit v1.2.3 From c7e5cf8e259956120c07206c4e7df235b129cb56 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Tue, 22 Nov 2016 15:17:04 +0100 Subject: Restructure code:which() and where_is_file() --- lib/kernel/src/code.erl | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index 1b149bbb4e..bb19d6716d 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -719,29 +719,14 @@ start_get_mode() -> which(Module) when is_atom(Module) -> case is_loaded(Module) of false -> - File = atom_to_list(Module) ++ objfile_extension(), - which(File, get_path()); + which(Module, get_path()); {file, File} -> File end. --spec which(file:filename(), [file:filename()]) -> - 'non_existing' | file:filename(). - -which(_, []) -> - non_existing; -which(File, [Path|Tail]) -> - case erl_prim_loader:list_dir(Path) of - {ok,Files} -> - case lists:member(File,Files) of - true -> - filename:append(Path, File); - false -> - which(File, Tail) - end; - _Error -> - which(File, Tail) - end. +which(Module, Path) when is_atom(Module) -> + File = atom_to_list(Module) ++ objfile_extension(), + where_is_file(Path, File). %% Search the code path for a specific file. Try to locate %% it in the code path cache if possible. @@ -751,13 +736,28 @@ which(File, [Path|Tail]) -> Absname :: file:filename(). where_is_file(File) when is_list(File) -> Path = get_path(), - which(File, Path). + where_is_file(Path, File). -spec where_is_file(Path :: file:filename(), Filename :: file:filename()) -> file:filename() | 'non_existing'. -where_is_file(Path, File) when is_list(Path), is_list(File) -> - which(File, Path). +where_is_file([], _) -> + non_existing; +where_is_file([Path|Tail], File) -> + case erl_prim_loader:list_dir(Path) of + {ok,Files} -> + where_is_file(Tail, File, Path, Files); + _Error -> + where_is_file(Tail, File) + end. + +where_is_file(Tail, File, Path, Files) -> + case lists:member(File, Files) of + true -> + filename:append(Path, File); + false -> + where_is_file(Tail, File) + end. -spec set_primary_archive(ArchiveFile :: file:filename(), ArchiveBin :: binary(), -- cgit v1.2.3 From a834d721e5047c5c43a8ede249fdf6711234ff1b Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Tue, 22 Nov 2016 15:33:14 +0100 Subject: Handle prefetched paths --- lib/kernel/src/code.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index bb19d6716d..0ad0676f98 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -738,11 +738,16 @@ where_is_file(File) when is_list(File) -> Path = get_path(), where_is_file(Path, File). --spec where_is_file(Path :: file:filename(), Filename :: file:filename()) -> - file:filename() | 'non_existing'. +%% To avoid unnecessary work when looking at many modules, this also +%% accepts pairs of directories and pre-fetched contents in the path +-spec where_is_file(Path :: [Dir|{Dir,Files}], Filename :: file:filename()) -> + 'non_existing' | file:filename() when + Dir :: file:filename(), Files :: [file:filename()]. where_is_file([], _) -> non_existing; +where_is_file([{Path, Files}|Tail], File) -> + where_is_file(Tail, File, Path, Files); where_is_file([Path|Tail], File) -> case erl_prim_loader:list_dir(Path) of {ok,Files} -> -- cgit v1.2.3 From fa73cc8e23d62c6112bd67f5d311c6043ee2ae94 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Thu, 9 Jul 2015 22:39:49 +0200 Subject: Add license headers to hipe files in kernel --- lib/kernel/src/hipe_ext_format.hrl | 12 ++++++++++++ lib/kernel/src/hipe_unified_loader.erl | 11 +++++++++++ 2 files changed, 23 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/src/hipe_ext_format.hrl b/lib/kernel/src/hipe_ext_format.hrl index 102cb49a2b..05c678fdec 100644 --- a/lib/kernel/src/hipe_ext_format.hrl +++ b/lib/kernel/src/hipe_ext_format.hrl @@ -1,3 +1,15 @@ +%% 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. + %% hipe_x86_ext_format.hrl %% Definitions for unified external object format %% Currently: sparc, x86, amd64 diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl index 04ec1479cb..f4c7c277ed 100644 --- a/lib/kernel/src/hipe_unified_loader.erl +++ b/lib/kernel/src/hipe_unified_loader.erl @@ -1,4 +1,15 @@ %% -*- erlang-indent-level: 2 -*- +%% 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. %% ======================================================================= %% Filename : hipe_unified_loader.erl %% Module : hipe_unified_loader -- cgit v1.2.3 From d1301e814920bde89dec4acfab58d0f2600e2b78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Thu, 24 Nov 2016 21:26:20 +0100 Subject: Remove duplicate docs for the priority inet option --- lib/kernel/doc/src/inet.xml | 5 ----- 1 file changed, 5 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index d907cef7d3..4c4a5c39cb 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -987,11 +987,6 @@ setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp

Sets the line delimiting character for line-oriented protocols (line). Defaults to $\n.

- {priority, Priority} - -

Sets the protocol-defined priority for all packets to be sent - on this socket.

-
{raw, Protocol, OptionNum, ValueBin}

See below.

-- cgit v1.2.3 From 86fa667f9731c790d6575f31efa156c02cb7984b Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Thu, 19 May 2016 20:29:45 +0200 Subject: Add code:module_status/1 and modified_modules/0 These functions use the MD5 beam/native checksum to determine whether the code for a module has changed on disk and is a candidate for loading. --- lib/kernel/doc/src/code.xml | 42 ++++++++++ lib/kernel/src/code.erl | 98 +++++++++++++++++++++- lib/kernel/test/code_SUITE.erl | 180 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 319 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml index 3143cdc825..f881fd76fd 100644 --- a/lib/kernel/doc/src/code.xml +++ b/lib/kernel/doc/src/code.xml @@ -898,6 +898,48 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), identical names and writes a report to stdout.

+ + + Return the status of the module in relation to object file on disk. + +

Returns:

+ + not_loaded +

If Module is not currently loaded.

+ loaded +

If Module is loaded and the object file + exists and contains the same code.

+ removed +

If Module is loaded but no + corresponding object file can be found in the code path.

+ modified +

If Module is loaded but the object file + contains code with a different MD5 checksum.

+
+

Preloaded modules are always reported as loaded, without + inspecting the contents on disk. Cover compiled modules will always + be reported as modified if an object file exists, or as + removed otherwise. Modules whose load path is an empty string + (which is the convention for auto-generated code) will only be + reported as loaded or not_loaded.

+

For modules that have native code loaded (see + is_module_native/1), + the MD5 sum of the native code in the object file is used for the + comparison, if it exists; the Beam code in the file is ignored. + Reversely, for modules that do not currently have native code + loaded, any native code in the file will be ignored.

+

See also modified_modules/0.

+
+
+ + + Return a list of all modules modified on disk. + +

Returns the list of all currently loaded modules for which + module_status/1 + returns modified. See also all_loaded/0.

+
+
Test if a module has native code. diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index 0ad0676f98..5a7ca493cc 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -70,7 +70,9 @@ where_is_file/2, set_primary_archive/4, clash/0, - get_mode/0]). + module_status/1, + modified_modules/0, + get_mode/0]). -deprecated({rehash,0,next_major_release}). @@ -895,3 +897,97 @@ load_all_native_1([{Mod,BeamFilename}|T], ChunkTag) -> load_all_native_1(T, ChunkTag); load_all_native_1([], _) -> ok. + +%% Returns the status of the module in relation to object file on disk. +-spec module_status(Module :: module()) -> not_loaded | loaded | modified | removed. +module_status(Module) -> + module_status(Module, code:get_path()). + +%% Note that we don't want to go via which/1, since it doesn't look at the +%% disk contents at all if the module is already loaded. +module_status(Module, PathFiles) -> + case code:is_loaded(Module) of + false -> not_loaded; + {file, preloaded} -> loaded; + {file, cover_compiled} -> + %% cover compilation loads directly to memory and does not + %% create a beam file, so report 'modified' if a file exists + case which(Module, PathFiles) of + non_existing -> removed; + _File -> modified + end; + {file, []} -> loaded; % no beam file - generated code + {file, OldFile} when is_list(OldFile) -> + %% we don't care whether or not the file is in the same location + %% as when last loaded, as long as it can be found in the path + case which(Module, PathFiles) of + non_existing -> removed; + Path -> + case module_changed_on_disk(Module, Path) of + true -> modified; + false -> loaded + end + end + end. + +%% Detects actual code changes only, e.g. to decide whether a module should +%% be reloaded; does not care about file timestamps or compilation time +module_changed_on_disk(Module, Path) -> + MD5 = erlang:get_module_info(Module, md5), + case erlang:system_info(hipe_architecture) of + undefined -> + %% straightforward, since native is not supported + MD5 =/= beam_file_md5(Path); + Architecture -> + case code:is_module_native(Module) of + true -> + %% MD5 is for native code, so we check only the native + %% code on disk, ignoring the beam code + MD5 =/= beam_file_native_md5(Path, Architecture); + _ -> + %% MD5 is for beam code, so check only the beam code on + %% disk, even if the file contains native code as well + MD5 =/= beam_file_md5(Path) + end + end. + +beam_file_md5(Path) -> + case beam_lib:md5(Path) of + {ok,{_Mod,MD5}} -> MD5; + _ -> undefined + end. + +beam_file_native_md5(Path, Architecture) -> + try + get_beam_chunk(Path, hipe_unified_loader:chunk_name(Architecture)) + of + NativeCode when is_binary(NativeCode) -> + erlang:md5(NativeCode) + catch + _:_ -> undefined + end. + +get_beam_chunk(Path, Chunk) -> + {ok, {_, [{_, Bin}]}} = beam_lib:chunks(Path, [Chunk]), + Bin. + +%% Returns a list of all modules modified on disk. +-spec modified_modules() -> [module()]. +modified_modules() -> + PathFiles = path_files(), + [M || {M, _} <- code:all_loaded(), + module_status(M, PathFiles) =:= modified]. + +%% prefetch the directory contents of code path directories +path_files() -> + path_files(code:get_path()). + +path_files([]) -> + []; +path_files([Path|Tail]) -> + case erl_prim_loader:list_dir(Path) of + {ok, Files} -> + [{Path,Files} | path_files(Tail)]; + _Error -> + path_files(Tail) + end. diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 29b3f7caaf..4914ce9e4c 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -38,6 +38,7 @@ on_load_purge/1, on_load_self_call/1, on_load_pending/1, on_load_deleted/1, big_boot_embedded/1, + module_status/1, native_early_modules/1, get_mode/1, normalized_paths/1]). @@ -68,6 +69,7 @@ all() -> on_load_binary, on_load_embedded, on_load_errors, on_load_update, on_load_purge, on_load_self_call, on_load_pending, on_load_deleted, + module_status, big_boot_embedded, native_early_modules, get_mode, normalized_paths]. groups() -> @@ -93,6 +95,11 @@ init_per_suite(Config) -> end_per_suite(Config) -> Config. +-define(TESTMOD, test_dummy). +-define(TESTMODSTR, "test_dummy"). +-define(TESTMODSRC, ?TESTMODSTR ".erl"). +-define(TESTMODOBJ, ?TESTMODSTR ".beam"). + init_per_testcase(big_boot_embedded, Config) -> case catch crypto:start() of ok -> @@ -104,6 +111,13 @@ init_per_testcase(_Func, Config) -> P = code:get_path(), [{code_path, P}|Config]. +end_per_testcase(module_status, Config) -> + code:purge(?TESTMOD), + code:delete(?TESTMOD), + code:purge(?TESTMOD), + file:delete(?TESTMODOBJ), + file:delete(?TESTMODSRC), + end_per_testcase(Config); end_per_testcase(TC, Config) when TC == mult_lib_roots; TC == big_boot_embedded -> {ok, HostName} = inet:gethostname(), @@ -1757,6 +1771,172 @@ do_normalized_paths([M|Ms]) -> do_normalized_paths([]) -> ok. +%% Test that module_status/1 behaves as expected +module_status(_Config) -> + %% basics + not_loaded = code:module_status(fubar), % nonexisting + {file, preloaded} = code:is_loaded(erlang), + loaded = code:module_status(erlang), % preloaded + loaded = code:module_status(?MODULE), % normal known loaded + + non_existing = code:which(?TESTMOD), % verify dummy name not in path + code:purge(?TESTMOD), % ensure no previous version in memory + code:delete(?TESTMOD), + code:purge(?TESTMOD), + + %% generated code is detected as such + {ok,?TESTMOD,Bin} = compile:forms(dummy_ast(), []), + {module,?TESTMOD} = code:load_binary(?TESTMOD,"",Bin), % no source file + ok = ?TESTMOD:f(), + "" = code:which(?TESTMOD), % verify empty string for source file + loaded = code:module_status(?TESTMOD), + + %% deleting generated code + true = code:delete(?TESTMOD), + non_existing = code:which(?TESTMOD), % verify still not in path + not_loaded = code:module_status(?TESTMOD), + + %% beam file exists but not loaded + make_source_file(<<"0">>), + compile_beam(0), + true = (non_existing =/= code:which(?TESTMOD)), % verify in path + not_loaded = code:module_status(?TESTMOD), + + %% loading code from disk makes it loaded + load_code(), + loaded = code:module_status(?TESTMOD), % loaded + + %% cover compiling a module + {ok,?TESTMOD} = cover:compile(?TESTMOD), + {file, cover_compiled} = code:is_loaded(?TESTMOD), % verify cover compiled + modified = code:module_status(?TESTMOD), % loaded cover code but file exists + remove_code(), + removed = code:module_status(?TESTMOD), % removed + compile_beam(0), + modified = code:module_status(?TESTMOD), % recreated + load_code(), + loaded = code:module_status(?TESTMOD), % loading removes cover status + code:purge(?TESTMOD), + true = code:delete(?TESTMOD), + not_loaded = code:module_status(?TESTMOD), % deleted + + %% recompilation ignores timestamps, only md5 matters + load_code(), + compile_beam(1100), + loaded = code:module_status(?TESTMOD), + + %% modifying module detects different md5 + make_source_file(<<"1">>), + compile_beam(0), + modified = code:module_status(?TESTMOD), + + %% loading the modified code from disk makes it loaded + load_code(), + loaded = code:module_status(?TESTMOD), + + %% removing and recreating a module with same md5 + remove_code(), + removed = code:module_status(?TESTMOD), + compile_beam(0), + loaded = code:module_status(?TESTMOD), + + case erlang:system_info(hipe_architecture) of + undefined -> + %% no native support + ok; + _ -> + %% native chunk is ignored if beam code is already loaded + load_code(), + loaded = code:module_status(?TESTMOD), + false = has_native(?TESTMOD), + compile_native(0), + BeamMD5 = erlang:get_module_info(?TESTMOD, md5), + {ok,{?TESTMOD,BeamMD5}} = beam_lib:md5(?TESTMODOBJ), % beam md5 unchanged + loaded = code:module_status(?TESTMOD), + + %% native code reported as loaded, though different md5 from beam + load_code(), + true = has_native(?TESTMOD), + NativeMD5 = erlang:get_module_info(?TESTMOD, md5), + true = (BeamMD5 =/= NativeMD5), + loaded = code:module_status(?TESTMOD), + + %% recompilation ignores timestamps, only md5 matters + compile_native(1100), % later timestamp + loaded = code:module_status(?TESTMOD), + + %% modifying native module detects different md5 + make_source_file(<<"2">>), + compile_native(0), + modified = code:module_status(?TESTMOD), + + %% loading the modified native code from disk makes it loaded + load_code(), + true = has_native(?TESTMOD), + NativeMD5_2 = erlang:get_module_info(?TESTMOD, md5), + true = (NativeMD5 =/= NativeMD5_2), % verify native md5 changed + {ok,{?TESTMOD,BeamMD5_2}} = beam_lib:md5(?TESTMODOBJ), + true = (BeamMD5_2 =/= NativeMD5_2), % verify md5 differs from beam + loaded = code:module_status(?TESTMOD), + + %% removing and recreating a native module with same md5 + remove_code(), + removed = code:module_status(?TESTMOD), + compile_native(0), + loaded = code:module_status(?TESTMOD), + + %% purging/deleting native module + code:purge(?TESTMOD), + true = code:delete(?TESTMOD), + not_loaded = code:module_status(?TESTMOD) + end, + ok. + +compile_beam(Sleep) -> + compile(Sleep, []). + +compile_native(Sleep) -> + compile(Sleep, [native]). + +compile(Sleep, Opts) -> + timer:sleep(Sleep), % increment compilation timestamp + {ok,?TESTMOD} = compile:file(?TESTMODSRC, Opts). + +load_code() -> + code:purge(?TESTMOD), + {module,?TESTMOD} = code:load_file(?TESTMOD). + +remove_code() -> + ok = file:delete(?TESTMODOBJ). + +has_native(Module) -> + case erlang:get_module_info(Module, native_addresses) of + [] -> false; + [_|_] -> true + end. + +make_source_file(Body) -> + ok = file:write_file(?TESTMODSRC, dummy_source(Body)). + +dummy_source(Body) -> + [<<"-module(" ?TESTMODSTR ").\n" + "-export([f/0]).\n" + "f() -> ">>, Body, <<".\n">>]. + +dummy_ast() -> + dummy_ast(?TESTMODSTR). + +dummy_ast(Mod) when is_atom(Mod) -> + dummy_ast(atom_to_list(Mod)); +dummy_ast(ModStr) -> + [scan_form("-module(" ++ ModStr ++ ")."), + scan_form("-export([f/0])."), + scan_form("f() -> ok.")]. + +scan_form(String) -> + {ok,Ts,_} = erl_scan:string(String), + {ok,F} = erl_parse:parse_form(Ts), + F. %%----------------------------------------------------------------- %% error_logger handler. -- cgit v1.2.3 From 9bb7aee366b04f6e8b6ea8491c0d7ebbdb304bb6 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Tue, 15 Nov 2016 18:49:39 +0100 Subject: Clarify that the type for disk log data is iodata() --- lib/kernel/doc/src/disk_log.xml | 13 +++---------- lib/kernel/src/disk_log.erl | 18 +++++++----------- lib/kernel/src/disk_log.hrl | 3 +-- 3 files changed, 11 insertions(+), 23 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/disk_log.xml b/lib/kernel/doc/src/disk_log.xml index 3c9bc7f6e8..aebeacee28 100644 --- a/lib/kernel/doc/src/disk_log.xml +++ b/lib/kernel/doc/src/disk_log.xml @@ -109,8 +109,7 @@ These functions log one or more Erlang terms. By prefixing each of the functions with a b (for "binary"), we get the corresponding blog() functions for the external format. - These functions log one or more deep lists of bytes or, alternatively, - binaries of deep lists of bytes. + These functions log one or more chunks of bytes. For example, to log the string "hello" in ASCII format, you can use disk_log:blog(Log, "hello"), or disk_log:blog(Log, list_to_binary("hello")). The two @@ -218,9 +217,6 @@ - - - @@ -233,9 +229,6 @@ chunk/2,3, bchunk/2,3, or chunk_step/3.

- - - @@ -953,7 +946,7 @@ written first on the log file. If the log is a wrap log, the item Head is written first in each new file. Head is to be a term if the format is - internal, otherwise a deep list of bytes (or a binary). + internal, otherwise a sequence of bytes. Defaults to none, which means that no header is written first on the file.

@@ -965,7 +958,7 @@ The call M:F(A) is assumed to return {ok, Head}. The item Head is written first in each file. Head is to be a term if the format is - internal, otherwise a deep list of bytes (or a binary). + internal, otherwise a sequence of bytes.

{mode, Mode} diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index 1b28d05691..bc31f1fbbd 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.erl @@ -75,8 +75,6 @@ -opaque continuation() :: #continuation{}. --type bytes() :: binary() | [byte()]. - -type file_error() :: term(). % XXX: refine -type invalid_header() :: term(). % XXX: refine @@ -131,7 +129,7 @@ log(Log, Term) -> -spec blog(Log, Bytes) -> ok | {error, Reason :: log_error_rsn()} when Log :: log(), - Bytes :: bytes(). + Bytes :: iodata(). blog(Log, Bytes) -> req(Log, {blog, ensure_binary(Bytes)}). @@ -145,7 +143,7 @@ log_terms(Log, Terms) -> -spec blog_terms(Log, BytesList) -> ok | {error, Reason :: log_error_rsn()} when Log :: log(), - BytesList :: [bytes()]. + BytesList :: [iodata()]. blog_terms(Log, Bytess) -> Bs = ensure_binary_list(Bytess), req(Log, {blog, Bs}). @@ -167,13 +165,13 @@ alog_terms(Log, Terms) -> -spec balog(Log, Bytes) -> notify_ret() when Log :: log(), - Bytes :: bytes(). + Bytes :: iodata(). balog(Log, Bytes) -> notify(Log, {balog, ensure_binary(Bytes)}). -spec balog_terms(Log, ByteList) -> notify_ret() when Log :: log(), - ByteList :: [bytes()]. + ByteList :: [iodata()]. balog_terms(Log, Bytess) -> Bs = ensure_binary_list(Bytess), notify(Log, {balog, Bs}). @@ -219,7 +217,7 @@ truncate(Log, Head) -> -spec btruncate(Log, BHead) -> 'ok' | {'error', trunc_error_rsn()} when Log :: log(), - BHead :: bytes(). + BHead :: iodata(). btruncate(Log, Head) -> req(Log, {truncate, {ok, ensure_binary(Head)}, btruncate, 2}). @@ -248,7 +246,7 @@ reopen(Log, NewFile, NewHead) -> -spec breopen(Log, File, BHead) -> 'ok' | {'error', reopen_error_rsn()} when Log :: log(), File :: file:filename(), - BHead :: bytes(). + BHead :: iodata(). breopen(Log, NewFile, NewHead) -> req(Log, {reopen, NewFile, {ok, ensure_binary(NewHead)}, breopen, 3}). @@ -1348,10 +1346,8 @@ make_binary_list([B | Bs]) -> make_binary_list([]) -> []. -ensure_binary(Binary) when is_binary(Binary) -> - Binary; ensure_binary(Bytes) -> - list_to_binary(Bytes). + iolist_to_binary(Bytes). %%----------------------------------------------------------------- %% Change size of the logs in runtime. diff --git a/lib/kernel/src/disk_log.hrl b/lib/kernel/src/disk_log.hrl index 3262d979ee..3cf8a3b3a2 100644 --- a/lib/kernel/src/disk_log.hrl +++ b/lib/kernel/src/disk_log.hrl @@ -54,11 +54,10 @@ %% Types -- alphabetically %%------------------------------------------------------------------------ --type dlog_byte() :: [dlog_byte()] | byte(). -type dlog_format() :: 'external' | 'internal'. -type dlog_format_type() :: 'halt_ext' | 'halt_int' | 'wrap_ext' | 'wrap_int'. -type dlog_head() :: 'none' | {'ok', binary()} | mfa(). --type dlog_head_opt() :: none | term() | binary() | [dlog_byte()]. +-type dlog_head_opt() :: none | term() | iodata(). -type log() :: term(). % XXX: refine -type dlog_mode() :: 'read_only' | 'read_write'. -type dlog_name() :: atom() | string(). -- cgit v1.2.3 From 3900d246682a46cec3b4dcf4c1b13259038ab358 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Wed, 16 Nov 2016 15:11:45 +0100 Subject: Simplify for rflat --- lib/kernel/src/disk_log.erl | 11 ++++------- lib/kernel/test/disk_log_SUITE.erl | 10 +++++----- 2 files changed, 9 insertions(+), 12 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index bc31f1fbbd..2a7afb4c53 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.erl @@ -125,13 +125,13 @@ open(A) -> Log :: log(), Term :: term(). log(Log, Term) -> - req(Log, {log, term_to_binary(Term)}). + req(Log, {log, [term_to_binary(Term)]}). -spec blog(Log, Bytes) -> ok | {error, Reason :: log_error_rsn()} when Log :: log(), Bytes :: iodata(). blog(Log, Bytes) -> - req(Log, {blog, ensure_binary(Bytes)}). + req(Log, {blog, [ensure_binary(Bytes)]}). -spec log_terms(Log, TermList) -> ok | {error, Resaon :: log_error_rsn()} when Log :: log(), @@ -154,7 +154,7 @@ blog_terms(Log, Bytess) -> Log :: log(), Term :: term(). alog(Log, Term) -> - notify(Log, {alog, term_to_binary(Term)}). + notify(Log, {alog, [term_to_binary(Term)]}). -spec alog_terms(Log, TermList) -> notify_ret() when Log :: log(), @@ -167,7 +167,7 @@ alog_terms(Log, Terms) -> Log :: log(), Bytes :: iodata(). balog(Log, Bytes) -> - notify(Log, {balog, ensure_binary(Bytes)}). + notify(Log, {balog, [ensure_binary(Bytes)]}). -spec balog_terms(Log, ByteList) -> notify_ret() when Log :: log(), @@ -1093,12 +1093,9 @@ log_end_sync(S, Sync) -> state_err(S, Res). %% Inlined. -rflat([B]=L) when is_binary(B) -> L; rflat([B]) -> B; rflat(B) -> rflat(B, []). -rflat([B | Bs], L) when is_binary(B) -> - rflat(Bs, [B | L]); rflat([B | Bs], L) -> rflat(Bs, B ++ L); rflat([], L) -> L. diff --git a/lib/kernel/test/disk_log_SUITE.erl b/lib/kernel/test/disk_log_SUITE.erl index f7ad9c0c04..a25b315d9d 100644 --- a/lib/kernel/test/disk_log_SUITE.erl +++ b/lib/kernel/test/disk_log_SUITE.erl @@ -421,7 +421,7 @@ halt_ro_alog(Conf) when is_list(Conf) -> halt_ro_alog_wait_notify(Log, T) -> Term = term_to_binary(T), receive - {disk_log, _, Log,{read_only, Term}} -> + {disk_log, _, Log,{read_only, [Term]}} -> ok; Other -> Other @@ -449,7 +449,7 @@ halt_ro_balog(Conf) when is_list(Conf) -> halt_ro_balog_wait_notify(Log, T) -> Term = list_to_binary(T), receive - {disk_log, _, Log,{read_only, Term}} -> + {disk_log, _, Log,{read_only, [Term]}} -> ok; Other -> Other @@ -1385,15 +1385,15 @@ blocked_notif(Conf) when is_list(Conf) -> "The requested operation" ++ _ = format_error(Error1), ok = disk_log:blog(n, B), ok = disk_log:alog(n, B), - rec(1, {disk_log, node(), n, {format_external, term_to_binary(B)}}), + rec(1, {disk_log, node(), n, {format_external, [term_to_binary(B)]}}), ok = disk_log:alog_terms(n, [B,B,B,B]), rec(1, {disk_log, node(), n, {format_external, lists:map(fun term_to_binary/1, [B,B,B,B])}}), ok = disk_log:block(n, false), ok = disk_log:alog(n, B), - rec(1, {disk_log, node(), n, {blocked_log, term_to_binary(B)}}), + rec(1, {disk_log, node(), n, {blocked_log, [term_to_binary(B)]}}), ok = disk_log:balog(n, B), - rec(1, {disk_log, node(), n, {blocked_log, list_to_binary(B)}}), + rec(1, {disk_log, node(), n, {blocked_log, [list_to_binary(B)]}}), ok = disk_log:balog_terms(n, [B,B,B,B]), disk_log:close(n), rec(1, {disk_log, node(), n, {blocked_log, -- cgit v1.2.3 From 3aadf13da204f955fe7cb6932c75bf71a856650b Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Wed, 16 Nov 2016 21:53:34 +0100 Subject: Eliminate more code duplication --- lib/kernel/src/disk_log.erl | 62 +++++++++++++-------------------------------- 1 file changed, 18 insertions(+), 44 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index 2a7afb4c53..ad3c5ae227 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.erl @@ -125,20 +125,20 @@ open(A) -> Log :: log(), Term :: term(). log(Log, Term) -> - req(Log, {log, [term_to_binary(Term)]}). + req(Log, {log, internal, [term_to_binary(Term)]}). -spec blog(Log, Bytes) -> ok | {error, Reason :: log_error_rsn()} when Log :: log(), Bytes :: iodata(). blog(Log, Bytes) -> - req(Log, {blog, [ensure_binary(Bytes)]}). + req(Log, {log, external, [ensure_binary(Bytes)]}). -spec log_terms(Log, TermList) -> ok | {error, Resaon :: log_error_rsn()} when Log :: log(), TermList :: [term()]. log_terms(Log, Terms) -> Bs = terms2bins(Terms), - req(Log, {log, Bs}). + req(Log, {log, internal, Bs}). -spec blog_terms(Log, BytesList) -> ok | {error, Reason :: log_error_rsn()} when @@ -146,7 +146,7 @@ log_terms(Log, Terms) -> BytesList :: [iodata()]. blog_terms(Log, Bytess) -> Bs = ensure_binary_list(Bytess), - req(Log, {blog, Bs}). + req(Log, {log, external, Bs}). -type notify_ret() :: 'ok' | {'error', 'no_such_log'}. @@ -154,27 +154,27 @@ blog_terms(Log, Bytess) -> Log :: log(), Term :: term(). alog(Log, Term) -> - notify(Log, {alog, [term_to_binary(Term)]}). + notify(Log, {alog, internal, [term_to_binary(Term)]}). -spec alog_terms(Log, TermList) -> notify_ret() when Log :: log(), TermList :: [term()]. alog_terms(Log, Terms) -> Bs = terms2bins(Terms), - notify(Log, {alog, Bs}). + notify(Log, {alog, internal, Bs}). -spec balog(Log, Bytes) -> notify_ret() when Log :: log(), Bytes :: iodata(). balog(Log, Bytes) -> - notify(Log, {balog, [ensure_binary(Bytes)]}). + notify(Log, {alog, external, [ensure_binary(Bytes)]}). -spec balog_terms(Log, ByteList) -> notify_ret() when Log :: log(), ByteList :: [iodata()]. balog_terms(Log, Bytess) -> Bs = ensure_binary_list(Bytess), - notify(Log, {balog, Bs}). + notify(Log, {alog, external, Bs}). -type close_error_rsn() ::'no_such_log' | 'nonode' | {'file_error', file:filename(), file_error()}. @@ -684,25 +684,12 @@ handle({From, write_cache}, S) when From =:= self() -> Error -> loop(S#state{cache_error = Error}) end; -handle({From, {log, B}}=Message, S) -> +handle({From, {log, Format, B}}=Message, S) -> case get(log) of L when L#log.mode =:= read_only -> reply(From, {error, {read_only_mode, L#log.name}}, S); - L when L#log.status =:= ok, L#log.format =:= internal -> - log_loop(S, From, [B], []); - L when L#log.status =:= ok, L#log.format =:= external -> + L when L#log.status =:= ok, L#log.format =:= external, Format =:= internal -> reply(From, {error, {format_external, L#log.name}}, S); - L when L#log.status =:= {blocked, false} -> - reply(From, {error, {blocked_log, L#log.name}}, S); - L when L#log.blocked_by =:= From -> - reply(From, {error, {blocked_log, L#log.name}}, S); - _ -> - enqueue(Message, S) - end; -handle({From, {blog, B}}=Message, S) -> - case get(log) of - L when L#log.mode =:= read_only -> - reply(From, {error, {read_only_mode, L#log.name}}, S); L when L#log.status =:= ok -> log_loop(S, From, [B], []); L when L#log.status =:= {blocked, false} -> @@ -712,27 +699,14 @@ handle({From, {blog, B}}=Message, S) -> _ -> enqueue(Message, S) end; -handle({alog, B}=Message, S) -> +handle({alog, Format, B}=Message, S) -> case get(log) of L when L#log.mode =:= read_only -> notify_owners({read_only,B}), loop(S); - L when L#log.status =:= ok, L#log.format =:= internal -> - log_loop(S, [], [B], []); - L when L#log.status =:= ok -> + L when L#log.status =:= ok, L#log.format =:= external, Format =:= internal -> notify_owners({format_external, B}), loop(S); - L when L#log.status =:= {blocked, false} -> - notify_owners({blocked_log, B}), - loop(S); - _ -> - enqueue(Message, S) - end; -handle({balog, B}=Message, S) -> - case get(log) of - L when L#log.mode =:= read_only -> - notify_owners({read_only,B}), - loop(S); L when L#log.status =:= ok -> log_loop(S, [], [B], []); L when L#log.status =:= {blocked, false} -> @@ -1053,15 +1027,15 @@ log_loop(#state{messages = [M | Ms]}=S, Pids, Bins, Sync, Sz, F) -> log_loop(M, Pids, Bins, Sync, Sz, F, S1). %% Items logged after the last sync request found are sync:ed as well. -log_loop({alog,B}, Pids, Bins, Sync, Sz, internal=F, S) -> - %% {alog, _} allowed for the internal format only. +log_loop({alog, internal, B}, Pids, Bins, Sync, Sz, internal=F, S) -> + %% alog of terms allowed for the internal format only log_loop(S, Pids, [B | Bins], Sync, Sz+iolist_size(B), F); -log_loop({balog, B}, Pids, Bins, Sync, Sz, F, S) -> +log_loop({alog, binary, B}, Pids, Bins, Sync, Sz, F, S) -> log_loop(S, Pids, [B | Bins], Sync, Sz+iolist_size(B), F); -log_loop({From, {log, B}}, Pids, Bins, Sync, Sz, internal=F, S) -> - %% {log, _} allowed for the internal format only. +log_loop({From, {log, internal, B}}, Pids, Bins, Sync, Sz, internal=F, S) -> + %% log of terms allowed for the internal format only log_loop(S, [From | Pids], [B | Bins], Sync, Sz+iolist_size(B), F); -log_loop({From, {blog, B}}, Pids, Bins, Sync, Sz, F, S) -> +log_loop({From, {log, binary, B}}, Pids, Bins, Sync, Sz, F, S) -> log_loop(S, [From | Pids], [B | Bins], Sync, Sz+iolist_size(B), F); log_loop({From, sync}, Pids, Bins, Sync, Sz, F, S) -> log_loop(S, Pids, Bins, [From | Sync], Sz, F); -- cgit v1.2.3 From 5a8a1a963edd6d6dbb35bf615a4bad7d77041443 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Wed, 16 Nov 2016 23:07:29 +0100 Subject: Use pattern matching for records where suitable --- lib/kernel/src/disk_log.erl | 166 ++++++++++++++++++++++---------------------- 1 file changed, 82 insertions(+), 84 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index ad3c5ae227..69c65c9c43 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.erl @@ -668,13 +668,12 @@ init(Parent, Server) -> process_flag(trap_exit, true), loop(#state{parent = Parent, server = Server}). -loop(State) when State#state.messages =:= [] -> +loop(#state{messages = []}=State) -> receive Message -> handle(Message, State) end; -loop(State) -> - [M | Ms] = State#state.messages, +loop(#state{messages = [M | Ms]}=State) -> handle(M, State#state{messages = Ms}). handle({From, write_cache}, S) when From =:= self() -> @@ -686,30 +685,30 @@ handle({From, write_cache}, S) when From =:= self() -> end; handle({From, {log, Format, B}}=Message, S) -> case get(log) of - L when L#log.mode =:= read_only -> + #log{mode = read_only}=L -> reply(From, {error, {read_only_mode, L#log.name}}, S); - L when L#log.status =:= ok, L#log.format =:= external, Format =:= internal -> + #log{status = ok, format=external}=L when Format =:= internal -> reply(From, {error, {format_external, L#log.name}}, S); - L when L#log.status =:= ok -> + #log{status = ok} -> log_loop(S, From, [B], []); - L when L#log.status =:= {blocked, false} -> + #log{status = {blocked, false}}=L -> reply(From, {error, {blocked_log, L#log.name}}, S); - L when L#log.blocked_by =:= From -> + #log{blocked_by = From}=L -> reply(From, {error, {blocked_log, L#log.name}}, S); _ -> enqueue(Message, S) end; handle({alog, Format, B}=Message, S) -> case get(log) of - L when L#log.mode =:= read_only -> + #log{mode = read_only} -> notify_owners({read_only,B}), loop(S); - L when L#log.status =:= ok, L#log.format =:= external, Format =:= internal -> + #log{status = ok, format = external} when Format =:= internal -> notify_owners({format_external, B}), loop(S); - L when L#log.status =:= ok -> + #log{status = ok} -> log_loop(S, [], [B], []); - L when L#log.status =:= {blocked, false} -> + #log{status = {blocked, false}} -> notify_owners({blocked_log, B}), loop(S); _ -> @@ -717,21 +716,21 @@ handle({alog, Format, B}=Message, S) -> end; handle({From, {block, QueueLogRecs}}=Message, S) -> case get(log) of - L when L#log.status =:= ok -> + #log{status = ok}=L -> do_block(From, QueueLogRecs, L), reply(From, ok, S); - L when L#log.status =:= {blocked, false} -> + #log{status = {blocked, false}}=L -> reply(From, {error, {blocked_log, L#log.name}}, S); - L when L#log.blocked_by =:= From -> + #log{blocked_by = From}=L -> reply(From, {error, {blocked_log, L#log.name}}, S); _ -> enqueue(Message, S) end; handle({From, unblock}, S) -> case get(log) of - L when L#log.status =:= ok -> + #log{status = ok}=L -> reply(From, {error, {not_blocked, L#log.name}}, S); - L when L#log.blocked_by =:= From -> + #log{blocked_by = From}=L -> S2 = do_unblock(L, S), reply(From, ok, S2); L -> @@ -739,24 +738,24 @@ handle({From, unblock}, S) -> end; handle({From, sync}=Message, S) -> case get(log) of - L when L#log.mode =:= read_only -> + #log{mode = read_only}=L -> reply(From, {error, {read_only_mode, L#log.name}}, S); - L when L#log.status =:= ok -> + #log{status = ok} -> log_loop(S, [], [], [From]); - L when L#log.status =:= {blocked, false} -> + #log{status = {blocked, false}}=L -> reply(From, {error, {blocked_log, L#log.name}}, S); - L when L#log.blocked_by =:= From -> + #log{blocked_by = From}=L -> reply(From, {error, {blocked_log, L#log.name}}, S); _ -> enqueue(Message, S) end; handle({From, {truncate, Head, F, A}}=Message, S) -> case get(log) of - L when L#log.mode =:= read_only -> + #log{mode = read_only}=L -> reply(From, {error, {read_only_mode, L#log.name}}, S); - L when L#log.status =:= ok, S#state.cache_error =/= ok -> + #log{status = ok} when S#state.cache_error =/= ok -> loop(cache_error(S, [From])); - L when L#log.status =:= ok -> + #log{status = ok}=L -> H = merge_head(Head, L#log.head), case catch do_trunc(L, H) of ok -> @@ -767,46 +766,46 @@ handle({From, {truncate, Head, F, A}}=Message, S) -> Error -> do_exit(S, From, Error, ?failure(Error, F, A)) end; - L when L#log.status =:= {blocked, false} -> + #log{status = {blocked, false}}=L -> reply(From, {error, {blocked_log, L#log.name}}, S); - L when L#log.blocked_by =:= From -> + #log{blocked_by = From}=L -> reply(From, {error, {blocked_log, L#log.name}}, S); _ -> enqueue(Message, S) end; handle({From, {chunk, Pos, B, N}}=Message, S) -> case get(log) of - L when L#log.status =:= ok, S#state.cache_error =/= ok -> + #log{status = ok} when S#state.cache_error =/= ok -> loop(cache_error(S, [From])); - L when L#log.status =:= ok -> + #log{status = ok}=L -> R = do_chunk(L, Pos, B, N), reply(From, R, S); - L when L#log.blocked_by =:= From -> + #log{blocked_by = From}=L -> R = do_chunk(L, Pos, B, N), reply(From, R, S); - L when L#log.status =:= {blocked, false} -> + #log{status = {blocked, false}}=L -> reply(From, {error, {blocked_log, L#log.name}}, S); _L -> enqueue(Message, S) end; handle({From, {chunk_step, Pos, N}}=Message, S) -> case get(log) of - L when L#log.status =:= ok, S#state.cache_error =/= ok -> + #log{status = ok} when S#state.cache_error =/= ok -> loop(cache_error(S, [From])); - L when L#log.status =:= ok -> + #log{status = ok}=L -> R = do_chunk_step(L, Pos, N), reply(From, R, S); - L when L#log.blocked_by =:= From -> + #log{blocked_by = From}=L -> R = do_chunk_step(L, Pos, N), reply(From, R, S); - L when L#log.status =:= {blocked, false} -> + #log{status = {blocked, false}}=L -> reply(From, {error, {blocked_log, L#log.name}}, S); _ -> enqueue(Message, S) end; handle({From, {change_notify, Pid, NewNotify}}=Message, S) -> case get(log) of - L when L#log.status =:= ok -> + #log{status = ok}=L -> case do_change_notify(L, Pid, NewNotify) of {ok, L1} -> put(log, L1), @@ -814,37 +813,37 @@ handle({From, {change_notify, Pid, NewNotify}}=Message, S) -> Error -> reply(From, Error, S) end; - L when L#log.status =:= {blocked, false} -> + #log{status = {blocked, false}}=L -> reply(From, {error, {blocked_log, L#log.name}}, S); - L when L#log.blocked_by =:= From -> + #log{blocked_by = From}=L -> reply(From, {error, {blocked_log, L#log.name}}, S); _ -> enqueue(Message, S) end; handle({From, {change_header, NewHead}}=Message, S) -> case get(log) of - L when L#log.mode =:= read_only -> + #log{mode = read_only}=L -> reply(From, {error, {read_only_mode, L#log.name}}, S); - L when L#log.status =:= ok -> - case check_head(NewHead, L#log.format) of + #log{status = ok, format = Format}=L -> + case check_head(NewHead, Format) of {ok, Head} -> - put(log, L#log{head = mk_head(Head, L#log.format)}), + put(log, L#log{head = mk_head(Head, Format)}), reply(From, ok, S); Error -> reply(From, Error, S) end; - L when L#log.status =:= {blocked, false} -> + #log{status = {blocked, false}}=L -> reply(From, {error, {blocked_log, L#log.name}}, S); - L when L#log.blocked_by =:= From -> + #log{blocked_by = From}=L -> reply(From, {error, {blocked_log, L#log.name}}, S); _ -> enqueue(Message, S) end; handle({From, {change_size, NewSize}}=Message, S) -> case get(log) of - L when L#log.mode =:= read_only -> + #log{mode = read_only}=L -> reply(From, {error, {read_only_mode, L#log.name}}, S); - L when L#log.status =:= ok -> + #log{status = ok}=L -> case check_size(L#log.type, NewSize) of ok -> case catch do_change_size(L, NewSize) of % does the put @@ -861,22 +860,22 @@ handle({From, {change_size, NewSize}}=Message, S) -> not_ok -> reply(From, {error, {badarg, size}}, S) end; - L when L#log.status =:= {blocked, false} -> + #log{status = {blocked, false}}=L -> reply(From, {error, {blocked_log, L#log.name}}, S); - L when L#log.blocked_by =:= From -> + #log{blocked_by = From}=L -> reply(From, {error, {blocked_log, L#log.name}}, S); _ -> enqueue(Message, S) end; handle({From, inc_wrap_file}=Message, S) -> case get(log) of - L when L#log.mode =:= read_only -> + #log{mode = read_only}=L -> reply(From, {error, {read_only_mode, L#log.name}}, S); - L when L#log.type =:= halt -> + #log{type = halt}=L -> reply(From, {error, {halt_log, L#log.name}}, S); - L when L#log.status =:= ok, S#state.cache_error =/= ok -> + #log{status = ok} when S#state.cache_error =/= ok -> loop(cache_error(S, [From])); - L when L#log.status =:= ok -> + #log{status = ok}=L -> case catch do_inc_wrap_file(L) of {ok, L2, Lost} -> put(log, L2), @@ -886,20 +885,22 @@ handle({From, inc_wrap_file}=Message, S) -> put(log, L2), reply(From, Error, state_err(S, Error)) end; - L when L#log.status =:= {blocked, false} -> + #log{status = {blocked, false}}=L -> reply(From, {error, {blocked_log, L#log.name}}, S); - L when L#log.blocked_by =:= From -> + #log{blocked_by = From}=L -> reply(From, {error, {blocked_log, L#log.name}}, S); _ -> enqueue(Message, S) end; handle({From, {reopen, NewFile, Head, F, A}}, S) -> case get(log) of - L when L#log.mode =:= read_only -> + #log{mode = read_only}=L -> reply(From, {error, {read_only_mode, L#log.name}}, S); - L when L#log.status =:= ok, S#state.cache_error =/= ok -> + #log{status = ok} when S#state.cache_error =/= ok -> loop(cache_error(S, [From])); - L when L#log.status =:= ok, L#log.filename =/= NewFile -> + #log{status = ok, filename = NewFile}=L -> + reply(From, {error, {same_file_name, L#log.name}}, S); + #log{status = ok}=L -> case catch close_disk_log2(L) of closed -> File = L#log.filename, @@ -932,8 +933,6 @@ handle({From, {reopen, NewFile, Head, F, A}}, S) -> Error -> do_exit(S, From, Error, ?failure(Error, F, A)) end; - L when L#log.status =:= ok -> - reply(From, {error, {same_file_name, L#log.name}}, S); L -> reply(From, {error, {blocked_log, L#log.name}}, S) end; @@ -971,11 +970,11 @@ handle({From, close}, S) -> end; handle({From, info}, S) -> reply(From, do_info(get(log), S#state.cnt), S); -handle({'EXIT', From, Reason}, S) when From =:= S#state.parent -> +handle({'EXIT', From, Reason}, #state{parent=From}=S) -> %% Parent orders shutdown. _ = do_stop(S), exit(Reason); -handle({'EXIT', From, Reason}, S) when From =:= S#state.server -> +handle({'EXIT', From, Reason}, #state{server=From}=S) -> %% The server is gone. _ = do_stop(S), exit(Reason); @@ -1000,8 +999,8 @@ handle({system, From, Req}, S) -> handle(_, S) -> loop(S). -enqueue(Message, S) -> - loop(S#state{queue = [Message | S#state.queue]}). +enqueue(Message, #state{queue = Queue}=S) -> + loop(S#state{queue = [Message | Queue]}). %% Collect further log and sync requests already in the mailbox or queued @@ -1045,17 +1044,17 @@ log_loop(Message, Pids, Bins, Sync, Sz, _F, S) -> log_end(S, [], [], Sync, _Sz) -> log_end_sync(S, Sync); -log_end(S, Pids, Bins, Sync, Sz) -> +log_end(#state{cnt = Cnt}=S, Pids, Bins, Sync, Sz) -> case do_log(get(log), rflat(Bins), Sz) of N when is_integer(N) -> ok = replies(Pids, ok), - S1 = (state_ok(S))#state{cnt = S#state.cnt+N}, + S1 = (state_ok(S))#state{cnt = Cnt + N}, log_end_sync(S1, Sync); {error, {error, {full, _Name}}, N} when Pids =:= [] -> - log_end_sync(state_ok(S#state{cnt = S#state.cnt + N}), Sync); + log_end_sync(state_ok(S#state{cnt = Cnt + N}), Sync); {error, Error, N} -> ok = replies(Pids, Error), - state_err(S#state{cnt = S#state.cnt + N}, Error) + state_err(S#state{cnt = Cnt + N}, Error) end. %% Inlined. @@ -1106,17 +1105,17 @@ close_owner(Pid, L, S) -> S2 = do_unblock(Pid, get(log), S), unlink(Pid), do_close2(L1, S2). - + %% -> {stop, S} | {continue, S} -close_user(Pid, L, S) when L#log.users > 0 -> - L1 = L#log{users = L#log.users - 1}, +close_user(Pid, #log{users=Users}=L, S) when Users > 0 -> + L1 = L#log{users = Users - 1}, put(log, L1), S2 = do_unblock(Pid, get(log), S), do_close2(L1, S2); close_user(_Pid, _L, S) -> {continue, S}. -do_close2(L, S) when L#log.users =:= 0, L#log.owners =:= [] -> +do_close2(#log{users = 0, owners = []}, S) -> {stop, S}; do_close2(_L, S) -> {continue, S}. @@ -1195,14 +1194,14 @@ add_pid(Pid, Notify, L) when is_pid(Pid) -> add_pid(_NotAPid, _Notify, L) -> {ok, L#log{users = L#log.users + 1}}. -unblock_pid(L) when L#log.blocked_by =:= none -> +unblock_pid(#log{blocked_by = none}) -> ok; -unblock_pid(L) -> - case is_owner(L#log.blocked_by, L) of +unblock_pid(#log{blocked_by = Pid}=L) -> + case is_owner(Pid, L) of {true, _Notify} -> ok; false -> - unlink(L#log.blocked_by) + unlink(Pid) end. %% -> true | false @@ -1324,7 +1323,7 @@ ensure_binary(Bytes) -> %% Change size of the logs in runtime. %%----------------------------------------------------------------- %% -> ok | {big, CurSize} | throw(Error) -do_change_size(L, NewSize) when L#log.type =:= halt -> +do_change_size(#log{type = halt}=L, NewSize) -> Halt = L#log.extra, CurB = Halt#halt.curB, NewLog = L#log{extra = Halt#halt{size = NewSize}}, @@ -1340,7 +1339,7 @@ do_change_size(L, NewSize) when L#log.type =:= halt -> true -> {big, CurB} end; -do_change_size(L, NewSize) when L#log.type =:= wrap -> +do_change_size(#log{type = wrap}=L, NewSize) -> #log{extra = Extra, version = Version} = L, {ok, Handle} = disk_log_1:change_size_wrap(Extra, NewSize, Version), erase(is_full), @@ -1641,7 +1640,7 @@ do_block(Pid, QueueLogRecs, L) -> link(Pid) end. -do_unblock(Pid, L, S) when L#log.blocked_by =:= Pid -> +do_unblock(Pid, #log{blocked_by = Pid}=L, S) -> do_unblock(L, S); do_unblock(_Pid, _L, S) -> S. @@ -1662,7 +1661,7 @@ do_unblock(L, S) -> do_log(L, B) -> do_log(L, B, iolist_size(B)). -do_log(L, B, BSz) when L#log.type =:= halt -> +do_log(#log{type = halt}=L, B, BSz) -> #log{format = Format, extra = Halt} = L, #halt{curB = CurSize, size = Sz} = Halt, {Bs, BSize} = logl(B, Format, BSz), @@ -1674,7 +1673,7 @@ do_log(L, B, BSz) when L#log.type =:= halt -> undefined -> halt_write_full(L, B, Format, 0) end; -do_log(L, B, _BSz) when L#log.format_type =:= wrap_int -> +do_log(#log{format_type = wrap_int}=L, B, _BSz) -> case disk_log_1:mf_int_log(L#log.extra, B, L#log.head) of {ok, Handle, Logged, Lost, Wraps} -> notify_owners_wrap(Wraps), @@ -1687,7 +1686,7 @@ do_log(L, B, _BSz) when L#log.format_type =:= wrap_int -> put(log, L#log{extra = Handle}), {error, Error, Logged - Lost} end; -do_log(L, B, _BSz) when L#log.format_type =:= wrap_ext -> +do_log(#log{format_type = wrap_ext}=L, B, _BSz) -> case disk_log_1:mf_ext_log(L#log.extra, B, L#log.head) of {ok, Handle, Logged, Lost, Wraps} -> notify_owners_wrap(Wraps), @@ -1762,7 +1761,7 @@ do_sync(#log{type = wrap, extra = Handle} = Log) -> Reply. %% -> ok | Error | throw(Error) -do_trunc(L, Head) when L#log.type =:= halt -> +do_trunc(#log{type = halt}=L, Head) -> #log{filename = FName, extra = Halt} = L, FdC = Halt#halt.fdc, {Reply1, FdC2} = @@ -1791,7 +1790,7 @@ do_trunc(L, Head) when L#log.type =:= halt -> end, put(log, L#log{extra = NewHalt}), Reply; -do_trunc(L, Head) when L#log.type =:= wrap -> +do_trunc(#log{type = wrap}=L, Head) -> Handle = L#log.extra, OldHead = L#log.head, {MaxB, MaxF} = disk_log_1:get_wrap_size(Handle), @@ -1985,8 +1984,7 @@ notify_owners(Note) -> (_) -> ok end, L#log.owners). -cache_error(S, Pids) -> - Error = S#state.cache_error, +cache_error(#state{cache_error=Error}=S, Pids) -> ok = replies(Pids, Error), state_err(S#state{cache_error = ok}, Error). -- cgit v1.2.3 From eda5c4859f561fae7d3a3b74f19dd8596c212966 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Tue, 1 Nov 2016 15:36:57 +0100 Subject: Improve caching in disk_log Avoid starting timers for flushing when the written data is empty or larger than the max cache size. Previously, a single huge write to an empty cache would be put in the cache until the next write or the timer event. Also increase the cache size from 16K to 64K. --- lib/kernel/src/disk_log.hrl | 1 + lib/kernel/src/disk_log_1.erl | 32 ++++++++++++++++++++++---------- lib/kernel/test/disk_log_SUITE.erl | 4 ++-- 3 files changed, 25 insertions(+), 12 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/disk_log.hrl b/lib/kernel/src/disk_log.hrl index 3cf8a3b3a2..593dbb31ab 100644 --- a/lib/kernel/src/disk_log.hrl +++ b/lib/kernel/src/disk_log.hrl @@ -39,6 +39,7 @@ -define(MAX_FILES, 65000). -define(MAX_BYTES, ((1 bsl 64) - 1)). -define(MAX_CHUNK_SIZE, 65536). +-define(MAX_FWRITE_CACHE, 65536). %% Object defines -define(LOGMAGIC, <<1,2,3,4>>). diff --git a/lib/kernel/src/disk_log_1.erl b/lib/kernel/src/disk_log_1.erl index 2e61363aa6..d83c30f35f 100644 --- a/lib/kernel/src/disk_log_1.erl +++ b/lib/kernel/src/disk_log_1.erl @@ -1416,24 +1416,36 @@ open_truncate(FileName) -> %%% Functions that access files, and throw on error. --define(MAX, 16384). % bytes -define(TIMEOUT, 2000). % ms %% -> {Reply, cache()}; Reply = ok | Error -fwrite(#cache{c = []} = FdC, _FN, B, Size) -> +fwrite(FdC, _FN, _B, 0) -> + {ok, FdC}; % avoid starting a timer for empty writes +fwrite(#cache{fd = Fd, c = C, sz = Sz} = FdC, FileName, B, Size) -> + Sz1 = Sz + Size, + C1 = cache_append(C, B), + if Sz1 > ?MAX_FWRITE_CACHE -> + write_cache(Fd, FileName, C1); + true -> + maybe_start_timer(C), + {ok, FdC#cache{sz = Sz1, c = C1}} + end. + +cache_append([], B) -> B; +cache_append(C, B) -> [C | B]. + +%% if the cache was empty, start timer (unless it's already running) +maybe_start_timer([]) -> case get(write_cache_timer_is_running) of - true -> + true -> ok; - _ -> + _ -> put(write_cache_timer_is_running, true), erlang:send_after(?TIMEOUT, self(), {self(), write_cache}), ok - end, - {ok, FdC#cache{sz = Size, c = B}}; -fwrite(#cache{sz = Sz, c = C} = FdC, _FN, B, Size) when Sz < ?MAX -> - {ok, FdC#cache{sz = Sz+Size, c = [C | B]}}; -fwrite(#cache{fd = Fd, c = C}, FileName, B, _Size) -> - write_cache(Fd, FileName, [C | B]). + end; +maybe_start_timer(_C) -> + ok. fwrite_header(Fd, B, Size) -> {ok, #cache{fd = Fd, sz = Size, c = B}}. diff --git a/lib/kernel/test/disk_log_SUITE.erl b/lib/kernel/test/disk_log_SUITE.erl index a25b315d9d..23fe975ef7 100644 --- a/lib/kernel/test/disk_log_SUITE.erl +++ b/lib/kernel/test/disk_log_SUITE.erl @@ -4666,7 +4666,7 @@ other_groups(Conf) when is_list(Conf) -> ok. --define(MAX, 16384). % MAX in disk_log_1.erl +-define(MAX, ?MAX_FWRITE_CACHE). % as in disk_log_1.erl %% Evil cases such as closed file descriptor port. evil(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), @@ -4690,7 +4690,7 @@ evil(Conf) when is_list(Conf) -> {size,?MAX+50},{format,external}]), [Fd] = erlang:ports() -- Ports0, {B,_} = x_mk_bytes(30), - ok = disk_log:blog(Log, <<0:(?MAX+1)/unit:8>>), + ok = disk_log:blog(Log, <<0:(?MAX-1)/unit:8>>), exit(Fd, kill), {error, {file_error,_,einval}} = disk_log:blog_terms(Log, [B,B]), ok= disk_log:close(Log), -- cgit v1.2.3 From 93ac8d2b4b2937b9b7651b5e043c31929f3b2c7c Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Fri, 25 Nov 2016 21:26:19 +0100 Subject: Pass log format through from handle() --- lib/kernel/src/disk_log.erl | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index 69c65c9c43..2ade7fd77a 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.erl @@ -689,8 +689,8 @@ handle({From, {log, Format, B}}=Message, S) -> reply(From, {error, {read_only_mode, L#log.name}}, S); #log{status = ok, format=external}=L when Format =:= internal -> reply(From, {error, {format_external, L#log.name}}, S); - #log{status = ok} -> - log_loop(S, From, [B], []); + #log{status = ok, format=LogFormat} -> + log_loop(S, From, [B], [], iolist_size(B), LogFormat); #log{status = {blocked, false}}=L -> reply(From, {error, {blocked_log, L#log.name}}, S); #log{blocked_by = From}=L -> @@ -706,8 +706,8 @@ handle({alog, Format, B}=Message, S) -> #log{status = ok, format = external} when Format =:= internal -> notify_owners({format_external, B}), loop(S); - #log{status = ok} -> - log_loop(S, [], [B], []); + #log{status = ok, format=LogFormat} -> + log_loop(S, [], [B], [], iolist_size(B), LogFormat); #log{status = {blocked, false}} -> notify_owners({blocked_log, B}), loop(S); @@ -740,8 +740,8 @@ handle({From, sync}=Message, S) -> case get(log) of #log{mode = read_only}=L -> reply(From, {error, {read_only_mode, L#log.name}}, S); - #log{status = ok} -> - log_loop(S, [], [], [From]); + #log{status = ok, format=LogFormat} -> + log_loop(S, [], [], [From], 0, LogFormat); #log{status = {blocked, false}}=L -> reply(From, {error, {blocked_log, L#log.name}}, S); #log{blocked_by = From}=L -> @@ -1006,9 +1006,6 @@ enqueue(Message, #state{queue = Queue}=S) -> -define(MAX_LOOK_AHEAD, 64*1024). -log_loop(S, Pids, Bins, Sync) -> - log_loop(S, Pids, Bins, Sync, iolist_size(Bins), (get(log))#log.format). - %% Inlined. log_loop(#state{cache_error = CE}=S, Pids, _Bins, _Sync, _Sz, _F) when CE =/= ok -> loop(cache_error(S, Pids)); -- cgit v1.2.3 From 3eddb0f762de248d3230b38bc9d478bfbc8e7331 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Wed, 7 Dec 2016 13:15:31 +0100 Subject: Update copyright-year --- lib/kernel/src/global_group.erl | 2 +- lib/kernel/src/inet_tcp_dist.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/global_group.erl b/lib/kernel/src/global_group.erl index 8ac0bd9551..f5ead2a4c5 100644 --- a/lib/kernel/src/global_group.erl +++ b/lib/kernel/src/global_group.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2015. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. 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. diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl index 3084bd599a..8c8fe86811 100644 --- a/lib/kernel/src/inet_tcp_dist.erl +++ b/lib/kernel/src/inet_tcp_dist.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2015. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. 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. -- cgit v1.2.3 From fc0427be6d482182ec70f3cd87c73027cfb17ea9 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 9 Dec 2016 11:45:22 +0100 Subject: Prepare release --- lib/kernel/doc/src/notes.xml | 33 +++++++++++++++++++++++++++++++++ lib/kernel/vsn.mk | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 5bcc0b7c09..9277c2d353 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,39 @@

This document describes the changes made to the Kernel application.

+
Kernel 5.1.1 + +
Fixed Bugs and Malfunctions + + +

+ code:add_pathsa/1 and command line option + -pa both revert the given list of directories when + adding it at the beginning of the code path. This is now + documented.

+

+ Own Id: OTP-13920 Aux Id: ERL-267

+
+ +

+ Add lost runtime dependency to erts-8.1. This should have + been done in kernel-5.1 (OTP-19.1) as it cannot run + without at least erts-8.1 (OTP-19.1).

+

+ Own Id: OTP-14003

+
+ +

+ Type and doc for gen_{tcp,udp,sctp}:controlling_process/2 + has been improved.

+

+ Own Id: OTP-14022 Aux Id: PR-1208

+
+
+
+ +
+
Kernel 5.1
Fixed Bugs and Malfunctions diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index d3b2d18ae5..8d2517e680 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 5.1 +KERNEL_VSN = 5.1.1 -- cgit v1.2.3 From 68728bb7fb82331c1aa7aeee71a971de842eff0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 11 Jan 2017 13:21:33 +0100 Subject: seq_trace: Remove superfluous reference to R3B MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported-by: Loïc Hoguin --- lib/kernel/doc/src/seq_trace.xml | 6 ------ 1 file changed, 6 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/seq_trace.xml b/lib/kernel/doc/src/seq_trace.xml index ba7259219d..b80e87c118 100644 --- a/lib/kernel/doc/src/seq_trace.xml +++ b/lib/kernel/doc/src/seq_trace.xml @@ -427,12 +427,6 @@ prev_cnt := tcurr built with Erl_Interface only maintains one trace token, which means that the C-node appears as one process from the sequential tracing point of view.

-

To be able to perform sequential tracing between - distributed Erlang nodes, the distribution protocol has been - extended (in a backward compatible way). An Erlang node - supporting sequential tracing can communicate with an older - (Erlang/OTP R3B) node but messages passed within that node can - not be traced.

-- cgit v1.2.3 From 5056a16cfcfdcdcb1b48e24936f12a70269dcf02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 11 Jan 2017 13:24:12 +0100 Subject: code.xml: Remove superfluous reference to R12B --- lib/kernel/doc/src/code.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml index f881fd76fd..878a450f0f 100644 --- a/lib/kernel/doc/src/code.xml +++ b/lib/kernel/doc/src/code.xml @@ -258,7 +258,7 @@ zip:create("mnesia-4.4.7.ez", both strings and atoms, but a future release will probably only allow the arguments that are documented.

-

As from Erlang/OTP R12B, functions in this module generally fail with an +

Functions in this module generally fail with an exception if they are passed an incorrect type (for example, an integer or a tuple where an atom is expected). An error tuple is returned if the argument type is correct, but there are some other errors (for example, a non-existing directory -- cgit v1.2.3 From b69086df7487e74879de6b6f027df6507ec4fc86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 11 Jan 2017 14:13:49 +0100 Subject: config.xml: Remove superfluous reference to R10B --- lib/kernel/doc/src/config.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/config.xml b/lib/kernel/doc/src/config.xml index c5f37fd036..c10f11b187 100644 --- a/lib/kernel/doc/src/config.xml +++ b/lib/kernel/doc/src/config.xml @@ -77,8 +77,8 @@ to update the application configurations.

This means that specifying another .config file, or more .config files, leads to inconsistent update of application - configurations. Therefore, in Erlang 5.4/OTP R10B, the syntax of - sys.config was extended to allow pointing out other + configurations. There is, however, a syntax for + sys.config that allows pointing out other .config files:

[{Application, [{Par, Val}]} | File]. -- cgit v1.2.3 From 5aff60d96efac96a41b514ed167f13eb787a415f Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 26 Sep 2016 17:05:50 +0200 Subject: Support for dirty BIFs --- lib/kernel/src/erts_debug.erl | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl index 7b3f1e313a..ad92aafc2f 100644 --- a/lib/kernel/src/erts_debug.erl +++ b/lib/kernel/src/erts_debug.erl @@ -35,7 +35,8 @@ dump_monitors/1, dump_links/1, flat_size/1, get_internal_state/1, instructions/0, lock_counters/1, map_info/1, same/2, set_internal_state/2, - size_shared/1, copy_shared/1]). + size_shared/1, copy_shared/1, dirty_cpu/2, dirty_io/2, + dirty/3]). -spec breakpoint(MFA, Flag) -> non_neg_integer() when MFA :: {Module :: module(), @@ -182,6 +183,28 @@ same(_, _) -> set_internal_state(_, _) -> erlang:nif_error(undef). +-spec dirty_cpu(Term1, Term2) -> term() when + Term1 :: term(), + Term2 :: term(). + +dirty_cpu(_, _) -> + erlang:nif_error(undef). + +-spec dirty_io(Term1, Term2) -> term() when + Term1 :: term(), + Term2 :: term(). + +dirty_io(_, _) -> + erlang:nif_error(undef). + +-spec dirty(Term1, Term2, Term3) -> term() when + Term1 :: term(), + Term2 :: term(), + Term3 :: term(). + +dirty(_, _, _) -> + erlang:nif_error(undef). + %%% End of BIFs %% size(Term) -- cgit v1.2.3 From a7b52ad679e6a58a9351a26e198eee70067b000f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 13 Apr 2015 18:47:06 +0200 Subject: kernel: Add gen_event signal server and default handler --- lib/kernel/src/Makefile | 1 + lib/kernel/src/erl_signal_handler.erl | 47 +++++++++++++++++++++++++++++++++++ lib/kernel/src/kernel.app.src | 1 + lib/kernel/src/kernel.erl | 13 +++++++++- 4 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 lib/kernel/src/erl_signal_handler.erl (limited to 'lib/kernel') diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile index 2b72f78dcf..2a89faaf13 100644 --- a/lib/kernel/src/Makefile +++ b/lib/kernel/src/Makefile @@ -71,6 +71,7 @@ MODULES = \ erl_distribution \ erl_epmd \ erl_reply \ + erl_signal_handler \ erts_debug \ error_handler \ error_logger \ diff --git a/lib/kernel/src/erl_signal_handler.erl b/lib/kernel/src/erl_signal_handler.erl new file mode 100644 index 0000000000..6bd8f992a7 --- /dev/null +++ b/lib/kernel/src/erl_signal_handler.erl @@ -0,0 +1,47 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(erl_signal_handler). +-behaviour(gen_event). +-export([init/1, format_status/2, + handle_event/2, handle_call/2, handle_info/2, + terminate/2, code_change/3]). + +-record(state,{}). + +init(_Args) -> + {ok, #state{}}. + +handle_event(_SignalMsg, S) -> + {ok, S}. + +handle_info(_Info, S) -> + {ok, S}. + +handle_call(_Request, S) -> + {ok, ok, S}. + +format_status(_Opt, [_Pdict,_S]) -> + ok. + +code_change(_OldVsn, S, _Extra) -> + {ok, S}. + +terminate(_Args, _S) -> + ok. diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 4d08a55c7c..25e4ddd95c 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -34,6 +34,7 @@ erl_boot_server, erl_distribution, erl_reply, + erl_signal_handler, error_handler, error_logger, file, diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl index 3d0ef81318..59eca242b1 100644 --- a/lib/kernel/src/kernel.erl +++ b/lib/kernel/src/kernel.erl @@ -32,6 +32,14 @@ start(_, []) -> case supervisor:start_link({local, kernel_sup}, kernel, []) of {ok, Pid} -> + %% add signal handler + case whereis(erl_signal_server) of + %% in case of minimal mode + undefined -> ok; + _ -> + ok = gen_event:add_handler(erl_signal_server, erl_signal_handler, []) + end, + %% add error handler Type = get_error_logger_type(), case error_logger:swap_handler(Type) of ok -> {ok, Pid, []}; @@ -131,6 +139,9 @@ init([]) -> permanent, 2000, worker, [inet_db]}, NetSup = {net_sup, {erl_distribution, start_link, []}, permanent, infinity, supervisor,[erl_distribution]}, + SigSrv = #{id => erl_signal_server, + start => {gen_event, start_link, [{local, erl_signal_server}]}, + type => worker, restart => permanent, shutdown => 2000, modules => dynamic}, DistAC = start_dist_ac(), Timer = start_timer(), @@ -141,7 +152,7 @@ init([]) -> permanent, infinity, supervisor, [?MODULE]}, {ok, {SupFlags, [Code, Rpc, Global, InetDb | DistAC] ++ - [NetSup, Glo_grp, File, + [NetSup, Glo_grp, File, SigSrv, StdError, User, Config, SafeSupervisor] ++ Timer}} end; init(safe) -> -- cgit v1.2.3 From 40c82769def0cfa59e75669ff1c6fc4abcecd764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 15 Dec 2016 18:07:13 +0100 Subject: erts: Handle SIGTERM via signal service instead --- lib/kernel/src/erl_signal_handler.erl | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/src/erl_signal_handler.erl b/lib/kernel/src/erl_signal_handler.erl index 6bd8f992a7..04130eac66 100644 --- a/lib/kernel/src/erl_signal_handler.erl +++ b/lib/kernel/src/erl_signal_handler.erl @@ -28,6 +28,10 @@ init(_Args) -> {ok, #state{}}. +handle_event(sigterm, S) -> + error_logger:info_msg("SIGTERM received - shutting down~n"), + ok = init:stop(), + {ok, S}; handle_event(_SignalMsg, S) -> {ok, S}. -- cgit v1.2.3 From 2d3bf84d8167b50728b0a5411a4e2dfa71d52c10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 16 Dec 2016 12:24:13 +0100 Subject: erts: Handle SIGUSR1 via signal service instead --- lib/kernel/src/erl_signal_handler.erl | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/src/erl_signal_handler.erl b/lib/kernel/src/erl_signal_handler.erl index 04130eac66..43cc307ffd 100644 --- a/lib/kernel/src/erl_signal_handler.erl +++ b/lib/kernel/src/erl_signal_handler.erl @@ -28,6 +28,9 @@ init(_Args) -> {ok, #state{}}. +handle_event(sigusr1, S) -> + erlang:halt("Received SIGUSR1"), + {ok, S}; handle_event(sigterm, S) -> error_logger:info_msg("SIGTERM received - shutting down~n"), ok = init:stop(), -- cgit v1.2.3 From 26b59dfe67ef551cd94765557cdd8c79794bcc38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 31 May 2016 14:28:54 +0200 Subject: Add new AtU8 beam chunk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new chunk stores atoms encoded in UTF-8. beam_lib has also been modified to handle the new 'utf8_atoms' attribute while the 'atoms' attribute may be a missing chunk from now on. The binary_to_atom/2 BIF can now encode any utf8 binary with up to 255 characters. The list_to_atom/1 BIF can now accept codepoints higher than 255 with up to 255 characters (thanks to Björn Gustavsson). --- lib/kernel/test/code_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 4914ce9e4c..f368232bfc 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -323,7 +323,7 @@ load_abs(Config) when is_list(Config) -> {error, nofile} = code:load_abs(TestDir ++ "/duuuumy_mod"), {error, badfile} = code:load_abs(TestDir ++ "/code_a_test"), {'EXIT', _} = (catch code:load_abs({})), - {'EXIT', _} = (catch code:load_abs("Non-latin-имя-файла")), + {error, nofile} = code:load_abs("Non-latin-имя-файла"), {module, code_b_test} = code:load_abs(TestDir ++ "/code_b_test"), code:stick_dir(TestDir), {error, sticky_directory} = code:load_abs(TestDir ++ "/code_b_test"), -- cgit v1.2.3 From c02ea6ca5154f6f9f2fa4cc89ab70077df0dd66b Mon Sep 17 00:00:00 2001 From: pulitta Date: Wed, 14 Dec 2016 18:23:24 +0300 Subject: file: match enoent and enotdir in path_open --- lib/kernel/src/file.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index 58b601e456..6d94f7770f 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -1413,7 +1413,7 @@ path_open_first([Path|Rest], Name, Mode, LastError) -> case open(FileName, Mode) of {ok, Fd} -> {ok, Fd, FileName}; - {error, enoent} -> + {error, Reason} when Reason =:= enoent; Reason =:= enotdir -> path_open_first(Rest, Name, Mode, LastError); Error -> Error -- cgit v1.2.3 From 120f04387ade07ef5b8b6d20a04de7d21e0c40ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 5 Jan 2017 17:17:52 +0100 Subject: erts: Use generic signal handler --- lib/kernel/src/erl_signal_handler.erl | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/src/erl_signal_handler.erl b/lib/kernel/src/erl_signal_handler.erl index 43cc307ffd..8f924d2adc 100644 --- a/lib/kernel/src/erl_signal_handler.erl +++ b/lib/kernel/src/erl_signal_handler.erl @@ -31,6 +31,9 @@ init(_Args) -> handle_event(sigusr1, S) -> erlang:halt("Received SIGUSR1"), {ok, S}; +handle_event(sigquit, S) -> + erlang:halt(), + {ok, S}; handle_event(sigterm, S) -> error_logger:info_msg("SIGTERM received - shutting down~n"), ok = init:stop(), -- cgit v1.2.3 From d4bd17da9759af54227891a90d9fb83d5bfe6d7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 24 Jan 2017 10:59:30 +0100 Subject: erts: Use os module instead of erts_internal for set_signal/2 * Add specs * Change return signature to 'ok' instead of 'true' --- lib/kernel/src/os.erl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl index f8519d3a5e..c3ffcb3f70 100644 --- a/lib/kernel/src/os.erl +++ b/lib/kernel/src/os.erl @@ -29,7 +29,7 @@ -export([getenv/0, getenv/1, getenv/2, getpid/0, perf_counter/0, perf_counter/1, - putenv/2, system_time/0, system_time/1, + putenv/2, set_signal/2, system_time/0, system_time/1, timestamp/0, unsetenv/1]). -spec getenv() -> [string()]. @@ -104,6 +104,15 @@ timestamp() -> unsetenv(_) -> erlang:nif_error(undef). +-spec set_signal(Signal, Option) -> 'ok' when + Signal :: 'sighup' | 'sigquit' | 'sigabrt' | 'sigalrm' | + 'sigterm' | 'sigusr1' | 'sigusr2' | 'sigchld' | + 'sigstop' | 'sigtstp', + Option :: 'default' | 'handle' | 'ignore'. + +set_signal(_Signal, _Option) -> + erlang:nif_error(undef). + %%% End of BIFs -spec type() -> {Osfamily, Osname} when -- cgit v1.2.3 From 1409b6ae110f60c410e83d1923dd59ae3659a887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 1 Feb 2017 16:44:50 +0100 Subject: kernel: Document signal server --- lib/kernel/doc/src/kernel_app.xml | 59 +++++++++++++++++++++++++++++++++++++-- lib/kernel/doc/src/os.xml | 31 ++++++++++++++++++-- 2 files changed, 84 insertions(+), 6 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index df681a505f..c581fa9d1e 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -11,7 +11,7 @@ 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 @@ -19,7 +19,7 @@ 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. - + kernel @@ -57,6 +57,60 @@ error_logger(3).

+
+ OS Signal Event Handler +

Asynchronous OS signals may be subscribed to via the Kernel applications event manager + (see OTP Design Principles and + gen_event(3)) registered as erl_signal_server. + A default signal handler is installed which handles the following signals:

+ + sigusr1 +

The default handler will halt Erlang and produce a crashdump + with slogan "Received SIGUSR1". + This is equivalent to calling erlang:halt("Received SIGUSR1"). +

+ + sigquit +

The default handler will halt Erlang immediately. + This is equivalent to calling erlang:halt(). +

+ + sigterm +

The default handler will terminate Erlang normally. + This is equivalent to calling init:stop(). +

+
+ +
+ Events +

Any event handler added to erl_signal_server must handle the following events.

+ + sighup +

Hangup detected on controlling terminal or death of controlling process

+ sigquit +

Quit from keyboard

+ sigabrt +

Abort signal from abort

+ sigalrm +

Timer signal from alarm

+ sigterm +

Termination signal

+ sigusr1 +

User-defined signal 1

+ sigusr2 +

User-defined signal 2

+ sigchld +

Child process stopped or terminated

+ sigstop +

Stop process

+ sigtstp +

Stop typed at terminal

+
+ +

Setting OS signals are described in os:set_signal/2.

+
+
+
Configuration

The following configuration parameters are defined for the Kernel @@ -405,4 +459,3 @@ MaxT = TickTime + TickTime / 4 timer(3)

- diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml index 739ac35d2a..6ba69d12a3 100644 --- a/lib/kernel/doc/src/os.xml +++ b/lib/kernel/doc/src/os.xml @@ -11,7 +11,7 @@ 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 @@ -19,7 +19,7 @@ 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. - + os @@ -155,6 +155,32 @@ DirOut = os:cmd("dir"), % on Win32 platform + + + Enables or disables handling of OS signals. + +

Enables or disables OS signals.

+

Each signal my be set to one of the following options:

+ + ignore + + This signal will be ignored. + + + default + + This signal will use the default signal handler for the operating system. + + + handle + + This signal will notify erl_signal_server when it is received by + the Erlang runtime system. + + +
+
+ Current OS system time. @@ -296,4 +322,3 @@ calendar:now_to_universal_time(TS), - -- cgit v1.2.3 From 5f7f86be7bc7ec7a1c9b0042ff08254dca063d76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 3 Feb 2017 13:27:09 +0100 Subject: Store messages for 'rex' and 'error_logger' off heap Performance for processes that receive huge amounts of messages can be increased by storing the incoming messages outside the heap (that avoids copying the message in a garbage collection). Two OTP processes that are known to receive many messages are 'rex' (used by 'rpc') and 'error_logger'. --- lib/kernel/src/error_logger.erl | 8 ++++++-- lib/kernel/src/rpc.erl | 14 ++++++++++++-- lib/kernel/test/error_logger_SUITE.erl | 13 ++++++++++++- lib/kernel/test/rpc_SUITE.erl | 12 ++++++++++-- 4 files changed, 40 insertions(+), 7 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index 3523f680a3..3ee8e2c6e6 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -360,8 +360,12 @@ init(Max) when is_integer(Max) -> %% go back. init({go_back, _PostState}) -> {ok, {?buffer_size, 0, []}}; -init(_) -> %% Start and just relay to other - {ok, []}. %% node if node(GLeader) =/= node(). +init(_) -> + %% The error logger process may receive a huge amount of + %% messages. Make sure that they are stored off heap to + %% avoid exessive GCs. + process_flag(message_queue_data, off_heap), + {ok, []}. -spec handle_event(term(), state()) -> {'ok', state()}. diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl index 21bff02214..bd6ea26678 100644 --- a/lib/kernel/src/rpc.erl +++ b/lib/kernel/src/rpc.erl @@ -67,17 +67,27 @@ %%------------------------------------------------------------------------ + +%% The rex server may receive a huge amount of +%% messages. Make sure that they are stored off heap to +%% avoid exessive GCs. + +-define(SPAWN_OPTS, [{spawn_opt,[{message_queue_data,off_heap}]}]). + %% Remote execution and broadcasting facility -spec start() -> {'ok', pid()} | 'ignore' | {'error', term()}. start() -> - gen_server:start({local,?NAME}, ?MODULE, [], []). + gen_server:start({local,?NAME}, ?MODULE, [], ?SPAWN_OPTS). -spec start_link() -> {'ok', pid()} | 'ignore' | {'error', term()}. start_link() -> - gen_server:start_link({local,?NAME}, ?MODULE, [], []). + %% The rex server process may receive a huge amount of + %% messages. Make sure that they are stored off heap to + %% avoid exessive GCs. + gen_server:start_link({local,?NAME}, ?MODULE, [], ?SPAWN_OPTS). -spec stop() -> term(). diff --git a/lib/kernel/test/error_logger_SUITE.erl b/lib/kernel/test/error_logger_SUITE.erl index b6e7551741..bb01c2384d 100644 --- a/lib/kernel/test/error_logger_SUITE.erl +++ b/lib/kernel/test/error_logger_SUITE.erl @@ -30,6 +30,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, + off_heap/1, error_report/1, info_report/1, error/1, info/1, emulator/1, tty/1, logfile/1, add/1, delete/1]). @@ -45,7 +46,7 @@ suite() -> {timetrap,{minutes,1}}]. all() -> - [error_report, info_report, error, info, emulator, tty, + [off_heap, error_report, info_report, error, info, emulator, tty, logfile, add, delete]. groups() -> @@ -66,6 +67,16 @@ end_per_group(_GroupName, Config) -> %%----------------------------------------------------------------- +off_heap(_Config) -> + %% The error_logger process may receive a huge amount of + %% messages. Make sure that they are stored off heap to + %% avoid exessive GCs. + MQD = message_queue_data, + {MQD,off_heap} = process_info(whereis(error_logger), MQD), + ok. + +%%----------------------------------------------------------------- + error_report(Config) when is_list(Config) -> error_logger:add_report_handler(?MODULE, self()), Rep1 = [{tag1,"data1"},{tag2,data2},{tag3,3}], diff --git a/lib/kernel/test/rpc_SUITE.erl b/lib/kernel/test/rpc_SUITE.erl index 1c72ddc87f..d76c4097d8 100644 --- a/lib/kernel/test/rpc_SUITE.erl +++ b/lib/kernel/test/rpc_SUITE.erl @@ -21,7 +21,8 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). --export([call/1, block_call/1, multicall/1, multicall_timeout/1, +-export([off_heap/1, + call/1, block_call/1, multicall/1, multicall_timeout/1, multicall_dies/1, multicall_node_dies/1, called_dies/1, called_node_dies/1, called_throws/1, call_benchmark/1, async_call/1]). @@ -35,7 +36,7 @@ suite() -> {timetrap,{minutes,2}}]. all() -> - [call, block_call, multicall, multicall_timeout, + [off_heap, call, block_call, multicall, multicall_timeout, multicall_dies, multicall_node_dies, called_dies, called_node_dies, called_throws, call_benchmark, async_call]. @@ -55,6 +56,13 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +off_heap(_Config) -> + %% The rex server process may receive a huge amount of + %% messages. Make sure that they are stored off heap to + %% avoid exessive GCs. + MQD = message_queue_data, + {MQD,off_heap} = process_info(whereis(rex), MQD), + ok. %% Test different rpc calls. -- cgit v1.2.3 From eab4b024957a27be018f8bc155bdd1dc05ec7969 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Thu, 19 Jan 2017 17:11:19 +0100 Subject: Document the kernel source_search_rules parameter --- lib/kernel/doc/src/kernel_app.xml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index df681a505f..0db3aa50c5 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -379,6 +379,28 @@ MaxT = TickTime + TickTime / 4 return as soon as possible for application_controller to terminate properly.

+ source_search_rules = [DirRule] | [SuffixRule] + + +

Where:

+ + DirRule = {ObjDirSuffix,SrcDirSuffix} + SuffixRule = {ObjSuffix,SrcSuffix,[DirRule]} + ObjDirSuffix = string() + SrcDirSuffix = string() + ObjSuffix = string() + SrcSuffix = string() + +

Specifies a list of rules for use by filelib:find_file/2 and + filelib:find_source/2. If this is set to some other value + than the empty list, it replaces the default rules. Rules can be + simple pairs of directory suffixes, such as {"ebin", + "src"}, which are used by filelib:find_file/2, or + triples specifying separate directory suffix rules depending on + file name extensions, for example [{".beam", ".erl", [{"ebin", + "src"}]}, which are used by filelib:find_source/2. Both + kinds of rules can be mixed in the list.

+
-- cgit v1.2.3 From 5a97997217e5c3f901e8fefbd7bbf6c64652c9a8 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 23 Jan 2017 17:07:23 +0100 Subject: Use magic refs for code loading state --- lib/kernel/src/code.erl | 12 ++++++------ lib/kernel/test/multi_load_SUITE.erl | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index 5a7ca493cc..2a06d0cb15 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -489,13 +489,13 @@ prepare_check_uniq_1([], [_|_]=Errors) -> {error,Errors}. partition_on_load(Prep) -> - P = fun({_,{Bin,_,_}}) -> - erlang:has_prepared_code_on_load(Bin) + P = fun({_,{PC,_,_}}) -> + erlang:has_prepared_code_on_load(PC) end, lists:partition(P, Prep). verify_prepared([{M,{Prep,Name,_Native}}|T]) - when is_atom(M), is_binary(Prep), is_list(Name) -> + when is_atom(M), is_list(Name) -> try erlang:has_prepared_code_on_load(Prep) of false -> verify_prepared(T); @@ -562,10 +562,10 @@ prepare_loading_fun() -> GetNative = get_native_fun(), fun(Mod, FullName, Beam) -> case erlang:prepare_loading(Mod, Beam) of - Prepared when is_binary(Prepared) -> - {ok,{Prepared,FullName,GetNative(Beam)}}; {error,_}=Error -> - Error + Error; + Prepared -> + {ok,{Prepared,FullName,GetNative(Beam)}} end end. diff --git a/lib/kernel/test/multi_load_SUITE.erl b/lib/kernel/test/multi_load_SUITE.erl index 369e25ac64..920839f4f9 100644 --- a/lib/kernel/test/multi_load_SUITE.erl +++ b/lib/kernel/test/multi_load_SUITE.erl @@ -144,14 +144,14 @@ prep_magic([H|T]) -> prep_magic(Tuple) when is_tuple(Tuple) -> L = prep_magic(tuple_to_list(Tuple)), list_to_tuple(L); -prep_magic(Bin) when is_binary(Bin) -> - try erlang:has_prepared_code_on_load(Bin) of +prep_magic(Ref) when is_reference(Ref) -> + try erlang:has_prepared_code_on_load(Ref) of false -> - %% Create a different kind of magic binary. + %% Create a different kind of magic ref. ets:match_spec_compile([{'_',[true],['$_']}]) catch _:_ -> - Bin + Ref end; prep_magic(Other) -> Other. -- cgit v1.2.3 From ace8b57ec6be7292f7b6c27fa8eabeb8fe1c716b Mon Sep 17 00:00:00 2001 From: Juraj Hlista Date: Sat, 11 Feb 2017 16:41:07 +0000 Subject: Fix function name From camel case to snake case. --- lib/kernel/src/dist_ac.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/dist_ac.erl b/lib/kernel/src/dist_ac.erl index 6c2fa0b6b1..e63c969b79 100644 --- a/lib/kernel/src/dist_ac.erl +++ b/lib/kernel/src/dist_ac.erl @@ -123,7 +123,7 @@ load_application(AppName, DistNodes) -> gen_server:call(?DIST_AC, {load_application, AppName, DistNodes}, infinity). takeover_application(AppName, RestartType) -> - case validRestartType(RestartType) of + case valid_restart_type(RestartType) of true -> wait_for_sync_dacs(), Nodes = get_nodes(AppName), @@ -1514,10 +1514,10 @@ dist_del_node(Appls, Node) -> Appl#appl{run = NRun} end, Appls). -validRestartType(permanent) -> true; -validRestartType(temporary) -> true; -validRestartType(transient) -> true; -validRestartType(_RestartType) -> false. +valid_restart_type(permanent) -> true; +valid_restart_type(temporary) -> true; +valid_restart_type(transient) -> true; +valid_restart_type(_RestartType) -> false. dist_mismatch(AppName, Node) -> error_msg("Distribution mismatch for application \"~p\" on nodes ~p and ~p~n", -- cgit v1.2.3 From 517db4752997cf19e18c65302d21eed1f1eb6e1b Mon Sep 17 00:00:00 2001 From: Andrew Dryga Date: Sun, 12 Feb 2017 19:45:39 +0200 Subject: Fixed typos in lib/kernel --- lib/kernel/doc/src/notes.xml | 4 ++-- lib/kernel/include/inet.hrl | 2 +- lib/kernel/src/inet_parse.erl | 4 ++-- lib/kernel/src/inet_udp.erl | 6 +++--- lib/kernel/test/application_SUITE.erl | 2 +- lib/kernel/test/erl_distribution_SUITE.erl | 4 ++-- lib/kernel/test/erl_distribution_wb_SUITE.erl | 2 +- lib/kernel/test/file_SUITE.erl | 2 +- lib/kernel/test/file_SUITE_data/realmen.html | 4 ++-- 9 files changed, 15 insertions(+), 15 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 9277c2d353..d80c4f077c 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -108,7 +108,7 @@

Close stdin of commands run in os:cmd. This is a - backwards compatiblity fix that restores the behaviour of + backwards compatibility fix that restores the behaviour of pre 19.0 os:cmd.

Own Id: OTP-13867 Aux Id: seq13178

@@ -1445,7 +1445,7 @@ dependent, so applications aiming to be portable should consider using {ipv6_v6only,true} when creating an inet6 listening/destination socket, and if - neccesary also create an inet socket on the same + necessary also create an inet socket on the same port for IPv4 traffic. See the documentation.

Own Id: OTP-8928 Aux Id: kunagi-193 [104]

diff --git a/lib/kernel/include/inet.hrl b/lib/kernel/include/inet.hrl index b39df8c3f2..df788aca08 100644 --- a/lib/kernel/include/inet.hrl +++ b/lib/kernel/include/inet.hrl @@ -22,7 +22,7 @@ -record(hostent, { - h_name :: inet:hostname(), %% offical name of host + h_name :: inet:hostname(), %% official name of host h_aliases = [] :: [inet:hostname()], %% alias list h_addrtype :: 'inet' | 'inet6', %% host address type h_length :: non_neg_integer(), %% length of address diff --git a/lib/kernel/src/inet_parse.erl b/lib/kernel/src/inet_parse.erl index b0a3ee3008..9b47199e08 100644 --- a/lib/kernel/src/inet_parse.erl +++ b/lib/kernel/src/inet_parse.erl @@ -701,8 +701,8 @@ dup(N, E, L) when is_integer(N), N >= 1 -> -%% Convert IPv4 adress to ascii -%% Convert IPv6 / IPV4 adress to ascii (plain format) +%% Convert IPv4 address to ascii +%% Convert IPv6 / IPV4 address to ascii (plain format) ntoa({A,B,C,D}) -> integer_to_list(A) ++ "." ++ integer_to_list(B) ++ "." ++ integer_to_list(C) ++ "." ++ integer_to_list(D); diff --git a/lib/kernel/src/inet_udp.erl b/lib/kernel/src/inet_udp.erl index 8a8aa8ecca..c69791b9aa 100644 --- a/lib/kernel/src/inet_udp.erl +++ b/lib/kernel/src/inet_udp.erl @@ -113,7 +113,7 @@ fdopen(Fd, Opts) -> %% Here's how: %% Reverse the list. %% For each head option go through the tail and remove -%% all occurences of the same option from the tail. +%% all occurrences of the same option from the tail. %% Store that head option and iterate using the new tail. %% Return the list of stored head options. optuniquify(List) -> @@ -122,8 +122,8 @@ optuniquify(List) -> optuniquify([], Result) -> Result; optuniquify([Opt | Tail], Result) -> - %% Remove all occurences of Opt in Tail, - %% prepend Opt to Result, + %% Remove all occurrences of Opt in Tail, + %% prepend Opt to Result, %% then iterate back here. optuniquify(Opt, Tail, [], Result). diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl index 81407e9d96..b4cf31b210 100644 --- a/lib/kernel/test/application_SUITE.erl +++ b/lib/kernel/test/application_SUITE.erl @@ -1498,7 +1498,7 @@ otp_5363(Conf) when is_list(Conf) -> %% Ticket: OTP-5606 %% Slogan: Problems with starting a distributed application %%----------------------------------------------------------------- -%% Test of several processes simultanously starting the same +%% Test of several processes simultaneously starting the same %% distributed application. otp_5606(Conf) when is_list(Conf) -> diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl index f630896e03..09c80a0956 100644 --- a/lib/kernel/test/erl_distribution_SUITE.erl +++ b/lib/kernel/test/erl_distribution_SUITE.erl @@ -233,7 +233,7 @@ time_ping(Node) -> erlang:convert_time_unit(T1 - T0, native, millisecond). %% Keep the connection with the client node up. -%% This is neccessary as the client node runs with much shorter +%% This is necessary as the client node runs with much shorter %% tick time !! keep_conn(Node) -> sleep(1), @@ -1059,7 +1059,7 @@ monitor_nodes_otp_6481_test(Config, TestType) when is_list(Config) -> RemotePid = spawn(Node, fun () -> receive after 1500 -> ok end, - %% infinit loop of msgs + %% infinite loop of msgs %% we want an endless stream of messages and the kill %% the node mercilessly. %% We then want to ensure that the nodedown message arrives diff --git a/lib/kernel/test/erl_distribution_wb_SUITE.erl b/lib/kernel/test/erl_distribution_wb_SUITE.erl index 6a23ad0d11..61aa3b32ee 100644 --- a/lib/kernel/test/erl_distribution_wb_SUITE.erl +++ b/lib/kernel/test/erl_distribution_wb_SUITE.erl @@ -30,7 +30,7 @@ %% 1) %% -%% Connections are now always set up symetrically with respect to +%% Connections are now always set up symmetrically with respect to %% publication. If connecting node doesn't send DFLAG_PUBLISHED %% the other node wont send DFLAG_PUBLISHED. If the connecting %% node send DFLAG_PUBLISHED but the other node doesn't send diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index f2094431d8..b402f01758 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -18,7 +18,7 @@ %% %CopyrightEnd% %% -%% This is a developement feature when developing a new file module, +%% This is a development feature when developing a new file module, %% ugly but practical. -ifndef(FILE_MODULE). -define(FILE_MODULE, file). diff --git a/lib/kernel/test/file_SUITE_data/realmen.html b/lib/kernel/test/file_SUITE_data/realmen.html index c810a5d088..92e13f23b8 100644 --- a/lib/kernel/test/file_SUITE_data/realmen.html +++ b/lib/kernel/test/file_SUITE_data/realmen.html @@ -237,7 +237,7 @@ destroy most of the interesting uses for EQUIVALENCE, and make it impossible to modify the operating system code with negative subscripts. Worst of all, bounds checking is inefficient. -
  • Source code maintainance systems. A Real Programmer keeps his +
  • Source code maintenance systems. A Real Programmer keeps his code locked up in a card file, because it implies that its owner cannot leave his important programs unguarded [5]. @@ -396,7 +396,7 @@ double stuff Oreos for special occasions.
  • Underneath the Oreos is a flow-charting template, left there by the previous occupant of the office. (Real Programmers write programs, -not documentation. Leave that to the maintainence people.) +not documentation. Leave that to the maintenance people.)

    -- cgit v1.2.3 From 44ab65763bbeb307a3431de284805325114a3d35 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 20 Feb 2017 10:31:42 +0100 Subject: kernel: Fail sticky_dir tc if module not sticky Running this test when for some reason stdlib has been unstickied could cause the emulator to die. So we check first to make sure that the expected files are sticky. --- lib/kernel/test/code_SUITE.erl | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index c5167efa56..5777b397b8 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -588,20 +588,28 @@ sticky_compiler(Files, PrivDir) -> [R || R <- Rets, R =/= ok]. do_sticky_compile(Mod, Dir) -> - %% Make sure that the module is loaded. A module being sticky - %% only prevents it from begin reloaded, not from being loaded - %% from the wrong place to begin with. - Mod = Mod:module_info(module), - File = filename:append(Dir, atom_to_list(Mod)), - Src = io_lib:format("-module(~s).\n" - "-export([test/1]).\n" - "test(me) -> fail.\n", [Mod]), - ok = file:write_file(File++".erl", Src), - case c:c(File, [{outdir,Dir}]) of - {ok,Module} -> - Module:test(me); - {error,sticky_directory} -> - ok + case code:is_sticky(Mod) of + true -> + %% Make sure that the module is loaded. A module being sticky + %% only prevents it from begin reloaded, not from being loaded + %% from the wrong place to begin with. + Mod = Mod:module_info(module), + File = filename:append(Dir, atom_to_list(Mod)), + Src = io_lib:format("-module(~s).\n" + "-export([test/1]).\n" + "test(me) -> fail.\n", [Mod]), + ok = file:write_file(File++".erl", Src), + case c:c(File, [{outdir,Dir}]) of + {ok,Module} -> + Module:test(me); + {error,sticky_directory} -> + ok + end; + false -> + %% For some reason the module is not sticky + %% could be that the .erlang file has + %% unstuck it? + {Mod, is_not_sticky} end. %% Test that the -pa and -pz options work as expected. -- cgit v1.2.3 From bf9f79e81530b37d5ac4abe1494f270986d92cb4 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 21 Feb 2017 10:34:13 +0100 Subject: kernel: Fix hanging os:cmd race condition If the port terminates before Port ! close is issued, there will be no 'closed' reply, so the old code would hang. As it turns out there is no way to figure out if a closed reply is supposed to come as that reply is sent after all links and monitors are triggered. So we have to use the synchronous port_close to close the port. --- lib/kernel/src/os.erl | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl index f8519d3a5e..03d4324992 100644 --- a/lib/kernel/src/os.erl +++ b/lib/kernel/src/os.erl @@ -289,12 +289,11 @@ get_data(Port, MonRef, Eot, Sofar) -> more -> get_data(Port, MonRef, Eot, [Sofar,Bytes]); Last -> - Port ! {self(), close}, - flush_until_closed(Port), - flush_exit(Port), + catch port_close(Port), + flush_until_down(Port, MonRef), iolist_to_binary([Sofar, Last]) end; - {'DOWN', MonRef, _, _ , _} -> + {'DOWN', MonRef, _, _, _} -> flush_exit(Port), iolist_to_binary(Sofar) end. @@ -308,18 +307,25 @@ eot(Bs, Eot) -> binary:part(Bs,{0, Pos}) end. -flush_until_closed(Port) -> +%% When port_close returns we know that all the +%% messages sent have been sent and that the +%% DOWN message is after them all. +flush_until_down(Port, MonRef) -> receive {Port, {data, _Bytes}} -> - flush_until_closed(Port); - {Port, closed} -> - true + flush_until_down(Port, MonRef); + {'DOWN', MonRef, _, _, _} -> + flush_exit(Port) end. +%% The exit signal is always delivered before +%% the down signal, so we can be sure that if there +%% was an exit message sent, it will be in the +%% mailbox now. flush_exit(Port) -> receive {'EXIT', Port, _} -> ok - after 1 -> % force context switch + after 0 -> ok end. -- cgit v1.2.3 From 310e7fd72c241929fca010ae3f39c967aa2856c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 1 Mar 2017 10:35:01 +0100 Subject: Ensure code_SUITE:on_load_embedded/1 does not leak links Make sure that the symlink created in the lib directory is deleted even if the test case fails. --- lib/kernel/test/code_SUITE.erl | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 1f4591a5a3..b665d7e592 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -107,6 +107,10 @@ init_per_testcase(big_boot_embedded, Config) -> _Else -> {skip, "Needs crypto!"} end; +init_per_testcase(on_load_embedded, Config) -> + LibRoot = code:lib_dir(), + LinkName = filename:join(LibRoot, "on_load_app-1.0"), + [{link_name,LinkName}|Config]; init_per_testcase(_Func, Config) -> P = code:get_path(), [{code_path, P}|Config]. @@ -124,6 +128,10 @@ end_per_testcase(TC, Config) when TC == mult_lib_roots; NodeName = list_to_atom(atom_to_list(TC)++"@"++HostName), test_server:stop_node(NodeName), end_per_testcase(Config); +end_per_testcase(on_load_embedded, Config) -> + LinkName = proplists:get_value(link_name, Config), + _ = del_link(LinkName), + end_per_testcase(Config); end_per_testcase(_Func, Config) -> end_per_testcase(Config). @@ -1271,10 +1279,9 @@ on_load_embedded(Config) when is_list(Config) -> on_load_embedded_1(Config) -> DataDir = proplists:get_value(data_dir, Config), + LinkName = proplists:get_value(link_name, Config), %% Link the on_load_app application into the lib directory. - LibRoot = code:lib_dir(), - LinkName = filename:join(LibRoot, "on_load_app-1.0"), OnLoadApp = filename:join(DataDir, "on_load_app-1.0"), del_link(LinkName), io:format("LinkName :~p, OnLoadApp: ~p~n",[LinkName,OnLoadApp]), @@ -1308,8 +1315,7 @@ on_load_embedded_1(Config) -> ok = rpc:call(Node, on_load_embedded, status, []), %% Clean up. - stop_node(Node), - ok = del_link(LinkName). + stop_node(Node). del_link(LinkName) -> case file:delete(LinkName) of -- cgit v1.2.3 From a0381d6ee2164bf8ae03d5b9fb01f0dfaf5b394f Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 8 Mar 2017 12:35:20 +0100 Subject: Update appups in kernel and stdlib for OTP-19.3 --- lib/kernel/src/kernel.appup.src | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src index 82cf73cbda..15e9e7c462 100644 --- a/lib/kernel/src/kernel.appup.src +++ b/lib/kernel/src/kernel.appup.src @@ -18,9 +18,9 @@ %% %CopyrightEnd% {"%VSN%", %% Up from - max one major revision back - [{<<"5\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.* + [{<<"5\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.* {<<"4\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-18.* %% Down to - max one major revision back - [{<<"5\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.* + [{<<"5\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.* {<<"4\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-18.* }. -- cgit v1.2.3 From d37a0d105f5332d4f4b23642eb311012a5fe3a64 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Mon, 13 Feb 2017 15:51:30 +0100 Subject: kernel: Fix handling of locations and annotations --- lib/kernel/src/application_controller.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/application_controller.erl b/lib/kernel/src/application_controller.erl index 0e61153613..3b642f5873 100644 --- a/lib/kernel/src/application_controller.erl +++ b/lib/kernel/src/application_controller.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. @@ -1620,7 +1620,7 @@ conv(_) -> []. make_term(Str) -> case erl_scan:string(Str) of {ok, Tokens, _} -> - case erl_parse:parse_term(Tokens ++ [{dot, 1}]) of + case erl_parse:parse_term(Tokens ++ [{dot, erl_anno:new(1)}]) of {ok, Term} -> Term; {error, {_,M,Reason}} -> -- cgit v1.2.3 From 6a4d4b43914a5898671e7f9dea7988771450af0f Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 2 Mar 2017 17:36:48 +0100 Subject: Remove typer application The application now has an own repo, https://github.com/erlang/typer --- lib/kernel/test/code_SUITE.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 1f4591a5a3..19d36a7613 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -1360,9 +1360,8 @@ create_big_boot(Config) -> %% corresponding beam file (if hipe is not enabled). filter_app("hipe",_) -> false; -%% Dialyzer and typer depends on hipe +%% Dialyzer depends on hipe filter_app("dialyzer",_) -> false; -filter_app("typer",_) -> false; %% Orber requires explicit configuration filter_app("orber",_) -> false; -- cgit v1.2.3 From 26c3cd82529836cb5b6eefbf7f92f318fd91f847 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 10 Mar 2017 15:00:46 +0100 Subject: Update copyright year --- lib/kernel/src/error_logger.erl | 2 +- lib/kernel/src/kernel.appup.src | 2 +- lib/kernel/src/os.erl | 2 +- lib/kernel/src/rpc.erl | 2 +- lib/kernel/test/code_SUITE.erl | 2 +- lib/kernel/test/error_logger_SUITE.erl | 2 +- lib/kernel/test/rpc_SUITE.erl | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index 3ee8e2c6e6..81f1bf8d97 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src index 15e9e7c462..e1173cd2f4 100644 --- a/lib/kernel/src/kernel.appup.src +++ b/lib/kernel/src/kernel.appup.src @@ -1,7 +1,7 @@ %% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-2017. 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. diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl index 03d4324992..a90a6dcca7 100644 --- a/lib/kernel/src/os.erl +++ b/lib/kernel/src/os.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl index bd6ea26678..0e0b7dffa3 100644 --- a/lib/kernel/src/rpc.erl +++ b/lib/kernel/src/rpc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 5777b397b8..62ad7b6a27 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. diff --git a/lib/kernel/test/error_logger_SUITE.erl b/lib/kernel/test/error_logger_SUITE.erl index bb01c2384d..2d26a7246c 100644 --- a/lib/kernel/test/error_logger_SUITE.erl +++ b/lib/kernel/test/error_logger_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. diff --git a/lib/kernel/test/rpc_SUITE.erl b/lib/kernel/test/rpc_SUITE.erl index d76c4097d8..a89a7600a2 100644 --- a/lib/kernel/test/rpc_SUITE.erl +++ b/lib/kernel/test/rpc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2016. All Rights Reserved. +%% Copyright Ericsson AB 2000-2017. 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. -- cgit v1.2.3 From 5d9bb41114544c9205a8b8f26642bad8231e8d4e Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 10 Mar 2017 15:42:13 +0100 Subject: kernel: Try mend disk_log whitebox tests after atom encoding got 1 byte smaller when changed from <<$d, Len:16, Bytes/binary>> to <<$w, Len:8, Bytes/binary>> --- lib/kernel/test/disk_log_SUITE.erl | 50 +++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 25 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/disk_log_SUITE.erl b/lib/kernel/test/disk_log_SUITE.erl index 23fe975ef7..079cc2f90f 100644 --- a/lib/kernel/test/disk_log_SUITE.erl +++ b/lib/kernel/test/disk_log_SUITE.erl @@ -481,7 +481,7 @@ halt_ro_crash(Conf) when is_list(Conf) -> %% This is how it was before R6B: %% {C1,T1,15} = disk_log:chunk(a,start), %% {C2,T2} = disk_log:chunk(a,C1), - {C1,_OneItem,7478} = disk_log:chunk(a,start), + {C1,_OneItem,7476} = disk_log:chunk(a,start), {C2, [], 7} = disk_log:chunk(a,C1), eof = disk_log:chunk(a,C2), ok = disk_log:close(a), @@ -2502,8 +2502,8 @@ error_repair(Conf) when is_list(Conf) -> ok = disk_log:close(n), BadFile = add_ext(File, 2), % current file set_opened(BadFile), - crash(BadFile, 28), % the binary is now invalid - {repaired,n,{recovered,0},{badbytes,26}} = + crash(BadFile, 26), % the binary is now invalid + {repaired,n,{recovered,0},{badbytes,24}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, internal}, {size, {40,No}}]), ok = disk_log:close(n), @@ -2518,8 +2518,8 @@ error_repair(Conf) when is_list(Conf) -> ok = disk_log:close(n), BadFile2 = add_ext(File, 1), % current file set_opened(BadFile2), - crash(BadFile2, 51), % the second binary is now invalid - {repaired,n,{recovered,1},{badbytes,26}} = + crash(BadFile2, 47), % the second binary is now invalid + {repaired,n,{recovered,1},{badbytes,24}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, internal}, {size, {4000,No}}]), ok = disk_log:close(n), @@ -2571,7 +2571,7 @@ error_repair(Conf) when is_list(Conf) -> ok = disk_log:close(n), set_opened(File), crash(File, 30), - {repaired,n,{recovered,3},{badbytes,16}} = + {repaired,n,{recovered,3},{badbytes,15}} = disk_log:open([{name, n}, {file, File}, {type, halt}, {format, internal},{repair,true}, {head_func, {?MODULE, head_fun, [{ok,"head"}]}}]), @@ -2797,7 +2797,7 @@ chunk(Conf) when is_list(Conf) -> ok = disk_log:log_terms(n, [{some,terms}]), % second file full 2 = curf(n), BadFile = add_ext(File, 1), - crash(BadFile, 28), % the _binary_ is now invalid + crash(BadFile, 26), % the _binary_ is now invalid {error, {corrupt_log_file, BFile}} = disk_log:chunk(n, start, 1), BadFile = BFile, ok = disk_log:close(n), @@ -2807,7 +2807,7 @@ chunk(Conf) when is_list(Conf) -> {format, internal}]), ok = disk_log:log_terms(n, [{this,is}]), ok = disk_log:sync(n), - crash(File, 28), % the _binary_ is now invalid + crash(File, 26), % the _binary_ is now invalid {error, {corrupt_log_file, File2}} = disk_log:chunk(n, start, 1), crash(File, 10), {error,{corrupt_log_file,_}} = disk_log:bchunk(n, start, 1), @@ -2901,8 +2901,8 @@ chunk(Conf) when is_list(Conf) -> {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, internal}, {mode, read_only}]), CrashFile = add_ext(File, 1), - crash(CrashFile, 51), % the binary term {some,terms} is now bad - {H1, [{this,is}], 18} = disk_log:chunk(n, start, 10), + crash(CrashFile, 46), % the binary term {some,terms} is now bad + {H1, [{this,is}], 16} = disk_log:chunk(n, start, 10), {H2, [{on,a},{wrap,file}]} = disk_log:chunk(n, H1), eof = disk_log:chunk(n, H2), ok = disk_log:close(n), @@ -2916,8 +2916,8 @@ chunk(Conf) when is_list(Conf) -> ok = disk_log:close(n), {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, {format, internal}, {mode, read_only}]), - crash(File, 51), % the binary term {some,terms} is now bad - {J1, [{this,is}], 18} = disk_log:chunk(n, start, 10), + crash(File, 46), % the binary term {some,terms} is now bad + {J1, [{this,is}], 16} = disk_log:chunk(n, start, 10), {J2, [{on,a},{halt,file}]} = disk_log:chunk(n, J1), eof = disk_log:chunk(n, J2), ok = disk_log:close(n), @@ -2932,8 +2932,8 @@ chunk(Conf) when is_list(Conf) -> ok = disk_log:close(n), {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, {format, internal}, {mode, read_only}]), - crash(File, 44), % the binary term {s} is now bad - {J11, [{this,is}], 7} = disk_log:chunk(n, start, 10), + crash(File, 40), % the binary term {s} is now bad + {J11, [{this,is}], 6} = disk_log:chunk(n, start, 10), {J21, [{on,a},{halt,file}]} = disk_log:chunk(n, J11), eof = disk_log:chunk(n, J21), ok = disk_log:close(n), @@ -3052,7 +3052,7 @@ truncate(Conf) when is_list(Conf) -> ok = disk_log:truncate(n, apa), rec(1, {disk_log, node(), n, {truncated, 6}}), {0, 0} = no_overflows(n), - 23 = curb(n), + 22 = curb(n), 1 = curf(n), 1 = cur_cnt(n), true = (Size == sz(n)), @@ -3072,7 +3072,7 @@ truncate(Conf) when is_list(Conf) -> ok = disk_log:truncate(n, apa), rec(1, {disk_log, node(), n, {truncated, 3}}), {0, 0} = no_overflows(n), - 23 = curb(n), + 22 = curb(n), 1 = curf(n), 1 = cur_cnt(n), true = (Size == sz(n)), @@ -3181,45 +3181,45 @@ info_current(Conf) when is_list(Conf) -> %% Internal with header. {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {head, header}, {size, {100,No}}]), - {26, 1} = {curb(n), cur_cnt(n)}, + {25, 1} = {curb(n), cur_cnt(n)}, {1, 1} = {no_written_items(n), no_items(n)}, ok = disk_log:log(n, B), - {94, 2} = {curb(n), cur_cnt(n)}, + {93, 2} = {curb(n), cur_cnt(n)}, {2, 2} = {no_written_items(n), no_items(n)}, ok = disk_log:close(n), {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {notify, true}, {head, header}, {size, {100,No}}]), - {94, 2} = {curb(n), cur_cnt(n)}, + {93, 2} = {curb(n), cur_cnt(n)}, {0, 2} = {no_written_items(n), no_items(n)}, ok = disk_log:log(n, B), rec(1, {disk_log, node(), n, {wrap, 0}}), - {94, 2} = {curb(n), cur_cnt(n)}, + {93, 2} = {curb(n), cur_cnt(n)}, {2, 4} = {no_written_items(n), no_items(n)}, disk_log:inc_wrap_file(n), rec(1, {disk_log, node(), n, {wrap, 0}}), - {26, 1} = {curb(n), cur_cnt(n)}, + {25, 1} = {curb(n), cur_cnt(n)}, {3, 4} = {no_written_items(n), no_items(n)}, ok = disk_log:log_terms(n, [B,B,B]), %% Used to be one message, but now one per wrapped file. rec(1, {disk_log, node(), n, {wrap, 0}}), rec(1, {disk_log, node(), n, {wrap, 2}}), - {94, 2} = {curb(n), cur_cnt(n)}, + {93, 2} = {curb(n), cur_cnt(n)}, {8, 7} = {no_written_items(n), no_items(n)}, ok = disk_log:log_terms(n, [B]), rec(1, {disk_log, node(), n, {wrap, 2}}), ok = disk_log:log_terms(n, [B]), rec(1, {disk_log, node(), n, {wrap, 2}}), - {94, 2} = {curb(n), cur_cnt(n)}, + {93, 2} = {curb(n), cur_cnt(n)}, {12, 7} = {no_written_items(n), no_items(n)}, ok = disk_log:log_terms(n, [BB,BB]), %% Used to be one message, but now one per wrapped file. rec(2, {disk_log, node(), n, {wrap, 2}}), - {194, 2} = {curb(n), cur_cnt(n)}, + {193, 2} = {curb(n), cur_cnt(n)}, {16, 7} = {no_written_items(n), no_items(n)}, ok = disk_log:log_terms(n, [SB,SB,SB]), rec(1, {disk_log, node(), n, {wrap, 2}}), - {80, 4} = {curb(n), cur_cnt(n)}, + {79, 4} = {curb(n), cur_cnt(n)}, {20, 9} = {no_written_items(n), no_items(n)}, ok = disk_log:close(n), del(File, No), -- cgit v1.2.3 From af8bc77e32af655f57c82409939ad674e22f3ccf Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 13 Mar 2017 18:20:16 +0100 Subject: kernel: Rewrite distribution flag verification --- lib/kernel/src/dist_util.erl | 48 ++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 26 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index 8d2fc4d4b7..a2cebfc2c6 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -131,7 +131,7 @@ handshake_other_started(#hs_data{request_type=ReqType}=HSData0) -> other_version=Version, other_node=Node, other_started=true}, - check_dflag_xnc(HSData), + check_dflags(HSData), is_allowed(HSData), ?debug({"MD5 connection from ~p (V~p)~n", [Node, HSData#hs_data.other_version]}), @@ -168,27 +168,23 @@ is_allowed(#hs_data{other_node = Node, %% Check that both nodes can handle the same types of extended %% node containers. If they can not, abort the connection. %% -check_dflag_xnc(#hs_data{other_node = Node, - other_flags = OtherFlags, - other_started = OtherStarted} = HSData) -> - XRFlg = ?DFLAG_EXTENDED_REFERENCES, - XPPFlg = case erlang:system_info(compat_rel) of - R when R >= 10 -> - ?DFLAG_EXTENDED_PIDS_PORTS; - _ -> - 0 - end, - ReqXncFlags = XRFlg bor XPPFlg, - case OtherFlags band ReqXncFlags =:= ReqXncFlags of - true -> - ok; - false -> - What = case {OtherFlags band XRFlg =:= XRFlg, - OtherFlags band XPPFlg =:= XPPFlg} of - {false, false} -> "references, pids and ports"; - {true, false} -> "pids and ports"; - {false, true} -> "references" - end, +check_dflags(#hs_data{other_node = Node, + other_flags = OtherFlags, + other_started = OtherStarted} = HSData) -> + + Mandatory = [{?DFLAG_EXTENDED_REFERENCES, "EXTENDED_REFERENCES"}, + {?DFLAG_EXTENDED_PIDS_PORTS, "EXTENDED_PIDS_PORTS"}], + Missing = lists:filtermap(fun({Bit, Str}) -> + case Bit band OtherFlags of + Bit -> false; + 0 -> {true, Str} + end + end, + Mandatory), + case Missing of + [] -> + ok; + _ -> case OtherStarted of true -> send_status(HSData, not_allowed), @@ -199,9 +195,9 @@ check_dflag_xnc(#hs_data{other_node = Node, How = "aborted" end, error_msg("** ~w: Connection attempt ~s node ~w ~s " - "since it cannot handle extended ~s. " - "**~n", [node(), Dir, Node, How, What]), - ?shutdown2(Node, {check_dflag_xnc_failed, What}) + "since it cannot handle ~p." + "**~n", [node(), Dir, Node, How, Missing]), + ?shutdown2(Node, {check_dflags_failed, Missing}) end. @@ -327,7 +323,7 @@ handshake_we_started(#hs_data{request_type=ReqType, NewHSData = HSData#hs_data{this_flags = ThisFlags, other_flags = OtherFlags, other_started = false}, - check_dflag_xnc(NewHSData), + check_dflags(NewHSData), MyChallenge = gen_challenge(), {MyCookie,HisCookie} = get_cookies(Node), send_challenge_reply(NewHSData,MyChallenge, -- cgit v1.2.3 From 6c236a63e0ac8ce5851f853d11e4f1374146c6c8 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 13 Mar 2017 18:20:43 +0100 Subject: kernel: Make DFLAG_UTF8_ATOMS mandatory --- lib/kernel/src/dist_util.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index a2cebfc2c6..d929179715 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -173,7 +173,8 @@ check_dflags(#hs_data{other_node = Node, other_started = OtherStarted} = HSData) -> Mandatory = [{?DFLAG_EXTENDED_REFERENCES, "EXTENDED_REFERENCES"}, - {?DFLAG_EXTENDED_PIDS_PORTS, "EXTENDED_PIDS_PORTS"}], + {?DFLAG_EXTENDED_PIDS_PORTS, "EXTENDED_PIDS_PORTS"}, + {?DFLAG_UTF8_ATOMS, "UTF8_ATOMS"}], Missing = lists:filtermap(fun({Bit, Str}) -> case Bit band OtherFlags of Bit -> false; -- cgit v1.2.3 From 4d658008be5a08ddadbe75ebadb9ef124436b76e Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Tue, 14 Mar 2017 15:59:23 +0100 Subject: Prepare release --- lib/kernel/doc/src/notes.xml | 39 +++++++++++++++++++++++++++++++++++++++ lib/kernel/vsn.mk | 2 +- 2 files changed, 40 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 9277c2d353..f5a0e68805 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,45 @@

    This document describes the changes made to the Kernel application.

    +
    Kernel 5.2 + +
    Fixed Bugs and Malfunctions + + +

    + Fix a race during cleanup of os:cmd that would cause + os:cmd to hang indefinitely.

    +

    + Own Id: OTP-14232 Aux Id: seq13275

    +
    +
    +
    + + +
    Improvements and New Features + + +

    The functions in the 'file' module that take a + list of paths (e.g. file:path_consult/2) will now + continue to search in the path if the path contains + something that is not a directory.

    +

    + Own Id: OTP-14191

    +
    + +

    Two OTP processes that are known to receive many + messages are 'rex' (used by 'rpc') and 'error_logger'. + Those processes will now store unprocessed messages + outside the process heap, which will potentially decrease + the cost of garbage collections.

    +

    + Own Id: OTP-14192

    +
    +
    +
    + +
    +
    Kernel 5.1.1
    Fixed Bugs and Malfunctions diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 8d2517e680..76b020e8ed 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 5.1.1 +KERNEL_VSN = 5.2 -- cgit v1.2.3 From 0ef9dd12e5e77d67244dca580c1a706878c272bf Mon Sep 17 00:00:00 2001 From: vans163 Date: Tue, 31 Jan 2017 14:07:27 -0500 Subject: Update inet.xml. Note buffer relation to MTU The documentation does not indicate this is the buffer responsible for the maximum bytes we can receive from a recv call. --- lib/kernel/doc/src/inet.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index 4c4a5c39cb..d557efb6a8 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -659,7 +659,8 @@ get_tcpi_sacked(Sock) -> {buffer, Size}

    The size of the user-level software buffer used by - the driver. Not to be confused with options sndbuf + the driver. + Not to be confused with options sndbuf and recbuf, which correspond to the Kernel socket buffers. It is recommended to have val(buffer) >= max(val(sndbuf),val(recbuf)) to @@ -670,6 +671,9 @@ get_tcpi_sacked(Sock) -> usually become larger, you are encouraged to use getopts/2 to analyze the behavior of your operating system.

    +

    Note that this is also the maximum amount of data that can be + received from a single recv call. If you are using higher than + normal MTU consider setting buffer higher.

    {delay_send, Boolean} -- cgit v1.2.3 From 0b821683cb11d78945bbf9cef65ffeb9dc2e357e Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 20 Mar 2017 18:59:32 +0100 Subject: kernel: Remove pg2_SUITE:compat as it tests removed compatibility with R13 node. --- lib/kernel/test/pg2_SUITE.erl | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/pg2_SUITE.erl b/lib/kernel/test/pg2_SUITE.erl index fdc268cb5a..9460608a3e 100644 --- a/lib/kernel/test/pg2_SUITE.erl +++ b/lib/kernel/test/pg2_SUITE.erl @@ -31,7 +31,7 @@ -export([ otp_7277/1, otp_8259/1, otp_8653/1, - compat/1, basic/1]). + basic/1]). -define(TESTCASE, testcase_name). -define(testcase, proplists:get_value(?TESTCASE, Config)). @@ -56,7 +56,7 @@ all() -> groups() -> [{tickets, [], - [otp_7277, otp_8259, otp_8653, compat, basic]}]. + [otp_7277, otp_8259, otp_8653, basic]}]. init_per_suite(Config) -> Config. @@ -218,29 +218,6 @@ loop() -> exit(normal) end. -%% OTP-8259. Check that 'exchange' and 'del_member' work. -compat(Config) when is_list(Config) -> - case test_server:is_release_available("r13b") of - true -> - Pid = spawn(forever()), - G = a, - ok = pg2:create(G), - ok = pg2:join(G, Pid), - ok = pg2:join(G, Pid), - {ok, A} = start_node_rel(r13, r13b, slave), - pong = net_adm:ping(A), - wait_for_ready_net(Config), - {ok, _} = rpc:call(A, pg2, start, []), - ?UNTIL([Pid,Pid] =:= rpc:call(A, pg2, get_members, [a])), - true = exit(Pid, kill), - ?UNTIL([] =:= pg2:get_members(a)), - ?UNTIL([] =:= rpc:call(A, pg2, get_members, [a])), - test_server:stop_node(A), - ok; - false -> - {skipped, "No support for old node"} - end. - %% OTP-8259. Some basic tests. basic(Config) when is_list(Config) -> _ = [pg2:delete(G) || G <- pg2:which_groups()], -- cgit v1.2.3 From a72e675fce23b9bebb7c9ff8beb6f962c4f9930a Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 20 Mar 2017 19:08:58 +0100 Subject: kernel: Fix erl_distribution_wb_SUITE:whitebox for OTP 20. --- lib/kernel/test/erl_distribution_wb_SUITE.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/erl_distribution_wb_SUITE.erl b/lib/kernel/test/erl_distribution_wb_SUITE.erl index 61aa3b32ee..c1dc208cc1 100644 --- a/lib/kernel/test/erl_distribution_wb_SUITE.erl +++ b/lib/kernel/test/erl_distribution_wb_SUITE.erl @@ -56,10 +56,14 @@ -define(DFLAG_HIDDEN_ATOM_CACHE,16#40). -define(DFLAG_NEW_FUN_TAGS,16#80). -define(DFLAG_EXTENDED_PIDS_PORTS,16#100). +-define(DFLAG_UTF8_ATOMS, 16#10000). %% From R9 and forward extended references is compulsory %% From R10 and forward extended pids and ports are compulsory --define(COMPULSORY_DFLAGS, (?DFLAG_EXTENDED_REFERENCES bor ?DFLAG_EXTENDED_PIDS_PORTS)). +%% From R20 and forward UTF8 atoms are compulsory +-define(COMPULSORY_DFLAGS, (?DFLAG_EXTENDED_REFERENCES bor + ?DFLAG_EXTENDED_PIDS_PORTS bor + ?DFLAG_UTF8_ATOMS)). -define(shutdown(X), exit(X)). -- cgit v1.2.3 From 1b4dedb155a4fe6faf35448c2d119e391204701a Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 16 Mar 2017 10:38:23 +0100 Subject: kernel: Expand gen_tcp:close docs with send text This commit attempts to clarify some of the guarantees given by the TCP standard when issuing close. This is quite a complex topic so there are probably corner cases still left, but this at least tells the user that things can go wrong when doing a close. --- lib/kernel/doc/src/gen_tcp.xml | 17 +++++++++++++++++ lib/kernel/doc/src/inet.xml | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index e97db20062..bef8096aed 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -140,6 +140,23 @@ do_recv(Sock, Bs) -> Close a TCP socket.

    Closes a TCP socket.

    +

    Note that in most implementations of TCP, doing a close does + not guarantee that any data sent is delivered to the recipient before + the close is detected at the remote side. If you want to guarantee + delivery of the data to the recipient there are two common ways to + achieve this.

    + +

    Use + gen_tcp:shutdown(Sock, write) to signal that + no more data is to be sent and wait for the read side of the + socket to be closed.

    +
    +

    Use the socket option + {packet, N} (or something similar) to make + it possible for the receiver to close the connection when it + knowns it has received all the data.

    +
    +
    diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index 4c4a5c39cb..36a99f7a7a 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -909,7 +909,7 @@ setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp
    {packet, PacketType}(TCP/IP sockets) -

    Defines the type of packets to use for a socket. +

    Defines the type of packets to use for a socket. Possible values:

    raw | 0 -- cgit v1.2.3 From 3edc6dbf8f150bb6ba7c800ed5cc379771f8b279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 30 Mar 2017 15:26:55 +0200 Subject: kernel: Refactor supervision tree Use maps in definitions to make them more readable. --- lib/kernel/src/kernel.erl | 244 ++++++++++++++++++++++++++++++---------------- 1 file changed, 159 insertions(+), 85 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl index 59eca242b1..b901da95b8 100644 --- a/lib/kernel/src/kernel.erl +++ b/lib/kernel/src/kernel.erl @@ -100,63 +100,112 @@ get_error_logger_type() -> %%%----------------------------------------------------------------- init([]) -> - SupFlags = {one_for_all, 0, 1}, - - Config = {kernel_config, - {kernel_config, start_link, []}, - permanent, 2000, worker, [kernel_config]}, - Code = {code_server, - {code, start_link, []}, - permanent, 2000, worker, [code]}, - File = {file_server_2, - {file_server, start_link, []}, - permanent, 2000, worker, - [file, file_server, file_io_server, prim_file]}, - StdError = {standard_error, - {standard_error, start_link, []}, - temporary, 2000, supervisor, [user_sup]}, - User = {user, - {user_sup, start, []}, - temporary, 2000, supervisor, [user_sup]}, - + SupFlags = #{strategy => one_for_all, + intensity => 0, + period => 1}, + + Config = #{id => kernel_config, + start => {kernel_config, start_link, []}, + restart => permanent, + shutdown => 2000, + type => worker, + modules => [kernel_config]}, + + Code = #{id => code_server, + start => {code, start_link, []}, + restart => permanent, + shutdown => 2000, + type => worker, + modules => [code]}, + + File = #{id => file_server_2, + start => {file_server, start_link, []}, + restart => permanent, + shutdown => 2000, + type => worker, + modeules => [file, file_server, file_io_server, prim_file]}, + + StdError = #{id => standard_error, + start => {standard_error, start_link, []}, + restart => temporary, + shutdown => 2000, + type => supervisor, + modules => [user_sup]}, + + User = #{id => user, + start => {user_sup, start, []}, + restart => temporary, + shutdown => 2000, + type => supervisor, + modules => [user_sup]}, + + SafeSup = #{id => kernel_safe_sup, + start =>{supervisor, start_link, [{local, kernel_safe_sup}, ?MODULE, safe]}, + restart => permanent, + shutdown => infinity, + type => supervisor, + modules => [?MODULE]}, + case init:get_argument(mode) of - {ok, [["minimal"]]} -> - SafeSupervisor = {kernel_safe_sup, - {supervisor, start_link, - [{local, kernel_safe_sup}, ?MODULE, safe]}, - permanent, infinity, supervisor, [?MODULE]}, - {ok, {SupFlags, - [Code, File, StdError, User, - Config, SafeSupervisor]}}; - _ -> - Rpc = {rex, {rpc, start_link, []}, - permanent, 2000, worker, [rpc]}, - Global = {global_name_server, {global, start_link, []}, - permanent, 2000, worker, [global]}, - Glo_grp = {global_group, {global_group,start_link,[]}, - permanent, 2000, worker, [global_group]}, - InetDb = {inet_db, {inet_db, start_link, []}, - permanent, 2000, worker, [inet_db]}, - NetSup = {net_sup, {erl_distribution, start_link, []}, - permanent, infinity, supervisor,[erl_distribution]}, + {ok, [["minimal"]]} -> + {ok, {SupFlags, [Code, File, StdError, User, Config, SafeSup]}}; + _ -> + Rpc = #{id => rex, + start => {rpc, start_link, []}, + restart => permanent, + shutdown => 2000, + type => worker, + modules => [rpc]}, + + Global = #{id => global_name_server, + start => {global, start_link, []}, + restart => permanent, + shutdown => 2000, + type => worker, + modules => [global]}, + + GlGroup = #{id => global_group, + start => {global_group,start_link,[]}, + restart => permanent, + shutdown => 2000, + type => worker, + modules => [global_group]}, + + InetDb = #{id => inet_db, + start => {inet_db, start_link, []}, + restart => permanent, + shutdown => 2000, + type => worker, + modules => [inet_db]}, + + NetSup = #{id => net_sup, + start => {erl_distribution, start_link, []}, + restart => permanent, + shutdown => infinity, + type => supervisor, + modules => [erl_distribution]}, + SigSrv = #{id => erl_signal_server, start => {gen_event, start_link, [{local, erl_signal_server}]}, - type => worker, restart => permanent, shutdown => 2000, modules => dynamic}, - DistAC = start_dist_ac(), - - Timer = start_timer(), - - SafeSupervisor = {kernel_safe_sup, - {supervisor, start_link, - [{local, kernel_safe_sup}, ?MODULE, safe]}, - permanent, infinity, supervisor, [?MODULE]}, - {ok, {SupFlags, - [Code, Rpc, Global, InetDb | DistAC] ++ - [NetSup, Glo_grp, File, SigSrv, - StdError, User, Config, SafeSupervisor] ++ Timer}} + restart => permanent, + shutdown => 2000, + type => worker, + modules => dynamic}, + + DistAC = start_dist_ac(), + + Timer = start_timer(), + + {ok, {SupFlags, + [Code, Rpc, Global, InetDb | DistAC] ++ + [NetSup, GlGroup, File, SigSrv, + StdError, User, Config, SafeSup] ++ Timer}} end; init(safe) -> - SupFlags = {one_for_one, 4, 3600}, + SupFlags = #{strategy => one_for_one, + intensity => 4, + period => 3600}, + Boot = start_boot_server(), DiskLog = start_disk_log(), Pg2 = start_pg2(), @@ -170,60 +219,85 @@ init(safe) -> {ok, {SupFlags, Boot ++ DiskLog ++ Pg2}}. start_dist_ac() -> - Spec = [{dist_ac,{dist_ac,start_link,[]},permanent,2000,worker,[dist_ac]}], + Spec = [#{id => dist_ac, + start => {dist_ac,start_link,[]}, + restart => permanent, + shutdown => 2000, + type => worker, + modules => [dist_ac]}], case application:get_env(kernel, start_dist_ac) of - {ok, true} -> Spec; - {ok, false} -> []; - undefined -> - case application:get_env(kernel, distributed) of - {ok, _} -> Spec; - _ -> [] - end + {ok, true} -> Spec; + {ok, false} -> []; + undefined -> + case application:get_env(kernel, distributed) of + {ok, _} -> Spec; + _ -> [] + end end. start_boot_server() -> case application:get_env(kernel, start_boot_server) of - {ok, true} -> - Args = get_boot_args(), - [{boot_server, {erl_boot_server, start_link, [Args]}, permanent, - 1000, worker, [erl_boot_server]}]; - _ -> - [] + {ok, true} -> + Args = get_boot_args(), + [#{id => boot_server, + start => {erl_boot_server, start_link, [Args]}, + restart => permanent, + shutdown => 1000, + type => worker, + modules => [erl_boot_server]}]; + _ -> + [] end. get_boot_args() -> case application:get_env(kernel, boot_server_slaves) of - {ok, Slaves} -> Slaves; - _ -> [] + {ok, Slaves} -> Slaves; + _ -> [] end. start_disk_log() -> case application:get_env(kernel, start_disk_log) of - {ok, true} -> - [{disk_log_server, - {disk_log_server, start_link, []}, - permanent, 2000, worker, [disk_log_server]}, - {disk_log_sup, {disk_log_sup, start_link, []}, permanent, - 1000, supervisor, [disk_log_sup]}]; - _ -> - [] + {ok, true} -> + [#{id => disk_log_server, + start => {disk_log_server, start_link, []}, + restart => permanent, + shutdown => 2000, + type => worker, + modules => [disk_log_server]}, + #{id => disk_log_sup, + start => {disk_log_sup, start_link, []}, + restart => permanent, + shutdown => 1000, + type => supervisor, + modules => [disk_log_sup]}]; + _ -> + [] end. start_pg2() -> case application:get_env(kernel, start_pg2) of - {ok, true} -> - [{pg2, {pg2, start_link, []}, permanent, 1000, worker, [pg2]}]; - _ -> - [] + {ok, true} -> + [#{id => pg2, + start => {pg2, start_link, []}, + restart => permanent, + shutdown => 1000, + type => worker, + modules => [pg2]}]; + _ -> + [] end. start_timer() -> case application:get_env(kernel, start_timer) of - {ok, true} -> - [{timer_server, {timer, start_link, []}, permanent, 1000, worker, - [timer]}]; - _ -> - [] + {ok, true} -> + [#{id => timer_server, + start => {timer, start_link, []}, + restart => permanent, + shutdown => 1000, + type => worker, + modules => [timer]}]; + _ -> + [] end. %%----------------------------------------------------------------- -- cgit v1.2.3 From b87894a1e91ad8370196a0c0d9ec6bfefab1dcbd Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 10 Apr 2017 11:25:53 +0200 Subject: kernel: Add mem check to prim_file:large_write tc --- lib/kernel/test/prim_file_SUITE.erl | 47 ++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 6 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl index 8be94f1e57..0c84a7d1cc 100644 --- a/lib/kernel/test/prim_file_SUITE.erl +++ b/lib/kernel/test/prim_file_SUITE.erl @@ -19,8 +19,8 @@ %% -module(prim_file_SUITE). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - read_write_file/1]). + init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2, + read_write_file/1, free_memory/0]). -export([cur_dir_0a/1, cur_dir_0b/1, cur_dir_1a/1, cur_dir_1b/1, make_del_dir_a/1, make_del_dir_b/1, @@ -115,6 +115,18 @@ groups() -> [make_link_a, make_link_b, read_link_info_for_non_link, symlinks_a, symlinks_b, list_dir_error]}]. +init_per_testcase(large_write, Config) -> + {ok, Started} = application:ensure_all_started(os_mon), + [{started, Started}|Config]; +init_per_testcase(_TestCase, Config) -> + Config. + +end_per_testcase(large_write, Config) -> + [application:stop(App) || App <- lists:reverse(proplists:get_value(started, Config))], + ok; +end_per_testcase(_, _Config) -> + ok. + init_per_group(_GroupName, Config) -> Config. @@ -2022,11 +2034,13 @@ run_large_file_test(Config, Run, Name) -> {{unix,sunos},OsVersion} when OsVersion < {5,5,1} -> {skip,"Only supported on Win32, Unix or SunOS >= 5.5.1"}; {{unix,_},_} -> - N = unix_free(proplists:get_value(priv_dir, Config)), - io:format("Free disk: ~w KByte~n", [N]), - if N < 5 bsl 20 -> + DiscFree = unix_free(proplists:get_value(priv_dir, Config)), + MemFree = free_memory(), + io:format("Free disk: ~w KByte~n", [DiscFree]), + io:format("Free mem: ~w MByte~n", [MemFree]), + if DiscFree < 5 bsl 20; MemFree < 5 bsl 10 -> %% Less than 5 GByte free - {skip,"Less than 5 GByte free disk"}; + {skip,"Less than 5 GByte free disk/mem"}; true -> do_run_large_file_test(Config, Run, Name) end; @@ -2079,6 +2093,27 @@ zip_data([], Bs) -> zip_data(As, []) -> As. +%% Stolen from emulator -> alloc_SUITE +free_memory() -> + %% Free memory in MB. + try + SMD = memsup:get_system_memory_data(), + {value, {free_memory, Free}} = lists:keysearch(free_memory, 1, SMD), + TotFree = (Free + + case lists:keysearch(cached_memory, 1, SMD) of + {value, {cached_memory, Cached}} -> Cached; + false -> 0 + end + + case lists:keysearch(buffered_memory, 1, SMD) of + {value, {buffered_memory, Buffed}} -> Buffed; + false -> 0 + end), + TotFree div (1024*1024) + catch + error : undef -> + ct:fail({"os_mon not built"}) + end. + %%%----------------------------------------------------------------- %%% Utilities rm_rf(Mod,Dir) -> -- cgit v1.2.3 From 308841d8c99907a364d6876ed9375507153729eb Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Thu, 2 Feb 2017 11:17:17 +0100 Subject: implement SO_BINDTODEVICE for inet protocols bind to device is needed to properly support VRF-Lite under Linux (see [1] for details). [1]: https://www.kernel.org/doc/Documentation/networking/vrf.txt --- lib/kernel/doc/src/inet.xml | 26 ++++++++++++++++ lib/kernel/src/inet.erl | 8 ++--- lib/kernel/src/inet_int.hrl | 1 + lib/kernel/test/inet_SUITE.erl | 70 ++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 99 insertions(+), 6 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index 076e50cd10..947e4d4560 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -897,6 +897,32 @@ setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp file:native_name_encoding/0.

    + {bind_to_device, Ifname :: binary()} + +

    Binds a socket to a specific network interface. This option + must be used in a function call that creates a socket, that is, + gen_tcp:connect/3,4, + gen_tcp:listen/2, + gen_udp:open/1,2, or + gen_sctp:open/0,1,2.

    +

    Unlike getifaddrs/0, Ifname + is encoded a binary. In the unlikely case that a system is using + non-7-bit-ASCII characters in network device names, special care + has to be taken when encoding this argument.

    +

    This option uses the Linux-specific socket option + SO_BINDTODEVICE, such as in Linux kernel 2.0.30 or later, + and therefore only exists when the runtime system + is compiled for such an operating system.

    +

    Before Linux 3.8, this socket option could be set, but could not retrieved + with getopts/2. Since Linux 3.8, + it is readable.

    +

    The virtual machine also needs elevated privileges, either + running as superuser or (for Linux) having capability + CAP_NET_RAW.

    +

    The primary use case for this option is to bind sockets into + Linux VRF instances. +

    +
    list

    Received Packet is delivered as a list.

    diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index f5c13ecdd7..5be790b7d9 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -702,7 +702,7 @@ connect_options() -> header, active, packet, packet_size, buffer, mode, deliver, line_delimiter, exit_on_close, high_watermark, low_watermark, high_msgq_watermark, low_msgq_watermark, send_timeout, send_timeout_close, delay_send, raw, - show_econnreset]. + show_econnreset, bind_to_device]. connect_options(Opts, Mod) -> BaseOpts = @@ -770,7 +770,7 @@ listen_options() -> header, active, packet, buffer, mode, deliver, backlog, ipv6_v6only, exit_on_close, high_watermark, low_watermark, high_msgq_watermark, low_msgq_watermark, send_timeout, send_timeout_close, delay_send, - packet_size, raw, show_econnreset]. + packet_size, raw, show_econnreset, bind_to_device]. listen_options(Opts, Mod) -> BaseOpts = @@ -850,7 +850,7 @@ udp_options() -> deliver, ipv6_v6only, broadcast, dontroute, multicast_if, multicast_ttl, multicast_loop, add_membership, drop_membership, read_packets,raw, - high_msgq_watermark, low_msgq_watermark]. + high_msgq_watermark, low_msgq_watermark, bind_to_device]. udp_options(Opts, Mod) -> @@ -919,6 +919,7 @@ sctp_options() -> [ % The following are generic inet options supported for SCTP sockets: mode, active, buffer, tos, tclass, priority, dontroute, reuseaddr, linger, sndbuf, recbuf, ipv6_v6only, high_msgq_watermark, low_msgq_watermark, + bind_to_device, % Other options are SCTP-specific (though they may be similar to their % TCP and UDP counter-parts): @@ -1055,7 +1056,6 @@ binary2filename(Bin) -> Bin end. - translate_ip(any, inet) -> {0,0,0,0}; translate_ip(loopback, inet) -> {127,0,0,1}; translate_ip(any, inet6) -> {0,0,0,0,0,0,0,0}; diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl index 4e8f59a3b9..e6cd48935a 100644 --- a/lib/kernel/src/inet_int.hrl +++ b/lib/kernel/src/inet_int.hrl @@ -154,6 +154,7 @@ -define(INET_LOPT_TCP_SHOW_ECONNRESET, 39). -define(INET_LOPT_LINE_DELIM, 40). -define(INET_OPT_TCLASS, 41). +-define(INET_OPT_BIND_TO_DEVICE, 42). % Specific SCTP options: separate range: -define(SCTP_OPT_RTOINFO, 100). -define(SCTP_OPT_ASSOCINFO, 101). diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl index f60c13d2e3..86f6b95fb9 100644 --- a/lib/kernel/test/inet_SUITE.erl +++ b/lib/kernel/test/inet_SUITE.erl @@ -40,7 +40,8 @@ lookup_bad_search_option/1, getif/1, getif_ifr_name_overflow/1,getservbyname_overflow/1, getifaddrs/1, - parse_strict_address/1, simple_netns/1, simple_netns_open/1]). + parse_strict_address/1, simple_netns/1, simple_netns_open/1, + simple_bind_to_device/1, simple_bind_to_device_open/1]). -export([get_hosts/1, get_ipv6_hosts/1, parse_hosts/1, parse_address/1, kill_gethost/0, parallell_gethost/0, test_netns/0]). @@ -58,7 +59,8 @@ all() -> gethostnative_debug_level, gethostnative_soft_restart, lookup_bad_search_option, getif, getif_ifr_name_overflow, getservbyname_overflow, - getifaddrs, parse_strict_address, simple_netns, simple_netns_open]. + getifaddrs, parse_strict_address, simple_netns, simple_netns_open, + simple_bind_to_device, simple_bind_to_device_open]. groups() -> [{parse, [], [parse_hosts, parse_address]}]. @@ -1247,3 +1249,67 @@ cmd(CmdString) -> io:put_chars(["# ",CmdString,io_lib:nl()]), io:put_chars([os:cmd(CmdString++" ; echo ' =>' $?")]), ok. + +-define(CAP_NET_RAW, 13). %% from /usr/include/linux/capability.h + +can_bind_to_device({unix, linux}, {Major, _, _}) + when Major > 2 -> + Status = os:cmd("cat /proc/self/status | grep CapEff"), + [_, CapEffStr] = string:tokens(Status, [$\n, $\t]), + CapEff = list_to_integer(CapEffStr, 16), + if CapEff band (1 bsl ?CAP_NET_RAW) =/= 0 -> + ok; + true -> + {skip,"insufficient capabilities, CAP_NET_RAW not granted"} + end; +can_bind_to_device(_OS, _Version) -> + {skip,"socket option bind_to_device not supported on this OS or version"}. + +simple_bind_to_device(Config) when is_list(Config) -> + case can_bind_to_device(os:type(), os:version()) of + ok -> + {ok,U} = gen_udp:open(0), + jog_bind_to_device_opt(U), + ok = gen_udp:close(U), + %% + {ok,L} = gen_tcp:listen(0, []), + jog_bind_to_device_opt(L), + ok = gen_tcp:close(L), + %% + case gen_sctp:open() of + {ok,S} -> + jog_bind_to_device_opt(S), + ok = gen_sctp:close(S); + {error,eprotonosupport} -> + ok + end; + Other -> + Other + end. + +%% Smoke test bind_to_device support. +simple_bind_to_device_open(Config) when is_list(Config) -> + case can_bind_to_device(os:type(), os:version()) of + ok -> + {ok,U} = gen_udp:open(0, [binary,{bind_to_device,<<"lo">>},inet]), + ok = gen_udp:close(U), + {ok,T} = gen_tcp:listen(0, [binary,{bind_to_device,<<"lo">>},inet]), + ok = gen_tcp:close(T), + + case gen_sctp:open(0, [binary,{bind_to_device,<<"lo">>},inet]) of + {ok,S} -> + ok = gen_sctp:close(S); + {error,eprotonosupport} -> + ok + end; + Other -> + Other + end. + +jog_bind_to_device_opt(S) -> + %% This is just jogging the option mechanics + ok = inet:setopts(S, [{bind_to_device,<<>>}]), + {ok,[{bind_to_device,<<>>}]} = inet:getopts(S, [bind_to_device]), + ok = inet:setopts(S, [{bind_to_device,<<"lo">>}]), + {ok,[{bind_to_device,<<"lo">>}]} = inet:getopts(S, [bind_to_device]), + ok. -- cgit v1.2.3 From 571912132247220e062db73819456e3c54476d64 Mon Sep 17 00:00:00 2001 From: Zandra Norman Date: Tue, 25 Apr 2017 17:17:08 +0200 Subject: Kernel: fix file_name_SUITE:icky test case --- lib/kernel/test/file_name_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl index 10b6b105d0..b2051af050 100644 --- a/lib/kernel/test/file_name_SUITE.erl +++ b/lib/kernel/test/file_name_SUITE.erl @@ -383,7 +383,7 @@ check_icky(Mod) -> ok end, - _ = make_icky_dir(Mod, treat_icky(<<"åäö_dir">>)), + _ = make_icky_dir(Mod, treat_icky(<<"åäö_dir"/utf8>>)), if UniMode and (OS =/= win32) -> {error,enoent} = Mod:set_cwd("åäö_dir"); -- cgit v1.2.3 From 25952bf726e4dc20fb3e2067e0b92c55932ce616 Mon Sep 17 00:00:00 2001 From: Zandra Norman Date: Fri, 28 Apr 2017 12:12:52 +0200 Subject: kernel: Do not allow unicode in nodenames Verify that unicode isn't used in a node name and logi an info message if it is. Filter the head part of the name a bit harder to work with epmd. --- lib/kernel/src/net_kernel.erl | 38 ++++++++++-- lib/kernel/test/erl_distribution_SUITE.erl | 99 ++++++++++++++++++++++++++++-- 2 files changed, 129 insertions(+), 8 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index 0a9f9316b0..9921a0adfd 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -1262,11 +1262,22 @@ create_name(Name, LongOrShortNames, Try) -> {Head,Host1} = create_hostpart(Name, LongOrShortNames), case Host1 of {ok,HostPart} -> - {ok,list_to_atom(Head ++ HostPart)}; + case valid_name_head(Head) of + true -> + {ok,list_to_atom(Head ++ HostPart)}; + false -> + error_logger:info_msg("Invalid node name!\n" + "Please check your configuration\n"), + {error, badarg} + end; {error,long} when Try =:= 1 -> %% It could be we haven't read domain name from resolv file yet inet_config:do_load_resolv(os:type(), longnames), create_name(Name, LongOrShortNames, 0); + {error, hostname_not_allowed} -> + error_logger:info_msg("Invalid node name!\n" + "Please check your configuration\n"), + {error, badarg}; {error,Type} -> error_logger:info_msg( lists:concat(["Can\'t set ", @@ -1279,12 +1290,13 @@ create_name(Name, LongOrShortNames, Try) -> create_hostpart(Name, LongOrShortNames) -> {Head,Host} = split_node(Name), Host1 = case {Host,LongOrShortNames} of - {[$@,_|_],longnames} -> - {ok,Host}; + {[$@,_|_] = Host,longnames} -> + validate_hostname(Host); {[$@,_|_],shortnames} -> case lists:member($.,Host) of true -> {error,short}; - _ -> {ok,Host} + _ -> + validate_hostname(Host) end; {_,shortnames} -> case inet_db:gethostname() of @@ -1304,6 +1316,24 @@ create_hostpart(Name, LongOrShortNames) -> end, {Head,Host1}. +validate_hostname([$@|HostPart] = Host) -> + {ok, MP} = re:compile("^[!-ÿ]*$", [unicode]), + case re:run(HostPart, MP) of + {match, _} -> + {ok, Host}; + nomatch -> + {error, hostname_not_allowed} + end. + +valid_name_head(Head) -> + {ok, MP} = re:compile("^[0-9A-Za-z_\\-]*$", [unicode]), + case re:run(Head, MP) of + {match, _} -> + true; + nomatch -> + false + end. + split_node(Name) -> lists:splitwith(fun(C) -> C =/= $@ end, atom_to_list(Name)). diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl index 09c80a0956..ecfa3d6cdb 100644 --- a/lib/kernel/test/erl_distribution_SUITE.erl +++ b/lib/kernel/test/erl_distribution_SUITE.erl @@ -24,7 +24,9 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). --export([tick/1, tick_change/1, illegal_nodenames/1, hidden_node/1, +-export([tick/1, tick_change/1, + nodenames/1, hostnames/1, + illegal_nodenames/1, hidden_node/1, setopts/1, table_waste/1, net_setuptime/1, inet_dist_options_options/1, @@ -53,7 +55,6 @@ -export([pinger/1]). - -define(DUMMY_NODE,dummy@test01). %%----------------------------------------------------------------- @@ -68,8 +69,8 @@ suite() -> {timetrap,{minutes,4}}]. all() -> - [tick, tick_change, illegal_nodenames, hidden_node, - setopts, + [tick, tick_change, nodenames, hostnames, illegal_nodenames, + hidden_node, setopts, table_waste, net_setuptime, inet_dist_options_options, {group, monitor_nodes}]. @@ -179,7 +180,97 @@ table_waste(Config) when is_list(Config) -> stop_node(N), ok. +%% Test that starting nodes with different legal name part works, and that illegal +%% ones are filtered +nodenames(Config) when is_list(Config) -> + legal("a1@b"), + legal("a-1@b"), + legal("a_1@b"), + + illegal("cdé@a"), + illegal("te欢st@a"). + +%% Test that starting nodes with different legal host part works, and that illegal +%% ones are filtered +hostnames(Config) when is_list(Config) -> + Host = gethostname(), + legal([$a,$@|atom_to_list(Host)]), + legal("1@b1"), + legal("b@b1-c"), + legal("c@b1_c"), + legal("d@b1#c"), + legal("f@::1"), + legal("g@1:bc3:4e3f:f20:0:1"), + + case file:native_name_encoding() of + latin1 -> ignore; + _ -> legal("e@b1é") + end, + long_hostnames(net_kernel:longnames()), + + illegal("h@testالع"), + illegal("i@языtest"), + illegal("j@te欢st"). + +long_hostnames(true) -> + legal("k@b.b.c"), + legal("l@b.b-c.d"), + legal("m@b.b_c.d"), + legal("n@127.0.0.1"), + legal("o@207.123.456.789"); +long_hostnames(false) -> + illegal("k@b.b.c"). + +legal(Name) -> + case test_node(Name) of + started -> + ok; + not_started -> + ct:fail("no ~p node started", [Name]) + end. + +illegal(Name) -> + case test_node(Name) of + not_started -> + ok; + started -> + ct:fail("~p node started with illegal name", [Name]) + end. +test_node(Name) -> + ProgName = atom_to_list(lib:progname()), + Command = ProgName ++ " -noinput " ++ long_or_short() ++ Name ++ + " -eval \"net_adm:ping('" ++ atom_to_list(node()) ++ "')\"", + net_kernel:monitor_nodes(true), + BinCommand = unicode:characters_to_binary(Command, utf8), + open_port({spawn, BinCommand}, [stream]), + Node = list_to_atom(Name), + receive + {nodeup, Node} -> + net_kernel:monitor_nodes(false), + slave:stop(Node), + started + after 5000 -> + net_kernel:monitor_nodes(false), + not_started + end. + +long_or_short() -> + case net_kernel:longnames() of + true -> " -name "; + false -> " -sname " + end. + +% get the localhost's name, depending on the using name policy +gethostname() -> + Hostname = case net_kernel:longnames() of + true-> + net_adm:localhost(); + _-> + {ok, Name}=inet:gethostname(), + Name + end, + list_to_atom(Hostname). %% Test that pinging an illegal nodename does not kill the node. illegal_nodenames(Config) when is_list(Config) -> -- cgit v1.2.3 From afa12d457b0e71f1ffe917c4d43e96350c6b4f4e Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Thu, 4 May 2017 09:42:19 +0200 Subject: Fix missing release note --- lib/kernel/doc/src/notes.xml | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index ad349c5aaf..6d6367a2bd 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -166,6 +166,14 @@

    Own Id: OTP-13564

    + +

    + Rudimentary support for DSCP has been implemented + in the guise of a tclass socket option + for IPv6 sockets.

    +

    + Own Id: OTP-13582

    +
    -- cgit v1.2.3 From df75dcf7287cb0c0ffd4dc7f253161eb3f3779c3 Mon Sep 17 00:00:00 2001 From: Kenji Rikitake Date: Thu, 4 May 2017 19:45:30 +0900 Subject: inet.xml: fix invalid IPv4-mapped-address examples * IPv6 address FFFF::192.168.42.2 is an invalid form as an IPv4-mapped address. This should be fixed as ::FFFF:192.168.42.2. The assigned address block is ::ffff:0:0/96. See RFC6890 Table 20 in the Section 2.2.2 for the further details. And for this IPv4-mapped address use *only*, the 32bit LSB of the IPv6 address can be written with the respective IPv4 address. See RFC4291 Section 2.2 Form 3 for the further details. --- lib/kernel/doc/src/inet.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index 947e4d4560..b9f5f87efb 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -75,8 +75,8 @@ Address ip_address() ------- ------------ ::1 {0,0,0,0,0,0,0,1} ::192.168.42.2 {0,0,0,0,0,0,(192 bsl 8) bor 168,(42 bsl 8) bor 2} -FFFF::192.168.42.2 - {16#FFFF,0,0,0,0,0,(192 bsl 8) bor 168,(42 bsl 8) bor 2} +::FFFF:192.168.42.2 + {0,0,0,0,0,16#FFFF,(192 bsl 8) bor 168,(42 bsl 8) bor 2} 3ffe:b80:1f8d:2:204:acff:fe17:bf38 {16#3ffe,16#b80,16#1f8d,16#2,16#204,16#acff,16#fe17,16#bf38} fe80::204:acff:fe17:bf38 @@ -87,8 +87,8 @@ fe80::204:acff:fe17:bf38
     1> inet:parse_address("192.168.42.2").
     {ok,{192,168,42,2}}
    -2> inet:parse_address("FFFF::192.168.42.2").
    -{ok,{65535,0,0,0,0,0,49320,10754}}
    +2> inet:parse_address("::FFFF:192.168.42.2"). +{ok,{0,0,0,0,0,65535,49320,10754}} -- cgit v1.2.3 From 83e20c62057ebc1d8064bf57b01be560cd244e1d Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Thu, 4 May 2017 15:42:21 +0200 Subject: Update copyright year --- lib/kernel/doc/src/code.xml | 2 +- lib/kernel/doc/src/config.xml | 2 +- lib/kernel/doc/src/gen_tcp.xml | 2 +- lib/kernel/doc/src/inet.xml | 2 +- lib/kernel/doc/src/kernel_app.xml | 2 +- lib/kernel/doc/src/notes.xml | 2 +- lib/kernel/doc/src/os.xml | 2 +- lib/kernel/doc/src/seq_trace.xml | 2 +- lib/kernel/include/inet.hrl | 2 +- lib/kernel/src/code.erl | 2 +- lib/kernel/src/disk_log.erl | 2 +- lib/kernel/src/disk_log.hrl | 2 +- lib/kernel/src/dist_ac.erl | 2 +- lib/kernel/src/dist_util.erl | 2 +- lib/kernel/src/erl_signal_handler.erl | 2 +- lib/kernel/src/error_logger.erl | 2 +- lib/kernel/src/file.erl | 2 +- lib/kernel/src/global.erl | 2 +- lib/kernel/src/inet.erl | 2 +- lib/kernel/src/inet_int.hrl | 2 +- lib/kernel/src/inet_parse.erl | 2 +- lib/kernel/src/inet_udp.erl | 2 +- lib/kernel/src/kernel.appup.src | 2 +- lib/kernel/src/kernel.erl | 2 +- lib/kernel/src/net_kernel.erl | 2 +- lib/kernel/src/os.erl | 2 +- lib/kernel/src/rpc.erl | 2 +- lib/kernel/test/application_SUITE.erl | 2 +- lib/kernel/test/code_SUITE.erl | 2 +- lib/kernel/test/disk_log_SUITE.erl | 2 +- lib/kernel/test/erl_distribution_SUITE.erl | 2 +- lib/kernel/test/erl_distribution_wb_SUITE.erl | 2 +- lib/kernel/test/error_logger_SUITE.erl | 2 +- lib/kernel/test/file_SUITE.erl | 2 +- lib/kernel/test/file_name_SUITE.erl | 2 +- lib/kernel/test/inet_SUITE.erl | 2 +- lib/kernel/test/multi_load_SUITE.erl | 2 +- lib/kernel/test/pg2_SUITE.erl | 2 +- lib/kernel/test/prim_file_SUITE.erl | 2 +- lib/kernel/test/rpc_SUITE.erl | 2 +- 40 files changed, 40 insertions(+), 40 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml index 878a450f0f..c94f612c01 100644 --- a/lib/kernel/doc/src/code.xml +++ b/lib/kernel/doc/src/code.xml @@ -4,7 +4,7 @@
    - 19962016 + 19962017 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/config.xml b/lib/kernel/doc/src/config.xml index c10f11b187..fdb2d29f63 100644 --- a/lib/kernel/doc/src/config.xml +++ b/lib/kernel/doc/src/config.xml @@ -4,7 +4,7 @@
    - 19972016 + 19972017 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index bef8096aed..070782e1f3 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -4,7 +4,7 @@
    - 19972016 + 19972017 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index 947e4d4560..b7c904ff45 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -4,7 +4,7 @@
    - 19972016 + 19972017 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index b342fff0d3..28af155493 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -4,7 +4,7 @@
    - 19962016 + 19962017 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 6d6367a2bd..7127a59a0c 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -4,7 +4,7 @@
    - 20042016 + 20042017 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml index 6ba69d12a3..64c5cbe571 100644 --- a/lib/kernel/doc/src/os.xml +++ b/lib/kernel/doc/src/os.xml @@ -4,7 +4,7 @@
    - 19972016 + 19972017 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/seq_trace.xml b/lib/kernel/doc/src/seq_trace.xml index b80e87c118..197851021f 100644 --- a/lib/kernel/doc/src/seq_trace.xml +++ b/lib/kernel/doc/src/seq_trace.xml @@ -4,7 +4,7 @@
    - 19982016 + 19982017 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/include/inet.hrl b/lib/kernel/include/inet.hrl index df788aca08..daa2e14b46 100644 --- a/lib/kernel/include/inet.hrl +++ b/lib/kernel/include/inet.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index 2a06d0cb15..9969021a6c 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index 2ade7fd77a..50a20c918c 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2015. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. diff --git a/lib/kernel/src/disk_log.hrl b/lib/kernel/src/disk_log.hrl index 593dbb31ab..c9fda8bef5 100644 --- a/lib/kernel/src/disk_log.hrl +++ b/lib/kernel/src/disk_log.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2015. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. diff --git a/lib/kernel/src/dist_ac.erl b/lib/kernel/src/dist_ac.erl index e63c969b79..2a5cf0ba92 100644 --- a/lib/kernel/src/dist_ac.erl +++ b/lib/kernel/src/dist_ac.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index d929179715..1c326afca8 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-2017. 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. diff --git a/lib/kernel/src/erl_signal_handler.erl b/lib/kernel/src/erl_signal_handler.erl index 8f924d2adc..22f235d4e4 100644 --- a/lib/kernel/src/erl_signal_handler.erl +++ b/lib/kernel/src/erl_signal_handler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index 3ee8e2c6e6..81f1bf8d97 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index 79e72cdc6d..933f2d5f65 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. diff --git a/lib/kernel/src/global.erl b/lib/kernel/src/global.erl index 5e8bc2ba5d..3d6415036c 100644 --- a/lib/kernel/src/global.erl +++ b/lib/kernel/src/global.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2015. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 5be790b7d9..6aef5476f1 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl index e6cd48935a..bc5b67f7bf 100644 --- a/lib/kernel/src/inet_int.hrl +++ b/lib/kernel/src/inet_int.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. diff --git a/lib/kernel/src/inet_parse.erl b/lib/kernel/src/inet_parse.erl index 9b47199e08..0f5dc40553 100644 --- a/lib/kernel/src/inet_parse.erl +++ b/lib/kernel/src/inet_parse.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. diff --git a/lib/kernel/src/inet_udp.erl b/lib/kernel/src/inet_udp.erl index c69791b9aa..1e624b9e90 100644 --- a/lib/kernel/src/inet_udp.erl +++ b/lib/kernel/src/inet_udp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src index 2dc90e2b3e..77085b2064 100644 --- a/lib/kernel/src/kernel.appup.src +++ b/lib/kernel/src/kernel.appup.src @@ -1,7 +1,7 @@ %% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-2017. 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. diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl index b901da95b8..6e5022a405 100644 --- a/lib/kernel/src/kernel.erl +++ b/lib/kernel/src/kernel.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index 9921a0adfd..ddda396713 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl index 7e83b17add..0250783632 100644 --- a/lib/kernel/src/os.erl +++ b/lib/kernel/src/os.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl index bd6ea26678..0e0b7dffa3 100644 --- a/lib/kernel/src/rpc.erl +++ b/lib/kernel/src/rpc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl index b4cf31b210..866043cfb4 100644 --- a/lib/kernel/test/application_SUITE.erl +++ b/lib/kernel/test/application_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 088d851b09..7831777726 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. diff --git a/lib/kernel/test/disk_log_SUITE.erl b/lib/kernel/test/disk_log_SUITE.erl index 079cc2f90f..069df5a11d 100644 --- a/lib/kernel/test/disk_log_SUITE.erl +++ b/lib/kernel/test/disk_log_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl index ecfa3d6cdb..d7a9ac39a3 100644 --- a/lib/kernel/test/erl_distribution_SUITE.erl +++ b/lib/kernel/test/erl_distribution_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. diff --git a/lib/kernel/test/erl_distribution_wb_SUITE.erl b/lib/kernel/test/erl_distribution_wb_SUITE.erl index c1dc208cc1..03aaee56b7 100644 --- a/lib/kernel/test/erl_distribution_wb_SUITE.erl +++ b/lib/kernel/test/erl_distribution_wb_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-2017. 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. diff --git a/lib/kernel/test/error_logger_SUITE.erl b/lib/kernel/test/error_logger_SUITE.erl index bb01c2384d..2d26a7246c 100644 --- a/lib/kernel/test/error_logger_SUITE.erl +++ b/lib/kernel/test/error_logger_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index b402f01758..119e1f24bb 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl index b2051af050..899102c908 100644 --- a/lib/kernel/test/file_name_SUITE.erl +++ b/lib/kernel/test/file_name_SUITE.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl index 86f6b95fb9..97f789b61c 100644 --- a/lib/kernel/test/inet_SUITE.erl +++ b/lib/kernel/test/inet_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. diff --git a/lib/kernel/test/multi_load_SUITE.erl b/lib/kernel/test/multi_load_SUITE.erl index 920839f4f9..f3258ea520 100644 --- a/lib/kernel/test/multi_load_SUITE.erl +++ b/lib/kernel/test/multi_load_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-2017. 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. diff --git a/lib/kernel/test/pg2_SUITE.erl b/lib/kernel/test/pg2_SUITE.erl index 9460608a3e..acecd34ead 100644 --- a/lib/kernel/test/pg2_SUITE.erl +++ b/lib/kernel/test/pg2_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2016. All Rights Reserved. +%% Copyright Ericsson AB 2008-2017. 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. diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl index 0c84a7d1cc..2f4330c217 100644 --- a/lib/kernel/test/prim_file_SUITE.erl +++ b/lib/kernel/test/prim_file_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2016. All Rights Reserved. +%% Copyright Ericsson AB 2000-2017. 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. diff --git a/lib/kernel/test/rpc_SUITE.erl b/lib/kernel/test/rpc_SUITE.erl index d76c4097d8..a89a7600a2 100644 --- a/lib/kernel/test/rpc_SUITE.erl +++ b/lib/kernel/test/rpc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2016. All Rights Reserved. +%% Copyright Ericsson AB 2000-2017. 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. -- cgit v1.2.3 From dc57404252c47520f352834ad9be45ad684f96c9 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Thu, 4 May 2017 17:05:25 +0200 Subject: Prepare release --- lib/kernel/doc/src/notes.xml | 84 ++++++++++++++++++++++++++++++++++++++++++++ lib/kernel/vsn.mk | 2 +- 2 files changed, 85 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 7127a59a0c..efb822f1b3 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,90 @@

    This document describes the changes made to the Kernel application.

    +
    Kernel 5.3 + +
    Fixed Bugs and Malfunctions + + +

    + Fix bug where gethostname would incorrectly fail with + enametoolong on Linux.

    +

    + Own Id: OTP-14310

    +
    +
    +
    + + +
    Improvements and New Features + + +

    + Since Unicode is now allowed in atoms an extra check is + needed for node names, which are restricted to Latin-1.

    +

    + Own Id: OTP-13805

    +
    + +

    Replaced usage of deprecated symbolic time + unit representations.

    +

    + Own Id: OTP-13831 Aux Id: OTP-13735

    +
    + +

    file:write_file(Name, Data, [raw]) would turn + Data into a single binary before writing. This + meant it could not take advantage of the writev() + system call if it was given a list of binaries and told + to write with raw mode.

    +

    + Own Id: OTP-13909

    +
    + +

    The performance of the disk_log has been + somewhat improved in some corner cases (big items), and + the documentation has been clarified.

    +

    + Own Id: OTP-14057 Aux Id: PR-1245

    +
    + +

    Functions for detecting changed code has been added. + code:modified_modules/0 returns all currently + loaded modules that have changed on disk. + code:module_status/1 returns the status for a + module. In the shell and in c module, mm/0 + is short for code:modified_modules/0, and + lm/0 reloads all currently loaded modules that + have changed on disk.

    +

    + Own Id: OTP-14059

    +
    + +

    + Introduce an event manager in Erlang to handle OS + signals. A subset of OS signals may be subscribed to and + those are described in the Kernel application.

    +

    + Own Id: OTP-14186

    +
    + +

    Sockets can now be bound to device (SO_BINDTODEVICE) + on platforms where it is supported.

    This has + been implemented e.g to support VRF-Lite under Linux; see + + VRF , and GitHub pull request #1326. +

    +

    + Own Id: OTP-14357 Aux Id: PR-1326

    +
    +
    +
    + +
    +
    Kernel 5.2
    Fixed Bugs and Malfunctions diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 76b020e8ed..4edecd8969 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 5.2 +KERNEL_VSN = 5.3 -- cgit v1.2.3 From eace29905be436d77245656b2511c9a9c2c67c90 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Fri, 5 May 2017 13:15:42 +0200 Subject: Revert "Prepare release" This reverts commit dc57404252c47520f352834ad9be45ad684f96c9. --- lib/kernel/doc/src/notes.xml | 84 -------------------------------------------- lib/kernel/vsn.mk | 2 +- 2 files changed, 1 insertion(+), 85 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index efb822f1b3..7127a59a0c 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,90 +31,6 @@

    This document describes the changes made to the Kernel application.

    -
    Kernel 5.3 - -
    Fixed Bugs and Malfunctions - - -

    - Fix bug where gethostname would incorrectly fail with - enametoolong on Linux.

    -

    - Own Id: OTP-14310

    -
    -
    -
    - - -
    Improvements and New Features - - -

    - Since Unicode is now allowed in atoms an extra check is - needed for node names, which are restricted to Latin-1.

    -

    - Own Id: OTP-13805

    -
    - -

    Replaced usage of deprecated symbolic time - unit representations.

    -

    - Own Id: OTP-13831 Aux Id: OTP-13735

    -
    - -

    file:write_file(Name, Data, [raw]) would turn - Data into a single binary before writing. This - meant it could not take advantage of the writev() - system call if it was given a list of binaries and told - to write with raw mode.

    -

    - Own Id: OTP-13909

    -
    - -

    The performance of the disk_log has been - somewhat improved in some corner cases (big items), and - the documentation has been clarified.

    -

    - Own Id: OTP-14057 Aux Id: PR-1245

    -
    - -

    Functions for detecting changed code has been added. - code:modified_modules/0 returns all currently - loaded modules that have changed on disk. - code:module_status/1 returns the status for a - module. In the shell and in c module, mm/0 - is short for code:modified_modules/0, and - lm/0 reloads all currently loaded modules that - have changed on disk.

    -

    - Own Id: OTP-14059

    -
    - -

    - Introduce an event manager in Erlang to handle OS - signals. A subset of OS signals may be subscribed to and - those are described in the Kernel application.

    -

    - Own Id: OTP-14186

    -
    - -

    Sockets can now be bound to device (SO_BINDTODEVICE) - on platforms where it is supported.

    This has - been implemented e.g to support VRF-Lite under Linux; see - - VRF , and GitHub pull request #1326. -

    -

    - Own Id: OTP-14357 Aux Id: PR-1326

    -
    -
    -
    - -
    -
    Kernel 5.2
    Fixed Bugs and Malfunctions diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 4edecd8969..76b020e8ed 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 5.3 +KERNEL_VSN = 5.2 -- cgit v1.2.3 From 513ea88e6c16e205cfb3c4164d1ca90b989d4ac7 Mon Sep 17 00:00:00 2001 From: Ben Tyler Date: Sat, 6 May 2017 22:09:50 +0200 Subject: Fix typo in child spec for file_server 'modeules' -> 'modules' Introduced in 3edc6dbf8f150bb6ba7c800ed5cc379771f8b279 (present in 20.0-rc1, but not in the 19 release series). --- lib/kernel/src/kernel.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl index 6e5022a405..cba57088ec 100644 --- a/lib/kernel/src/kernel.erl +++ b/lib/kernel/src/kernel.erl @@ -123,7 +123,7 @@ init([]) -> restart => permanent, shutdown => 2000, type => worker, - modeules => [file, file_server, file_io_server, prim_file]}, + modules => [file, file_server, file_io_server, prim_file]}, StdError = #{id => standard_error, start => {standard_error, start_link, []}, -- cgit v1.2.3 From 515d00c3f2a4d2fc4cd4eca0af870789d8632d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 12 May 2017 10:55:43 +0200 Subject: Don't crash in end_per_testcase/2 in code_SUITE:on_load_embedded/1 --- lib/kernel/test/code_SUITE.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 7831777726..afc32283ba 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -107,14 +107,19 @@ init_per_testcase(big_boot_embedded, Config) -> _Else -> {skip, "Needs crypto!"} end; -init_per_testcase(on_load_embedded, Config) -> +init_per_testcase(on_load_embedded, Config0) -> LibRoot = code:lib_dir(), LinkName = filename:join(LibRoot, "on_load_app-1.0"), - [{link_name,LinkName}|Config]; + Config = [{link_name,LinkName}|Config0], + init_per_testcase(Config); init_per_testcase(_Func, Config) -> + init_per_testcase(Config). + +init_per_testcase(Config) -> P = code:get_path(), [{code_path, P}|Config]. + end_per_testcase(module_status, Config) -> code:purge(?TESTMOD), code:delete(?TESTMOD), -- cgit v1.2.3 From 8a1138ddfec89b23c19d4d12d30dd575c678356b Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Sat, 22 Apr 2017 17:36:29 -0400 Subject: Fix type declaration for disk_log.hrl A specific type was given as an atom rather than an actual type defintion. --- lib/kernel/src/disk_log.hrl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/disk_log.hrl b/lib/kernel/src/disk_log.hrl index c9fda8bef5..58f6155adf 100644 --- a/lib/kernel/src/disk_log.hrl +++ b/lib/kernel/src/disk_log.hrl @@ -69,7 +69,7 @@ | {file, FileName :: file:filename()} | {linkto, LinkTo :: none | pid()} | {repair, Repair :: true | false | truncate} - | {type, Type :: dlog_type} + | {type, Type :: dlog_type()} | {format, Format :: dlog_format()} | {size, Size :: dlog_size()} | {distributed, Nodes :: [node()]} -- cgit v1.2.3 From ed4d30888beb58c27f61dcf86384696cc221be35 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Sat, 13 May 2017 08:41:32 -0400 Subject: add option 'quiet' to disk_log This option allows to suppress output of info_msgs to error_logger when recoverable errors are encountered in disk_log by setting the value to 'true'. Defaults to 'false', the backwards compatible behaviour. --- lib/kernel/doc/src/disk_log.xml | 6 ++++++ lib/kernel/src/disk_log.erl | 5 ++++- lib/kernel/src/disk_log.hrl | 2 ++ lib/kernel/src/disk_log_1.erl | 28 ++++++++++++++++++++++------ lib/kernel/test/disk_log_SUITE.erl | 17 +++++++++++++++-- 5 files changed, 49 insertions(+), 9 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/disk_log.xml b/lib/kernel/doc/src/disk_log.xml index aebeacee28..570d3ef9bd 100644 --- a/lib/kernel/doc/src/disk_log.xml +++ b/lib/kernel/doc/src/disk_log.xml @@ -968,6 +968,12 @@ read_write.

    + {quiet, Boolean} + +

    Specifies if messages will be sent to + error_logger on recoverable errors with + the log files. Defaults to true.

    +

    open/1 returns {ok, Log} if the log file is successfully opened. If the file is diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index 50a20c918c..70cbf1c87c 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.erl @@ -638,6 +638,8 @@ check_arg([{mode, read_only}|Tail], Res) -> check_arg(Tail, Res#arg{mode = read_only}); check_arg([{mode, read_write}|Tail], Res) -> check_arg(Tail, Res#arg{mode = read_write}); +check_arg([{quiet, Boolean}|Tail], Res) when is_boolean(Boolean) -> + check_arg(Tail, Res#arg{quiet = Boolean}); check_arg(Arg, _) -> {error, {badarg, Arg}}. @@ -1276,7 +1278,8 @@ compare_arg(_Attr, _Val, _A) -> do_open(A) -> #arg{type = Type, format = Format, name = Name, head = Head0, file = FName, repair = Repair, size = Size, mode = Mode, - version = V} = A, + quiet = Quiet, version = V} = A, + disk_log_1:set_quiet(Quiet), Head = mk_head(Head0, Format), case do_open2(Type, Format, Name, FName, Repair, Size, Mode, Head, V) of {ok, Ret, Extra, FormatType, NoItems} -> diff --git a/lib/kernel/src/disk_log.hrl b/lib/kernel/src/disk_log.hrl index 58f6155adf..a362881f40 100644 --- a/lib/kernel/src/disk_log.hrl +++ b/lib/kernel/src/disk_log.hrl @@ -76,6 +76,7 @@ | {notify, boolean()} | {head, Head :: dlog_head_opt()} | {head_func, MFA :: {atom(), atom(), list()}} + | {quiet, boolean()} | {mode, Mode :: dlog_mode()}. -type dlog_options() :: [dlog_option()]. -type dlog_repair() :: 'truncate' | boolean(). @@ -102,6 +103,7 @@ head = none, mode = read_write :: dlog_mode(), notify = false :: boolean(), + quiet = false :: boolean(), options = [] :: dlog_options()}). -record(cache, %% Cache for logged terms (per file descriptor). diff --git a/lib/kernel/src/disk_log_1.erl b/lib/kernel/src/disk_log_1.erl index d83c30f35f..10c22e1ad6 100644 --- a/lib/kernel/src/disk_log_1.erl +++ b/lib/kernel/src/disk_log_1.erl @@ -37,6 +37,7 @@ -export([get_wrap_size/1]). -export([is_head/1]). -export([position/3, truncate_at/3, fwrite/4, fclose/2]). +-export([set_quiet/1, is_quiet/0]). -compile({inline,[{scan_f2,7}]}). @@ -500,7 +501,10 @@ lh(H, _F) -> % cannot happen repair(In, File) -> FSz = file_size(File), - error_logger:info_msg("disk_log: repairing ~tp ...\n", [File]), + case is_quiet() of + true -> ok; + _ -> error_logger:info_msg("disk_log: repairing ~tp ...\n", [File]) + end, Tmp = add_ext(File, "TMP"), {ok, {_Alloc, Out, {0, _}, _FileSize}} = new_int_file(Tmp, none), scan_f_read(<<>>, In, Out, File, FSz, Tmp, ?MAX_CHUNK_SIZE, 0, 0). @@ -769,8 +773,11 @@ mf_int_chunk(Handle, {FileNo, Pos}, Bin, N) -> NFileNo = inc(FileNo, Handle#handle.maxF), case catch int_open(FName, true, read_only, any) of {error, _Reason} -> - error_logger:info_msg("disk_log: chunk error. File ~tp missing.\n\n", - [FName]), + case is_quiet() of + true -> ok; + _ -> error_logger:info_msg("disk_log: chunk error. File ~tp missing.\n\n", + [FName]) + end, mf_int_chunk(Handle, {NFileNo, 0}, [], N); {ok, {_Alloc, FdC, _HeadSize, _FileSize}} -> case chunk(FdC, FName, Pos, Bin, N) of @@ -797,9 +804,12 @@ mf_int_chunk_read_only(Handle, {FileNo, Pos}, Bin, N) -> NFileNo = inc(FileNo, Handle#handle.maxF), case catch int_open(FName, true, read_only, any) of {error, _Reason} -> - error_logger:info_msg("disk_log: chunk error. File ~tp missing.\n\n", - [FName]), - mf_int_chunk_read_only(Handle, {NFileNo, 0}, [], N); + case is_quiet() of + true -> ok; + _ -> error_logger:info_msg("disk_log: chunk error. File ~tp missing.\n\n", + [FName]) + end, + mf_int_chunk_read_only(Handle, {NFileNo, 0}, [], N); {ok, {_Alloc, FdC, _HeadSize, _FileSize}} -> case do_chunk_read_only(FdC, FName, Pos, Bin, N) of {NewFdC, eof} -> @@ -1549,6 +1559,12 @@ fclose(#cache{fd = Fd, c = C}, FileName) -> _ = write_cache_close(Fd, FileName, C), file:close(Fd). +set_quiet(Bool) -> + put(quiet, Bool). + +is_quiet() -> + get(quiet) =:= true. + %% -> {Reply, #cache{}}; Reply = ok | Error write_cache(Fd, _FileName, []) -> {ok, #cache{fd = Fd}}; diff --git a/lib/kernel/test/disk_log_SUITE.erl b/lib/kernel/test/disk_log_SUITE.erl index 069df5a11d..2b11a0381f 100644 --- a/lib/kernel/test/disk_log_SUITE.erl +++ b/lib/kernel/test/disk_log_SUITE.erl @@ -2493,6 +2493,7 @@ error_repair(Conf) when is_list(Conf) -> del(File, No), ok = file:del_dir(Dir), + error_logger:add_report_handler(?MODULE, self()), %% repair a file P1 = pps(), {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, @@ -2509,6 +2510,8 @@ error_repair(Conf) when is_list(Conf) -> ok = disk_log:close(n), true = (P1 == pps()), del(File, No), + receive {info_msg, _, "disk_log: repairing" ++ _, _} -> ok + after 1000 -> ct:fail(failed) end, %% yet another repair P2 = pps(), @@ -2525,6 +2528,8 @@ error_repair(Conf) when is_list(Conf) -> ok = disk_log:close(n), true = (P2 == pps()), del(File, No), + receive {info_msg, _, "disk_log: repairing" ++ _, _} -> ok + after 1000 -> ct:fail(failed) end, %% Repair, large term Big = term_to_binary(lists:duplicate(66000,$a)), @@ -2540,6 +2545,8 @@ error_repair(Conf) when is_list(Conf) -> ok = disk_log:close(n), Got = Big, del(File, No), + receive {info_msg, _, "disk_log: repairing" ++ _, _} -> ok + after 1000 -> ct:fail(failed) end, %% A term a little smaller than a chunk, then big terms. BigSmall = mk_bytes(1024*64-8-12), @@ -2560,6 +2567,8 @@ error_repair(Conf) when is_list(Conf) -> {type, halt}, {format, internal}]), ok = disk_log:close(n), file:delete(File), + receive {info_msg, _, "disk_log: repairing" ++ _, _} -> ok + after 1000 -> ct:fail(failed) end, %% The header is recovered. {ok,n} = @@ -2573,12 +2582,13 @@ error_repair(Conf) when is_list(Conf) -> crash(File, 30), {repaired,n,{recovered,3},{badbytes,15}} = disk_log:open([{name, n}, {file, File}, {type, halt}, - {format, internal},{repair,true}, + {format, internal},{repair,true}, {quiet, true}, {head_func, {?MODULE, head_fun, [{ok,"head"}]}}]), ["head",'of',terms] = get_all_terms(n), ok = disk_log:close(n), - + error_logger:delete_report_handler(?MODULE), file:delete(File), + {messages, []} = process_info(self(), messages), ok. @@ -5007,6 +5017,9 @@ init(Tester) -> handle_event({error_report, _GL, {Pid, crash_report, Report}}, Tester) -> Tester ! {crash_report, Pid, Report}, {ok, Tester}; +handle_event({info_msg, _GL, {Pid, F,A}}, Tester) -> + Tester ! {info_msg, Pid, F, A}, + {ok, Tester}; handle_event(_Event, State) -> {ok, State}. -- cgit v1.2.3 From 4b9e1dd37f6e55ba1a04fad36d8ed3c0201b1942 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Sat, 22 Apr 2017 17:39:08 -0400 Subject: Add persistence to history of the non-legacy shell This patch adds a mechanism by which shell history gets stored persistently on disk and gets loaded whenever a new shell session begins. The log files are added to the user's cache directory, with multiple files in it, although the location is configurable. All Erlang instances on a given host will share the same log file. There are two hook points in group.erl that are used: when instantiating the buffer, where we fetch from disk, and when adding a line to the buffer (gets stored). This allows all shell instances to use the same base set of lines when loaded, but to keep their history separate after the fact. As lines are added as you go, a new shell session (with ^G -> s -> c) will have access to previous sessions' lines. The implementation makes use of the disk_log library with a rotating log in order to store data, and allow automated repairs. By default, the feature is disabled and must be turned on through OTP environment variable part of the kernel application. The options include: | Option | Accepted values | Default | Description | | ------------------------ | ----------------- |---------- | -------------------- | | shell_history | enabled, disabled | disabled | turn feature on/off | | shell_history_path | any string | user cache| where to put files | | shell_history_file_bytes | 51200..N bytes | 512kb | max data size (>50kb)| | shell_history_drop | ["q().", ...] | [] | blacklisted lines | A version header is added to the disk_log configuration, allowing changes in the storage format to be enacted in the future (i.e. adding segmentation by node name in the same file, or encoding) without conflict. Log rotation is used with multiple log files to ensure proper enforcement of resizing without too much data loss. Because disk_log does not allow to just flush bits of content on rewrite (it truncates any full file), we instead use a wrap log and try to divide the configured size into up to 10 log files so that every time we rotate a log, we lose only 10% of the data. This remains true for corrupted files that cannot fully be repaired. This many-logs-based approach in turn forces the use of a minimal log size for the `shell_history_file_bytes` configuration, since it has to be divided by 10. The shell history is not loaded for the `user` process which, despite running the group.erl module, does not actually require history as it mostly just forwards IO protocol information. --- lib/kernel/doc/src/kernel_app.xml | 23 +++ lib/kernel/src/Makefile | 1 + lib/kernel/src/group.erl | 3 +- lib/kernel/src/group_history.erl | 341 ++++++++++++++++++++++++++++++++++++++ lib/kernel/src/kernel.app.src | 1 + 5 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 lib/kernel/src/group_history.erl (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index 28af155493..9fccb4c7ac 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -419,6 +419,29 @@ MaxT = TickTime + TickTime / 4 using this service.

    Defaults to false.

    + shell_history = enabled | disabled + +

    Specifies whether shell history should be logged to disk + between usages of erl.

    +
    + shell_history_drop = [string()] + +

    Specific log lines that should not be persisted. For + example ["q().", "init:stop()."] will allow to + ignore commands that shut the node down. Defaults to + [].

    +
    + shell_history_file_bytes = integer() + +

    how many bytes the shell should remember. By default, the + value is set to 512kb, and the minimal value is 50kb.

    +
    + shell_history_path = string() + +

    Specifies where the shell history files will be stored. + defaults to the user's cache directory as returned by + filename:basedir(user_cache, "erlang-history").

    +
    shutdown_func = {Mod, Func}

    Where:

    diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile index 2a89faaf13..78aa6192a9 100644 --- a/lib/kernel/src/Makefile +++ b/lib/kernel/src/Makefile @@ -85,6 +85,7 @@ MODULES = \ global_group \ global_search \ group \ + group_history \ heart \ hipe_unified_loader \ inet \ diff --git a/lib/kernel/src/group.erl b/lib/kernel/src/group.erl index b5e73117dd..0eeaaad8d2 100644 --- a/lib/kernel/src/group.erl +++ b/lib/kernel/src/group.erl @@ -33,7 +33,7 @@ start(Drv, Shell, Options) -> server(Drv, Shell, Options) -> process_flag(trap_exit, true), edlin:init(), - put(line_buffer, proplists:get_value(line_buffer, Options, [])), + put(line_buffer, proplists:get_value(line_buffer, Options, group_history:load())), put(read_mode, list), put(user_drv, Drv), put(expand_fun, @@ -783,6 +783,7 @@ save_line_buffer("\n", Lines) -> save_line_buffer(Line, [Line|_Lines]=Lines) -> save_line_buffer(Lines); save_line_buffer(Line, Lines) -> + group_history:add(Line), save_line_buffer([Line|Lines]). save_line_buffer(Lines) -> diff --git a/lib/kernel/src/group_history.erl b/lib/kernel/src/group_history.erl new file mode 100644 index 0000000000..5ca5e2d1dd --- /dev/null +++ b/lib/kernel/src/group_history.erl @@ -0,0 +1,341 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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(group_history). +-export([load/0, add/1]). + +%% Make a minimal size that should encompass set of lines and then make +%% a file rotation for N files of this size. +-define(DEFAULT_HISTORY_FILE, "erlang-shell-log"). +-define(MAX_HISTORY_FILES, 10). +-define(DEFAULT_SIZE, 1024*512). % 512 kb total default +-define(DEFAULT_STATUS, disabled). +-define(MIN_HISTORY_SIZE, (50*1024)). % 50 kb, in bytes +-define(DEFAULT_DROP, []). +-define(DISK_LOG_FORMAT, internal). % since we want repairs +-define(LOG_NAME, '$#group_history'). +-define(VSN, {0,1,0}). + +%%%%%%%%%%%%%% +%%% PUBLIC %%% +%%%%%%%%%%%%%% + +%% @doc Loads the shell history from memory. This function should only be +%% called from group:server/3 to inject itself in the previous commands +%% stack. +-spec load() -> [string()]. +load() -> + wait_for_kernel_safe_sup(), + case history_status() of + enabled -> + case open_log() of + {ok, ?LOG_NAME} -> + read_full_log(?LOG_NAME); + {repaired, ?LOG_NAME, {recovered, Good}, {badbytes, Bad}} -> + report_repairs(?LOG_NAME, Good, Bad), + read_full_log(?LOG_NAME); + {error, {need_repair, _FileName}} -> + repair_log(?LOG_NAME); + {error, {arg_mismatch, repair, true, false}} -> + repair_log(?LOG_NAME); + {error, {name_already_open, _}} -> + show_rename_warning(), + read_full_log(?LOG_NAME); + {error, {size_mismatch, Current, New}} -> + show_size_warning(Current, New), + resize_log(?LOG_NAME, Current, New), + load(); + {error, {invalid_header, {vsn, Version}}} -> + upgrade_version(?LOG_NAME, Version), + load(); + {error, Reason} -> + handle_open_error(Reason), + disable_history(), + [] + end; + _ -> + [] + end. + +%% @doc adds a log line to the erlang history log, if configured to do so. +-spec add(iodata()) -> ok. +add(Line) -> add(Line, history_status()). + +add(Line, enabled) -> + case lists:member(Line, to_drop()) of + false -> + case disk_log:log(?LOG_NAME, Line) of + ok -> + ok; + {error, no_such_log} -> + _ = open_log(), % a wild attempt we hope works! + disk_log:log(?LOG_NAME, Line); + {error, _Other} -> + % just ignore, we're too late + ok + end; + true -> + ok + end; +add(_Line, disabled) -> + ok. + +%%%%%%%%%%%%%%% +%%% PRIVATE %%% +%%%%%%%%%%%%%%% + +%% Because loading the shell happens really damn early, processes we depend on +%% might not be there yet. Luckily, the load function is called from the shell +%% after a new process has been spawned, so we can block in here +wait_for_kernel_safe_sup() -> + case whereis(kernel_safe_sup) of + undefined -> + timer:sleep(50), + wait_for_kernel_safe_sup(); + _ -> + ok + end. + +%% Repair the log out of band +repair_log(Name) -> + Opts = lists:keydelete(size, 1, log_options()), + case disk_log:open(Opts) of + {repaired, ?LOG_NAME, {recovered, Good}, {badbytes, Bad}} -> + report_repairs(?LOG_NAME, Good, Bad); + _ -> + ok + end, + _ = disk_log:close(Name), + load(). + +%% Return whether the shell history is enabled or not +-spec history_status() -> enabled | disabled. +history_status() -> + case is_user() orelse application:get_env(kernel, shell_history) of + true -> disabled; % don't run for user proc + {ok, enabled} -> enabled; + undefined -> ?DEFAULT_STATUS; + _ -> disabled + end. + +%% Return whether the user process is running this +-spec is_user() -> boolean(). +is_user() -> + case process_info(self(), registered_name) of + {registered_name, user} -> true; + _ -> false + end. + +%% Open a disk_log file while ensuring the required path is there. +open_log() -> + Opts = log_options(), + _ = ensure_path(Opts), + disk_log:open(Opts). + +%% Return logger options +log_options() -> + Path = find_path(), + File = filename:join([Path, ?DEFAULT_HISTORY_FILE]), + Size = find_wrap_values(), + [{name, ?LOG_NAME}, + {file, File}, + {repair, true}, + {format, internal}, + {type, wrap}, + {size, Size}, + {distributed, []}, + {notify, false}, + {head, {vsn, ?VSN}}, + {quiet, true}, + {mode, read_write}]. + +-spec ensure_path([{file, string()} | {atom(), _}, ...]) -> ok | {error, term()}. +ensure_path(Opts) -> + {file, Path} = lists:keyfind(file, 1, Opts), + filelib:ensure_dir(Path). + +%% @private read the logs from an already open file. Treat closed files +%% as wrong and returns an empty list to avoid crash loops in the shell. +-spec read_full_log(term()) -> [string()]. +read_full_log(Name) -> + case disk_log:chunk(Name, start) of + {error, no_such_log} -> + show_unexpected_close_warning(), + []; + eof -> + []; + {Cont, Logs} -> + lists:reverse(maybe_drop_header(Logs) ++ read_full_log(Name, Cont)) + end. + +read_full_log(Name, Cont) -> + case disk_log:chunk(Name, Cont) of + {error, no_such_log} -> + show_unexpected_close_warning(), + []; + eof -> + []; + {NextCont, Logs} -> + maybe_drop_header(Logs) ++ read_full_log(Name, NextCont) + end. + +maybe_drop_header([{vsn, _} | Rest]) -> Rest; +maybe_drop_header(Logs) -> Logs. + +-spec handle_open_error(_) -> ok. +handle_open_error({arg_mismatch, OptName, CurrentVal, NewVal}) -> + show('$#erlang-history-arg-mismatch', + "Log file argument ~p changed value from ~p to ~p " + "and cannot be automatically updated. Please clear the " + "history files and try again.~n", + [OptName, CurrentVal, NewVal]); +handle_open_error({not_a_log_file, FileName}) -> + show_invalid_file_warning(FileName); +handle_open_error({invalid_index_file, FileName}) -> + show_invalid_file_warning(FileName); +handle_open_error({invalid_header, Term}) -> + show('$#erlang-history-invalid-header', + "Shell history expects to be able to use the log files " + "which currently have unknown headers (~p) and may belong to " + "another mechanism. History logging will be " + "disabled.~n", + [Term]); +handle_open_error({file_error, FileName, Reason}) -> + show('$#erlang-history-file-error', + "Error handling File ~s. Reason: ~p~n" + "History logging will be disabled.~n", + [FileName, Reason]); +handle_open_error(Err) -> + show_unexpected_warning({disk_log, open, 1}, Err). + +find_wrap_values() -> + ConfSize = case application:get_env(kernel, shell_history_file_bytes) of + undefined -> ?DEFAULT_SIZE; + {ok, S} -> S + end, + SizePerFile = max(?MIN_HISTORY_SIZE, ConfSize div ?MAX_HISTORY_FILES), + FileCount = if SizePerFile > ?MIN_HISTORY_SIZE -> + ?MAX_HISTORY_FILES + ; SizePerFile =< ?MIN_HISTORY_SIZE -> + max(1, ConfSize div SizePerFile) + end, + {SizePerFile, FileCount}. + +report_repairs(_, _, 0) -> + %% just a regular close repair + ok; +report_repairs(_, Good, Bad) -> + show('$#erlang-history-report-repairs', + "The shell history log file was corrupted and was repaired. " + "~p bytes were recovered and ~p were lost.~n", [Good, Bad]). + +resize_log(Name, _OldSize, NewSize) -> + show('$#erlang-history-resize-attempt', + "Attempting to resize the log history file to ~p...", [NewSize]), + Opts = lists:keydelete(size, 1, log_options()), + _ = case disk_log:open(Opts) of + {error, {need_repair, _}} -> + _ = repair_log(Name), + disk_log:open(Opts); + _ -> + ok + end, + case disk_log:change_size(Name, NewSize) of + ok -> + show('$#erlang-history-resize-result', + "ok~n", []); + {error, {new_size_too_small, _}} -> + show('$#erlang-history-resize-result', + "failed (new size is too small)~n", []), + disable_history(); + {error, Reason} -> + show('$#erlang-history-resize-result', + "failed (~p)~n", [Reason]), + disable_history() + end. + +upgrade_version(_Name, Unsupported) -> + %% We only know of one version and can't support a newer one + show('$#erlang-history-upgrade', + "The version for the shell logs found on disk (~p) is " + "not supported by the current version (~p)~n", + [Unsupported, ?VSN]), + disable_history(). + +disable_history() -> + show('$#erlang-history-disable', "Disabling shell history logging.~n", []), + application:set_env(kernel, shell_history, force_disabled). + +find_path() -> + case application:get_env(kernel, shell_history_path) of + undefined -> filename:basedir(user_cache, "erlang-history"); + {ok, Path} -> Path + end. + +to_drop() -> + case application:get_env(kernel, shell_history_drop) of + undefined -> + application:set_env(kernel, shell_history_drop, ?DEFAULT_DROP), + ?DEFAULT_DROP; + {ok, V} when is_list(V) -> [Ln++"\n" || Ln <- V]; + {ok, _} -> ?DEFAULT_DROP + end. + +%%%%%%%%%%%%%%%%%%%%%%%% +%%% Output functions %%% +%%%%%%%%%%%%%%%%%%%%%%%% +show_rename_warning() -> + show('$#erlang-history-rename-warn', + "A history file with a different path has already " + "been started for the shell of this node. The old " + "name will keep being used for this session.~n", + []). + +show_invalid_file_warning(FileName) -> + show('$#erlang-history-invalid-file', + "Shell history expects to be able to use the file ~s " + "which currently exists and is not a file usable for " + "history logging purposes. History logging will be " + "disabled.~n", [FileName]). + +show_unexpected_warning({M,F,A}, Term) -> + show('$#erlang-history-unexpected-return', + "unexpected return value from ~p:~p/~p: ~p~n" + "shell history will be disabled for this session.~n", + [M,F,A,Term]). + +show_unexpected_close_warning() -> + show('$#erlang-history-unexpected-close', + "The shell log file has mysteriousy closed. Ignoring " + "currently unread history.~n", []). + +show_size_warning(_Current, _New) -> + show('$#erlang-history-size', + "The configured log history file size is different from " + "the size of the log file on disk.~n", []). + +show(Key, Format, Args) -> + case get(Key) of + undefined -> + io:format(standard_error, Format, Args), + put(Key, true), + ok; + true -> + ok + end. diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 25e4ddd95c..1128ee3ec5 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -44,6 +44,7 @@ global_group, global_search, group, + group_history, heart, hipe_unified_loader, inet6_tcp, -- cgit v1.2.3 From f97921c7ea300ddfce3c37735a6373bd85e55da1 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Mon, 15 May 2017 14:27:00 +0200 Subject: kernel: Add error_logger:limit_term/1 Calling error_logger:limit_term/1 before sending terms as events to the error_logger can be used for limiting the size of the messages. Doing so will not change the output, but potentially save memory. The Kernel variable error_logger_format_depth is used when limiting the size of terms. --- lib/kernel/src/error_logger.erl | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index 81f1bf8d97..93a3f4f2e5 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -31,6 +31,8 @@ handle_event/2, handle_call/2, handle_info/2, terminate/2]). +-export([limit_term/1]). + -define(buffer_size, 10). %%----------------------------------------------------------------- @@ -518,3 +520,19 @@ string_p1([H|T]) when is_list(H) -> end; string_p1([]) -> true; string_p1(_) -> false. + +-spec limit_term(term()) -> term(). + +limit_term(Term) -> + case get_depth() of + unlimited -> Term; + D -> io_lib:limit_term(Term, D) + end. + +get_depth() -> + case application:get_env(kernel, error_logger_format_depth) of + {ok, Depth} when is_integer(Depth) -> + max(10, Depth); + undefined -> + unlimited + end. -- cgit v1.2.3 From a99b0a2a570e7429b05f3ce424880744ee3a8814 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Wed, 17 May 2017 16:16:58 +0200 Subject: kernel: Introcude error_logger:get_format_depth() --- lib/kernel/doc/src/error_logger.xml | 15 ++++++++++++++- lib/kernel/src/error_logger.erl | 8 +++++--- 2 files changed, 19 insertions(+), 4 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml index 814e8eac46..27db00819f 100644 --- a/lib/kernel/doc/src/error_logger.xml +++ b/lib/kernel/doc/src/error_logger.xml @@ -4,7 +4,7 @@
    - 19962016 + 19962017 Ericsson AB. All Rights Reserved. @@ -162,6 +162,19 @@ ok error_report/1.

    + + + Get the value of the Kernel application variable + error_logger_format_depth. + +

    Returns max(10, Depth), where Depth is the + value of + + error_logger_format_depth + in the Kernel application, if Depth is an integer. Otherwise, + unlimited is returned.

    +
    +
    diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index 93a3f4f2e5..9bf8547745 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -31,7 +31,7 @@ handle_event/2, handle_call/2, handle_info/2, terminate/2]). --export([limit_term/1]). +-export([get_format_depth/0, limit_term/1]). -define(buffer_size, 10). @@ -524,12 +524,14 @@ string_p1(_) -> false. -spec limit_term(term()) -> term(). limit_term(Term) -> - case get_depth() of + case get_format_depth() of unlimited -> Term; D -> io_lib:limit_term(Term, D) end. -get_depth() -> +-spec get_format_depth() -> 'unlimited' | pos_integer(). + +get_format_depth() -> case application:get_env(kernel, error_logger_format_depth) of {ok, Depth} when is_integer(Depth) -> max(10, Depth); -- cgit v1.2.3 From 40a5c8b1dd9e751488c11f47f8c6fe68a4e55aa2 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Fri, 19 May 2017 13:10:04 +0200 Subject: kernel: Create table 'global_names' with read_concurrency Measurements showed contention on one of the ETS tables created by the 'global' module. --- lib/kernel/src/global.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/global.erl b/lib/kernel/src/global.erl index 3d6415036c..a9e92b28b8 100644 --- a/lib/kernel/src/global.erl +++ b/lib/kernel/src/global.erl @@ -447,7 +447,8 @@ info() -> init([]) -> process_flag(trap_exit, true), _ = ets:new(global_locks, [set, named_table, protected]), - _ = ets:new(global_names, [set, named_table, protected]), + _ = ets:new(global_names, [set, named_table, protected, + {read_concurrency, true}]), _ = ets:new(global_names_ext, [set, named_table, protected]), _ = ets:new(global_pid_names, [bag, named_table, protected]), -- cgit v1.2.3 From 1d6ec21da2d91f4226fb42506299e782ccdb4644 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 22 May 2017 16:21:58 +0200 Subject: kernel: Skip ipv6 tcs when cpiv6 is disabled For some reason doing a gen_udp:open(0, [inet6,{ipv6_v6only,true}]) works on some systems where ipv6 is disabled. So instead we explicitly require the localhost ip to do the test at which seems to work better. ipv6 was disabled in sysctl using the line net.ipv6.conf.all.disable_ipv6=1 when this behaviour was found. --- lib/kernel/test/gen_tcp_api_SUITE.erl | 8 ++++---- lib/kernel/test/gen_udp_SUITE.erl | 4 ++-- lib/kernel/test/inet_sockopt_SUITE.erl | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl index 92a74465b7..3f11e25b93 100644 --- a/lib/kernel/test/gen_tcp_api_SUITE.erl +++ b/lib/kernel/test/gen_tcp_api_SUITE.erl @@ -302,9 +302,9 @@ t_implicit_inet6(Config) when is_list(Config) -> end. t_implicit_inet6(Host, Addr) -> - case gen_tcp:listen(0, [inet6]) of + Loopback = {0,0,0,0,0,0,0,1}, + case gen_tcp:listen(0, [inet6, {ip,Loopback}]) of {ok,S1} -> - Loopback = {0,0,0,0,0,0,0,1}, io:format("~s ~p~n", ["::1",Loopback]), implicit_inet6(S1, Loopback), ok = gen_tcp:close(S1), @@ -524,10 +524,10 @@ local_handshake(S, SAddr, C, CAddr) -> t_accept_inet6_tclass(Config) when is_list(Config) -> TClassOpt = {tclass,8#56 bsl 2}, % Expedited forwarding - case gen_tcp:listen(0, [inet6,TClassOpt]) of + Loopback = {0,0,0,0,0,0,0,1}, + case gen_tcp:listen(0, [inet6, {ip, Loopback}, TClassOpt]) of {ok,L} -> LPort = ok(inet:port(L)), - Loopback = {0,0,0,0,0,0,0,1}, Sa = ok(gen_tcp:connect(Loopback, LPort, [])), Sb = ok(gen_tcp:accept(L)), [TClassOpt] = ok(inet:getopts(Sb, [tclass])), diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index 1029d7ef0a..836e0c5a05 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -717,9 +717,9 @@ implicit_inet6(Config) when is_list(Config) -> implicit_inet6(Host, Addr) -> Active = {active,false}, - case gen_udp:open(0, [inet6,Active]) of + Loopback = {0,0,0,0,0,0,0,1}, + case gen_udp:open(0, [inet6,Active,{ip, Loopback}]) of {ok,S1} -> - Loopback = {0,0,0,0,0,0,0,1}, io:format("~s ~p~n", ["::1",Loopback]), implicit_inet6(S1, Active, Loopback), ok = gen_udp:close(S1), diff --git a/lib/kernel/test/inet_sockopt_SUITE.erl b/lib/kernel/test/inet_sockopt_SUITE.erl index 322b9f30fe..9413cbd976 100644 --- a/lib/kernel/test/inet_sockopt_SUITE.erl +++ b/lib/kernel/test/inet_sockopt_SUITE.erl @@ -620,7 +620,7 @@ ipv6_v6only_close(Module, Socket) -> %% Test using socket option ipv6_v6only for UDP. use_ipv6_v6only_udp(Config) when is_list(Config) -> - case gen_udp:open(0, [inet6,{ipv6_v6only,true}]) of + case gen_udp:open(0, [inet6,{ip,{0,0,0,0,0,0,0,1}}, {ipv6_v6only,true}]) of {ok,S6} -> case inet:getopts(S6, [ipv6_v6only]) of {ok,[{ipv6_v6only,true}]} -> -- cgit v1.2.3 From d4bac182e5395ec2d0f4da5b9487488cb4a40707 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 22 May 2017 11:47:23 +0200 Subject: kernel: Add doc link from os man page to signal service --- lib/kernel/doc/src/kernel_app.xml | 1 + lib/kernel/doc/src/os.xml | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index 9fccb4c7ac..75e1e18d86 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -58,6 +58,7 @@
    + OS Signal Event Handler

    Asynchronous OS signals may be subscribed to via the Kernel applications event manager (see OTP Design Principles and diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml index 64c5cbe571..0e9add4161 100644 --- a/lib/kernel/doc/src/os.xml +++ b/lib/kernel/doc/src/os.xml @@ -174,8 +174,9 @@ DirOut = os:cmd("dir"), % on Win32 platform handle - This signal will notify erl_signal_server when it is received by - the Erlang runtime system. + This signal will notify + erl_signal_server + when it is received by the Erlang runtime system. -- cgit v1.2.3 From 30f4fc6963e5793368713897f32afd2172dc1578 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 18 May 2017 16:11:11 +0200 Subject: otp: Extend secure distribution docs warnings Warnings have been added to the relevant documentation about not using un-secure distributed nodes in exposed environments. --- lib/kernel/doc/src/net_kernel.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml index 4e2b0c69db..7ddb849824 100644 --- a/lib/kernel/doc/src/net_kernel.xml +++ b/lib/kernel/doc/src/net_kernel.xml @@ -64,6 +64,19 @@ $ erl -sname foobar by the magic cookie system, see section Distributed Erlang in the Erlang Reference Manual.

    + +

    + Starting a distributed node without also specifying + -proto_dist inet_tls + will expose the node to attacks that may give the attacker + complete access to the node and in extension the cluster. + When using un-secure distributed nodes, make sure that the + network is configured to keep potential attackers out. + See the + Using SSL for Erlang Distribution User's Guide + for details on how to setup a secure distributed node. +

    +
    -- cgit v1.2.3 From 2fa8ade78ef987ad1f3ab42bed691ebfcf29d29d Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 23 May 2017 15:06:04 +0200 Subject: kernel: Add early reject of invalid node names --- lib/kernel/src/dist_util.erl | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index 1c326afca8..b3507e5d13 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -572,12 +572,25 @@ recv_name(#hs_data{socket = Socket, f_recv = Recv}) -> ?shutdown(no_node) end. -get_name([$n,VersionA, VersionB, Flag1, Flag2, Flag3, Flag4 | OtherNode]) -> - {?u32(Flag1, Flag2, Flag3, Flag4), list_to_atom(OtherNode), - ?u16(VersionA,VersionB)}; +get_name([$n,VersionA, VersionB, Flag1, Flag2, Flag3, Flag4 | OtherNode] = Data) -> + case is_valid_name(OtherNode) of + true -> + {?u32(Flag1, Flag2, Flag3, Flag4), list_to_atom(OtherNode), + ?u16(VersionA,VersionB)}; + false -> + ?shutdown(Data) + end; get_name(Data) -> ?shutdown(Data). +is_valid_name(OtherNodeName) -> + case string:lexemes(OtherNodeName,"@") of + [_OtherNodeName,_OtherNodeHost] -> + true; + _else -> + false + end. + publish_type(Flags) -> case Flags band ?DFLAG_PUBLISHED of 0 -> -- cgit v1.2.3 From 967a7deaaee2782fb91c8af592096c6b39c666f8 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 23 May 2017 17:35:49 +0200 Subject: kernel: Make sure to cleanup after distr tests Because this test suite did not cleanup after itself, the error_logger_warn_SUITE:file_utc failed to start its node that it needed for the test. --- lib/kernel/test/erl_distribution_SUITE.erl | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl index d7a9ac39a3..bbfaa9d147 100644 --- a/lib/kernel/test/erl_distribution_SUITE.erl +++ b/lib/kernel/test/erl_distribution_SUITE.erl @@ -230,7 +230,7 @@ legal(Name) -> end. illegal(Name) -> - case test_node(Name) of + case test_node(Name, true) of not_started -> ok; started -> @@ -238,12 +238,20 @@ illegal(Name) -> end. test_node(Name) -> + test_node(Name, false). +test_node(Name, Illigal) -> ProgName = atom_to_list(lib:progname()), Command = ProgName ++ " -noinput " ++ long_or_short() ++ Name ++ - " -eval \"net_adm:ping('" ++ atom_to_list(node()) ++ "')\"", + " -eval \"net_adm:ping('" ++ atom_to_list(node()) ++ "')\"" ++ + case Illigal of + true -> + " -eval \"timer:sleep(10000),init:stop().\""; + false -> + "" + end, net_kernel:monitor_nodes(true), BinCommand = unicode:characters_to_binary(Command, utf8), - open_port({spawn, BinCommand}, [stream]), + Prt = open_port({spawn, BinCommand}, [stream]), Node = list_to_atom(Name), receive {nodeup, Node} -> -- cgit v1.2.3 From 7ba1983bb5b5cd0d391a5ba944d1fd28ae4187d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 24 May 2017 10:43:35 +0200 Subject: Stop the cover server after running code_SUITE:module_status/1 Leaving the cover server running could cause problems in other test cases. --- lib/kernel/test/code_SUITE.erl | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index afc32283ba..6f8e949aac 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -1791,6 +1791,19 @@ do_normalized_paths([]) -> %% Test that module_status/1 behaves as expected module_status(_Config) -> + case test_server:is_cover() of + true -> + module_status(); + false -> + %% Make sure that we terminate the cover server. + try + module_status() + after + cover:stop() + end + end. + +module_status() -> %% basics not_loaded = code:module_status(fubar), % nonexisting {file, preloaded} = code:is_loaded(erlang), -- cgit v1.2.3 From eaf8ca41dfa4850437ad270d3897399c9358ced0 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Tue, 30 May 2017 16:15:30 +0200 Subject: Prepare release --- lib/kernel/doc/src/notes.xml | 135 +++++++++++++++++++++++++++++++++++++++++++ lib/kernel/vsn.mk | 2 +- 2 files changed, 136 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 7127a59a0c..975518d791 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,141 @@

    This document describes the changes made to the Kernel application.

    +
    Kernel 5.3 + +
    Fixed Bugs and Malfunctions + + +

    + Fix bug where gethostname would incorrectly fail with + enametoolong on Linux.

    +

    + Own Id: OTP-14310

    +
    + +

    + Fix bug causing code:is_module_native to falsely + return true when local call trace is enabled for + the module.

    +

    + Own Id: OTP-14390

    +
    + +

    + Add early reject of invalid node names from distributed + nodes.

    +

    + Own Id: OTP-14426

    +
    +
    +
    + + +
    Improvements and New Features + + +

    + Since Unicode is now allowed in atoms an extra check is + needed for node names, which are restricted to Latin-1.

    +

    + Own Id: OTP-13805

    +
    + +

    Replaced usage of deprecated symbolic time + unit representations.

    +

    + Own Id: OTP-13831 Aux Id: OTP-13735

    +
    + +

    file:write_file(Name, Data, [raw]) would turn + Data into a single binary before writing. This + meant it could not take advantage of the writev() + system call if it was given a list of binaries and told + to write with raw mode.

    +

    + Own Id: OTP-13909

    +
    + +

    The performance of the disk_log has been + somewhat improved in some corner cases (big items), and + the documentation has been clarified.

    +

    + Own Id: OTP-14057 Aux Id: PR-1245

    +
    + +

    Functions for detecting changed code has been added. + code:modified_modules/0 returns all currently + loaded modules that have changed on disk. + code:module_status/1 returns the status for a + module. In the shell and in c module, mm/0 + is short for code:modified_modules/0, and + lm/0 reloads all currently loaded modules that + have changed on disk.

    +

    + Own Id: OTP-14059

    +
    + +

    + Introduce an event manager in Erlang to handle OS + signals. A subset of OS signals may be subscribed to and + those are described in the Kernel application.

    +

    + Own Id: OTP-14186

    +
    + +

    Sockets can now be bound to device (SO_BINDTODEVICE) + on platforms where it is supported.

    This has + been implemented e.g to support VRF-Lite under Linux; see + + VRF , and GitHub pull request #1326. +

    +

    + Own Id: OTP-14357 Aux Id: PR-1326

    +
    + +

    + Added option to store shell_history on disk so that the + history can be reused between sessions.

    +

    + Own Id: OTP-14409 Aux Id: PR-1420

    +
    + +

    The size of crash reports created by + gen_server, gen_statem and proc_lib + is limited with aid of the Kernel application variable + error_logger_format_depth. The purpose is to limit + the size of the error_logger process when + processes with huge message queues or states crash.

    +

    The new function + error_logger:get_format_depth/0 can be used to + retrieve the value of the Kernel application variable + error_logger_format_depth.

    +

    + Own Id: OTP-14417

    +
    + +

    One of the ETS tables used by the global + module is created with {read_concurrency, true} in + order to reduce contention.

    +

    + Own Id: OTP-14419

    +
    + +

    + Warnings have been added to the relevant documentation + about not using un-secure distributed nodes in exposed + environments.

    +

    + Own Id: OTP-14425

    +
    +
    +
    + +
    +
    Kernel 5.2
    Fixed Bugs and Malfunctions diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 76b020e8ed..4edecd8969 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 5.2 +KERNEL_VSN = 5.3 -- cgit v1.2.3 From 32275a2fc0b86d1f1b124706afc80f3ff92216eb Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Wed, 31 May 2017 16:21:00 +0200 Subject: Revert "Prepare release" This reverts commit eaf8ca41dfa4850437ad270d3897399c9358ced0. --- lib/kernel/doc/src/notes.xml | 135 ------------------------------------------- lib/kernel/vsn.mk | 2 +- 2 files changed, 1 insertion(+), 136 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 975518d791..7127a59a0c 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,141 +31,6 @@

    This document describes the changes made to the Kernel application.

    -
    Kernel 5.3 - -
    Fixed Bugs and Malfunctions - - -

    - Fix bug where gethostname would incorrectly fail with - enametoolong on Linux.

    -

    - Own Id: OTP-14310

    -
    - -

    - Fix bug causing code:is_module_native to falsely - return true when local call trace is enabled for - the module.

    -

    - Own Id: OTP-14390

    -
    - -

    - Add early reject of invalid node names from distributed - nodes.

    -

    - Own Id: OTP-14426

    -
    -
    -
    - - -
    Improvements and New Features - - -

    - Since Unicode is now allowed in atoms an extra check is - needed for node names, which are restricted to Latin-1.

    -

    - Own Id: OTP-13805

    -
    - -

    Replaced usage of deprecated symbolic time - unit representations.

    -

    - Own Id: OTP-13831 Aux Id: OTP-13735

    -
    - -

    file:write_file(Name, Data, [raw]) would turn - Data into a single binary before writing. This - meant it could not take advantage of the writev() - system call if it was given a list of binaries and told - to write with raw mode.

    -

    - Own Id: OTP-13909

    -
    - -

    The performance of the disk_log has been - somewhat improved in some corner cases (big items), and - the documentation has been clarified.

    -

    - Own Id: OTP-14057 Aux Id: PR-1245

    -
    - -

    Functions for detecting changed code has been added. - code:modified_modules/0 returns all currently - loaded modules that have changed on disk. - code:module_status/1 returns the status for a - module. In the shell and in c module, mm/0 - is short for code:modified_modules/0, and - lm/0 reloads all currently loaded modules that - have changed on disk.

    -

    - Own Id: OTP-14059

    -
    - -

    - Introduce an event manager in Erlang to handle OS - signals. A subset of OS signals may be subscribed to and - those are described in the Kernel application.

    -

    - Own Id: OTP-14186

    -
    - -

    Sockets can now be bound to device (SO_BINDTODEVICE) - on platforms where it is supported.

    This has - been implemented e.g to support VRF-Lite under Linux; see - - VRF , and GitHub pull request #1326. -

    -

    - Own Id: OTP-14357 Aux Id: PR-1326

    -
    - -

    - Added option to store shell_history on disk so that the - history can be reused between sessions.

    -

    - Own Id: OTP-14409 Aux Id: PR-1420

    -
    - -

    The size of crash reports created by - gen_server, gen_statem and proc_lib - is limited with aid of the Kernel application variable - error_logger_format_depth. The purpose is to limit - the size of the error_logger process when - processes with huge message queues or states crash.

    -

    The new function - error_logger:get_format_depth/0 can be used to - retrieve the value of the Kernel application variable - error_logger_format_depth.

    -

    - Own Id: OTP-14417

    -
    - -

    One of the ETS tables used by the global - module is created with {read_concurrency, true} in - order to reduce contention.

    -

    - Own Id: OTP-14419

    -
    - -

    - Warnings have been added to the relevant documentation - about not using un-secure distributed nodes in exposed - environments.

    -

    - Own Id: OTP-14425

    -
    -
    -
    - -
    -
    Kernel 5.2
    Fixed Bugs and Malfunctions diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 4edecd8969..76b020e8ed 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 5.3 +KERNEL_VSN = 5.2 -- cgit v1.2.3 From 95ebfa0b19bd4b1cac8a0eb98e775517ebb2ca6d Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Tue, 30 May 2017 17:19:45 +0200 Subject: Update inet:ntoa according to modern RFCs --- lib/kernel/src/inet_parse.erl | 24 +++++-- lib/kernel/test/inet_SUITE.erl | 158 +++++++++++++++++++++++++++-------------- 2 files changed, 123 insertions(+), 59 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/inet_parse.erl b/lib/kernel/src/inet_parse.erl index 0f5dc40553..f150521e92 100644 --- a/lib/kernel/src/inet_parse.erl +++ b/lib/kernel/src/inet_parse.erl @@ -714,7 +714,13 @@ ntoa({0,0,0,0,0,0,0,1}) -> "::1"; ntoa({0,0,0,0,0,0,A,B}) -> "::" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B); %% IPV4 non ipv6 host address ntoa({0,0,0,0,0,16#ffff,A,B}) -> - "::FFFF:" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B); + "::ffff:" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B); +%% RFC 2765 IPv4-translated address +ntoa({0,0,0,0,16#ffff,0,A,B}) -> + "::ffff:0:" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B); +%% RFC 6052 Well-known Prefix address +ntoa({16#64,16#ff9b,0,0,0,0,A,B}) -> + "64:ff9b::" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B); ntoa({_,_,_,_,_,_,_,_}=T) -> %% Find longest sequence of zeros, at least 2, to replace with "::" ntoa(tuple_to_list(T), []); @@ -780,9 +786,19 @@ dig_to_dec(X) -> integer_to_list((X bsr 8) band 16#ff) ++ "." ++ integer_to_list(X band 16#ff). -%% Convert a integer to hex string -dig_to_hex(X) -> - erlang:integer_to_list(X, 16). +%% Convert a integer to hex string (lowercase) +dig_to_hex(0) -> "0"; +dig_to_hex(X) when is_integer(X), 0 < X -> + dig_to_hex(X, ""). +%% +dig_to_hex(0, Acc) -> Acc; +dig_to_hex(X, Acc) -> + dig_to_hex( + X bsr 4, + [case X band 15 of + D when D < 10 -> D + $0; + D -> D - 10 + $a + end|Acc]). %% %% Count number of '.' in a name diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl index 97f789b61c..1d4efe17a5 100644 --- a/lib/kernel/test/inet_SUITE.erl +++ b/lib/kernel/test/inet_SUITE.erl @@ -447,91 +447,126 @@ parse_hosts(Config) when is_list(Config) -> inet_parse:resolv(ResolvErr1). parse_address(Config) when is_list(Config) -> - V4Strict = + V4Reversable = [{{0,0,0,0},"0.0.0.0"}, - {{1,2,3,4},"1.2.3.4"}, + {{1,2,3,4},"1.2.3.4"}, {{253,252,251,250},"253.252.251.250"}, {{1,2,255,254},"1.2.255.254"}], - V6Strict = + V6Reversable = [{{0,0,0,0,0,0,0,0},"::"}, + {{0,0,0,0,0,0,0,1},"::1"}, + {{0,0,0,0,0,0,0,2},"::0.0.0.2"}, {{15,0,0,0,0,0,0,2},"f::2"}, - {{15,16#f11,0,0,0,0,256,2},"f:f11::0100:2"}, - {{0,0,0,0,0,0,0,16#17},"::17"}, - {{16#700,0,0,0,0,0,0,0},"0700::"}, - {{0,0,0,0,0,0,2,1},"::2:1"}, + {{15,16#f11,0,0,0,0,256,2},"f:f11::100:2"}, + {{16#700,0,0,0,0,0,0,0},"700::"}, + {{0,0,0,0,0,0,2,1},"::0.2.0.1"}, {{0,0,0,0,0,3,2,1},"::3:2:1"}, {{0,0,0,0,4,3,2,1},"::4:3:2:1"}, {{0,0,0,5,4,3,2,1},"::5:4:3:2:1"}, {{0,0,6,5,4,3,2,1},"::6:5:4:3:2:1"}, - {{0,7,6,5,4,3,2,1},"::7:6:5:4:3:2:1"}, + {{0,7,6,5,4,3,2,1},"0:7:6:5:4:3:2:1"}, {{7,0,0,0,0,0,0,0},"7::"}, {{7,6,0,0,0,0,0,0},"7:6::"}, {{7,6,5,0,0,0,0,0},"7:6:5::"}, {{7,6,5,4,0,0,0,0},"7:6:5:4::"}, {{7,6,5,4,3,0,0,0},"7:6:5:4:3::"}, {{7,6,5,4,3,2,0,0},"7:6:5:4:3:2::"}, - {{7,6,5,4,3,2,1,0},"7:6:5:4:3:2:1::"}, + {{7,6,5,4,3,2,1,0},"7:6:5:4:3:2:1:0"}, + {{0,0,6,5,4,3,0,0},"::6:5:4:3:0:0"}, + {{0,0,6,5,4,0,0,0},"0:0:6:5:4::"}, + {{8,0,0,5,4,0,0,1},"8::5:4:0:0:1"}, + {{8,0,0,5,0,0,0,1},"8:0:0:5::1"}, + {{0,7,6,5,4,3,2,0},"0:7:6:5:4:3:2:0"}, + {{0,0,6,5,4,3,0,0},"::6:5:4:3:0:0"}, + {{0,0,0,5,4,0,0,0},"::5:4:0:0:0"}, + {{0,0,0,0,4,0,0,0},"::4:0:0:0"}, + {{0,0,0,5,0,0,0,0},"0:0:0:5::"}, {{16#c11,16#c22,16#5c33,16#c440,16#55c0,16#c66c,16#77,16#88}, - "c11:0c22:5c33:c440:55c0:c66c:77:0088"}, + "c11:c22:5c33:c440:55c0:c66c:77:88"}, + {{0,16#c22,16#5c33,16#c440,16#55c0,16#c66c,16#77,16#88}, + "0:c22:5c33:c440:55c0:c66c:77:88"}, {{16#c11,0,16#5c33,16#c440,16#55c0,16#c66c,16#77,16#88}, - "c11::5c33:c440:55c0:c66c:77:0088"}, + "c11:0:5c33:c440:55c0:c66c:77:88"}, {{16#c11,16#c22,0,16#c440,16#55c0,16#c66c,16#77,16#88}, - "c11:0c22::c440:55c0:c66c:77:0088"}, + "c11:c22:0:c440:55c0:c66c:77:88"}, {{16#c11,16#c22,16#5c33,0,16#55c0,16#c66c,16#77,16#88}, - "c11:0c22:5c33::55c0:c66c:77:0088"}, + "c11:c22:5c33:0:55c0:c66c:77:88"}, {{16#c11,16#c22,16#5c33,16#c440,0,16#c66c,16#77,16#88}, - "c11:0c22:5c33:c440::c66c:77:0088"}, + "c11:c22:5c33:c440:0:c66c:77:88"}, {{16#c11,16#c22,16#5c33,16#c440,16#55c0,0,16#77,16#88}, - "c11:0c22:5c33:c440:55c0::77:0088"}, + "c11:c22:5c33:c440:55c0:0:77:88"}, {{16#c11,16#c22,16#5c33,16#c440,16#55c0,16#c66c,0,16#88}, - "c11:0c22:5c33:c440:55c0:c66c::0088"}, + "c11:c22:5c33:c440:55c0:c66c:0:88"}, + {{16#c11,16#c22,16#5c33,16#c440,16#55c0,16#c66c,16#77,0}, + "c11:c22:5c33:c440:55c0:c66c:77:0"}, + {{0,0,16#5c33,16#c440,16#55c0,16#c66c,16#77,16#88}, + "::5c33:c440:55c0:c66c:77:88"}, {{16#c11,0,0,16#c440,16#55c0,16#c66c,16#77,16#88}, - "c11::c440:55c0:c66c:77:0088"}, + "c11::c440:55c0:c66c:77:88"}, {{16#c11,16#c22,0,0,16#55c0,16#c66c,16#77,16#88}, - "c11:0c22::55c0:c66c:77:0088"}, + "c11:c22::55c0:c66c:77:88"}, {{16#c11,16#c22,16#5c33,0,0,16#c66c,16#77,16#88}, - "c11:0c22:5c33::c66c:77:0088"}, + "c11:c22:5c33::c66c:77:88"}, {{16#c11,16#c22,16#5c33,16#c440,0,0,16#77,16#88}, - "c11:0c22:5c33:c440::77:0088"}, + "c11:c22:5c33:c440::77:88"}, {{16#c11,16#c22,16#5c33,16#c440,16#55c0,0,0,16#88}, - "c11:0c22:5c33:c440:55c0::0088"}, + "c11:c22:5c33:c440:55c0::88"}, + {{16#c11,16#c22,16#5c33,16#c440,16#55c0,16#c66c,0,0}, + "c11:c22:5c33:c440:55c0:c66c::"}, + {{0,0,0,16#c440,16#55c0,16#c66c,16#77,16#88}, + "::c440:55c0:c66c:77:88"}, {{16#c11,0,0,0,16#55c0,16#c66c,16#77,16#88}, - "c11::55c0:c66c:77:0088"}, + "c11::55c0:c66c:77:88"}, {{16#c11,16#c22,0,0,0,16#c66c,16#77,16#88}, - "c11:0c22::c66c:77:0088"}, + "c11:c22::c66c:77:88"}, {{16#c11,16#c22,16#5c33,0,0,0,16#77,16#88}, - "c11:0c22:5c33::77:0088"}, + "c11:c22:5c33::77:88"}, {{16#c11,16#c22,16#5c33,16#c440,0,0,0,16#88}, - "c11:0c22:5c33:c440::0088"}, + "c11:c22:5c33:c440::88"}, + {{16#c11,16#c22,16#5c33,16#c440,16#55c0,0,0,0}, + "c11:c22:5c33:c440:55c0::"}, + {{0,0,0,0,16#55c0,16#c66c,16#77,16#88}, + "::55c0:c66c:77:88"}, {{16#c11,0,0,0,0,16#c66c,16#77,16#88}, - "c11::c66c:77:0088"}, + "c11::c66c:77:88"}, {{16#c11,16#c22,0,0,0,0,16#77,16#88}, - "c11:0c22::77:0088"}, + "c11:c22::77:88"}, {{16#c11,16#c22,16#5c33,0,0,0,0,16#88}, - "c11:0c22:5c33::0088"}, + "c11:c22:5c33::88"}, + {{16#c11,16#c22,16#5c33,16#c440,0,0,0,0}, + "c11:c22:5c33:c440::"}, + {{0,0,0,0,0,16#c66c,16#77,16#88}, + "::c66c:77:88"}, {{16#c11,0,0,0,0,0,16#77,16#88}, - "c11::77:0088"}, + "c11::77:88"}, {{16#c11,16#c22,0,0,0,0,0,16#88}, - "c11:0c22::0088"}, - {{0,0,0,0,0,65535,258,65534},"::FFFF:1.2.255.254"}, + "c11:c22::88"}, + {{16#c11,16#c22,16#5c33,0,0,0,0,0}, + "c11:c22:5c33::"}, + {{0,0,0,0,0,65535,258,65534},"::ffff:1.2.255.254"}, {{16#ffff,16#ffff,16#ffff,16#ffff,16#ffff,16#ffff,16#ffff,16#ffff}, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"} - |[{{D2,0,0,0,0,P,(D1 bsl 8) bor D2,(D3 bsl 8) bor D4}, - erlang:integer_to_list(D2, 16)++"::"++Q++S} - || {{D1,D2,D3,D4},S} <- V4Strict, - {P,Q} <- [{0,""},{16#17,"17:"},{16#ff0,"0ff0:"}]]], + |[{list_to_tuple(P++[(D1 bsl 8) bor D2,(D3 bsl 8) bor D4]), + Q++S} + || {{D1,D2,D3,D4},S} <- + tl(V4Reversable), + {P,Q} <- + [{[0,0,0,0,0,16#ffff],"::ffff:"}, + {[0,0,0,0,16#ffff,0],"::ffff:0:"}, + {[16#64,16#ff9b,0,0,0,0],"64:ff9b::"}, + {[0,0,0,0,0,0],"::"}]]], V4Sloppy = [{{10,1,16#98,16#76},"10.0x019876"}, {{8#12,1,8#130,8#321},"012.01.054321"}, - {{255,255,255,255},"255.255.255.0377"}, - {{255,255,255,255},"0Xff.000000000377.0x0000ff.255"}, - {{255,255,255,255},"255.255.65535"}, - {{255,255,255,255},"255.0xFF.0177777"}, - {{255,255,255,255},"255.16777215"}, - {{255,255,255,255},"00377.0XFFFFFF"}, - {{255,255,255,255},"4294967295"}, - {{255,255,255,255},"0xffffffff"}, - {{255,255,255,255},"00000000000037777777777"}, + {{252,253,254,255},"252.253.254.0377"}, + {{252,253,254,255},"0Xfc.000000000375.0x0000fe.255"}, + {{252,253,254,255},"252.253.65279"}, + {{252,253,254,255},"252.0xFD.0177377"}, + {{252,253,254,255},"252.16645887"}, + {{252,253,254,255},"00374.0XFDFEFF"}, + {{252,253,254,255},"4244504319"}, + {{252,253,254,255},"0xfcfdfeff"}, + {{252,253,254,255},"00000000000037477377377"}, {{16#12,16#34,16#56,16#78},"0x12345678"}, {{16#12,16#34,16#56,16#78},"0x12.0x345678"}, {{16#12,16#34,16#56,16#78},"0x12.0X34.0x5678"}, @@ -543,8 +578,13 @@ parse_address(Config) when is_list(Config) -> {{0,0,0,0},"0.00.0.0"}, {{0,0,0,0},"0.0.000000000000.0"}], V6Sloppy = - [{{0,0,0,0,0,65535,(D1 bsl 8) bor D2,(D3 bsl 8) bor D4},S} - || {{D1,D2,D3,D4},S} <- V4Strict++V4Sloppy], + [{{16#a,16#b,16#c,16#0,16#0,16#d,16#e,16#f},"A:B:C::d:e:f"}] + ++ + [{{P,0,0,0,0,D2,(D1 bsl 8) bor D2,(D3 bsl 8) bor D4}, + Q++erlang:integer_to_list(D2, 16)++":"++S} + || {{D1,D2,D3,D4},S} <- V4Reversable, + {P,Q} <- + [{16#2001,"2001::"},{16#177,"177::"},{16#ff0,"Ff0::"}]], V4Err = ["0.256.0.1", "1.2.3.4.5", @@ -588,28 +628,36 @@ parse_address(Config) when is_list(Config) -> "fec0::fFfF:127.0.0.1."], t_parse_address (parse_ipv6_address, - V6Strict++V6Sloppy++V6Err++V4Err), + false, + V6Reversable++V6Sloppy++V6Err++V4Err), t_parse_address (parse_ipv6strict_address, - V6Strict++V6Err++V4Err++[S || {_,S} <- V6Sloppy]), + true, + V6Reversable++V6Err++V4Err), t_parse_address (parse_ipv4_address, - V4Strict++V4Sloppy++V4Err++V6Err++[S || {_,S} <- V6Strict]), + false, + V4Reversable++V4Sloppy++V4Err++V6Err++[S || {_,S} <- V6Reversable]), t_parse_address (parse_ipv4strict_address, - V4Strict++V4Err++V6Err++[S || {_,S} <- V4Sloppy++V6Strict]). + true, + V4Reversable++V4Err++V6Err++[S || {_,S} <- V4Sloppy++V6Reversable]). -t_parse_address(Func, []) -> +t_parse_address(Func, _Reversable, []) -> io:format("~p done.~n", [Func]), ok; -t_parse_address(Func, [{Addr,String}|L]) -> +t_parse_address(Func, Reversable, [{Addr,String}|L]) -> io:format("~p = ~p.~n", [Addr,String]), {ok,Addr} = inet:Func(String), - t_parse_address(Func, L); -t_parse_address(Func, [String|L]) -> + case Reversable of + true ->String = inet:ntoa(Addr); + false -> ok + end, + t_parse_address(Func, Reversable, L); +t_parse_address(Func, Reversable, [String|L]) -> io:format("~p.~n", [String]), {error,einval} = inet:Func(String), - t_parse_address(Func, L). + t_parse_address(Func, Reversable, L). parse_strict_address(Config) when is_list(Config) -> {ok, {127,0,0,1}} = -- cgit v1.2.3 From 923f9ac453af420eda69c4727b3b29ae7a91f2f1 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 22 May 2017 15:15:27 +0200 Subject: kernel: Iterate for correct time in os_SUITE:perf_counter --- lib/kernel/test/os_SUITE.erl | 47 +++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 20 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl index 3cbb75a633..1233e362f2 100644 --- a/lib/kernel/test/os_SUITE.erl +++ b/lib/kernel/test/os_SUITE.erl @@ -319,31 +319,38 @@ perf_counter_api(_Config) -> true = is_integer(os:perf_counter()), true = os:perf_counter() > 0, - T1 = os:perf_counter(), + Conv = fun(T1, T2) -> + erlang:convert_time_unit(T2 - T1, perf_counter, nanosecond) + end, + + do_perf_counter_test([], Conv, 120000000, 80000000), + do_perf_counter_test([1000], fun(T1, T2) -> T2 - T1 end, 120, 80). + +do_perf_counter_test(CntArgs, Conv, Upper, Lower) -> + %% We run the test multiple times to try to get a somewhat + %% stable value... what does this test? That the + %% calculate_perf_counter_unit in sys_time.c works somewhat ok. + do_perf_counter_test(CntArgs, Conv, Upper, Lower, 10). + +do_perf_counter_test(CntArgs, _Conv, Upper, Lower, 0) -> + ct:fail("perf_counter_test ~p ~p ~p",[CntArgs, Upper, Lower]); +do_perf_counter_test(CntArgs, Conv, Upper, Lower, Iters) -> + + T1 = apply(os, perf_counter, CntArgs), timer:sleep(100), - T2 = os:perf_counter(), - TsDiff = erlang:convert_time_unit(T2 - T1, perf_counter, nanosecond), - ct:pal("T1: ~p~n" + T2 = apply(os, perf_counter, CntArgs), + TsDiff = Conv(T1, T2), + ct:log("T1: ~p~n" "T2: ~p~n" "TsDiff: ~p~n", [T1,T2,TsDiff]), - %% We allow a 15% diff - true = TsDiff < 115000000, - true = TsDiff > 85000000, - - T1Ms = os:perf_counter(1000), - timer:sleep(100), - T2Ms = os:perf_counter(1000), - MsDiff = T2Ms - T1Ms, - ct:pal("T1Ms: ~p~n" - "T2Ms: ~p~n" - "MsDiff: ~p~n", - [T1Ms,T2Ms,MsDiff]), - - %% We allow a 15% diff - true = MsDiff < 115, - true = MsDiff > 85. + if + TsDiff < Upper, TsDiff > Lower -> + ok; + true -> + do_perf_counter_test(CntArgs, Conv, Upper, Lower, Iters-1) + end. %% Util functions -- cgit v1.2.3 From 4e38e959a1ecfaa5fa5f3571599a60ab0a88c712 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 22 May 2017 11:25:20 +0200 Subject: erts: Fix sendfile closeduring scenario on sunos On Solaris, giving a too long sfv_len results in an EINVAL error, but data is still transmitted and len is correctly. So we translate this to a success with that amount of data sent. This may hide some other errors that causes EINVAL, but it is the best we can do for now. --- lib/kernel/test/sendfile_SUITE.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/sendfile_SUITE.erl b/lib/kernel/test/sendfile_SUITE.erl index 2673c38494..e839959623 100644 --- a/lib/kernel/test/sendfile_SUITE.erl +++ b/lib/kernel/test/sendfile_SUITE.erl @@ -100,13 +100,13 @@ init_per_testcase(TC,Config) when TC == t_sendfile_recvduring; %% Check if sendfile is supported on this platform case catch sendfile_send(Send) of ok -> - Config; + init_per_testcase(t_sendfile, Config); Error -> ct:log("Error: ~p",[Error]), {skip,"Not supported"} end; init_per_testcase(_Tc,Config) -> - Config. + Config ++ [{sendfile_opts,[{use_threads,false}]}]. t_sendfile_small(Config) when is_list(Config) -> -- cgit v1.2.3 From 33ed2e24a516e1024fa984a96ddab90a01a97475 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Wed, 7 Jun 2017 10:10:39 +0200 Subject: Adjust inet:ntoa/1 to RFC5952, but not deeper There are a multitude of RFC:s that point to each other and some of them suggest different addresses with IPv4 suffixes. Use the IPv4 suffix text representation only for the old well known ::a.b.c.d and ::ffff:a.b.c.d prefixes. The others seems to be moving targets. --- lib/kernel/src/inet_parse.erl | 6 ------ lib/kernel/test/inet_SUITE.erl | 2 -- 2 files changed, 8 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/inet_parse.erl b/lib/kernel/src/inet_parse.erl index f150521e92..06e1048afe 100644 --- a/lib/kernel/src/inet_parse.erl +++ b/lib/kernel/src/inet_parse.erl @@ -715,12 +715,6 @@ ntoa({0,0,0,0,0,0,A,B}) -> "::" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B); %% IPV4 non ipv6 host address ntoa({0,0,0,0,0,16#ffff,A,B}) -> "::ffff:" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B); -%% RFC 2765 IPv4-translated address -ntoa({0,0,0,0,16#ffff,0,A,B}) -> - "::ffff:0:" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B); -%% RFC 6052 Well-known Prefix address -ntoa({16#64,16#ff9b,0,0,0,0,A,B}) -> - "64:ff9b::" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B); ntoa({_,_,_,_,_,_,_,_}=T) -> %% Find longest sequence of zeros, at least 2, to replace with "::" ntoa(tuple_to_list(T), []); diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl index 1d4efe17a5..bd842da4b3 100644 --- a/lib/kernel/test/inet_SUITE.erl +++ b/lib/kernel/test/inet_SUITE.erl @@ -552,8 +552,6 @@ parse_address(Config) when is_list(Config) -> tl(V4Reversable), {P,Q} <- [{[0,0,0,0,0,16#ffff],"::ffff:"}, - {[0,0,0,0,16#ffff,0],"::ffff:0:"}, - {[16#64,16#ff9b,0,0,0,0],"64:ff9b::"}, {[0,0,0,0,0,0],"::"}]]], V4Sloppy = [{{10,1,16#98,16#76},"10.0x019876"}, -- cgit v1.2.3 From 06e679b3652f6757c7d033d97ec9fb4dfc3f532c Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Fri, 9 Jun 2017 12:15:00 +0200 Subject: kernel: Improve handling of Unicode filenames --- lib/kernel/src/group_history.erl | 4 ++-- lib/kernel/src/inet_tcp_dist.erl | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/group_history.erl b/lib/kernel/src/group_history.erl index 5ca5e2d1dd..91f3663cc5 100644 --- a/lib/kernel/src/group_history.erl +++ b/lib/kernel/src/group_history.erl @@ -218,7 +218,7 @@ handle_open_error({invalid_header, Term}) -> [Term]); handle_open_error({file_error, FileName, Reason}) -> show('$#erlang-history-file-error', - "Error handling File ~s. Reason: ~p~n" + "Error handling File ~ts. Reason: ~p~n" "History logging will be disabled.~n", [FileName, Reason]); handle_open_error(Err) -> @@ -309,7 +309,7 @@ show_rename_warning() -> show_invalid_file_warning(FileName) -> show('$#erlang-history-invalid-file', - "Shell history expects to be able to use the file ~s " + "Shell history expects to be able to use the file ~ts " "which currently exists and is not a file usable for " "history logging purposes. History logging will be " "disabled.~n", [FileName]). diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl index 8c8fe86811..e3fdb1bb22 100644 --- a/lib/kernel/src/inet_tcp_dist.erl +++ b/lib/kernel/src/inet_tcp_dist.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. @@ -390,14 +390,14 @@ splitnode(Driver, Node, LongOrShortNames) -> error_msg("** System running to use " "fully qualified " "hostnames **~n" - "** Hostname ~s is illegal **~n", + "** Hostname ~ts is illegal **~n", [Host]), ?shutdown(Node) end; L when length(L) > 1, LongOrShortNames =:= shortnames -> error_msg("** System NOT running to use fully qualified " "hostnames **~n" - "** Hostname ~s is illegal **~n", + "** Hostname ~ts is illegal **~n", [Host]), ?shutdown(Node); _ -> -- cgit v1.2.3 From 052863543868b442fa649ee99ea8e3e454c838b1 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Wed, 7 Jun 2017 15:36:49 +0200 Subject: Accept IPv6 address %suffixes when parsing This only implements decimal suffixes, and furthermore only with a leading "0". A complete implementation would probably allow one and probably two digit suffixes without. But what primarily is missing is translating interface names to and from interface indexes. This also only implements the parsing, and uses the FreeBSD trick of squeezing in the Scope Id in the second 16-bit word of the fe80::/64 or ff02:/64 address prefix. But inet_drv is not prepared to handle this word, so it might only work on FreeBSD, not being supported even there... So inet_drv needs to handle this too. --- lib/kernel/src/inet_parse.erl | 80 ++++++++++++++++++++++++++++++++++++++---- lib/kernel/test/inet_SUITE.erl | 4 ++- 2 files changed, 77 insertions(+), 7 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/inet_parse.erl b/lib/kernel/src/inet_parse.erl index 06e1048afe..29804dc50b 100644 --- a/lib/kernel/src/inet_parse.erl +++ b/lib/kernel/src/inet_parse.erl @@ -644,8 +644,12 @@ ipv6_addr(Cs) -> ipv6_addr(hex(Cs), [], 0). %% Before "::" +ipv6_addr({Cs0,"%"++Cs1}, A, N) when N == 7 -> + ipv6_addr_scope(Cs1, [hex_to_int(Cs0)|A], [], N+1, []); ipv6_addr({Cs0,[]}, A, N) when N == 7 -> ipv6_addr_done([hex_to_int(Cs0)|A]); +ipv6_addr({Cs0,"::%"++Cs1}, A, N) when N =< 6 -> + ipv6_addr_scope(Cs1, [hex_to_int(Cs0)|A], [], N+1, []); ipv6_addr({Cs0,"::"}, A, N) when N =< 6 -> ipv6_addr_done([hex_to_int(Cs0)|A], [], N+1); ipv6_addr({Cs0,"::"++Cs1}, A, N) when N =< 5 -> @@ -658,6 +662,8 @@ ipv6_addr(_, _, _) -> erlang:error(badarg). %% After "::" +ipv6_addr({Cs0,"%"++Cs1}, A, B, N) when N =< 6 -> + ipv6_addr_scope(Cs1, A, [hex_to_int(Cs0)|B], N+1, []); ipv6_addr({Cs0,[]}, A, B, N) when N =< 6 -> ipv6_addr_done(A, [hex_to_int(Cs0)|B], N+1); ipv6_addr({Cs0,":"++Cs1}, A, B, N) when N =< 5 -> @@ -667,6 +673,43 @@ ipv6_addr({Cs0,"."++_=Cs1}, A, B, N) when N =< 5 -> ipv6_addr(_, _, _, _) -> erlang:error(badarg). +%% After "%" +ipv6_addr_scope([], Ar, Br, N, Sr) -> + ScopeId = + case lists:reverse(Sr) of + %% Empty scope id + "" -> 0; + %% Scope id starts with 0 + "0"++S -> dec16(S); + _ -> 0 + end, + %% Suggested formats for scope id parsing: + %% "" -> "0" + %% "0" -> Scope id 0 + %% "1" - "9", "10" - "99" -> "0"++S + %% "0"++DecimalScopeId -> decimal scope id + %% "25"++PercentEncoded -> Percent encoded interface name + %% S -> Interface name (Unicode?) + %% Missing: translation from interface name into integer scope id. + %% XXX: scope id is actually 32 bit, but we only have room for + %% 16 bit in the second address word - ignore or fix (how)? + ipv6_addr_scope(ScopeId, Ar, Br, N); +ipv6_addr_scope([C|Cs], Ar, Br, N, Sr) -> + ipv6_addr_scope(Cs, Ar, Br, N, [C|Sr]). +%% +ipv6_addr_scope(ScopeId, [P], Br, N) + when N =< 7, P =:= 16#fe80; + N =< 7, P =:= 16#ff02 -> + %% Optimized special case + ipv6_addr_done([ScopeId,P], Br, N+1); +ipv6_addr_scope(ScopeId, Ar, Br, N) -> + case lists:reverse(Br++dup(8-N, 0, Ar)) of + [P,0|Xs] when P =:= 16#fe80; P =:= 16#ff02 -> + list_to_tuple([P,ScopeId|Xs]); + _ -> + erlang:error(badarg) + end. + ipv6_addr_done(Ar, Br, N, {D1,D2,D3,D4}) -> ipv6_addr_done(Ar, [((D3 bsl 8) bor D4),((D1 bsl 8) bor D2)|Br], N+2). @@ -690,6 +733,19 @@ hex(Cs, [_|_]=R, _) when is_list(Cs) -> hex(_, _, _) -> erlang:error(badarg). +%% Parse a reverse decimal integer string, empty is 0 +dec16(Cs) -> dec16(Cs, 0). +%% +dec16([], I) -> I; +dec16([C|Cs], I) when C >= $0, C =< $9 -> + case 10*I + (C - $0) of + J when 16#ffff < J -> + erlang:error(badarg); + J -> + dec16(Cs, J) + end; +dec16(_, _) -> erlang:error(badarg). + %% Hex string to integer hex_to_int(Cs) -> erlang:list_to_integer(Cs, 16). @@ -703,7 +759,7 @@ dup(N, E, L) when is_integer(N), N >= 1 -> %% Convert IPv4 address to ascii %% Convert IPv6 / IPV4 address to ascii (plain format) -ntoa({A,B,C,D}) -> +ntoa({A,B,C,D}) when (A band B band C band D band (bnot 16#ff)) =:= 0 -> integer_to_list(A) ++ "." ++ integer_to_list(B) ++ "." ++ integer_to_list(C) ++ "." ++ integer_to_list(D); %% ANY @@ -711,13 +767,25 @@ ntoa({0,0,0,0,0,0,0,0}) -> "::"; %% LOOPBACK ntoa({0,0,0,0,0,0,0,1}) -> "::1"; %% IPV4 ipv6 host address -ntoa({0,0,0,0,0,0,A,B}) -> "::" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B); +ntoa({0,0,0,0,0,0,A,B}) when (A band B band (bnot 16#ffff)) =:= 0 -> + "::" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B); %% IPV4 non ipv6 host address -ntoa({0,0,0,0,0,16#ffff,A,B}) -> +ntoa({0,0,0,0,0,16#ffff,A,B}) when (A band B band (bnot 16#ffff)) =:= 0 -> "::ffff:" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B); -ntoa({_,_,_,_,_,_,_,_}=T) -> - %% Find longest sequence of zeros, at least 2, to replace with "::" - ntoa(tuple_to_list(T), []); +ntoa({A,B,C,D,E,F,G,H}) + when (A band B band C band D band E band F band G band H band + (bnot 16#ffff)) =:= 0 -> + if + A =:= 16#fe80, B =/= 0; + A =:= 16#ff02, B =/= 0 -> + %% Find longest sequence of zeros, at least 2, + %% to replace with "::" + ntoa([A,0,C,D,E,F,G,H], []) ++ "%0" ++ integer_to_list(B); + true -> + %% Find longest sequence of zeros, at least 2, + %% to replace with "::" + ntoa([A,B,C,D,E,F,G,H], []) + end; ntoa(_) -> {error, einval}. diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl index bd842da4b3..3b502be8b8 100644 --- a/lib/kernel/test/inet_SUITE.erl +++ b/lib/kernel/test/inet_SUITE.erl @@ -544,6 +544,7 @@ parse_address(Config) when is_list(Config) -> {{16#c11,16#c22,16#5c33,0,0,0,0,0}, "c11:c22:5c33::"}, {{0,0,0,0,0,65535,258,65534},"::ffff:1.2.255.254"}, + {{16#fe80,12345,0,0,0,0,0,16#12},"fe80::12%012345"}, {{16#ffff,16#ffff,16#ffff,16#ffff,16#ffff,16#ffff,16#ffff,16#ffff}, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"} |[{list_to_tuple(P++[(D1 bsl 8) bor D2,(D3 bsl 8) bor D4]), @@ -576,7 +577,8 @@ parse_address(Config) when is_list(Config) -> {{0,0,0,0},"0.00.0.0"}, {{0,0,0,0},"0.0.000000000000.0"}], V6Sloppy = - [{{16#a,16#b,16#c,16#0,16#0,16#d,16#e,16#f},"A:B:C::d:e:f"}] + [{{16#a,16#b,16#c,16#0,16#0,16#d,16#e,16#f},"A:B:C::d:e:f"}, + {{16#fe80,0,0,0,0,0,0,16#12},"fe80::12%XXXXXXX"}] ++ [{{P,0,0,0,0,D2,(D1 bsl 8) bor D2,(D3 bsl 8) bor D4}, Q++erlang:integer_to_list(D2, 16)++":"++S} -- cgit v1.2.3 From b3a234b9f22990cbd1fd7add092a631b461ee7a4 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 13 Jun 2017 15:52:42 +0200 Subject: Revert "kernel: Try mend disk_log whitebox tests" This reverts commit 5d9bb41114544c9205a8b8f26642bad8231e8d4e. --- lib/kernel/test/disk_log_SUITE.erl | 50 +++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 25 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/disk_log_SUITE.erl b/lib/kernel/test/disk_log_SUITE.erl index 2b11a0381f..fe2fc778f2 100644 --- a/lib/kernel/test/disk_log_SUITE.erl +++ b/lib/kernel/test/disk_log_SUITE.erl @@ -481,7 +481,7 @@ halt_ro_crash(Conf) when is_list(Conf) -> %% This is how it was before R6B: %% {C1,T1,15} = disk_log:chunk(a,start), %% {C2,T2} = disk_log:chunk(a,C1), - {C1,_OneItem,7476} = disk_log:chunk(a,start), + {C1,_OneItem,7478} = disk_log:chunk(a,start), {C2, [], 7} = disk_log:chunk(a,C1), eof = disk_log:chunk(a,C2), ok = disk_log:close(a), @@ -2503,8 +2503,8 @@ error_repair(Conf) when is_list(Conf) -> ok = disk_log:close(n), BadFile = add_ext(File, 2), % current file set_opened(BadFile), - crash(BadFile, 26), % the binary is now invalid - {repaired,n,{recovered,0},{badbytes,24}} = + crash(BadFile, 28), % the binary is now invalid + {repaired,n,{recovered,0},{badbytes,26}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, internal}, {size, {40,No}}]), ok = disk_log:close(n), @@ -2521,8 +2521,8 @@ error_repair(Conf) when is_list(Conf) -> ok = disk_log:close(n), BadFile2 = add_ext(File, 1), % current file set_opened(BadFile2), - crash(BadFile2, 47), % the second binary is now invalid - {repaired,n,{recovered,1},{badbytes,24}} = + crash(BadFile2, 51), % the second binary is now invalid + {repaired,n,{recovered,1},{badbytes,26}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, internal}, {size, {4000,No}}]), ok = disk_log:close(n), @@ -2580,7 +2580,7 @@ error_repair(Conf) when is_list(Conf) -> ok = disk_log:close(n), set_opened(File), crash(File, 30), - {repaired,n,{recovered,3},{badbytes,15}} = + {repaired,n,{recovered,3},{badbytes,16}} = disk_log:open([{name, n}, {file, File}, {type, halt}, {format, internal},{repair,true}, {quiet, true}, {head_func, {?MODULE, head_fun, [{ok,"head"}]}}]), @@ -2807,7 +2807,7 @@ chunk(Conf) when is_list(Conf) -> ok = disk_log:log_terms(n, [{some,terms}]), % second file full 2 = curf(n), BadFile = add_ext(File, 1), - crash(BadFile, 26), % the _binary_ is now invalid + crash(BadFile, 28), % the _binary_ is now invalid {error, {corrupt_log_file, BFile}} = disk_log:chunk(n, start, 1), BadFile = BFile, ok = disk_log:close(n), @@ -2817,7 +2817,7 @@ chunk(Conf) when is_list(Conf) -> {format, internal}]), ok = disk_log:log_terms(n, [{this,is}]), ok = disk_log:sync(n), - crash(File, 26), % the _binary_ is now invalid + crash(File, 28), % the _binary_ is now invalid {error, {corrupt_log_file, File2}} = disk_log:chunk(n, start, 1), crash(File, 10), {error,{corrupt_log_file,_}} = disk_log:bchunk(n, start, 1), @@ -2911,8 +2911,8 @@ chunk(Conf) when is_list(Conf) -> {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, internal}, {mode, read_only}]), CrashFile = add_ext(File, 1), - crash(CrashFile, 46), % the binary term {some,terms} is now bad - {H1, [{this,is}], 16} = disk_log:chunk(n, start, 10), + crash(CrashFile, 51), % the binary term {some,terms} is now bad + {H1, [{this,is}], 18} = disk_log:chunk(n, start, 10), {H2, [{on,a},{wrap,file}]} = disk_log:chunk(n, H1), eof = disk_log:chunk(n, H2), ok = disk_log:close(n), @@ -2926,8 +2926,8 @@ chunk(Conf) when is_list(Conf) -> ok = disk_log:close(n), {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, {format, internal}, {mode, read_only}]), - crash(File, 46), % the binary term {some,terms} is now bad - {J1, [{this,is}], 16} = disk_log:chunk(n, start, 10), + crash(File, 51), % the binary term {some,terms} is now bad + {J1, [{this,is}], 18} = disk_log:chunk(n, start, 10), {J2, [{on,a},{halt,file}]} = disk_log:chunk(n, J1), eof = disk_log:chunk(n, J2), ok = disk_log:close(n), @@ -2942,8 +2942,8 @@ chunk(Conf) when is_list(Conf) -> ok = disk_log:close(n), {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, {format, internal}, {mode, read_only}]), - crash(File, 40), % the binary term {s} is now bad - {J11, [{this,is}], 6} = disk_log:chunk(n, start, 10), + crash(File, 44), % the binary term {s} is now bad + {J11, [{this,is}], 7} = disk_log:chunk(n, start, 10), {J21, [{on,a},{halt,file}]} = disk_log:chunk(n, J11), eof = disk_log:chunk(n, J21), ok = disk_log:close(n), @@ -3062,7 +3062,7 @@ truncate(Conf) when is_list(Conf) -> ok = disk_log:truncate(n, apa), rec(1, {disk_log, node(), n, {truncated, 6}}), {0, 0} = no_overflows(n), - 22 = curb(n), + 23 = curb(n), 1 = curf(n), 1 = cur_cnt(n), true = (Size == sz(n)), @@ -3082,7 +3082,7 @@ truncate(Conf) when is_list(Conf) -> ok = disk_log:truncate(n, apa), rec(1, {disk_log, node(), n, {truncated, 3}}), {0, 0} = no_overflows(n), - 22 = curb(n), + 23 = curb(n), 1 = curf(n), 1 = cur_cnt(n), true = (Size == sz(n)), @@ -3191,45 +3191,45 @@ info_current(Conf) when is_list(Conf) -> %% Internal with header. {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {head, header}, {size, {100,No}}]), - {25, 1} = {curb(n), cur_cnt(n)}, + {26, 1} = {curb(n), cur_cnt(n)}, {1, 1} = {no_written_items(n), no_items(n)}, ok = disk_log:log(n, B), - {93, 2} = {curb(n), cur_cnt(n)}, + {94, 2} = {curb(n), cur_cnt(n)}, {2, 2} = {no_written_items(n), no_items(n)}, ok = disk_log:close(n), {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {notify, true}, {head, header}, {size, {100,No}}]), - {93, 2} = {curb(n), cur_cnt(n)}, + {94, 2} = {curb(n), cur_cnt(n)}, {0, 2} = {no_written_items(n), no_items(n)}, ok = disk_log:log(n, B), rec(1, {disk_log, node(), n, {wrap, 0}}), - {93, 2} = {curb(n), cur_cnt(n)}, + {94, 2} = {curb(n), cur_cnt(n)}, {2, 4} = {no_written_items(n), no_items(n)}, disk_log:inc_wrap_file(n), rec(1, {disk_log, node(), n, {wrap, 0}}), - {25, 1} = {curb(n), cur_cnt(n)}, + {26, 1} = {curb(n), cur_cnt(n)}, {3, 4} = {no_written_items(n), no_items(n)}, ok = disk_log:log_terms(n, [B,B,B]), %% Used to be one message, but now one per wrapped file. rec(1, {disk_log, node(), n, {wrap, 0}}), rec(1, {disk_log, node(), n, {wrap, 2}}), - {93, 2} = {curb(n), cur_cnt(n)}, + {94, 2} = {curb(n), cur_cnt(n)}, {8, 7} = {no_written_items(n), no_items(n)}, ok = disk_log:log_terms(n, [B]), rec(1, {disk_log, node(), n, {wrap, 2}}), ok = disk_log:log_terms(n, [B]), rec(1, {disk_log, node(), n, {wrap, 2}}), - {93, 2} = {curb(n), cur_cnt(n)}, + {94, 2} = {curb(n), cur_cnt(n)}, {12, 7} = {no_written_items(n), no_items(n)}, ok = disk_log:log_terms(n, [BB,BB]), %% Used to be one message, but now one per wrapped file. rec(2, {disk_log, node(), n, {wrap, 2}}), - {193, 2} = {curb(n), cur_cnt(n)}, + {194, 2} = {curb(n), cur_cnt(n)}, {16, 7} = {no_written_items(n), no_items(n)}, ok = disk_log:log_terms(n, [SB,SB,SB]), rec(1, {disk_log, node(), n, {wrap, 2}}), - {79, 4} = {curb(n), cur_cnt(n)}, + {80, 4} = {curb(n), cur_cnt(n)}, {20, 9} = {no_written_items(n), no_items(n)}, ok = disk_log:close(n), del(File, No), -- cgit v1.2.3 From 02c0cf44437df3ed1ff8cdbb48b477b489977b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Wed, 14 Jun 2017 12:14:10 +0200 Subject: Add a testcase for OTP-13939/ERL-193 --- lib/kernel/test/gen_tcp_misc_SUITE.erl | 41 +++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index 81c6dcd0fd..5e0ec911fb 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -51,7 +51,7 @@ several_accepts_in_one_go/1, accept_system_limit/1, active_once_closed/1, send_timeout/1, send_timeout_active/1, otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1, wrapping_oct/1, - otp_9389/1]). + otp_9389/1, otp_13939/1]). %% Internal exports. -export([sender/3, not_owner/1, passive_sockets_server/2, priority_server/1, @@ -3131,3 +3131,42 @@ oct_aloop(S,X,Times) -> end. ok({ok,V}) -> V. + +otp_13939(doc) -> + ["Check that writing to a remotely closed socket doesn't block forever " + "when exit_on_close is false."]; +otp_13939(suite) -> + []; +otp_13939(Config) when is_list(Config) -> + {Pid, Ref} = spawn_opt( + fun() -> + {ok, Listener} = gen_tcp:listen(0, [{exit_on_close, false}]), + {ok, Port} = inet:port(Listener), + + spawn_link( + fun() -> + {ok, Client} = gen_tcp:connect("localhost", Port, + [{active, false}]), + ok = gen_tcp:close(Client) + end), + + {ok, Accepted} = gen_tcp:accept(Listener), + + ok = gen_tcp:send(Accepted, <<0:(10*1024*1024*8)>>), + + %% The bug surfaces when there's a delay between the send + %% operations; inet:getstat is a red herring. + timer:sleep(100), + + {error, Code} = gen_tcp:send(Accepted, <<0:(10*1024*1024*8)>>), + ct:pal("gen_tcp:send returned ~p~n", [Code]) + end, [link, monitor]), + + receive + {'DOWN', Ref, process, Pid, normal} -> + ok + after 1000 -> + demonitor(Ref, [flush]), + exit(Pid, normal), + ct:fail("Server process blocked on send.") + end. -- cgit v1.2.3 From 43718d3b81d7f3d08e25047e22d579801bbe5044 Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Wed, 14 Jun 2017 15:36:21 +0200 Subject: Update copyright year --- lib/kernel/doc/src/disk_log.xml | 2 +- lib/kernel/doc/src/net_kernel.xml | 2 +- lib/kernel/src/Makefile | 2 +- lib/kernel/src/disk_log_1.erl | 2 +- lib/kernel/src/group.erl | 2 +- lib/kernel/src/kernel.app.src | 2 +- lib/kernel/test/gen_tcp_api_SUITE.erl | 2 +- lib/kernel/test/gen_udp_SUITE.erl | 2 +- lib/kernel/test/inet_sockopt_SUITE.erl | 2 +- lib/kernel/test/os_SUITE.erl | 2 +- lib/kernel/test/sendfile_SUITE.erl | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/disk_log.xml b/lib/kernel/doc/src/disk_log.xml index 570d3ef9bd..1be28adfb8 100644 --- a/lib/kernel/doc/src/disk_log.xml +++ b/lib/kernel/doc/src/disk_log.xml @@ -5,7 +5,7 @@
    1997 - 2016 + 2017 Ericsson AB, All Rights Reserved diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml index 7ddb849824..0b94fc0fa6 100644 --- a/lib/kernel/doc/src/net_kernel.xml +++ b/lib/kernel/doc/src/net_kernel.xml @@ -4,7 +4,7 @@
    - 19962016 + 19962017 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile index 78aa6192a9..5946620f0f 100644 --- a/lib/kernel/src/Makefile +++ b/lib/kernel/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2016. All Rights Reserved. +# Copyright Ericsson AB 1996-2017. 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. diff --git a/lib/kernel/src/disk_log_1.erl b/lib/kernel/src/disk_log_1.erl index 10c22e1ad6..93856aa7b3 100644 --- a/lib/kernel/src/disk_log_1.erl +++ b/lib/kernel/src/disk_log_1.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. diff --git a/lib/kernel/src/group.erl b/lib/kernel/src/group.erl index 0eeaaad8d2..bf785959ff 100644 --- a/lib/kernel/src/group.erl +++ b/lib/kernel/src/group.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 1128ee3ec5..e150938487 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl index 3f11e25b93..12d22519ce 100644 --- a/lib/kernel/test/gen_tcp_api_SUITE.erl +++ b/lib/kernel/test/gen_tcp_api_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2016. All Rights Reserved. +%% Copyright Ericsson AB 1998-2017. 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. diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index 836e0c5a05..aa616d43d6 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2016. All Rights Reserved. +%% Copyright Ericsson AB 1998-2017. 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. diff --git a/lib/kernel/test/inet_sockopt_SUITE.erl b/lib/kernel/test/inet_sockopt_SUITE.erl index 9413cbd976..ada9c2689c 100644 --- a/lib/kernel/test/inet_sockopt_SUITE.erl +++ b/lib/kernel/test/inet_sockopt_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2016. All Rights Reserved. +%% Copyright Ericsson AB 2007-2017. 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. diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl index 1233e362f2..53a9e168ef 100644 --- a/lib/kernel/test/os_SUITE.erl +++ b/lib/kernel/test/os_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. diff --git a/lib/kernel/test/sendfile_SUITE.erl b/lib/kernel/test/sendfile_SUITE.erl index e839959623..bfa564c32c 100644 --- a/lib/kernel/test/sendfile_SUITE.erl +++ b/lib/kernel/test/sendfile_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011-2016. All Rights Reserved. +%% Copyright Ericsson AB 2011-2017. 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. -- cgit v1.2.3 From 1fc964a89f5fe360d6d5bc03645b2593221e2afe Mon Sep 17 00:00:00 2001 From: John Eckersberg Date: Wed, 14 Jun 2017 12:32:17 -0400 Subject: Fix erl_epmd:port_please when using IPv6 This mirrors the behavior of inet:gethostbyname when no family or timeout is supplied. --- lib/kernel/src/erl_epmd.erl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl index 7bc9e2ede3..f96bc88913 100644 --- a/lib/kernel/src/erl_epmd.erl +++ b/lib/kernel/src/erl_epmd.erl @@ -79,7 +79,13 @@ port_please(Node, EpmdAddr, Timeout) -> port_please1(Node,HostName, Timeout) -> - case inet:gethostbyname(HostName, inet, Timeout) of + Family = case inet_db:res_option(inet6) of + true -> + inet6; + false -> + inet + end, + case inet:gethostbyname(HostName, Family, Timeout) of {ok,{hostent, _Name, _ , _Af, _Size, [EpmdAddr | _]}} -> get_port(Node, EpmdAddr, Timeout); Else -> -- cgit v1.2.3 From 98e0ccb4a56b3b2b6a552463f78e699ec7490669 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 9 Jun 2017 17:45:09 +0200 Subject: [stdlib] Open error log file as utf8 This allows the use of ~ts/~tp/~tw in calls to error_logger:format/2, error_logger:error_msg/2 and error_logger:info_msg/2. --- lib/kernel/doc/src/error_logger.xml | 20 +++++++++++++++++++- lib/kernel/doc/src/kernel_app.xml | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml index 27db00819f..91bf57cb91 100644 --- a/lib/kernel/doc/src/error_logger.xml +++ b/lib/kernel/doc/src/error_logger.xml @@ -126,6 +126,12 @@ ok error_report/1 instead.

    + +

    If the Unicode translation modifier (t) is used in + the format string, all error handlers must ensure that the + formatted output is correctly encoded for the I/O + device.

    +
    @@ -197,6 +203,12 @@ ok the standard event handler, meaning no further events are logged. When in doubt, use info_report/1 instead.

    + +

    If the Unicode translation modifier (t) is used in + the format string, all error handlers must ensure that the + formatted output is correctly encoded for the I/O + device.

    +
    @@ -262,7 +274,7 @@ ok successful, or {error, allready_have_logfile} if logging to file is already enabled, or an error tuple if another error occurred (for example, if Filename - cannot be opened).

    + cannot be opened). The file is opened with encoding UTF-8.

    close @@ -345,6 +357,12 @@ ok the standard event handler, meaning no further events are logged. When in doubt, use warning_report/1 instead.

    + +

    If the Unicode translation modifier (t) is used in + the format string, all error handlers must ensure that the + formatted output is correctly encoded for the I/O + device.

    +
    diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index 75e1e18d86..e5ac031539 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -186,7 +186,7 @@ {file, FileName}

    Installs the standard event handler, which prints error reports to file FileName, where FileName - is a string.

    + is a string. The file is opened with encoding UTF-8.

    false

    No standard event handler is installed, but -- cgit v1.2.3 From c18b13d4c8aa31b145703bbbf228fb07d6b2a0a5 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Wed, 21 Jun 2017 10:53:19 +0200 Subject: Prepare release --- lib/kernel/doc/src/notes.xml | 155 +++++++++++++++++++++++++++++++++++++++++++ lib/kernel/vsn.mk | 2 +- 2 files changed, 156 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 7127a59a0c..e1cf45109d 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,161 @@

    This document describes the changes made to the Kernel application.

    +
    Kernel 5.3 + +
    Fixed Bugs and Malfunctions + + +

    Function inet:ntoa/1 has been fixed to return + lowercase letters according to RFC 5935 that has been + approved after this function was written. Previously + uppercase letters were returned so this may be a + backwards incompatible change depending on how the + returned address string is used.

    +

    Function inet:parse_address/1 has been fixed to + accept %-suffixes on scoped addresses. The addresses does + not work yet, but gives no parse errors.

    +

    + *** POTENTIAL INCOMPATIBILITY ***

    +

    + Own Id: OTP-13006 Aux Id: ERIERL-20, ERL-429

    +
    + +

    + Fix bug where gethostname would incorrectly fail with + enametoolong on Linux.

    +

    + Own Id: OTP-14310

    +
    + +

    + Fix bug causing code:is_module_native to falsely + return true when local call trace is enabled for + the module.

    +

    + Own Id: OTP-14390

    +
    + +

    + Add early reject of invalid node names from distributed + nodes.

    +

    + Own Id: OTP-14426

    +
    +
    +
    + + +
    Improvements and New Features + + +

    + Since Unicode is now allowed in atoms an extra check is + needed for node names, which are restricted to Latin-1.

    +

    + Own Id: OTP-13805

    +
    + +

    Replaced usage of deprecated symbolic time + unit representations.

    +

    + Own Id: OTP-13831 Aux Id: OTP-13735

    +
    + +

    file:write_file(Name, Data, [raw]) would turn + Data into a single binary before writing. This + meant it could not take advantage of the writev() + system call if it was given a list of binaries and told + to write with raw mode.

    +

    + Own Id: OTP-13909

    +
    + +

    The performance of the disk_log has been + somewhat improved in some corner cases (big items), and + the documentation has been clarified.

    +

    + Own Id: OTP-14057 Aux Id: PR-1245

    +
    + +

    Functions for detecting changed code has been added. + code:modified_modules/0 returns all currently + loaded modules that have changed on disk. + code:module_status/1 returns the status for a + module. In the shell and in c module, mm/0 + is short for code:modified_modules/0, and + lm/0 reloads all currently loaded modules that + have changed on disk.

    +

    + Own Id: OTP-14059

    +
    + +

    + Introduce an event manager in Erlang to handle OS + signals. A subset of OS signals may be subscribed to and + those are described in the Kernel application.

    +

    + Own Id: OTP-14186

    +
    + +

    Sockets can now be bound to device (SO_BINDTODEVICE) + on platforms where it is supported.

    This has + been implemented e.g to support VRF-Lite under Linux; see + + VRF , and GitHub pull request #1326. +

    +

    + Own Id: OTP-14357 Aux Id: PR-1326

    +
    + +

    + Added option to store shell_history on disk so that the + history can be reused between sessions.

    +

    + Own Id: OTP-14409 Aux Id: PR-1420

    +
    + +

    The size of crash reports created by + gen_server, gen_statem and proc_lib + is limited with aid of the Kernel application variable + error_logger_format_depth. The purpose is to limit + the size of the messages sent to the error_logger + process when processes with huge message queues or states + crash.

    The crash report generated by + proc_lib includes the new tag + message_queue_len. The neighbour report also + includes the new tag current_stacktrace. Finally, + the neighbour report no longer includes the tags + messages and dictionary.

    The new + function error_logger:get_format_depth/0 can be + used to retrieve the value of the Kernel application + variable error_logger_format_depth.

    +

    + Own Id: OTP-14417

    +
    + +

    One of the ETS tables used by the global + module is created with {read_concurrency, true} in + order to reduce contention.

    +

    + Own Id: OTP-14419

    +
    + +

    + Warnings have been added to the relevant documentation + about not using un-secure distributed nodes in exposed + environments.

    +

    + Own Id: OTP-14425

    +
    +
    +
    + +
    +
    Kernel 5.2
    Fixed Bugs and Malfunctions diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 76b020e8ed..4edecd8969 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 5.2 +KERNEL_VSN = 5.3 -- cgit v1.2.3 From a0a45213a17fd57b0892300fde5d71828f75dbd5 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 29 Jun 2017 16:25:51 +0200 Subject: Update appups in kernel, stdlib, and sasl for OTP-21.0 --- lib/kernel/src/kernel.appup.src | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src index 77085b2064..fc5417597f 100644 --- a/lib/kernel/src/kernel.appup.src +++ b/lib/kernel/src/kernel.appup.src @@ -18,7 +18,7 @@ %% %CopyrightEnd% {"%VSN%", %% Up from - max one major revision back - [{<<"5\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-19.* + [{<<"5\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-20.* %% Down to - max one major revision back - [{<<"5\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-19.* + [{<<"5\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-20.* }. -- cgit v1.2.3 From 33a1962290ba6a6116cefe9fabb2e6cef823b33b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Fri, 23 Jun 2017 13:15:22 +0200 Subject: Break erts_debug:lock_counters/1 into separate BIFs --- lib/kernel/src/erts_debug.erl | 35 +++++++++++++++++++++++++++-------- lib/kernel/src/kernel.app.src | 2 +- 2 files changed, 28 insertions(+), 9 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl index ad92aafc2f..480db6814e 100644 --- a/lib/kernel/src/erts_debug.erl +++ b/lib/kernel/src/erts_debug.erl @@ -33,10 +33,10 @@ -export([breakpoint/2, disassemble/1, display/1, dist_ext_to_term/2, dump_monitors/1, dump_links/1, flat_size/1, - get_internal_state/1, instructions/0, lock_counters/1, + get_internal_state/1, instructions/0, map_info/1, same/2, set_internal_state/2, - size_shared/1, copy_shared/1, dirty_cpu/2, dirty_io/2, - dirty/3]). + size_shared/1, copy_shared/1, dirty_cpu/2, dirty_io/2, dirty/3, + lcnt_control/1, lcnt_control/2, lcnt_collect/0, lcnt_clear/0]). -spec breakpoint(MFA, Flag) -> non_neg_integer() when MFA :: {Module :: module(), @@ -142,12 +142,31 @@ ic(F) when is_function(F) -> io:format("Total: ~w~n",[lists:sum([C||{_I,C}<-Is])]), R. --spec lock_counters(info) -> term(); - (clear) -> ok; - ({copy_save, boolean()}) -> boolean(); - ({process_locks, boolean()}) -> boolean(). +-spec lcnt_control + (copy_save, boolean()) -> ok; + (mask, list(atom())) -> ok. -lock_counters(_) -> +lcnt_control(_Option, _Value) -> + erlang:nif_error(undef). + +-spec lcnt_control + (copy_save) -> boolean(); + (mask) -> list(atom()). + +lcnt_control(_Option) -> + erlang:nif_error(undef). + +-type lcnt_lock_info() :: {atom(), term(), atom(), term()}. + +-spec lcnt_collect() -> + list({duration, {non_neg_integer(), non_neg_integer()}} | + {locks, list(lcnt_lock_info())}). + +lcnt_collect() -> + erlang:nif_error(undef). + +-spec lcnt_clear() -> ok. +lcnt_clear() -> erlang:nif_error(undef). -spec same(Term1, Term2) -> boolean() when diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 1128ee3ec5..7aefbbd9b6 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -120,6 +120,6 @@ {applications, []}, {env, [{error_logger, tty}]}, {mod, {kernel, []}}, - {runtime_dependencies, ["erts-9.0", "stdlib-3.0", "sasl-3.0"]} + {runtime_dependencies, ["erts-9.1", "stdlib-3.0", "sasl-3.0"]} ] }. -- cgit v1.2.3 From abb8e79663d0527f2987d1d7e914c65117b396a9 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Wed, 28 Jun 2017 16:11:34 +0200 Subject: kernel: Do not use deprecated functions in string(3) --- lib/kernel/src/erl_boot_server.erl | 6 +++--- lib/kernel/src/erl_reply.erl | 4 ++-- lib/kernel/src/group.erl | 12 ++++++------ lib/kernel/src/inet_config.erl | 4 ++-- lib/kernel/src/inet_dns.erl | 6 +++--- lib/kernel/src/inet_parse.erl | 4 ++-- lib/kernel/src/os.erl | 2 +- 7 files changed, 19 insertions(+), 19 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/erl_boot_server.erl b/lib/kernel/src/erl_boot_server.erl index ac81cc9689..2a38266579 100644 --- a/lib/kernel/src/erl_boot_server.erl +++ b/lib/kernel/src/erl_boot_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. @@ -253,9 +253,9 @@ handle_info({udp, U, IP, Port, Data}, S0) -> "~w is not a valid address ** ~n", [IP]), {noreply,S0}; {true,_,_} -> - case catch string:substr(Data, 1, length(?EBOOT_REQUEST)) of + case catch string:slice(Data, 0, length(?EBOOT_REQUEST)) of ?EBOOT_REQUEST -> - Vsn = string:substr(Data, length(?EBOOT_REQUEST)+1, length(Data)), + Vsn = string:slice(Data, length(?EBOOT_REQUEST), length(Data)), error_logger:error_msg("** Illegal boot server connection attempt: " "client version is ~s ** ~n", [Vsn]); _ -> diff --git a/lib/kernel/src/erl_reply.erl b/lib/kernel/src/erl_reply.erl index e1e046cbb4..e1c4ffe839 100644 --- a/lib/kernel/src/erl_reply.erl +++ b/lib/kernel/src/erl_reply.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. @@ -42,7 +42,7 @@ reply(_) -> %% convert ip number to tuple ip_string_to_tuple(Ip) -> - [Ip1,Ip2,Ip3,Ip4] = string:tokens(Ip,"."), + [Ip1,Ip2,Ip3,Ip4] = string:lexemes(Ip,"."), {list_to_integer(Ip1), list_to_integer(Ip2), list_to_integer(Ip3), diff --git a/lib/kernel/src/group.erl b/lib/kernel/src/group.erl index bf785959ff..a5210901f2 100644 --- a/lib/kernel/src/group.erl +++ b/lib/kernel/src/group.erl @@ -793,9 +793,9 @@ search_up_stack(Stack, Substr) -> case up_stack(Stack) of {none,NewStack} -> {none,NewStack}; {L, NewStack} -> - case string:str(L, Substr) of - 0 -> search_up_stack(NewStack, Substr); - _ -> {string:strip(L,right,$\n), NewStack} + case string:find(L, Substr) of + nomatch -> search_up_stack(NewStack, Substr); + _ -> {string:trim(L, trailing, "$\n"), NewStack} end end. @@ -803,9 +803,9 @@ search_down_stack(Stack, Substr) -> case down_stack(Stack) of {none,NewStack} -> {none,NewStack}; {L, NewStack} -> - case string:str(L, Substr) of - 0 -> search_down_stack(NewStack, Substr); - _ -> {string:strip(L,right,$\n), NewStack} + case string:find(L, Substr) of + nomatch -> search_down_stack(NewStack, Substr); + _ -> {string:trim(L, trailing, "$\n"), NewStack} end end. diff --git a/lib/kernel/src/inet_config.erl b/lib/kernel/src/inet_config.erl index 4bbc520449..9f76360b8b 100644 --- a/lib/kernel/src/inet_config.erl +++ b/lib/kernel/src/inet_config.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. @@ -369,7 +369,7 @@ win32_load1(Reg,Type,HFileKey) -> end. win32_split_line(Line,nt) -> inet_parse:split_line(Line); -win32_split_line(Line,windows) -> string:tokens(Line, ","). +win32_split_line(Line,windows) -> string:lexemes(Line, ","). win32_get_strings(Reg, Names) -> win32_get_strings(Reg, Names, []). diff --git a/lib/kernel/src/inet_dns.erl b/lib/kernel/src/inet_dns.erl index d5f982cc51..f1f58bc872 100644 --- a/lib/kernel/src/inet_dns.erl +++ b/lib/kernel/src/inet_dns.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. @@ -29,7 +29,7 @@ -export([decode/1, encode/1]). --import(lists, [reverse/1, reverse/2, nthtail/2]). +-import(lists, [reverse/1]). -include("inet_int.hrl"). -include("inet_dns.hrl"). @@ -473,7 +473,7 @@ decode_data(<>, _, ?S_NAPTR, Buffer) -> {Data2,Services} = decode_string(Data1), {Data,Regexp} = decode_characters(Data2, utf8), Replacement = decode_domain(Data, Buffer), - {Order,Preference,string:to_lower(Flags),string:to_lower(Services), + {Order,Preference,string:lowercase(Flags),string:lowercase(Services), Regexp,Replacement}; %% ?S_OPT falls through to default decode_data(Data, _, ?S_TXT, _) -> diff --git a/lib/kernel/src/inet_parse.erl b/lib/kernel/src/inet_parse.erl index 29804dc50b..e9685c6554 100644 --- a/lib/kernel/src/inet_parse.erl +++ b/lib/kernel/src/inet_parse.erl @@ -95,7 +95,7 @@ hosts(Fname,File) -> %% interface with a %if suffix. These kind of %% addresses maybe need to be gracefully handled %% throughout inet* and inet_drv. - case string:tokens(Address, "%") of + case string:lexemes(Address, "%") of [Addr,_] -> {ok,_} = address(Addr), skip; @@ -407,7 +407,7 @@ is_dom1([C | Cs]) when C >= $a, C =< $z -> is_dom_ldh(Cs); is_dom1([C | Cs]) when C >= $A, C =< $Z -> is_dom_ldh(Cs); is_dom1([C | Cs]) when C >= $0, C =< $9 -> case is_dom_ldh(Cs) of - true -> is_dom2(string:tokens([C | Cs],".")); + true -> is_dom2(string:lexemes([C | Cs],".")); false -> false end; is_dom1(_) -> false. diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl index 0250783632..209899d587 100644 --- a/lib/kernel/src/os.erl +++ b/lib/kernel/src/os.erl @@ -178,7 +178,7 @@ verify_executable(Name0, [Ext|Rest], OrigExtensions) -> end; verify_executable(Name, [], OrigExtensions) when OrigExtensions =/= [""] -> %% Windows %% Will only happen on windows, hence case insensitivity - case can_be_full_name(string:to_lower(Name),OrigExtensions) of + case can_be_full_name(string:lowercase(Name),OrigExtensions) of true -> verify_executable(Name,[""],[""]); _ -> -- cgit v1.2.3 From 2a84c3d7f6822f1293ad860cf094b549fd3fe634 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 13 Jul 2017 12:28:49 +0200 Subject: Fix testcases after removal of non-smp emulator --- lib/kernel/test/gen_udp_SUITE.erl | 12 ------------ lib/kernel/test/zlib_SUITE.erl | 40 +++++++++++++++++---------------------- 2 files changed, 17 insertions(+), 35 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index aa616d43d6..8364fe7cdc 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -295,21 +295,9 @@ bad_address(Config) when is_list(Config) -> %% are received per in/out scheduling, which should be %% the same as the read_packets parameter. %% -%% What happens on the SMP emulator remains to be seen... -%% %% OTP-6249 UDP option for number of packet reads. read_packets(Config) when is_list(Config) -> - case erlang:system_info(smp_support) of - false -> - read_packets_1(); - true -> - %% We would need some new sort of tracing to test this - %% option reliably in an SMP emulator. - {skip,"SMP emulator"} - end. - -read_packets_1() -> N1 = 5, N2 = 7, {ok,R} = gen_udp:open(0, [{read_packets,N1}]), diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl index 4b67fce9a8..a44ef106d3 100644 --- a/lib/kernel/test/zlib_SUITE.erl +++ b/lib/kernel/test/zlib_SUITE.erl @@ -885,29 +885,23 @@ split_bin(Last,Acc) -> %% Check concurrent access to zlib driver. smp(Config) -> - case erlang:system_info(smp_support) of - true -> - NumOfProcs = lists:min([8,erlang:system_info(schedulers)]), - io:format("smp starting ~p workers\n",[NumOfProcs]), - - %% Tests to run in parallel. - Funcs = [zip_usage, gz_usage, compress_usage, dictionary_usage, - crc, adler], - - %% We get all function arguments here to avoid repeated parallel - %% file read access. - FnAList = lists:map(fun(F) -> {F,?MODULE:F({get_arg,Config})} - end, Funcs), - - Pids = [spawn_link(?MODULE, worker, [rand:uniform(9999), - list_to_tuple(FnAList), - self()]) - || _ <- lists:seq(1,NumOfProcs)], - wait_pids(Pids); - - false -> - {skipped,"No smp support"} - end. + NumOfProcs = lists:min([8,erlang:system_info(schedulers)]), + io:format("smp starting ~p workers\n",[NumOfProcs]), + + %% Tests to run in parallel. + Funcs = [zip_usage, gz_usage, compress_usage, dictionary_usage, + crc, adler], + + %% We get all function arguments here to avoid repeated parallel + %% file read access. + FnAList = lists:map(fun(F) -> {F,?MODULE:F({get_arg,Config})} + end, Funcs), + + Pids = [spawn_link(?MODULE, worker, [rand:uniform(9999), + list_to_tuple(FnAList), + self()]) + || _ <- lists:seq(1,NumOfProcs)], + wait_pids(Pids). worker(Seed, FnATpl, Parent) -> -- cgit v1.2.3 From b3a617b7398603c047204fb17198657a626a0f90 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Thu, 20 Jul 2017 16:08:23 +0200 Subject: Improve spec and doc for inet:get_rc/0 --- lib/kernel/doc/src/inet.xml | 11 +++++++++-- lib/kernel/src/inet.erl | 3 ++- 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index b71e8a1e5d..169a76463b 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -222,11 +222,18 @@ fe80::204:acff:fe17:bf38 Return a list of IP configuration parameters. -

    Returns the state of the Inet configuration database in +

    + Returns the state of the Inet configuration database in form of a list of recorded configuration parameters. For more information, see ERTS User's Guide: Inet Configuration. - Only parameters with other than default values are returned.

    +

    +

    + Only actual parameters with other than default values + are returned, for example not directives that specify + other sources for configuration parameters nor + directives that clear parameters. +

    diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 6aef5476f1..dc20c21c77 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -151,7 +151,8 @@ %%% --------------------------------- --spec get_rc() -> [{Par :: any(), Val :: any()}]. +-spec get_rc() -> [{Par :: atom(), Val :: any()} | + {Par :: atom(), Val1 :: any(), Val2 :: any()}]. get_rc() -> inet_db:get_rc(). -- cgit v1.2.3 From 36b356d8333d50efa3655d83b7473262340dbabd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Thu, 6 Jul 2017 10:04:55 +0200 Subject: Fix doc for the 'quiet' option; it defaults to false --- lib/kernel/doc/src/disk_log.xml | 2 +- lib/kernel/src/kernel.appup.src | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/disk_log.xml b/lib/kernel/doc/src/disk_log.xml index 570d3ef9bd..4316a309d5 100644 --- a/lib/kernel/doc/src/disk_log.xml +++ b/lib/kernel/doc/src/disk_log.xml @@ -972,7 +972,7 @@

    Specifies if messages will be sent to error_logger on recoverable errors with - the log files. Defaults to true.

    + the log files. Defaults to false.

    open/1 returns {ok, Log} if the diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src index 77085b2064..f1ef70a373 100644 --- a/lib/kernel/src/kernel.appup.src +++ b/lib/kernel/src/kernel.appup.src @@ -18,7 +18,7 @@ %% %CopyrightEnd% {"%VSN%", %% Up from - max one major revision back - [{<<"5\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-19.* + [{<<"5\\.[0-3](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-19.*, OTP-20.0 %% Down to - max one major revision back - [{<<"5\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-19.* + [{<<"5\\.[0-3](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-19.*, OTP-20.0 }. -- cgit v1.2.3 From b43756a45f872255fbb82ce0c235b9aa4a1e01ae Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Wed, 26 Jul 2017 11:46:21 +0200 Subject: Update version numbers --- lib/kernel/vsn.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 4edecd8969..c9463241d1 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 5.3 +KERNEL_VSN = 5.3.1 -- cgit v1.2.3 From 4c73c0b0fa607c5f1e0befec2efc36b940a08106 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Wed, 26 Jul 2017 11:46:38 +0200 Subject: Update release notes --- lib/kernel/doc/src/notes.xml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index e1cf45109d..9cd03ffcad 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,21 @@

    This document describes the changes made to the Kernel application.

    +
    Kernel 5.3.1 + +
    Fixed Bugs and Malfunctions + + +

    The documentation for the 'quiet' option in + disk_log:open/1 had an incorrect default value.

    +

    + Own Id: OTP-14498

    +
    +
    +
    + +
    +
    Kernel 5.3
    Fixed Bugs and Malfunctions -- cgit v1.2.3 From 1f8be88437566ad0aff20a3f6f8230ee78574e4c Mon Sep 17 00:00:00 2001 From: bitnitdit Date: Fri, 25 Aug 2017 19:24:20 +0800 Subject: Switch query back to be normal atom query was no longer a keyword since the commit 0dc3a29744bed0b7f483ad72e19773dc0982ea50 2012-11-19. So the quotes around query, when it used as atom, no needed. --- lib/kernel/doc/src/inet_res.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/inet_res.xml b/lib/kernel/doc/src/inet_res.xml index 4ada4203c0..3454e3c6f9 100644 --- a/lib/kernel/doc/src/inet_res.xml +++ b/lib/kernel/doc/src/inet_res.xml @@ -130,7 +130,7 @@ dns_header() = DnsHeader inet_dns:header(DnsHeader) -> [ {id, integer()} | {qr, boolean()} - | {opcode, 'query' | iquery | status | integer()} + | {opcode, query | iquery | status | integer()} | {aa, boolean()} | {tc, boolean()} | {rd, boolean()} -- cgit v1.2.3 From 78fad16ef7c5477239bc0b51125fabfe6567039d Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 30 Jun 2017 16:32:42 +0200 Subject: Support for distribution controller processes --- lib/kernel/include/dist_util.hrl | 9 ++++-- lib/kernel/src/dist_util.erl | 62 +++++++++++++++++++++------------------- lib/kernel/src/net_kernel.erl | 4 +-- 3 files changed, 41 insertions(+), 34 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/include/dist_util.hrl b/lib/kernel/include/dist_util.hrl index e3d2fe0eb6..e668b12a82 100644 --- a/lib/kernel/include/dist_util.hrl +++ b/lib/kernel/include/dist_util.hrl @@ -29,9 +29,9 @@ -endif. -ifdef(dist_trace). --define(trace(Fmt,Args), io:format("~p ~p:~s",[erlang:timestamp(),node(),lists:flatten(io_lib:format(Fmt, Args))])). +-define(trace(Fmt,Args), io:format("~p ~p:~s",[erlang:convert_time_unit(erlang:monotonic_time()-erlang:system_info(start_time), native, microsecond),node(),lists:flatten(io_lib:format(Fmt, Args))])). % Use the one below for config-file (early boot) connection tracing -%-define(trace(Fmt,Args), erlang:display([erlang:now(),node(),lists:flatten(io_lib:format(Fmt, Args))])). +%-define(trace(Fmt,Args), erlang:display([erlang:convert_time_unit(erlang:monotonic_time()-erlang:system_info(start_time), native, microsecond),node(),lists:flatten(io_lib:format(Fmt, Args))])). -define(trace_factor,8). -else. -define(trace(Fmt,Args), ok). @@ -78,7 +78,10 @@ %% New in kernel-5.1 (OTP 19.1): mf_setopts, %% netkernel:setopts on active connection - mf_getopts %% netkernel:getopts on active connection + mf_getopts, %% netkernel:getopts on active connection + + %% New in kernel-6.0 (OTP 21.0) + f_handshake_complete, %% Notify handshake complete }). diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index b3507e5d13..81dbf442d1 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -336,15 +336,11 @@ handshake_we_started(#hs_data{request_type=ReqType, handshake_we_started(OldHsData) when element(1,OldHsData) =:= hs_data -> handshake_we_started(convert_old_hsdata(OldHsData)). -convert_old_hsdata({hs_data, KP, ON, TN, S, T, TF, A, OV, OF, OS, FS, FR, - FS_PRE, FS_POST, FG, FA, MFT, MFG, RT}) -> - #hs_data{ - kernel_pid = KP, other_node = ON, this_node = TN, socket = S, timer = T, - this_flags = TF, allowed = A, other_version = OV, other_flags = OF, - other_started = OS, f_send = FS, f_recv = FR, f_setopts_pre_nodeup = FS_PRE, - f_setopts_post_nodeup = FS_POST, f_getll = FG, f_address = FA, - mf_tick = MFT, mf_getstat = MFG, request_type = RT}. - +convert_old_hsdata(OldHsData) -> + OHSDL = tuple_to_list(OldHsData), + NoMissing = tuple_size(#hs_data{}) - tuple_size(OldHsData), + true = NoMissing > 0, + list_to_tuple(OHSDL ++ lists:duplicate(NoMissing, undefined)). %% -------------------------------------------------------------- %% The connection has been established. @@ -359,15 +355,20 @@ connection(#hs_data{other_node = Node, PType = publish_type(HSData#hs_data.other_flags), case FPreNodeup(Socket) of ok -> - do_setnode(HSData), % Succeeds or exits the process. + DHandle = do_setnode(HSData), % Succeeds or exits the process. Address = FAddress(Socket,Node), mark_nodeup(HSData,Address), case FPostNodeup(Socket) of ok -> + case HSData#hs_data.f_handshake_complete of + undefined -> ok; + HsComplete -> HsComplete(Socket, Node, DHandle) + end, con_loop({HSData#hs_data.kernel_pid, Node, Socket, PType, + DHandle, HSData#hs_data.mf_tick, HSData#hs_data.mf_getstat, HSData#hs_data.mf_setopts, @@ -425,18 +426,16 @@ do_setnode(#hs_data{other_node = Node, socket = Socket, [Node, Port, {publish_type(Flags), '(', Flags, ')', Version}]), - case (catch - erlang:setnode(Node, Port, - {Flags, Version, '', ''})) of - {'EXIT', {system_limit, _}} -> + try + erlang:setnode(Node, Port, {Flags, Version, '', ''}) + catch + error:system_limit -> error_msg("** Distribution system limit reached, " "no table space left for node ~w ** ~n", [Node]), ?shutdown(Node); - {'EXIT', Other} -> - exit(Other); - _Else -> - ok + error:Other -> + exit({Other, erlang:get_stacktrace()}) end; _ -> error_msg("** Distribution connection error, " @@ -468,7 +467,13 @@ mark_nodeup(#hs_data{kernel_pid = Kernel, ?shutdown(Node) end. -con_loop({Kernel, Node, Socket, Type, MFTick, MFGetstat, MFSetOpts, MFGetOpts}=ConData, +getstat(DHandle, _Socket, undefined) -> + erlang:dist_get_stat(DHandle); +getstat(_DHandle, Socket, MFGetstat) -> + MFGetstat(Socket). + +con_loop({Kernel, Node, Socket, Type, DHandle, MFTick, MFGetstat, + MFSetOpts, MFGetOpts}=ConData, Tick) -> receive {tcp_closed, Socket} -> @@ -476,7 +481,7 @@ con_loop({Kernel, Node, Socket, Type, MFTick, MFGetstat, MFSetOpts, MFGetOpts}=C {Kernel, disconnect} -> ?shutdown2(Node, disconnected); {Kernel, aux_tick} -> - case MFGetstat(Socket) of + case getstat(DHandle, Socket, MFGetstat) of {ok, _, _, PendWrite} -> send_tick(Socket, PendWrite, MFTick); _ -> @@ -484,7 +489,7 @@ con_loop({Kernel, Node, Socket, Type, MFTick, MFGetstat, MFSetOpts, MFGetOpts}=C end, con_loop(ConData, Tick); {Kernel, tick} -> - case send_tick(Socket, Tick, Type, + case send_tick(DHandle, Socket, Tick, Type, MFTick, MFGetstat) of {ok, NewTick} -> con_loop(ConData, NewTick); @@ -497,7 +502,7 @@ con_loop({Kernel, Node, Socket, Type, MFTick, MFGetstat, MFSetOpts, MFGetOpts}=C ?shutdown2(Node, send_net_tick_failed) end; {From, get_status} -> - case MFGetstat(Socket) of + case getstat(DHandle, Socket, MFGetstat) of {ok, Read, Write, _} -> From ! {self(), get_status, {ok, Read, Write}}, con_loop(ConData, Tick); @@ -735,14 +740,14 @@ send_status(#hs_data{socket = Socket, other_node = Node, %% we haven't read anything as a hidden node only ticks when it receives %% a TICK !! -send_tick(Socket, Tick, Type, MFTick, MFGetstat) -> +send_tick(DHandle, Socket, Tick, Type, MFTick, MFGetstat) -> #tick{tick = T0, read = Read, write = Write, ticked = Ticked} = Tick, T = T0 + 1, T1 = T rem 4, - case MFGetstat(Socket) of + case getstat(DHandle, Socket, MFGetstat) of {ok, Read, _, _} when Ticked =:= T -> {error, not_responding}; {ok, Read, W, Pend} when Type =:= hidden -> @@ -771,11 +776,10 @@ send_tick(Socket, Tick, Type, MFTick, MFGetstat) -> Error end. -send_tick(Socket, 0, MFTick) -> - MFTick(Socket); -send_tick(_, _Pend, _) -> - %% Dont send tick if pending write. - ok. +send_tick(_, Pend, _) when Pend /= false, Pend /= 0 -> + ok; %% Dont send tick if pending write. +send_tick(Socket, _Pend, MFTick) -> + MFTick(Socket). %% ------------------------------------------------------------ %% Connection setup timeout timer. diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index ddda396713..fb4faea420 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -423,8 +423,8 @@ handle_call({connect, Type, Node}, From, State) -> {ok, SetupPid} -> Owners = [{SetupPid, Node} | State#state.conn_owners], {noreply,State#state{conn_owners=Owners}}; - _ -> - ?connect_failure(Node, {setup_call, failed}), + _Error -> + ?connect_failure(Node, {setup_call, failed, _Error}), async_reply({reply, false, State}, From) end end; -- cgit v1.2.3 From 87677f64629f1f738e132659c445c3431b02c0b2 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 30 Jun 2017 16:34:26 +0200 Subject: Example of using distribution controller processes --- lib/kernel/examples/Makefile | 2 +- lib/kernel/examples/gen_tcp_dist/Makefile | 20 + lib/kernel/examples/gen_tcp_dist/ebin/.gitignore | 0 .../examples/gen_tcp_dist/src/gen_tcp_dist.erl | 772 +++++++++++++++++++++ 4 files changed, 793 insertions(+), 1 deletion(-) create mode 100644 lib/kernel/examples/gen_tcp_dist/Makefile create mode 100644 lib/kernel/examples/gen_tcp_dist/ebin/.gitignore create mode 100644 lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl (limited to 'lib/kernel') diff --git a/lib/kernel/examples/Makefile b/lib/kernel/examples/Makefile index 26ec58f571..f86e662838 100644 --- a/lib/kernel/examples/Makefile +++ b/lib/kernel/examples/Makefile @@ -45,7 +45,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/kernel-$(KERNEL_VSN)/examples # Pack and install the complete directory structure from # here (CWD) and down, for all examples. -EXAMPLES = uds_dist +EXAMPLES = uds_dist gen_tcp_dist release_spec: $(INSTALL_DIR) "$(RELSYSDIR)" diff --git a/lib/kernel/examples/gen_tcp_dist/Makefile b/lib/kernel/examples/gen_tcp_dist/Makefile new file mode 100644 index 0000000000..65513a1729 --- /dev/null +++ b/lib/kernel/examples/gen_tcp_dist/Makefile @@ -0,0 +1,20 @@ +RM=rm -f +CP=cp +EBIN=ebin +ERLC=erlc +# Works if building in open source source tree +KERNEL_INCLUDE=$(ERL_TOP)/lib/kernel/include +ERLCFLAGS+= -W -I$(KERNEL_INCLUDE) + +MODULES=gen_tcp_dist + +TARGET_FILES=$(MODULES:%=$(EBIN)/%.beam) + +opt: $(TARGET_FILES) + +$(EBIN)/%.beam: src/%.erl + $(ERLC) $(ERLCFLAGS) -o$(EBIN) $< + +clean: + $(RM) $(TARGET_FILES) + diff --git a/lib/kernel/examples/gen_tcp_dist/ebin/.gitignore b/lib/kernel/examples/gen_tcp_dist/ebin/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl b/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl new file mode 100644 index 0000000000..002d09fc74 --- /dev/null +++ b/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl @@ -0,0 +1,772 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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(gen_tcp_dist). + +%% +%% This is an example of how to plug in an arbitrary distribution +%% carrier for Erlang using distribution processes. +%% +%% This example uses gen_tcp for transportation of data, but +%% you can use whatever underlying protocol you want as long +%% as your implementation reliably delivers data chunks to the +%% receiving VM in the order they were sent from the sending +%% VM. +%% +%% This code is a rewrite of the lib/kernel/src/inet_tcp_dist.erl +%% distribution impementation for TCP used by default. That +%% implementation use distribution ports instead of distribution +%% processes and is more efficient compared to this implementation. +%% This since this implementation more or less gets the +%% distribution processes in between the VM and the ports without +%% any gain specific gain. +%% + +-export([listen/1, accept/1, accept_connection/5, + setup/5, close/1, select/1, is_node_name/1]). + +%% Optional +-export([setopts/2, getopts/2]). + +%% internal exports + +-export([dist_cntrlr_setup/1, dist_cntrlr_input_setup/3, + dist_cntrlr_tick_handler/1]). + +-export([accept_loop/2,do_accept/6,do_setup/6]). + +-import(error_logger,[error_msg/2]). + +-include("net_address.hrl"). + +-include("dist.hrl"). +-include("dist_util.hrl"). + +%% ------------------------------------------------------------ +%% Select this protocol based on node name +%% select(Node) => Bool +%% ------------------------------------------------------------ + +select(Node) -> + case split_node(atom_to_list(Node), $@, []) of + [_, Host] -> + case inet:getaddr(Host, inet) of + {ok,_} -> true; + _ -> false + end; + _ -> false + end. + +%% ------------------------------------------------------------ +%% Create the listen socket, i.e. the port that this erlang +%% node is accessible through. +%% ------------------------------------------------------------ + +listen(Name) -> + case do_listen([binary, {active, false}, {packet,2}, {reuseaddr, true}]) of + {ok, Socket} -> + TcpAddress = get_tcp_address(Socket), + {_,Port} = TcpAddress#net_address.address, + ErlEpmd = net_kernel:epmd_module(), + case ErlEpmd:register_node(Name, Port) of + {ok, Creation} -> + {ok, {Socket, TcpAddress, Creation}}; + Error -> + Error + end; + Error -> + Error + end. + +do_listen(Options) -> + {First,Last} = case application:get_env(kernel,inet_dist_listen_min) of + {ok,N} when is_integer(N) -> + case application:get_env(kernel, + inet_dist_listen_max) of + {ok,M} when is_integer(M) -> + {N,M}; + _ -> + {N,N} + end; + _ -> + {0,0} + end, + do_listen(First, Last, listen_options([{backlog,128}|Options])). + +do_listen(First,Last,_) when First > Last -> + {error,eaddrinuse}; +do_listen(First,Last,Options) -> + case gen_tcp:listen(First, Options) of + {error, eaddrinuse} -> + do_listen(First+1,Last,Options); + Other -> + Other + end. + +listen_options(Opts0) -> + Opts1 = + case application:get_env(kernel, inet_dist_use_interface) of + {ok, Ip} -> + [{ip, Ip} | Opts0]; + _ -> + Opts0 + end, + case application:get_env(kernel, inet_dist_listen_options) of + {ok,ListenOpts} -> + ListenOpts ++ Opts1; + _ -> + Opts1 + end. + + +%% ------------------------------------------------------------ +%% Accepts new connection attempts from other Erlang nodes. +%% ------------------------------------------------------------ + +accept(Listen) -> + spawn_opt(?MODULE, accept_loop, [self(), Listen], [link, {priority, max}]). + +accept_loop(Kernel, Listen) -> + ?trace("~p~n",[{?MODULE, accept_loop, self()}]), + case gen_tcp:accept(Listen) of + {ok, Socket} -> + DistCtrl = spawn_dist_cntrlr(Socket), + ?trace("~p~n",[{?MODULE, accept_loop, accepted, Socket, DistCtrl, self()}]), + flush_controller(DistCtrl, Socket), + gen_tcp:controlling_process(Socket, DistCtrl), + flush_controller(DistCtrl, Socket), + Kernel ! {accept,self(),DistCtrl,inet,tcp}, + receive + {Kernel, controller, Pid} -> + call_ctrlr(DistCtrl, {supervisor, Pid}), + Pid ! {self(), controller}; + {Kernel, unsupported_protocol} -> + exit(unsupported_protocol) + end, + accept_loop(Kernel, Listen); + Error -> + exit(Error) + end. + +flush_controller(Pid, Socket) -> + receive + {tcp, Socket, Data} -> + Pid ! {tcp, Socket, Data}, + flush_controller(Pid, Socket); + {tcp_closed, Socket} -> + Pid ! {tcp_closed, Socket}, + flush_controller(Pid, Socket) + after 0 -> + ok + end. + +%% ------------------------------------------------------------ +%% Accepts a new connection attempt from another Erlang node. +%% Performs the handshake with the other side. +%% ------------------------------------------------------------ + +accept_connection(AcceptPid, DistCtrl, MyNode, Allowed, SetupTime) -> + spawn_opt(?MODULE, do_accept, + [self(), AcceptPid, DistCtrl, MyNode, Allowed, SetupTime], + [link, {priority, max}]). + +do_accept(Kernel, AcceptPid, DistCtrl, MyNode, Allowed, SetupTime) -> + ?trace("~p~n",[{?MODULE, do_accept, self(), MyNode}]), + receive + {AcceptPid, controller} -> + Timer = dist_util:start_timer(SetupTime), + case check_ip(DistCtrl) of + true -> + HSData0 = hs_data_common(DistCtrl), + HSData = HSData0#hs_data{kernel_pid = Kernel, + this_node = MyNode, + socket = DistCtrl, + timer = Timer, + this_flags = 0, + allowed = Allowed}, + dist_util:handshake_other_started(HSData); + {false,IP} -> + error_msg("** Connection attempt from " + "disallowed IP ~w ** ~n", [IP]), + ?shutdown(no_node) + end + end. + +%% we may not always want the nodelay behaviour +%% for performance reasons + +nodelay() -> + case application:get_env(kernel, dist_nodelay) of + undefined -> + {nodelay, true}; + {ok, true} -> + {nodelay, true}; + {ok, false} -> + {nodelay, false}; + _ -> + {nodelay, true} + end. + +%% ------------------------------------------------------------ +%% Setup a new connection to another Erlang node. +%% Performs the handshake with the other side. +%% ------------------------------------------------------------ + +setup(Node, Type, MyNode, LongOrShortNames,SetupTime) -> + spawn_opt(?MODULE, do_setup, + [self(), Node, Type, MyNode, LongOrShortNames, SetupTime], + [link, {priority, max}]). + +do_setup(Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) -> + ?trace("~p~n",[{?MODULE, do_setup, self(), Node}]), + [Name, Address] = splitnode(Node, LongOrShortNames), + case inet:getaddr(Address, inet) of + {ok, Ip} -> + Timer = dist_util:start_timer(SetupTime), + ErlEpmd = net_kernel:epmd_module(), + case ErlEpmd:port_please(Name, Ip) of + {port, TcpPort, Version} -> + ?trace("port_please(~p) -> version ~p~n", + [Node,Version]), + dist_util:reset_timer(Timer), + case + gen_tcp:connect( + Ip, TcpPort, + connect_options([binary, {active, false}, {packet, 2}])) + of + {ok, Socket} -> + DistCtrl = spawn_dist_cntrlr(Socket), + call_ctrlr(DistCtrl, {supervisor, self()}), + flush_controller(DistCtrl, Socket), + gen_tcp:controlling_process(Socket, DistCtrl), + flush_controller(DistCtrl, Socket), + HSData0 = hs_data_common(DistCtrl), + HSData = HSData0#hs_data{kernel_pid = Kernel, + other_node = Node, + this_node = MyNode, + socket = DistCtrl, + timer = Timer, + this_flags = 0, + other_version = Version, + request_type = Type}, + dist_util:handshake_we_started(HSData); + _ -> + %% Other Node may have closed since + %% port_please ! + ?trace("other node (~p) " + "closed since port_please.~n", + [Node]), + ?shutdown(Node) + end; + _ -> + ?trace("port_please (~p) " + "failed.~n", [Node]), + ?shutdown(Node) + end; + _Other -> + ?trace("inet_getaddr(~p) " + "failed (~p).~n", [Node,_Other]), + ?shutdown(Node) + end. + +connect_options(Opts) -> + case application:get_env(kernel, inet_dist_connect_options) of + {ok,ConnectOpts} -> + ConnectOpts ++ Opts; + _ -> + Opts + end. + +%% +%% Close a socket. +%% +close(Listen) -> + gen_tcp:close(Listen). + + +%% If Node is illegal terminate the connection setup!! +splitnode(Node, LongOrShortNames) -> + case split_node(atom_to_list(Node), $@, []) of + [Name|Tail] when Tail =/= [] -> + Host = lists:append(Tail), + case split_node(Host, $., []) of + [_] when LongOrShortNames =:= longnames -> + case inet:parse_address(Host) of + {ok, _} -> + [Name, Host]; + _ -> + error_msg("** System running to use " + "fully qualified " + "hostnames **~n" + "** Hostname ~ts is illegal **~n", + [Host]), + ?shutdown(Node) + end; + L when length(L) > 1, LongOrShortNames =:= shortnames -> + error_msg("** System NOT running to use fully qualified " + "hostnames **~n" + "** Hostname ~ts is illegal **~n", + [Host]), + ?shutdown(Node); + _ -> + [Name, Host] + end; + [_] -> + error_msg("** Nodename ~p illegal, no '@' character **~n", + [Node]), + ?shutdown(Node); + _ -> + error_msg("** Nodename ~p illegal **~n", [Node]), + ?shutdown(Node) + end. + +split_node([Chr|T], Chr, Ack) -> [lists:reverse(Ack)|split_node(T, Chr, [])]; +split_node([H|T], Chr, Ack) -> split_node(T, Chr, [H|Ack]); +split_node([], _, Ack) -> [lists:reverse(Ack)]. + +%% ------------------------------------------------------------ +%% Fetch local information about a Socket. +%% ------------------------------------------------------------ +get_tcp_address(Socket) -> + {ok, Address} = inet:sockname(Socket), + {ok, Host} = inet:gethostname(), + #net_address { + address = Address, + host = Host, + protocol = tcp, + family = inet + }. + +%% ------------------------------------------------------------ +%% Do only accept new connection attempts from nodes at our +%% own LAN, if the check_ip environment parameter is true. +%% ------------------------------------------------------------ +check_ip(DistCtrl) -> + case application:get_env(check_ip) of + {ok, true} -> + case get_ifs(DistCtrl) of + {ok, IFs, IP} -> + check_ip(IFs, IP); + _ -> + ?shutdown(no_node) + end; + _ -> + true + end. + +get_ifs(DistCtrl) -> + Socket = call_ctrlr(DistCtrl, socket), + case inet:peername(Socket) of + {ok, {IP, _}} -> + case inet:getif(Socket) of + {ok, IFs} -> {ok, IFs, IP}; + Error -> Error + end; + Error -> + Error + end. + +check_ip([{OwnIP, _, Netmask}|IFs], PeerIP) -> + case {inet_tcp:mask(Netmask, PeerIP), inet_tcp:mask(Netmask, OwnIP)} of + {M, M} -> true; + _ -> check_ip(IFs, PeerIP) + end; +check_ip([], PeerIP) -> + {false, PeerIP}. + +is_node_name(Node) when is_atom(Node) -> + case split_node(atom_to_list(Node), $@, []) of + [_, _Host] -> true; + _ -> false + end; +is_node_name(_Node) -> + false. + +hs_data_common(DistCtrl) -> + TickHandler = call_ctrlr(DistCtrl, tick_handler), + Socket = call_ctrlr(DistCtrl, socket), + #hs_data{f_send = send_fun(), + f_recv = recv_fun(), + f_setopts_pre_nodeup = setopts_pre_nodeup_fun(), + f_setopts_post_nodeup = setopts_post_nodeup_fun(), + f_getll = getll_fun(), + f_handshake_complete = handshake_complete_fun(), + f_address = address_fun(), + mf_setopts = setopts_fun(DistCtrl, Socket), + mf_getopts = getopts_fun(DistCtrl, Socket), + mf_getstat = getstat_fun(DistCtrl, Socket), + mf_tick = tick_fun(DistCtrl, TickHandler)}. + +%%% ------------------------------------------------------------ +%%% Distribution controller processes +%%% ------------------------------------------------------------ + +%% +%% There will be five parties working together when the +%% connection is up: +%% - The gen_tcp socket. Providing a tcp/ip connection +%% to the other node. +%% - The output handler. It will dispatch all outgoing +%% traffic from the VM to the gen_tcp socket. This +%% process is registered as distribution controller +%% for this channel with the VM. +%% - The input handler. It will dispatch all incoming +%% traffic from the gen_tcp socket to the VM. This +%% process is also the socket owner and receives +%% incoming traffic using active-N. +%% - The tick handler. Dispatches asynchronous tick +%% requests to the socket. It executes on max priority +%% since it is important to get ticks through to the +%% other end. +%% - The channel supervisor (provided by dist_util). It +%% monitors traffic. Issue tick requests to the tick +%% handler when no outgoing traffic is seen and bring +%% the connection down if no incoming traffic is seen. +%% This process also executes on max priority. +%% +%% These parties are linked togheter so should one +%% of them fail, all of them are terminated and the +%% connection is taken down. +%% + +%% In order to avoid issues with lingering signal binaries +%% we enable off-heap message queue data as well as fullsweep +%% after 0. The fullsweeps will be cheap since we have more +%% or less no live data. +-define(DIST_CNTRL_COMMON_SPAWN_OPTS, + [{message_queue_data, off_heap}, + {fullsweep_after, 0}]). + +tick_fun(DistCtrl, TickHandler) -> + fun (Ctrl) when Ctrl == DistCtrl -> + TickHandler ! tick + end. + +getstat_fun(DistCtrl, Socket) -> + fun (Ctrl) when Ctrl == DistCtrl -> + case inet:getstat(Socket, [recv_cnt, send_cnt, send_pend]) of + {ok, Stat} -> + split_stat(Stat,0,0,0); + Error -> + Error + end + end. + +split_stat([{recv_cnt, R}|Stat], _, W, P) -> + split_stat(Stat, R, W, P); +split_stat([{send_cnt, W}|Stat], R, _, P) -> + split_stat(Stat, R, W, P); +split_stat([{send_pend, P}|Stat], R, W, _) -> + split_stat(Stat, R, W, P); +split_stat([], R, W, P) -> + {ok, R, W, P}. + +setopts_fun(DistCtrl, Socket) -> + fun (Ctrl, Opts) when Ctrl == DistCtrl -> + setopts(Socket, Opts) + end. + +getopts_fun(DistCtrl, Socket) -> + fun (Ctrl, Opts) when Ctrl == DistCtrl -> + getopts(Socket, Opts) + end. + +setopts(S, Opts) -> + case [Opt || {K,_}=Opt <- Opts, + K =:= active orelse K =:= deliver orelse K =:= packet] of + [] -> inet:setopts(S,Opts); + Opts1 -> {error, {badopts,Opts1}} + end. + +getopts(S, Opts) -> + inet:getopts(S, Opts). + +send_fun() -> + fun (Ctrlr, Packet) -> + call_ctrlr(Ctrlr, {send, Packet}) + end. + +recv_fun() -> + fun (Ctrlr, Length, Timeout) -> + case call_ctrlr(Ctrlr, {recv, Length, Timeout}) of + {ok, Bin} when is_binary(Bin) -> + {ok, binary_to_list(Bin)}; + Other -> + Other + end + end. + +getll_fun() -> + fun (Ctrlr) -> + call_ctrlr(Ctrlr, getll) + end. + +address_fun() -> + fun (Ctrlr, Node) -> + case call_ctrlr(Ctrlr, {address, Node}) of + {error, no_node} -> %% No '@' or more than one '@' in node name. + ?shutdown(no_node); + Res -> + Res + end + end. + +setopts_pre_nodeup_fun() -> + fun (Ctrlr) -> + call_ctrlr(Ctrlr, pre_nodeup) + end. + +setopts_post_nodeup_fun() -> + fun (Ctrlr) -> + call_ctrlr(Ctrlr, post_nodeup) + end. + +handshake_complete_fun() -> + fun (Ctrlr, Node, DHandle) -> + call_ctrlr(Ctrlr, {handshake_complete, Node, DHandle}) + end. + +call_ctrlr(Ctrlr, Msg) -> + Ref = erlang:monitor(process, Ctrlr), + Ctrlr ! {Ref, self(), Msg}, + receive + {Ref, Res} -> + erlang:demonitor(Ref, [flush]), + Res; + {'DOWN', Ref, process, Ctrlr, Reason} -> + exit({dist_controller_exit, Reason}) + end. + +%% +%% The tick handler process writes a tick to the +%% socket when it receives a 'tick' message from +%% the connection supervisor. +%% +%% We are not allowed to block the connection +%% superviser when writing a tick and we also want +%% the tick to go through even during a heavily +%% loaded system. gen_tcp does not have a +%% non-blocking send operation exposed in its API +%% and we don't want to run the distribution +%% controller under high priority. Therefore this +%% sparate process with max prio that dispatches +%% ticks. +%% +dist_cntrlr_tick_handler(Socket) -> + receive + tick -> + %% May block due to busy port... + sock_send(Socket, ""); + _ -> + ok + end, + dist_cntrlr_tick_handler(Socket). + +spawn_dist_cntrlr(Socket) -> + spawn_opt(?MODULE, dist_cntrlr_setup, [Socket], + [{priority, max}] ++ ?DIST_CNTRL_COMMON_SPAWN_OPTS). + +dist_cntrlr_setup(Socket) -> + TickHandler = spawn_opt(?MODULE, dist_cntrlr_tick_handler, + [Socket], + [link, {priority, max}] + ++ ?DIST_CNTRL_COMMON_SPAWN_OPTS), + dist_cntrlr_setup_loop(Socket, TickHandler, undefined). + +%% +%% During the handshake phase we loop in dist_cntrlr_setup(). +%% When the connection is up we spawn an input handler and +%% continue as output handler. +%% +dist_cntrlr_setup_loop(Socket, TickHandler, Sup) -> + receive + {tcp_closed, Socket} -> + exit(connection_closed); + + {Ref, From, {supervisor, Pid}} -> + Res = link(Pid), + From ! {Ref, Res}, + dist_cntrlr_setup_loop(Socket, TickHandler, Pid); + + {Ref, From, tick_handler} -> + From ! {Ref, TickHandler}, + dist_cntrlr_setup_loop(Socket, TickHandler, Sup); + + {Ref, From, socket} -> + From ! {Ref, Socket}, + dist_cntrlr_setup_loop(Socket, TickHandler, Sup); + + {Ref, From, {send, Packet}} -> + Res = gen_tcp:send(Socket, Packet), + From ! {Ref, Res}, + dist_cntrlr_setup_loop(Socket, TickHandler, Sup); + + {Ref, From, {recv, Length, Timeout}} -> + Res = gen_tcp:recv(Socket, Length, Timeout), + From ! {Ref, Res}, + dist_cntrlr_setup_loop(Socket, TickHandler, Sup); + + {Ref, From, getll} -> + From ! {Ref, {ok, self()}}, + dist_cntrlr_setup_loop(Socket, TickHandler, Sup); + + {Ref, From, {address, Node}} -> + Res = case inet:peername(Socket) of + {ok, Address} -> + case split_node(atom_to_list(Node), $@, []) of + [_,Host] -> + #net_address{address=Address,host=Host, + protocol=tcp, family=inet}; + _ -> + {error, no_node} + end + end, + From ! {Ref, Res}, + dist_cntrlr_setup_loop(Socket, TickHandler, Sup); + + {Ref, From, pre_nodeup} -> + Res = inet:setopts(Socket, + [{active, false}, + {packet, 4}, + nodelay()]), + From ! {Ref, Res}, + dist_cntrlr_setup_loop(Socket, TickHandler, Sup); + + {Ref, From, post_nodeup} -> + Res = inet:setopts(Socket, + [{active, false}, + {packet, 4}, + nodelay()]), + From ! {Ref, Res}, + dist_cntrlr_setup_loop(Socket, TickHandler, Sup); + + {Ref, From, {handshake_complete, _Node, DHandle}} -> + From ! {Ref, ok}, + %% Handshake complete! Begin dispatching traffic... + + %% We use separate process for dispatching input. This + %% is not necessary, but it enables parallel execution + %% of independent work loads at the same time as it + %% simplifies the the implementation... + InputHandler = spawn_opt(?MODULE, dist_cntrlr_input_setup, + [DHandle, Socket, Sup], + [link] ++ ?DIST_CNTRL_COMMON_SPAWN_OPTS), + + flush_controller(InputHandler, Socket), + gen_tcp:controlling_process(Socket, InputHandler), + flush_controller(InputHandler, Socket), + + ok = erlang:dist_ctrl_input_handler(DHandle, InputHandler), + + InputHandler ! DHandle, + + %% From now on we execute on normal priority + process_flag(priority, normal), + erlang:dist_ctrl_get_data_notification(DHandle), + dist_cntrlr_output_loop(DHandle, Socket) + end. + +%% We use active 10 for good throughput while still +%% maintaining back-pressure if the input controller +%% isn't able to handle all incoming messages... +-define(ACTIVE_INPUT, 10). + +dist_cntrlr_input_setup(DHandle, Socket, Sup) -> + link(Sup), + %% Ensure we don't try to put data before registerd + %% as input handler... + receive + DHandle -> + dist_cntrlr_input_loop(DHandle, Socket, 0) + end. + +dist_cntrlr_input_loop(DHandle, Socket, N) when N =< ?ACTIVE_INPUT/2 -> + inet:setopts(Socket, [{active, ?ACTIVE_INPUT - N}]), + dist_cntrlr_input_loop(DHandle, Socket, ?ACTIVE_INPUT); +dist_cntrlr_input_loop(DHandle, Socket, N) -> + receive + {tcp_closed, Socket} -> + %% Connection to remote node terminated... + exit(connection_closed); + + {tcp, Socket, Data} -> + %% Incoming data from remote node... + try erlang:dist_ctrl_put_data(DHandle, Data) + catch _ : _ -> death_row() + end, + dist_cntrlr_input_loop(DHandle, Socket, N-1); + + _ -> + %% Ignore... + dist_cntrlr_input_loop(DHandle, Socket, N) + end. + +dist_cntrlr_send_data(DHandle, Socket) -> + case erlang:dist_ctrl_get_data(DHandle) of + none -> + erlang:dist_ctrl_get_data_notification(DHandle); + Data -> + sock_send(Socket, Data), + dist_cntrlr_send_data(DHandle, Socket) + end. + + +dist_cntrlr_output_loop(DHandle, Socket) -> + receive + dist_data -> + %% Outgoing data from this node... + try dist_cntrlr_send_data(DHandle, Socket) + catch _ : _ -> death_row() + end, + dist_cntrlr_output_loop(DHandle, Socket); + + _ -> + %% Drop garbage message... + dist_cntrlr_output_loop(DHandle, Socket) + + end. + +sock_send(Socket, Data) -> + try gen_tcp:send(Socket, Data) of + ok -> ok; + {error, Reason} -> death_row({send_error, Reason}) + catch + Type : Reason -> death_row({send_error, {Type, Reason}}) + end. + +death_row() -> + death_row(connection_closed). + +death_row(normal) -> + %% We do not want to exit with normal + %% exit reason since it wont bring down + %% linked processes... + death_row(); +death_row(Reason) -> + %% When the connection is on its way down operations + %% begin to fail. We catch the failures and call + %% this function waiting for termination. We should + %% be terminated by one of our links to the other + %% involved parties that began bringing the + %% connection down. By waiting for termination we + %% avoid altering the exit reason for the connection + %% teardown. We however limit the wait to 5 seconds + %% and bring down the connection ourselves if not + %% terminated... + receive after 5000 -> exit(Reason) end. -- cgit v1.2.3 From ca4b42a159c5cc937967c2d04818afa6b8022e0b Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Fri, 30 Jun 2017 19:10:31 +0200 Subject: Support for running test suites with gen_tcp_dist --- lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl b/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl index 002d09fc74..98554ed805 100644 --- a/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl +++ b/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl @@ -736,6 +736,15 @@ dist_cntrlr_output_loop(DHandle, Socket) -> end, dist_cntrlr_output_loop(DHandle, Socket); + {send, From, Ref, Data} -> + %% This is for testing only! + %% + %% Needed by some OTP distribution + %% test suites... + sock_send(Socket, Data), + From ! {Ref, ok}, + dist_cntrlr_output_loop(DHandle, Socket); + _ -> %% Drop garbage message... dist_cntrlr_output_loop(DHandle, Socket) -- cgit v1.2.3 From ffd59fbd9ac262b7aba4b86e7da4992a3e668e24 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 1 Aug 2017 18:34:58 +0200 Subject: Introduce sender in distributed signals and dflag configuration --- lib/kernel/include/dist.hrl | 30 ++++++ lib/kernel/include/dist_util.hrl | 3 + lib/kernel/src/dist_util.erl | 192 ++++++++++++++++++++++++++++++--------- 3 files changed, 181 insertions(+), 44 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/include/dist.hrl b/lib/kernel/include/dist.hrl index d6bccdf474..db4a5eaebc 100644 --- a/lib/kernel/include/dist.hrl +++ b/lib/kernel/include/dist.hrl @@ -40,3 +40,33 @@ -define(DFLAG_UTF8_ATOMS, 16#10000). -define(DFLAG_MAP_TAG, 16#20000). -define(DFLAG_BIG_CREATION, 16#40000). +-define(DFLAG_SEND_SENDER, 16#80000). + +%% DFLAGs that require strict ordering or:ed together... +-define(DFLAGS_STRICT_ORDER_DELIVERY, + ?DFLAG_DIST_HDR_ATOM_CACHE). + + +%% Also update dflag2str() in ../src/dist_util.erl +%% when adding flags... + +-define(DFLAGS_ALL, + (?DFLAG_PUBLISHED + bor ?DFLAG_ATOM_CACHE + bor ?DFLAG_EXTENDED_REFERENCES + bor ?DFLAG_DIST_MONITOR + bor ?DFLAG_FUN_TAGS + bor ?DFLAG_DIST_MONITOR_NAME + bor ?DFLAG_HIDDEN_ATOM_CACHE + bor ?DFLAG_NEW_FUN_TAGS + bor ?DFLAG_EXTENDED_PIDS_PORTS + bor ?DFLAG_EXPORT_PTR_TAG + bor ?DFLAG_BIT_BINARIES + bor ?DFLAG_NEW_FLOATS + bor ?DFLAG_UNICODE_IO + bor ?DFLAG_DIST_HDR_ATOM_CACHE + bor ?DFLAG_SMALL_ATOM_TAGS + bor ?DFLAG_UTF8_ATOMS + bor ?DFLAG_MAP_TAG + bor ?DFLAG_BIG_CREATION + bor ?DFLAG_SEND_SENDER)). diff --git a/lib/kernel/include/dist_util.hrl b/lib/kernel/include/dist_util.hrl index e668b12a82..eeb0f8dd43 100644 --- a/lib/kernel/include/dist_util.hrl +++ b/lib/kernel/include/dist_util.hrl @@ -82,6 +82,9 @@ %% New in kernel-6.0 (OTP 21.0) f_handshake_complete, %% Notify handshake complete + add_flags, %% dflags to add + reject_flags, %% dflags not to use (not all can be rejected) + require_flags %% dflags that are required }). diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index 81dbf442d1..08bd5946cd 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -74,6 +74,48 @@ ticked = 0 }). +dflag2str(?DFLAG_PUBLISHED) -> + "PUBLISHED"; +dflag2str(?DFLAG_ATOM_CACHE) -> + "ATOM_CACHE"; +dflag2str(?DFLAG_EXTENDED_REFERENCES) -> + "EXTENDED_REFERENCES"; +dflag2str(?DFLAG_DIST_MONITOR) -> + "DIST_MONITOR"; +dflag2str(?DFLAG_FUN_TAGS) -> + "FUN_TAGS"; +dflag2str(?DFLAG_DIST_MONITOR_NAME) -> + "DIST_MONITOR_NAME"; +dflag2str(?DFLAG_HIDDEN_ATOM_CACHE) -> + "HIDDEN_ATOM_CACHE"; +dflag2str(?DFLAG_NEW_FUN_TAGS) -> + "NEW_FUN_TAGS"; +dflag2str(?DFLAG_EXTENDED_PIDS_PORTS) -> + "EXTENDED_PIDS_PORTS"; +dflag2str(?DFLAG_EXPORT_PTR_TAG) -> + "EXPORT_PTR_TAG"; +dflag2str(?DFLAG_BIT_BINARIES) -> + "BIT_BINARIES"; +dflag2str(?DFLAG_NEW_FLOATS) -> + "NEW_FLOATS"; +dflag2str(?DFLAG_UNICODE_IO) -> + "UNICODE_IO"; +dflag2str(?DFLAG_DIST_HDR_ATOM_CACHE) -> + "DIST_HDR_ATOM_CACHE"; +dflag2str(?DFLAG_SMALL_ATOM_TAGS) -> + "SMALL_ATOM_TAGS"; +dflag2str(?DFLAG_UTF8_ATOMS) -> + "UTF8_ATOMS"; +dflag2str(?DFLAG_MAP_TAG) -> + "MAP_TAG"; +dflag2str(?DFLAG_BIG_CREATION) -> + "BIG_CREATION"; +dflag2str(?DFLAG_SEND_SENDER) -> + "SEND_SENDER"; +dflag2str(_) -> + "UNKNOWN". + + remove_flag(Flag, Flags) -> case Flags band Flag of 0 -> @@ -82,13 +124,13 @@ remove_flag(Flag, Flags) -> Flags - Flag end. -adjust_flags(ThisFlags, OtherFlags) -> +adjust_flags(ThisFlags, OtherFlags, RejectFlags) -> case (?DFLAG_PUBLISHED band ThisFlags) band OtherFlags of 0 -> {remove_flag(?DFLAG_PUBLISHED, ThisFlags), remove_flag(?DFLAG_PUBLISHED, OtherFlags)}; _ -> - {ThisFlags, OtherFlags} + {ThisFlags, OtherFlags band (bnot RejectFlags)} end. publish_flag(hidden, _) -> @@ -101,36 +143,71 @@ publish_flag(_, OtherNode) -> 0 end. -make_this_flags(RequestType, OtherNode) -> - publish_flag(RequestType, OtherNode) bor - %% The parenthesis below makes the compiler generate better code. - (?DFLAG_EXPORT_PTR_TAG bor - ?DFLAG_EXTENDED_PIDS_PORTS bor - ?DFLAG_EXTENDED_REFERENCES bor - ?DFLAG_DIST_MONITOR bor - ?DFLAG_FUN_TAGS bor - ?DFLAG_DIST_MONITOR_NAME bor - ?DFLAG_HIDDEN_ATOM_CACHE bor - ?DFLAG_NEW_FUN_TAGS bor - ?DFLAG_BIT_BINARIES bor - ?DFLAG_NEW_FLOATS bor - ?DFLAG_UNICODE_IO bor - ?DFLAG_DIST_HDR_ATOM_CACHE bor - ?DFLAG_SMALL_ATOM_TAGS bor - ?DFLAG_UTF8_ATOMS bor - ?DFLAG_MAP_TAG bor - ?DFLAG_BIG_CREATION). - -handshake_other_started(#hs_data{request_type=ReqType}=HSData0) -> +-define(DFLAGS_REMOVABLE, + (?DFLAG_DIST_HDR_ATOM_CACHE + bor ?DFLAG_HIDDEN_ATOM_CACHE + bor ?DFLAG_ATOM_CACHE)). + +-define(DFLAGS_ADDABLE, + (?DFLAGS_ALL + band (bnot (?DFLAG_PUBLISHED + bor ?DFLAG_HIDDEN_ATOM_CACHE + bor ?DFLAG_ATOM_CACHE)))). + +-define(DFLAGS_THIS_DEFAULT, + (?DFLAG_EXPORT_PTR_TAG + bor ?DFLAG_EXTENDED_PIDS_PORTS + bor ?DFLAG_EXTENDED_REFERENCES + bor ?DFLAG_DIST_MONITOR + bor ?DFLAG_FUN_TAGS + bor ?DFLAG_DIST_MONITOR_NAME + bor ?DFLAG_NEW_FUN_TAGS + bor ?DFLAG_BIT_BINARIES + bor ?DFLAG_NEW_FLOATS + bor ?DFLAG_UNICODE_IO + bor ?DFLAG_DIST_HDR_ATOM_CACHE + bor ?DFLAG_SMALL_ATOM_TAGS + bor ?DFLAG_UTF8_ATOMS + bor ?DFLAG_MAP_TAG + bor ?DFLAG_BIG_CREATION + bor ?DFLAG_SEND_SENDER)). + +make_this_flags(RequestType, AddFlags, RemoveFlags, OtherNode) -> + case RemoveFlags band (bnot ?DFLAGS_REMOVABLE) of + 0 -> ok; + Rerror -> exit({"Rejecting non rejectable flags", Rerror}) + end, + case AddFlags band (bnot ?DFLAGS_ADDABLE) of + 0 -> ok; + Aerror -> exit({"Adding non addable flags", Aerror}) + end, + Flgs0 = ?DFLAGS_THIS_DEFAULT, + Flgs1 = Flgs0 bor publish_flag(RequestType, OtherNode), + Flgs2 = Flgs1 bor AddFlags, + Flgs3 = Flgs2 band (bnot (?DFLAG_HIDDEN_ATOM_CACHE + bor ?DFLAG_ATOM_CACHE)), + Flgs3 band (bnot RemoveFlags). + +handshake_other_started(#hs_data{request_type=ReqType, + add_flags=AddFlgs0, + reject_flags=RejFlgs0, + require_flags=ReqFlgs0}=HSData0) -> + AddFlgs = convert_flags(AddFlgs0), + RejFlgs = convert_flags(RejFlgs0), + ReqFlgs = convert_flags(ReqFlgs0), {PreOtherFlags,Node,Version} = recv_name(HSData0), - PreThisFlags = make_this_flags(ReqType, Node), + PreThisFlags = make_this_flags(ReqType, AddFlgs, RejFlgs, Node), {ThisFlags, OtherFlags} = adjust_flags(PreThisFlags, - PreOtherFlags), + PreOtherFlags, + RejFlgs), HSData = HSData0#hs_data{this_flags=ThisFlags, other_flags=OtherFlags, other_version=Version, other_node=Node, - other_started=true}, + other_started=true, + add_flags=AddFlgs, + reject_flags=RejFlgs, + require_flags=ReqFlgs}, check_dflags(HSData), is_allowed(HSData), ?debug({"MD5 connection from ~p (V~p)~n", @@ -165,23 +242,18 @@ is_allowed(#hs_data{other_node = Node, end. %% -%% Check that both nodes can handle the same types of extended -%% node containers. If they can not, abort the connection. +%% Check mandatory flags... %% check_dflags(#hs_data{other_node = Node, other_flags = OtherFlags, - other_started = OtherStarted} = HSData) -> - - Mandatory = [{?DFLAG_EXTENDED_REFERENCES, "EXTENDED_REFERENCES"}, - {?DFLAG_EXTENDED_PIDS_PORTS, "EXTENDED_PIDS_PORTS"}, - {?DFLAG_UTF8_ATOMS, "UTF8_ATOMS"}], - Missing = lists:filtermap(fun({Bit, Str}) -> - case Bit band OtherFlags of - Bit -> false; - 0 -> {true, Str} - end - end, - Mandatory), + other_started = OtherStarted, + require_flags = RequiredFlags} = HSData) -> + Mandatory = ((?DFLAG_EXTENDED_REFERENCES + bor ?DFLAG_EXTENDED_PIDS_PORTS + bor ?DFLAG_UTF8_ATOMS) + bor RequiredFlags), + Missing = check_mandatory(0, ?DFLAGS_ALL, Mandatory, + OtherFlags, []), case Missing of [] -> ok; @@ -201,6 +273,22 @@ check_dflags(#hs_data{other_node = Node, ?shutdown2(Node, {check_dflags_failed, Missing}) end. +check_mandatory(_Bit, 0, _Mandatory, _OtherFlags, Missing) -> + Missing; +check_mandatory(Bit, Left, Mandatory, OtherFlags, Missing) -> + DFlag = (1 bsl Bit), + NewLeft = Left band (bnot DFlag), + NewMissing = case {DFlag band Mandatory, + DFlag band OtherFlags} of + {DFlag, 0} -> + %% Mandatory and missing... + [dflag2str(DFlag) | Missing]; + _ -> + %% Not mandatory or present... + Missing + end, + check_mandatory(Bit+1, NewLeft, Mandatory, OtherFlags, NewMissing). + %% No nodedown will be sent if we fail before this process has %% succeeded to mark the node as pending. @@ -314,13 +402,24 @@ flush_down() -> end. handshake_we_started(#hs_data{request_type=ReqType, - other_node=Node}=PreHSData) -> - PreThisFlags = make_this_flags(ReqType, Node), - HSData = PreHSData#hs_data{this_flags=PreThisFlags}, + other_node=Node, + add_flags=AddFlgs0, + reject_flags=RejFlgs0, + require_flags=ReqFlgs0}=PreHSData) -> + AddFlgs = convert_flags(AddFlgs0), + RejFlgs = convert_flags(RejFlgs0), + ReqFlgs = convert_flags(ReqFlgs0), + PreThisFlags = make_this_flags(ReqType, AddFlgs, RejFlgs, Node), + HSData = PreHSData#hs_data{this_flags = PreThisFlags, + add_flags = AddFlgs, + reject_flags = RejFlgs, + require_flags = ReqFlgs}, send_name(HSData), recv_status(HSData), {PreOtherFlags,ChallengeA} = recv_challenge(HSData), - {ThisFlags,OtherFlags} = adjust_flags(PreThisFlags, PreOtherFlags), + {ThisFlags,OtherFlags} = adjust_flags(PreThisFlags, + PreOtherFlags, + RejFlgs), NewHSData = HSData#hs_data{this_flags = ThisFlags, other_flags = OtherFlags, other_started = false}, @@ -342,6 +441,11 @@ convert_old_hsdata(OldHsData) -> true = NoMissing > 0, list_to_tuple(OHSDL ++ lists:duplicate(NoMissing, undefined)). +convert_flags(Flags) when is_integer(Flags) -> + Flags; +convert_flags(_Undefined) -> + 0. + %% -------------------------------------------------------------- %% The connection has been established. %% -------------------------------------------------------------- -- cgit v1.2.3 From fb2bd4fad031d87984cfa1715ce28be384478a15 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Mon, 28 Aug 2017 17:12:08 +0200 Subject: Always reply to sync requests Otherwise processes doing io could get stuck. See ERL-472. --- lib/kernel/src/user_drv.erl | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/user_drv.erl b/lib/kernel/src/user_drv.erl index b794d4f45e..99ea4210bd 100644 --- a/lib/kernel/src/user_drv.erl +++ b/lib/kernel/src/user_drv.erl @@ -175,6 +175,18 @@ server_loop(Iport, Oport, Curr, User, Gr, {Resp, IOQ} = IOQueue) -> {Iport,eof} -> Curr ! {self(),eof}, server_loop(Iport, Oport, Curr, User, Gr, IOQueue); + + %% We always handle geometry and unicode requests + {Requester,tty_geometry} -> + Requester ! {self(),tty_geometry,get_tty_geometry(Iport)}, + server_loop(Iport, Oport, Curr, User, Gr, IOQueue); + {Requester,get_unicode_state} -> + Requester ! {self(),get_unicode_state,get_unicode_state(Iport)}, + server_loop(Iport, Oport, Curr, User, Gr, IOQueue); + {Requester,set_unicode_state, Bool} -> + Requester ! {self(),set_unicode_state,set_unicode_state(Iport,Bool)}, + server_loop(Iport, Oport, Curr, User, Gr, IOQueue); + Req when element(1,Req) =:= User orelse element(1,Req) =:= Curr, tuple_size(Req) =:= 2 orelse tuple_size(Req) =:= 3 -> %% We match {User|Curr,_}|{User|Curr,_,_} @@ -224,21 +236,16 @@ server_loop(Iport, Oport, Curr, User, Gr, {Resp, IOQ} = IOQueue) -> _ -> % not current, just remove it server_loop(Iport, Oport, Curr, User, gr_del_pid(Gr, Pid), IOQueue) end; + {Requester, {put_chars_sync, _, _, Reply}} -> + %% We need to ack the Req otherwise originating process will hang forever + %% Do discard the output to non visible shells (as was done previously) + Requester ! {reply, Reply}, + server_loop(Iport, Oport, Curr, User, Gr, IOQueue); _X -> - %% Ignore unknown messages. - server_loop(Iport, Oport, Curr, User, Gr, IOQueue) + %% Ignore unknown messages. + server_loop(Iport, Oport, Curr, User, Gr, IOQueue) end. -%% We always handle geometry and unicode requests -handle_req({Curr,tty_geometry},Iport,_Oport,IOQueue) -> - Curr ! {self(),tty_geometry,get_tty_geometry(Iport)}, - IOQueue; -handle_req({Curr,get_unicode_state},Iport,_Oport,IOQueue) -> - Curr ! {self(),get_unicode_state,get_unicode_state(Iport)}, - IOQueue; -handle_req({Curr,set_unicode_state, Bool},Iport,_Oport,IOQueue) -> - Curr ! {self(),set_unicode_state,set_unicode_state(Iport,Bool)}, - IOQueue; handle_req(next,Iport,Oport,{false,IOQ}=IOQueue) -> case queue:out(IOQ) of {empty,_} -> -- cgit v1.2.3 From 4fb3486f3566da9ffac43119484bff233a81c6fc Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Wed, 30 Aug 2017 12:18:46 +0200 Subject: Let io:get_chars/2 (echo off) fetch chars without eol When option 'echo off' is set no line editing is done and there is no need to wait until eol for io:get_chars. This solves the issue of not needing to wait for eol until chars are received from stdin, sometimes the user wants direct/raw unprocessed input. --- lib/kernel/src/group.erl | 79 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 15 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/group.erl b/lib/kernel/src/group.erl index bf785959ff..6e2f2d1865 100644 --- a/lib/kernel/src/group.erl +++ b/lib/kernel/src/group.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-2016. 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. @@ -261,11 +261,11 @@ io_request({put_chars,latin1,M,F,As}, Drv, From, Buf) -> end; io_request({get_chars,Encoding,Prompt,N}, Drv, _From, Buf) -> - get_chars(Prompt, io_lib, collect_chars, N, Drv, Buf, Encoding); + get_chars_n(Prompt, io_lib, collect_chars, N, Drv, Buf, Encoding); io_request({get_line,Encoding,Prompt}, Drv, _From, Buf) -> - get_chars(Prompt, io_lib, collect_line, [], Drv, Buf, Encoding); + get_chars_line(Prompt, io_lib, collect_line, [], Drv, Buf, Encoding); io_request({get_until,Encoding, Prompt,M,F,As}, Drv, _From, Buf) -> - get_chars(Prompt, io_lib, get_until, {M,F,As}, Drv, Buf, Encoding); + get_chars_line(Prompt, io_lib, get_until, {M,F,As}, Drv, Buf, Encoding); io_request({get_password,_Encoding},Drv,_From,Buf) -> get_password_chars(Drv, Buf); io_request({setopts,Opts}, Drv, _From, Buf) when is_list(Opts) -> @@ -434,7 +434,7 @@ getopts(Drv,Buf) -> {ok,[Exp,Echo,Bin,Uni],Buf}. -%% get_chars(Prompt, Module, Function, XtraArgument, Drv, Buffer) +%% get_chars_*(Prompt, Module, Function, XtraArgument, Drv, Buffer) %% Gets characters from the input Drv until as the applied function %% returns {stop,Result,Rest}. Does not block output until input has been %% received. @@ -452,12 +452,21 @@ get_password_chars(Drv,Buf) -> {exit, terminated} end. -get_chars(Prompt, M, F, Xa, Drv, Buf, Encoding) -> +get_chars_n(Prompt, M, F, Xa, Drv, Buf, Encoding) -> + Pbs = prompt_bytes(Prompt, Encoding), + case get(echo) of + true -> + get_chars_loop(Pbs, M, F, Xa, Drv, Buf, start, Encoding); + false -> + get_chars_n_loop(Pbs, M, F, Xa, Drv, Buf, start, Encoding) + end. + +get_chars_line(Prompt, M, F, Xa, Drv, Buf, Encoding) -> Pbs = prompt_bytes(Prompt, Encoding), get_chars_loop(Pbs, M, F, Xa, Drv, Buf, start, Encoding). get_chars_loop(Pbs, M, F, Xa, Drv, Buf0, State, Encoding) -> - Result = case get(echo) of + Result = case get(echo) of true -> get_line(Buf0, Pbs, Drv, Encoding); false -> @@ -466,8 +475,8 @@ get_chars_loop(Pbs, M, F, Xa, Drv, Buf0, State, Encoding) -> get_line_echo_off(Buf0, Pbs, Drv) end, case Result of - {done,Line,Buf1} -> - get_chars_apply(Pbs, M, F, Xa, Drv, Buf1, State, Line, Encoding); + {done,Line,Buf} -> + get_chars_apply(Pbs, M, F, Xa, Drv, Buf, State, Line, Encoding); interrupted -> {error,{error,interrupted},[]}; terminated -> @@ -476,12 +485,29 @@ get_chars_loop(Pbs, M, F, Xa, Drv, Buf0, State, Encoding) -> get_chars_apply(Pbs, M, F, Xa, Drv, Buf, State0, Line, Encoding) -> case catch M:F(State0, cast(Line,get(read_mode), Encoding), Encoding, Xa) of - {stop,Result,Rest} -> - {ok,Result,append(Rest, Buf, Encoding)}; - {'EXIT',_} -> - {error,{error,err_func(M, F, Xa)},[]}; - State1 -> - get_chars_loop(Pbs, M, F, Xa, Drv, Buf, State1, Encoding) + {stop,Result,Rest} -> + {ok,Result,append(Rest, Buf, Encoding)}; + {'EXIT',_} -> + {error,{error,err_func(M, F, Xa)},[]}; + State1 -> + get_chars_loop(Pbs, M, F, Xa, Drv, Buf, State1, Encoding) + end. + +get_chars_n_loop(Pbs, M, F, Xa, Drv, Buf0, State, Encoding) -> + try M:F(State, cast(Buf0, get(read_mode), Encoding), Encoding, Xa) of + {stop,Result,Rest} -> + {ok, Result, Rest}; + State1 -> + case get_chars_echo_off(Pbs, Drv) of + interrupted -> + {error,{error,interrupted},[]}; + terminated -> + {exit,terminated}; + Buf -> + get_chars_n_loop(Pbs, M, F, Xa, Drv, Buf, State1, Encoding) + end + catch _:_ -> + {error,{error,err_func(M, F, Xa)},[]} end. %% Convert error code to make it look as before @@ -684,6 +710,29 @@ get_line_echo_off1({Chars,[]}, Drv) -> get_line_echo_off1({Chars,Rest}, _Drv) -> {done,lists:reverse(Chars),case Rest of done -> []; _ -> Rest end}. +get_chars_echo_off(Pbs, Drv) -> + send_drv_reqs(Drv, [{put_chars, unicode,Pbs}]), + get_chars_echo_off1(Drv). + +get_chars_echo_off1(Drv) -> + receive + {Drv, {data, Cs}} -> + Cs; + {Drv, eof} -> + eof; + {io_request,From,ReplyAs,Req} when is_pid(From) -> + io_request(Req, From, ReplyAs, Drv, []), + get_chars_echo_off1(Drv); + {reply,{{From,ReplyAs},Reply}} when From =/= undefined -> + %% We take care of replies from puts here as well + io_reply(From, ReplyAs, Reply), + get_chars_echo_off1(Drv); + {'EXIT',Drv,interrupt} -> + interrupted; + {'EXIT',Drv,_} -> + terminated + end. + %% We support line editing for the ICANON mode except the following %% line editing characters, which already has another meaning in %% echo-on mode (See Advanced Programming in the Unix Environment, 2nd ed, -- cgit v1.2.3 From d88c1c73f733f57e1a176639186bd5d99d585a12 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Thu, 6 Jul 2017 12:28:09 +0200 Subject: kernel: Improve handling of Unicode --- lib/kernel/src/file_server.erl | 8 ++++---- lib/kernel/src/global.erl | 16 ++++++++-------- lib/kernel/src/net_kernel.erl | 2 +- lib/kernel/src/pg2.erl | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/file_server.erl b/lib/kernel/src/file_server.erl index 6504174cbc..6e8f64d932 100644 --- a/lib/kernel/src/file_server.erl +++ b/lib/kernel/src/file_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2016. All Rights Reserved. +%% Copyright Ericsson AB 2000-2017. 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. @@ -207,7 +207,7 @@ handle_call(stop, _From, Handle) -> {stop, normal, stopped, Handle}; handle_call(Request, From, Handle) -> - error_logger:error_msg("handle_call(~p, ~p, _)", [Request, From]), + error_logger:error_msg("handle_call(~tp, ~tp, _)", [Request, From]), {noreply, Handle}. %%---------------------------------------------------------------------- @@ -220,7 +220,7 @@ handle_call(Request, From, Handle) -> -spec handle_cast(term(), state()) -> {'noreply', state()}. handle_cast(Msg, State) -> - error_logger:error_msg("handle_cast(~p, _)", [Msg]), + error_logger:error_msg("handle_cast(~tp, _)", [Msg]), {noreply, State}. %%---------------------------------------------------------------------- @@ -243,7 +243,7 @@ handle_info({'EXIT', Handle, _Reason}, Handle) -> {stop, normal, Handle}; handle_info(Info, State) -> - error_logger:error_msg("handle_Info(~p, _)", [Info]), + error_logger:error_msg("handle_Info(~tp, _)", [Info]), {noreply, State}. %%---------------------------------------------------------------------- diff --git a/lib/kernel/src/global.erl b/lib/kernel/src/global.erl index a9e92b28b8..a38522eb5c 100644 --- a/lib/kernel/src/global.erl +++ b/lib/kernel/src/global.erl @@ -262,7 +262,7 @@ check_dupname(Name, Pid) -> {ok, allow} -> true; _ -> - S = "global: ~w registered under several names: ~w\n", + S = "global: ~w registered under several names: ~tw\n", Names = [Name | [Name1 || {_Pid, Name1} <- PidNames]], error_logger:error_msg(S, [Pid, Names]), false @@ -659,7 +659,7 @@ handle_call(stop, _From, S) -> handle_call(Request, From, S) -> error_logger:warning_msg("The global_name_server " "received an unexpected message:\n" - "handle_call(~p, ~p, _)\n", + "handle_call(~tp, ~tp, _)\n", [Request, From]), {noreply, S}. @@ -828,7 +828,7 @@ handle_cast({async_del_lock, _ResourceId, _Pid}, S) -> handle_cast(Request, S) -> error_logger:warning_msg("The global_name_server " "received an unexpected message:\n" - "handle_cast(~p, _)\n", [Request]), + "handle_cast(~tp, _)\n", [Request]), {noreply, S}. %%======================================================================== @@ -955,7 +955,7 @@ handle_info({'DOWN', MonitorRef, process, _Pid, _Info}, S0) -> handle_info(Message, S) -> error_logger:warning_msg("The global_name_server " "received an unexpected message:\n" - "handle_info(~p, _)\n", [Message]), + "handle_info(~tp, _)\n", [Message]), {noreply, S}. @@ -1949,13 +1949,13 @@ exchange_names([{Name, Pid, Method} | Tail], Node, Ops, Res) -> exchange_names(Tail, Node, [Op | Ops], [Op | Res]); {badrpc, Badrpc} -> error_logger:info_msg("global: badrpc ~w received when " - "conflicting name ~w was found\n", + "conflicting name ~tw was found\n", [Badrpc, Name]), Op = {insert, {Name, Pid, Method}}, exchange_names(Tail, Node, [Op | Ops], Res); Else -> error_logger:info_msg("global: Resolve method ~w for " - "conflicting name ~w returned ~w\n", + "conflicting name ~tw returned ~tw\n", [Method, Name, Else]), Op = {delete, Name}, exchange_names(Tail, Node, [Op | Ops], [Op | Res]) @@ -1984,7 +1984,7 @@ minmax(P1,P2) -> Pid2 :: pid(). random_exit_name(Name, Pid, Pid2) -> {Min, Max} = minmax(Pid, Pid2), - error_logger:info_msg("global: Name conflict terminating ~w\n", + error_logger:info_msg("global: Name conflict terminating ~tw\n", [{Name, Max}]), exit(Max, kill), Min. @@ -2200,7 +2200,7 @@ unexpected_message({'EXIT', _Pid, _Reason}, _What) -> ok; unexpected_message(Message, What) -> error_logger:warning_msg("The global_name_server ~w process " - "received an unexpected message:\n~p\n", + "received an unexpected message:\n~tp\n", [What, Message]). %%% Utilities diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index ddda396713..7da89dd7cb 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -778,7 +778,7 @@ handle_info(transition_period_end, {noreply,State#state{tick = #tick{ticker = Tckr, time = T}}}; handle_info(X, State) -> - error_msg("Net kernel got ~w~n",[X]), + error_msg("Net kernel got ~tw~n",[X]), {noreply,State}. %% ----------------------------------------------------------- diff --git a/lib/kernel/src/pg2.erl b/lib/kernel/src/pg2.erl index edf4aedde2..c4732f37ee 100644 --- a/lib/kernel/src/pg2.erl +++ b/lib/kernel/src/pg2.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. @@ -199,7 +199,7 @@ handle_call({delete, Name}, _From, S) -> {reply, ok, S}; handle_call(Request, From, S) -> error_logger:warning_msg("The pg2 server received an unexpected message:\n" - "handle_call(~p, ~p, _)\n", + "handle_call(~tp, ~tp, _)\n", [Request, From]), {noreply, S}. -- cgit v1.2.3 From 41b856a2cb270ef807beaa84afe1bb890de5f237 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 24 Aug 2017 12:18:53 +0200 Subject: kernel: update simple error logger to print Unicode strings If printable range is set to 'unicode', the simple error logger will now recognize code points > 255 and print lists containing these as strings. --- lib/kernel/src/error_logger.erl | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index 9bf8547745..f07ec52cfa 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -499,27 +499,16 @@ display4(A = [_|_]) -> display4(A) -> erlang:display(A). -string_p([]) -> + +string_p(Term) when is_list(Term) -> + string_p1(lists:flatten(Term)); +string_p(_Term) -> + false. + +string_p1([]) -> false; -string_p(Term) -> - string_p1(Term). - -string_p1([H|T]) when is_integer(H), H >= $\s, H < 255 -> - string_p1(T); -string_p1([$\n|T]) -> string_p1(T); -string_p1([$\r|T]) -> string_p1(T); -string_p1([$\t|T]) -> string_p1(T); -string_p1([$\v|T]) -> string_p1(T); -string_p1([$\b|T]) -> string_p1(T); -string_p1([$\f|T]) -> string_p1(T); -string_p1([$\e|T]) -> string_p1(T); -string_p1([H|T]) when is_list(H) -> - case string_p1(H) of - true -> string_p1(T); - _ -> false - end; -string_p1([]) -> true; -string_p1(_) -> false. +string_p1(FlatList) -> + io_lib:printable_list(FlatList). -spec limit_term(term()) -> term(). -- cgit v1.2.3 From 948ee9b34f34a55cb4b70b7077a849c7dc7a5f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Fri, 21 Jul 2017 14:20:16 +0200 Subject: Replace the zlib driver with a NIF All operations will now yield appropriately, allowing them to be used freely in concurrent applications. This commit also deprecates the functions listed below, although they won't raise deprecation warnings until OTP 21: zlib:adler32 zlib:crc32 zlib:inflateChunk zlib:getBufSize zlib:setBufSize The behavior of throwing an error when a dictionary is required for decompression has also been deprecated. --- lib/kernel/test/Makefile | 4 +- lib/kernel/test/kernel_bench.spec | 1 + lib/kernel/test/zlib_SUITE.erl | 351 +++++++++++++++++++++++++++----------- 3 files changed, 258 insertions(+), 98 deletions(-) create mode 100644 lib/kernel/test/kernel_bench.spec (limited to 'lib/kernel') diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile index b9942e899f..efe3a68531 100644 --- a/lib/kernel/test/Makefile +++ b/lib/kernel/test/Makefile @@ -148,8 +148,8 @@ release_tests_spec: make_emakefile $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)" $(INSTALL_DATA) $(APP_FILES) "$(RELSYSDIR)" - $(INSTALL_DATA) kernel.spec kernel_smoke.spec $(EMAKEFILE)\ - $(COVERFILE) "$(RELSYSDIR)" + $(INSTALL_DATA) kernel.spec kernel_smoke.spec kernel_bench.spec \ + $(EMAKEFILE) $(COVERFILE) "$(RELSYSDIR)" chmod -R u+w "$(RELSYSDIR)" @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) diff --git a/lib/kernel/test/kernel_bench.spec b/lib/kernel/test/kernel_bench.spec new file mode 100644 index 0000000000..8de60dae31 --- /dev/null +++ b/lib/kernel/test/kernel_bench.spec @@ -0,0 +1 @@ +{groups,"../kernel_test",zlib_SUITE,[bench]}. diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl index 4b67fce9a8..efbbc805a0 100644 --- a/lib/kernel/test/zlib_SUITE.erl +++ b/lib/kernel/test/zlib_SUITE.erl @@ -21,6 +21,7 @@ -module(zlib_SUITE). -include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). -compile(export_all). @@ -43,7 +44,7 @@ end end()). --define(BARG, {'EXIT',{badarg,[{zlib,_,_,_}|_]}}). +-define(BARG, {'EXIT',{badarg,[{_,_,_,_}|_]}}). -define(DATA_ERROR, {'EXIT',{data_error,[{zlib,_,_,_}|_]}}). init_per_testcase(_Func, Config) -> @@ -74,7 +75,8 @@ suite() -> {timetrap,{minutes,1}}]. all() -> - [{group, api}, {group, examples}, {group, func}, smp, + [{group, api}, {group, examples}, {group, func}, + {group, bench}, smp, otp_9981, otp_7359]. @@ -84,14 +86,19 @@ groups() -> api_deflateSetDictionary, api_deflateReset, api_deflateParams, api_deflate, api_deflateEnd, api_inflateInit, api_inflateSetDictionary, api_inflateGetDictionary, - api_inflateSync, api_inflateReset, api_inflate, api_inflateChunk, - api_inflateEnd, api_setBufsz, api_getBufsz, api_crc32, - api_adler32, api_getQSize, api_un_compress, api_un_zip, + api_inflateReset, api_inflate2, api_inflate3, api_inflateChunk, + api_safeInflate, api_inflateEnd, api_crc32, + api_adler32, api_un_compress, api_un_zip, api_g_un_zip]}, {examples, [], [intro]}, {func, [], [zip_usage, gz_usage, gz_usage2, compress_usage, - dictionary_usage, large_deflate, crc, adler]}]. + dictionary_usage, large_deflate, crc, adler, + only_allow_owner, sub_heap_binaries]}, + {bench, + [inflate_bench_zeroed, inflate_bench_rand, + deflate_bench_zeroed, deflate_bench_rand, + chunk_bench_zeroed, chunk_bench_rand]}]. init_per_suite(Config) -> Config. @@ -244,7 +251,7 @@ api_deflateEnd(Config) when is_list(Config) -> Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1, default)), ?m(ok, zlib:deflateEnd(Z1)), - ?m({'EXIT', {einval,_}}, zlib:deflateEnd(Z1)), %% ?? + ?m(?BARG, zlib:deflateEnd(Z1)), ?m(?BARG, zlib:deflateEnd(gurka)), ?m(ok, zlib:deflateInit(Z1, default)), ?m(B when is_list(B), zlib:deflate(Z1, <<"Kilroy was here">>)), @@ -261,7 +268,7 @@ api_inflateInit(Config) when is_list(Config) -> Z1 = zlib:open(), ?m(?BARG, zlib:inflateInit(gurka)), ?m(ok, zlib:inflateInit(Z1)), - ?m({'EXIT',{einval,_}}, zlib:inflateInit(Z1, 15)), %% ?? + ?m(?BARG, zlib:inflateInit(Z1, 15)), lists:foreach(fun(Wbits) -> Z11 = zlib:open(), ?m(ok, zlib:inflateInit(Z11,Wbits)), @@ -291,12 +298,13 @@ api_inflateSetDictionary(Config) when is_list(Config) -> %% Test inflateGetDictionary. api_inflateGetDictionary(Config) when is_list(Config) -> Z1 = zlib:open(), + zlib:inflateInit(Z1), IsOperationSupported = case catch zlib:inflateGetDictionary(Z1) of - {'EXIT',{einval,_}} -> true; - {'EXIT',{enotsup,_}} -> false + ?BARG -> false; + _ -> true end, - _ = zlib:close(Z1), + zlib:close(Z1), api_inflateGetDictionary_if_supported(IsOperationSupported). api_inflateGetDictionary_if_supported(false) -> @@ -306,45 +314,42 @@ api_inflateGetDictionary_if_supported(true) -> Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1)), Dict = <<"foobar barfoo foo bar far boo">>, - ?m(_, zlib:deflateSetDictionary(Z1, Dict)), + Checksum = zlib:deflateSetDictionary(Z1, Dict), Payload = <<"foobarbarbar">>, Compressed = zlib:deflate(Z1, Payload, finish), ?m(ok, zlib:close(Z1)), - % Decompress and test dictionary extraction + % Decompress and test dictionary extraction with inflate/2 Z2 = zlib:open(), ?m(ok, zlib:inflateInit(Z2)), ?m(<<>>, iolist_to_binary(zlib:inflateGetDictionary(Z2))), ?m({'EXIT',{stream_error,_}}, zlib:inflateSetDictionary(Z2, Dict)), - ?m({'EXIT',{{need_dictionary,_},_}}, zlib:inflate(Z2, Compressed)), + ?m({'EXIT',{{need_dictionary,Checksum},_}}, zlib:inflate(Z2, Compressed)), ?m(ok, zlib:inflateSetDictionary(Z2, Dict)), ?m(Dict, iolist_to_binary(zlib:inflateGetDictionary(Z2))), - ?m(Payload, iolist_to_binary(zlib:inflate(Z2, Compressed))), + Payload = iolist_to_binary(zlib:inflate(Z2, [])), ?m(ok, zlib:close(Z2)), ?m(?BARG, zlib:inflateSetDictionary(Z2, Dict)), - ok. -%% Test inflateSync. -api_inflateSync(Config) when is_list(Config) -> - {skip,"inflateSync/1 sucks"}. -%% Z1 = zlib:open(), -%% ?m(ok, zlib:deflateInit(Z1)), -%% B1list0 = zlib:deflate(Z1, "gurkan gurra ger galna tunnor", full), -%% B2 = zlib:deflate(Z1, "grodan boll", finish), -%% io:format("~p\n", [B1list0]), -%% io:format("~p\n", [B2]), -%% ?m(ok, zlib:deflateEnd(Z1)), -%% B1 = clobber(14, list_to_binary(B1list0)), -%% Compressed = list_to_binary([B1,B2]), -%% io:format("~p\n", [Compressed]), - -%% ?m(ok, zlib:inflateInit(Z1)), -%% ?m(?BARG, zlib:inflateSync(gurka)), -%% ?m({'EXIT',{data_error,_}}, zlib:inflate(Z1, Compressed)), -%% ?m(ok, zlib:inflateSync(Z1)), -%% Ubs = zlib:inflate(Z1, []), -%% <<"grodan boll">> = list_to_binary(Ubs), -%% ?m(ok, zlib:close(Z1)). + %% ... And do the same for inflate/3 + Z3 = zlib:open(), + ?m(ok, zlib:inflateInit(Z3)), + ?m(<<>>, iolist_to_binary(zlib:inflateGetDictionary(Z3))), + ?m({'EXIT',{stream_error,_}}, zlib:inflateSetDictionary(Z3, Dict)), + + {need_dictionary, Checksum, _Output = []} = + zlib:inflate(Z3, Compressed, [{exception_on_need_dict, false}]), + + ?m(ok, zlib:inflateSetDictionary(Z3, Dict)), + ?m(Dict, iolist_to_binary(zlib:inflateGetDictionary(Z3))), + + Payload = iolist_to_binary( + zlib:inflate(Z3, [], [{exception_on_need_dict, false}])), + + ?m(ok, zlib:close(Z3)), + ?m(?BARG, zlib:inflateSetDictionary(Z3, Dict)), + + ok. clobber(N, Bin) when is_binary(Bin) -> T = list_to_tuple(binary_to_list(Bin)), @@ -362,8 +367,8 @@ api_inflateReset(Config) when is_list(Config) -> ?m(ok, zlib:inflateReset(Z1)), ?m(ok, zlib:close(Z1)). -%% Test inflate. -api_inflate(Config) when is_list(Config) -> +%% Test inflate/2 +api_inflate2(Config) when is_list(Config) -> Data = [<<1,2,2,3,3,3,4,4,4,4>>], Compressed = zlib:compress(Data), Z1 = zlib:open(), @@ -375,12 +380,32 @@ api_inflate(Config) when is_list(Config) -> ?m(Data, zlib:inflate(Z1, Compressed)), ?m(?BARG, zlib:inflate(gurka, Compressed)), ?m(?BARG, zlib:inflate(Z1, 4384)), - ?m(?BARG, zlib:inflate(Z1, [atom_list])), + ?m(?BARG, zlib:inflate(Z1, [atom_list])), ?m(ok, zlib:inflateEnd(Z1)), ?m(ok, zlib:inflateInit(Z1)), ?m({'EXIT',{data_error,_}}, zlib:inflate(Z1, <<2,1,2,1,2>>)), ?m(ok, zlib:close(Z1)). +%% Test inflate/3; same as inflate/2 but with the default options inverted. +api_inflate3(Config) when is_list(Config) -> + Data = [<<1,2,2,3,3,3,4,4,4,4>>], + Options = [{exception_on_need_dict, false}], + Compressed = zlib:compress(Data), + Z1 = zlib:open(), + ?m(ok, zlib:inflateInit(Z1)), + ?m([], zlib:inflate(Z1, <<>>, Options)), + ?m(Data, zlib:inflate(Z1, Compressed)), + ?m(ok, zlib:inflateEnd(Z1)), + ?m(ok, zlib:inflateInit(Z1)), + ?m(Data, zlib:inflate(Z1, Compressed, Options)), + ?m(?BARG, zlib:inflate(gurka, Compressed, Options)), + ?m(?BARG, zlib:inflate(Z1, 4384, Options)), + ?m(?BARG, zlib:inflate(Z1, [atom_list], Options)), + ?m(ok, zlib:inflateEnd(Z1)), + ?m(ok, zlib:inflateInit(Z1)), + ?m({'EXIT',{data_error,_}}, zlib:inflate(Z1, <<2,1,2,1,2>>, Options)), + ?m(ok, zlib:close(Z1)). + %% Test inflateChunk. api_inflateChunk(Config) when is_list(Config) -> ChunkSize = 1024, @@ -388,71 +413,107 @@ api_inflateChunk(Config) when is_list(Config) -> Part1 = binary:part(Data, 0, ChunkSize), Part2 = binary:part(Data, ChunkSize, ChunkSize), Part3 = binary:part(Data, ChunkSize * 2, ChunkSize), + Compressed = zlib:compress(Data), Z1 = zlib:open(), + zlib:setBufSize(Z1, ChunkSize), + ?m(ok, zlib:inflateInit(Z1)), - ?m([], zlib:inflateChunk(Z1, <<>>)), - ?m({more, Part1}, zlib:inflateChunk(Z1, Compressed)), - ?m({more, Part2}, zlib:inflateChunk(Z1)), - ?m(Part3, zlib:inflateChunk(Z1)), - ?m(ok, zlib:inflateEnd(Z1)), + 0 = iolist_size(zlib:inflateChunk(Z1, <<>>)), + + {more, Part1AsIOList} = zlib:inflateChunk(Z1, Compressed), + {more, Part2AsIOList} = zlib:inflateChunk(Z1), + {more, Part3AsIOList} = zlib:inflateChunk(Z1), + [] = zlib:inflateChunk(Z1), + + ?m(Part1, iolist_to_binary(Part1AsIOList)), + ?m(Part2, iolist_to_binary(Part2AsIOList)), + ?m(Part3, iolist_to_binary(Part3AsIOList)), + + ?m(ok, zlib:inflateEnd(Z1)), ?m(ok, zlib:inflateInit(Z1)), - ?m({more, Part1}, zlib:inflateChunk(Z1, Compressed)), + + ?m({more, Part1AsIOList}, zlib:inflateChunk(Z1, Compressed)), ?m(ok, zlib:inflateReset(Z1)), - zlib:setBufSize(Z1, size(Data)), - ?m(Data, zlib:inflateChunk(Z1, Compressed)), - ?m(ok, zlib:inflateEnd(Z1)), + zlib:setBufSize(Z1, byte_size(Data) + 1), + DataAsIOList = zlib:inflateChunk(Z1, Compressed), + ?m(Data, iolist_to_binary(DataAsIOList)), + + ?m(ok, zlib:inflateEnd(Z1)), ?m(ok, zlib:inflateInit(Z1)), + ?m(?BARG, zlib:inflateChunk(gurka, Compressed)), ?m(?BARG, zlib:inflateChunk(Z1, 4384)), + + ?m({'EXIT',{data_error,_}}, zlib:inflateEnd(Z1)), + + ?m(ok, zlib:close(Z1)). + +%% Test safeInflate as a mirror of inflateChunk, but ignore the stuff about +%% exact chunk sizes. +api_safeInflate(Config) when is_list(Config) -> + Data = << <<(I rem 150)>> || I <- lists:seq(1, 20 bsl 10) >>, + Compressed = zlib:compress(Data), + Z1 = zlib:open(), + + ?m(ok, zlib:inflateInit(Z1)), + + SafeInflateLoop = + fun + Loop({continue, Chunk}, Output) -> + Loop(zlib:safeInflate(Z1, []), [Output, Chunk]); + Loop({finished, Chunk}, Output) -> + [Output, Chunk] + end, + + Decompressed = SafeInflateLoop(zlib:safeInflate(Z1, Compressed), []), + Data = iolist_to_binary(Decompressed), + + ?m(ok, zlib:inflateEnd(Z1)), + ?m(ok, zlib:inflateInit(Z1)), + + {continue, Partial} = zlib:safeInflate(Z1, Compressed), + PBin = iolist_to_binary(Partial), + PSize = byte_size(PBin), + <> = Data, + + ?m(ok, zlib:inflateReset(Z1)), + + {continue, Partial} = zlib:safeInflate(Z1, Compressed), + PBin = iolist_to_binary(Partial), + PSize = byte_size(PBin), + <> = Data, + + ?m(ok, zlib:inflateReset(Z1)), + + SafeInflateLoop(zlib:safeInflate(Z1, Compressed), []), + + ?m({'EXIT',{data_error,_}}, zlib:safeInflate(Z1, Compressed)), + + ?m(ok, zlib:inflateReset(Z1)), + ?m(?BARG, zlib:safeInflate(gurka, Compressed)), + ?m(?BARG, zlib:safeInflate(Z1, 4384)), ?m({'EXIT',{data_error,_}}, zlib:inflateEnd(Z1)), ?m(ok, zlib:close(Z1)). %% Test inflateEnd. api_inflateEnd(Config) when is_list(Config) -> Z1 = zlib:open(), - ?m({'EXIT',{einval,_}}, zlib:inflateEnd(Z1)), + ?m(?BARG, zlib:inflateEnd(Z1)), ?m(ok, zlib:inflateInit(Z1)), ?m(?BARG, zlib:inflateEnd(gurka)), ?m({'EXIT',{data_error,_}}, zlib:inflateEnd(Z1)), - ?m({'EXIT',{einval,_}}, zlib:inflateEnd(Z1)), + ?m(?BARG, zlib:inflateEnd(Z1)), ?m(ok, zlib:inflateInit(Z1)), ?m(B when is_list(B), zlib:inflate(Z1, zlib:compress("abc"))), ?m(ok, zlib:inflateEnd(Z1)), ?m(ok, zlib:close(Z1)). -%% Test getBufsz. -api_getBufsz(Config) when is_list(Config) -> - Z1 = zlib:open(), - ?m(Val when is_integer(Val), zlib:getBufSize(Z1)), - ?m(?BARG, zlib:getBufSize(gurka)), - ?m(ok, zlib:close(Z1)). - -%% Test setBufsz. -api_setBufsz(Config) when is_list(Config) -> - Z1 = zlib:open(), - ?m(?BARG, zlib:setBufSize(Z1, gurka)), - ?m(?BARG, zlib:setBufSize(gurka, 1232330)), - Sz = ?m( Val when is_integer(Val), zlib:getBufSize(Z1)), - ?m(ok, zlib:setBufSize(Z1, Sz*2)), - DSz = Sz*2, - ?m(DSz, zlib:getBufSize(Z1)), - ?m(ok, zlib:close(Z1)). - -%%% Debug function ?? -%% Test getQSize. -api_getQSize(Config) when is_list(Config) -> - Z1 = zlib:open(), - Q = ?m(Val when is_integer(Val), zlib:getQSize(Z1)), - io:format("QSize ~p ~n", [Q]), - ?m(?BARG, zlib:getQSize(gurka)), - ?m(ok, zlib:close(Z1)). - %% Test crc32. api_crc32(Config) when is_list(Config) -> Z1 = zlib:open(), @@ -594,24 +655,9 @@ intro(Config) when is_list(Config) -> large_deflate(Config) when is_list(Config) -> large_deflate_do(). large_deflate_do() -> - Z = zlib:open(), - Plain = rand_bytes(zlib:getBufSize(Z)*5), - ok = zlib:deflateInit(Z), - _ZlibHeader = zlib:deflate(Z, [], full), - Deflated = zlib:deflate(Z, Plain, full), - ?m(ok, zlib:close(Z)), - ?m(Plain, zlib:unzip(list_to_binary([Deflated, 3, 0]))). - -rand_bytes(Sz) -> - L = <<8,2,3,6,1,2,3,2,3,4,8,7,3,7,2,3,4,7,5,8,9,3>>, - rand_bytes(erlang:md5(L),Sz). - -rand_bytes(Bin, Sz) when byte_size(Bin) >= Sz -> - <> = Bin, - Res; -rand_bytes(Bin, Sz) -> - rand_bytes(<<(erlang:md5(Bin))/binary, Bin/binary>>, Sz). - + Plain = gen_determ_rand_bytes(64 bsl 10), + Deflated = zlib:zip(Plain), + ?m(Plain, zlib:unzip(Deflated)). %% Test a standard compressed zip file. zip_usage(Config) when is_list(Config) -> @@ -869,10 +915,14 @@ dictionary_usage({run}) -> %% Now uncompress. Z2 = zlib:open(), ?m(ok, zlib:inflateInit(Z2)), + {'EXIT',{{need_dictionary,DictID},_}} = (catch zlib:inflate(Z2, Compressed)), + ?m(ok, zlib:inflateSetDictionary(Z2, Dict)), ?m(ok, zlib:inflateSetDictionary(Z2, binary_to_list(Dict))), + Uncompressed = ?m(B when is_list(B), zlib:inflate(Z2, [])), + ?m(ok, zlib:inflateEnd(Z2)), ?m(ok, zlib:close(Z2)), ?m(Data, list_to_binary(Uncompressed)). @@ -882,6 +932,35 @@ split_bin(<>, Acc) -> split_bin(Last,Acc) -> lists:reverse([Last|Acc]). +only_allow_owner(Config) when is_list(Config) -> + Z = zlib:open(), + + ?m(ok, zlib:inflateInit(Z)), + ?m(ok, zlib:inflateReset(Z)), + + {Pid, Ref} = spawn_monitor( + fun() -> + ?m(?BARG, zlib:inflateReset(Z)) + end), + + receive + {'DOWN', Ref, process, Pid, _Reason} -> + ok + after 200 -> + ?error("Spawned worker timed out.", []) + end, + + ?m(ok, zlib:inflateReset(Z)). + +sub_heap_binaries(Config) when is_list(Config) -> + Compressed = zlib:compress(<<"gurka">>), + ConfLen = erlang:length(Config), + + HeapBin = <>, + <<_:8/integer, SubHeapBin/binary>> = HeapBin, + + ?m(<<"gurka">>, zlib:uncompress(SubHeapBin)), + ok. %% Check concurrent access to zlib driver. smp(Config) -> @@ -999,7 +1078,78 @@ otp_9981(Config) when is_list(Config) -> Ports = lists:sort(erlang:ports()), ok. +-define(BENCH_SIZE, (16 bsl 20)). + +-define(DECOMPRESS_BENCH(Name, What, Data), + Name(Config) when is_list(Config) -> + Uncompressed = Data, + Compressed = zlib:compress(Uncompressed), + What(Compressed, byte_size(Uncompressed))). + +-define(COMPRESS_BENCH(Name, What, Data), + Name(Config) when is_list(Config) -> + Compressed = Data, + What(Compressed, byte_size(Compressed))). + +?DECOMPRESS_BENCH(inflate_bench_zeroed, throughput_bench_inflate, + <<0:(8 * ?BENCH_SIZE)>>). +?DECOMPRESS_BENCH(inflate_bench_rand, throughput_bench_inflate, + gen_determ_rand_bytes(?BENCH_SIZE)). + +?DECOMPRESS_BENCH(chunk_bench_zeroed, throughput_bench_chunk, + <<0:(8 * ?BENCH_SIZE)>>). +?DECOMPRESS_BENCH(chunk_bench_rand, throughput_bench_chunk, + gen_determ_rand_bytes(?BENCH_SIZE)). + +?COMPRESS_BENCH(deflate_bench_zeroed, throughput_bench_deflate, + <<0:(8 * ?BENCH_SIZE)>>). +?COMPRESS_BENCH(deflate_bench_rand, throughput_bench_deflate, + gen_determ_rand_bytes(?BENCH_SIZE)). + +throughput_bench_inflate(Compressed, Size) -> + Z = zlib:open(), + zlib:inflateInit(Z), + + submit_throughput_results(Size, + fun() -> + zlib:inflate(Z, Compressed) + end). + +throughput_bench_deflate(Uncompressed, Size) -> + Z = zlib:open(), + zlib:deflateInit(Z), + + submit_throughput_results(Size, + fun() -> + zlib:deflate(Z, Uncompressed, finish) + end). + +throughput_bench_chunk(Compressed, Size) -> + Z = zlib:open(), + zlib:inflateInit(Z), + + ChunkLoop = + fun + Loop({more, _}) -> Loop(zlib:inflateChunk(Z)); + Loop(_) -> ok + end, + + submit_throughput_results(Size, + fun() -> + ChunkLoop(zlib:inflateChunk(Z, Compressed)) + end). + +submit_throughput_results(Size, Fun) -> + TimeTaken = measure_perf_counter(Fun, millisecond), + + KBPS = trunc((Size bsr 10) / (TimeTaken / 1000)), + ct_event:notify(#event{ name = benchmark_data, data = [{value,KBPS}] }), + {comment, io_lib:format("~p ms, ~p KBPS", [TimeTaken, KBPS])}. +measure_perf_counter(Fun, Unit) -> + Start = os:perf_counter(Unit), + Fun(), + os:perf_counter(Unit) - Start. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Helps with testing directly %%%%%%%%%%%%% @@ -1039,3 +1189,12 @@ expand([H|T], Acc) -> end; expand([], Acc) -> Acc. +%% Generates a bunch of statistically random bytes using the size as seed. +gen_determ_rand_bytes(Size) -> + gen_determ_rand_bytes(Size, erlang:md5_init(), <<>>). +gen_determ_rand_bytes(Size, _Context, Acc) when Size =< 0 -> + Acc; +gen_determ_rand_bytes(Size, Context0, Acc) when Size > 0 -> + Context = erlang:md5_update(Context0, <>), + Checksum = erlang:md5_final(Context), + gen_determ_rand_bytes(Size - 16, Context, <>). -- cgit v1.2.3 From 8e8a7f16acb479fc2737ff73e5e0b5569a7ab71f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Thu, 10 Aug 2017 14:25:34 +0200 Subject: Improve zlib error messages and update test suite to fit OTP-14527 --- lib/kernel/test/zlib_SUITE.erl | 227 ++++++++++++++++++++++------------------- 1 file changed, 122 insertions(+), 105 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl index efbbc805a0..6a91523ea1 100644 --- a/lib/kernel/test/zlib_SUITE.erl +++ b/lib/kernel/test/zlib_SUITE.erl @@ -44,8 +44,7 @@ end end()). --define(BARG, {'EXIT',{badarg,[{_,_,_,_}|_]}}). --define(DATA_ERROR, {'EXIT',{data_error,[{zlib,_,_,_}|_]}}). +-define(EXIT(Reason), {'EXIT',{Reason,[{_,_,_,_}|_]}}). init_per_testcase(_Func, Config) -> Config. @@ -120,7 +119,7 @@ api_open_close(Config) when is_list(Config) -> Fd2 = zlib:open(), ?m(false,Fd1 == Fd2), ?m(ok,zlib:close(Fd1)), - ?m(?BARG, zlib:close(Fd1)), + ?m(?EXIT(not_initialized), zlib:close(Fd1)), ?m(ok,zlib:close(Fd2)), %% Make sure that we don't get any EXIT messages if trap_exit is enabled. @@ -135,9 +134,11 @@ api_open_close(Config) when is_list(Config) -> %% Test deflateInit/2 and /6. api_deflateInit(Config) when is_list(Config) -> Z1 = zlib:open(), - ?m(?BARG, zlib:deflateInit(gurka, none)), - ?m(?BARG, zlib:deflateInit(gurka, gurka)), - ?m(?BARG, zlib:deflateInit(Z1, gurka)), + + ?m(?EXIT(badarg), zlib:deflateInit(gurka, none)), + + ?m(?EXIT(bad_compression_level), zlib:deflateInit(gurka, gurka)), + ?m(?EXIT(bad_compression_level), zlib:deflateInit(Z1, gurka)), Levels = [none, default, best_speed, best_compression] ++ lists:seq(0,9), lists:foreach(fun(Level) -> Z = zlib:open(), @@ -145,20 +146,30 @@ api_deflateInit(Config) when is_list(Config) -> ?m(ok,zlib:close(Z)) end, Levels), %% /6 - ?m(?BARG, zlib:deflateInit(Z1,gurka,deflated,-15,8,default)), - - ?m(?BARG, zlib:deflateInit(Z1,default,undefined,-15,8,default)), - - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,48,8,default)), - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-20,8,default)), - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-7,8,default)), - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,7,8,default)), - - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-15,0,default)), - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-15,10,default)), - - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-15,8,0)), - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-15,8,undefined)), + ?m(?EXIT(bad_compression_level), + zlib:deflateInit(Z1,gurka,deflated,-15,8,default)), + + ?m(?EXIT(bad_compression_method), + zlib:deflateInit(Z1,default,undefined,-15,8,default)), + + ?m(?EXIT(bad_compression_strategy), + zlib:deflateInit(Z1,default,deflated,-15,8,0)), + ?m(?EXIT(bad_compression_strategy), + zlib:deflateInit(Z1,default,deflated,-15,8,undefined)), + + ?m(?EXIT(bad_windowbits), + zlib:deflateInit(Z1,default,deflated,48,8,default)), + ?m(?EXIT(bad_windowbits), + zlib:deflateInit(Z1,default,deflated,-20,8,default)), + ?m(?EXIT(bad_windowbits), + zlib:deflateInit(Z1,default,deflated,-7,8,default)), + ?m(?EXIT(bad_windowbits), + zlib:deflateInit(Z1,default,deflated,7,8,default)), + + ?m(?EXIT(bad_memlevel), + zlib:deflateInit(Z1,default,deflated,-15,0,default)), + ?m(?EXIT(bad_memlevel), + zlib:deflateInit(Z1,default,deflated,-15,10,default)), lists:foreach(fun(Level) -> Z = zlib:open(), @@ -190,7 +201,11 @@ api_deflateInit(Config) when is_list(Config) -> ?m(ok,zlib:close(Z)) end, Strategies), ?m(ok, zlib:deflateInit(Z1,default,deflated,-15,8,default)), - ?m({'EXIT',_}, zlib:deflateInit(Z1,none,deflated,-15,8,default)), %% ?? + + %% Let it crash for any reason; we don't care about the order in which the + %% parameters are checked. + ?m(?EXIT(_), zlib:deflateInit(Z1,none,deflated,-15,8,default)), + ?m(ok, zlib:close(Z1)). %% Test deflateSetDictionary. @@ -199,10 +214,10 @@ api_deflateSetDictionary(Config) when is_list(Config) -> ?m(ok, zlib:deflateInit(Z1, default)), ?m(Id when is_integer(Id), zlib:deflateSetDictionary(Z1, <<1,1,2,3,4,5,1>>)), ?m(Id when is_integer(Id), zlib:deflateSetDictionary(Z1, [1,1,2,3,4,5,1])), - ?m(?BARG, zlib:deflateSetDictionary(Z1, gurka)), - ?m(?BARG, zlib:deflateSetDictionary(Z1, 128)), + ?m(?EXIT(badarg), zlib:deflateSetDictionary(Z1, gurka)), + ?m(?EXIT(badarg), zlib:deflateSetDictionary(Z1, 128)), ?m(_, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)), - ?m({'EXIT',{stream_error,_}},zlib:deflateSetDictionary(Z1,<<1,1,2,3,4,5,1>>)), + ?m(?EXIT(stream_error), zlib:deflateSetDictionary(Z1,<<1,1,2,3,4,5,1>>)), ?m(ok, zlib:close(Z1)). %% Test deflateReset. @@ -238,11 +253,13 @@ api_deflate(Config) when is_list(Config) -> ?m(B when is_list(B), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, full)), ?m(B when is_list(B), zlib:deflate(Z1, <<>>, finish)), - ?m(?BARG, zlib:deflate(gurka, <<1,1,1,1,1,1,1,1,1>>, full)), - ?m(?BARG, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, asdj)), - ?m(?BARG, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, 198)), + ?m(?EXIT(badarg), zlib:deflate(gurka, <<1,1,1,1,1,1,1,1,1>>, full)), + + ?m(?EXIT(bad_flush_mode), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, asdj)), + ?m(?EXIT(bad_flush_mode), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, 198)), + %% Causes problems ERROR REPORT - ?m(?BARG, zlib:deflate(Z1, [asdj,asd], none)), + ?m(?EXIT(badarg), zlib:deflate(Z1, [asdj,asd], none)), ?m(ok, zlib:close(Z1)). @@ -251,11 +268,11 @@ api_deflateEnd(Config) when is_list(Config) -> Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1, default)), ?m(ok, zlib:deflateEnd(Z1)), - ?m(?BARG, zlib:deflateEnd(Z1)), - ?m(?BARG, zlib:deflateEnd(gurka)), + ?m(?EXIT(not_initialized), zlib:deflateEnd(Z1)), + ?m(?EXIT(badarg), zlib:deflateEnd(gurka)), ?m(ok, zlib:deflateInit(Z1, default)), ?m(B when is_list(B), zlib:deflate(Z1, <<"Kilroy was here">>)), - ?m({'EXIT', {data_error,_}}, zlib:deflateEnd(Z1)), + ?m(?EXIT(data_error), zlib:deflateEnd(Z1)), ?m(ok, zlib:deflateInit(Z1, default)), ?m(B when is_list(B), zlib:deflate(Z1, <<"Kilroy was here">>)), ?m(B when is_list(B), zlib:deflate(Z1, <<"Kilroy was here">>, finish)), @@ -266,9 +283,9 @@ api_deflateEnd(Config) when is_list(Config) -> %% Test inflateInit /1 and /2. api_inflateInit(Config) when is_list(Config) -> Z1 = zlib:open(), - ?m(?BARG, zlib:inflateInit(gurka)), + ?m(?EXIT(badarg), zlib:inflateInit(gurka)), ?m(ok, zlib:inflateInit(Z1)), - ?m(?BARG, zlib:inflateInit(Z1, 15)), + ?m(?EXIT(already_initialized), zlib:inflateInit(Z1, 15)), lists:foreach(fun(Wbits) -> Z11 = zlib:open(), ?m(ok, zlib:inflateInit(Z11,Wbits)), @@ -277,22 +294,22 @@ api_inflateInit(Config) when is_list(Config) -> ?m(ok,zlib:close(Z11)), ?m(ok,zlib:close(Z12)) end, lists:seq(8,15)), - ?m(?BARG, zlib:inflateInit(gurka, -15)), - ?m(?BARG, zlib:inflateInit(Z1, 7)), - ?m(?BARG, zlib:inflateInit(Z1, -7)), - ?m(?BARG, zlib:inflateInit(Z1, 48)), - ?m(?BARG, zlib:inflateInit(Z1, -16)), + ?m(?EXIT(badarg), zlib:inflateInit(gurka, -15)), + ?m(?EXIT(already_initialized), zlib:inflateInit(Z1, 7)), + ?m(?EXIT(already_initialized), zlib:inflateInit(Z1, -7)), + ?m(?EXIT(already_initialized), zlib:inflateInit(Z1, 48)), + ?m(?EXIT(already_initialized), zlib:inflateInit(Z1, -16)), ?m(ok, zlib:close(Z1)). %% Test inflateSetDictionary. api_inflateSetDictionary(Config) when is_list(Config) -> Z1 = zlib:open(), ?m(ok, zlib:inflateInit(Z1)), - ?m(?BARG, zlib:inflateSetDictionary(gurka,<<1,1,1,1,1>>)), - ?m(?BARG, zlib:inflateSetDictionary(Z1,102)), - ?m(?BARG, zlib:inflateSetDictionary(Z1,gurka)), + ?m(?EXIT(badarg), zlib:inflateSetDictionary(gurka,<<1,1,1,1,1>>)), + ?m(?EXIT(badarg), zlib:inflateSetDictionary(Z1,102)), + ?m(?EXIT(badarg), zlib:inflateSetDictionary(Z1,gurka)), Dict = <<1,1,1,1,1>>, - ?m({'EXIT',{stream_error,_}}, zlib:inflateSetDictionary(Z1,Dict)), + ?m(?EXIT(stream_error), zlib:inflateSetDictionary(Z1,Dict)), ?m(ok, zlib:close(Z1)). %% Test inflateGetDictionary. @@ -301,7 +318,7 @@ api_inflateGetDictionary(Config) when is_list(Config) -> zlib:inflateInit(Z1), IsOperationSupported = case catch zlib:inflateGetDictionary(Z1) of - ?BARG -> false; + ?EXIT(not_supported) -> false; _ -> true end, zlib:close(Z1), @@ -323,19 +340,19 @@ api_inflateGetDictionary_if_supported(true) -> Z2 = zlib:open(), ?m(ok, zlib:inflateInit(Z2)), ?m(<<>>, iolist_to_binary(zlib:inflateGetDictionary(Z2))), - ?m({'EXIT',{stream_error,_}}, zlib:inflateSetDictionary(Z2, Dict)), - ?m({'EXIT',{{need_dictionary,Checksum},_}}, zlib:inflate(Z2, Compressed)), + ?m(?EXIT(stream_error), zlib:inflateSetDictionary(Z2, Dict)), + ?m(?EXIT({need_dictionary,Checksum}), zlib:inflate(Z2, Compressed)), ?m(ok, zlib:inflateSetDictionary(Z2, Dict)), ?m(Dict, iolist_to_binary(zlib:inflateGetDictionary(Z2))), Payload = iolist_to_binary(zlib:inflate(Z2, [])), ?m(ok, zlib:close(Z2)), - ?m(?BARG, zlib:inflateSetDictionary(Z2, Dict)), + ?m(?EXIT(not_initialized), zlib:inflateSetDictionary(Z2, Dict)), %% ... And do the same for inflate/3 Z3 = zlib:open(), ?m(ok, zlib:inflateInit(Z3)), ?m(<<>>, iolist_to_binary(zlib:inflateGetDictionary(Z3))), - ?m({'EXIT',{stream_error,_}}, zlib:inflateSetDictionary(Z3, Dict)), + ?m(?EXIT(stream_error), zlib:inflateSetDictionary(Z3, Dict)), {need_dictionary, Checksum, _Output = []} = zlib:inflate(Z3, Compressed, [{exception_on_need_dict, false}]), @@ -347,7 +364,7 @@ api_inflateGetDictionary_if_supported(true) -> zlib:inflate(Z3, [], [{exception_on_need_dict, false}])), ?m(ok, zlib:close(Z3)), - ?m(?BARG, zlib:inflateSetDictionary(Z3, Dict)), + ?m(?EXIT(not_initialized), zlib:inflateSetDictionary(Z3, Dict)), ok. @@ -363,7 +380,7 @@ clobber(N, Bin) when is_binary(Bin) -> api_inflateReset(Config) when is_list(Config) -> Z1 = zlib:open(), ?m(ok, zlib:inflateInit(Z1)), - ?m(?BARG, zlib:inflateReset(gurka)), + ?m(?EXIT(badarg), zlib:inflateReset(gurka)), ?m(ok, zlib:inflateReset(Z1)), ?m(ok, zlib:close(Z1)). @@ -378,12 +395,12 @@ api_inflate2(Config) when is_list(Config) -> ?m(ok, zlib:inflateEnd(Z1)), ?m(ok, zlib:inflateInit(Z1)), ?m(Data, zlib:inflate(Z1, Compressed)), - ?m(?BARG, zlib:inflate(gurka, Compressed)), - ?m(?BARG, zlib:inflate(Z1, 4384)), - ?m(?BARG, zlib:inflate(Z1, [atom_list])), + ?m(?EXIT(badarg), zlib:inflate(gurka, Compressed)), + ?m(?EXIT(badarg), zlib:inflate(Z1, 4384)), + ?m(?EXIT(badarg), zlib:inflate(Z1, [atom_list])), ?m(ok, zlib:inflateEnd(Z1)), ?m(ok, zlib:inflateInit(Z1)), - ?m({'EXIT',{data_error,_}}, zlib:inflate(Z1, <<2,1,2,1,2>>)), + ?m(?EXIT(data_error), zlib:inflate(Z1, <<2,1,2,1,2>>)), ?m(ok, zlib:close(Z1)). %% Test inflate/3; same as inflate/2 but with the default options inverted. @@ -398,12 +415,12 @@ api_inflate3(Config) when is_list(Config) -> ?m(ok, zlib:inflateEnd(Z1)), ?m(ok, zlib:inflateInit(Z1)), ?m(Data, zlib:inflate(Z1, Compressed, Options)), - ?m(?BARG, zlib:inflate(gurka, Compressed, Options)), - ?m(?BARG, zlib:inflate(Z1, 4384, Options)), - ?m(?BARG, zlib:inflate(Z1, [atom_list], Options)), + ?m(?EXIT(badarg), zlib:inflate(gurka, Compressed, Options)), + ?m(?EXIT(badarg), zlib:inflate(Z1, 4384, Options)), + ?m(?EXIT(badarg), zlib:inflate(Z1, [atom_list], Options)), ?m(ok, zlib:inflateEnd(Z1)), ?m(ok, zlib:inflateInit(Z1)), - ?m({'EXIT',{data_error,_}}, zlib:inflate(Z1, <<2,1,2,1,2>>, Options)), + ?m(?EXIT(data_error), zlib:inflate(Z1, <<2,1,2,1,2>>, Options)), ?m(ok, zlib:close(Z1)). %% Test inflateChunk. @@ -447,10 +464,10 @@ api_inflateChunk(Config) when is_list(Config) -> ?m(ok, zlib:inflateEnd(Z1)), ?m(ok, zlib:inflateInit(Z1)), - ?m(?BARG, zlib:inflateChunk(gurka, Compressed)), - ?m(?BARG, zlib:inflateChunk(Z1, 4384)), + ?m(?EXIT(badarg), zlib:inflateChunk(gurka, Compressed)), + ?m(?EXIT(badarg), zlib:inflateChunk(Z1, 4384)), - ?m({'EXIT',{data_error,_}}, zlib:inflateEnd(Z1)), + ?m(?EXIT(data_error), zlib:inflateEnd(Z1)), ?m(ok, zlib:close(Z1)). @@ -493,22 +510,22 @@ api_safeInflate(Config) when is_list(Config) -> SafeInflateLoop(zlib:safeInflate(Z1, Compressed), []), - ?m({'EXIT',{data_error,_}}, zlib:safeInflate(Z1, Compressed)), + ?m(?EXIT(data_error), zlib:safeInflate(Z1, Compressed)), ?m(ok, zlib:inflateReset(Z1)), - ?m(?BARG, zlib:safeInflate(gurka, Compressed)), - ?m(?BARG, zlib:safeInflate(Z1, 4384)), - ?m({'EXIT',{data_error,_}}, zlib:inflateEnd(Z1)), + ?m(?EXIT(badarg), zlib:safeInflate(gurka, Compressed)), + ?m(?EXIT(badarg), zlib:safeInflate(Z1, 4384)), + ?m(?EXIT(data_error), zlib:inflateEnd(Z1)), ?m(ok, zlib:close(Z1)). %% Test inflateEnd. api_inflateEnd(Config) when is_list(Config) -> Z1 = zlib:open(), - ?m(?BARG, zlib:inflateEnd(Z1)), + ?m(?EXIT(not_initialized), zlib:inflateEnd(Z1)), ?m(ok, zlib:inflateInit(Z1)), - ?m(?BARG, zlib:inflateEnd(gurka)), - ?m({'EXIT',{data_error,_}}, zlib:inflateEnd(Z1)), - ?m(?BARG, zlib:inflateEnd(Z1)), + ?m(?EXIT(badarg), zlib:inflateEnd(gurka)), + ?m(?EXIT(data_error), zlib:inflateEnd(Z1)), + ?m(?EXIT(not_initialized), zlib:inflateEnd(Z1)), ?m(ok, zlib:inflateInit(Z1)), ?m(B when is_list(B), zlib:inflate(Z1, zlib:compress("abc"))), ?m(ok, zlib:inflateEnd(Z1)), @@ -528,15 +545,15 @@ api_crc32(Config) when is_list(Config) -> ?m(CRC2 when is_integer(CRC2), zlib:crc32(Z1,Compressed)), CRC2 = ?m(CRC2 when is_integer(CRC2), zlib:crc32(Z1,0,Compressed)), ?m(CRC3 when CRC2 /= CRC3, zlib:crc32(Z1,234,Compressed)), - ?m(?BARG, zlib:crc32(gurka)), - ?m(?BARG, zlib:crc32(Z1, not_a_binary)), - ?m(?BARG, zlib:crc32(gurka, <<1,1,2,4,4>>)), - ?m(?BARG, zlib:crc32(Z1, 2298929, not_a_binary)), - ?m(?BARG, zlib:crc32(Z1, not_an_int, <<123,123,123,35,231>>)), - ?m(?BARG, zlib:crc32_combine(Z1, not_an_int, 123123, 123)), - ?m(?BARG, zlib:crc32_combine(Z1, noint, 123123, 123)), - ?m(?BARG, zlib:crc32_combine(Z1, 123123, noint, 123)), - ?m(?BARG, zlib:crc32_combine(Z1, 123123, 123, noint)), + ?m(?EXIT(badarg), zlib:crc32(gurka)), + ?m(?EXIT(badarg), zlib:crc32(Z1, not_a_binary)), + ?m(?EXIT(badarg), zlib:crc32(gurka, <<1,1,2,4,4>>)), + ?m(?EXIT(badarg), zlib:crc32(Z1, 2298929, not_a_binary)), + ?m(?EXIT(badarg), zlib:crc32(Z1, not_an_int, <<123,123,123,35,231>>)), + ?m(?EXIT(badarg), zlib:crc32_combine(Z1, not_an_int, 123123, 123)), + ?m(?EXIT(badarg), zlib:crc32_combine(Z1, noint, 123123, 123)), + ?m(?EXIT(badarg), zlib:crc32_combine(Z1, 123123, noint, 123)), + ?m(?EXIT(badarg), zlib:crc32_combine(Z1, 123123, 123, noint)), ?m(ok, zlib:deflateEnd(Z1)), ?m(ok, zlib:close(Z1)). @@ -553,41 +570,41 @@ api_adler32(Config) when is_list(Config) -> ADLER2 = ?m(ADLER2 when is_integer(ADLER2), zlib:adler32(Z1,Compressed)), ?m(ADLER2 when is_integer(ADLER2), zlib:adler32(Z1,1,Compressed)), ?m(ADLER3 when ADLER2 /= ADLER3, zlib:adler32(Z1,234,Compressed)), - ?m(?BARG, zlib:adler32(Z1, not_a_binary)), - ?m(?BARG, zlib:adler32(gurka, <<1,1,2,4,4>>)), - ?m(?BARG, zlib:adler32(Z1, 2298929, not_a_binary)), - ?m(?BARG, zlib:adler32(Z1, not_an_int, <<123,123,123,35,231>>)), - ?m(?BARG, zlib:adler32_combine(Z1, noint, 123123, 123)), - ?m(?BARG, zlib:adler32_combine(Z1, 123123, noint, 123)), - ?m(?BARG, zlib:adler32_combine(Z1, 123123, 123, noint)), + ?m(?EXIT(badarg), zlib:adler32(Z1, not_a_binary)), + ?m(?EXIT(badarg), zlib:adler32(gurka, <<1,1,2,4,4>>)), + ?m(?EXIT(badarg), zlib:adler32(Z1, 2298929, not_a_binary)), + ?m(?EXIT(badarg), zlib:adler32(Z1, not_an_int, <<123,123,123,35,231>>)), + ?m(?EXIT(badarg), zlib:adler32_combine(Z1, noint, 123123, 123)), + ?m(?EXIT(badarg), zlib:adler32_combine(Z1, 123123, noint, 123)), + ?m(?EXIT(badarg), zlib:adler32_combine(Z1, 123123, 123, noint)), ?m(ok, zlib:deflateEnd(Z1)), ?m(ok, zlib:close(Z1)). %% Test compress. api_un_compress(Config) when is_list(Config) -> - ?m(?BARG,zlib:compress(not_a_binary)), + ?m(?EXIT(badarg),zlib:compress(not_a_binary)), Bin = <<1,11,1,23,45>>, Comp = zlib:compress(Bin), - ?m(?BARG,zlib:uncompress(not_a_binary)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<171,171,171,171,171>>)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<>>)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<120>>)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<120,156>>)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<120,156,3>>)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<120,156,3,0>>)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<0,156,3,0,0,0,0,1>>)), + ?m(?EXIT(badarg),zlib:uncompress(not_a_binary)), + ?m(?EXIT(data_error), zlib:uncompress(<<171,171,171,171,171>>)), + ?m(?EXIT(data_error), zlib:uncompress(<<>>)), + ?m(?EXIT(data_error), zlib:uncompress(<<120>>)), + ?m(?EXIT(data_error), zlib:uncompress(<<120,156>>)), + ?m(?EXIT(data_error), zlib:uncompress(<<120,156,3>>)), + ?m(?EXIT(data_error), zlib:uncompress(<<120,156,3,0>>)), + ?m(?EXIT(data_error), zlib:uncompress(<<0,156,3,0,0,0,0,1>>)), ?m(Bin, zlib:uncompress(binary_to_list(Comp))), ?m(Bin, zlib:uncompress(Comp)). %% Test zip. api_un_zip(Config) when is_list(Config) -> - ?m(?BARG,zlib:zip(not_a_binary)), + ?m(?EXIT(badarg),zlib:zip(not_a_binary)), Bin = <<1,11,1,23,45>>, Comp = zlib:zip(Bin), ?m(Comp, zlib:zip(binary_to_list(Bin))), - ?m(?BARG,zlib:unzip(not_a_binary)), - ?m({'EXIT',{data_error,_}}, zlib:unzip(<<171,171,171,171,171>>)), - ?m({'EXIT',{data_error,_}}, zlib:unzip(<<>>)), + ?m(?EXIT(badarg),zlib:unzip(not_a_binary)), + ?m(?EXIT(data_error), zlib:unzip(<<171,171,171,171,171>>)), + ?m(?EXIT(data_error), zlib:unzip(<<>>)), ?m(Bin, zlib:unzip(Comp)), ?m(Bin, zlib:unzip(binary_to_list(Comp))), @@ -598,21 +615,21 @@ api_un_zip(Config) when is_list(Config) -> %% Test gunzip. api_g_un_zip(Config) when is_list(Config) -> - ?m(?BARG,zlib:gzip(not_a_binary)), + ?m(?EXIT(badarg),zlib:gzip(not_a_binary)), Bin = <<1,11,1,23,45>>, Comp = zlib:gzip(Bin), ?m(Comp, zlib:gzip(binary_to_list(Bin))), - ?m(?BARG, zlib:gunzip(not_a_binary)), - ?m(?DATA_ERROR, zlib:gunzip(<<171,171,171,171,171>>)), - ?m(?DATA_ERROR, zlib:gunzip(<<>>)), + ?m(?EXIT(badarg), zlib:gunzip(not_a_binary)), + ?m(?EXIT(data_error), zlib:gunzip(<<171,171,171,171,171>>)), + ?m(?EXIT(data_error), zlib:gunzip(<<>>)), ?m(Bin, zlib:gunzip(Comp)), ?m(Bin, zlib:gunzip(binary_to_list(Comp))), %% Bad CRC; bad length. BadCrc = bad_crc_data(), - ?m({'EXIT',{data_error,_}},(catch zlib:gunzip(BadCrc))), + ?m(?EXIT(data_error),(catch zlib:gunzip(BadCrc))), BadLen = bad_len_data(), - ?m({'EXIT',{data_error,_}},(catch zlib:gunzip(BadLen))), + ?m(?EXIT(data_error),(catch zlib:gunzip(BadLen))), ok. bad_crc_data() -> @@ -916,7 +933,7 @@ dictionary_usage({run}) -> Z2 = zlib:open(), ?m(ok, zlib:inflateInit(Z2)), - {'EXIT',{{need_dictionary,DictID},_}} = (catch zlib:inflate(Z2, Compressed)), + ?EXIT({need_dictionary, DictID}) = (catch zlib:inflate(Z2, Compressed)), ?m(ok, zlib:inflateSetDictionary(Z2, Dict)), ?m(ok, zlib:inflateSetDictionary(Z2, binary_to_list(Dict))), @@ -940,7 +957,7 @@ only_allow_owner(Config) when is_list(Config) -> {Pid, Ref} = spawn_monitor( fun() -> - ?m(?BARG, zlib:inflateReset(Z)) + ?m(?EXIT(not_on_controlling_process), zlib:inflateReset(Z)) end), receive -- cgit v1.2.3 From 2dc225ed172419066c8b3f54441ec14ffc2b0908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Thu, 10 Aug 2017 13:33:01 +0200 Subject: Clean up zlib test suite --- lib/kernel/test/zlib_SUITE.erl | 263 ++++++++++++++++++++--------------------- 1 file changed, 127 insertions(+), 136 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl index 6a91523ea1..e246276262 100644 --- a/lib/kernel/test/zlib_SUITE.erl +++ b/lib/kernel/test/zlib_SUITE.erl @@ -23,52 +23,47 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("common_test/include/ct_event.hrl"). --compile(export_all). - --define(error(Format,Args), - put(test_server_loc,{?MODULE,?LINE}), - error(Format,Args,?MODULE,?LINE)). - -%% Learn erts team how to really write tests ;-) --define(m(ExpectedRes,Expr), - fun() -> - ACtual1 = (catch (Expr)), - try case ACtual1 of - ExpectedRes -> ACtual1 - end - catch - error:{case_clause,ACtuAl} -> - ?error("Not Matching Actual result was:~n ~p ~n", - [ACtuAl]), - ACtuAl - end - end()). +-export([suite/0, all/0, groups/0]). + +%% API group +-export([api_open_close/1]). +-export([api_deflateInit/1, api_deflateSetDictionary/1, api_deflateReset/1, + api_deflateParams/1, api_deflate/1, api_deflateEnd/1]). +-export([api_inflateInit/1, api_inflateReset/1, api_inflate2/1, api_inflate3/1, + api_inflateChunk/1, api_safeInflate/1, api_inflateEnd/1]). +-export([api_inflateSetDictionary/1, api_inflateGetDictionary/1]). +-export([api_crc32/1, api_adler32/1]). +-export([api_un_compress/1, api_un_zip/1, api_g_un_zip/1]). + +%% Examples group +-export([intro/1]). + +%% Usage group +-export([zip_usage/1, gz_usage/1, gz_usage2/1, compress_usage/1, + dictionary_usage/1, large_deflate/1, crc/1, adler/1, + only_allow_owner/1, sub_heap_binaries/1]). + +%% Bench group +-export([inflate_bench_zeroed/1, inflate_bench_rand/1, + deflate_bench_zeroed/1, deflate_bench_rand/1, + chunk_bench_zeroed/1, chunk_bench_rand/1]). + +%% Others +-export([smp/1, otp_9981/1, otp_7359/1]). + +-define(m(Guard, Expression), + fun() -> + Actual = (catch (Expression)), + case Actual of + Guard -> Actual; + _Other -> + ct:fail("Failed to match ~p, actual result was ~p", + [??Guard, Actual]) + end + end()). -define(EXIT(Reason), {'EXIT',{Reason,[{_,_,_,_}|_]}}). -init_per_testcase(_Func, Config) -> - Config. - -end_per_testcase(_Func, _Config) -> - ok. - -error(Format, Args, File, Line) -> - io:format("~p:~p: ERROR: " ++ Format, [File,Line|Args]), - group_leader() ! {failed, File, Line}. - -%% Hopefully I don't need this to get it to work with the testserver.. -%% Fail = #'REASON'{file = filename:basename(File), -%% line = Line, -%% desc = Args}, -%% case global:whereis_name(mnesia_test_case_sup) of -%% undefined -> -%% ignore; -%% Pid -> -%% Pid ! Fail -%% %% global:send(mnesia_test_case_sup, Fail), -%% end, -%% log("<>ERROR<>~n" ++ Format, Args, File, Line). - suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,1}}]. @@ -99,20 +94,6 @@ groups() -> deflate_bench_zeroed, deflate_bench_rand, chunk_bench_zeroed, chunk_bench_rand]}]. -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - - %% Test open/0 and close/1. api_open_close(Config) when is_list(Config) -> Fd1 = zlib:open(), @@ -216,7 +197,7 @@ api_deflateSetDictionary(Config) when is_list(Config) -> ?m(Id when is_integer(Id), zlib:deflateSetDictionary(Z1, [1,1,2,3,4,5,1])), ?m(?EXIT(badarg), zlib:deflateSetDictionary(Z1, gurka)), ?m(?EXIT(badarg), zlib:deflateSetDictionary(Z1, 128)), - ?m(_, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)), + ?m(L when is_list(L), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)), ?m(?EXIT(stream_error), zlib:deflateSetDictionary(Z1,<<1,1,2,3,4,5,1>>)), ?m(ok, zlib:close(Z1)). @@ -224,7 +205,7 @@ api_deflateSetDictionary(Config) when is_list(Config) -> api_deflateReset(Config) when is_list(Config) -> Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1, default)), - ?m(_, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)), + ?m(L when is_list(L), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)), ?m(ok, zlib:deflateReset(Z1)), ?m(ok, zlib:deflateReset(Z1)), %% FIXME how do I make this go wrong?? @@ -234,9 +215,9 @@ api_deflateReset(Config) when is_list(Config) -> api_deflateParams(Config) when is_list(Config) -> Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1, default)), - ?m(_, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)), + ?m(L when is_list(L), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)), ?m(ok, zlib:deflateParams(Z1, best_compression, huffman_only)), - ?m(_, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, sync)), + ?m(L when is_list(L), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, sync)), ?m(ok, zlib:close(Z1)). %% Test deflate. @@ -368,14 +349,6 @@ api_inflateGetDictionary_if_supported(true) -> ok. -clobber(N, Bin) when is_binary(Bin) -> - T = list_to_tuple(binary_to_list(Bin)), - Byte = case element(N, T) of - 255 -> 254; - B -> B+1 - end, - list_to_binary(tuple_to_list(setelement(N, T, Byte))). - %% Test inflateReset. api_inflateReset(Config) when is_list(Config) -> Z1 = zlib:open(), @@ -536,8 +509,8 @@ api_crc32(Config) when is_list(Config) -> Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1,best_speed,deflated,-15,8,default)), Bin = <<1,1,1,1,1,1,1,1,1>>, - Compressed1 = ?m(_, zlib:deflate(Z1, Bin, none)), - Compressed2 = ?m(_, zlib:deflate(Z1, <<>>, finish)), + Compressed1 = ?m(L when is_list(L), zlib:deflate(Z1, Bin, none)), + Compressed2 = ?m(L when is_list(L), zlib:deflate(Z1, <<>>, finish)), Compressed = list_to_binary(Compressed1 ++ Compressed2), CRC1 = ?m( CRC1 when is_integer(CRC1), zlib:crc32(Z1)), ?m(CRC1 when is_integer(CRC1), zlib:crc32(Z1,Bin)), @@ -562,8 +535,8 @@ api_adler32(Config) when is_list(Config) -> Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1,best_speed,deflated,-15,8,default)), Bin = <<1,1,1,1,1,1,1,1,1>>, - Compressed1 = ?m(_, zlib:deflate(Z1, Bin, none)), - Compressed2 = ?m(_, zlib:deflate(Z1, <<>>, finish)), + Compressed1 = ?m(L when is_list(L), zlib:deflate(Z1, Bin, none)), + Compressed2 = ?m(L when is_list(L), zlib:deflate(Z1, <<>>, finish)), Compressed = list_to_binary(Compressed1 ++ Compressed2), ?m(ADLER1 when is_integer(ADLER1), zlib:adler32(Z1,Bin)), ?m(ADLER1 when is_integer(ADLER1), zlib:adler32(Z1,binary_to_list(Bin))), @@ -609,7 +582,48 @@ api_un_zip(Config) when is_list(Config) -> ?m(Bin, zlib:unzip(binary_to_list(Comp))), %% OTP-6396 - B = <<131,104,19,100,0,13,99,95,99,105,100,95,99,115,103,115,110,95,50,97,1,107,0,4,208,161,246,29,107,0,3,237,166,224,107,0,6,66,240,153,0,2,10,1,0,8,97,116,116,97,99,104,101,100,104,2,100,0,22,117,112,100,97,116,101,95,112,100,112,95,99,111,110,116,101,120,116,95,114,101,113,107,0,114,69,3,12,1,11,97,31,113,150,64,104,132,61,64,104,12,3,197,31,113,150,64,104,132,61,64,104,12,1,11,97,31,115,150,64,104,116,73,64,104,0,0,0,0,0,0,65,149,16,61,65,149,16,61,1,241,33,4,5,0,33,4,4,10,6,10,181,4,10,6,10,181,38,15,99,111,109,109,97,110,100,1,114,45,97,112,110,45,49,3,99,111,109,5,109,110,99,57,57,6,109,99,99,50,52,48,4,103,112,114,115,8,0,104,2,104,2,100,0,8,97,99,116,105,118,97,116,101,104,23,100,0,11,112,100,112,95,99,111,110,116,1,120,116,100,0,7,112,114,105,109,97,114,121,97,1,100,0,9,117,110,100,101,102,105,110,101,100,97,1,97,4,97,4,97,7,100,0,9,117,110,100,101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,110,10100,100,0,9,117,110,100,101,102,105,110,101,100,100,0,5,102,97,108,115,101,100,0,9,117,110,100,101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,1,101,100,97,0,100,0,9,117,110,100,101,102,105,110,101,100,107,0,4,16,0,1,144,107,0,4,61,139,186,181,107,0,4,10,8,201,49,100,0,9,117,110,100,101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,0,101,100,100,0,9,117,110,100,101,102,105,110,101,100,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97,16,97,21,106,108,0,0,0,3,104,2,97,1,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97,167,20,104,2,97,4,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97,16,97,21,104,2,97,10,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97,16,97,26,106,100,0,5,118,101,114,57,57,100,0,9,117,110,0,101,102,105,110,101,100,107,0,2,0,244,107,0,4,10,6,102,195,107,0,4,10,6,102,195,100,0,9,117,110,100,101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,110,101,100,107,0,125,248,143,0,203,25115,157,116,65,185,65,172,55,87,164,88,225,50,203,251,115,157,116,65,185,65,172,55,87,164,88,225,50,0,0,82,153,50,0,200,98,87,148,237,193,185,65,149,167,69,144,14,16,153,50,3,81,70,94,13,109,193,1,120,5,181,113,198,118,50,3,81,70,94,13,109,193,185,120,5,181,113,198,118,153,3,81,70,94,13,109,193,185,120,5,181,113,198,118,153,50,16,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,113,92,2,119,128,0,0,108,0,0,1,107,0,114,69,3,12,1,11,97,31,113,150,64,104,132,61,64,104,12,3,11,97,31,113,150,64,104,132,61,64,104,12,1,11,97,31,115,150,64,104,116,73,64,104,0,0,0,0,0,0,65,149,16,61,65,149,16,61,1,241,33,4,0,33,4,4,10,6,10,181,4,10,6,10,181,38,15,99,111,109,109,97,110,100,101,114,45,97,112,110,45,49,3,99,111,109,5,109,110,99,57,57,6,109,99,99,50,52,48,4,103,112,114,115,8,0,106>>, + B = + <<131,104,19,100,0,13,99,95,99,105,100,95,99,115,103,115,110,95,50,97, + 1,107,0,4,208,161,246,29,107,0,3,237,166,224,107,0,6,66,240,153,0,2, + 10,1,0,8,97,116,116,97,99,104,101,100,104,2,100,0,22,117,112,100,97, + 116,101,95,112,100,112,95,99,111,110,116,101,120,116,95,114,101,113, + 107,0,114,69,3,12,1,11,97,31,113,150,64,104,132,61,64,104,12,3,197, + 31,113,150,64,104,132,61,64,104,12,1,11,97,31,115,150,64,104,116,73, + 64,104,0,0,0,0,0,0,65,149,16,61,65,149,16,61,1,241,33,4,5,0,33,4,4,10 + ,6,10,181,4,10,6,10,181,38,15,99,111,109,109,97,110,100,1,114,45,97, + 112,110,45,49,3,99,111,109,5,109,110,99,57,57,6,109,99,99,50,52,48,4, + 103,112,114,115,8,0,104,2,104,2,100,0,8,97,99,116,105,118,97,116,101, + 104,23,100,0,11,112,100,112,95,99,111,110,116,1,120,116,100,0,7,112, + 114,105,109,97,114,121,97,1,100,0,9,117,110,100,101,102,105,110,101, + 100,97,1,97,4,97,4,97,7,100,0,9,117,110,100,101,102,105,110,101,100, + 100,0,9,117,110,100,101,102,105,110,10100,100,0,9,117,110,100,101, + 102,105,110,101,100,100,0,5,102,97,108,115,101,100,0,9,117,110,100, + 101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,110,101,100, + 100,0,9,117,110,100,101,102,105,1,101,100,97,0,100,0,9,117,110,100, + 101,102,105,110,101,100,107,0,4,16,0,1,144,107,0,4,61,139,186,181, + 107,0,4,10,8,201,49,100,0,9,117,110,100,101,102,105,110,101,100,100, + 0,9,117,110,100,101,102,105,0,101,100,100,0,9,117,110,100,101,102, + 105,110,101,100,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97, + 16,97,21,106,108,0,0,0,3,104,2,97,1,104,2,104,3,98,0,0,7,214,97,11, + 97,20,104,3,97,17,97,167,20,104,2,97,4,104,2,104,3,98,0,0,7,214,97, + 11,97,20,104,3,97,17,97,16,97,21,104,2,97,10,104,2,104,3,98,0,0,7, + 214,97,11,97,20,104,3,97,17,97,16,97,26,106,100,0,5,118,101,114,57, + 57,100,0,9,117,110,0,101,102,105,110,101,100,107,0,2,0,244,107,0,4, + 10,6,102,195,107,0,4,10,6,102,195,100,0,9,117,110,100,101,102,105, + 110,101,100,100,0,9,117,110,100,101,102,105,110,101,100,107,0,125, + 248,143,0,203,25115,157,116,65,185,65,172,55,87,164,88,225,50,203, + 251,115,157,116,65,185,65,172,55,87,164,88,225,50,0,0,82,153,50,0, + 200,98,87,148,237,193,185,65,149,167,69,144,14,16,153,50,3,81,70,94, + 13,109,193,1,120,5,181,113,198,118,50,3,81,70,94,13,109,193,185,120, + 5,181,113,198,118,153,3,81,70,94,13,109,193,185,120,5,181,113,198, + 118,153,50,16,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,113,92,2,119,128,0,0, + 108,0,0,1,107,0,114,69,3,12,1,11,97,31,113,150,64,104,132,61,64,104, + 12,3,11,97,31,113,150,64,104,132,61,64,104,12,1,11,97,31,115,150,64, + 104,116,73,64,104,0,0,0,0,0,0,65,149,16,61,65,149,16,61,1,241,33,4,0, + 33,4,4,10,6,10,181,4,10,6,10,181,38,15,99,111,109,109,97,110,100,101, + 114,45,97,112,110,45,49,3,99,111,109,5,109,110,99,57,57,6,109,99,99, + 50,52,48,4,103,112,114,115,8,0,106>>, + Z = zlib:zip(B), ?m(B, zlib:unzip(Z)). @@ -680,7 +694,7 @@ large_deflate_do() -> zip_usage(Config) when is_list(Config) -> zip_usage(zip_usage({get_arg,Config})); zip_usage({get_arg,Config}) -> - Out = conf(data_dir,Config), + Out = get_data_dir(Config), {ok,ZIP} = file:read_file(filename:join(Out,"zipdoc.zip")), {ok,ORIG} = file:read_file(filename:join(Out,"zipdoc")), {run,ZIP,ORIG}; @@ -751,7 +765,7 @@ zip_usage({run,ZIP,ORIG}) -> gz_usage(Config) when is_list(Config) -> gz_usage(gz_usage({get_arg,Config})); gz_usage({get_arg,Config}) -> - Out = conf(data_dir,Config), + Out = get_data_dir(Config), {ok,GZIP} = file:read_file(filename:join(Out,"zipdoc.1.gz")), {ok,ORIG} = file:read_file(filename:join(Out,"zipdoc")), {ok,GZIP2} = file:read_file(filename:join(Out,"zipdoc.txt.gz")), @@ -772,7 +786,7 @@ gz_usage2(Config) -> case os:find_executable("gzip") of Name when is_list(Name) -> Z = zlib:open(), - Out = conf(data_dir,Config), + Out = get_data_dir(Config), {ok,ORIG} = file:read_file(filename:join(Out,"zipdoc")), Compressed = zlib:gzip(ORIG), GzOutFile = filename:join(Out,"out.gz"), @@ -800,7 +814,7 @@ gz_usage2(Config) -> compress_usage(Config) when is_list(Config) -> compress_usage(compress_usage({get_arg,Config})); compress_usage({get_arg,Config}) -> - Out = conf(data_dir,Config), + Out = get_data_dir(Config), {ok,C1} = file:read_file(filename:join(Out,"png-compressed.zlib")), {run,C1}; compress_usage({run,C1}) -> @@ -855,7 +869,7 @@ compress_usage({run,C1}) -> crc(Config) when is_list(Config) -> crc(crc({get_arg,Config})); crc({get_arg,Config}) -> - Out = conf(data_dir,Config), + Out = get_data_dir(Config), {ok,C1} = file:read_file(filename:join(Out,"zipdoc")), {run,C1}; crc({run,C1}) -> @@ -884,7 +898,7 @@ crc({run,C1}) -> adler(Config) when is_list(Config) -> adler(adler({get_arg,Config})); adler({get_arg,Config}) -> - Out = conf(data_dir,Config), + Out = get_data_dir(Config), File1 = filename:join(Out,"zipdoc"), {ok,C1} = file:read_file(File1), {run,C1}; @@ -933,7 +947,7 @@ dictionary_usage({run}) -> Z2 = zlib:open(), ?m(ok, zlib:inflateInit(Z2)), - ?EXIT({need_dictionary, DictID}) = (catch zlib:inflate(Z2, Compressed)), + ?m(?EXIT({need_dictionary, DictID}), zlib:inflate(Z2, Compressed)), ?m(ok, zlib:inflateSetDictionary(Z2, Dict)), ?m(ok, zlib:inflateSetDictionary(Z2, binary_to_list(Dict))), @@ -964,7 +978,7 @@ only_allow_owner(Config) when is_list(Config) -> {'DOWN', Ref, process, Pid, _Reason} -> ok after 200 -> - ?error("Spawned worker timed out.", []) + ct:fail("Spawned worker timed out.") end, ?m(ok, zlib:inflateReset(Z)). @@ -982,30 +996,32 @@ sub_heap_binaries(Config) when is_list(Config) -> %% Check concurrent access to zlib driver. smp(Config) -> case erlang:system_info(smp_support) of - true -> - NumOfProcs = lists:min([8,erlang:system_info(schedulers)]), - io:format("smp starting ~p workers\n",[NumOfProcs]), - - %% Tests to run in parallel. - Funcs = [zip_usage, gz_usage, compress_usage, dictionary_usage, - crc, adler], - - %% We get all function arguments here to avoid repeated parallel - %% file read access. - FnAList = lists:map(fun(F) -> {F,?MODULE:F({get_arg,Config})} - end, Funcs), - - Pids = [spawn_link(?MODULE, worker, [rand:uniform(9999), - list_to_tuple(FnAList), - self()]) - || _ <- lists:seq(1,NumOfProcs)], - wait_pids(Pids); - - false -> - {skipped,"No smp support"} + true -> + NumOfProcs = lists:min([8,erlang:system_info(schedulers)]), + io:format("smp starting ~p workers\n",[NumOfProcs]), + + %% Tests to run in parallel. + Funcs = + [zip_usage, gz_usage, compress_usage, dictionary_usage, + crc, adler], + + %% We get all function arguments here to avoid repeated parallel + %% file read access. + UsageArgs = + list_to_tuple([{F, ?MODULE:F({get_arg,Config})} || F <- Funcs]), + Parent = self(), + + WorkerFun = + fun() -> + worker(rand:uniform(9999), UsageArgs, Parent) + end, + + Pids = [spawn_link(WorkerFun) || _ <- lists:seq(1, NumOfProcs)], + wait_pids(Pids); + false -> + {skipped,"No smp support"} end. - worker(Seed, FnATpl, Parent) -> io:format("smp worker ~p, seed=~p~n",[self(),Seed]), rand:seed(exsplus, {Seed,Seed,Seed}), @@ -1171,41 +1187,16 @@ measure_perf_counter(Fun, Unit) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Helps with testing directly %%%%%%%%%%%%% -conf(What,Config) -> - try proplists:get_value(What,Config) of - undefined -> - "./zlib_SUITE_data"; - Dir -> - Dir +get_data_dir(Config) -> + try proplists:get_value(data_dir,Config) of + undefined -> + "./zlib_SUITE_data"; + Dir -> + Dir catch - _:_ -> "./zlib_SUITE_data" + _:_ -> "./zlib_SUITE_data" end. -t() -> t([all]). - -t(What) when not is_list(What) -> - t([What]); -t(What) -> - lists:foreach(fun(T) -> - try ?MODULE:T([]) - catch _E:_R -> - Line = get(test_server_loc), - io:format("Failed ~p:~p ~p ~p ~p~n", - [T,Line,_E,_R, erlang:get_stacktrace()]) - end - end, expand(What)). - -expand(All) -> - lists:reverse(expand(All,[])). -expand([H|T], Acc) -> - case ?MODULE:H(suite) of - [] -> expand(T,[H|Acc]); - Cs -> - R = expand(Cs, Acc), - expand(T, R) - end; -expand([], Acc) -> Acc. - %% Generates a bunch of statistically random bytes using the size as seed. gen_determ_rand_bytes(Size) -> gen_determ_rand_bytes(Size, erlang:md5_init(), <<>>). -- cgit v1.2.3 From bde065de5088d896cda8ac708788bd8849c3164b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 13 Sep 2017 15:23:01 +0200 Subject: code_SUITE:on_load_trace_on_load --- lib/kernel/test/code_SUITE.erl | 50 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 62ad7b6a27..a5a5da4684 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -35,6 +35,7 @@ purge_stacktrace/1, mult_lib_roots/1, bad_erl_libs/1, code_archive/1, code_archive2/1, on_load/1, on_load_binary/1, on_load_embedded/1, on_load_errors/1, on_load_update/1, + on_load_trace_on_load/1, on_load_purge/1, on_load_self_call/1, on_load_pending/1, on_load_deleted/1, big_boot_embedded/1, @@ -65,13 +66,15 @@ all() -> ext_mod_dep, clash, where_is_file, purge_stacktrace, mult_lib_roots, bad_erl_libs, code_archive, code_archive2, on_load, - on_load_binary, on_load_embedded, on_load_errors, on_load_update, + on_load_binary, on_load_embedded, on_load_errors, + {group, sequence}, on_load_purge, on_load_self_call, on_load_pending, on_load_deleted, big_boot_embedded, native_early_modules, get_mode, normalized_paths]. -groups() -> - []. +%% These need to run in order +groups() -> [{sequence, [sequence], [on_load_update, + on_load_trace_on_load]}]. init_per_group(_GroupName, Config) -> Config. @@ -1456,7 +1459,7 @@ do_on_load_error(ReturnValue) -> {undef,[{on_load_error,main,[],_}|_]} = Exit end. -on_load_update(_Config) -> +on_load_update(Config) -> {Mod,Code1} = on_load_update_code(1), {module,Mod} = code:load_binary(Mod, "", Code1), 42 = Mod:a(), @@ -1466,7 +1469,7 @@ on_load_update(_Config) -> {Mod,Code2} = on_load_update_code(2), {error,on_load_failure} = code:load_binary(Mod, "", Code2), 42 = Mod:a(), - 100 = Mod:b(99), + 78 = Mod:b(77), {'EXIT',{undef,_}} = (catch Mod:never()), 4 = erlang:trace_pattern({Mod,'_','_'}, false), @@ -1477,6 +1480,9 @@ on_load_update(_Config) -> {'EXIT',{undef,_}} = (catch Mod:b(10)), {'EXIT',{undef,_}} = (catch Mod:never()), + code:purge(Mod), + code:delete(Mod), + code:purge(Mod), ok. on_load_update_code(Version) -> @@ -1508,6 +1514,40 @@ on_load_update_code_1(3, Mod) -> "f() -> ok.\n", "c() -> 100.\n"]). +%% Test -on_load while trace feature 'on_load' is enabled (OTP-14612) +on_load_trace_on_load(Config) -> + Papa = self(), + Tracer = spawn_link(fun F() -> receive M -> Papa ! M end, F() end), + {tracer,[]} = erlang:trace_info(self(),tracer), + erlang:trace(self(), true, [call, {tracer, Tracer}]), + erlang:trace_pattern(on_load, true, []), + on_load_update(Config), + erlang:trace_pattern(on_load, false, []), + erlang:trace(self(), false, [call]), + + %% WE GET TRACES FOR CALLS TO UNDEFINED FUNCTIONS ??? + %% Remove filter when that is fixed. + Ms = lists:filter(fun({trace,Papa,call, + {error_handler,undefined_function, + [on_load_update_code,_,_]}}) + -> false; + (_) -> true + end, + flush()), + + [{trace, Papa, call, {on_load_update_code, a, []}}, + {trace, Papa, call, {on_load_update_code, b, [99]}}, + {trace, Papa, call, {on_load_update_code, c, []}}] = Ms, + + exit(Tracer, normal), + ok. + +flush() -> + receive M -> [M | flush()] + after 100 -> [] + end. + + on_load_purge(_Config) -> Mod = ?FUNCTION_NAME, register(Mod, self()), -- cgit v1.2.3 From 53e83c1ee2c7d6a95512162b31f72ea2442a836e Mon Sep 17 00:00:00 2001 From: Kozlov Yakov Date: Fri, 1 Sep 2017 11:27:31 +0300 Subject: Fixed dns lookup in case of server close connection with econnrefused --- lib/kernel/src/inet_res.erl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl index 90e49ddfdf..49aa5f8bda 100644 --- a/lib/kernel/src/inet_res.erl +++ b/lib/kernel/src/inet_res.erl @@ -859,15 +859,17 @@ query_ns(S0, Id, Buffer, IP, Port, Timer, Retry, I, {ok,S} -> Timeout = inet:timeout( (Tm * (1 bsl I)) div Retry, Timer), - {S, case query_udp( S, Id, Buffer, IP, Port, Timeout, Verbose) of {ok,#dns_rec{header=H}} when H#dns_header.tc -> TcpTimeout = inet:timeout(Tm*5, Timer), - query_tcp( - TcpTimeout, Id, Buffer, IP, Port, Verbose); - Reply -> Reply - end}; + {S, query_tcp( + TcpTimeout, Id, Buffer, IP, Port, Verbose)}; + {error, econnrefused} = Err -> + ok = udp_close(S), + {#sock{}, Err}; + Reply -> {S, Reply} + end; Error -> {S0,Error} end -- cgit v1.2.3 From 2b68cd320d28f6bf9b41bb8d52d34cef15e4baf2 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 14 Sep 2017 16:35:27 +0200 Subject: kernel: Don't call other modules from simple error logger error_logger implements a simple handler which is used when no other handler is active. This handler is used during system startup, when there is no guarantee that all modules are loaded and must therefore not use any other modules for the formatting. Commit 41b856a2cb270ef807beaa84afe1bb890de5f237 introduced a call to io_lib:printable_list/1, which is now removed. --- lib/kernel/src/error_logger.erl | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index f07ec52cfa..585507c545 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -499,16 +499,31 @@ display4(A = [_|_]) -> display4(A) -> erlang:display(A). - -string_p(Term) when is_list(Term) -> - string_p1(lists:flatten(Term)); -string_p(_Term) -> - false. - -string_p1([]) -> +string_p([]) -> false; -string_p1(FlatList) -> - io_lib:printable_list(FlatList). +string_p(Term) -> + string_p1(Term). + +string_p1([H|T]) when is_integer(H), H >= $\040, H =< $\176 -> + string_p1(T); +string_p1([H|T]) when is_integer(H), H >= 16#A0, H < 16#D800; + is_integer(H), H > 16#DFFF, H < 16#FFFE; + is_integer(H), H > 16#FFFF, H =< 16#10FFFF -> + string_p1(T); +string_p1([$\n|T]) -> string_p1(T); +string_p1([$\r|T]) -> string_p1(T); +string_p1([$\t|T]) -> string_p1(T); +string_p1([$\v|T]) -> string_p1(T); +string_p1([$\b|T]) -> string_p1(T); +string_p1([$\f|T]) -> string_p1(T); +string_p1([$\e|T]) -> string_p1(T); +string_p1([H|T]) when is_list(H) -> + case string_p1(H) of + true -> string_p1(T); + _ -> false + end; +string_p1([]) -> true; +string_p1(_) -> false. -spec limit_term(term()) -> term(). -- cgit v1.2.3 From 5bc277e16e359072bba4c872de31ea486d77e042 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Tue, 19 Sep 2017 12:54:58 +0200 Subject: Update runtime deps to depend on new stdlib functionality ~tw and new string functions are new since OTP-20 (stdlib-3.4) --- lib/kernel/src/kernel.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 2a11b04310..2a88cc7e26 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -120,6 +120,6 @@ {applications, []}, {env, [{error_logger, tty}]}, {mod, {kernel, []}}, - {runtime_dependencies, ["erts-9.1", "stdlib-3.0", "sasl-3.0"]} + {runtime_dependencies, ["erts-9.1", "stdlib-3.4", "sasl-3.0"]} ] }. -- cgit v1.2.3 From 79f28cfd8df1b700baff0df5e3d766d0a5592581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 22 Sep 2017 07:24:51 +0200 Subject: Speed up erts_debug:df() The test case erts_debug_SUITE:df/1 in the emulator test suite is about 4 times faster with this change. --- lib/kernel/src/erts_debug.erl | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl index 480db6814e..2887014c1c 100644 --- a/lib/kernel/src/erts_debug.erl +++ b/lib/kernel/src/erts_debug.erl @@ -378,16 +378,11 @@ df(Mod, Func, Arity) when is_atom(Mod), is_atom(Func) -> catch _:_ -> {undef,Mod} end. -dff(File, Fs) when is_pid(File), is_list(Fs) -> - lists:foreach(fun(Mfa) -> - disassemble_function(File, Mfa), - io:nl(File) - end, Fs); -dff(Name, Fs) when is_list(Name) -> - case file:open(Name, [write]) of +dff(Name, Fs) -> + case file:open(Name, [write,raw,delayed_write]) of {ok,F} -> try - dff(F, Fs) + dff_1(F, Fs) after _ = file:close(F) end; @@ -395,12 +390,18 @@ dff(Name, Fs) when is_list(Name) -> {error,{badopen,Reason}} end. +dff_1(File, Fs) -> + lists:foreach(fun(Mfa) -> + disassemble_function(File, Mfa), + file:write(File, "\n") + end, Fs). + disassemble_function(File, {_,_,_}=MFA) -> cont_dis(File, erts_debug:disassemble(MFA), MFA). cont_dis(_, false, _) -> ok; cont_dis(File, {Addr,Str,MFA}, MFA) -> - io:put_chars(File, binary_to_list(Str)), + ok = file:write(File, Str), cont_dis(File, erts_debug:disassemble(Addr), MFA); cont_dis(_, {_,_,_}, _) -> ok. -- cgit v1.2.3 From 1bf0b21344d951c65e54c6abfe7907ef8b63d946 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 22 Sep 2017 15:19:03 +0200 Subject: Update version numbers --- lib/kernel/vsn.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index c9463241d1..cef54dd41a 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 5.3.1 +KERNEL_VSN = 5.4 -- cgit v1.2.3 From af2073640c4b7c67c9b978ebc203d57ac43e96dc Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 22 Sep 2017 15:21:49 +0200 Subject: Update release notes --- lib/kernel/doc/src/notes.xml | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 9cd03ffcad..a5316dd476 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,50 @@

    This document describes the changes made to the Kernel application.

    +
    Kernel 5.4 + +
    Fixed Bugs and Malfunctions + + +

    + Processes which did output after switching jobs (Ctrl+G) + could be left forever stuck in the io request.

    +

    + Own Id: OTP-14571 Aux Id: ERL-472

    +
    +
    +
    + + +
    Improvements and New Features + + +

    Lock counting can now be fully toggled at runtime in + the lock counting emulator (-emu_type lcnt). + Everything is enabled by default to match the old + behavior, but specific categories can be toggled at will + with minimal runtime overhead when disabled. Refer to the + documentation on lcnt:rt_mask/1 for details.

    +

    + Own Id: OTP-13170

    +
    + +

    lcnt:collect and lcnt:clear will no + longer block all other threads in the runtime system.

    +

    + Own Id: OTP-14412

    +
    + +

    + General Unicode improvements.

    +

    + Own Id: OTP-14462

    +
    +
    +
    + +
    +
    Kernel 5.3.1
    Fixed Bugs and Malfunctions -- cgit v1.2.3 From eae496a72e270fd7af411714738e99a7fadfd19b Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 6 Sep 2017 17:00:14 +0200 Subject: Don't allow null in filenames --- lib/kernel/doc/src/file.xml | 38 +++++++++++++++++++++++++++++++++---- lib/kernel/src/kernel.app.src | 2 +- lib/kernel/test/file_name_SUITE.erl | 2 ++ 3 files changed, 37 insertions(+), 5 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index b674b3ca93..593bee74fe 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -59,7 +59,7 @@ terminal supports UTF-8, otherwise latin1. The default can be overridden using +fnl (to force latin1 mode) or +fnu (to force utf8 mode) when starting - erts:erl.

    + erl.

    On operating systems with transparent naming, files can be inconsistently named, for example, some files are encoded in UTF-8 while @@ -81,6 +81,22 @@

    See also section Notes About Raw Filenames in the STDLIB User's Guide.

    +

    + File operations used to accept filenames containing + null characters (integer value zero). This caused + the name to be truncated at the first null character. + Filenames containing null characters inside the filename + are now rejected and will cause primitive + file operations fail. +

    +

    + Currently null characters at the end of the filename + will be accepted by primitive file operations. Such + filenames are however still documented as invalid. The + implementation will also change in the future and + reject such filenames. +

    + @@ -96,9 +112,21 @@ + +

    + See also the documentation of the + name_all() type. +

    +
    + +

    + See also the documentation of the + name_all() type. +

    +
    @@ -112,21 +140,23 @@

    If VM is in Unicode filename mode, string() and char() - are allowed to be > 255. + are allowed to be > 255. See also the documentation of the + name_all() type.

    -

    If VM is in Unicode filename mode, string() and char() +

    If VM is in Unicode filename mode, characters are allowed to be > 255. RawFilename is a filename not subject to Unicode translation, meaning that it can contain characters not conforming to the Unicode encoding expected from the file system (that is, non-UTF-8 characters although the VM is started - in Unicode filename mode). + in Unicode filename mode). Null characters (integer value zero) + are not allowed in filenames (not even at the end).

    diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 2a88cc7e26..b5e5f8eb73 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -120,6 +120,6 @@ {applications, []}, {env, [{error_logger, tty}]}, {mod, {kernel, []}}, - {runtime_dependencies, ["erts-9.1", "stdlib-3.4", "sasl-3.0"]} + {runtime_dependencies, ["erts-9.1.1", "stdlib-3.4.3", "sasl-3.0"]} ] }. diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl index 899102c908..f23529fec9 100644 --- a/lib/kernel/test/file_name_SUITE.erl +++ b/lib/kernel/test/file_name_SUITE.erl @@ -302,7 +302,9 @@ check_normal(Mod) -> {ok, BC} = Mod:read(FD,1024), ok = file:close(FD) end || {regular,Name,Content} <- NormalDir ], + {error, badarg} = Mod:rename("fil1\0tmp_fil2","tmp_fil1"), Mod:rename("fil1","tmp_fil1"), + {error, badarg} = Mod:read_file("tmp_fil1\0.txt"), {ok, <<"fil1">>} = Mod:read_file("tmp_fil1"), {error,enoent} = Mod:read_file("fil1"), Mod:rename("tmp_fil1","fil1"), -- cgit v1.2.3 From 9395c7b4a2079349fdb881a1f537af5b73eacfd2 Mon Sep 17 00:00:00 2001 From: Lars Thorsen Date: Mon, 25 Sep 2017 15:15:37 +0200 Subject: Remove unused files from the documentation build --- lib/kernel/doc/src/Makefile | 13 +++++----- lib/kernel/doc/src/fascicules.xml | 15 ----------- lib/kernel/doc/src/part_notes.xml | 40 ------------------------------ lib/kernel/doc/src/part_notes_history.xml | 40 ------------------------------ lib/kernel/doc/src/user_guide.gif | Bin 1581 -> 0 bytes 5 files changed, 6 insertions(+), 102 deletions(-) delete mode 100644 lib/kernel/doc/src/fascicules.xml delete mode 100644 lib/kernel/doc/src/part_notes.xml delete mode 100644 lib/kernel/doc/src/part_notes_history.xml delete mode 100644 lib/kernel/doc/src/user_guide.gif (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile index 8976a3b800..c9d23ac4c4 100644 --- a/lib/kernel/doc/src/Makefile +++ b/lib/kernel/doc/src/Makefile @@ -70,7 +70,7 @@ XML_REF4_FILES = app.xml config.xml XML_REF6_FILES = kernel_app.xml -XML_PART_FILES = part_notes.xml part_notes_history.xml +XML_PART_FILES = XML_CHAPTER_FILES = notes.xml notes_history.xml BOOK_FILES = book.xml @@ -100,9 +100,9 @@ SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml) TOP_SPECS_FILE = specs.xml # ---------------------------------------------------- -# FLAGS +# FLAGS # ---------------------------------------------------- -XML_FLAGS += +XML_FLAGS += SPECS_ESRC = ../../src @@ -125,7 +125,7 @@ html: gifs $(HTML_REF_MAN_FILE) man: $(MAN3_FILES) $(MAN4_FILES) $(MAN6_FILES) gifs: $(GIF_FILES:%=$(HTMLDIR)/%) -debug opt: +debug opt: clean clean_docs: rm -rf $(HTMLDIR)/* @@ -134,7 +134,7 @@ clean clean_docs: rm -f $(MAN6DIR)/* rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) rm -f $(SPECDIR)/* - rm -f errs core *~ + rm -f errs core *~ $(SPECDIR)/specs_erl_prim_loader_stub.xml: escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ @@ -151,7 +151,7 @@ $(SPECDIR)/specs_zlib_stub.xml: # ---------------------------------------------------- # Release Target -# ---------------------------------------------------- +# ---------------------------------------------------- include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs @@ -169,4 +169,3 @@ release_docs_spec: docs $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6" release_spec: - diff --git a/lib/kernel/doc/src/fascicules.xml b/lib/kernel/doc/src/fascicules.xml deleted file mode 100644 index fadd37eefb..0000000000 --- a/lib/kernel/doc/src/fascicules.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - Reference Manual - - - Release Notes - - - Off-Print - - - diff --git a/lib/kernel/doc/src/part_notes.xml b/lib/kernel/doc/src/part_notes.xml deleted file mode 100644 index 5e849039ee..0000000000 --- a/lib/kernel/doc/src/part_notes.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - -
    - - 20042016 - Ericsson AB. 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. - - - - Kernel Release Notes - - - - -
    - -

    The Kernel application has all the code necessary to run - the Erlang runtime system itself; File servers and code servers - etc.

    -

    For information about older versions, see - Release Notes History.

    -
    - -
    - diff --git a/lib/kernel/doc/src/part_notes_history.xml b/lib/kernel/doc/src/part_notes_history.xml deleted file mode 100644 index 0cf7b793da..0000000000 --- a/lib/kernel/doc/src/part_notes_history.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - -
    - - 2006 - 2016 - Ericsson AB, 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. - - The Initial Developer of the Original Code is Ericsson AB. - - - Kernel Release Notes History - - - - -
    - -

    The Kernel application has all the code necessary to run - the Erlang runtime system itself; File servers and code servers - etc.

    -
    - -
    - diff --git a/lib/kernel/doc/src/user_guide.gif b/lib/kernel/doc/src/user_guide.gif deleted file mode 100644 index e6275a803d..0000000000 Binary files a/lib/kernel/doc/src/user_guide.gif and /dev/null differ -- cgit v1.2.3 From bf9f00f16b9825c5df235c2df4b325f97fed199a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Fri, 29 Sep 2017 14:51:09 +0200 Subject: Fix minor incompatibilities in inflate behavior When presented with multiple valid but concatenated streams, the old driver returned an empty result once the end of the first stream was reached, and kept doing so even if fed new data. The new driver/NIF returned a data_error instead. zlib:inflateInit/3 has been added to control this behavior, but is not yet ready for public use. --- lib/kernel/test/zlib_SUITE.erl | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl index e246276262..b7e053b137 100644 --- a/lib/kernel/test/zlib_SUITE.erl +++ b/lib/kernel/test/zlib_SUITE.erl @@ -276,10 +276,10 @@ api_inflateInit(Config) when is_list(Config) -> ?m(ok,zlib:close(Z12)) end, lists:seq(8,15)), ?m(?EXIT(badarg), zlib:inflateInit(gurka, -15)), - ?m(?EXIT(already_initialized), zlib:inflateInit(Z1, 7)), - ?m(?EXIT(already_initialized), zlib:inflateInit(Z1, -7)), - ?m(?EXIT(already_initialized), zlib:inflateInit(Z1, 48)), - ?m(?EXIT(already_initialized), zlib:inflateInit(Z1, -16)), + ?m(?EXIT(bad_windowbits), zlib:inflateInit(Z1, 7)), + ?m(?EXIT(bad_windowbits), zlib:inflateInit(Z1, -7)), + ?m(?EXIT(bad_windowbits), zlib:inflateInit(Z1, 48)), + ?m(?EXIT(bad_windowbits), zlib:inflateInit(Z1, -16)), ?m(ok, zlib:close(Z1)). %% Test inflateSetDictionary. @@ -416,6 +416,9 @@ api_inflateChunk(Config) when is_list(Config) -> {more, Part1AsIOList} = zlib:inflateChunk(Z1, Compressed), {more, Part2AsIOList} = zlib:inflateChunk(Z1), {more, Part3AsIOList} = zlib:inflateChunk(Z1), + + [] = zlib:inflateChunk(Z1), + [] = zlib:inflateChunk(Z1), [] = zlib:inflateChunk(Z1), ?m(Part1, iolist_to_binary(Part1AsIOList)), @@ -483,7 +486,8 @@ api_safeInflate(Config) when is_list(Config) -> SafeInflateLoop(zlib:safeInflate(Z1, Compressed), []), - ?m(?EXIT(data_error), zlib:safeInflate(Z1, Compressed)), + ?m({finished, []}, zlib:safeInflate(Z1, Compressed)), + ?m({finished, []}, zlib:safeInflate(Z1, Compressed)), ?m(ok, zlib:inflateReset(Z1)), ?m(?EXIT(badarg), zlib:safeInflate(gurka, Compressed)), -- cgit v1.2.3 From 2257a0a2debf0a390e887e09c85bf14ba186f278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Fri, 29 Sep 2017 15:25:02 +0200 Subject: Fix gunzip/1 of concatenated gzip files Quoting RFC 1952: "A gzip file consists of a series of "members" (compressed data sets). [...] The members simply appear one after another in the file, with no additional information before, between, or after them." --- lib/kernel/test/zlib_SUITE.erl | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl index b7e053b137..d17eded811 100644 --- a/lib/kernel/test/zlib_SUITE.erl +++ b/lib/kernel/test/zlib_SUITE.erl @@ -636,6 +636,7 @@ api_g_un_zip(Config) when is_list(Config) -> ?m(?EXIT(badarg),zlib:gzip(not_a_binary)), Bin = <<1,11,1,23,45>>, Comp = zlib:gzip(Bin), + ?m(Comp, zlib:gzip(binary_to_list(Bin))), ?m(?EXIT(badarg), zlib:gunzip(not_a_binary)), ?m(?EXIT(data_error), zlib:gunzip(<<171,171,171,171,171>>)), @@ -643,6 +644,14 @@ api_g_un_zip(Config) when is_list(Config) -> ?m(Bin, zlib:gunzip(Comp)), ?m(Bin, zlib:gunzip(binary_to_list(Comp))), + %% RFC 1952: + %% + %% "A gzip file consists of a series of "members" (compressed data + %% sets). [...] The members simply appear one after another in the file, + %% with no additional information before, between, or after them." + Concatenated = <>, + ?m(Concatenated, zlib:gunzip([Comp, Comp])), + %% Bad CRC; bad length. BadCrc = bad_crc_data(), ?m(?EXIT(data_error),(catch zlib:gunzip(BadCrc))), -- cgit v1.2.3 From c5d336980c862be5e5872f1a01d877c19efb7d92 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 8 Sep 2017 11:25:28 +0200 Subject: kernel: Rewrite gen_udp_SUITE:read_packet tc The old testcase did not test anything at all, it seems like it was written with the non-smp emulator inmind. --- lib/kernel/test/gen_udp_SUITE.erl | 108 +++++++++++++++++++++----------------- 1 file changed, 59 insertions(+), 49 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index 8364fe7cdc..96e495505a 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -288,46 +288,56 @@ bad_address(Config) when is_list(Config) -> %% %% Starts a slave node that on command sends a bunch of messages %% to our UDP port. The receiving process just receives and -%% ignores the incoming messages, but counts them. -%% A tracing process traces the receiving process for -%% 'receive' and scheduling events. From the trace, -%% message contents is verified; and, how many messages -%% are received per in/out scheduling, which should be -%% the same as the read_packets parameter. -%% +%% ignores the incoming messages. +%% A tracing process traces the receiving port for +%% 'send' and scheduling events. From the trace, +%% how many messages are received per in/out scheduling, +%% which should never be more than the read_packet parameter. %% OTP-6249 UDP option for number of packet reads. read_packets(Config) when is_list(Config) -> N1 = 5, - N2 = 7, + N2 = 1, + Msgs = 30000, {ok,R} = gen_udp:open(0, [{read_packets,N1}]), {ok,RP} = inet:port(R), {ok,Node} = start_node(gen_udp_SUITE_read_packets), Die = make_ref(), - Loop = erlang:spawn_link(fun () -> infinite_loop(Die) end), %% - Msgs1 = [erlang:integer_to_list(M) || M <- lists:seq(1, N1*3)], - [V1|_] = read_packets_test(R, RP, Msgs1, Node), + {V1, Trace1} = read_packets_test(R, RP, Msgs, Node), {ok,[{read_packets,N1}]} = inet:getopts(R, [read_packets]), %% ok = inet:setopts(R, [{read_packets,N2}]), - Msgs2 = [erlang:integer_to_list(M) || M <- lists:seq(1, N2*3)], - [V2|_] = read_packets_test(R, RP, Msgs2, Node), + {V2, Trace2} = read_packets_test(R, RP, Msgs, Node), {ok,[{read_packets,N2}]} = inet:getopts(R, [read_packets]), %% stop_node(Node), - Mref = erlang:monitor(process, Loop), - Loop ! Die, - receive - {'DOWN',Mref,_,_, normal} -> - case {V1,V2} of - {N1,N2} -> - ok; - _ when V1 =/= N1, V2 =/= N2 -> - ok - end + ct:log("N1=~p, V1=~p vs N2=~p, V2=~p",[N1,V1,N2,V2]), + + dump_terms(Config, "trace1.terms", Trace2), + dump_terms(Config, "trace2.terms", Trace2), + + %% Because of the inherit racy-ness of the feature it is + %% hard to test that it behaves correctly. + %% Right now (OTP 21) a port task takes 5% of the + %% allotted port task reductions to execute, so + %% the max number of executions a port is allowed to + %% do before being re-scheduled is N * 20 + + if + V1 > (N1 * 20) -> + ct:fail("Got ~p msgs, max was ~p", [V1, N1]); + V2 > (N2 * 20) -> + ct:fail("Got ~p msgs, max was ~p", [V2, N2]); + true -> + ok end. +dump_terms(Config, Name, Terms) -> + FName = filename:join(proplists:get_value(priv_dir, Config),Name), + file:write_file(FName, term_to_binary(Terms)), + ct:log("Logged terms to ~s",[FName]). + infinite_loop(Die) -> receive Die -> @@ -338,7 +348,6 @@ infinite_loop(Die) -> end. read_packets_test(R, RP, Msgs, Node) -> - Len = length(Msgs), Receiver = self(), Tracer = spawn_link( @@ -363,24 +372,24 @@ read_packets_test(R, RP, Msgs, Node) -> [link,{priority,high}]), receive {Sender,{port,SP}} -> - erlang:trace(self(), true, - [running,'receive',{tracer,Tracer}]), + erlang:trace(R, true, + [running_ports,'send',{tracer,Tracer}]), erlang:yield(), Sender ! {Receiver,go}, - read_packets_recv(Len), - erlang:trace(self(), false, [all]), + read_packets_recv(Msgs), + erlang:trace(R, false, [all]), Tracer ! {Receiver,get_trace}, receive {Tracer,{trace,Trace}} -> - read_packets_verify(R, SP, Msgs, Trace) + {read_packets_verify(R, SP, Trace), Trace} end end. -read_packets_send(S, RP, [Msg|Msgs]) -> - ok = gen_udp:send(S, localhost, RP, Msg), - read_packets_send(S, RP, Msgs); -read_packets_send(_S, _RP, []) -> - ok. +read_packets_send(_S, _RP, 0) -> + ok; +read_packets_send(S, RP, Msgs) -> + ok = gen_udp:send(S, localhost, RP, "UDP FLOOOOOOD"), + read_packets_send(S, RP, Msgs - 1). read_packets_recv(0) -> ok; @@ -392,23 +401,24 @@ read_packets_recv(N) -> timeout end. -read_packets_verify(R, SP, Msg, Trace) -> - lists:reverse( - lists:sort(read_packets_verify(R, SP, Msg, Trace, 0))). - -read_packets_verify(R, SP, Msgs, [{trace,Self,OutIn,_}|Trace], M) - when Self =:= self(), OutIn =:= out; - Self =:= self(), OutIn =:= in -> - push(M, read_packets_verify(R, SP, Msgs, Trace, 0)); -read_packets_verify(R, SP, [Msg|Msgs], - [{trace,Self,'receive',{udp,R,{127,0,0,1},SP,Msg}} - |Trace], M) +read_packets_verify(R, SP, Trace) -> + [Max | _] = Pkts = lists:reverse(lists:sort(read_packets_verify(R, SP, Trace, 0))), + ct:pal("~p",[lists:sublist(Pkts,10)]), + Max. + +read_packets_verify(R, SP, [{trace,R,OutIn,_}|Trace], M) + when OutIn =:= out; OutIn =:= in -> + push(M, read_packets_verify(R, SP, Trace, 0)); +read_packets_verify(R, SP, [{trace, R,'receive',timeout}|Trace], M) -> + push(M, read_packets_verify(R, SP, Trace, 0)); +read_packets_verify(R, SP, + [{trace,R,'send',{udp,R,{127,0,0,1},SP,_Msg}, Self} | Trace], M) when Self =:= self() -> - read_packets_verify(R, SP, Msgs, Trace, M+1); -read_packets_verify(_R, _SP, [], [], M) -> + read_packets_verify(R, SP, Trace, M+1); +read_packets_verify(_R, _SP, [], M) -> push(M, []); -read_packets_verify(_R, _SP, Msgs, Trace, M) -> - ct:fail({read_packets_verify,mismatch,Msgs,Trace,M}). +read_packets_verify(_R, _SP, Trace, M) -> + ct:fail({read_packets_verify,mismatch,Trace,M}). push(0, Vs) -> Vs; -- cgit v1.2.3 From 23817dd3f1bd70ede570b2aa065fe4ff26aedc04 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Tue, 26 Sep 2017 09:25:43 +0200 Subject: stdlib: Make ets:i/1 exit cleaner upon ^D (old Erlang shell) Instead of crashing, ^D now exits Erlang if started with -oldshell. --- lib/kernel/src/user.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/user.erl b/lib/kernel/src/user.erl index a5cc7b0ec1..872e63ab53 100644 --- a/lib/kernel/src/user.erl +++ b/lib/kernel/src/user.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. @@ -398,7 +398,7 @@ get_line(Prompt, Port, Q, Acc, Enc) -> get_line_bytes(Prompt, Port, Q, Acc, Bytes, Enc); {Port, eof} -> put(eof, true), - {ok, eof, []}; + {ok, eof, queue:new()}; {io_request,From,ReplyAs,{get_geometry,_}=Req} when is_pid(From) -> do_io_request(Req, From, ReplyAs, Port, queue:new()), @@ -615,7 +615,7 @@ get_chars(Prompt, M, F, Xa, Port, Q, State, Enc) -> get_chars_bytes(State, M, F, Xa, Port, Q, Bytes, Enc); {Port, eof} -> put(eof, true), - {ok, eof, []}; + {ok, eof, queue:new()}; %%{io_request,From,ReplyAs,Request} when is_pid(From) -> %% get_chars_req(Prompt, M, F, Xa, Port, queue:new(), State, %% Request, From, ReplyAs); -- cgit v1.2.3 From c0bbf64eeffcfd1aa662ddec4744186862d9ca69 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 3 Oct 2017 19:21:15 +0200 Subject: kernel: Cleanup trace_pattern after code_SUITE:upgrade --- lib/kernel/test/code_SUITE.erl | 11 +---------- lib/kernel/test/code_SUITE_data/upgrade_client.erl | 1 + 2 files changed, 2 insertions(+), 10 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 612f77149d..569753155f 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -1562,16 +1562,7 @@ on_load_trace_on_load(Config) -> erlang:trace_pattern(on_load, false, []), erlang:trace(self(), false, [call]), - %% WE GET TRACES FOR CALLS TO UNDEFINED FUNCTIONS ??? - %% Remove filter when that is fixed. - Ms = lists:filter(fun({trace,Papa,call, - {error_handler,undefined_function, - [on_load_update_code,_,_]}}) - -> false; - (_) -> true - end, - flush()), - + Ms = flush(), [{trace, Papa, call, {on_load_update_code, a, []}}, {trace, Papa, call, {on_load_update_code, b, [99]}}, {trace, Papa, call, {on_load_update_code, c, []}}] = Ms, diff --git a/lib/kernel/test/code_SUITE_data/upgrade_client.erl b/lib/kernel/test/code_SUITE_data/upgrade_client.erl index faa18e1410..1c3c2def53 100644 --- a/lib/kernel/test/code_SUITE_data/upgrade_client.erl +++ b/lib/kernel/test/code_SUITE_data/upgrade_client.erl @@ -341,6 +341,7 @@ check_tracing_loop(N, MsgList) -> stop_tracing(Tracer) -> + erlang:trace_pattern({error_handler,undefined_function,3}, false, [global]), erlang:trace(self(), false, [call]), Tracer ! die_please, receive -- cgit v1.2.3 From 8ab02314baa4bf6fd1e3769b7222943a7084db28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Mon, 9 Oct 2017 08:59:32 +0200 Subject: Add zlib:set_controlling_process/2 --- lib/kernel/test/zlib_SUITE.erl | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl index d17eded811..1afcd155b3 100644 --- a/lib/kernel/test/zlib_SUITE.erl +++ b/lib/kernel/test/zlib_SUITE.erl @@ -978,23 +978,37 @@ split_bin(Last,Acc) -> only_allow_owner(Config) when is_list(Config) -> Z = zlib:open(), + Owner = self(), ?m(ok, zlib:inflateInit(Z)), ?m(ok, zlib:inflateReset(Z)), {Pid, Ref} = spawn_monitor( fun() -> - ?m(?EXIT(not_on_controlling_process), zlib:inflateReset(Z)) + ?m(?EXIT(not_on_controlling_process), zlib:inflateReset(Z)), + Owner ! '$transfer_ownership', + receive + '$ownership_transferred' -> + ?m(ok, zlib:inflateReset(Z)) + after 200 -> + ct:fail("Never received transfer signal.") + end end), + ownership_transfer_check(Z, Pid, Ref). +ownership_transfer_check(Z, WorkerPid, Ref) -> receive - {'DOWN', Ref, process, Pid, _Reason} -> - ok + '$transfer_ownership' -> + zlib:set_controlling_process(Z, WorkerPid), + WorkerPid ! '$ownership_transferred', + ownership_transfer_check(Z, WorkerPid, Ref); + {'DOWN', Ref, process, WorkerPid, normal} -> + ok; + {'DOWN', Ref, process, WorkerPid, Reason} -> + ct:fail("Spawned worker crashed with reason ~p.", [Reason]) after 200 -> ct:fail("Spawned worker timed out.") - end, - - ?m(ok, zlib:inflateReset(Z)). + end. sub_heap_binaries(Config) when is_list(Config) -> Compressed = zlib:compress(<<"gurka">>), -- cgit v1.2.3 From bb0b43eae854125688f3143e53c8974cafed4ad2 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 6 Sep 2017 17:00:14 +0200 Subject: Don't allow null chars in various strings Various places that now reject null chars inside strings - Primitive file operations reject it in filenames. - Primitive environment variable operations reject it in names and values. - os:cmd() reject it in its input. Also '=' characters are rejected by primitive environment variable operations in environment variable names. Documentation has been updated to document null characters in these types of data as invalid. Currently these operations accept null chars at the end of strings, but that will change in the future. --- lib/kernel/doc/src/file.xml | 43 ++++++++++++-- lib/kernel/doc/src/os.xml | 109 ++++++++++++++++++++++++++++++++++++ lib/kernel/src/kernel.app.src | 2 +- lib/kernel/src/os.erl | 63 ++++++++++++++------- lib/kernel/test/file_name_SUITE.erl | 2 + lib/kernel/test/os_SUITE.erl | 14 ++++- 6 files changed, 204 insertions(+), 29 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index b674b3ca93..2ab35b9b05 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -41,7 +41,7 @@

    Regarding filename encoding, the Erlang VM can operate in two modes. The current mode can be queried using function - native_name_encoding/0. + native_name_encoding/0. It returns latin1 or utf8.

    In latin1 mode, the Erlang VM does not change the @@ -59,7 +59,7 @@ terminal supports UTF-8, otherwise latin1. The default can be overridden using +fnl (to force latin1 mode) or +fnu (to force utf8 mode) when starting - erts:erl.

    + erl.

    On operating systems with transparent naming, files can be inconsistently named, for example, some files are encoded in UTF-8 while @@ -81,6 +81,23 @@

    See also section Notes About Raw Filenames in the STDLIB User's Guide.

    +

    + File operations used to accept filenames containing + null characters (integer value zero). This caused + the name to be truncated and in some cases arguments + to primitive operations to be mixed up. Filenames + containing null characters inside the filename + are now rejected and will cause primitive + file operations fail. +

    +

    + Currently null characters at the end of the filename + will be accepted by primitive file operations. Such + filenames are however still documented as invalid. The + implementation will also change in the future and + reject such filenames. +

    + @@ -96,9 +113,21 @@ + +

    + See also the documentation of the + name_all() type. +

    +
    + +

    + See also the documentation of the + name_all() type. +

    +
    @@ -112,21 +141,23 @@

    If VM is in Unicode filename mode, string() and char() - are allowed to be > 255. + are allowed to be > 255. See also the documentation of the + name_all() type.

    -

    If VM is in Unicode filename mode, string() and char() +

    If VM is in Unicode filename mode, characters are allowed to be > 255. RawFilename is a filename not subject to Unicode translation, meaning that it can contain characters not conforming to the Unicode encoding expected from the file system (that is, non-UTF-8 characters although the VM is started - in Unicode filename mode). + in Unicode filename mode). Null characters (integer value zero) + are not allowed in filenames (not even at the end).

    @@ -1825,7 +1856,7 @@ f.txt: {person, "kalle", 25}.

    The functions in the module file usually treat binaries as raw filenames, that is, they are passed "as is" even when the encoding of the binary does not agree with - native_name_encoding(). + native_name_encoding(). However, this function expects binaries to be encoded according to the value returned by native_name_encoding().

    Typical error reasons are:

    diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml index 0e9add4161..0a08e2c78a 100644 --- a/lib/kernel/doc/src/os.xml +++ b/lib/kernel/doc/src/os.xml @@ -36,8 +36,99 @@ only run on a specific platform. On the other hand, with careful use, these functions can be of help in enabling a program to run on most platforms.

    + + +

    + File operations used to accept filenames containing + null characters (integer value zero). This caused + the name to be truncated and in some cases arguments + to primitive operations to be mixed up. Filenames + containing null characters inside the filename + are now rejected and will cause primitive + file operations to fail. +

    +

    + Also environment variable operations used to accept + names and values of environment variables containing + null characters (integer value zero). This caused + operations to silently produce erroneous results. + Environment variable names and values containing + null characters inside the name or value are now + rejected and will cause environment variable + operations to fail. +

    +
    + +

    + Currently null characters at the end of filenames, + environment variable names and values will be accepted + by the primitive operations. Such filenames, environment + variable names and values are however still documented as + invalid. The implementation will also change in the + future and reject such filenames, environment variable + names and values. +

    +
    + + + + +

    A string containing valid characters on the specific + OS for environment variable names using + file:native_name_encoding() + encoding. Note that specifically null characters (integer + value zero) and $= characters are not allowed. + However, note that not all invalid characters necessarily + will cause the primitiv operations to fail, but may instead + produce invalid results. +

    +
    +
    + + + +

    A string containing valid characters on the specific + OS for environment variable values using + file:native_name_encoding() + encoding. Note that specifically null characters (integer + value zero) are not allowed. However, note that not all + invalid characters necessarily will cause the primitiv + operations to fail, but may instead produce invalid results. +

    +
    +
    + + + +

    + Assuming that environment variables has been correctly + set, a strings containing valid characters on the specific + OS for environment variable names and values using + file:native_name_encoding() + encoding. The first $= characters appearing in + the string separates environment variable name (on the + left) from environment variable value (on the right). +

    +
    +
    + + + +

    All characters needs to be valid characters on the + specific OS using + file:native_name_encoding() + encoding. Note that specifically null characters (integer + value zero) are not allowed. However, note that not all + invalid characters not necessarily will cause + os:cmd/1 + to fail, but may instead produce invalid results. +

    +
    +
    +
    + @@ -49,6 +140,15 @@ result as a string. This function is a replacement of the previous function unix:cmd/1; they are equivalent on a Unix platform.

    +

    Previous implementation used to allow all characters + as long as they were integer values greater than or equal to zero. + This sometimes lead to unwanted results since null characters + (integer value zero) often are interpreted as string termination. + Current implementation still accepts null characters at the end + of Command even though the documentation + states that no null characters are allowed. This will however + be changed in the future so that no null characters at all will + be accepted.

    Examples:

    LsOut = os:cmd("ls"), % on unix platform @@ -152,6 +252,15 @@ DirOut = os:cmd("dir"), % on Win32 platform

    On Unix platforms, the environment is set using UTF-8 encoding if Unicode filename translation is in effect. On Windows, the environment is set using wide character interfaces.

    + +

    + VarName is not allowed to contain + an $= character. Previous implementations used + to just let the $= character through which + silently caused erroneous results. Current implementation + will instead throw a badarg exception. +

    +
    diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 2a88cc7e26..080b11fc4d 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -120,6 +120,6 @@ {applications, []}, {env, [{error_logger, tty}]}, {mod, {kernel, []}}, - {runtime_dependencies, ["erts-9.1", "stdlib-3.4", "sasl-3.0"]} + {runtime_dependencies, ["erts-10.0", "stdlib-3.5", "sasl-3.0"]} ] }. diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl index 0250783632..3675e923a3 100644 --- a/lib/kernel/src/os.erl +++ b/lib/kernel/src/os.erl @@ -25,6 +25,8 @@ -include("file.hrl"). +-export_type([env_var_name/0, env_var_value/0, env_var_name_value/0, command_input/0]). + %%% BIFs -export([getenv/0, getenv/1, getenv/2, getpid/0, @@ -32,21 +34,29 @@ putenv/2, set_signal/2, system_time/0, system_time/1, timestamp/0, unsetenv/1]). --spec getenv() -> [string()]. +-type env_var_name() :: nonempty_string(). + +-type env_var_value() :: string(). + +-type env_var_name_value() :: nonempty_string(). + +-type command_input() :: atom() | io_lib:chars(). + +-spec getenv() -> [env_var_name_value()]. getenv() -> erlang:nif_error(undef). -spec getenv(VarName) -> Value | false when - VarName :: string(), - Value :: string(). + VarName :: env_var_name(), + Value :: env_var_value(). getenv(_) -> erlang:nif_error(undef). -spec getenv(VarName, DefaultValue) -> Value when - VarName :: string(), - DefaultValue :: string(), - Value :: string(). + VarName :: env_var_name(), + DefaultValue :: env_var_value(), + Value :: env_var_value(). getenv(VarName, DefaultValue) -> case os:getenv(VarName) of @@ -75,8 +85,8 @@ perf_counter(Unit) -> erlang:convert_time_unit(os:perf_counter(), perf_counter, Unit). -spec putenv(VarName, Value) -> true when - VarName :: string(), - Value :: string(). + VarName :: env_var_name(), + Value :: env_var_value(). putenv(_, _) -> erlang:nif_error(undef). @@ -99,7 +109,7 @@ timestamp() -> erlang:nif_error(undef). -spec unsetenv(VarName) -> true when - VarName :: string(). + VarName :: env_var_name(). unsetenv(_) -> erlang:nif_error(undef). @@ -232,10 +242,9 @@ extensions() -> %% Executes the given command in the default shell for the operating system. -spec cmd(Command) -> string() when - Command :: atom() | io_lib:chars(). + Command :: os:command_input(). cmd(Cmd) -> - validate(Cmd), - {SpawnCmd, SpawnOpts, SpawnInput, Eot} = mk_cmd(os:type(), Cmd), + {SpawnCmd, SpawnOpts, SpawnInput, Eot} = mk_cmd(os:type(), validate(Cmd)), Port = open_port({spawn, SpawnCmd}, [binary, stderr_to_stdout, stream, in, hide | SpawnOpts]), MonRef = erlang:monitor(port, Port), @@ -255,8 +264,6 @@ mk_cmd({win32,Wtype}, Cmd) -> {Cspec,_} -> lists:concat([Cspec," /c",Cmd]) end, {Command, [], [], <<>>}; -mk_cmd(OsType,Cmd) when is_atom(Cmd) -> - mk_cmd(OsType, atom_to_list(Cmd)); mk_cmd(_,Cmd) -> %% Have to send command in like this in order to make sh commands like %% cd and ulimit available @@ -279,17 +286,33 @@ mk_cmd(_,Cmd) -> <<$\^D>>}. validate(Atom) when is_atom(Atom) -> - ok; + validate(atom_to_list(Atom)); validate(List) when is_list(List) -> - validate1(List). + case validate1(List) of + false -> + List; + true -> + %% Had zeros at end; remove them... + string:trim(List, trailing, [0]) + end. -validate1([C|Rest]) when is_integer(C) -> +validate1([0|Rest]) -> + validate2(Rest); +validate1([C|Rest]) when is_integer(C), C > 0 -> validate1(Rest); validate1([List|Rest]) when is_list(List) -> - validate1(List), - validate1(Rest); + validate1(List) or validate1(Rest); validate1([]) -> - ok. + false. + +%% Ensure that the rest is zero only... +validate2([]) -> + true; +validate2([0|Rest]) -> + validate2(Rest); +validate2([List|Rest]) when is_list(List) -> + validate2(List), + validate2(Rest). get_data(Port, MonRef, Eot, Sofar) -> receive diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl index 899102c908..f23529fec9 100644 --- a/lib/kernel/test/file_name_SUITE.erl +++ b/lib/kernel/test/file_name_SUITE.erl @@ -302,7 +302,9 @@ check_normal(Mod) -> {ok, BC} = Mod:read(FD,1024), ok = file:close(FD) end || {regular,Name,Content} <- NormalDir ], + {error, badarg} = Mod:rename("fil1\0tmp_fil2","tmp_fil1"), Mod:rename("fil1","tmp_fil1"), + {error, badarg} = Mod:read_file("tmp_fil1\0.txt"), {ok, <<"fil1">>} = Mod:read_file("tmp_fil1"), {error,enoent} = Mod:read_file("fil1"), Mod:rename("tmp_fil1","fil1"), diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl index 53a9e168ef..8056321448 100644 --- a/lib/kernel/test/os_SUITE.erl +++ b/lib/kernel/test/os_SUITE.erl @@ -22,7 +22,8 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2]). --export([space_in_cwd/1, quoting/1, cmd_unicode/1, space_in_name/1, bad_command/1, +-export([space_in_cwd/1, quoting/1, cmd_unicode/1, + null_in_command/1, space_in_name/1, bad_command/1, find_executable/1, unix_comment_in_command/1, deep_list_command/1, large_output_command/1, background_command/0, background_command/1, message_leak/1, close_stdin/0, close_stdin/1, perf_counter_api/1]). @@ -34,7 +35,8 @@ suite() -> {timetrap,{minutes,1}}]. all() -> - [space_in_cwd, quoting, cmd_unicode, space_in_name, bad_command, + [space_in_cwd, quoting, cmd_unicode, null_in_command, + space_in_name, bad_command, find_executable, unix_comment_in_command, deep_list_command, large_output_command, background_command, message_leak, close_stdin, perf_counter_api]. @@ -125,6 +127,14 @@ cmd_unicode(Config) when is_list(Config) -> [] = receive_all(), ok. +null_in_command(Config) -> + {Ok, Error} = case os:type() of + {win32,_} -> {"dir", "di\0r"}; + _ -> {"ls", "l\0s"} + end, + true = is_list(try os:cmd(Ok) catch Class0:_ -> Class0 end), + error = try os:cmd(Error) catch Class1:_ -> Class1 end, + ok. %% Test that program with a space in its name can be executed. space_in_name(Config) when is_list(Config) -> -- cgit v1.2.3 From f28539265d753bbaa473cb488e951bfe47304a01 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Thu, 12 Oct 2017 16:28:31 +0200 Subject: Revert "Merge branch 'rickard/null-char-filenames/ERL-370/OTP-14543' into maint" This reverts commit 0717a2194e863f3a78595184ccc5637697f03353, reversing changes made to 71a40658a0cef8b3e25df3a8e48a72d0563a89bf. --- lib/kernel/doc/src/file.xml | 38 ++++--------------------------------- lib/kernel/src/kernel.app.src | 2 +- lib/kernel/test/file_name_SUITE.erl | 2 -- 3 files changed, 5 insertions(+), 37 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index 593bee74fe..b674b3ca93 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -59,7 +59,7 @@ terminal supports UTF-8, otherwise latin1. The default can be overridden using +fnl (to force latin1 mode) or +fnu (to force utf8 mode) when starting - erl.

    + erts:erl.

    On operating systems with transparent naming, files can be inconsistently named, for example, some files are encoded in UTF-8 while @@ -81,22 +81,6 @@

    See also section Notes About Raw Filenames in the STDLIB User's Guide.

    -

    - File operations used to accept filenames containing - null characters (integer value zero). This caused - the name to be truncated at the first null character. - Filenames containing null characters inside the filename - are now rejected and will cause primitive - file operations fail. -

    -

    - Currently null characters at the end of the filename - will be accepted by primitive file operations. Such - filenames are however still documented as invalid. The - implementation will also change in the future and - reject such filenames. -

    - @@ -112,21 +96,9 @@ - -

    - See also the documentation of the - name_all() type. -

    -
    - -

    - See also the documentation of the - name_all() type. -

    -
    @@ -140,23 +112,21 @@

    If VM is in Unicode filename mode, string() and char() - are allowed to be > 255. See also the documentation of the - name_all() type. + are allowed to be > 255.

    -

    If VM is in Unicode filename mode, characters +

    If VM is in Unicode filename mode, string() and char() are allowed to be > 255. RawFilename is a filename not subject to Unicode translation, meaning that it can contain characters not conforming to the Unicode encoding expected from the file system (that is, non-UTF-8 characters although the VM is started - in Unicode filename mode). Null characters (integer value zero) - are not allowed in filenames (not even at the end). + in Unicode filename mode).

    diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index b5e5f8eb73..2a88cc7e26 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -120,6 +120,6 @@ {applications, []}, {env, [{error_logger, tty}]}, {mod, {kernel, []}}, - {runtime_dependencies, ["erts-9.1.1", "stdlib-3.4.3", "sasl-3.0"]} + {runtime_dependencies, ["erts-9.1", "stdlib-3.4", "sasl-3.0"]} ] }. diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl index f23529fec9..899102c908 100644 --- a/lib/kernel/test/file_name_SUITE.erl +++ b/lib/kernel/test/file_name_SUITE.erl @@ -302,9 +302,7 @@ check_normal(Mod) -> {ok, BC} = Mod:read(FD,1024), ok = file:close(FD) end || {regular,Name,Content} <- NormalDir ], - {error, badarg} = Mod:rename("fil1\0tmp_fil2","tmp_fil1"), Mod:rename("fil1","tmp_fil1"), - {error, badarg} = Mod:read_file("tmp_fil1\0.txt"), {ok, <<"fil1">>} = Mod:read_file("tmp_fil1"), {error,enoent} = Mod:read_file("fil1"), Mod:rename("tmp_fil1","fil1"), -- cgit v1.2.3 From 1d8f12c010f9e31a986f80caae6c7ab749c2a3bb Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 12 Oct 2017 16:55:34 +0200 Subject: kernel: Fix and improve send_tick logic Problem 1: Tick was not sent to hidden when pending sends. Don't hidden need the tick in order to reply? Problem 2: Written packets (#tick.write) was increased even when tick was suppressed due to pending sends. Code rewritten to handle outgoing and incoming separate. --- lib/kernel/src/dist_util.erl | 67 ++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 30 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index 08bd5946cd..3f35398b1b 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -587,7 +587,7 @@ con_loop({Kernel, Node, Socket, Type, DHandle, MFTick, MFGetstat, {Kernel, aux_tick} -> case getstat(DHandle, Socket, MFGetstat) of {ok, _, _, PendWrite} -> - send_tick(Socket, PendWrite, MFTick); + send_aux_tick(Type, Socket, PendWrite, MFTick); _ -> ignore_it end, @@ -840,49 +840,56 @@ send_status(#hs_data{socket = Socket, other_node = Node, %% The detection time interval is thus, by default, 45s < DT < 75s -%% A HIDDEN node is always (if not a pending write) ticked if -%% we haven't read anything as a hidden node only ticks when it receives -%% a TICK !! +%% A HIDDEN node is always ticked if we haven't read anything +%% as a (primitive) hidden node only ticks when it receives a TICK !! send_tick(DHandle, Socket, Tick, Type, MFTick, MFGetstat) -> #tick{tick = T0, read = Read, write = Write, - ticked = Ticked} = Tick, + ticked = Ticked0} = Tick, T = T0 + 1, T1 = T rem 4, case getstat(DHandle, Socket, MFGetstat) of - {ok, Read, _, _} when Ticked =:= T -> + {ok, Read, _, _} when Ticked0 =:= T -> {error, not_responding}; - {ok, Read, W, Pend} when Type =:= hidden -> - send_tick(Socket, Pend, MFTick), - {ok, Tick#tick{write = W + 1, - tick = T1}}; - {ok, Read, Write, Pend} -> - send_tick(Socket, Pend, MFTick), - {ok, Tick#tick{write = Write + 1, - tick = T1}}; - {ok, R, Write, Pend} -> - send_tick(Socket, Pend, MFTick), - {ok, Tick#tick{write = Write + 1, - read = R, - tick = T1, - ticked = T}}; - {ok, Read, W, _} -> - {ok, Tick#tick{write = W, - tick = T1}}; - {ok, R, W, _} -> - {ok, Tick#tick{write = W, - read = R, - tick = T1, - ticked = T}}; + + {ok, R, W1, Pend} -> + RDiff = R - Read, + W2 = case need_to_tick(Type, RDiff, W1-Write, Pend) of + true -> + MFTick(Socket), + W1 + 1; + false -> + W1 + end, + + Ticked1 = case RDiff of + 0 -> Ticked0; + _ -> T + end, + + {ok, Tick#tick{write = W2, + tick = T1, + read = R, + ticked = Ticked1}}; + Error -> Error end. -send_tick(_, Pend, _) when Pend /= false, Pend /= 0 -> +need_to_tick(_, _, 0, 0) -> % nothing written and empty send queue + true; +need_to_tick(_, _, 0, false) -> % nothing written and empty send queue + true; +need_to_tick(hidden, 0, _, _) -> % nothing read from hidden + true; +need_to_tick(_, _, _, _) -> + false. + +send_aux_tick(normal, _, Pend, _) when Pend /= false, Pend /= 0 -> ok; %% Dont send tick if pending write. -send_tick(Socket, _Pend, MFTick) -> +send_aux_tick(_Type, Socket, _Pend, MFTick) -> MFTick(Socket). %% ------------------------------------------------------------ -- cgit v1.2.3 From 6a6019c287a54b71d4d0bcf0a72d244d89de90d1 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 16 Oct 2017 18:01:20 +0200 Subject: kernel: Fix erl_distribution_wb_SUITE to use binary_to_term/2 with 'used' option and not rely on term_to_binary generating the same format. --- lib/kernel/test/erl_distribution_wb_SUITE.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/erl_distribution_wb_SUITE.erl b/lib/kernel/test/erl_distribution_wb_SUITE.erl index 03aaee56b7..258ed4f88c 100644 --- a/lib/kernel/test/erl_distribution_wb_SUITE.erl +++ b/lib/kernel/test/erl_distribution_wb_SUITE.erl @@ -65,6 +65,7 @@ ?DFLAG_EXTENDED_PIDS_PORTS bor ?DFLAG_UTF8_ATOMS)). +-define(PASS_THROUGH, $p). -define(shutdown(X), exit(X)). -define(int16(X), [((X) bsr 8) band 16#ff, (X) band 16#ff]). @@ -676,10 +677,9 @@ recv_message(Socket) -> case gen_tcp:recv(Socket, 0) of {ok,Data} -> B0 = list_to_binary(Data), - {_,B1} = erlang:split_binary(B0,1), - Header = binary_to_term(B1), - Siz = byte_size(term_to_binary(Header)), - {_,B2} = erlang:split_binary(B1,Siz), + <> = B0, + {Header,Siz} = binary_to_term(B1,[used]), + <<_:Siz/binary,B2/binary>> = B1, Message = case (catch binary_to_term(B2)) of {'EXIT', _} -> could_not_digest_message; -- cgit v1.2.3 From 56587b4c81c337d198a414334c3f6429f12cbf64 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 5 Oct 2017 17:19:53 +0200 Subject: kernel: Fix gen_tcp_misc indentation --- lib/kernel/test/gen_tcp_misc_SUITE.erl | 62 ++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 29 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index 331864b5de..e47023d201 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -1572,52 +1572,56 @@ fill_sendq(Config) when is_list(Config) -> Master = self(), Server = spawn_link(fun () -> - {ok,L} = gen_tcp:listen - (0, [{active,false},binary, - {reuseaddr,true},{packet,0}]), + {ok,L} = gen_tcp:listen(0, [{active,false},binary, + {reuseaddr,true},{packet,0}]), {ok,Port} = inet:port(L), Master ! {self(),client, fill_sendq_client(Port, Master)}, fill_sendq_srv(L, Master) end), io:format("~p Server~n", [Server]), - receive {Server,client,Client} -> - io:format("~p Client~n", [Client]), - receive {Server,reader,Reader} -> - io:format("~p Reader~n", [Reader]), - fill_sendq_loop(Server, Client, Reader) + receive + {Server,client,Client} -> + io:format("~p Client~n", [Client]), + receive + {Server,reader,Reader} -> + io:format("~p Reader~n", [Reader]), + fill_sendq_loop(Server, Client, Reader) end end. fill_sendq_loop(Server, Client, Reader) -> %% Master %% - receive {Server,send} -> + receive + {Server,send} -> fill_sendq_loop(Server, Client, Reader) after 2000 -> %% Send queue full, sender blocked -> close client. io:format("Send timeout, closing Client...~n", []), Client ! {self(),close}, - receive {Server,[{error,closed}]} -> - io:format("Got server closed.~n"), - receive {Reader,[{error,closed}]} -> - io:format - ("Got reader closed.~n"), - ok - after 3000 -> - ct:fail({timeout,{closed,reader}}) - end; - {Reader,[{error,closed}]} -> - io:format("Got reader closed.~n"), - receive {Server,[{error,closed}]} -> - io:format("Got server closed~n"), - ok - after 3000 -> - ct:fail({timeout,{closed,server}}) - end - after 3000 -> - ct:fail({timeout,{closed,[server,reader]}}) - end + receive + {Server,[{error,closed}]} -> + io:format("Got server closed.~n"), + receive + {Reader,[{error,closed}]} -> + io:format("Got reader closed.~n"), + ok + after 3000 -> + ct:fail({timeout,{closed,reader}}) + end; + {Reader,[{error,closed}]} -> + io:format("Got reader closed.~n"), + receive + {Server,[{error,closed}]} -> + io:format("Got server closed~n"), + ok + after 3000 -> + ct:fail({timeout,{closed,server}}) + end + after 3000 -> + ct:fail({timeout,{closed,[server,reader]}}) + end end. fill_sendq_srv(L, Master) -> -- cgit v1.2.3 From c0fb141fddbe1f514d97e0891f260f6b6f77fe63 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 23 Oct 2017 12:35:55 +0200 Subject: Update kernel appup for OTP-20.2 --- lib/kernel/src/kernel.appup.src | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src index f1ef70a373..4ee497bbbd 100644 --- a/lib/kernel/src/kernel.appup.src +++ b/lib/kernel/src/kernel.appup.src @@ -18,7 +18,9 @@ %% %CopyrightEnd% {"%VSN%", %% Up from - max one major revision back - [{<<"5\\.[0-3](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-19.*, OTP-20.0 + [{<<"5\\.[0-3](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*, OTP-20.0 + {<<"5\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-20.1+ %% Down to - max one major revision back - [{<<"5\\.[0-3](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-19.*, OTP-20.0 + [{<<"5\\.[0-3](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*, OTP-20.0 + {<<"5\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-20.1+ }. -- cgit v1.2.3 From c5b2a6161381951d273b124ff37a25597a27d2ef Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 23 Oct 2017 10:15:01 +0200 Subject: kernel: Clarify gen_tcp:listen documentation --- lib/kernel/doc/src/gen_tcp.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index 070782e1f3..e6104b0c76 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -51,6 +51,7 @@ server() -> {ok, Sock} = gen_tcp:accept(LSock), {ok, Bin} = do_recv(Sock, []), ok = gen_tcp:close(Sock), + ok = gen_tcp:close(LSock), Bin. do_recv(Sock, Bs) -> @@ -309,9 +310,9 @@ do_recv(Sock, Bs) -> inet:setopts/2.

    -

    The returned socket ListenSocket can only be - used in calls to - accept/1,2.

    +

    The returned socket ListenSocket should be used + in calls to accept/1,2 to + accept incoming connection requests.

    The default values for options specified to listen can be affected by the Kernel configuration parameter -- cgit v1.2.3 From cc8dade5326d130f1c704f7a47a4d0ce88af370e Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 23 Oct 2017 13:10:21 +0200 Subject: Refactor xmllint check and make it fail on failure This commit also adds a check to see that all files that are part of an xi:include also have part of XML_FILES and vice versa. It also fixes any applications where this was not true. --- lib/kernel/doc/src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile index c9d23ac4c4..0759f362d4 100644 --- a/lib/kernel/doc/src/Makefile +++ b/lib/kernel/doc/src/Makefile @@ -71,7 +71,7 @@ XML_REF4_FILES = app.xml config.xml XML_REF6_FILES = kernel_app.xml XML_PART_FILES = -XML_CHAPTER_FILES = notes.xml notes_history.xml +XML_CHAPTER_FILES = notes.xml BOOK_FILES = book.xml -- cgit v1.2.3 From 9b1f2439845e5627a00fb816244af06d77c2d70a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Wed, 1 Nov 2017 16:53:51 +0100 Subject: Only apply EOS behaviors if there's pending data --- lib/kernel/test/zlib_SUITE.erl | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl index 1afcd155b3..b20d64ec6a 100644 --- a/lib/kernel/test/zlib_SUITE.erl +++ b/lib/kernel/test/zlib_SUITE.erl @@ -652,6 +652,11 @@ api_g_un_zip(Config) when is_list(Config) -> Concatenated = <>, ?m(Concatenated, zlib:gunzip([Comp, Comp])), + %% Don't explode if the uncompressed size is a perfect multiple of the + %% internal inflate chunk size. + ChunkSizedData = <<0:16384/unit:8>>, + ?m(ChunkSizedData, zlib:gunzip(zlib:gzip(ChunkSizedData))), + %% Bad CRC; bad length. BadCrc = bad_crc_data(), ?m(?EXIT(data_error),(catch zlib:gunzip(BadCrc))), -- cgit v1.2.3 From 8cfe9496873fa36eccda939e56bf7a922e945ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Tue, 7 Nov 2017 17:24:55 +0100 Subject: Fix deflateParams on zlib 1.2.11 1.2.11 started bailing when avail_out==0 regardless of whether there's anything to flush or not, and there's no point in adapting the old method since it was vulnerable to bugs in other zlib versions which updated the deflate parameters even on failure. The api_deflateParams test has been expanded accordingly, and two white-box cases in zip_usage has been updated to make fewer assumptions about the output; the validity of the compressed data is what matters, not whether it's exactly the same as the test vector. --- lib/kernel/test/zlib_SUITE.erl | 46 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 6 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl index 1afcd155b3..6f40c74ee8 100644 --- a/lib/kernel/test/zlib_SUITE.erl +++ b/lib/kernel/test/zlib_SUITE.erl @@ -213,12 +213,46 @@ api_deflateReset(Config) when is_list(Config) -> %% Test deflateParams. api_deflateParams(Config) when is_list(Config) -> + Levels = [none, default, best_speed, best_compression] ++ lists:seq(0, 9), + Strategies = [filtered, huffman_only, rle, default], + Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1, default)), - ?m(L when is_list(L), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)), - ?m(ok, zlib:deflateParams(Z1, best_compression, huffman_only)), - ?m(L when is_list(L), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, sync)), - ?m(ok, zlib:close(Z1)). + + ApiTest = + fun(Level, Strategy) -> + ?m(ok, zlib:deflateParams(Z1, Level, Strategy)), + ?m(ok, zlib:deflateReset(Z1)) + end, + + [ ApiTest(Level, Strategy) || Level <- Levels, Strategy <- Strategies ], + + ?m(ok, zlib:close(Z1)), + + FlushTest = + fun FlushTest(Size, Level, Strategy) -> + Z = zlib:open(), + ok = zlib:deflateInit(Z, default), + Data = gen_determ_rand_bytes(Size), + case zlib:deflate(Z, Data, none) of + [<<120, 156>>] -> + %% All data is present in the internal zlib state, and will + %% be flushed on deflateParams. + + ok = zlib:deflateParams(Z, Level, Strategy), + Compressed = [<<120, 156>>, zlib:deflate(Z, <<>>, finish)], + Data = zlib:uncompress(Compressed), + zlib:close(Z), + + FlushTest(Size + (1 bsl 10), Level, Strategy); + _Other -> + ok + end + end, + + [ FlushTest(1, Level, Strategy) || Level <- Levels, Strategy <- Strategies ], + + ok. %% Test deflate. api_deflate(Config) when is_list(Config) -> @@ -762,13 +796,13 @@ zip_usage({run,ZIP,ORIG}) -> ?m(ok, zlib:deflateInit(Z, default, deflated, -15, 8, default)), C2 = zlib:deflate(Z, ORIG, finish), - ?m(true, C1 == list_to_binary(C2)), + ?m(ORIG, zlib:unzip(C2)), ?m(ok, zlib:deflateEnd(Z)), ?m(ok, zlib:deflateInit(Z, none, deflated, -15, 8, filtered)), ?m(ok, zlib:deflateParams(Z, default, default)), C3 = zlib:deflate(Z, ORIG, finish), - ?m(true, C1 == list_to_binary(C3)), + ?m(ORIG, zlib:unzip(C3)), ?m(ok, zlib:deflateEnd(Z)), ok = zlib:close(Z), -- cgit v1.2.3 From e96969083dbfebfaaa600d2ba6c2cf6d5a690cbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Tue, 7 Nov 2017 17:31:18 +0100 Subject: Avoid WindowBits=8 as per the manual The docs for zlib:deflateInit/6 explicitly mention that 8 is broken and should not be used unless we know for certain that our zlib version supports it. --- lib/kernel/test/zlib_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl index 6f40c74ee8..6d4e21b8cf 100644 --- a/lib/kernel/test/zlib_SUITE.erl +++ b/lib/kernel/test/zlib_SUITE.erl @@ -166,7 +166,7 @@ api_deflateInit(Config) when is_list(Config) -> ?m(ok, zlib:deflateInit(Z12,default,deflated,-Wbits,8,default)), ?m(ok,zlib:close(Z11)), ?m(ok,zlib:close(Z12)) - end, lists:seq(8, 15)), + end, lists:seq(9, 15)), lists:foreach(fun(MemLevel) -> Z = zlib:open(), -- cgit v1.2.3 From 92d383cac5ddcb3856432e653ef84b0a3f84cc0a Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 22 Aug 2017 16:48:33 +0200 Subject: erts: Make DFLAGS_NEW_FUN_TAGS mandatory and remove ugly encoding fallback as {fun, ...} DFLAGS_NEW_FUN_TAGS has been supported by vm/erl_interface/jinterface since R13 or even older. Renamed test case obsolete_funs to term2bin_tuple_fallbacks and removed test for {fun,...} fallback and added missing test for bitstring fallback {Binary, Bits}. --- lib/kernel/src/dist_util.erl | 3 ++- lib/kernel/test/erl_distribution_wb_SUITE.erl | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index 08bd5946cd..fb9f7fd7eb 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -250,7 +250,8 @@ check_dflags(#hs_data{other_node = Node, require_flags = RequiredFlags} = HSData) -> Mandatory = ((?DFLAG_EXTENDED_REFERENCES bor ?DFLAG_EXTENDED_PIDS_PORTS - bor ?DFLAG_UTF8_ATOMS) + bor ?DFLAG_UTF8_ATOMS + bor ?DFLAG_NEW_FUN_TAGS) bor RequiredFlags), Missing = check_mandatory(0, ?DFLAGS_ALL, Mandatory, OtherFlags, []), diff --git a/lib/kernel/test/erl_distribution_wb_SUITE.erl b/lib/kernel/test/erl_distribution_wb_SUITE.erl index 258ed4f88c..1145d30e5e 100644 --- a/lib/kernel/test/erl_distribution_wb_SUITE.erl +++ b/lib/kernel/test/erl_distribution_wb_SUITE.erl @@ -61,9 +61,11 @@ %% From R9 and forward extended references is compulsory %% From R10 and forward extended pids and ports are compulsory %% From R20 and forward UTF8 atoms are compulsory +%% From R21 and forward NEW_FUN_TAGS is compulsory (no more tuple fallback {fun, ...}) -define(COMPULSORY_DFLAGS, (?DFLAG_EXTENDED_REFERENCES bor ?DFLAG_EXTENDED_PIDS_PORTS bor - ?DFLAG_UTF8_ATOMS)). + ?DFLAG_UTF8_ATOMS bor + ?DFLAG_NEW_FUN_TAGS)). -define(PASS_THROUGH, $p). @@ -682,7 +684,7 @@ recv_message(Socket) -> <<_:Siz/binary,B2/binary>> = B1, Message = case (catch binary_to_term(B2)) of {'EXIT', _} -> - could_not_digest_message; + {could_not_digest_message,B2}; Other -> Other end, -- cgit v1.2.3 From c8a6219d1f6c678f8421b1062ff633487d52bf04 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 10 Jul 2017 19:47:51 +0200 Subject: kernel: Move auto connect into net_kernel server as a preparation for async auto-connect requests. --- lib/kernel/src/net_kernel.erl | 174 ++++++++++++++++++++++-------------------- 1 file changed, 90 insertions(+), 84 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index f36b4f1e6a..c68036a291 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -247,14 +247,14 @@ ticktime_res(A) when is_atom(A) -> A. %% Called though BIF's -connect(Node) -> do_connect(Node, normal, false). +connect(Node) -> auto_connect(Node, normal, false). %%% Long timeout if blocked (== barred), only affects nodes with %%% {dist_auto_connect, once} set. -passive_cnct(Node) -> do_connect(Node, normal, true). +passive_cnct(Node) -> auto_connect(Node, normal, true). disconnect(Node) -> request({disconnect, Node}). %% connect but not seen -hidden_connect(Node) -> do_connect(Node, hidden, false). +hidden_connect(Node) -> auto_connect(Node, hidden, false). %% Should this node publish itself on Node? publish_on_node(Node) when is_atom(Node) -> @@ -272,67 +272,35 @@ connect_node(Node) when is_atom(Node) -> hidden_connect_node(Node) when is_atom(Node) -> request({connect, hidden, Node}). -do_connect(Node, Type, WaitForBarred) -> %% Type = normal | hidden - case catch ets:lookup(sys_dist, Node) of - {'EXIT', _} -> - ?connect_failure(Node,{table_missing, sys_dist}), - false; - [#barred_connection{}] -> - case WaitForBarred of - false -> - false; - true -> - Pid = spawn(?MODULE,passive_connect_monitor,[self(),Node]), - receive - {Pid, true} -> - %%io:format("Net Kernel: barred connection (~p) " - %% "connected from other end.~n",[Node]), - true; - {Pid, false} -> - ?connect_failure(Node,{barred_connection, - ets:lookup(sys_dist, Node)}), - %%io:format("Net Kernel: barred connection (~p) " - %% "- failure.~n",[Node]), - false - end - end; - Else -> - case application:get_env(kernel, dist_auto_connect) of - {ok, never} -> - ?connect_failure(Node,{dist_auto_connect,never}), - false; - % This might happen due to connection close - % not beeing propagated to user space yet. - % Save the day by just not connecting... - {ok, once} when Else =/= [], - (hd(Else))#connection.state =:= up -> - ?connect_failure(Node,{barred_connection, - ets:lookup(sys_dist, Node)}), - false; - _ -> - request({connect, Type, Node}) - end +auto_connect(Node, Type, WaitForBarred) -> %% Type = normal | hidden + case request({auto_connect, Type, Node, WaitForBarred}) of + ignored -> false; + Other -> Other end. -passive_connect_monitor(Parent, Node) -> +passive_connect_monitor(From, Node) -> ok = monitor_nodes(true,[{node_type,all}]), - case lists:member(Node,nodes([connected])) of - true -> - ok = monitor_nodes(false,[{node_type,all}]), - Parent ! {self(),true}; - _ -> - Ref = make_ref(), - Tref = erlang:send_after(connecttime(),self(),Ref), - receive - Ref -> - ok = monitor_nodes(false,[{node_type,all}]), - Parent ! {self(), false}; - {nodeup,Node,_} -> - ok = monitor_nodes(false,[{node_type,all}]), - _ = erlang:cancel_timer(Tref), - Parent ! {self(),true} - end - end. + Reply = case lists:member(Node,nodes([connected])) of + true -> + io:format("~p: passive_connect_monitor ~p\n", [self(), ?LINE]), + true; + _ -> + receive + {nodeup,Node,_} -> + io:format("~p: passive_connect_monitor ~p\n", [self(), ?LINE]), + true + after connecttime() -> + io:format("~p: passive_connect_monitor ~p\n", [self(), ?LINE]), + false + end + end, + ok = monitor_nodes(false,[{node_type,all}]), + io:format("~p: passive_connect_monitor ~p\n", [self(), ?LINE]), + {Pid, Tag} = From, + io:format("~p: passive_connect_monitor ~p\n", [self(), ?LINE]), + erlang:send(Pid, {Tag, Reply}), + io:format("~p: passive_connect_monitor ~p\n", [self(), ?LINE]). + %% If the net_kernel isn't running we ignore all requests to the %% kernel, thus basically accepting them :-) @@ -394,40 +362,78 @@ init({Name, LongOrShortNames, TickT, CleanHalt}) -> end. +handle_connect([Conn], _, _, From, State) when Conn#connection.state =:= up -> + async_reply({reply, true, State}, From); +handle_connect([Conn], _, _, From, State) when Conn#connection.state =:= pending; + Conn#connection.state =:= up_pending -> + Waiting = Conn#connection.waiting, + ets:insert(sys_dist, Conn#connection{waiting = [From|Waiting]}), + {noreply, State}; +handle_connect(_, Type, Node, From , State) -> + case setup(Node,Type,From,State) of + {ok, SetupPid} -> + Owners = [{SetupPid, Node} | State#state.conn_owners], + {noreply,State#state{conn_owners=Owners}}; + _Error -> + ?connect_failure(Node, {setup_call, failed, _Error}), + async_reply({reply, false, State}, From) + end. + %% ------------------------------------------------------------ %% handle_call. %% ------------------------------------------------------------ %% -%% Set up a connection to Node. +%% Auto-connect to Node. %% The response is delayed until the connection is up and %% running. %% -handle_call({connect, _, Node}, From, State) when Node =:= node() -> +handle_call({auto_connect, _, Node, _}, From, State) when Node =:= node() -> + async_reply({reply, true, State}, From); +handle_call({auto_connect, Type, Node, WaitForBarred}, From, State) -> + verbose({auto_connect, Type, Node, WaitForBarred}, 1, State), + + ConnLookup = ets:lookup(sys_dist, Node), + + case ConnLookup of + [#barred_connection{}] -> + case WaitForBarred of + false -> + async_reply({reply, false, State}, From); + true -> + spawn(?MODULE,passive_connect_monitor,[From,Node]), + {noreply, State} + end; + + _ -> + case application:get_env(kernel, dist_auto_connect) of + {ok, never} -> + ?connect_failure(Node,{dist_auto_connect,never}), + async_reply({reply, false, State}, From); + + %% This might happen due to connection close + %% not beeing propagated to user space yet. + %% Save the day by just not connecting... + {ok, once} when ConnLookup =/= [], + (hd(ConnLookup))#connection.state =:= up -> + ?connect_failure(Node,{barred_connection, + ets:lookup(sys_dist, Node)}), + async_reply({reply, false, State}, From); + _ -> + handle_connect(ConnLookup, Type, Node, From, State) + end + end; + +%% +%% Explicit connect +%% The response is delayed until the connection is up and running. +%% +handle_call({connect, _, Node, _, _}, From, State) when Node =:= node() -> async_reply({reply, true, State}, From); handle_call({connect, Type, Node}, From, State) -> verbose({connect, Type, Node}, 1, State), - case ets:lookup(sys_dist, Node) of - [Conn] when Conn#connection.state =:= up -> - async_reply({reply, true, State}, From); - [Conn] when Conn#connection.state =:= pending -> - Waiting = Conn#connection.waiting, - ets:insert(sys_dist, Conn#connection{waiting = [From|Waiting]}), - {noreply, State}; - [Conn] when Conn#connection.state =:= up_pending -> - Waiting = Conn#connection.waiting, - ets:insert(sys_dist, Conn#connection{waiting = [From|Waiting]}), - {noreply, State}; - _ -> - case setup(Node,Type,From,State) of - {ok, SetupPid} -> - Owners = [{SetupPid, Node} | State#state.conn_owners], - {noreply,State#state{conn_owners=Owners}}; - _Error -> - ?connect_failure(Node, {setup_call, failed, _Error}), - async_reply({reply, false, State}, From) - end - end; + ConnLookup = ets:lookup(sys_dist, Node), + handle_connect(ConnLookup, Type, Node, From, State); %% %% Close the connection to Node. -- cgit v1.2.3 From f89fb92384280e2939414287a2ecb8f86a199318 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 14 Jul 2017 19:34:54 +0200 Subject: erts: Introduce asynchronous auto-connect --- lib/kernel/src/net_kernel.erl | 210 ++++++++++++++++++++++++++++++++---------- 1 file changed, 159 insertions(+), 51 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index c68036a291..f929e4bf11 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -122,6 +122,7 @@ -record(connection, { node, %% remote node name + conn_id, %% Connection identity state, %% pending | up | up_pending owner, %% owner pid pending_owner, %% possible new owner @@ -362,54 +363,33 @@ init({Name, LongOrShortNames, TickT, CleanHalt}) -> end. -handle_connect([Conn], _, _, From, State) when Conn#connection.state =:= up -> - async_reply({reply, true, State}, From); -handle_connect([Conn], _, _, From, State) when Conn#connection.state =:= pending; - Conn#connection.state =:= up_pending -> - Waiting = Conn#connection.waiting, - ets:insert(sys_dist, Conn#connection{waiting = [From|Waiting]}), - {noreply, State}; -handle_connect(_, Type, Node, From , State) -> - case setup(Node,Type,From,State) of - {ok, SetupPid} -> - Owners = [{SetupPid, Node} | State#state.conn_owners], - {noreply,State#state{conn_owners=Owners}}; - _Error -> - ?connect_failure(Node, {setup_call, failed, _Error}), - async_reply({reply, false, State}, From) - end. - -%% ------------------------------------------------------------ -%% handle_call. -%% ------------------------------------------------------------ - -%% -%% Auto-connect to Node. -%% The response is delayed until the connection is up and -%% running. -%% -handle_call({auto_connect, _, Node, _}, From, State) when Node =:= node() -> - async_reply({reply, true, State}, From); -handle_call({auto_connect, Type, Node, WaitForBarred}, From, State) -> - verbose({auto_connect, Type, Node, WaitForBarred}, 1, State), - +handle_auto_connect(Type, Node, ConnId, WaitForBarred, From, State) -> ConnLookup = ets:lookup(sys_dist, Node), case ConnLookup of [#barred_connection{}] -> case WaitForBarred of false -> - async_reply({reply, false, State}, From); + {reply, false, State}; true -> spawn(?MODULE,passive_connect_monitor,[From,Node]), {noreply, State} end; + [#connection{conn_id=ConnId, state = up}] -> + {reply, true, State}; + [#connection{conn_id=ConnId, waiting=Waiting}=Conn] -> + case From of + noreply -> ok; + _ -> ets:insert(sys_dist, Conn#connection{waiting = [From|Waiting]}) + end, + {noreply, State}; + _ -> case application:get_env(kernel, dist_auto_connect) of {ok, never} -> ?connect_failure(Node,{dist_auto_connect,never}), - async_reply({reply, false, State}, From); + {reply, false, State}; %% This might happen due to connection close %% not beeing propagated to user space yet. @@ -418,11 +398,78 @@ handle_call({auto_connect, Type, Node, WaitForBarred}, From, State) -> (hd(ConnLookup))#connection.state =:= up -> ?connect_failure(Node,{barred_connection, ets:lookup(sys_dist, Node)}), - async_reply({reply, false, State}, From); + {reply, false, State}; _ -> - handle_connect(ConnLookup, Type, Node, From, State) + case setup(ConnLookup, Node,ConnId,Type,From,State) of + {ok, SetupPid} -> + Owners = [{SetupPid, Node} | State#state.conn_owners], + {noreply,State#state{conn_owners=Owners}}; + _Error -> + ?connect_failure(Node, {setup_call, failed, _Error}), + {reply, false, State} + end end - end; + end. + + +handle_connect([#connection{conn_id = ConnId, state = up}], _, _, ConnId, _From, State) -> + {reply, true, State}; +handle_connect([#connection{conn_id = ConnId}=Conn], _, _, ConnId, From, State) + when Conn#connection.state =:= pending; + Conn#connection.state =:= up_pending -> + Waiting = Conn#connection.waiting, + ets:insert(sys_dist, Conn#connection{waiting = [From|Waiting]}), + {noreply, State}; +handle_connect([#barred_connection{}], Type, Node, ConnId, From , State) -> + %% Barred connection only affects auto_connect, ignore it. + handle_connect([], Type, Node, ConnId, From , State); +handle_connect(ConnLookup, Type, Node, ConnId, From , State) -> + case setup(ConnLookup, Node,ConnId,Type,From,State) of + {ok, SetupPid} -> + Owners = [{SetupPid, Node} | State#state.conn_owners], + {noreply,State#state{conn_owners=Owners}}; + _Error -> + ?connect_failure(Node, {setup_call, failed, _Error}), + {reply, false, State} + end. + +-define(ERTS_DIST_CON_ID_MASK, 16#ffffff). % also in external.h + +verify_new_conn_id([], ConnId) + when (ConnId band (bnot ?ERTS_DIST_CON_ID_MASK)) =:= 0 -> + true; +verify_new_conn_id([#connection{conn_id = Old}], New) + when New =:= ((Old+1) band ?ERTS_DIST_CON_ID_MASK) -> + true; +verify_new_conn_id(_, _) -> + false. + + + +%% ------------------------------------------------------------ +%% handle_call. +%% ------------------------------------------------------------ + +%% +%% Auto-connect to Node. +%% The response is delayed until the connection is up and running. +%% +handle_call({auto_connect, _, Node, _}, From, State) when Node =:= node() -> + async_reply({reply, true, State}, From); +handle_call({auto_connect, Type, Node, WaitForBarred}, From, State) -> + verbose({auto_connect, Type, Node, WaitForBarred}, 1, State), + + R = case (catch erlang:new_connection_id(Node)) of + ConnId when is_integer(ConnId) -> + handle_auto_connect(Type, Node, ConnId, WaitForBarred, From, State); + + _Error -> + error_logger:error_msg("~n** Cannot get connection id for node ~w~n", + [Node]), + {reply, false, State} + end, + + return_call(R, From); %% %% Explicit connect @@ -433,7 +480,17 @@ handle_call({connect, _, Node, _, _}, From, State) when Node =:= node() -> handle_call({connect, Type, Node}, From, State) -> verbose({connect, Type, Node}, 1, State), ConnLookup = ets:lookup(sys_dist, Node), - handle_connect(ConnLookup, Type, Node, From, State); + R = case (catch erlang:new_connection_id(Node)) of + ConnId when is_integer(ConnId) -> + handle_connect(ConnLookup, Type, Node, ConnId, From, State); + + _Error -> + error_logger:error_msg("~n** Cannot get connection id for node ~w~n", + [Node]), + {reply, false, State} + end, + return_call(R, From); + %% %% Close the connection to Node. @@ -639,6 +696,25 @@ terminate(_Reason, State) -> %% handle_info. %% ------------------------------------------------------------ +%% +%% Asynchronous auto connect request +%% +handle_info({auto_connect,Node,ConnId}, State) -> + verbose({auto_connect, Node, ConnId}, 1, State), + NewState = + case handle_auto_connect(normal, Node, ConnId, false, noreply, State) of + {noreply, S} -> %% Pending connection + S; + + {reply, true, S} -> %% Already connected + S; + + {reply, false, S} -> %% Connection refused + erlang:abort_connection_id(Node, ConnId), + S + end, + {noreply, NewState}; + %% %% accept a new connection. %% @@ -719,7 +795,12 @@ handle_info({AcceptPid, {accept_pending,MyNode,Node,Address,Type}}, State) -> AcceptPid ! {self(), {accept_pending, already_pending}}, {noreply, State}; _ -> + ConnId = case (catch erlang:new_connection_id(Node)) of + CI when is_integer(CI) -> CI + %% SVERK What to do? + end, ets:insert(sys_dist, #connection{node = Node, + conn_id = ConnId, state = pending, owner = AcceptPid, address = Address, @@ -912,6 +993,7 @@ pending_nodedown(Conn, Node, Type, State) -> % Don't bar connections that have never been alive %mark_sys_dist_nodedown(Node), % - instead just delete the node: + erlang:abort_connection_id(Node, Conn#connection.conn_id), ets:delete(sys_dist, Node), reply_waiting(Node,Conn#connection.waiting, false), case Type of @@ -934,15 +1016,16 @@ up_pending_nodedown(Conn, Node, _Reason, _Type, State) -> State#state{conn_owners = [{AcceptPid,Node}|Owners], pend_owners = Pend}. -up_nodedown(_Conn, Node, _Reason, Type, State) -> - mark_sys_dist_nodedown(Node), +up_nodedown(Conn, Node, _Reason, Type, State) -> + mark_sys_dist_nodedown(Conn, Node), case Type of normal -> ?nodedown(Node, State); _ -> ok end, State. -mark_sys_dist_nodedown(Node) -> +mark_sys_dist_nodedown(Conn, Node) -> + erlang:abort_connection_id(Node, Conn#connection.conn_id), case application:get_env(kernel, dist_auto_connect) of {ok, once} -> ets:insert(sys_dist, #barred_connection{node = Node}); @@ -1185,15 +1268,8 @@ spawn_func(_,{From,Tag},M,F,A,Gleader) -> %% Set up connection to a new node. %% ----------------------------------------------------------- -setup(Node,Type,From,State) -> - Allowed = State#state.allowed, - case lists:member(Node, Allowed) of - false when Allowed =/= [] -> - error_msg("** Connection attempt with " - "disallowed node ~w ** ~n", [Node]), - {error, bad_node}; - _ -> - case select_mod(Node, State#state.listen) of +setup(ConnLookup, Node,ConnId,Type,From,State) -> + case setup_check(ConnLookup, Node, ConnId, State) of {ok, L} -> Mod = L#listen.module, LAddr = L#listen.address, @@ -1206,18 +1282,45 @@ setup(Node,Type,From,State) -> Addr = LAddr#net_address { address = undefined, host = undefined }, + Waiting = case From of + noreply -> []; + _ -> [From] + end, ets:insert(sys_dist, #connection{node = Node, + conn_id = ConnId, state = pending, owner = Pid, - waiting = [From], + waiting = Waiting, address = Addr, type = normal}), {ok, Pid}; Error -> Error - end end. +setup_check(ConnLookup, Node, ConnId, State) -> + Allowed = State#state.allowed, + case lists:member(Node, Allowed) of + false when Allowed =/= [] -> + error_msg("** Connection attempt with " + "disallowed node ~w ** ~n", [Node]), + {error, bad_node}; + _ -> + case verify_new_conn_id(ConnLookup, ConnId) of + false -> + error_msg("** Connection attempt to ~w with " + "bad connection id ~w ** ~n", [Node, ConnId]), + {error, bad_conn_id}; + true -> + case select_mod(Node, State#state.listen) of + {ok, _L}=OK -> OK; + Error -> Error + end + end + end. + + + %% %% Find a module that is willing to handle connection setup to Node %% @@ -1658,6 +1761,11 @@ verbose(_, _, _) -> getnode(P) when is_pid(P) -> node(P); getnode(P) -> P. +return_call({noreply, _State}=R, _From) -> + R; +return_call(R, From) -> + async_reply(R, From). + async_reply({reply, Msg, State}, From) -> async_gen_server_reply(From, Msg), {noreply, State}. -- cgit v1.2.3 From 771abbd23709b5a03416278595588931889ab7c5 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 20 Sep 2017 15:06:07 +0200 Subject: erts: Keep magic ref to DistEntry in net_kernel to make sure it's kept alive. --- lib/kernel/src/net_kernel.erl | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index f929e4bf11..eee915b15b 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -435,10 +435,10 @@ handle_connect(ConnLookup, Type, Node, ConnId, From , State) -> -define(ERTS_DIST_CON_ID_MASK, 16#ffffff). % also in external.h -verify_new_conn_id([], ConnId) - when (ConnId band (bnot ?ERTS_DIST_CON_ID_MASK)) =:= 0 -> +verify_new_conn_id([], {Nr,_DHandle}) + when (Nr band (bnot ?ERTS_DIST_CON_ID_MASK)) =:= 0 -> true; -verify_new_conn_id([#connection{conn_id = Old}], New) +verify_new_conn_id([#connection{conn_id = {Old,_}}], {New,_}) when New =:= ((Old+1) band ?ERTS_DIST_CON_ID_MASK) -> true; verify_new_conn_id(_, _) -> @@ -460,7 +460,7 @@ handle_call({auto_connect, Type, Node, WaitForBarred}, From, State) -> verbose({auto_connect, Type, Node, WaitForBarred}, 1, State), R = case (catch erlang:new_connection_id(Node)) of - ConnId when is_integer(ConnId) -> + {Nr,_DHandle}=ConnId when is_integer(Nr) -> handle_auto_connect(Type, Node, ConnId, WaitForBarred, From, State); _Error -> @@ -481,7 +481,7 @@ handle_call({connect, Type, Node}, From, State) -> verbose({connect, Type, Node}, 1, State), ConnLookup = ets:lookup(sys_dist, Node), R = case (catch erlang:new_connection_id(Node)) of - ConnId when is_integer(ConnId) -> + {Nr,_DHandle}=ConnId when is_integer(Nr) -> handle_connect(ConnLookup, Type, Node, ConnId, From, State); _Error -> @@ -699,8 +699,9 @@ terminate(_Reason, State) -> %% %% Asynchronous auto connect request %% -handle_info({auto_connect,Node,ConnId}, State) -> - verbose({auto_connect, Node, ConnId}, 1, State), +handle_info({auto_connect,Node, Nr, DHandle}, State) -> + verbose({auto_connect, Node, Nr, DHandle}, 1, State), + ConnId = {Nr, DHandle}, NewState = case handle_auto_connect(normal, Node, ConnId, false, noreply, State) of {noreply, S} -> %% Pending connection @@ -796,7 +797,7 @@ handle_info({AcceptPid, {accept_pending,MyNode,Node,Address,Type}}, State) -> {noreply, State}; _ -> ConnId = case (catch erlang:new_connection_id(Node)) of - CI when is_integer(CI) -> CI + {Nr,_DHandle}=CI when is_integer(Nr) -> CI %% SVERK What to do? end, ets:insert(sys_dist, #connection{node = Node, -- cgit v1.2.3 From 6c4a3094d0263e46827c6b1869a6de0c033b6b64 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 27 Sep 2017 19:45:42 +0200 Subject: Improve connection aborting --- lib/kernel/src/net_kernel.erl | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index eee915b15b..fb17e7c1b6 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -796,19 +796,23 @@ handle_info({AcceptPid, {accept_pending,MyNode,Node,Address,Type}}, State) -> AcceptPid ! {self(), {accept_pending, already_pending}}, {noreply, State}; _ -> - ConnId = case (catch erlang:new_connection_id(Node)) of - {Nr,_DHandle}=CI when is_integer(Nr) -> CI - %% SVERK What to do? - end, - ets:insert(sys_dist, #connection{node = Node, - conn_id = ConnId, - state = pending, - owner = AcceptPid, - address = Address, - type = Type}), - AcceptPid ! {self(),{accept_pending,ok}}, - Owners = [{AcceptPid,Node} | State#state.conn_owners], - {noreply, State#state{conn_owners = Owners}} + case (catch erlang:new_connection_id(Node)) of + {Nr,_DHandle}=ConnId when is_integer(Nr) -> + ets:insert(sys_dist, #connection{node = Node, + conn_id = ConnId, + state = pending, + owner = AcceptPid, + address = Address, + type = Type}), + AcceptPid ! {self(),{accept_pending,ok}}, + Owners = [{AcceptPid,Node} | State#state.conn_owners], + {noreply, State#state{conn_owners = Owners}}; + + _ -> + error_logger:error_msg("~n** Cannot get connection id for node ~w~n", + [Node]), + AcceptPid ! {self(),{accept_pending,nok_pending}} + end end; handle_info({SetupPid, {is_pending, Node}}, State) -> @@ -1009,7 +1013,9 @@ up_pending_nodedown(Conn, Node, _Reason, _Type, State) -> AcceptPid = Conn#connection.pending_owner, Owners = State#state.conn_owners, Pend = lists:keydelete(AcceptPid, 1, State#state.pend_owners), + erlang:abort_connection_id(Node, Conn#connection.conn_id), Conn1 = Conn#connection { owner = AcceptPid, + conn_id = erlang:new_connection_id(Node), pending_owner = undefined, state = pending }, ets:insert(sys_dist, Conn1), -- cgit v1.2.3 From 06ff5ffb402f00752b5415349ed817a40a76218f Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 11 Oct 2017 19:35:13 +0200 Subject: Cleanup net_kernel --- lib/kernel/src/net_kernel.erl | 49 ++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 26 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index fb17e7c1b6..3c4e8df88f 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -70,8 +70,8 @@ protocol_childspecs/0, epmd_module/0]). --export([connect/1, disconnect/1, hidden_connect/1, passive_cnct/1]). --export([hidden_connect_node/1]). %% explicit connect +-export([disconnect/1, passive_cnct/1]). +-export([hidden_connect_node/1]). -export([set_net_ticktime/1, set_net_ticktime/2, get_net_ticktime/0]). -export([node_info/1, node_info/2, nodes_info/0, @@ -248,14 +248,15 @@ ticktime_res(A) when is_atom(A) -> A. %% Called though BIF's -connect(Node) -> auto_connect(Node, normal, false). %%% Long timeout if blocked (== barred), only affects nodes with %%% {dist_auto_connect, once} set. -passive_cnct(Node) -> auto_connect(Node, normal, true). -disconnect(Node) -> request({disconnect, Node}). +passive_cnct(Node) -> + case request({passive_cnct, Node}) of + ignored -> false; + Other -> Other + end. -%% connect but not seen -hidden_connect(Node) -> auto_connect(Node, hidden, false). +disconnect(Node) -> request({disconnect, Node}). %% Should this node publish itself on Node? publish_on_node(Node) when is_atom(Node) -> @@ -273,11 +274,6 @@ connect_node(Node) when is_atom(Node) -> hidden_connect_node(Node) when is_atom(Node) -> request({connect, hidden, Node}). -auto_connect(Node, Type, WaitForBarred) -> %% Type = normal | hidden - case request({auto_connect, Type, Node, WaitForBarred}) of - ignored -> false; - Other -> Other - end. passive_connect_monitor(From, Node) -> ok = monitor_nodes(true,[{node_type,all}]), @@ -363,7 +359,7 @@ init({Name, LongOrShortNames, TickT, CleanHalt}) -> end. -handle_auto_connect(Type, Node, ConnId, WaitForBarred, From, State) -> +do_auto_connect(Type, Node, ConnId, WaitForBarred, From, State) -> ConnLookup = ets:lookup(sys_dist, Node), case ConnLookup of @@ -412,18 +408,18 @@ handle_auto_connect(Type, Node, ConnId, WaitForBarred, From, State) -> end. -handle_connect([#connection{conn_id = ConnId, state = up}], _, _, ConnId, _From, State) -> +do_explicit_connect([#connection{conn_id = ConnId, state = up}], _, _, ConnId, _From, State) -> {reply, true, State}; -handle_connect([#connection{conn_id = ConnId}=Conn], _, _, ConnId, From, State) +do_explicit_connect([#connection{conn_id = ConnId}=Conn], _, _, ConnId, From, State) when Conn#connection.state =:= pending; Conn#connection.state =:= up_pending -> Waiting = Conn#connection.waiting, ets:insert(sys_dist, Conn#connection{waiting = [From|Waiting]}), {noreply, State}; -handle_connect([#barred_connection{}], Type, Node, ConnId, From , State) -> +do_explicit_connect([#barred_connection{}], Type, Node, ConnId, From , State) -> %% Barred connection only affects auto_connect, ignore it. - handle_connect([], Type, Node, ConnId, From , State); -handle_connect(ConnLookup, Type, Node, ConnId, From , State) -> + do_explicit_connect([], Type, Node, ConnId, From , State); +do_explicit_connect(ConnLookup, Type, Node, ConnId, From , State) -> case setup(ConnLookup, Node,ConnId,Type,From,State) of {ok, SetupPid} -> Owners = [{SetupPid, Node} | State#state.conn_owners], @@ -451,17 +447,18 @@ verify_new_conn_id(_, _) -> %% ------------------------------------------------------------ %% -%% Auto-connect to Node. +%% Passive auto-connect to Node. %% The response is delayed until the connection is up and running. %% -handle_call({auto_connect, _, Node, _}, From, State) when Node =:= node() -> +handle_call({passive_cnct, Node}, From, State) when Node =:= node() -> async_reply({reply, true, State}, From); -handle_call({auto_connect, Type, Node, WaitForBarred}, From, State) -> - verbose({auto_connect, Type, Node, WaitForBarred}, 1, State), - +handle_call({passive_cnct, Node}, From, State) -> + verbose({passive_cnct, Node}, 1, State), + Type = normal, + WaitForBarred = true, R = case (catch erlang:new_connection_id(Node)) of {Nr,_DHandle}=ConnId when is_integer(Nr) -> - handle_auto_connect(Type, Node, ConnId, WaitForBarred, From, State); + do_auto_connect(Type, Node, ConnId, WaitForBarred, From, State); _Error -> error_logger:error_msg("~n** Cannot get connection id for node ~w~n", @@ -482,7 +479,7 @@ handle_call({connect, Type, Node}, From, State) -> ConnLookup = ets:lookup(sys_dist, Node), R = case (catch erlang:new_connection_id(Node)) of {Nr,_DHandle}=ConnId when is_integer(Nr) -> - handle_connect(ConnLookup, Type, Node, ConnId, From, State); + do_explicit_connect(ConnLookup, Type, Node, ConnId, From, State); _Error -> error_logger:error_msg("~n** Cannot get connection id for node ~w~n", @@ -703,7 +700,7 @@ handle_info({auto_connect,Node, Nr, DHandle}, State) -> verbose({auto_connect, Node, Nr, DHandle}, 1, State), ConnId = {Nr, DHandle}, NewState = - case handle_auto_connect(normal, Node, ConnId, false, noreply, State) of + case do_auto_connect(normal, Node, ConnId, false, noreply, State) of {noreply, S} -> %% Pending connection S; -- cgit v1.2.3 From 19c9f3a61fbef3682056bab3db4787cd763bd06e Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 15 Nov 2017 19:44:52 +0100 Subject: Move new|abort_connection_id to erts_internal and drop _id suffix. --- lib/kernel/src/net_kernel.erl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index 3c4e8df88f..cdb10a7b12 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -456,7 +456,7 @@ handle_call({passive_cnct, Node}, From, State) -> verbose({passive_cnct, Node}, 1, State), Type = normal, WaitForBarred = true, - R = case (catch erlang:new_connection_id(Node)) of + R = case (catch erts_internal:new_connection(Node)) of {Nr,_DHandle}=ConnId when is_integer(Nr) -> do_auto_connect(Type, Node, ConnId, WaitForBarred, From, State); @@ -477,7 +477,7 @@ handle_call({connect, _, Node, _, _}, From, State) when Node =:= node() -> handle_call({connect, Type, Node}, From, State) -> verbose({connect, Type, Node}, 1, State), ConnLookup = ets:lookup(sys_dist, Node), - R = case (catch erlang:new_connection_id(Node)) of + R = case (catch erts_internal:new_connection(Node)) of {Nr,_DHandle}=ConnId when is_integer(Nr) -> do_explicit_connect(ConnLookup, Type, Node, ConnId, From, State); @@ -708,7 +708,7 @@ handle_info({auto_connect,Node, Nr, DHandle}, State) -> S; {reply, false, S} -> %% Connection refused - erlang:abort_connection_id(Node, ConnId), + erts_internal:abort_connection(Node, ConnId), S end, {noreply, NewState}; @@ -793,7 +793,7 @@ handle_info({AcceptPid, {accept_pending,MyNode,Node,Address,Type}}, State) -> AcceptPid ! {self(), {accept_pending, already_pending}}, {noreply, State}; _ -> - case (catch erlang:new_connection_id(Node)) of + case (catch erts_internal:new_connection(Node)) of {Nr,_DHandle}=ConnId when is_integer(Nr) -> ets:insert(sys_dist, #connection{node = Node, conn_id = ConnId, @@ -995,7 +995,7 @@ pending_nodedown(Conn, Node, Type, State) -> % Don't bar connections that have never been alive %mark_sys_dist_nodedown(Node), % - instead just delete the node: - erlang:abort_connection_id(Node, Conn#connection.conn_id), + erts_internal:abort_connection(Node, Conn#connection.conn_id), ets:delete(sys_dist, Node), reply_waiting(Node,Conn#connection.waiting, false), case Type of @@ -1010,9 +1010,9 @@ up_pending_nodedown(Conn, Node, _Reason, _Type, State) -> AcceptPid = Conn#connection.pending_owner, Owners = State#state.conn_owners, Pend = lists:keydelete(AcceptPid, 1, State#state.pend_owners), - erlang:abort_connection_id(Node, Conn#connection.conn_id), + erts_internal:abort_connection(Node, Conn#connection.conn_id), Conn1 = Conn#connection { owner = AcceptPid, - conn_id = erlang:new_connection_id(Node), + conn_id = erts_internal:new_connection(Node), pending_owner = undefined, state = pending }, ets:insert(sys_dist, Conn1), @@ -1029,7 +1029,7 @@ up_nodedown(Conn, Node, _Reason, Type, State) -> State. mark_sys_dist_nodedown(Conn, Node) -> - erlang:abort_connection_id(Node, Conn#connection.conn_id), + erts_internal:abort_connection(Node, Conn#connection.conn_id), case application:get_env(kernel, dist_auto_connect) of {ok, once} -> ets:insert(sys_dist, #barred_connection{node = Node}); -- cgit v1.2.3 From d8d07a7593d811a6adad060951bc425ee0d81446 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 19 Oct 2017 14:50:50 +0200 Subject: compiler: Add +to_dis option that dumps loaded asm --- lib/kernel/src/erts_debug.erl | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl index 480db6814e..9662f8fa90 100644 --- a/lib/kernel/src/erts_debug.erl +++ b/lib/kernel/src/erts_debug.erl @@ -21,7 +21,7 @@ %% Low-level debugging support. EXPERIMENTAL! --export([size/1,df/1,df/2,df/3,ic/1]). +-export([size/1,df/1,df/2,df/3,df/4,ic/1]). %% This module contains the following *experimental* BIFs: %% disassemble/1 @@ -347,31 +347,39 @@ is_term_seen(_, []) -> false. -spec df(module()) -> df_ret(). df(Mod) when is_atom(Mod) -> + df(lists:concat([Mod, ".dis"]), Mod). + +-spec df(module(), atom()) -> df_ret(); + (file:io_device() | file:filename(), module()) -> df_ret(). + +df(Mod, Func) when is_atom(Mod), is_atom(Func) -> + df(lists:concat([Mod, "_", Func, ".dis"]), Mod, Func); +df(Name, Mod) when is_atom(Mod) -> try Mod:module_info(functions) of Fs0 when is_list(Fs0) -> - Name = lists:concat([Mod, ".dis"]), Fs = [{Mod,Func,Arity} || {Func,Arity} <- Fs0], dff(Name, Fs) catch _:_ -> {undef,Mod} end. --spec df(module(), atom()) -> df_ret(). -df(Mod, Func) when is_atom(Mod), is_atom(Func) -> +-spec df(module(), atom(), arity()) -> df_ret(); + (file:io_device() | file:filename(), module(), atom()) -> df_ret(). + +df(Mod, Func, Arity) when is_atom(Mod), is_atom(Func), is_integer(Arity) -> + df(lists:concat([Mod, "_", Func, "_", Arity, ".dis"]), Mod, Func, Arity); +df(Name, Mod, Func) when is_atom(Mod), is_atom(Func) -> try Mod:module_info(functions) of Fs0 when is_list(Fs0) -> - Name = lists:concat([Mod, "_", Func, ".dis"]), Fs = [{Mod,Func1,Arity} || {Func1,Arity} <- Fs0, Func1 =:= Func], dff(Name, Fs) catch _:_ -> {undef,Mod} end. --spec df(module(), atom(), arity()) -> df_ret(). - -df(Mod, Func, Arity) when is_atom(Mod), is_atom(Func) -> +-spec df(file:io_device() | file:filename(), module(), atom(), arity()) -> df_ret(). +df(Name, Mod, Func, Arity) when is_atom(Mod), is_atom(Func), is_integer(Arity) -> try Mod:module_info(functions) of Fs0 when is_list(Fs0) -> - Name = lists:concat([Mod, "_", Func, "_", Arity, ".dis"]), Fs = [{Mod,Func1,Arity1} || {Func1,Arity1} <- Fs0, Func1 =:= Func, Arity1 =:= Arity], dff(Name, Fs) -- cgit v1.2.3 From 2d34363405f327e4883876f8917b6eeb127f5b6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 22 Nov 2017 15:53:29 +0100 Subject: Fix broken to_dis after merge from maint to master d8d07a7593d811 that added the to_dis option to the compiler no longer works when merged to master. That is because of 79f28cfd8df1b7 that removed some unused code in erts_debug. Fix this by adding a new function erts_debug:dis_to_file/2 and use it from compile module where we have access to the filename (in beam_listing we only have an opened file). --- lib/kernel/src/erts_debug.erl | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl index ea8d64b2c7..3456c8511e 100644 --- a/lib/kernel/src/erts_debug.erl +++ b/lib/kernel/src/erts_debug.erl @@ -21,7 +21,7 @@ %% Low-level debugging support. EXPERIMENTAL! --export([size/1,df/1,df/2,df/3,df/4,ic/1]). +-export([size/1,df/1,df/2,df/3,dis_to_file/2,ic/1]). %% This module contains the following *experimental* BIFs: %% disassemble/1 @@ -347,45 +347,47 @@ is_term_seen(_, []) -> false. -spec df(module()) -> df_ret(). df(Mod) when is_atom(Mod) -> - df(lists:concat([Mod, ".dis"]), Mod). - --spec df(module(), atom()) -> df_ret(); - (file:io_device() | file:filename(), module()) -> df_ret(). - -df(Mod, Func) when is_atom(Mod), is_atom(Func) -> - df(lists:concat([Mod, "_", Func, ".dis"]), Mod, Func); -df(Name, Mod) when is_atom(Mod) -> try Mod:module_info(functions) of Fs0 when is_list(Fs0) -> + Name = lists:concat([Mod, ".dis"]), Fs = [{Mod,Func,Arity} || {Func,Arity} <- Fs0], dff(Name, Fs) catch _:_ -> {undef,Mod} end. +-spec df(module(), atom()) -> df_ret(). --spec df(module(), atom(), arity()) -> df_ret(); - (file:io_device() | file:filename(), module(), atom()) -> df_ret(). - -df(Mod, Func, Arity) when is_atom(Mod), is_atom(Func), is_integer(Arity) -> - df(lists:concat([Mod, "_", Func, "_", Arity, ".dis"]), Mod, Func, Arity); -df(Name, Mod, Func) when is_atom(Mod), is_atom(Func) -> +df(Mod, Func) when is_atom(Mod), is_atom(Func) -> try Mod:module_info(functions) of Fs0 when is_list(Fs0) -> + Name = lists:concat([Mod, "_", Func, ".dis"]), Fs = [{Mod,Func1,Arity} || {Func1,Arity} <- Fs0, Func1 =:= Func], dff(Name, Fs) catch _:_ -> {undef,Mod} end. --spec df(file:io_device() | file:filename(), module(), atom(), arity()) -> df_ret(). -df(Name, Mod, Func, Arity) when is_atom(Mod), is_atom(Func), is_integer(Arity) -> +-spec df(module(), atom(), arity()) -> df_ret(). + +df(Mod, Func, Arity) when is_atom(Mod), is_atom(Func) -> try Mod:module_info(functions) of Fs0 when is_list(Fs0) -> + Name = lists:concat([Mod, "_", Func, "_", Arity, ".dis"]), Fs = [{Mod,Func1,Arity1} || {Func1,Arity1} <- Fs0, Func1 =:= Func, Arity1 =:= Arity], dff(Name, Fs) catch _:_ -> {undef,Mod} end. +-spec dis_to_file(module(), file:filename()) -> df_ret(). + +dis_to_file(Mod, Name) when is_atom(Mod) -> + try Mod:module_info(functions) of + Fs0 when is_list(Fs0) -> + Fs = [{Mod,Func,Arity} || {Func,Arity} <- Fs0], + dff(Name, Fs) + catch _:_ -> {undef,Mod} + end. + dff(Name, Fs) -> case file:open(Name, [write,raw,delayed_write]) of {ok,F} -> -- cgit v1.2.3 From 8ed0d75c186d9da24bd6cfb85732487b17a3b054 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 23 Nov 2017 15:05:34 +0100 Subject: erts: Fix erlang:monitor toward c-nodes by suppressing DOP_MONITOR_P, DOP_MONITOR_P_EXIT and DOP_DEMONITOR_P if not supported by the remote node. In 17e198d6ee60f7dec9abfed272cf4226aea44535 I changed the behavior of erlang:monitor to not raise badarg for c-nodes but instead create a monitor to only supervise the connection. But I forgot to prevent DOP_MONITOR_P and friends from being sent to the node that does not expect them. Note: We test both DFLAG_DIST_MONITOR and DFLAG_DIST_MONITOR_NAME for the node to support process monitoring. This is because erl_interface is buggy as it sets DFLAG_DIST_MONITOR without really supporting it. ToDo: Should erl_interface stop setting DFLAG_DIST_MONITOR or should we change the meaning of these flags? --- lib/kernel/test/erl_distribution_wb_SUITE.erl | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/test/erl_distribution_wb_SUITE.erl b/lib/kernel/test/erl_distribution_wb_SUITE.erl index 1145d30e5e..8256444bdc 100644 --- a/lib/kernel/test/erl_distribution_wb_SUITE.erl +++ b/lib/kernel/test/erl_distribution_wb_SUITE.erl @@ -677,6 +677,8 @@ build_rex_message(Cookie,OurName) -> %% Receive a distribution message recv_message(Socket) -> case gen_tcp:recv(Socket, 0) of + {ok,[]} -> + recv_message(Socket); %% a tick, ignore {ok,Data} -> B0 = list_to_binary(Data), <> = B0, -- cgit v1.2.3 From ebbd26eeea4115c946d1254d94acd50f150b4455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Fri, 3 Nov 2017 11:49:27 +0100 Subject: Reimplement efile_drv as a dirty NIF This improves the latency of file operations as dirty schedulers are a bit more eager to run jobs than async threads, and use a single global queue rather than per-thread queues, eliminating the risk of a job stalling behind a long-running job on the same thread while other async threads sit idle. There's no such thing as a free lunch though; the lowered latency comes at the cost of increased busy-waiting which may have an adverse effect on some applications. This behavior can be tweaked with the +sbwt flag, but unfortunately it affects all types of schedulers and not just dirty ones. We plan to add type-specific flags at a later stage. sendfile has been moved to inet_drv to lessen the effect of a nasty race; the cooperation between inet_drv and efile has never been airtight and the socket dying at the wrong time (Regardless of reason) could result in fd aliasing. Moving it to the inet driver makes it impossible to trigger this by closing the socket in the middle of a sendfile operation, while still allowing it to be aborted -- something that can't be done if it stays in the file driver. The race still occurs if the controlling process dies in the short window between dispatching the sendfile operation and the dup(2) call in the driver, but it's much less likely to happen now. A proper fix is in the works. -- Notable functional differences: * The use_threads option for file:sendfile/5 no longer has any effect. * The file-specific DTrace probes have been removed. The same effect can be achieved with normal tracing together with the nif__entry/nif__return probes to track scheduling. -- OTP-14256 --- lib/kernel/src/Makefile | 17 +- lib/kernel/src/file.erl | 40 +--- lib/kernel/src/file_int.hrl | 33 +++ lib/kernel/src/file_io_server.erl | 52 +++-- lib/kernel/src/file_server.erl | 137 ++++++------- lib/kernel/src/inet_int.hrl | 8 + lib/kernel/src/kernel.app.src | 7 + lib/kernel/src/raw_file_io.erl | 75 +++++++ lib/kernel/src/raw_file_io_compressed.erl | 134 +++++++++++++ lib/kernel/src/raw_file_io_deflate.erl | 159 +++++++++++++++ lib/kernel/src/raw_file_io_delayed.erl | 320 ++++++++++++++++++++++++++++++ lib/kernel/src/raw_file_io_inflate.erl | 261 ++++++++++++++++++++++++ lib/kernel/src/raw_file_io_list.erl | 128 ++++++++++++ lib/kernel/src/raw_file_io_raw.erl | 25 +++ 14 files changed, 1265 insertions(+), 131 deletions(-) create mode 100644 lib/kernel/src/file_int.hrl create mode 100644 lib/kernel/src/raw_file_io.erl create mode 100644 lib/kernel/src/raw_file_io_compressed.erl create mode 100644 lib/kernel/src/raw_file_io_deflate.erl create mode 100644 lib/kernel/src/raw_file_io_delayed.erl create mode 100644 lib/kernel/src/raw_file_io_inflate.erl create mode 100644 lib/kernel/src/raw_file_io_list.erl create mode 100644 lib/kernel/src/raw_file_io_raw.erl (limited to 'lib/kernel') diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile index 5946620f0f..4a713b2a99 100644 --- a/lib/kernel/src/Makefile +++ b/lib/kernel/src/Makefile @@ -120,6 +120,13 @@ MODULES = \ user \ user_drv \ user_sup \ + raw_file_io \ + raw_file_io_compressed \ + raw_file_io_inflate \ + raw_file_io_deflate \ + raw_file_io_delayed \ + raw_file_io_list \ + raw_file_io_raw \ wrap_log_reader HRL_FILES= ../include/file.hrl ../include/inet.hrl ../include/inet_sctp.hrl \ @@ -226,7 +233,8 @@ $(EBIN)/disk_log_server.beam: disk_log.hrl $(EBIN)/dist_util.beam: ../include/dist_util.hrl ../include/dist.hrl $(EBIN)/erl_boot_server.beam: inet_boot.hrl $(EBIN)/erl_epmd.beam: inet_int.hrl erl_epmd.hrl -$(EBIN)/file.beam: ../include/file.hrl +$(EBIN)/file.beam: ../include/file.hrl file_int.hrl +$(EBIN)/file_io_server.beam: ../include/file.hrl file_int.hrl $(EBIN)/gen_tcp.beam: inet_int.hrl $(EBIN)/gen_udp.beam: inet_int.hrl $(EBIN)/gen_sctp.beam: ../include/inet_sctp.hrl @@ -254,3 +262,10 @@ $(EBIN)/net_kernel.beam: ../include/net_address.hrl $(EBIN)/os.beam: ../include/file.hrl $(EBIN)/ram_file.beam: ../include/file.hrl $(EBIN)/wrap_log_reader.beam: disk_log.hrl ../include/file.hrl +$(EBIN)/raw_file_io.beam: ../include/file.hrl file_int.hrl +$(EBIN)/raw_file_io_compressed.beam: ../include/file.hrl file_int.hrl +$(EBIN)/raw_file_io_inflate.beam: ../include/file.hrl file_int.hrl +$(EBIN)/raw_file_io_deflate.beam: ../include/file.hrl file_int.hrl +$(EBIN)/raw_file_io_delayed.beam: ../include/file.hrl file_int.hrl +$(EBIN)/raw_file_io_list.beam: ../include/file.hrl file_int.hrl +$(EBIN)/raw_file_io_raw.beam: ../include/file.hrl file_int.hrl diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index 933f2d5f65..b5a51c3410 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -72,7 +72,7 @@ io_device/0, name/0, name_all/0, posix/0]). %%% Includes and defines --include("file.hrl"). +-include("file_int.hrl"). -define(FILE_IO_SERVER_TABLE, file_io_servers). @@ -454,41 +454,23 @@ raw_write_file_info(Name, #file_info{} = Info) -> Reason :: posix() | badarg | system_limit. open(Item, ModeList) when is_list(ModeList) -> - case lists:member(raw, ModeList) of - %% Raw file, use ?PRIM_FILE to handle this file - true -> + case {lists:member(raw, ModeList), lists:member(ram, ModeList)} of + {false, false} -> + %% File server file Args = [file_name(Item) | ModeList], case check_args(Args) of ok -> [FileName | _] = Args, - %% We rely on the returned Handle (in {ok, Handle}) - %% being a pid() or a #file_descriptor{} - ?PRIM_FILE:open(FileName, ModeList); + call(open, [FileName, ModeList]); Error -> Error - end; - false -> - case lists:member(ram, ModeList) of - %% RAM file, use ?RAM_FILE to handle this file - true -> - case check_args(ModeList) of - ok -> - ?RAM_FILE:open(Item, ModeList); - Error -> - Error - end; - %% File server file - false -> - Args = [file_name(Item) | ModeList], - case check_args(Args) of - ok -> - [FileName | _] = Args, - call(open, [FileName, ModeList]); - Error -> - Error - end - end + end; + {true, _Either} -> + raw_file_io:open(file_name(Item), ModeList); + {false, true} -> + ram_file:open(Item, ModeList) end; + %% Old obsolete mode specification in atom or 2-tuple format open(Item, Mode) -> open(Item, mode_list(Mode)). diff --git a/lib/kernel/src/file_int.hrl b/lib/kernel/src/file_int.hrl new file mode 100644 index 0000000000..bafc330c04 --- /dev/null +++ b/lib/kernel/src/file_int.hrl @@ -0,0 +1,33 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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% +%% + +%% +%% Internal definitions for the 'file' module and friends. +%% + +-ifndef(FILE_INTERNAL_HRL_). +-define(FILE_INTERNAL_HRL_, 1). + +-include("file.hrl"). + +-define(CALL_FD(Fd, Method, Args), + apply(Fd#file_descriptor.module, Method, [Fd | Args])). + +-endif. diff --git a/lib/kernel/src/file_io_server.erl b/lib/kernel/src/file_io_server.erl index deb7b315b1..2b35d2acfb 100644 --- a/lib/kernel/src/file_io_server.erl +++ b/lib/kernel/src/file_io_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2015. All Rights Reserved. +%% Copyright Ericsson AB 2000-2017. 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. @@ -28,7 +28,8 @@ -record(state, {handle,owner,mref,buf,read_mode,unic}). --define(PRIM_FILE, prim_file). +-include("file_int.hrl"). + -define(READ_SIZE_LIST, 128). -define(READ_SIZE_BINARY, (8*1024)). @@ -68,7 +69,7 @@ do_start(Spawn, Owner, FileName, ModeList) -> %% process_flag(trap_exit, true), case parse_options(ModeList) of {ReadMode, UnicodeMode, Opts} -> - case ?PRIM_FILE:open(FileName, Opts) of + case raw_file_io:open(FileName, [raw | Opts]) of {error, Reason} = Error -> Self ! {Ref, Error}, exit(Reason); @@ -205,7 +206,7 @@ io_reply(From, ReplyAs, Reply) -> file_request({advise,Offset,Length,Advise}, #state{handle=Handle}=State) -> - case ?PRIM_FILE:advise(Handle, Offset, Length, Advise) of + case ?CALL_FD(Handle, advise, [Offset, Length, Advise]) of {error,Reason}=Reply -> {stop,Reason,Reply,State}; Reply -> @@ -213,7 +214,7 @@ file_request({advise,Offset,Length,Advise}, end; file_request({allocate, Offset, Length}, #state{handle = Handle} = State) -> - Reply = ?PRIM_FILE:allocate(Handle, Offset, Length), + Reply = ?CALL_FD(Handle, allocate, [Offset, Length]), {reply, Reply, State}; file_request({pread,At,Sz}, State) when At =:= cur; @@ -256,7 +257,7 @@ file_request({pwrite,At,Data}, end; file_request(datasync, #state{handle=Handle}=State) -> - case ?PRIM_FILE:datasync(Handle) of + case ?CALL_FD(Handle, datasync, []) of {error,Reason}=Reply -> {stop,Reason,Reply,State}; Reply -> @@ -264,7 +265,7 @@ file_request(datasync, end; file_request(sync, #state{handle=Handle}=State) -> - case ?PRIM_FILE:sync(Handle) of + case ?CALL_FD(Handle, sync, []) of {error,Reason}=Reply -> {stop,Reason,Reply,State}; Reply -> @@ -272,7 +273,7 @@ file_request(sync, end; file_request(close, #state{handle=Handle}=State) -> - case ?PRIM_FILE:close(Handle) of + case ?CALL_FD(Handle, close, []) of {error,Reason}=Reply -> {stop,Reason,Reply,State#state{buf= <<>>}}; Reply -> @@ -288,7 +289,7 @@ file_request({position,At}, end; file_request(truncate, #state{handle=Handle}=State) -> - case ?PRIM_FILE:truncate(Handle) of + case ?CALL_FD(Handle, truncate, []) of {error,Reason}=Reply -> {stop,Reason,Reply,State#state{buf= <<>>}}; Reply -> @@ -398,7 +399,7 @@ io_request_loop([Request|Tail], %% put_chars(Chars, latin1, #state{handle=Handle, unic=latin1}=State) -> NewState = State#state{buf = <<>>}, - case ?PRIM_FILE:write(Handle, Chars) of + case ?CALL_FD(Handle, write, [Chars]) of {error,Reason}=Reply -> {stop,Reason,Reply,NewState}; Reply -> @@ -408,7 +409,7 @@ put_chars(Chars, InEncoding, #state{handle=Handle, unic=OutEncoding}=State) -> NewState = State#state{buf = <<>>}, case unicode:characters_to_binary(Chars,InEncoding,OutEncoding) of Bin when is_binary(Bin) -> - case ?PRIM_FILE:write(Handle, Bin) of + case ?CALL_FD(Handle, write, [Bin]) of {error,Reason}=Reply -> {stop,Reason,Reply,NewState}; Reply -> @@ -422,7 +423,7 @@ put_chars(Chars, InEncoding, #state{handle=Handle, unic=OutEncoding}=State) -> get_line(S, {<<>>, Cont}, OutEnc, #state{handle=Handle, read_mode=Mode, unic=InEnc}=State) -> - case ?PRIM_FILE:read(Handle, read_size(Mode)) of + case ?CALL_FD(Handle, read, [read_size(Mode)]) of {ok,Bin} -> get_line(S, convert_enc([Cont, Bin], InEnc, OutEnc), OutEnc, State); eof -> @@ -472,7 +473,7 @@ get_chars(N, OutEnc,#state{handle=Handle,buf=Buf,read_mode=ReadMode,unic=latin1} BufSize = byte_size(Buf), NeedSize = N-BufSize, Size = erlang:max(NeedSize, ?READ_SIZE_BINARY), - case ?PRIM_FILE:read(Handle, Size) of + case ?CALL_FD(Handle, read, [Size]) of {ok, B} -> if BufSize+byte_size(B) < N -> std_reply(cat(Buf, B, ReadMode,latin1,OutEnc), State); @@ -504,7 +505,7 @@ get_chars(N, OutEnc,#state{handle=Handle,buf=Buf,read_mode=ReadMode,unic=InEncod %% Need more, Try to read 4*needed in bytes... NeedSize = (N - BufCount) * 4, Size = erlang:max(NeedSize, ?READ_SIZE_BINARY), - case ?PRIM_FILE:read(Handle, Size) of + case ?CALL_FD(Handle, read, [Size]) of {ok, B} -> NewBuf = list_to_binary([Buf,B]), {NewCount,NewSplit} = count_and_find(NewBuf,N,InEncoding), @@ -544,7 +545,7 @@ get_chars(Mod, Func, XtraArg, OutEnc, #state{buf=Buf}=State) -> get_chars_empty(Mod, Func, XtraArg, S, latin1, #state{handle=Handle,read_mode=ReadMode, unic=latin1}=State) -> - case ?PRIM_FILE:read(Handle, read_size(ReadMode)) of + case ?CALL_FD(Handle, read, [read_size(ReadMode)]) of {ok,Bin} -> get_chars_apply(Mod, Func, XtraArg, S, latin1, State, Bin); eof -> @@ -554,7 +555,7 @@ get_chars_empty(Mod, Func, XtraArg, S, latin1, end; get_chars_empty(Mod, Func, XtraArg, S, OutEnc, #state{handle=Handle,read_mode=ReadMode}=State) -> - case ?PRIM_FILE:read(Handle, read_size(ReadMode)) of + case ?CALL_FD(Handle, read, [read_size(ReadMode)]) of {ok,Bin} -> get_chars_apply(Mod, Func, XtraArg, S, OutEnc, State, Bin); eof -> @@ -564,7 +565,7 @@ get_chars_empty(Mod, Func, XtraArg, S, OutEnc, end. get_chars_notempty(Mod, Func, XtraArg, S, OutEnc, #state{handle=Handle,read_mode=ReadMode,buf = B}=State) -> - case ?PRIM_FILE:read(Handle, read_size(ReadMode)) of + case ?CALL_FD(Handle, read, [read_size(ReadMode)]) of {ok,Bin} -> get_chars_apply(Mod, Func, XtraArg, S, OutEnc, State, list_to_binary([B,Bin])); eof -> @@ -918,13 +919,10 @@ cbv({utf32,little},_) -> %% Compensates ?PRIM_FILE:position/2 for the number of bytes %% we have buffered position(Handle, At, Buf) -> - ?PRIM_FILE:position( - Handle, - case At of - cur -> - {cur, -byte_size(Buf)}; - {cur, Offs} -> - {cur, Offs-byte_size(Buf)}; - _ -> - At - end). + SeekTo = + case At of + {cur, Offs} -> {cur, Offs-byte_size(Buf)}; + cur -> {cur, -byte_size(Buf)}; + _ -> At + end, + ?CALL_FD(Handle, position, [SeekTo]). diff --git a/lib/kernel/src/file_server.erl b/lib/kernel/src/file_server.erl index 6e8f64d932..ecc1ffbdd6 100644 --- a/lib/kernel/src/file_server.erl +++ b/lib/kernel/src/file_server.erl @@ -63,7 +63,7 @@ stop() -> %%% Callback functions from gen_server %%%---------------------------------------------------------------------- --type state() :: port(). % Internal type +-type state() :: term(). % Internal type %%---------------------------------------------------------------------- %% Func: init/1 @@ -77,14 +77,8 @@ stop() -> init([]) -> process_flag(trap_exit, true), - case ?PRIM_FILE:start() of - {ok, Handle} -> - ?FILE_IO_SERVER_TABLE = - ets:new(?FILE_IO_SERVER_TABLE, [named_table]), - {ok, Handle}; - {error, Reason} -> - {stop, Reason} - end. + ?FILE_IO_SERVER_TABLE = ets:new(?FILE_IO_SERVER_TABLE, [named_table]), + {ok, undefined}. %%---------------------------------------------------------------------- %% Func: handle_call/3 @@ -101,7 +95,7 @@ init([]) -> {'reply', 'eof' | 'ok' | {'error', term()} | {'ok', term()}, state()} | {'stop', 'normal', 'stopped', state()}. -handle_call({open, Name, ModeList}, {Pid, _Tag} = _From, Handle) +handle_call({open, Name, ModeList}, {Pid, _Tag} = _From, State) when is_list(ModeList) -> Child = ?FILE_IO_SERVER:start_link(Pid, Name, ModeList), case Child of @@ -110,78 +104,78 @@ handle_call({open, Name, ModeList}, {Pid, _Tag} = _From, Handle) _ -> ok end, - {reply, Child, Handle}; + {reply, Child, State}; -handle_call({open, _Name, _Mode}, _From, Handle) -> - {reply, {error, einval}, Handle}; +handle_call({open, _Name, _Mode}, _From, State) -> + {reply, {error, einval}, State}; -handle_call({read_file, Name}, _From, Handle) -> - {reply, ?PRIM_FILE:read_file(Name), Handle}; +handle_call({read_file, Name}, _From, State) -> + {reply, ?PRIM_FILE:read_file(Name), State}; -handle_call({write_file, Name, Bin}, _From, Handle) -> - {reply, ?PRIM_FILE:write_file(Name, Bin), Handle}; +handle_call({write_file, Name, Bin}, _From, State) -> + {reply, ?PRIM_FILE:write_file(Name, Bin), State}; -handle_call({set_cwd, Name}, _From, Handle) -> - {reply, ?PRIM_FILE:set_cwd(Handle, Name), Handle}; +handle_call({set_cwd, Name}, _From, State) -> + {reply, ?PRIM_FILE:set_cwd(Name), State}; -handle_call({delete, Name}, _From, Handle) -> - {reply, ?PRIM_FILE:delete(Handle, Name), Handle}; +handle_call({delete, Name}, _From, State) -> + {reply, ?PRIM_FILE:delete(Name), State}; -handle_call({rename, Fr, To}, _From, Handle) -> - {reply, ?PRIM_FILE:rename(Handle, Fr, To), Handle}; +handle_call({rename, Fr, To}, _From, State) -> + {reply, ?PRIM_FILE:rename(Fr, To), State}; -handle_call({make_dir, Name}, _From, Handle) -> - {reply, ?PRIM_FILE:make_dir(Handle, Name), Handle}; +handle_call({make_dir, Name}, _From, State) -> + {reply, ?PRIM_FILE:make_dir(Name), State}; -handle_call({del_dir, Name}, _From, Handle) -> - {reply, ?PRIM_FILE:del_dir(Handle, Name), Handle}; +handle_call({del_dir, Name}, _From, State) -> + {reply, ?PRIM_FILE:del_dir(Name), State}; -handle_call({list_dir, Name}, _From, Handle) -> - {reply, ?PRIM_FILE:list_dir(Handle, Name), Handle}; -handle_call({list_dir_all, Name}, _From, Handle) -> - {reply, ?PRIM_FILE:list_dir_all(Handle, Name), Handle}; +handle_call({list_dir, Name}, _From, State) -> + {reply, ?PRIM_FILE:list_dir(Name), State}; +handle_call({list_dir_all, Name}, _From, State) -> + {reply, ?PRIM_FILE:list_dir_all(Name), State}; -handle_call(get_cwd, _From, Handle) -> - {reply, ?PRIM_FILE:get_cwd(Handle), Handle}; -handle_call({get_cwd}, _From, Handle) -> - {reply, ?PRIM_FILE:get_cwd(Handle), Handle}; -handle_call({get_cwd, Name}, _From, Handle) -> - {reply, ?PRIM_FILE:get_cwd(Handle, Name), Handle}; +handle_call(get_cwd, _From, State) -> + {reply, ?PRIM_FILE:get_cwd(), State}; +handle_call({get_cwd}, _From, State) -> + {reply, ?PRIM_FILE:get_cwd(), State}; +handle_call({get_cwd, Name}, _From, State) -> + {reply, ?PRIM_FILE:get_cwd(Name), State}; -handle_call({read_file_info, Name}, _From, Handle) -> - {reply, ?PRIM_FILE:read_file_info(Handle, Name), Handle}; +handle_call({read_file_info, Name}, _From, State) -> + {reply, ?PRIM_FILE:read_file_info(Name), State}; -handle_call({read_file_info, Name, Opts}, _From, Handle) -> - {reply, ?PRIM_FILE:read_file_info(Handle, Name, Opts), Handle}; +handle_call({read_file_info, Name, Opts}, _From, State) -> + {reply, ?PRIM_FILE:read_file_info(Name, Opts), State}; -handle_call({altname, Name}, _From, Handle) -> - {reply, ?PRIM_FILE:altname(Handle, Name), Handle}; +handle_call({altname, Name}, _From, State) -> + {reply, ?PRIM_FILE:altname(Name), State}; -handle_call({write_file_info, Name, Info}, _From, Handle) -> - {reply, ?PRIM_FILE:write_file_info(Handle, Name, Info), Handle}; +handle_call({write_file_info, Name, Info}, _From, State) -> + {reply, ?PRIM_FILE:write_file_info(Name, Info), State}; -handle_call({write_file_info, Name, Info, Opts}, _From, Handle) -> - {reply, ?PRIM_FILE:write_file_info(Handle, Name, Info, Opts), Handle}; +handle_call({write_file_info, Name, Info, Opts}, _From, State) -> + {reply, ?PRIM_FILE:write_file_info(Name, Info, Opts), State}; -handle_call({read_link_info, Name}, _From, Handle) -> - {reply, ?PRIM_FILE:read_link_info(Handle, Name), Handle}; +handle_call({read_link_info, Name}, _From, State) -> + {reply, ?PRIM_FILE:read_link_info(Name), State}; -handle_call({read_link_info, Name, Opts}, _From, Handle) -> - {reply, ?PRIM_FILE:read_link_info(Handle, Name, Opts), Handle}; +handle_call({read_link_info, Name, Opts}, _From, State) -> + {reply, ?PRIM_FILE:read_link_info(Name, Opts), State}; -handle_call({read_link, Name}, _From, Handle) -> - {reply, ?PRIM_FILE:read_link(Handle, Name), Handle}; -handle_call({read_link_all, Name}, _From, Handle) -> - {reply, ?PRIM_FILE:read_link_all(Handle, Name), Handle}; +handle_call({read_link, Name}, _From, State) -> + {reply, ?PRIM_FILE:read_link(Name), State}; +handle_call({read_link_all, Name}, _From, State) -> + {reply, ?PRIM_FILE:read_link_all(Name), State}; -handle_call({make_link, Old, New}, _From, Handle) -> - {reply, ?PRIM_FILE:make_link(Handle, Old, New), Handle}; +handle_call({make_link, Old, New}, _From, State) -> + {reply, ?PRIM_FILE:make_link(Old, New), State}; -handle_call({make_symlink, Old, New}, _From, Handle) -> - {reply, ?PRIM_FILE:make_symlink(Handle, Old, New), Handle}; +handle_call({make_symlink, Old, New}, _From, State) -> + {reply, ?PRIM_FILE:make_symlink(Old, New), State}; handle_call({copy, SourceName, SourceOpts, DestName, DestOpts, Length}, - _From, Handle) -> + _From, State) -> Reply = case ?PRIM_FILE:open(SourceName, [read, binary | SourceOpts]) of {ok, Source} -> @@ -201,14 +195,14 @@ handle_call({copy, SourceName, SourceOpts, DestName, DestOpts, Length}, {error, _} = Error -> Error end, - {reply, Reply, Handle}; + {reply, Reply, State}; -handle_call(stop, _From, Handle) -> - {stop, normal, stopped, Handle}; +handle_call(stop, _From, State) -> + {stop, normal, stopped, State}; -handle_call(Request, From, Handle) -> +handle_call(Request, From, State) -> error_logger:error_msg("handle_call(~tp, ~tp, _)", [Request, From]), - {noreply, Handle}. + {noreply, State}. %%---------------------------------------------------------------------- %% Func: handle_cast/2 @@ -233,14 +227,9 @@ handle_cast(Msg, State) -> -spec handle_info(term(), state()) -> {'noreply', state()} | {'stop', 'normal', state()}. -handle_info({'EXIT', Pid, _Reason}, Handle) when is_pid(Pid) -> +handle_info({'EXIT', Pid, _Reason}, State) when is_pid(Pid) -> ets:delete(?FILE_IO_SERVER_TABLE, Pid), - {noreply, Handle}; - -handle_info({'EXIT', Handle, _Reason}, Handle) -> - error_logger:error_msg("Port controlling ~w terminated in ~w", - [?FILE_SERVER, ?MODULE]), - {stop, normal, Handle}; + {noreply, State}; handle_info(Info, State) -> error_logger:error_msg("handle_Info(~tp, _)", [Info]), @@ -254,8 +243,8 @@ handle_info(Info, State) -> -spec terminate(term(), state()) -> 'ok'. -terminate(_Reason, Handle) -> - ?PRIM_FILE:stop(Handle). +terminate(_Reason, _State) -> + ok. %%---------------------------------------------------------------------- %% Func: code_change/3 diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl index bc5b67f7bf..357e27826c 100644 --- a/lib/kernel/src/inet_int.hrl +++ b/lib/kernel/src/inet_int.hrl @@ -100,6 +100,8 @@ -define(TCP_REQ_RECV, 42). -define(TCP_REQ_UNRECV, 43). -define(TCP_REQ_SHUTDOWN, 44). +-define(TCP_REQ_SENDFILE, 45). + %% UDP and SCTP requests -define(PACKET_REQ_RECV, 60). %%-define(SCTP_REQ_LISTEN, 61). MERGED @@ -319,6 +321,12 @@ [((X) bsr 24) band 16#ff, ((X) bsr 16) band 16#ff, ((X) bsr 8) band 16#ff, (X) band 16#ff]). +-define(int64(X), + [((X) bsr 56) band 16#ff, ((X) bsr 48) band 16#ff, + ((X) bsr 40) band 16#ff, ((X) bsr 32) band 16#ff, + ((X) bsr 24) band 16#ff, ((X) bsr 16) band 16#ff, + ((X) bsr 8) band 16#ff, (X) band 16#ff]). + -define(intAID(X), % For SCTP AssocID ?int32(X)). diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 080b11fc4d..e4852a6e75 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -88,6 +88,13 @@ inet_udp, inet_sctp, pg2, + raw_file_io, + raw_file_io_compressed, + raw_file_io_deflate, + raw_file_io_delayed, + raw_file_io_inflate, + raw_file_io_list, + raw_file_io_raw, seq_trace, standard_error, wrap_log_reader]}, diff --git a/lib/kernel/src/raw_file_io.erl b/lib/kernel/src/raw_file_io.erl new file mode 100644 index 0000000000..e3c07c8f78 --- /dev/null +++ b/lib/kernel/src/raw_file_io.erl @@ -0,0 +1,75 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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(raw_file_io). + +-export([open/2]). + +open(Filename, Modes) -> + %% Layers are applied in this order, and the listed modules will call this + %% function again as necessary. eg. a raw compressed delayed file in list + %% mode will walk through [_list -> _compressed -> _delayed -> _raw]. + ModuleOrder = [{raw_file_io_list, fun match_list/1}, + {raw_file_io_compressed, fun match_compressed/1}, + {raw_file_io_delayed, fun match_delayed/1}, + {raw_file_io_raw, fun match_raw/1}], + open_1(ModuleOrder, Filename, add_implicit_modes(Modes)). +open_1([], _Filename, _Modes) -> + error(badarg); +open_1([{Module, Match} | Rest], Filename, Modes) -> + case lists:any(Match, Modes) of + true -> + {Options, ChildModes} = + lists:partition(fun(Mode) -> Match(Mode) end, Modes), + Module:open_layer(Filename, ChildModes, Options); + false -> + open_1(Rest, Filename, Modes) + end. + +%% 'read' and 'list' mode are enabled unless disabled by another option, so +%% we'll explicitly add them to avoid duplicating this logic in child layers. +add_implicit_modes(Modes0) -> + Modes1 = add_unless_matched(Modes0, fun match_writable/1, read), + add_unless_matched(Modes1, fun match_binary/1, list). +add_unless_matched(Modes, Match, Default) -> + case lists:any(Match, Modes) of + false -> [Default | Modes]; + true -> Modes + end. + +match_list(list) -> true; +match_list(_Other) -> false. + +match_compressed(compressed) -> true; +match_compressed(_Other) -> false. + +match_delayed({delayed_write, _Size, _Timeout}) -> true; +match_delayed(delayed_write) -> true; +match_delayed(_Other) -> false. + +match_raw(raw) -> true; +match_raw(_Other) -> false. + +match_writable(write) -> true; +match_writable(append) -> true; +match_writable(exclusive) -> true; +match_writable(_Other) -> false. + +match_binary(binary) -> true; +match_binary(_Other) -> false. diff --git a/lib/kernel/src/raw_file_io_compressed.erl b/lib/kernel/src/raw_file_io_compressed.erl new file mode 100644 index 0000000000..d5ab042d25 --- /dev/null +++ b/lib/kernel/src/raw_file_io_compressed.erl @@ -0,0 +1,134 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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(raw_file_io_compressed). + +-export([close/1, sync/1, datasync/1, truncate/1, advise/4, allocate/3, + position/2, write/2, pwrite/2, pwrite/3, + read_line/1, read/2, pread/2, pread/3]). + +%% OTP internal. +-export([ipread_s32bu_p32bu/3, sendfile/8]). + +-export([open_layer/3]). + +-include("file_int.hrl"). + +open_layer(Filename, Modes, Options) -> + IsAppend = lists:member(append, Modes), + IsDeflate = lists:member(write, Modes), + IsInflate = lists:member(read, Modes), + if + IsDeflate, IsInflate; IsAppend -> + {error, einval}; + IsDeflate, not IsInflate -> + start_server_module(raw_file_io_deflate, Filename, Modes, Options); + IsInflate -> + start_server_module(raw_file_io_inflate, Filename, Modes, Options) + end. + +start_server_module(Module, Filename, Modes, Options) -> + Secret = make_ref(), + case gen_statem:start(Module, {self(), Secret, Options}, []) of + {ok, Pid} -> open_next_layer(Pid, Secret, Filename, Modes); + Other -> Other + end. + +open_next_layer(Pid, Secret, Filename, Modes) -> + case gen_statem:call(Pid, {'$open', Secret, Filename, Modes}, infinity) of + ok -> + PublicFd = #file_descriptor{ + module = raw_file_io_compressed, data = {self(), Pid} }, + {ok, PublicFd}; + Other -> Other + end. + +close(Fd) -> + wrap_call(Fd, [close]). + +sync(Fd) -> + wrap_call(Fd, [sync]). +datasync(Fd) -> + wrap_call(Fd, [datasync]). + +truncate(Fd) -> + wrap_call(Fd, [truncate]). + +advise(Fd, Offset, Length, Advise) -> + wrap_call(Fd, [advise, Offset, Length, Advise]). +allocate(Fd, Offset, Length) -> + wrap_call(Fd, [allocate, Offset, Length]). + +position(Fd, Mark) -> + wrap_call(Fd, [position, Mark]). + +write(Fd, IOData) -> + try + CompactedData = erlang:iolist_to_iovec(IOData), + wrap_call(Fd, [write, CompactedData]) + catch + error:badarg -> {error, badarg} + end. + +pwrite(Fd, Offset, IOData) -> + try + CompactedData = erlang:iolist_to_iovec(IOData), + wrap_call(Fd, [pwrite, Offset, CompactedData]) + catch + error:badarg -> {error, badarg} + end. +pwrite(Fd, LocBytes) -> + try + CompactedLocBytes = + [ {Offset, erlang:iolist_to_iovec(IOData)} || + {Offset, IOData} <- LocBytes ], + wrap_call(Fd, [pwrite, CompactedLocBytes]) + catch + error:badarg -> {error, badarg} + end. + +read_line(Fd) -> + wrap_call(Fd, [read_line]). +read(Fd, Size) -> + wrap_call(Fd, [read, Size]). +pread(Fd, Offset, Size) -> + wrap_call(Fd, [pread, Offset, Size]). +pread(Fd, LocNums) -> + wrap_call(Fd, [pread, LocNums]). + +ipread_s32bu_p32bu(Fd, Offset, MaxSize) -> + wrap_call(Fd, [ipread_s32bu_p32bu, Offset, MaxSize]). + +sendfile(_,_,_,_,_,_,_,_) -> + {error, enotsup}. + +wrap_call(Fd, Command) -> + {_Owner, Pid} = get_fd_data(Fd), + try gen_statem:call(Pid, Command, infinity) of + Result -> Result + catch + exit:{noproc, _StackTrace} -> {error, einval} + end. + +get_fd_data(#file_descriptor{ data = Data }) -> + {Owner, _ServerPid} = Data, + case self() of + Owner -> Data; + _ -> error(not_on_controlling_process) + end. diff --git a/lib/kernel/src/raw_file_io_deflate.erl b/lib/kernel/src/raw_file_io_deflate.erl new file mode 100644 index 0000000000..acfc546743 --- /dev/null +++ b/lib/kernel/src/raw_file_io_deflate.erl @@ -0,0 +1,159 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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(raw_file_io_deflate). + +-behavior(gen_statem). + +-export([init/1, callback_mode/0, terminate/3]). +-export([opening/3, opened/3]). + +-include("file_int.hrl"). + +-define(GZIP_WBITS, 16 + 15). + +callback_mode() -> state_functions. + +init({Owner, Secret, [compressed]}) -> + Monitor = monitor(process, Owner), + Z = zlib:open(), + ok = zlib:deflateInit(Z, default, deflated, ?GZIP_WBITS, 8, default), + Data = + #{ owner => Owner, + monitor => Monitor, + secret => Secret, + position => 0, + zlib => Z }, + {ok, opening, Data}. + +opening({call, From}, {'$open', Secret, Filename, Modes}, #{ secret := Secret } = Data) -> + case raw_file_io:open(Filename, Modes) of + {ok, PrivateFd} -> + NewData = Data#{ handle => PrivateFd }, + {next_state, opened, NewData, [{reply, From, ok}]}; + Other -> + {stop_and_reply, normal, [{reply, From, Other}]} + end; +opening(_Event, _Contents, _Data) -> + {keep_state_and_data, [postpone]}. + +%% + +opened(info, {'DOWN', Monitor, process, _Owner, Reason}, #{ monitor := Monitor } = Data) -> + if + Reason =/= kill -> flush_deflate_state(Data); + Reason =:= kill -> ignored + end, + {stop, shutdown}; + +opened(info, _Message, _Data) -> + keep_state_and_data; + +opened({call, {Owner, _Tag} = From}, [close], #{ owner := Owner } = Data) -> + #{ handle := PrivateFd } = Data, + Response = + case flush_deflate_state(Data) of + ok -> ?CALL_FD(PrivateFd, close, []); + Other -> Other + end, + {stop_and_reply, normal, [{reply, From, Response}]}; + +opened({call, {Owner, _Tag} = From}, [position, Mark], #{ owner := Owner } = Data) -> + case position(Data, Mark) of + {ok, NewData, Result} -> + Response = {ok, Result}, + {keep_state, NewData, [{reply, From, Response}]}; + Other -> + {keep_state_and_data, [{reply, From, Other}]} + end; + +opened({call, {Owner, _Tag} = From}, [write, IOVec], #{ owner := Owner } = Data) -> + case write(Data, IOVec) of + {ok, NewData} -> {keep_state, NewData, [{reply, From, ok}]}; + Other -> {keep_state_and_data, [{reply, From, Other}]} + end; + +opened({call, {Owner, _Tag} = From}, [read, _Size], #{ owner := Owner }) -> + Response = {error, ebadf}, + {keep_state_and_data, [{reply, From, Response}]}; + +opened({call, {Owner, _Tag} = From}, [read_line], #{ owner := Owner }) -> + Response = {error, ebadf}, + {keep_state_and_data, [{reply, From, Response}]}; + +opened({call, {Owner, _Tag} = From}, _Command, #{ owner := Owner }) -> + Response = {error, enotsup}, + {keep_state_and_data, [{reply, From, Response}]}; + +opened({call, _From}, _Command, _Data) -> + %% The client functions filter this out, so we'll crash if the user does + %% anything stupid on purpose. + {shutdown, protocol_violation}; + +opened(_Event, _Request, _Data) -> + keep_state_and_data. + +write(Data, IOVec) -> + #{ handle := PrivateFd, position := Position, zlib := Z } = Data, + UncompressedSize = iolist_size(IOVec), + case ?CALL_FD(PrivateFd, write, [zlib:deflate(Z, IOVec)]) of + ok -> {ok, Data#{ position := (Position + UncompressedSize) }}; + Other -> Other + end. + +%% +%% We support "seeking" forward as long as it isn't relative to EOF. +%% +%% Seeking is a bit of a misnomer as it's really just compressing zeroes until +%% we reach the desired point, but it has always behaved like this. +%% + +position(Data, Mark) when is_atom(Mark) -> + position(Data, {Mark, 0}); +position(Data, Offset) when is_integer(Offset) -> + position(Data, {bof, Offset}); +position(Data, {bof, Offset}) when is_integer(Offset) -> + position_1(Data, Offset); +position(Data, {cur, Offset}) when is_integer(Offset) -> + #{ position := Position } = Data, + position_1(Data, Position + Offset); +position(_Data, {eof, Offset}) when is_integer(Offset) -> + {error, einval}; +position(_Data, _Any) -> + {error, badarg}. + +position_1(#{ position := Desired } = Data, Desired) -> + {ok, Data, Desired}; +position_1(#{ position := Current } = Data, Desired) when Current < Desired -> + BytesToWrite = min(Desired - Current, 4 bsl 20), + case write(Data, <<0:(BytesToWrite)/unit:8>>) of + {ok, NewData} -> position_1(NewData, Desired); + Other -> Other + end; +position_1(#{ position := Current }, Desired) when Current > Desired -> + {error, einval}. + +flush_deflate_state(#{ handle := PrivateFd, zlib := Z }) -> + case ?CALL_FD(PrivateFd, write, [zlib:deflate(Z, [], finish)]) of + ok -> ok; + Other -> Other + end. + +terminate(_Reason, _State, _Data) -> + ok. diff --git a/lib/kernel/src/raw_file_io_delayed.erl b/lib/kernel/src/raw_file_io_delayed.erl new file mode 100644 index 0000000000..d2ad7550a1 --- /dev/null +++ b/lib/kernel/src/raw_file_io_delayed.erl @@ -0,0 +1,320 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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(raw_file_io_delayed). + +-behavior(gen_statem). + +-export([close/1, sync/1, datasync/1, truncate/1, advise/4, allocate/3, + position/2, write/2, pwrite/2, pwrite/3, + read_line/1, read/2, pread/2, pread/3]). + +%% OTP internal. +-export([ipread_s32bu_p32bu/3, sendfile/8]). + +-export([open_layer/3]). + +-export([init/1, callback_mode/0, terminate/3]). +-export([opening/3, opened/3]). + +-include("file_int.hrl"). + +open_layer(Filename, Modes, Options) -> + Secret = make_ref(), + case gen_statem:start(?MODULE, {self(), Secret, Options}, []) of + {ok, Pid} -> + gen_statem:call(Pid, {'$open', Secret, Filename, Modes}, infinity); + Other -> + Other + end. + +callback_mode() -> state_functions. + +init({Owner, Secret, Options}) -> + Monitor = monitor(process, Owner), + Defaults = + #{ owner => Owner, + monitor => Monitor, + secret => Secret, + timer => none, + pid => self(), + buffer => prim_buffer:new(), + delay_size => 64 bsl 10, + delay_time => 2000 }, + Data = fill_delay_values(Defaults, Options), + {ok, opening, Data}. + +fill_delay_values(Data, []) -> + Data; +fill_delay_values(Data, [{delayed_write, Size, Time} | Options]) -> + fill_delay_values(Data#{ delay_size => Size, delay_time => Time }, Options); +fill_delay_values(Data, [_ | Options]) -> + fill_delay_values(Data, Options). + +opening({call, From}, {'$open', Secret, Filename, Modes}, #{ secret := Secret } = Data) -> + case raw_file_io:open(Filename, Modes) of + {ok, PrivateFd} -> + PublicData = maps:with([owner, buffer, delay_size, pid], Data), + PublicFd = #file_descriptor{ module = ?MODULE, data = PublicData }, + + NewData = Data#{ handle => PrivateFd }, + Response = {ok, PublicFd}, + {next_state, opened, NewData, [{reply, From, Response}]}; + Other -> + {stop_and_reply, normal, [{reply, From, Other}]} + end; +opening(_Event, _Contents, _Data) -> + {keep_state_and_data, [postpone]}. + +%% + +opened(info, {'$timed_out', Secret}, #{ secret := Secret } = Data) -> + %% If the user writes something at this exact moment, the flush will fail + %% and the timer won't reset on the next write since the buffer won't be + %% empty (Unless we collided on a flush). We therefore reset the timeout to + %% ensure that data won't sit idle for extended periods of time. + case try_flush_write_buffer(Data) of + busy -> gen_statem:cast(self(), '$reset_timeout'); + ok -> ok + end, + {keep_state, Data#{ timer => none }, []}; + +opened(info, {'DOWN', Monitor, process, _Owner, Reason}, #{ monitor := Monitor } = Data) -> + if + Reason =/= kill -> try_flush_write_buffer(Data); + Reason =:= kill -> ignored + end, + {stop, shutdown}; + +opened(info, _Message, _Data) -> + keep_state_and_data; + +opened({call, {Owner, _Tag} = From}, [close], #{ owner := Owner } = Data) -> + case flush_write_buffer(Data) of + ok -> + #{ handle := PrivateFd } = Data, + Response = ?CALL_FD(PrivateFd, close, []), + {stop_and_reply, normal, [{reply, From, Response}]}; + Other -> + {stop_and_reply, normal, [{reply, From, Other}]} + end; + +opened({call, {Owner, _Tag} = From}, '$wait', #{ owner := Owner }) -> + %% Used in write/2 to synchronize writes on lock conflicts. + {keep_state_and_data, [{reply, From, ok}]}; + +opened({call, {Owner, _Tag} = From}, '$synchronous_flush', #{ owner := Owner } = Data) -> + cancel_flush_timeout(Data), + Response = flush_write_buffer(Data), + {keep_state_and_data, [{reply, From, Response}]}; + +opened({call, {Owner, _Tag} = From}, Command, #{ owner := Owner } = Data) -> + Response = + case flush_write_buffer(Data) of + ok -> dispatch_command(Data, Command); + Other -> Other + end, + {keep_state_and_data, [{reply, From, Response}]}; + +opened({call, _From}, _Command, _Data) -> + %% The client functions filter this out, so we'll crash if the user does + %% anything stupid on purpose. + {shutdown, protocol_violation}; + +opened(cast, '$reset_timeout', #{ delay_time := Timeout, secret := Secret } = Data) -> + cancel_flush_timeout(Data), + Timer = erlang:send_after(Timeout, self(), {'$timed_out', Secret}), + {keep_state, Data#{ timer => Timer }, []}; + +opened(cast, _Message, _Data) -> + {keep_state_and_data, []}. + +dispatch_command(Data, [Function | Args]) -> + #{ handle := Handle } = Data, + Module = Handle#file_descriptor.module, + apply(Module, Function, [Handle | Args]). + +cancel_flush_timeout(#{ timer := none }) -> + ok; +cancel_flush_timeout(#{ timer := Timer }) -> + _ = erlang:cancel_timer(Timer, [{async, true}]), + ok. + +try_flush_write_buffer(#{ buffer := Buffer, handle := PrivateFd }) -> + case prim_buffer:try_lock(Buffer) of + acquired -> + flush_write_buffer_1(Buffer, PrivateFd), + prim_buffer:unlock(Buffer), + ok; + busy -> + busy + end. + +%% This is only safe to use when there is no chance of conflict with the owner +%% process, or in other words, "during synchronous calls outside of the locked +%% section of write/2" +flush_write_buffer(#{ buffer := Buffer, handle := PrivateFd }) -> + acquired = prim_buffer:try_lock(Buffer), + Result = flush_write_buffer_1(Buffer, PrivateFd), + prim_buffer:unlock(Buffer), + Result. + +flush_write_buffer_1(Buffer, PrivateFd) -> + case prim_buffer:size(Buffer) of + Size when Size > 0 -> + ?CALL_FD(PrivateFd, write, [prim_buffer:read_iovec(Buffer, Size)]); + 0 -> + ok + end. + +terminate(_Reason, _State, _Data) -> + ok. + +%% Client functions + +write(Fd, IOData) -> + try + enqueue_write(Fd, erlang:iolist_to_iovec(IOData)) + catch + error:badarg -> {error, badarg} + end. +enqueue_write(_Fd, []) -> + ok; +enqueue_write(Fd, IOVec) -> + %% get_fd_data will reject everyone except the process that opened the Fd, + %% so we can't race with anyone except the wrapper process. + #{ delay_size := DelaySize, + buffer := Buffer, + pid := Pid } = get_fd_data(Fd), + case prim_buffer:try_lock(Buffer) of + acquired -> + %% (The wrapper process will exit without flushing if we're killed + %% while holding the lock). + enqueue_write_locked(Pid, Buffer, DelaySize, IOVec); + busy -> + %% This can only happen while we're processing a timeout in the + %% wrapper process, so we perform a bogus call to get a completion + %% notification before trying again. + gen_statem:call(Pid, '$wait'), + enqueue_write(Fd, IOVec) + end. +enqueue_write_locked(Pid, Buffer, DelaySize, IOVec) -> + %% The synchronous operations (write, forced flush) are safe since we're + %% running on the only process that can fill the buffer; a timeout being + %% processed just before $synchronous_flush will cause the flush to nop, + %% and a timeout sneaking in just before a synchronous write won't do + %% anything since the buffer is guaranteed to be empty at that point. + BufSize = prim_buffer:size(Buffer), + case is_iovec_smaller_than(IOVec, DelaySize - BufSize) of + true when BufSize > 0 -> + prim_buffer:write(Buffer, IOVec), + prim_buffer:unlock(Buffer); + true -> + prim_buffer:write(Buffer, IOVec), + prim_buffer:unlock(Buffer), + gen_statem:cast(Pid, '$reset_timeout'); + false when BufSize > 0 -> + prim_buffer:write(Buffer, IOVec), + prim_buffer:unlock(Buffer), + gen_statem:call(Pid, '$synchronous_flush'); + false -> + prim_buffer:unlock(Buffer), + gen_statem:call(Pid, [write, IOVec]) + end. + +%% iolist_size/1 will always look through the entire list to get a precise +%% amount, which is pretty inefficient since we only need to know whether we've +%% hit the buffer threshold or not. +%% +%% We only handle the binary case since write/2 forcibly translates input to +%% erlang:iovec(). +is_iovec_smaller_than(IOVec, Max) -> + is_iovec_smaller_than_1(IOVec, Max, 0). +is_iovec_smaller_than_1(_IOVec, Max, Acc) when Acc >= Max -> + false; +is_iovec_smaller_than_1([], _Max, _Acc) -> + true; +is_iovec_smaller_than_1([Binary | Rest], Max, Acc) when is_binary(Binary) -> + is_iovec_smaller_than_1(Rest, Max, Acc + byte_size(Binary)). + +close(Fd) -> + wrap_call(Fd, [close]). + +sync(Fd) -> + wrap_call(Fd, [sync]). +datasync(Fd) -> + wrap_call(Fd, [datasync]). + +truncate(Fd) -> + wrap_call(Fd, [truncate]). + +advise(Fd, Offset, Length, Advise) -> + wrap_call(Fd, [advise, Offset, Length, Advise]). +allocate(Fd, Offset, Length) -> + wrap_call(Fd, [allocate, Offset, Length]). + +position(Fd, Mark) -> + wrap_call(Fd, [position, Mark]). + +pwrite(Fd, Offset, IOData) -> + try + CompactedData = erlang:iolist_to_iovec(IOData), + wrap_call(Fd, [pwrite, Offset, CompactedData]) + catch + error:badarg -> {error, badarg} + end. +pwrite(Fd, LocBytes) -> + try + CompactedLocBytes = + [ {Offset, erlang:iolist_to_iovec(IOData)} || + {Offset, IOData} <- LocBytes ], + wrap_call(Fd, [pwrite, CompactedLocBytes]) + catch + error:badarg -> {error, badarg} + end. + +read_line(Fd) -> + wrap_call(Fd, [read_line]). +read(Fd, Size) -> + wrap_call(Fd, [read, Size]). +pread(Fd, Offset, Size) -> + wrap_call(Fd, [pread, Offset, Size]). +pread(Fd, LocNums) -> + wrap_call(Fd, [pread, LocNums]). + +ipread_s32bu_p32bu(Fd, Offset, MaxSize) -> + wrap_call(Fd, [ipread_s32bu_p32bu, Offset, MaxSize]). + +sendfile(_,_,_,_,_,_,_,_) -> + {error, enotsup}. + +wrap_call(Fd, Command) -> + #{ pid := Pid } = get_fd_data(Fd), + try gen_statem:call(Pid, Command, infinity) of + Result -> Result + catch + exit:{noproc, _StackTrace} -> {error, einval} + end. + +get_fd_data(#file_descriptor{ data = Data }) -> + #{ owner := Owner } = Data, + case self() of + Owner -> Data; + _ -> error(not_on_controlling_process) + end. diff --git a/lib/kernel/src/raw_file_io_inflate.erl b/lib/kernel/src/raw_file_io_inflate.erl new file mode 100644 index 0000000000..7e9780310c --- /dev/null +++ b/lib/kernel/src/raw_file_io_inflate.erl @@ -0,0 +1,261 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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(raw_file_io_inflate). + +-behavior(gen_statem). + +-export([init/1, callback_mode/0, terminate/3]). +-export([opening/3, opened_gzip/3, opened_passthrough/3]). + +-include("file_int.hrl"). + +-define(INFLATE_CHUNK_SIZE, (1 bsl 10)). +-define(GZIP_WBITS, (16 + 15)). + +callback_mode() -> state_functions. + +init({Owner, Secret, [compressed]}) -> + Monitor = monitor(process, Owner), + %% We're using the undocumented inflateInit/3 to open the stream in + %% 'reset mode', which resets the inflate state at the end of every stream, + %% allowing us to read concatenated gzip files. + Z = zlib:open(), + ok = zlib:inflateInit(Z, ?GZIP_WBITS, reset), + Data = + #{ owner => Owner, + monitor => Monitor, + secret => Secret, + position => 0, + buffer => prim_buffer:new(), + zlib => Z }, + {ok, opening, Data}. + +%% The old driver fell back to plain reads if the file didn't start with the +%% magic gzip bytes. +choose_decompression_state(PrivateFd) -> + State = + case ?CALL_FD(PrivateFd, read, [2]) of + {ok, <<16#1F, 16#8B>>} -> opened_gzip; + _Other -> opened_passthrough + end, + {ok, 0} = ?CALL_FD(PrivateFd, position, [0]), + State. + +opening({call, From}, {'$open', Secret, Filename, Modes}, #{ secret := Secret } = Data) -> + case raw_file_io:open(Filename, Modes) of + {ok, PrivateFd} -> + NextState = choose_decompression_state(PrivateFd), + NewData = Data#{ handle => PrivateFd }, + {next_state, NextState, NewData, [{reply, From, ok}]}; + Other -> + {stop_and_reply, normal, [{reply, From, Other}]} + end; +opening(_Event, _Contents, _Data) -> + {keep_state_and_data, [postpone]}. + +internal_close(From, Data) -> + #{ handle := PrivateFd } = Data, + Response = ?CALL_FD(PrivateFd, close, []), + {stop_and_reply, normal, [{reply, From, Response}]}. + +opened_passthrough(info, {'DOWN', Monitor, process, _Owner, _Reason}, #{ monitor := Monitor }) -> + {stop, shutdown}; + +opened_passthrough(info, _Message, _Data) -> + keep_state_and_data; + +opened_passthrough({call, {Owner, _Tag} = From}, [close], #{ owner := Owner } = Data) -> + internal_close(From, Data); + +opened_passthrough({call, {Owner, _Tag} = From}, [Method | Args], #{ owner := Owner } = Data) -> + #{ handle := PrivateFd } = Data, + Response = ?CALL_FD(PrivateFd, Method, Args), + {keep_state_and_data, [{reply, From, Response}]}; + +opened_passthrough({call, _From}, _Command, _Data) -> + %% The client functions filter this out, so we'll crash if the user does + %% anything stupid on purpose. + {shutdown, protocol_violation}; + +opened_passthrough(_Event, _Request, _Data) -> + keep_state_and_data. + +%% + +opened_gzip(info, {'DOWN', Monitor, process, _Owner, _Reason}, #{ monitor := Monitor }) -> + {stop, shutdown}; + +opened_gzip(info, _Message, _Data) -> + keep_state_and_data; + +opened_gzip({call, {Owner, _Tag} = From}, [close], #{ owner := Owner } = Data) -> + internal_close(From, Data); + +opened_gzip({call, {Owner, _Tag} = From}, [position, Mark], #{ owner := Owner } = Data) -> + case position(Data, Mark) of + {ok, NewData, Result} -> + Response = {ok, Result}, + {keep_state, NewData, [{reply, From, Response}]}; + Other -> + {keep_state_and_data, [{reply, From, Other}]} + end; + +opened_gzip({call, {Owner, _Tag} = From}, [read, Size], #{ owner := Owner } = Data) -> + case read(Data, Size) of + {ok, NewData, Result} -> + Response = {ok, Result}, + {keep_state, NewData, [{reply, From, Response}]}; + Other -> + {keep_state_and_data, [{reply, From, Other}]} + end; + +opened_gzip({call, {Owner, _Tag} = From}, [read_line], #{ owner := Owner } = Data) -> + case read_line(Data) of + {ok, NewData, Result} -> + Response = {ok, Result}, + {keep_state, NewData, [{reply, From, Response}]}; + Other -> + {keep_state_and_data, [{reply, From, Other}]} + end; + +opened_gzip({call, {Owner, _Tag} = From}, [write, _IOData], #{ owner := Owner }) -> + Response = {error, ebadf}, + {keep_state_and_data, [{reply, From, Response}]}; + +opened_gzip({call, {Owner, _Tag} = From}, _Request, #{ owner := Owner }) -> + Response = {error, enotsup}, + {keep_state_and_data, [{reply, From, Response}]}; + +opened_gzip({call, _From}, _Request, _Data) -> + %% The client functions filter this out, so we'll crash if the user does + %% anything stupid on purpose. + {shutdown, protocol_violation}; + +opened_gzip(_Event, _Request, _Data) -> + keep_state_and_data. + +%% + +read(#{ buffer := Buffer } = Data, Size) -> + try read_1(Data, Buffer, prim_buffer:size(Buffer), Size) of + Result -> Result + catch + error:badarg -> {error, badarg}; + error:_ -> {error, eio} + end. +read_1(Data, Buffer, BufferSize, ReadSize) when BufferSize >= ReadSize -> + #{ position := Position } = Data, + Decompressed = prim_buffer:read(Buffer, ReadSize), + {ok, Data#{ position => (Position + ReadSize) }, Decompressed}; +read_1(Data, Buffer, BufferSize, ReadSize) when BufferSize < ReadSize -> + #{ handle := PrivateFd } = Data, + case ?CALL_FD(PrivateFd, read, [?INFLATE_CHUNK_SIZE]) of + {ok, Compressed} -> + #{ zlib := Z } = Data, + Uncompressed = erlang:iolist_to_iovec(zlib:inflate(Z, Compressed)), + prim_buffer:write(Buffer, Uncompressed), + read_1(Data, Buffer, prim_buffer:size(Buffer), ReadSize); + eof when BufferSize > 0 -> + read_1(Data, Buffer, BufferSize, BufferSize); + Other -> + Other + end. + +read_line(#{ buffer := Buffer } = Data) -> + try read_line_1(Data, Buffer, prim_buffer:find_byte_index(Buffer, $\n)) of + {ok, NewData, Decompressed} -> {ok, NewData, Decompressed}; + Other -> Other + catch + error:badarg -> {error, badarg}; + error:_ -> {error, eio} + end. + +read_line_1(Data, Buffer, not_found) -> + #{ handle := PrivateFd, zlib := Z } = Data, + case ?CALL_FD(PrivateFd, read, [?INFLATE_CHUNK_SIZE]) of + {ok, Compressed} -> + Uncompressed = erlang:iolist_to_iovec(zlib:inflate(Z, Compressed)), + prim_buffer:write(Buffer, Uncompressed), + read_line_1(Data, Buffer, prim_buffer:find_byte_index(Buffer, $\n)); + eof -> + case prim_buffer:size(Buffer) of + Size when Size > 0 -> {ok, prim_buffer:read(Buffer, Size)}; + Size when Size =:= 0 -> eof + end; + Error -> + Error + end; +read_line_1(Data, Buffer, {ok, LFIndex}) -> + %% Translate CRLF into just LF, completely ignoring which encoding is used, + %% but treat the file position as including CR. + #{ position := Position } = Data, + NewData = Data#{ position => (Position + LFIndex + 1) }, + CRIndex = (LFIndex - 1), + TranslatedLine = + case prim_buffer:read(Buffer, LFIndex + 1) of + <> -> <>; + Line -> Line + end, + {ok, NewData, TranslatedLine}. + +%% +%% We support seeking in both directions as long as it isn't relative to EOF. +%% +%% Seeking backwards is extremely inefficient since we have to seek to the very +%% beginning and then decompress up to the desired point. +%% + +position(Data, Mark) when is_atom(Mark) -> + position(Data, {Mark, 0}); +position(Data, Offset) when is_integer(Offset) -> + position(Data, {bof, Offset}); +position(Data, {bof, Offset}) when is_integer(Offset) -> + position_1(Data, Offset); +position(Data, {cur, Offset}) when is_integer(Offset) -> + #{ position := Position } = Data, + position_1(Data, Position + Offset); +position(_Data, {eof, Offset}) when is_integer(Offset) -> + {error, einval}; +position(_Data, _Other) -> + {error, badarg}. + +position_1(_Data, Desired) when Desired < 0 -> + {error, einval}; +position_1(#{ position := Desired } = Data, Desired) -> + {ok, Data, Desired}; +position_1(#{ position := Current } = Data, Desired) when Current < Desired -> + case read(Data, min(Desired - Current, ?INFLATE_CHUNK_SIZE)) of + {ok, NewData, _Data} -> position_1(NewData, Desired); + eof -> {ok, Data, Current}; + Other -> Other + end; +position_1(#{ position := Current } = Data, Desired) when Current > Desired -> + #{ handle := PrivateFd, buffer := Buffer, zlib := Z } = Data, + case ?CALL_FD(PrivateFd, position, [bof]) of + {ok, 0} -> + ok = zlib:inflateReset(Z), + prim_buffer:wipe(Buffer), + position_1(Data#{ position => 0 }, Desired); + Other -> + Other + end. + +terminate(_Reason, _State, _Data) -> + ok. diff --git a/lib/kernel/src/raw_file_io_list.erl b/lib/kernel/src/raw_file_io_list.erl new file mode 100644 index 0000000000..2e16e63f0e --- /dev/null +++ b/lib/kernel/src/raw_file_io_list.erl @@ -0,0 +1,128 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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(raw_file_io_list). + +-export([close/1, sync/1, datasync/1, truncate/1, advise/4, allocate/3, + position/2, write/2, pwrite/2, pwrite/3, + read_line/1, read/2, pread/2, pread/3]). + +%% OTP internal. +-export([ipread_s32bu_p32bu/3, sendfile/8]). + +-export([open_layer/3]). + +-include("file_int.hrl"). + +open_layer(Filename, Modes, [list]) -> + case raw_file_io:open(Filename, [binary | Modes]) of + {ok, PrivateFd} -> {ok, make_public_fd(PrivateFd, Modes)}; + Other -> Other + end. + +%% We can skip wrapping the file if it's write-only since only read operations +%% are affected by list mode. Since raw_file_io fills in all implicit options +%% for us, all we need to do is check whether 'read' is among them. +make_public_fd(PrivateFd, Modes) -> + case lists:member(read, Modes) of + true -> #file_descriptor{ module = ?MODULE, data = PrivateFd }; + false -> PrivateFd + end. + +close(Fd) -> + PrivateFd = Fd#file_descriptor.data, + ?CALL_FD(PrivateFd, close, []). + +sync(Fd) -> + PrivateFd = Fd#file_descriptor.data, + ?CALL_FD(PrivateFd, sync, []). +datasync(Fd) -> + PrivateFd = Fd#file_descriptor.data, + ?CALL_FD(PrivateFd, datasync, []). + +truncate(Fd) -> + PrivateFd = Fd#file_descriptor.data, + ?CALL_FD(PrivateFd, truncate, []). + +advise(Fd, Offset, Length, Advise) -> + PrivateFd = Fd#file_descriptor.data, + ?CALL_FD(PrivateFd, advise, [Offset, Length, Advise]). +allocate(Fd, Offset, Length) -> + PrivateFd = Fd#file_descriptor.data, + ?CALL_FD(PrivateFd, allocate, [Offset, Length]). + +position(Fd, Mark) -> + PrivateFd = Fd#file_descriptor.data, + ?CALL_FD(PrivateFd, position, [Mark]). + +write(Fd, IOData) -> + PrivateFd = Fd#file_descriptor.data, + ?CALL_FD(PrivateFd, write, [IOData]). + +pwrite(Fd, Offset, IOData) -> + PrivateFd = Fd#file_descriptor.data, + ?CALL_FD(PrivateFd, pwrite, [Offset, IOData]). +pwrite(Fd, LocBytes) -> + PrivateFd = Fd#file_descriptor.data, + ?CALL_FD(PrivateFd, pwrite, [LocBytes]). + +read_line(Fd) -> + PrivateFd = Fd#file_descriptor.data, + case ?CALL_FD(PrivateFd, read_line, []) of + {ok, Binary} -> {ok, binary_to_list(Binary)}; + Other -> Other + end. +read(Fd, Size) -> + PrivateFd = Fd#file_descriptor.data, + case ?CALL_FD(PrivateFd, read, [Size]) of + {ok, Binary} -> {ok, binary_to_list(Binary)}; + Other -> Other + end. +pread(Fd, Offset, Size) -> + PrivateFd = Fd#file_descriptor.data, + case ?CALL_FD(PrivateFd, pread, [Offset, Size]) of + {ok, Binary} -> {ok, binary_to_list(Binary)}; + Other -> Other + end. +pread(Fd, LocNums) -> + PrivateFd = Fd#file_descriptor.data, + case ?CALL_FD(PrivateFd, pread, [LocNums]) of + {ok, LocResults} -> + TranslatedResults = + [ case Result of + Result when is_binary(Result) -> binary_to_list(Result); + eof -> eof + end || Result <- LocResults ], + {ok, TranslatedResults}; + Other -> Other + end. + +ipread_s32bu_p32bu(Fd, Offset, MaxSize) -> + PrivateFd = Fd#file_descriptor.data, + case ?CALL_FD(PrivateFd, ipread_s32bu_p32bu, [Offset, MaxSize]) of + {ok, {Size, Pointer, Binary}} when is_binary(Binary) -> + {ok, {Size, Pointer, binary_to_list(Binary)}}; + Other -> + Other + end. + +sendfile(Fd, Dest, Offset, Bytes, ChunkSize, Headers, Trailers, Flags) -> + Args = [Dest, Offset, Bytes, ChunkSize, Headers, Trailers, Flags], + PrivateFd = Fd#file_descriptor.data, + ?CALL_FD(PrivateFd, sendfile, Args). diff --git a/lib/kernel/src/raw_file_io_raw.erl b/lib/kernel/src/raw_file_io_raw.erl new file mode 100644 index 0000000000..9a9fe78eb1 --- /dev/null +++ b/lib/kernel/src/raw_file_io_raw.erl @@ -0,0 +1,25 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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(raw_file_io_raw). + +-export([open_layer/3]). + +open_layer(Filename, Modes, [raw]) -> + prim_file:open(Filename, [raw | Modes]). -- cgit v1.2.3 From 26b39c8914cb030886ddfbc3ce4b938dbd5bafc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Wed, 11 Oct 2017 10:01:53 +0200 Subject: Account for new behavior in tests that touch prim_file This also hides the module behind ?PRIM_FILE to make testing new implementations less painful. --- lib/kernel/test/erl_prim_loader_SUITE.erl | 3 +- lib/kernel/test/file_SUITE.erl | 18 +- lib/kernel/test/file_name_SUITE.erl | 26 +- lib/kernel/test/prim_file_SUITE.erl | 722 +++++++++--------------------- lib/kernel/test/sendfile_SUITE.erl | 9 +- 5 files changed, 227 insertions(+), 551 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/erl_prim_loader_SUITE.erl b/lib/kernel/test/erl_prim_loader_SUITE.erl index b6417210b9..3502a4ad08 100644 --- a/lib/kernel/test/erl_prim_loader_SUITE.erl +++ b/lib/kernel/test/erl_prim_loader_SUITE.erl @@ -33,6 +33,7 @@ primary_archive/1, virtual_dir_in_archive/1, get_modules/1]). +-define(PRIM_FILE, prim_file). %%----------------------------------------------------------------- %% Test suite for erl_prim_loader. (Most code is run during system start/stop.) @@ -461,7 +462,7 @@ primary_archive(Config) when is_list(Config) -> %% Set primary archive ExpectedEbins = [Archive, DictDir ++ "/ebin", DummyDir ++ "/ebin"], io:format("ExpectedEbins: ~p\n", [ExpectedEbins]), - {ok, FileInfo} = prim_file:read_file_info(Archive), + {ok, FileInfo} = ?PRIM_FILE:read_file_info(Archive), {ok, Ebins} = rpc:call(Node, erl_prim_loader, set_primary_archive, [Archive, ArchiveBin, FileInfo, fun escript:parse_file/1]), diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 119e1f24bb..69cf8274a2 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -39,6 +39,8 @@ -define(FILE_FIN_PER_TESTCASE(Config), Config). -endif. +-define(PRIM_FILE, prim_file). + -module(?FILE_SUITE). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, @@ -3934,7 +3936,7 @@ read_line_create_files(TestData) -> read_line_remove_files(TestData) -> [ file:delete(File) || {_Function,File,_,_} <- TestData ]. -%% read_line with prim_file. +%% read_line with ?PRIM_FILE. read_line_1(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), All = read_line_testdata(PrivDir), @@ -4103,9 +4105,9 @@ read_line_create7(Filename) -> file:close(F). read_line_all(Filename) -> - {ok,F} = prim_file:open(Filename,[read,binary]), + {ok,F} = ?PRIM_FILE:open(Filename,[read,binary]), X=read_rl_lines(F), - prim_file:close(F), + ?PRIM_FILE:close(F), Bin = list_to_binary([B || {ok,B} <- X]), Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]), "\r\n","\n",[global,{return,binary}]), @@ -4138,7 +4140,7 @@ read_line_all4(Filename) -> {length(X),Bin}. read_rl_lines(F) -> - case prim_file:read_line(F) of + case ?PRIM_FILE:read_line(F) of eof -> []; {error,X} -> @@ -4158,9 +4160,9 @@ read_rl_lines2(F) -> end. read_line_all_alternating(Filename) -> - {ok,F} = prim_file:open(Filename,[read,binary]), + {ok,F} = ?PRIM_FILE:open(Filename,[read,binary]), X=read_rl_lines(F,true), - prim_file:close(F), + ?PRIM_FILE:close(F), Bin = list_to_binary([B || {ok,B} <- X]), Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]), "\r\n","\n",[global,{return,binary}]), @@ -4194,8 +4196,8 @@ read_line_all_alternating4(Filename) -> read_rl_lines(F,Alternate) -> case begin case Alternate of - true -> prim_file:read(F,1); - false -> prim_file:read_line(F) + true -> ?PRIM_FILE:read(F,1); + false -> ?PRIM_FILE:read_line(F) end end of eof -> diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl index f23529fec9..3afc647081 100644 --- a/lib/kernel/test/file_name_SUITE.erl +++ b/lib/kernel/test/file_name_SUITE.erl @@ -77,6 +77,7 @@ init_per_testcase/2, end_per_testcase/2]). -export([normal/1,icky/1,very_icky/1,normalize/1,home_dir/1]). +-define(PRIM_FILE, prim_file). init_per_testcase(_Func, Config) -> Config. @@ -131,7 +132,7 @@ home_dir(Config) when is_list(Config) -> os:putenv("HOME",NewHome), {"HOME",Save}; _ -> - rm_rf(prim_file,NewHome), + rm_rf(?PRIM_FILE,NewHome), throw(unsupported_os) end, try @@ -145,7 +146,7 @@ home_dir(Config) when is_list(Config) -> _ -> os:putenv(SaveOldName,SaveOldValue) end, - rm_rf(prim_file,NewHome) + rm_rf(?PRIM_FILE,NewHome) end catch throw:need_unicode_mode -> @@ -190,7 +191,7 @@ normal(Config) when is_list(Config) -> try Priv = proplists:get_value(priv_dir, Config), file:set_cwd(Priv), - ok = check_normal(prim_file), + ok = check_normal(?PRIM_FILE), ok = check_normal(file), %% If all is good, delete dir again (avoid hanging dir on windows) rm_rf(file,"normal_dir"), @@ -210,7 +211,7 @@ icky(Config) when is_list(Config) -> try Priv = proplists:get_value(priv_dir, Config), file:set_cwd(Priv), - ok = check_icky(prim_file), + ok = check_icky(?PRIM_FILE), ok = check_icky(file), %% If all is good, delete dir again (avoid hanging dir on windows) rm_rf(file,"icky_dir"), @@ -229,7 +230,7 @@ very_icky(Config) when is_list(Config) -> try Priv = proplists:get_value(priv_dir, Config), file:set_cwd(Priv), - case check_very_icky(prim_file) of + case check_very_icky(?PRIM_FILE) of need_unicode_mode -> {skipped,"VM needs to be started in Unicode filename mode"}; ok -> @@ -291,11 +292,6 @@ check_normal(Mod) -> {error,enotsup} -> ok end, - [ begin - {ok, FD} = Mod:open(Name,[read]), - {ok, Content} = Mod:read(FD,1024), - ok = file:close(FD) - end || {regular,Name,Content} <- NormalDir ], [ begin {ok, FD} = Mod:open(Name,[read,binary]), BC = list_to_binary(Content), @@ -411,11 +407,6 @@ check_icky(Mod) -> {error,enotsup} -> ok end, - [ begin - {ok, FD} = Mod:open(Name,[read]), - {ok, Content} = Mod:read(FD,1024), - ok = file:close(FD) - end || {regular,Name,Content} <- IckyDir ], [ begin {ok, FD} = Mod:open(Name,[read,binary]), BC = list_to_binary([Content]), @@ -520,11 +511,6 @@ check_very_icky(Mod) -> {error,enotsup} -> ok end, - [ begin - {ok, FD} = Mod:open(Name,[read]), - {ok, Content} = Mod:read(FD,1024), - ok = file:close(FD) - end || {regular,Name,Content} <- VeryIckyDir ], [ begin {ok, FD} = Mod:open(Name,[read,binary]), BC = list_to_binary([Content]), diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl index 2f4330c217..8a1e99b332 100644 --- a/lib/kernel/test/prim_file_SUITE.erl +++ b/lib/kernel/test/prim_file_SUITE.erl @@ -21,38 +21,23 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2, read_write_file/1, free_memory/0]). --export([cur_dir_0a/1, cur_dir_0b/1, - cur_dir_1a/1, cur_dir_1b/1, - make_del_dir_a/1, make_del_dir_b/1, - pos1/1, pos2/1]). --export([close/1, - delete_a/1, delete_b/1]). --export([ open1/1, modes/1]). --export([ - file_info_basic_file_a/1, file_info_basic_file_b/1, - file_info_basic_directory_a/1, file_info_basic_directory_b/1, - file_info_bad_a/1, file_info_bad_b/1, - file_info_times_a/1, file_info_times_b/1, - file_write_file_info_a/1, file_write_file_info_b/1, - file_read_file_info_opts/1, file_write_file_info_opts/1, - file_write_read_file_info_opts/1 - ]). --export([rename_a/1, rename_b/1, - access/1, truncate/1, datasync/1, sync/1, +-export([cur_dir_0/1, cur_dir_1/1, + make_del_dir/1, pos1/1, pos2/1]). +-export([close/1, delete/1]). +-export([open1/1, modes/1]). +-export([file_info_basic_file/1, file_info_basic_directory/1, file_info_bad/1, + file_info_times/1, file_write_file_info/1, + file_read_file_info_opts/1, file_write_file_info_opts/1, + file_write_read_file_info_opts/1]). +-export([rename/1, access/1, truncate/1, datasync/1, sync/1, read_write/1, pread_write/1, append/1, exclusive/1]). --export([ e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]). +-export([e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]). --export([ read_not_really_compressed/1, - read_compressed/1, write_compressed/1, - compress_errors/1]). - --export([ - make_link_a/1, make_link_b/1, - read_link_info_for_non_link/1, - symlinks_a/1, symlinks_b/1, - list_dir_limit/1, - list_dir_error/1, - list_dir/1]). +-export([make_link/1, read_link_info_for_non_link/1, + symlinks/1, + list_dir_limit/1, + list_dir_error/1, + list_dir/1]). -export([advise/1]). -export([large_write/1]). @@ -67,29 +52,16 @@ -define(PRIM_FILE, prim_file). -%% Calls ?PRIM_FILE:F with arguments A and an optional handle H -%% as first argument, unless the handle is [], i.e no handle. -%% This is a macro to give the compiler and thereby -%% the cross reference tool the possibility to interprete -%% the call, since M, F, A (or [H | A]) can all be known at -%% compile time. --define(PRIM_FILE_call(F, H, A), - case H of - [] -> apply(?PRIM_FILE, F, A); - _ -> apply(?PRIM_FILE, F, [H | A]) - end). - suite() -> []. all() -> [read_write_file, {group, dirs}, {group, files}, - delete_a, delete_b, rename_a, rename_b, {group, errors}, - {group, compression}, {group, links}, list_dir_limit, list_dir]. + delete, rename, {group, errors}, {group, links}, + list_dir_limit, list_dir]. groups() -> [{dirs, [], - [make_del_dir_a, make_del_dir_b, cur_dir_0a, cur_dir_0b, - cur_dir_1a, cur_dir_1b]}, + [make_del_dir, cur_dir_0, cur_dir_1]}, {files, [], [{group, open}, {group, pos}, {group, file_info}, truncate, sync, datasync, advise, large_write, allocate]}, @@ -98,22 +70,14 @@ groups() -> append, exclusive]}, {pos, [], [pos1, pos2]}, {file_info, [], - [file_info_basic_file_a, file_info_basic_file_b, - file_info_basic_directory_a, - file_info_basic_directory_b, file_info_bad_a, - file_info_bad_b, file_info_times_a, file_info_times_b, - file_write_file_info_a, file_write_file_info_b, - file_read_file_info_opts, file_write_file_info_opts, - file_write_read_file_info_opts + [file_info_basic_file,file_info_basic_directory, file_info_bad, + file_info_times, file_write_file_info, file_read_file_info_opts, + file_write_file_info_opts, file_write_read_file_info_opts ]}, {errors, [], [e_delete, e_rename, e_make_dir, e_del_dir]}, - {compression, [], - [read_compressed, read_not_really_compressed, - write_compressed, compress_errors]}, {links, [], - [make_link_a, make_link_b, read_link_info_for_non_link, - symlinks_a, symlinks_b, list_dir_error]}]. + [make_link, read_link_info_for_non_link, symlinks, list_dir_error]}]. init_per_testcase(large_write, Config) -> {ok, Started} = application:ensure_all_started(os_mon), @@ -246,39 +210,27 @@ read_write_file(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -make_del_dir_a(Config) when is_list(Config) -> - make_del_dir(Config, [], "_a"). - -make_del_dir_b(Config) when is_list(Config) -> - {ok, Handle} = ?PRIM_FILE:start(), - Result = make_del_dir(Config, Handle, "_b"), - ok = ?PRIM_FILE:stop(Handle), - %% Just to make sure the state of the server makes a difference - {error, einval} = ?PRIM_FILE_call(get_cwd, Handle, []), - Result. - -make_del_dir(Config, Handle, Suffix) -> +make_del_dir(Config) when is_list(Config) -> RootDir = proplists:get_value(priv_dir,Config), NewDir = filename:join(RootDir, atom_to_list(?MODULE) - ++"_mk-dir"++Suffix), - ok = ?PRIM_FILE_call(make_dir, Handle, [NewDir]), - {error, eexist} = ?PRIM_FILE_call(make_dir, Handle, [NewDir]), - ok = ?PRIM_FILE_call(del_dir, Handle, [NewDir]), - {error, enoent} = ?PRIM_FILE_call(del_dir, Handle, [NewDir]), + ++"_mk-dir"), + ok = ?PRIM_FILE:make_dir(NewDir), + {error, eexist} = ?PRIM_FILE:make_dir(NewDir), + ok = ?PRIM_FILE:del_dir(NewDir), + {error, enoent} = ?PRIM_FILE:del_dir(NewDir), %% Make sure we are not in a directory directly under test_server %% as that would result in eacces errors when trying to delete '..', %% because there are processes having that directory as current. - ok = ?PRIM_FILE_call(make_dir, Handle, [NewDir]), - {ok, CurrentDir} = ?PRIM_FILE_call(get_cwd, Handle, []), + ok = ?PRIM_FILE:make_dir(NewDir), + {ok, CurrentDir} = ?PRIM_FILE:get_cwd(), case {os:type(), length(NewDir) >= 260 } of {{win32,_}, true} -> io:format("Skip set_cwd for windows path longer than 260 (MAX_PATH)\n", []), io:format("\nNewDir = ~p\n", [NewDir]); _ -> - ok = ?PRIM_FILE_call(set_cwd, Handle, [NewDir]) + ok = ?PRIM_FILE:set_cwd(NewDir) end, try %% Check that we get an error when trying to create... @@ -286,14 +238,14 @@ make_del_dir(Config, Handle, Suffix) -> NewDir2 = filename:join(RootDir, atom_to_list(?MODULE) ++"_mk-dir-noexist/foo"), - {error, enoent} = ?PRIM_FILE_call(make_dir, Handle, [NewDir2]), + {error, enoent} = ?PRIM_FILE:make_dir(NewDir2), %% a nameless directory - {error, enoent} = ?PRIM_FILE_call(make_dir, Handle, [""]), + {error, enoent} = ?PRIM_FILE:make_dir(""), %% a directory with illegal name - {error, badarg} = ?PRIM_FILE_call(make_dir, Handle, ['mk-dir']), + {error, badarg} = ?PRIM_FILE:make_dir('mk-dir'), %% a directory with illegal name, even if it's a (bad) list - {error, badarg} = ?PRIM_FILE_call(make_dir, Handle, [[1,2,3,{}]]), + {error, badarg} = ?PRIM_FILE:make_dir([1,2,3,{}]), %% Maybe this isn't an error, exactly, but worth mentioning anyway: %% ok = ?PRIM_FILE:make_dir([$f,$o,$o,0,$b,$a,$r])), @@ -306,125 +258,101 @@ make_del_dir(Config, Handle, Suffix) -> %% Try deleting some bad directories %% Deleting the parent directory to the current, sounds dangerous, huh? %% Don't worry ;-) the parent directory should never be empty, right? - case ?PRIM_FILE_call(del_dir, Handle, [".."]) of + case ?PRIM_FILE:del_dir("..") of {error, eexist} -> ok; {error, eacces} -> ok; %OpenBSD {error, einval} -> ok %FreeBSD end, - {error, enoent} = ?PRIM_FILE_call(del_dir, Handle, [""]), - {error, badarg} = ?PRIM_FILE_call(del_dir, Handle, [[3,2,1,{}]]) + {error, enoent} = ?PRIM_FILE:del_dir(""), + {error, badarg} = ?PRIM_FILE:del_dir([3,2,1,{}]) after - ok = ?PRIM_FILE_call(set_cwd, Handle, [CurrentDir]) + ok = ?PRIM_FILE:set_cwd(CurrentDir) end, ok. -cur_dir_0a(Config) when is_list(Config) -> - cur_dir_0(Config, []). - -cur_dir_0b(Config) when is_list(Config) -> - {ok, Handle} = ?PRIM_FILE:start(), - Result = cur_dir_0(Config, Handle), - ok = ?PRIM_FILE:stop(Handle), - Result. - -cur_dir_0(Config, Handle) -> +cur_dir_0(Config) when is_list(Config) -> %% Find out the current dir, and cd to it ;-) - {ok,BaseDir} = ?PRIM_FILE_call(get_cwd, Handle, []), + {ok,BaseDir} = ?PRIM_FILE:get_cwd(), Dir1 = BaseDir ++ "", %% Check that it's a string - ok = ?PRIM_FILE_call(set_cwd, Handle, [Dir1]), - DirName = atom_to_list(?MODULE) ++ - case Handle of - [] -> - "_curdir"; - _ -> - "_curdir_h" - end, + ok = ?PRIM_FILE:set_cwd(Dir1), + DirName = atom_to_list(?MODULE) ++ "_curdir", %% Make a new dir, and cd to that RootDir = proplists:get_value(priv_dir,Config), NewDir = filename:join(RootDir, DirName), - ok = ?PRIM_FILE_call(make_dir, Handle, [NewDir]), + ok = ?PRIM_FILE:make_dir(NewDir), case {os:type(), length(NewDir) >= 260} of {{win32,_}, true} -> io:format("Skip set_cwd for windows path longer than 260 (MAX_PATH):\n"), io:format("\nNewDir = ~p\n", [NewDir]); _ -> io:format("cd to ~s",[NewDir]), - ok = ?PRIM_FILE_call(set_cwd, Handle, [NewDir]), + ok = ?PRIM_FILE:set_cwd(NewDir), %% Create a file in the new current directory, and check that it %% really is created there UncommonName = "uncommon.fil", {ok,Fd} = ?PRIM_FILE:open(UncommonName, [read, write]), ok = ?PRIM_FILE:close(Fd), - {ok,NewDirFiles} = ?PRIM_FILE_call(list_dir, Handle, ["."]), + {ok,NewDirFiles} = ?PRIM_FILE:list_dir("."), true = lists:member(UncommonName,NewDirFiles), %% Delete the directory and return to the old current directory %% and check that the created file isn't there (too!) expect({error, einval}, {error, eacces}, {error, eexist}, - ?PRIM_FILE_call(del_dir, Handle, [NewDir])), - ?PRIM_FILE_call(delete, Handle, [UncommonName]), - {ok,[]} = ?PRIM_FILE_call(list_dir, Handle, ["."]), - ok = ?PRIM_FILE_call(set_cwd, Handle, [Dir1]), + ?PRIM_FILE:del_dir(NewDir)), + ?PRIM_FILE:delete(UncommonName), + {ok,[]} = ?PRIM_FILE:list_dir("."), + ok = ?PRIM_FILE:set_cwd(Dir1), io:format("cd back to ~s",[Dir1]), - ok = ?PRIM_FILE_call(del_dir, Handle, [NewDir]), - {error, enoent} = ?PRIM_FILE_call(set_cwd, Handle, [NewDir]), - ok = ?PRIM_FILE_call(set_cwd, Handle, [Dir1]), + ok = ?PRIM_FILE:del_dir(NewDir), + {error, enoent} = ?PRIM_FILE:set_cwd(NewDir), + ok = ?PRIM_FILE:set_cwd(Dir1), io:format("cd back to ~s",[Dir1]), - {ok,OldDirFiles} = ?PRIM_FILE_call(list_dir, Handle, ["."]), + {ok,OldDirFiles} = ?PRIM_FILE:list_dir("."), false = lists:member(UncommonName,OldDirFiles) end, %% Try doing some bad things {error, badarg} = - ?PRIM_FILE_call(set_cwd, Handle, [{foo,bar}]), + ?PRIM_FILE:set_cwd({foo,bar}), {error, enoent} = - ?PRIM_FILE_call(set_cwd, Handle, [""]), + ?PRIM_FILE:set_cwd(""), {error, enoent} = - ?PRIM_FILE_call(set_cwd, Handle, [".......a......"]), + ?PRIM_FILE:set_cwd(".......a......"), {ok,BaseDir} = - ?PRIM_FILE_call(get_cwd, Handle, []), %% Still there? + ?PRIM_FILE:get_cwd(), %% Still there? %% On Windows, there should only be slashes, no backslashes, %% in the return value of get_cwd(). %% (The test is harmless on Unix, because filenames usually %% don't contain backslashes.) - {ok, BaseDir} = ?PRIM_FILE_call(get_cwd, Handle, []), + {ok, BaseDir} = ?PRIM_FILE:get_cwd(), false = lists:member($\\, BaseDir), ok. %% Tests ?PRIM_FILE:get_cwd/1. -cur_dir_1a(Config) when is_list(Config) -> - cur_dir_1(Config, []). - -cur_dir_1b(Config) when is_list(Config) -> - {ok, Handle} = ?PRIM_FILE:start(), - Result = cur_dir_1(Config, Handle), - ok = ?PRIM_FILE:stop(Handle), - Result. - -cur_dir_1(Config, Handle) -> +cur_dir_1(Config) when is_list(Config) -> case os:type() of {win32, _} -> - win_cur_dir_1(Config, Handle); + win_cur_dir_1(Config); _ -> {error, enotsup} = - ?PRIM_FILE_call(get_cwd, Handle, ["d:"]) + ?PRIM_FILE:get_cwd("d:") end, ok. -win_cur_dir_1(_Config, Handle) -> - {ok, BaseDir} = ?PRIM_FILE_call(get_cwd, Handle, []), +win_cur_dir_1(_Config) -> + {ok, BaseDir} = ?PRIM_FILE:get_cwd(), %% Get the drive letter from the current directory, %% and try to get current directory for that drive. [Drive, $:|_] = BaseDir, - {ok, BaseDir} = ?PRIM_FILE_call(get_cwd, Handle, [[Drive, $:]]), + {ok, BaseDir} = ?PRIM_FILE:get_cwd([Drive, $:]), io:format("BaseDir = ~s\n", [BaseDir]), %% Unfortunately, there is no way to move away from the @@ -446,12 +374,12 @@ open1(Config) when is_list(Config) -> Name = filename:join(NewDir, "foo1.fil"), {ok,Fd1} = ?PRIM_FILE:open(Name, [read, write]), {ok,Fd2} = ?PRIM_FILE:open(Name, [read]), - Str = "{a,tuple}.\n", - Length = length(Str), - ?PRIM_FILE:write(Fd1,Str), + Bin = list_to_binary("{a,tuple}.\n"), + Length = byte_size(Bin), + ?PRIM_FILE:write(Fd1,Bin), {ok,0} = ?PRIM_FILE:position(Fd1,bof), - {ok, Str} = ?PRIM_FILE:read(Fd1,Length), - {ok, Str} = ?PRIM_FILE:read(Fd2,Length), + {ok, Bin} = ?PRIM_FILE:read(Fd1,Length), + {ok, Bin} = ?PRIM_FILE:read(Fd2,Length), ok = ?PRIM_FILE:close(Fd2), {ok,0} = ?PRIM_FILE:position(Fd1,bof), ok = ?PRIM_FILE:truncate(Fd1), @@ -471,13 +399,13 @@ modes(Config) when is_list(Config) -> ++"_open_modes"), ok = ?PRIM_FILE:make_dir(NewDir), Name1 = filename:join(NewDir, "foo1.fil"), - Marker = "hello, world", - Length = length(Marker), + Marker = <<"hello, world">>, + Length = byte_size(Marker), %% write {ok, Fd1} = ?PRIM_FILE:open(Name1, [write]), ok = ?PRIM_FILE:write(Fd1, Marker), - ok = ?PRIM_FILE:write(Fd1, ".\n"), + ok = ?PRIM_FILE:write(Fd1, <<".\n">>), ok = ?PRIM_FILE:close(Fd1), %% read @@ -496,12 +424,6 @@ modes(Config) when is_list(Config) -> {ok, Marker} = ?PRIM_FILE:read(Fd4, Length), ok = ?PRIM_FILE:close(Fd4), - %% read and binary - BinaryMarker = list_to_binary(Marker), - {ok, Fd5} = ?PRIM_FILE:open(Name1, [read, binary]), - {ok, BinaryMarker} = ?PRIM_FILE:read(Fd5, Length), - ok = ?PRIM_FILE:close(Fd5), - ok. close(Config) when is_list(Config) -> @@ -528,9 +450,9 @@ access(Config) when is_list(Config) -> Name = filename:join(RootDir, atom_to_list(?MODULE) ++"_access.fil"), - Str = "ABCDEFGH", + Bin = <<"ABCDEFGH">>, {ok,Fd1} = ?PRIM_FILE:open(Name, [write]), - ?PRIM_FILE:write(Fd1,Str), + ?PRIM_FILE:write(Fd1,Bin), ok = ?PRIM_FILE:close(Fd1), %% Check that we can't write when in read only mode {ok,Fd2} = ?PRIM_FILE:open(Name, [read]), @@ -542,7 +464,7 @@ access(Config) when is_list(Config) -> end, ok = ?PRIM_FILE:close(Fd2), {ok, Fd3} = ?PRIM_FILE:open(Name, [read]), - {ok, Str} = ?PRIM_FILE:read(Fd3,length(Str)), + {ok, Bin} = ?PRIM_FILE:read(Fd3,byte_size(Bin)), ok = ?PRIM_FILE:close(Fd3), ok. @@ -564,7 +486,7 @@ read_write(Config) when is_list(Config) -> ok. read_write_test(File) -> - Marker = "hello, world", + Marker = <<"hello, world">>, ok = ?PRIM_FILE:write(File, Marker), {ok, 0} = ?PRIM_FILE:position(File, 0), {ok, Marker} = ?PRIM_FILE:read(File, 100), @@ -590,15 +512,15 @@ pread_write(Config) when is_list(Config) -> ok. pread_write_test(File) -> - Marker = "hello, world", - Len = length(Marker), + Marker = <<"hello, world">>, + Len = byte_size(Marker), ok = ?PRIM_FILE:write(File, Marker), {ok, Marker} = ?PRIM_FILE:pread(File, 0, 100), eof = ?PRIM_FILE:pread(File, 100, 1), ok = ?PRIM_FILE:pwrite(File, Len, Marker), {ok, Marker} = ?PRIM_FILE:pread(File, Len, 100), eof = ?PRIM_FILE:pread(File, 100, 1), - MM = Marker ++ Marker, + MM = <>, {ok, MM} = ?PRIM_FILE:pread(File, 0, 100), ok = ?PRIM_FILE:close(File), ok. @@ -655,24 +577,24 @@ pos1(Config) when is_list(Config) -> atom_to_list(?MODULE) ++"_pos1.fil"), {ok, Fd1} = ?PRIM_FILE:open(Name, [write]), - ?PRIM_FILE:write(Fd1,"ABCDEFGH"), + ?PRIM_FILE:write(Fd1,<<"ABCDEFGH">>), ok = ?PRIM_FILE:close(Fd1), {ok, Fd2} = ?PRIM_FILE:open(Name, [read]), %% Start pos is first char io:format("Relative positions"), - {ok, "A"} = ?PRIM_FILE:read(Fd2,1), + {ok, <<"A">>} = ?PRIM_FILE:read(Fd2,1), {ok, 2} = ?PRIM_FILE:position(Fd2,{cur,1}), - {ok, "C"} = ?PRIM_FILE:read(Fd2,1), + {ok, <<"C">>} = ?PRIM_FILE:read(Fd2,1), {ok, 0} = ?PRIM_FILE:position(Fd2,{cur,-3}), - {ok, "A"} = ?PRIM_FILE:read(Fd2,1), + {ok, <<"A">>} = ?PRIM_FILE:read(Fd2,1), %% Backwards from first char should be an error {ok,0} = ?PRIM_FILE:position(Fd2,{cur,-1}), {error, einval} = ?PRIM_FILE:position(Fd2,{cur,-1}), %% Reset position and move again {ok, 0} = ?PRIM_FILE:position(Fd2,0), {ok, 2} = ?PRIM_FILE:position(Fd2,{cur,2}), - {ok, "C"} = ?PRIM_FILE:read(Fd2,1), + {ok, <<"C">>} = ?PRIM_FILE:read(Fd2,1), %% Go a lot forwards {ok, 13} = ?PRIM_FILE:position(Fd2,{cur,10}), eof = ?PRIM_FILE:read(Fd2,1), @@ -684,27 +606,27 @@ pos1(Config) when is_list(Config) -> {ok, 8} = ?PRIM_FILE:position(Fd2,cur), eof = ?PRIM_FILE:read(Fd2,1), {ok, 7} = ?PRIM_FILE:position(Fd2,7), - {ok, "H"} = ?PRIM_FILE:read(Fd2,1), + {ok, <<"H">>} = ?PRIM_FILE:read(Fd2,1), {ok, 0} = ?PRIM_FILE:position(Fd2,0), - {ok, "A"} = ?PRIM_FILE:read(Fd2,1), + {ok, <<"A">>} = ?PRIM_FILE:read(Fd2,1), {ok, 3} = ?PRIM_FILE:position(Fd2,3), - {ok, "D"} = ?PRIM_FILE:read(Fd2,1), + {ok, <<"D">>} = ?PRIM_FILE:read(Fd2,1), {ok, 12} = ?PRIM_FILE:position(Fd2,12), eof = ?PRIM_FILE:read(Fd2,1), {ok, 3} = ?PRIM_FILE:position(Fd2,3), - {ok, "D"} = ?PRIM_FILE:read(Fd2,1), + {ok, <<"D">>} = ?PRIM_FILE:read(Fd2,1), %% Try the {bof,X} notation {ok, 3} = ?PRIM_FILE:position(Fd2,{bof,3}), - {ok, "D"} = ?PRIM_FILE:read(Fd2,1), + {ok, <<"D">>} = ?PRIM_FILE:read(Fd2,1), %% Try eof positions io:format("EOF positions"), {ok, 8} = ?PRIM_FILE:position(Fd2,{eof,0}), eof = ?PRIM_FILE:read(Fd2,1), {ok, 7} = ?PRIM_FILE:position(Fd2,{eof,-1}), - {ok, "H"} = ?PRIM_FILE:read(Fd2,1), + {ok, <<"H">>} = ?PRIM_FILE:read(Fd2,1), {ok, 0} = ?PRIM_FILE:position(Fd2,{eof,-8}), - {ok, "A"} = ?PRIM_FILE:read(Fd2,1), + {ok, <<"A">>} = ?PRIM_FILE:read(Fd2,1), {error, einval} = ?PRIM_FILE:position(Fd2,{eof,-9}), ok. @@ -714,7 +636,7 @@ pos2(Config) when is_list(Config) -> atom_to_list(?MODULE) ++"_pos2.fil"), {ok,Fd1} = ?PRIM_FILE:open(Name, [write]), - ?PRIM_FILE:write(Fd1,"ABCDEFGH"), + ?PRIM_FILE:write(Fd1,<<"ABCDEFGH">>), ok = ?PRIM_FILE:close(Fd1), {ok, Fd2} = ?PRIM_FILE:open(Name, [read]), {error, einval} = ?PRIM_FILE:position(Fd2,-1), @@ -722,35 +644,25 @@ pos2(Config) when is_list(Config) -> %% Make sure that we still can search after an error. {ok, 0} = ?PRIM_FILE:position(Fd2, 0), {ok, 3} = ?PRIM_FILE:position(Fd2, {bof,3}), - {ok, "D"} = ?PRIM_FILE:read(Fd2,1), + {ok, <<"D">>} = ?PRIM_FILE:read(Fd2,1), io:format("DONE"), ok. - -file_info_basic_file_a(Config) when is_list(Config) -> - file_info_basic_file(Config, [], "_a"). - -file_info_basic_file_b(Config) when is_list(Config) -> - {ok, Handle} = ?PRIM_FILE:start(), - Result = file_info_basic_file(Config, Handle, "_b"), - ok = ?PRIM_FILE:stop(Handle), - Result. - -file_info_basic_file(Config, Handle, Suffix) -> +file_info_basic_file(Config) when is_list(Config) -> RootDir = proplists:get_value(priv_dir, Config), %% Create a short file. Name = filename:join(RootDir, atom_to_list(?MODULE) - ++"_basic_test"++Suffix++".fil"), + ++"_basic_test"".fil"), {ok,Fd1} = ?PRIM_FILE:open(Name, [write]), ?PRIM_FILE:write(Fd1, "foo bar"), ok = ?PRIM_FILE:close(Fd1), %% Test that the file has the expected attributes. %% The times are tricky, so we will save them to a separate test case. - {ok, FileInfo} = ?PRIM_FILE_call(read_file_info, Handle, [Name]), + {ok, FileInfo} = ?PRIM_FILE:read_file_info(Name), #file_info{size = Size, type = Type, access = Access, atime = AccessTime, mtime = ModifyTime} = FileInfo, @@ -768,39 +680,30 @@ file_info_basic_file(Config, Handle, Suffix) -> ok. -file_info_basic_directory_a(Config) when is_list(Config) -> - file_info_basic_directory(Config, []). - -file_info_basic_directory_b(Config) when is_list(Config) -> - {ok, Handle} = ?PRIM_FILE:start(), - Result = file_info_basic_directory(Config, Handle), - ok = ?PRIM_FILE:stop(Handle), - Result. - -file_info_basic_directory(Config, Handle) -> +file_info_basic_directory(Config) when is_list(Config) -> %% Note: filename:join/1 removes any trailing slash, %% which is essential for ?PRIM_FILE:read_file_info/1 to work on %% platforms such as Windows95. RootDir = filename:join([proplists:get_value(priv_dir, Config)]), %% Test that the RootDir directory has the expected attributes. - test_directory(RootDir, read_write, Handle), + test_directory(RootDir, read_write), %% Note that on Windows file systems, "/" or "c:/" are *NOT* directories. %% Therefore, test that ?PRIM_FILE:read_file_info/1 behaves %% as if they were directories. case os:type() of {win32, _} -> - test_directory("/", read_write, Handle), - test_directory("c:/", read_write, Handle), - test_directory("c:\\", read_write, Handle); + test_directory("/", read_write), + test_directory("c:/", read_write), + test_directory("c:\\", read_write); _ -> - test_directory("/", read, Handle) + test_directory("/", read) end, ok. -test_directory(Name, ExpectedAccess, Handle) -> - {ok, FileInfo} = ?PRIM_FILE_call(read_file_info, Handle, [Name]), +test_directory(Name, ExpectedAccess) -> + {ok, FileInfo} = ?PRIM_FILE:read_file_info(Name), #file_info{size = Size, type = Type, access = Access, atime = AccessTime, mtime = ModifyTime} = FileInfo, @@ -824,45 +727,24 @@ all_integers([]) -> %% Try something nonexistent. -file_info_bad_a(Config) when is_list(Config) -> - file_info_bad(Config, []). - -file_info_bad_b(Config) when is_list(Config) -> - {ok, Handle} = ?PRIM_FILE:start(), - Result = file_info_bad(Config, Handle), - ok = ?PRIM_FILE:stop(Handle), - Result. - -file_info_bad(Config, Handle) -> +file_info_bad(Config) when is_list(Config) -> RootDir = filename:join([proplists:get_value(priv_dir, Config)]), - {error, enoent} = - ?PRIM_FILE_call( - read_file_info, Handle, - [filename:join(RootDir, - atom_to_list(?MODULE)++"_nonexistent")]), + NonExistent = filename:join(RootDir, atom_to_list(?MODULE)++"_nonexistent"), + {error, enoent} = ?PRIM_FILE:read_file_info(NonExistent), ok. %% Test that the file times behave as they should. -file_info_times_a(Config) when is_list(Config) -> - file_info_times(Config, [], "_a"). - -file_info_times_b(Config) when is_list(Config) -> - {ok, Handle} = ?PRIM_FILE:start(), - Result = file_info_times(Config, Handle, "_b"), - ok = ?PRIM_FILE:stop(Handle), - Result. - -file_info_times(Config, Handle, Suffix) -> +file_info_times(Config) when is_list(Config) -> %% We have to try this twice, since if the test runs across the change %% of a month the time diff calculations will fail. But it won't happen %% if you run it twice in succession. test_server:m_out_of_n( 1,2, - fun() -> file_info_int(Config, Handle, Suffix) end), + fun() -> file_info_int(Config) end), ok. -file_info_int(Config, Handle, Suffix) -> +file_info_int(Config) -> %% Note: filename:join/1 removes any trailing slash, %% which is essential for ?PRIM_FILE:read_file_info/1 to work on %% platforms such as Windows95. @@ -872,14 +754,14 @@ file_info_int(Config, Handle, Suffix) -> Name = filename:join(RootDir, atom_to_list(?MODULE) - ++"_file_info"++Suffix++".fil"), + ++"_file_info.fil"), {ok,Fd1} = ?PRIM_FILE:open(Name, [write]), ?PRIM_FILE:write(Fd1,"foo"), %% check that the file got a modify date max a few seconds away from now {ok, #file_info{type = regular, atime = AccTime1, mtime = ModTime1}} = - ?PRIM_FILE_call(read_file_info, Handle, [Name]), + ?PRIM_FILE:read_file_info(Name), Now = erlang:localtime(), io:format("Now ~p",[Now]), io:format("Open file Acc ~p Mod ~p",[AccTime1,ModTime1]), @@ -897,7 +779,7 @@ file_info_int(Config, Handle, Suffix) -> ok = ?PRIM_FILE:close(Fd1), {ok, #file_info{size = Size, type = regular, access = Access, atime = AccTime2, mtime = ModTime2}} = - ?PRIM_FILE_call(read_file_info, Handle, [Name]), + ?PRIM_FILE:read_file_info(Name), io:format("Closed file Acc ~p Mod ~p",[AccTime2,ModTime2]), true = time_dist(ModTime1, ModTime2) >= 0, @@ -909,7 +791,7 @@ file_info_int(Config, Handle, Suffix) -> {ok, #file_info{size = DSize, type = directory, access = DAccess, atime = AccTime3, mtime = ModTime3}} = - ?PRIM_FILE_call(read_file_info, Handle, [RootDir]), + ?PRIM_FILE:read_file_info(RootDir), %% this dir was modified only a few secs ago io:format("Dir Acc ~p; Mod ~p; Now ~p", [AccTime3, ModTime3, Now]), @@ -936,16 +818,7 @@ filter_atime(Atime, Config) -> %% Test the write_file_info/2 function. -file_write_file_info_a(Config) when is_list(Config) -> - file_write_file_info(Config, [], "_a"). - -file_write_file_info_b(Config) when is_list(Config) -> - {ok, Handle} = ?PRIM_FILE:start(), - Result = file_write_file_info(Config, Handle, "_b"), - ok = ?PRIM_FILE:stop(Handle), - Result. - -file_write_file_info(Config, Handle, Suffix) -> +file_write_file_info(Config) when is_list(Config) -> RootDir = get_good_directory(Config), io:format("RootDir = ~p", [RootDir]), @@ -955,16 +828,16 @@ file_write_file_info(Config, Handle, Suffix) -> Name = filename:join(RootDir, atom_to_list(?MODULE) - ++"_write_file_info_ro"++Suffix), + ++"_write_file_info_ro"), ok = ?PRIM_FILE:write_file(Name, "hello"), Time = {{1997, 01, 02}, {12, 35, 42}}, Info = #file_info{mode=8#400, atime=Time, mtime=Time, ctime=Time}, - ok = ?PRIM_FILE_call(write_file_info, Handle, [Name, Info]), + ok = ?PRIM_FILE:write_file_info(Name, Info), %% Read back the times. {ok, ActualInfo} = - ?PRIM_FILE_call(read_file_info, Handle, [Name]), + ?PRIM_FILE:read_file_info(Name), #file_info{mode=_Mode, atime=ActAtime, mtime=Time, ctime=ActCtime} = ActualInfo, FilteredAtime = filter_atime(Time, Config), @@ -980,14 +853,11 @@ file_write_file_info(Config, Handle, Suffix) -> {error, eacces} = ?PRIM_FILE:write_file(Name, "hello again"), %% Make the file writable again. - - ?PRIM_FILE_call(write_file_info, Handle, - [Name, #file_info{mode=8#600}]), + ?PRIM_FILE:write_file_info(Name, #file_info{mode=8#600}), ok = ?PRIM_FILE:write_file(Name, "hello again"), %% And unwritable. - ?PRIM_FILE_call(write_file_info, Handle, - [Name, #file_info{mode=8#400}]), + ?PRIM_FILE:write_file_info(Name, #file_info{mode=8#400}), {error, eacces} = ?PRIM_FILE:write_file(Name, "hello again"), %% Write the times again. @@ -995,9 +865,9 @@ file_write_file_info(Config, Handle, Suffix) -> NewTime = {{1997, 02, 15}, {13, 18, 20}}, NewInfo = #file_info{atime=NewTime, mtime=NewTime, ctime=NewTime}, - ok = ?PRIM_FILE_call(write_file_info, Handle, [Name, NewInfo]), + ok = ?PRIM_FILE:write_file_info(Name, NewInfo), {ok, ActualInfo2} = - ?PRIM_FILE_call(read_file_info, Handle, [Name]), + ?PRIM_FILE:read_file_info(Name), #file_info{atime=NewActAtime, mtime=NewTime, ctime=NewActCtime} = ActualInfo2, NewFilteredAtime = filter_atime(NewTime, Config), @@ -1012,14 +882,12 @@ file_write_file_info(Config, Handle, Suffix) -> %% Make the file writeable again, so that we can remove the %% test suites ... :-) - ?PRIM_FILE_call(write_file_info, Handle, - [Name, #file_info{mode=8#600}]), + ?PRIM_FILE:write_file_info(Name, #file_info{mode=8#600}), ok. %% Test the write_file_info/3 function. file_write_file_info_opts(Config) when is_list(Config) -> - {ok, Handle} = ?PRIM_FILE:start(), RootDir = get_good_directory(Config), io:format("RootDir = ~p", [RootDir]), @@ -1028,7 +896,7 @@ file_write_file_info_opts(Config) when is_list(Config) -> lists:foreach(fun ({FI, Opts}) -> - ok = ?PRIM_FILE_call(write_file_info, Handle, [Name, FI, Opts]) + ok = ?PRIM_FILE:write_file_info(Name, FI, Opts) end, [ {#file_info{ mode=8#600, atime = Time, mtime = Time, ctime = Time}, Opts} || Opts <- [[{time, posix}]], @@ -1038,7 +906,7 @@ file_write_file_info_opts(Config) when is_list(Config) -> %% REM: determine date range dependent on time_t = Uint32 | Sint32 | Sint64 | Uint64 %% Determine time_t on os:type()? lists:foreach(fun ({FI, Opts}) -> - ok = ?PRIM_FILE_call(write_file_info, Handle, [Name, FI, Opts]) + ok = ?PRIM_FILE:write_file_info(Name, FI, Opts) end, [ {#file_info{ mode=8#400, atime = Time, mtime = Time, ctime = Time}, Opts} || Opts <- [[{time, universal}],[{time, local}]], Time <- [ @@ -1050,11 +918,9 @@ file_write_file_info_opts(Config) when is_list(Config) -> {{2037,2,3},{23,59,59}}, erlang:localtime() ]]), - ok = ?PRIM_FILE:stop(Handle), ok. file_read_file_info_opts(Config) when is_list(Config) -> - {ok, Handle} = ?PRIM_FILE:start(), RootDir = get_good_directory(Config), io:format("RootDir = ~p", [RootDir]), @@ -1063,41 +929,38 @@ file_read_file_info_opts(Config) when is_list(Config) -> lists:foreach(fun (Opts) -> - {ok,_} = ?PRIM_FILE_call(read_file_info, Handle, [Name, Opts]) + {ok,_} = ?PRIM_FILE:read_file_info(Name, Opts) end, [[{time, Type}] || Type <- [local, universal, posix]]), - ok = ?PRIM_FILE:stop(Handle), ok. %% Test the write and read back *_file_info/3 functions. file_write_read_file_info_opts(Config) when is_list(Config) -> - {ok, Handle} = ?PRIM_FILE:start(), RootDir = get_good_directory(Config), io:format("RootDir = ~p", [RootDir]), Name = filename:join(RootDir, atom_to_list(?MODULE) ++"_read_write_file_info_opts"), ok = ?PRIM_FILE:write_file(Name, "hello_opts2"), - ok = file_write_read_file_info_opts(Handle, Name, {{1989, 04, 28}, {19,30,22}}, [{time, local}]), - ok = file_write_read_file_info_opts(Handle, Name, {{1989, 04, 28}, {19,30,22}}, [{time, universal}]), + ok = file_write_read_file_info_opts(Name, {{1989, 04, 28}, {19,30,22}}, [{time, local}]), + ok = file_write_read_file_info_opts(Name, {{1989, 04, 28}, {19,30,22}}, [{time, universal}]), %% will not work on platforms with unsigned time_t - %ok = file_write_read_file_info_opts(Handle, Name, {{1930, 04, 28}, {19,30,22}}, [{time, local}]), - %ok = file_write_read_file_info_opts(Handle, Name, {{1930, 04, 28}, {19,30,22}}, [{time, universal}]), - ok = file_write_read_file_info_opts(Handle, Name, 1, [{time, posix}]), + %ok = file_write_read_file_info_opts(Name, {{1930, 04, 28}, {19,30,22}}, [{time, local}]), + %ok = file_write_read_file_info_opts(Name, {{1930, 04, 28}, {19,30,22}}, [{time, universal}]), + ok = file_write_read_file_info_opts(Name, 1, [{time, posix}]), %% will not work on platforms with unsigned time_t - %ok = file_write_read_file_info_opts(Handle, Name, -1, [{time, posix}]), - %ok = file_write_read_file_info_opts(Handle, Name, -300000, [{time, posix}]), - ok = file_write_read_file_info_opts(Handle, Name, 300000, [{time, posix}]), - ok = file_write_read_file_info_opts(Handle, Name, 0, [{time, posix}]), + %ok = file_write_read_file_info_opts(Name, -1, [{time, posix}]), + %ok = file_write_read_file_info_opts(Name, -300000, [{time, posix}]), + ok = file_write_read_file_info_opts(Name, 300000, [{time, posix}]), + ok = file_write_read_file_info_opts(Name, 0, [{time, posix}]), - ok = ?PRIM_FILE:stop(Handle), ok. -file_write_read_file_info_opts(Handle, Name, Mtime, Opts) -> - {ok, FI} = ?PRIM_FILE_call(read_file_info, Handle, [Name, Opts]), +file_write_read_file_info_opts(Name, Mtime, Opts) -> + {ok, FI} = ?PRIM_FILE:read_file_info(Name, Opts), FI2 = FI#file_info{ mtime = Mtime }, - ok = ?PRIM_FILE_call(write_file_info, Handle, [Name, FI2, Opts]), - {ok, FI3} = ?PRIM_FILE_call(read_file_info, Handle, [Name, Opts]), + ok = ?PRIM_FILE:write_file_info(Name, FI2, Opts), + {ok, FI3} = ?PRIM_FILE:read_file_info(Name, Opts), io:format("Expecting mtime = ~p, got ~p~n", [FI2#file_info.mtime, FI3#file_info.mtime]), FI2 = FI3, ok. @@ -1175,8 +1038,8 @@ advise(Config) when is_list(Config) -> atom_to_list(?MODULE) ++"_advise.fil"), - Line1 = "Hello\n", - Line2 = "World!\n", + Line1 = <<"Hello\n">>, + Line2 = <<"World!\n">>, {ok, Fd} = ?PRIM_FILE:open(Advise, [write]), ok = ?PRIM_FILE:advise(Fd, 0, 0, normal), @@ -1226,7 +1089,7 @@ advise(Config) when is_list(Config) -> {ok, Fd9} = ?PRIM_FILE:open(Advise, [read]), Offset = 0, %% same as a 0 length in some implementations - Length = length(Line1) + length(Line2), + Length = byte_size(Line1) + byte_size(Line2), ok = ?PRIM_FILE:advise(Fd9, Offset, Length, sequential), {ok, Line1} = ?PRIM_FILE:read_line(Fd9), {ok, Line2} = ?PRIM_FILE:read_line(Fd9), @@ -1251,10 +1114,10 @@ do_large_write(Name) -> Data = zip_data(lists:duplicate(Chunks, Chunk), Interleave), Size = Chunks * ChunkSize + Chunks, % 4 G + 32 Wordsize = erlang:system_info(wordsize), - case prim_file:write_file(Name, Data) of + case ?PRIM_FILE:write_file(Name, Data) of ok when Wordsize =:= 8 -> {ok,#file_info{size=Size}} = file:read_file_info(Name), - {ok,Fd} = prim_file:open(Name, [read]), + {ok,Fd} = ?PRIM_FILE:open(Name, [read]), check_large_write(Fd, ChunkSize, 0, Interleave); {error,einval} when Wordsize =:= 4 -> ok @@ -1262,11 +1125,11 @@ do_large_write(Name) -> check_large_write(Fd, ChunkSize, Pos, [X|Interleave]) -> Pos1 = Pos + ChunkSize, - {ok,Pos1} = prim_file:position(Fd, {cur,ChunkSize}), - {ok,[X]} = prim_file:read(Fd, 1), + {ok,Pos1} = ?PRIM_FILE:position(Fd, {cur,ChunkSize}), + {ok,<>} = ?PRIM_FILE:read(Fd, 1), check_large_write(Fd, ChunkSize, Pos1+1, Interleave); check_large_write(Fd, _, _, []) -> - eof = prim_file:read(Fd, 1), + eof = ?PRIM_FILE:read(Fd, 1), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1338,71 +1201,53 @@ allocate_and_assert(Fd, Offset, Length) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -delete_a(Config) when is_list(Config) -> - delete(Config, [], "_a"). - -delete_b(Config) when is_list(Config) -> - {ok, Handle} = ?PRIM_FILE:start(), - Result = delete(Config, Handle, "_b"), - ok = ?PRIM_FILE:stop(Handle), - Result. - -delete(Config, Handle, Suffix) -> +delete(Config) when is_list(Config) -> RootDir = proplists:get_value(priv_dir,Config), Name = filename:join(RootDir, atom_to_list(?MODULE) - ++"_delete"++Suffix++".fil"), + ++"_delete.fil"), {ok, Fd1} = ?PRIM_FILE:open(Name, [write]), ?PRIM_FILE:write(Fd1,"ok.\n"), ok = ?PRIM_FILE:close(Fd1), %% Check that the file is readable {ok, Fd2} = ?PRIM_FILE:open(Name, [read]), ok = ?PRIM_FILE:close(Fd2), - ok = ?PRIM_FILE_call(delete, Handle, [Name]), + ok = ?PRIM_FILE:delete(Name), %% Check that the file is not readable anymore {error, _} = ?PRIM_FILE:open(Name, [read]), %% Try deleting a nonexistent file - {error, enoent} = ?PRIM_FILE_call(delete, Handle, [Name]), + {error, enoent} = ?PRIM_FILE:delete(Name), ok. -rename_a(Config) when is_list(Config) -> - rename(Config, [], "_a"). - -rename_b(Config) when is_list(Config) -> - {ok, Handle} = ?PRIM_FILE:start(), - Result = rename(Config, Handle, "_b"), - ok = ?PRIM_FILE:stop(Handle), - Result. - -rename(Config, Handle, Suffix) -> +rename(Config) when is_list(Config) -> RootDir = proplists:get_value(priv_dir,Config), - FileName1 = atom_to_list(?MODULE)++"_rename"++Suffix++".fil", - FileName2 = atom_to_list(?MODULE)++"_rename"++Suffix++".ful", + FileName1 = atom_to_list(?MODULE)++"_rename.fil", + FileName2 = atom_to_list(?MODULE)++"_rename.ful", Name1 = filename:join(RootDir, FileName1), Name2 = filename:join(RootDir, FileName2), {ok,Fd1} = ?PRIM_FILE:open(Name1, [write]), ok = ?PRIM_FILE:close(Fd1), %% Rename, and check that it really changed name - ok = ?PRIM_FILE_call(rename, Handle, [Name1, Name2]), + ok = ?PRIM_FILE:rename(Name1, Name2), {error, _} = ?PRIM_FILE:open(Name1, [read]), {ok, Fd2} = ?PRIM_FILE:open(Name2, [read]), ok = ?PRIM_FILE:close(Fd2), %% Try renaming something to itself - ok = ?PRIM_FILE_call(rename, Handle, [Name2, Name2]), + ok = ?PRIM_FILE:rename(Name2, Name2), %% Try renaming something that doesn't exist {error, enoent} = - ?PRIM_FILE_call(rename, Handle, [Name1, Name2]), + ?PRIM_FILE:rename(Name1, Name2), %% Try renaming to something else than a string {error, badarg} = - ?PRIM_FILE_call(rename, Handle, [Name1, foobar]), + ?PRIM_FILE:rename(Name1, foobar), %% Move between directories DirName1 = filename:join(RootDir, atom_to_list(?MODULE) - ++"_rename_dir"++Suffix), + ++"_rename_dir"), DirName2 = filename:join(RootDir, atom_to_list(?MODULE) - ++"_second_rename_dir"++Suffix), + ++"_second_rename_dir"), Name1foo = filename:join(DirName1, "foo.fil"), Name2foo = filename:join(DirName2, "foo.fil"), Name2bar = filename:join(DirName2, "bar.dir"), @@ -1410,21 +1255,21 @@ rename(Config, Handle, Suffix) -> %% The name has to include the full file name, path is not enough expect( {error, eexist}, {error, eisdir}, - ?PRIM_FILE_call(rename, Handle, [Name2, DirName1])), + ?PRIM_FILE:rename(Name2, DirName1)), ok = - ?PRIM_FILE_call(rename, Handle, [Name2, Name1foo]), + ?PRIM_FILE:rename(Name2, Name1foo), %% Now rename the directory - ok = ?PRIM_FILE_call(rename, Handle, [DirName1, DirName2]), + ok = ?PRIM_FILE:rename(DirName1, DirName2), %% And check that the file is there now {ok,Fd3} = ?PRIM_FILE:open(Name2foo, [read]), ok = ?PRIM_FILE:close(Fd3), %% Try some dirty things now: move the directory into itself {error, Msg1} = - ?PRIM_FILE_call(rename, Handle, [DirName2, Name2bar]), + ?PRIM_FILE:rename(DirName2, Name2bar), io:format("Errmsg1: ~p",[Msg1]), %% move dir into a file in itself {error, Msg2} = - ?PRIM_FILE_call(rename, Handle, [DirName2, Name2foo]), + ?PRIM_FILE:rename(DirName2, Name2foo), io:format("Errmsg2: ~p",[Msg2]), ok. @@ -1657,165 +1502,19 @@ e_del_dir(Config) when is_list(Config) -> ok. -%% Trying reading and positioning from a compressed file. - -read_compressed(Config) when is_list(Config) -> - Data = proplists:get_value(data_dir, Config), - Real = filename:join(Data, "realmen.html.gz"), - {ok, Fd} = ?PRIM_FILE:open(Real, [read, compressed]), - try_read_file(Fd). - -%% Trying reading and positioning from an uncompressed file, -%% but with the compressed flag given. - -read_not_really_compressed(Config) when is_list(Config) -> - Data = proplists:get_value(data_dir, Config), - Priv = proplists:get_value(priv_dir, Config), - - %% The file realmen.html might have got CRs added (by WinZip). - %% Remove them, or the file positions will not be correct. - - Real = filename:join(Data, "realmen.html"), - RealPriv = filename:join(Priv, - atom_to_list(?MODULE)++"_realmen.html"), - {ok, RealDataBin} = ?PRIM_FILE:read_file(Real), - RealData = remove_crs(binary_to_list(RealDataBin), []), - ok = ?PRIM_FILE:write_file(RealPriv, RealData), - {ok, Fd} = ?PRIM_FILE:open(RealPriv, [read, compressed]), - try_read_file(Fd). - -remove_crs([$\r|Rest], Result) -> - remove_crs(Rest, Result); -remove_crs([C|Rest], Result) -> - remove_crs(Rest, [C|Result]); -remove_crs([], Result) -> - lists:reverse(Result). - -try_read_file(Fd) -> - %% Seek to the current position (nothing should happen). - - {ok, 0} = ?PRIM_FILE:position(Fd, 0), - {ok, 0} = ?PRIM_FILE:position(Fd, {cur, 0}), - - %% Read a few lines from a compressed file. - - ShouldBe = "Real Programmers Don't Use PASCAL\n", - {ok, ShouldBe} = ?PRIM_FILE:read(Fd, length(ShouldBe)), - - %% Now seek forward. - - {ok, 381} = ?PRIM_FILE:position(Fd, 381), - Back = "Back in the good old days -- the \"Golden Era\" " ++ - "of computers, it was\n", - {ok, Back} = ?PRIM_FILE:read(Fd, length(Back)), - - %% Try to search forward relative to the current position. - - {ok, CurPos} = ?PRIM_FILE:position(Fd, {cur, 0}), - RealPos = 4273, - {ok, RealPos} = ?PRIM_FILE:position(Fd, {cur, RealPos-CurPos}), - RealProg = "

  • Real Programmers aren't afraid to use GOTOs.\n", - {ok, RealProg} = ?PRIM_FILE:read(Fd, length(RealProg)), - - %% Seek backward. - - AfterTitle = length(""), - {ok, AfterTitle} = ?PRIM_FILE:position(Fd, AfterTitle), - Title = "Real Programmers Don't Use PASCAL\n", - {ok, Title} = ?PRIM_FILE:read(Fd, length(Title)), - - %% Done. - - ?PRIM_FILE:close(Fd), - ok. - -write_compressed(Config) when is_list(Config) -> - Priv = proplists:get_value(priv_dir, Config), - MyFile = filename:join(Priv, - atom_to_list(?MODULE)++"_test.gz"), - - %% Write a file. - - {ok, Fd} = ?PRIM_FILE:open(MyFile, [write, compressed]), - {ok, 0} = ?PRIM_FILE:position(Fd, 0), - Prefix = "hello\n", - End = "end\n", - ok = ?PRIM_FILE:write(Fd, Prefix), - {ok, 143} = ?PRIM_FILE:position(Fd, 143), - ok = ?PRIM_FILE:write(Fd, End), - ok = ?PRIM_FILE:close(Fd), - - %% Read the file and verify the contents. - - {ok, Fd1} = ?PRIM_FILE:open(MyFile, [read, compressed]), - {ok, Prefix} = ?PRIM_FILE:read(Fd1, length(Prefix)), - Second = lists:duplicate(143-length(Prefix), 0) ++ End, - {ok, Second} = ?PRIM_FILE:read(Fd1, length(Second)), - ok = ?PRIM_FILE:close(Fd1), - - %% Ensure that the file is compressed. - - TotalSize = 143 + length(End), - case ?PRIM_FILE:read_file_info(MyFile) of - {ok, #file_info{size=Size}} when Size < TotalSize -> - ok; - {ok, #file_info{size=Size}} when Size == TotalSize -> - ct:fail(file_not_compressed) - end, - - %% Write again to ensure that the file is truncated. - - {ok, Fd2} = ?PRIM_FILE:open(MyFile, [write, compressed]), - NewString = "aaaaaaaaaaa", - ok = ?PRIM_FILE:write(Fd2, NewString), - ok = ?PRIM_FILE:close(Fd2), - {ok, Fd3} = ?PRIM_FILE:open(MyFile, [read, compressed]), - {ok, NewString} = ?PRIM_FILE:read(Fd3, 1024), - ok = ?PRIM_FILE:close(Fd3), - - ok. - -compress_errors(Config) when is_list(Config) -> - Data = proplists:get_value(data_dir, Config), - {error, enoent} = ?PRIM_FILE:open("non_existing__", - [compressed, read]), - {error, einval} = ?PRIM_FILE:open("non_existing__", - [compressed, read, write]), - - %% Read a corrupted .gz file. - - Corrupted = filename:join(Data, "corrupted.gz"), - {ok, Fd} = ?PRIM_FILE:open(Corrupted, [read, compressed]), - {error, eio} = ?PRIM_FILE:read(Fd, 100), - ?PRIM_FILE:close(Fd), - - ok. - - -%% Test creating a hard link. -make_link_a(Config) when is_list(Config) -> - make_link(Config, [], "_a"). - -%% Test creating a hard link. -make_link_b(Config) when is_list(Config) -> - {ok, Handle} = ?PRIM_FILE:start(), - Result = make_link(Config, Handle, "_b"), - ok = ?PRIM_FILE:stop(Handle), - Result. - -make_link(Config, Handle, Suffix) -> +make_link(Config) when is_list(Config) -> RootDir = proplists:get_value(priv_dir, Config), NewDir = filename:join(RootDir, atom_to_list(?MODULE) - ++"_make_link"++Suffix), - ok = ?PRIM_FILE_call(make_dir, Handle, [NewDir]), + ++"_make_link"), + ok = ?PRIM_FILE:make_dir(NewDir), Name = filename:join(NewDir, "a_file"), ok = ?PRIM_FILE:write_file(Name, "some contents\n"), Alias = filename:join(NewDir, "an_alias"), Result = - case ?PRIM_FILE_call(make_link, Handle, [Name, Alias]) of + case ?PRIM_FILE:make_link(Name, Alias) of {error, enotsup} -> {skipped, "Links not supported on this platform"}; ok -> @@ -1826,12 +1525,12 @@ make_link(Config, Handle, Suffix) -> %% since they are not used on symbolic links. {ok, Info} = - ?PRIM_FILE_call(read_link_info, Handle, [Name]), + ?PRIM_FILE:read_link_info(Name), {ok, Info} = - ?PRIM_FILE_call(read_link_info, Handle, [Alias]), + ?PRIM_FILE:read_link_info(Alias), #file_info{links = 2, type = regular} = Info, {error, eexist} = - ?PRIM_FILE_call(make_link, Handle, [Name, Alias]), + ?PRIM_FILE:make_link(Name, Alias), ok end, @@ -1843,30 +1542,19 @@ read_link_info_for_non_link(Config) when is_list(Config) -> {ok, #file_info{type=directory}} = ?PRIM_FILE:read_link_info("."), ok. -%% Test operations on symbolic links (for Unix). -symlinks_a(Config) when is_list(Config) -> - symlinks(Config, [], "_a"). - -%% Test operations on symbolic links (for Unix). -symlinks_b(Config) when is_list(Config) -> - {ok, Handle} = ?PRIM_FILE:start(), - Result = symlinks(Config, Handle, "_b"), - ok = ?PRIM_FILE:stop(Handle), - Result. - -symlinks(Config, Handle, Suffix) -> +symlinks(Config) when is_list(Config) -> RootDir = proplists:get_value(priv_dir, Config), NewDir = filename:join(RootDir, atom_to_list(?MODULE) - ++"_make_symlink"++Suffix), - ok = ?PRIM_FILE_call(make_dir, Handle, [NewDir]), + ++"_make_symlink"), + ok = ?PRIM_FILE:make_dir(NewDir), Name = filename:join(NewDir, "a_plain_file"), ok = ?PRIM_FILE:write_file(Name, "some stupid content\n"), Alias = filename:join(NewDir, "a_symlink_alias"), Result = - case ?PRIM_FILE_call(make_symlink, Handle, [Name, Alias]) of + case ?PRIM_FILE:make_symlink(Name, Alias) of {error, enotsup} -> {skipped, "Links not supported on this platform"}; {error, eperm} -> @@ -1874,20 +1562,20 @@ symlinks(Config, Handle, Suffix) -> {skipped, "Windows user not privileged to create links"}; ok -> {ok, Info1} = - ?PRIM_FILE_call(read_file_info, Handle, [Name]), + ?PRIM_FILE:read_file_info(Name), {ok, Info1} = - ?PRIM_FILE_call(read_file_info, Handle, [Alias]), + ?PRIM_FILE:read_file_info(Alias), {ok, Info1} = - ?PRIM_FILE_call(read_link_info, Handle, [Name]), + ?PRIM_FILE:read_link_info(Name), #file_info{links = 1, type = regular} = Info1, {ok, Info2} = - ?PRIM_FILE_call(read_link_info, Handle, [Alias]), + ?PRIM_FILE:read_link_info(Alias), #file_info{links=1, type=symlink} = Info2, {ok, Name} = - ?PRIM_FILE_call(read_link, Handle, [Alias]), + ?PRIM_FILE:read_link(Alias), {ok, Name} = - ?PRIM_FILE_call(read_link_all, Handle, [Alias]), + ?PRIM_FILE:read_link_all(Alias), %% If all is good, delete dir again (avoid hanging dir on windows) rm_rf(?PRIM_FILE,NewDir), ok @@ -1907,10 +1595,9 @@ list_dir_limit(Config) when is_list(Config) -> RootDir = proplists:get_value(priv_dir, Config), NewDir = filename:join(RootDir, atom_to_list(?MODULE)++"_list_dir_limit"), - {ok, Handle1} = ?PRIM_FILE:start(), - ok = ?PRIM_FILE_call(make_dir, Handle1, [NewDir]), + ok = ?PRIM_FILE:make_dir(NewDir), Ref = erlang:start_timer(MaxTime*1000, self(), []), - Result = list_dir_limit_loop(NewDir, Handle1, Ref, MaxNumber, 0), + Result = list_dir_limit_loop(NewDir, Ref, MaxNumber, 0), Time = case erlang:cancel_timer(Ref) of false -> MaxTime; T -> MaxTime - (T div 1000) @@ -1920,21 +1607,18 @@ list_dir_limit(Config) when is_list(Config) -> {error, _Reason, N} -> N; _ -> 0 end, - {ok, Handle2} = ?PRIM_FILE:start(), - list_dir_limit_cleanup(NewDir, Handle2, Number, 0), - ok = ?PRIM_FILE:stop(Handle1), - ok = ?PRIM_FILE:stop(Handle2), + list_dir_limit_cleanup(NewDir, Number, 0), {ok, Number} = Result, {comment, "Created " ++ integer_to_list(Number) ++ " files in " ++ integer_to_list(Time) ++ " seconds."}. -list_dir_limit_loop(Dir, Handle, _Ref, N, Cnt) when Cnt >= N -> - list_dir_check(Dir, Handle, Cnt); -list_dir_limit_loop(Dir, Handle, Ref, N, Cnt) -> +list_dir_limit_loop(Dir, _Ref, N, Cnt) when Cnt >= N -> + list_dir_check(Dir, Cnt); +list_dir_limit_loop(Dir, Ref, N, Cnt) -> receive {timeout, Ref, []} -> - list_dir_check(Dir, Handle, Cnt) + list_dir_check(Dir, Cnt) after 0 -> Name = integer_to_list(Cnt), case ?PRIM_FILE:write_file(filename:join(Dir, Name), Name) of @@ -1942,23 +1626,23 @@ list_dir_limit_loop(Dir, Handle, Ref, N, Cnt) -> Next = Cnt + 1, case Cnt rem 100 of 0 -> - case list_dir_check(Dir, Handle, Next) of + case list_dir_check(Dir, Next) of {ok, Next} -> list_dir_limit_loop( - Dir, Handle, Ref, N, Next); + Dir, Ref, N, Next); Other -> Other end; _ -> - list_dir_limit_loop(Dir, Handle, Ref, N, Next) + list_dir_limit_loop(Dir, Ref, N, Next) end; {error, Reason} -> {error, Reason, Cnt} end end. -list_dir_check(Dir, Handle, Cnt) -> - case ?PRIM_FILE:list_dir(Handle, Dir) of +list_dir_check(Dir, Cnt) -> + case ?PRIM_FILE:list_dir(Dir) of {ok, ListDir} -> case length(ListDir) of Cnt -> @@ -1975,18 +1659,18 @@ list_dir_check(Dir, Handle, Cnt) -> %% Deletes N files while ignoring errors, then continues deleting %% as long as they exist. -list_dir_limit_cleanup(Dir, Handle, N, Cnt) when Cnt >= N -> +list_dir_limit_cleanup(Dir, N, Cnt) when Cnt >= N -> Name = integer_to_list(Cnt), - case ?PRIM_FILE:delete(Handle, filename:join(Dir, Name)) of + case ?PRIM_FILE:delete(filename:join(Dir, Name)) of ok -> - list_dir_limit_cleanup(Dir, Handle, N, Cnt+1); + list_dir_limit_cleanup(Dir, N, Cnt+1); _ -> ok end; -list_dir_limit_cleanup(Dir, Handle, N, Cnt) -> +list_dir_limit_cleanup(Dir, N, Cnt) -> Name = integer_to_list(Cnt), - ?PRIM_FILE:delete(Handle, filename:join(Dir, Name)), - list_dir_limit_cleanup(Dir, Handle, N, Cnt+1). + ?PRIM_FILE:delete(filename:join(Dir, Name)), + list_dir_limit_cleanup(Dir, N, Cnt+1). %%% %%% Test list_dir() on a non-existing pathname. @@ -1995,7 +1679,7 @@ list_dir_limit_cleanup(Dir, Handle, N, Cnt) -> list_dir_error(Config) -> Priv = proplists:get_value(priv_dir, Config), NonExisting = filename:join(Priv, "non-existing-dir"), - {error,enoent} = prim_file:list_dir(NonExisting), + {error,enoent} = ?PRIM_FILE:list_dir(NonExisting), ok. %%% @@ -2063,7 +1747,7 @@ do_run_large_file_test(Config, Run, Name0) -> {'DOWN',Mref,_,_,_} -> ok; {Tester,done} -> ok end, - prim_file:delete(Name) + ?PRIM_FILE:delete(Name) end), %% Run the test case. diff --git a/lib/kernel/test/sendfile_SUITE.erl b/lib/kernel/test/sendfile_SUITE.erl index bfa564c32c..b68903ed39 100644 --- a/lib/kernel/test/sendfile_SUITE.erl +++ b/lib/kernel/test/sendfile_SUITE.erl @@ -91,9 +91,12 @@ init_per_testcase(TC,Config) when TC == t_sendfile_recvduring; Send = fun(Sock) -> {_Size, Data} = sendfile_file_info(Filename), - {ok,D} = file:open(Filename, [raw,binary,read]), - prim_file:sendfile(D, Sock, 0, 0, 0, - [],[],[]), + {ok,Fd} = file:open(Filename, [raw,binary,read]), + %% Determine whether the driver has native support by + %% hitting the raw module directly; file:sendfile/5 will + %% land in the fallback if it doesn't. + RawModule = Fd#file_descriptor.module, + {ok, _Ignored} = RawModule:sendfile(Fd,Sock,0,0,0,[],[],[]), Data end, -- cgit v1.2.3 From e767da08af42e8fae0fa8bb8789a45a390ec8c0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Tue, 26 Sep 2017 11:46:34 +0200 Subject: Add microbenchmarks for file:read/2 and file:write/2 --- lib/kernel/test/file_SUITE.erl | 95 ++++++++++++++++++++++++++++++++++++++- lib/kernel/test/kernel_bench.spec | 1 + 2 files changed, 94 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 69cf8274a2..e35394ab53 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -99,6 +99,10 @@ -export([unicode_mode/1]). +-export([tiny_writes/1, tiny_writes_delayed/1, + large_writes/1, large_writes_delayed/1, + tiny_reads/1, tiny_reads_ahead/1]). + %% Debug exports -export([create_file_slow/2, create_file/2, create_bin/2]). -export([verify_file/2, verify_bin/3]). @@ -109,6 +113,8 @@ -export([disc_free/1, memsize/0]). -include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + -include_lib("kernel/include/file.hrl"). -define(THROW_ERROR(RES), throw({fail, ?LINE, RES})). @@ -126,7 +132,7 @@ all() -> ipread, pid2name, interleaved_read_write, otp_5814, otp_10852, large_file, large_write, read_line_1, read_line_2, read_line_3, read_line_4, standard_io, old_io_protocol, - unicode_mode + unicode_mode, {group, bench} ]. groups() -> @@ -156,11 +162,19 @@ groups() -> write_compressed, compress_errors, catenated_gzips, compress_async_crash]}, {links, [], - [make_link, read_link_info_for_non_link, symlinks]}]. + [make_link, read_link_info_for_non_link, symlinks]}, + {bench, [], + [tiny_writes, tiny_writes_delayed, + large_writes, large_writes_delayed, + tiny_reads, tiny_reads_ahead]}]. init_per_group(_GroupName, Config) -> Config. +end_per_group(bench, Config) -> + ScratchDir = proplists:get_value(priv_dir, Config), + file:delete(filename:join(ScratchDir, "benchmark_scratch_file")), + Config; end_per_group(_GroupName, Config) -> Config. @@ -3701,6 +3715,83 @@ do_large_write(Name) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Benchmarks +%% +%% Note that we only measure the time it takes to run the isolated file +%% operations and that the actual test runtime can differ significantly, +%% especially on the write side as the files need to be truncated before +%% writing. + +large_writes(Config) when is_list(Config) -> + Modes = [raw, binary], + OpCount = 4096, + Data = <<0:(64 bsl 10)/unit:8>>, + run_write_benchmark(Config, Modes, OpCount, Data). + +large_writes_delayed(Config) when is_list(Config) -> + %% Each write is exactly as large as the delay buffer, causing the writes + %% to pass through each time, giving us a decent idea of how much overhead + %% delayed_write adds. + Modes = [raw, binary, {delayed_write, 64 bsl 10, 2000}], + OpCount = 4096, + Data = <<0:(64 bsl 10)/unit:8>>, + run_write_benchmark(Config, Modes, OpCount, Data). + +tiny_writes(Config) when is_list(Config) -> + Modes = [raw, binary], + OpCount = 512 bsl 10, + Data = <<0>>, + run_write_benchmark(Config, Modes, OpCount, Data). + +tiny_writes_delayed(Config) when is_list(Config) -> + Modes = [raw, binary, {delayed_write, 512 bsl 10, 2000}], + OpCount = 512 bsl 10, + Data = <<0>>, + run_write_benchmark(Config, Modes, OpCount, Data). + +%% The read benchmarks assume that "benchmark_scratch_file" has been filled by +%% the write benchmarks. + +tiny_reads(Config) when is_list(Config) -> + Modes = [raw, binary], + OpCount = 512 bsl 10, + run_read_benchmark(Config, Modes, OpCount, 1). + +tiny_reads_ahead(Config) when is_list(Config) -> + Modes = [raw, binary, {read_ahead, 512 bsl 10}], + OpCount = 512 bsl 10, + run_read_benchmark(Config, Modes, OpCount, 1). + +run_write_benchmark(Config, Modes, OpCount, Data) -> + run_benchmark(Config, [write | Modes], OpCount, fun file:write/2, Data). + +run_read_benchmark(Config, Modes, OpCount, OpSize) -> + run_benchmark(Config, [read | Modes], OpCount, fun file:read/2, OpSize). + +run_benchmark(Config, Modes, OpCount, Fun, Arg) -> + ScratchDir = proplists:get_value(priv_dir, Config), + Path = filename:join(ScratchDir, "benchmark_scratch_file"), + {ok, Fd} = file:open(Path, Modes), + submit_throughput_results(Fun, [Fd, Arg], OpCount). + +submit_throughput_results(Fun, Args, Times) -> + MSecs = measure_repeated_file_op(Fun, Args, Times, millisecond), + IOPS = trunc(Times * (1000 / MSecs)), + ct_event:notify(#event{ name = benchmark_data, data = [{value,IOPS}] }), + {comment, io_lib:format("~p IOPS, ~p ms", [IOPS, trunc(MSecs)])}. + +measure_repeated_file_op(Fun, Args, Times, Unit) -> + Start = os:perf_counter(Unit), + repeated_apply(Fun, Args, Times), + os:perf_counter(Unit) - Start. + +repeated_apply(_F, _Args, Times) when Times =< 0 -> + ok; +repeated_apply(F, Args, Times) -> + erlang:apply(F, Args), + repeated_apply(F, Args, Times - 1). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% response_analysis(Module, Function, Arguments) -> diff --git a/lib/kernel/test/kernel_bench.spec b/lib/kernel/test/kernel_bench.spec index 8de60dae31..4de133f21b 100644 --- a/lib/kernel/test/kernel_bench.spec +++ b/lib/kernel/test/kernel_bench.spec @@ -1 +1,2 @@ {groups,"../kernel_test",zlib_SUITE,[bench]}. +{groups,"../kernel_test",file_SUITE,[bench]}. -- cgit v1.2.3 From 1d0aa9e9d621d4ce654a62a462c9a7f13cddaf65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Fri, 13 Oct 2017 13:23:49 +0200 Subject: Test opening raw files in the same manner as regular ones --- lib/kernel/test/file_SUITE.erl | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index e35394ab53..1a472af279 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -2060,13 +2060,22 @@ names(Config) when is_list(Config) -> ok = ?FILE_MODULE:close(Fd2), {ok,Fd3} = ?FILE_MODULE:open(Name3,read), ok = ?FILE_MODULE:close(Fd3), + + %% Now try the same on raw files. + {ok,Fd4} = ?FILE_MODULE:open(Name2, [read, raw]), + ok = ?FILE_MODULE:close(Fd4), + {ok,Fd4f} = ?FILE_MODULE:open(lists:flatten(Name2), [read, raw]), + ok = ?FILE_MODULE:close(Fd4f), + {ok,Fd5} = ?FILE_MODULE:open(Name3, [read, raw]), + ok = ?FILE_MODULE:close(Fd5), + case length(Name1) > 255 of true -> io:format("Path too long for an atom:\n\n~p\n", [Name1]); false -> Name4 = list_to_atom(Name1), - {ok,Fd4} = ?FILE_MODULE:open(Name4,read), - ok = ?FILE_MODULE:close(Fd4) + {ok,Fd6} = ?FILE_MODULE:open(Name4,read), + ok = ?FILE_MODULE:close(Fd6) end, %% Try some path names -- cgit v1.2.3 From 90a5278f114cffd42dc48235422cc51b10304dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Tue, 24 Oct 2017 14:03:46 +0200 Subject: pread/2 must always return a list of results --- lib/kernel/test/file_SUITE.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 1a472af279..fb66bbdb6c 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -1148,8 +1148,8 @@ pread_write_test(File, Data) -> end, I = Size + 17, ok = ?FILE_MODULE:pwrite(File, 0, Data), - Res = ?FILE_MODULE:pread(File, 0, I), - {ok, Data} = Res, + {ok, Data} = ?FILE_MODULE:pread(File, 0, I), + {ok, [Data]} = ?FILE_MODULE:pread(File, [{0, I}]), eof = ?FILE_MODULE:pread(File, I, 1), ok = ?FILE_MODULE:pwrite(File, [{0, Data}, {I, Data}]), {ok, [Data, eof, Data]} = -- cgit v1.2.3 From e509cd7a5bcee7e6e4bd9a9f408a2e2fef897c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Mon, 9 Oct 2017 13:13:13 +0200 Subject: Tighten timings in delayed_write The cumulative wait time was as long as the delay itself in the flush-on-size test, causing the test to pass because the write managed to time out. --- lib/kernel/test/file_SUITE.erl | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index fb66bbdb6c..9ee53b90d8 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -2948,20 +2948,22 @@ delayed_write(Config) when is_list(Config) -> %% %% Test caching and normal close of non-raw file {ok, Fd1} = - ?FILE_MODULE:open(File, [write, {delayed_write, Size+1, 2000}]), + ?FILE_MODULE:open(File, [write, {delayed_write, Size+1, 400}]), ok = ?FILE_MODULE:write(Fd1, Data1), - timer:sleep(1000), % Just in case the file system is slow + %% Wait for a reasonable amount of time to check whether the write was + %% practically instantaneous or actually delayed. + timer:sleep(100), {ok, Fd2} = ?FILE_MODULE:open(File, [read]), eof = ?FILE_MODULE:read(Fd2, 1), ok = ?FILE_MODULE:write(Fd1, Data1), % Data flush on size - timer:sleep(1000), % Just in case the file system is slow + timer:sleep(100), {ok, Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 2*Size+1), ok = ?FILE_MODULE:write(Fd1, Data1), - timer:sleep(3000), % Wait until data flush on timeout + timer:sleep(500), % Wait until data flush on timeout {ok, Data1Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 3*Size+1), ok = ?FILE_MODULE:write(Fd1, Data1), ok = ?FILE_MODULE:close(Fd1), % Data flush on close - timer:sleep(1000), % Just in case the file system is slow + timer:sleep(100), {ok, Data1Data1Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 4*Size+1), ok = ?FILE_MODULE:close(Fd2), %% @@ -2995,7 +2997,7 @@ delayed_write(Config) when is_list(Config) -> {'DOWN', Mref1, _, _, _} = Down1a -> ct:fail(Down1a) end, - timer:sleep(1000), % Just in case the file system is slow + timer:sleep(100), % Just in case the file system is slow {ok, Fd3} = ?FILE_MODULE:open(File, [read]), eof = ?FILE_MODULE:read(Fd3, 1), Child1 ! {Parent, continue, normal}, @@ -3005,7 +3007,7 @@ delayed_write(Config) when is_list(Config) -> {'DOWN', Mref1, _, _, _} = Down1b -> ct:fail(Down1b) end, - timer:sleep(1000), % Just in case the file system is slow + timer:sleep(100), % Just in case the file system is slow {ok, Data1} = ?FILE_MODULE:pread(Fd3, bof, Size+1), ok = ?FILE_MODULE:close(Fd3), %% @@ -3018,7 +3020,7 @@ delayed_write(Config) when is_list(Config) -> {'DOWN', Mref2, _, _, _} = Down2a -> ct:fail(Down2a) end, - timer:sleep(1000), % Just in case the file system is slow + timer:sleep(100), % Just in case the file system is slow {ok, Fd4} = ?FILE_MODULE:open(File, [read]), eof = ?FILE_MODULE:read(Fd4, 1), Child2 ! {Parent, continue, kill}, @@ -3028,7 +3030,7 @@ delayed_write(Config) when is_list(Config) -> {'DOWN', Mref2, _, _, _} = Down2b -> ct:fail(Down2b) end, - timer:sleep(1000), % Just in case the file system is slow + timer:sleep(100), % Just in case the file system is slow eof = ?FILE_MODULE:pread(Fd4, bof, 1), ok = ?FILE_MODULE:close(Fd4), %% -- cgit v1.2.3 From 30aac6489f5f0623ed9107c024e4cabc32460e2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Fri, 6 Oct 2017 14:43:59 +0200 Subject: Ensure that root paths are translated to our preferred form --- lib/kernel/test/file_SUITE.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 9ee53b90d8..af7bc2a5f7 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -2666,8 +2666,8 @@ altname(Config) when is_list(Config) -> {skipped, "Altname not supported on this platform"}; {ok, "LONGAL~1"} -> {ok, "A_FILE~1"} = ?FILE_MODULE:altname(Name), - {ok, "C:/"} = ?FILE_MODULE:altname("C:/"), - {ok, "C:\\"} = ?FILE_MODULE:altname("C:\\"), + {ok, "c:/"} = ?FILE_MODULE:altname("C:/"), + {ok, "c:/"} = ?FILE_MODULE:altname("C:\\"), {error,enoent} = ?FILE_MODULE:altname(NonexName), {ok, "short"} = ?FILE_MODULE:altname(ShortName), ok -- cgit v1.2.3 From 52937d5b26af2b28573ab90bdcb7f59e9a18ff09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Mon, 9 Oct 2017 20:18:52 +0200 Subject: Ensure that trailing slashes are ignored on list_dir --- lib/kernel/test/file_SUITE.erl | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index af7bc2a5f7..767d88f113 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -654,6 +654,10 @@ cur_dir_0(Config) when is_list(Config) -> {ok,NewDirFiles} = ?FILE_MODULE:list_dir("."), true = lists:member(UncommonName,NewDirFiles), + %% Ensure that we get the same result with a trailing slash; the + %% APIs used on Windows will choke on them if passed directly. + {ok,NewDirFiles} = ?FILE_MODULE:list_dir("./"), + %% Delete the directory and return to the old current directory %% and check that the created file isn't there (too!) expect({error, einval}, {error, eacces}, -- cgit v1.2.3 From 93365454e55c7fc9f78f52e5a383f57a32b92de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Tue, 10 Oct 2017 09:45:02 +0200 Subject: Remove disk_log_SUITE:evil This test revolves around the possibility that the underlying port can be killed, which is nonsense now that the file suite no longer uses ports for anything. --- lib/kernel/test/disk_log_SUITE.erl | 119 +------------------------------------ 1 file changed, 2 insertions(+), 117 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/disk_log_SUITE.erl b/lib/kernel/test/disk_log_SUITE.erl index fe2fc778f2..7beaadb1d6 100644 --- a/lib/kernel/test/disk_log_SUITE.erl +++ b/lib/kernel/test/disk_log_SUITE.erl @@ -89,8 +89,6 @@ dist_terminate/1, dist_accessible/1, dist_deadlock/1, dist_open2/1, other_groups/1, - evil/1, - otp_6278/1, otp_10131/1]). -export([head_fun/1, hf/0, lserv/1, @@ -123,7 +121,7 @@ [halt_int, wrap_int, halt_ext, wrap_ext, read_mode, head, notif, new_idx_vsn, reopen, block, unblock, open, close, error, chunk, truncate, many_users, info, change_size, - change_attribute, distribution, evil, otp_6278, otp_10131]). + change_attribute, distribution, otp_6278, otp_10131]). %% These test cases should be skipped if the VxWorks card is %% configured without NFS cache. @@ -149,7 +147,7 @@ all() -> {group, open}, {group, close}, {group, error}, chunk, truncate, many_users, {group, info}, {group, change_size}, change_attribute, - {group, distribution}, evil, otp_6278, otp_10131]. + {group, distribution}, otp_6278, otp_10131]. groups() -> [{halt_int, [], [halt_int_inf, {group, halt_int_sz}]}, @@ -4676,119 +4674,6 @@ other_groups(Conf) when is_list(Conf) -> ok. --define(MAX, ?MAX_FWRITE_CACHE). % as in disk_log_1.erl -%% Evil cases such as closed file descriptor port. -evil(Conf) when is_list(Conf) -> - Dir = ?privdir(Conf), - File = filename:join(Dir, "n.LOG"), - Log = n, - - %% Not a very thorough test. - - ok = setup_evil_filled_cache_wrap(Log, Dir), - {error, {file_error,_,einval}} = disk_log:log(Log, apa), - ok = disk_log:close(Log), - - ok = setup_evil_filled_cache_halt(Log, Dir), - {error, {file_error,_,einval}} = disk_log:truncate(Log, apa), - ok = stop_evil(Log), - - %% White box test. - file:delete(File), - Ports0 = erlang:ports(), - {ok, Log} = disk_log:open([{name,Log},{file,File},{type,halt}, - {size,?MAX+50},{format,external}]), - [Fd] = erlang:ports() -- Ports0, - {B,_} = x_mk_bytes(30), - ok = disk_log:blog(Log, <<0:(?MAX-1)/unit:8>>), - exit(Fd, kill), - {error, {file_error,_,einval}} = disk_log:blog_terms(Log, [B,B]), - ok= disk_log:close(Log), - file:delete(File), - - ok = setup_evil_wrap(Log, Dir), - {error, {file_error,_,einval}} = disk_log:close(Log), - - ok = setup_evil_wrap(Log, Dir), - {error, {file_error,_,einval}} = disk_log:log(Log, apa), - ok = stop_evil(Log), - - ok = setup_evil_halt(Log, Dir), - {error, {file_error,_,einval}} = disk_log:log(Log, apa), - ok = stop_evil(Log), - - ok = setup_evil_wrap(Log, Dir), - {error, {file_error,_,einval}} = disk_log:reopen(Log, apa), - {error, {file_error,_,einval}} = disk_log:reopen(Log, apa), - ok = stop_evil(Log), - - ok = setup_evil_wrap(Log, Dir), - {error, {file_error,_,einval}} = disk_log:reopen(Log, apa), - ok = stop_evil(Log), - - ok = setup_evil_wrap(Log, Dir), - {error, {file_error,_,einval}} = disk_log:inc_wrap_file(Log), - ok = stop_evil(Log), - - ok = setup_evil_wrap(Log, Dir), - {error, {file_error,_,einval}} = disk_log:chunk(Log, start), - ok = stop_evil(Log), - - ok = setup_evil_wrap(Log, Dir), - {error, {file_error,_,einval}} = disk_log:truncate(Log), - ok = stop_evil(Log), - - ok = setup_evil_wrap(Log, Dir), - {error, {file_error,_,einval}} = disk_log:chunk_step(Log, start, 1), - ok = stop_evil(Log), - - io:format("messages: ~p~n", [erlang:process_info(self(), messages)]), - del(File, 2), - file:delete(File), - ok. - -setup_evil_wrap(Log, Dir) -> - setup_evil(Log, [{type,wrap},{size,{100,2}}], Dir). - -setup_evil_halt(Log, Dir) -> - setup_evil(Log, [{type,halt},{size,10000}], Dir). - -setup_evil(Log, Args, Dir) -> - File = filename:join(Dir, lists:concat([Log, ".LOG"])), - file:delete(File), - del(File, 2), - ok = disk_log:start(), - Ports0 = erlang:ports(), - {ok, Log} = disk_log:open([{name,Log},{file,File} | Args]), - [Fd] = erlang:ports() -- Ports0, - exit(Fd, kill), - ok = disk_log:log_terms(n, [<<0:10/unit:8>>]), - timer:sleep(2500), % TIMEOUT in disk_log_1.erl is 2000 - ok. - -stop_evil(Log) -> - {error, _} = disk_log:close(Log), - ok. - -setup_evil_filled_cache_wrap(Log, Dir) -> - setup_evil_filled_cache(Log, [{type,wrap},{size,{?MAX,2}}], Dir). - -setup_evil_filled_cache_halt(Log, Dir) -> - setup_evil_filled_cache(Log, [{type,halt},{size,infinity}], Dir). - -%% The cache is filled, and the file descriptor port gone. -setup_evil_filled_cache(Log, Args, Dir) -> - File = filename:join(Dir, lists:concat([Log, ".LOG"])), - file:delete(File), - del(File, 2), - ok = disk_log:start(), - Ports0 = erlang:ports(), - {ok, Log} = disk_log:open([{name,Log},{file,File} | Args]), - [Fd] = erlang:ports() -- Ports0, - ok = disk_log:log_terms(n, [<<0:?MAX/unit:8>>]), - exit(Fd, kill), - ok. - %% OTP-6278. open/1 creates no status or crash report. otp_6278(Conf) when is_list(Conf) -> Dir = ?privdir(Conf), -- cgit v1.2.3 From 1edafdf65feaab9a098e5888b95c059d28ec1a6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Tue, 10 Oct 2017 13:19:47 +0200 Subject: Use lexemes/2 instead of the deprecated tokens/2 --- lib/kernel/test/file_SUITE.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 767d88f113..2813028972 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -489,7 +489,7 @@ um_check_unicode(_Utf8Bin, {ok, _ListOrBin}, _, _UTF8_) -> um_filename(Bin, Dir, Options) when is_binary(Bin) -> um_filename(binary_to_list(Bin), Dir, Options); um_filename(Str = [_|_], Dir, Options) -> - Name = hd(string:tokens(Str, ":")), + Name = hd(string:lexemes(Str, ":")), Enc = atom_to_list(proplists:get_value(encoding, Options, latin1)), File = case lists:member(binary, Options) of true -> @@ -851,7 +851,7 @@ no_untranslatable_names() -> end. start_node(Name, Args) -> - [_,Host] = string:tokens(atom_to_list(node()), "@"), + [_,Host] = string:lexemes(atom_to_list(node()), "@"), ct:log("Trying to start ~w@~s~n", [Name,Host]), case test_server:start_node(Name, peer, [{args,Args}]) of {error,Reason} -> -- cgit v1.2.3 From 122d54acae3bac94c223b7d809cb78a08c934116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Wed, 11 Oct 2017 07:40:42 +0200 Subject: Operations on closed raw files should return EINVAL --- lib/kernel/test/file_SUITE.erl | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 2813028972..5e5e7cf6b2 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -1039,6 +1039,23 @@ close(Config) when is_list(Config) -> Val = ?FILE_MODULE:close(Fd1), io:format("Second close gave: ~p",[Val]), + %% All operations on a closed raw file should EINVAL, even if they're not + %% supported on the current platform. + {ok,Fd2} = ?FILE_MODULE:open(Name, [read, write, raw]), + ok = ?FILE_MODULE:close(Fd2), + + {error, einval} = ?FILE_MODULE:advise(Fd2, 5, 5, normal), + {error, einval} = ?FILE_MODULE:allocate(Fd2, 5, 5), + {error, einval} = ?FILE_MODULE:close(Fd2), + {error, einval} = ?FILE_MODULE:datasync(Fd2), + {error, einval} = ?FILE_MODULE:position(Fd2, 5), + {error, einval} = ?FILE_MODULE:pread(Fd2, 5, 1), + {error, einval} = ?FILE_MODULE:pwrite(Fd2, 5, "einval please"), + {error, einval} = ?FILE_MODULE:read(Fd2, 1), + {error, einval} = ?FILE_MODULE:sync(Fd2), + {error, einval} = ?FILE_MODULE:truncate(Fd2), + {error, einval} = ?FILE_MODULE:write(Fd2, "einval please"), + [] = flush(), ok. -- cgit v1.2.3 From 5273bc7c0fcf5354922eb3fd2eaf7c3e5ded6440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Wed, 11 Oct 2017 15:18:22 +0200 Subject: Volume-relative paths must work on Windows --- lib/kernel/test/file_SUITE.erl | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 5e5e7cf6b2..6139d2161e 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -99,6 +99,8 @@ -export([unicode_mode/1]). +-export([volume_relative_paths/1]). + -export([tiny_writes/1, tiny_writes_delayed/1, large_writes/1, large_writes_delayed/1, tiny_reads/1, tiny_reads_ahead/1]). @@ -126,8 +128,8 @@ suite() -> all() -> [unicode, altname, read_write_file, {group, dirs}, - {group, files}, delete, rename, names, {group, errors}, - {group, compression}, {group, links}, copy, + {group, files}, delete, rename, names, volume_relative_paths, + {group, errors}, {group, compression}, {group, links}, copy, delayed_write, read_ahead, segment_read, segment_write, ipread, pid2name, interleaved_read_write, otp_5814, otp_10852, large_file, large_write, read_line_1, read_line_2, read_line_3, @@ -2120,6 +2122,22 @@ names(Config) when is_list(Config) -> [] = flush(), ok. +volume_relative_paths(Config) when is_list(Config) -> + case os:type() of + {win32, _} -> + {ok, [Drive, $: | _]} = file:get_cwd(), + %% Relative to current device root. + {ok, RootInfo} = file:read_file_info([Drive, $:, $/]), + {ok, RootInfo} = file:read_file_info("/"), + %% Relative to current device directory. + {ok, DirContents} = file:list_dir([Drive, $:]), + {ok, DirContents} = file:list_dir("."), + [] = flush(), + ok; + _ -> + {skip, "This test is Windows-specific."} + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -- cgit v1.2.3 From 072a991bc2eafb283cd25eaad1f4fd2f70c0f8b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Fri, 13 Oct 2017 11:47:41 +0200 Subject: Reads that draw from both buffer and file must work --- lib/kernel/test/file_SUITE.erl | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 6139d2161e..3cb249c565 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -3161,6 +3161,16 @@ read_ahead(Config) when is_list(Config) -> Data1Data2Data3 = Data1++Data2++Data3, {ok, Data1Data2Data3} = ?FILE_MODULE:read(Fd5, 3*Size+1), ok = ?FILE_MODULE:close(Fd5), + + %% Ensure that a read that draws from both the buffer and the file won't + %% return anything wonky. + SplitData = << <<(I rem 256)>> || I <- lists:seq(1, 1024) >>, + file:write_file(File, SplitData), + {ok, Fd6} = ?FILE_MODULE:open(File, [raw, read, binary, {read_ahead, 256}]), + {ok, <<1>>} = file:read(Fd6, 1), + <<1, Shifted:512/binary, _Rest/binary>> = SplitData, + {ok, Shifted} = file:read(Fd6, 512), + %% [] = flush(), ok. -- cgit v1.2.3 From 601c9cd5e9440b78101ef9b73409ad3912f3981e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Fri, 13 Oct 2017 14:47:33 +0200 Subject: get_cwd/1 on non-existent drives should error out --- lib/kernel/test/file_SUITE.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 3cb249c565..0cb8087a76 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -712,10 +712,15 @@ win_cur_dir_1(_Config) -> %% Get the drive letter from the current directory, %% and try to get current directory for that drive. - [Drive,$:|_] = BaseDir, - {ok,BaseDir} = ?FILE_MODULE:get_cwd([Drive,$:]), + [CurDrive,$:|_] = BaseDir, + {ok,BaseDir} = ?FILE_MODULE:get_cwd([CurDrive,$:]), io:format("BaseDir = ~s\n", [BaseDir]), + %% We should error out on non-existent drives. Any reasonable system will + %% have at least one. + CurDirs = [?FILE_MODULE:get_cwd([Drive,$:]) || Drive <- lists:seq($A, $Z)], + lists:member({error,eaccess}, CurDirs), + %% Unfortunately, there is no way to move away from the %% current drive as we can't use the "subst" command from %% a SSH connection. We can't test any more. -- cgit v1.2.3 From c6f47dd9919c8b1555980a990cbc7af0fd1ca529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Wed, 25 Oct 2017 15:11:31 +0200 Subject: Stop testing the artificial size limit on writes The old driver didn't fall back to using write(2) if writev(2) failed due to the combined length of the iov overflowing a ssize_t, but the new one doesn't have any problems with it so we failed this test with a case_clause error on 32-bit machines. --- lib/kernel/test/prim_file_SUITE.erl | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl index 8a1e99b332..db753679ea 100644 --- a/lib/kernel/test/prim_file_SUITE.erl +++ b/lib/kernel/test/prim_file_SUITE.erl @@ -1113,15 +1113,10 @@ do_large_write(Name) -> Chunk = <<0:ChunkSize/unit:8>>, Data = zip_data(lists:duplicate(Chunks, Chunk), Interleave), Size = Chunks * ChunkSize + Chunks, % 4 G + 32 - Wordsize = erlang:system_info(wordsize), - case ?PRIM_FILE:write_file(Name, Data) of - ok when Wordsize =:= 8 -> - {ok,#file_info{size=Size}} = file:read_file_info(Name), - {ok,Fd} = ?PRIM_FILE:open(Name, [read]), - check_large_write(Fd, ChunkSize, 0, Interleave); - {error,einval} when Wordsize =:= 4 -> - ok - end. + ok = ?PRIM_FILE:write_file(Name, Data), + {ok,#file_info{size=Size}} = file:read_file_info(Name), + {ok,Fd} = ?PRIM_FILE:open(Name, [read]), + check_large_write(Fd, ChunkSize, 0, Interleave). check_large_write(Fd, ChunkSize, Pos, [X|Interleave]) -> Pos1 = Pos + ChunkSize, -- cgit v1.2.3 From 313a3fa962bb159a581c0e5a3105ece863a1dc21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Wed, 1 Nov 2017 10:02:55 +0100 Subject: Stop testing use_threads in sendfile_SUITE The option no longer does anything at all. --- lib/kernel/test/sendfile_SUITE.erl | 97 ++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 51 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/sendfile_SUITE.erl b/lib/kernel/test/sendfile_SUITE.erl index b68903ed39..4872ce53b7 100644 --- a/lib/kernel/test/sendfile_SUITE.erl +++ b/lib/kernel/test/sendfile_SUITE.erl @@ -23,30 +23,39 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). --compile(export_all). - -all() -> [{group,async_threads}, - {group,no_async_threads}]. - -groups() -> - [{async_threads,[],tcs()}, - {no_async_threads,[],tcs()}]. - -tcs() -> - [t_sendfile_small - ,t_sendfile_big_all - ,t_sendfile_big_size - ,t_sendfile_many_small - ,t_sendfile_partial - ,t_sendfile_offset - ,t_sendfile_sendafter - ,t_sendfile_recvafter - ,t_sendfile_recvafter_remoteclose - ,t_sendfile_sendduring - ,t_sendfile_recvduring - ,t_sendfile_closeduring - ,t_sendfile_crashduring - ]. +-export([all/0, init_per_suite/1, end_per_suite/1, init_per_testcase/2]). + +-export([sendfile_server/2, sendfile_do_recv/2, init/1, handle_event/2]). + +-export( + [t_sendfile_small/1, + t_sendfile_big_all/1, + t_sendfile_big_size/1, + t_sendfile_many_small/1, + t_sendfile_partial/1, + t_sendfile_offset/1, + t_sendfile_sendafter/1, + t_sendfile_recvafter/1, + t_sendfile_recvafter_remoteclose/1, + t_sendfile_sendduring/1, + t_sendfile_recvduring/1, + t_sendfile_closeduring/1, + t_sendfile_crashduring/1]). + +all() -> + [t_sendfile_small, + t_sendfile_big_all, + t_sendfile_big_size, + t_sendfile_many_small, + t_sendfile_partial, + t_sendfile_offset, + t_sendfile_sendafter, + t_sendfile_recvafter, + t_sendfile_recvafter_remoteclose, + t_sendfile_sendduring, + t_sendfile_recvduring, + t_sendfile_closeduring, + t_sendfile_crashduring]. init_per_suite(Config) -> case {os:type(),os:version()} of @@ -72,19 +81,6 @@ init_per_suite(Config) -> end_per_suite(Config) -> file:delete(proplists:get_value(big_file, Config)). -init_per_group(async_threads,Config) -> - case erlang:system_info(thread_pool_size) of - 0 -> - {skip,"No async threads"}; - _ -> - [{sendfile_opts,[{use_threads,true}]}|Config] - end; -init_per_group(no_async_threads,Config) -> - [{sendfile_opts,[{use_threads,false}]}|Config]. - -end_per_group(_,_Config) -> - ok. - init_per_testcase(TC,Config) when TC == t_sendfile_recvduring; TC == t_sendfile_sendduring -> Filename = proplists:get_value(small_file, Config), @@ -108,9 +104,8 @@ init_per_testcase(TC,Config) when TC == t_sendfile_recvduring; ct:log("Error: ~p",[Error]), {skip,"Not supported"} end; -init_per_testcase(_Tc,Config) -> - Config ++ [{sendfile_opts,[{use_threads,false}]}]. - +init_per_testcase(_TC,Config) -> + Config. t_sendfile_small(Config) when is_list(Config) -> Filename = proplists:get_value(small_file, Config), @@ -127,7 +122,7 @@ t_sendfile_small(Config) when is_list(Config) -> t_sendfile_many_small(Config) when is_list(Config) -> Filename = proplists:get_value(small_file, Config), FileOpts = proplists:get_value(file_opts, Config, []), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), error_logger:add_report_handler(?MODULE,[self()]), @@ -154,7 +149,7 @@ t_sendfile_many_small(Config) when is_list(Config) -> t_sendfile_big_all(Config) when is_list(Config) -> Filename = proplists:get_value(big_file, Config), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), Send = fun(Sock) -> {ok, #file_info{size = Size}} = @@ -168,7 +163,7 @@ t_sendfile_big_all(Config) when is_list(Config) -> t_sendfile_big_size(Config) -> Filename = proplists:get_value(big_file, Config), FileOpts = proplists:get_value(file_opts, Config, []), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), SendAll = fun(Sock) -> {ok, #file_info{size = Size}} = @@ -183,7 +178,7 @@ t_sendfile_big_size(Config) -> t_sendfile_partial(Config) -> Filename = proplists:get_value(small_file, Config), FileOpts = proplists:get_value(file_opts, Config, []), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), SendSingle = fun(Sock) -> {_Size, <>} = @@ -220,7 +215,7 @@ t_sendfile_partial(Config) -> t_sendfile_offset(Config) -> Filename = proplists:get_value(small_file, Config), FileOpts = proplists:get_value(file_opts, Config, []), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), Send = fun(Sock) -> {_Size, <<_:5/binary,Data:3/binary,_/binary>> = AllData} = @@ -236,7 +231,7 @@ t_sendfile_offset(Config) -> t_sendfile_sendafter(Config) -> Filename = proplists:get_value(small_file, Config), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), Send = fun(Sock) -> {Size, Data} = sendfile_file_info(Filename), @@ -249,7 +244,7 @@ t_sendfile_sendafter(Config) -> t_sendfile_recvafter(Config) -> Filename = proplists:get_value(small_file, Config), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), Send = fun(Sock) -> {Size, Data} = sendfile_file_info(Filename), @@ -282,7 +277,7 @@ t_sendfile_recvafter_remoteclose(Config) -> t_sendfile_sendduring(Config) -> Filename = proplists:get_value(big_file, Config), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), Send = fun(Sock) -> {ok, #file_info{size = Size}} = @@ -299,7 +294,7 @@ t_sendfile_sendduring(Config) -> t_sendfile_recvduring(Config) -> Filename = proplists:get_value(big_file, Config), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), Send = fun(Sock) -> {ok, #file_info{size = Size}} = @@ -318,7 +313,7 @@ t_sendfile_recvduring(Config) -> t_sendfile_closeduring(Config) -> Filename = proplists:get_value(big_file, Config), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), Send = fun(Sock,SFServPid) -> spawn_link(fun() -> @@ -348,7 +343,7 @@ t_sendfile_closeduring(Config) -> t_sendfile_crashduring(Config) -> Filename = proplists:get_value(big_file, Config), - SendfileOpts = proplists:get_value(sendfile_opts, Config), + SendfileOpts = proplists:get_value(sendfile_opts, Config, []), error_logger:add_report_handler(?MODULE,[self()]), -- cgit v1.2.3 From 4b5357ad04397b09f1acedd81e9439b0af4549ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Thu, 2 Nov 2017 16:41:26 +0100 Subject: Update file performance advice The parts relating to drivers/ports are now obsolete, and the provided example was far noisier than it had to be; the only relevant metric is the number of calls and it's up to the user to decide how those will be reduced. One could argue for its complete removal, but I'm inclined to leave it be. --- lib/kernel/doc/src/file.xml | 220 +++++++++++++++++--------------------------- 1 file changed, 82 insertions(+), 138 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index 2ab35b9b05..3e3c5b3bf3 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -33,11 +33,14 @@

    This module provides an interface to the file system.

    -

    On operating systems with thread support, - file operations can be performed in threads of their own, allowing - other Erlang processes to continue executing in parallel with - the file operations. See command-line flag - +A in erl(1).

    + +

    File operations are only guaranteed to appear atomic when going + through the same file server. A NIF or other OS process may observe + intermediate steps on certain operations on some operating systems, + eg. renaming an existing file on Windows, or + write_file_info/2 + on any OS at the time of writing.

    +

    Regarding filename encoding, the Erlang VM can operate in two modes. The current mode can be queried using function @@ -1438,8 +1441,12 @@ f.txt: {person, "kalle", 25}. which is 1970-01-01 00:00 UTC.

    Default is {time, local}.

    -

    If the option raw is set, the file server is not called - and only information about local files is returned.

    +

    If the option raw is set, the file server is not called and + only information about local files is returned. Note that this will + break this module's atomicity guarantees as it can race with a + concurrent call to + write_file_info/1,2 +

    As file times are stored in POSIX time on most OS, it is faster to query file information with option posix.

    @@ -1687,8 +1694,12 @@ f.txt: {person, "kalle", 25}. except that if Name is a symbolic link, information about the link is returned in the file_info record and the type field of the record is set to symlink.

    -

    If the option raw is set, the file server is not called - and only information about local files is returned.

    +

    If the option raw is set, the file server is not called and + only information about local files is returned. Note that this will + break this module's atomicity guarantees as it can race with a + concurrent call to + write_file_info/1,2 +

    If Name is not a symbolic link, this function returns the same result as read_file_info/1. On platforms that do not support symbolic links, this function @@ -2148,144 +2159,77 @@ f.txt: {person, "kalle", 25}.

    Performance -

    Some operating system file operations, for example, a - sync/1 or close/1 on a huge file, can block their - calling thread for seconds. If this affects the emulator main - thread, the response time is no longer in the order of - milliseconds, depending on the definition of "soft" in soft - real-time system.

    -

    If the device driver thread pool is active, file operations are - done through those threads instead, so the emulator can go on - executing Erlang processes. Unfortunately, the time for serving a - file operation increases because of the extra scheduling required - from the operating system.

    -

    If the device driver thread pool is disabled or of size 0, large - file reads and writes are segmented into many smaller, which - enable the emulator to serve other processes during the file - operation. This has the same effect as when using the thread - pool, but with larger overhead. Other file operations, for - example, sync/1 or close/1 on a huge file, still are - a problem.

    -

    For increased performance, raw files are recommended. Raw files - use the file system of the host machine of the node.

    +

    For increased performance, raw files are recommended.

    +

    A normal file is really a process so it can be used as an I/O + device (see io). + Therefore, when data is written to a normal file, the sending of the + data to the file process, copies all data that are not binaries. Opening + the file in binary mode and writing binaries is therefore recommended. + If the file is opened on another node, or if the file server runs as + slave to the file server of another node, also binaries are copied.

    -

    - For normal files (non-raw), the file server is used to find the files, - and if the node is running its file server as slave to the file server - of another node, and the other node runs on some other host machine, - they can have different file systems. - However, this is seldom a problem.

    +

    Raw files use the file system of the host machine of the node. + For normal files (non-raw), the file server is used to find the files, + and if the node is running its file server as slave to the file server + of another node, and the other node runs on some other host machine, + they can have different file systems. + However, this is seldom a problem.

    -

    A normal file is really a process so it can be used as an I/O - device (see - io). - Therefore, when data is written to a - normal file, the sending of the data to the file process, copies - all data that are not binaries. Opening the file in binary mode - and writing binaries is therefore recommended. If the file is - opened on another node, or if the file server runs as slave to - the file server of another node, also binaries are copied.

    -

    Caching data to reduce the number of file operations, or rather - the number of calls to the file driver, generally increases - performance. The following function writes 4 MBytes in 23 - seconds when tested:

    +

    open/2 can be given the + options delayed_write and read_ahead to turn on caching, + which will reduce the number of operating system calls and greatly + improve performance for small reads and writes. However, the overhead + won't disappear completely and it's best to keep the number of file + operations to a minimum. As a contrived example, the following function + writes 4MB in 2.5 seconds when tested:

    + = 0 -> - {ok, FD} = file:open(Name, [raw, write, delayed_write, binary]), - ok = create_file_slow(FD, 0, N), - ok = ?FILE_MODULE:close(FD), - ok. - -create_file_slow(FD, M, M) -> +create_file_slow(Name) -> + {ok, Fd} = file:open(Name, [raw, write, delayed_write, binary]), + create_file_slow_1(Fd, 4 bsl 20), + file:close(Fd). + +create_file_slow_1(_Fd, 0) -> ok; -create_file_slow(FD, M, N) -> - ok = file:write(FD, <>), - create_file_slow(FD, M+1, N).]]> +create_file_slow_1(Fd, M) -> + ok = file:write(Fd, <<0>>), + create_file_slow_1(Fd, M - 1).]]> + +

    The following functionally equivalent code writes 128 bytes per call + to write/2 and so does the + same work in 0.08 seconds, which is roughly 30 times faster:

    -

    The following, functionally equivalent, function collects 1024 - entries into a list of 128 32-byte binaries before each call to - write/2 and so - does the same work in 0.52 seconds, - which is 44 times faster:

    = 0 -> - {ok, FD} = file:open(Name, [raw, write, delayed_write, binary]), - ok = create_file(FD, 0, N), - ok = ?FILE_MODULE:close(FD), +create_file(Name) -> + {ok, Fd} = file:open(Name, [raw, write, delayed_write, binary]), + create_file_1(Fd, 4 bsl 20), + file:close(Fd), ok. - -create_file(FD, M, M) -> + +create_file_1(_Fd, 0) -> ok; -create_file(FD, M, N) when M + 1024 =< N -> - create_file(FD, M, M + 1024, []), - create_file(FD, M + 1024, N); -create_file(FD, M, N) -> - create_file(FD, M, N, []). - -create_file(FD, M, M, R) -> - ok = file:write(FD, R); -create_file(FD, M, N0, R) when M + 8 =< N0 -> - N1 = N0-1, N2 = N0-2, N3 = N0-3, N4 = N0-4, - N5 = N0-5, N6 = N0-6, N7 = N0-7, N8 = N0-8, - create_file(FD, M, N8, - [<> | R]); -create_file(FD, M, N0, R) -> - N1 = N0-1, - create_file(FD, M, N1, [<> | R]).]]> +create_file_1(Fd, M) when M >= 128 -> + ok = file:write(Fd, <<0:(128)/unit:8>>), + create_file_1(Fd, M - 128); +create_file_1(Fd, M) -> + ok = file:write(Fd, <<0:(M)/unit:8>>), + create_file_1(Fd, M - 1).]]> - -

    Trust only your own benchmarks. If the list length in - create_file/2 above is increased, it runs slightly - faster, but consumes more memory and causes more memory - fragmentation. How much this affects your application is - something that this simple benchmark cannot predict.

    -

    If the size of each binary is increased to 64 bytes, it - also runs slightly faster, but the code is then twice as clumsy. - In the current implementation, binaries larger than 64 bytes are - stored in memory common to all processes and not copied when - sent between processes, while these smaller binaries are stored - on the process heap and copied when sent like any other term.

    -

    So, with a binary size of 68 bytes, create_file/2 runs - 30 percent slower than with 64 bytes, and causes much more - memory fragmentation. Notice that if the binaries were to be sent - between processes (for example, a non-raw file), the results - would probably be completely different.

    -
    -

    A raw file is really a port. When writing data to a port, it is - efficient to write a list of binaries. It is not needed to - flatten a deep list before writing. On Unix hosts, scatter output, - which writes a set of buffers in one operation, is used when - possible. In this way write(FD, [Bin1, Bin2 | Bin3]) - writes the contents of the binaries without copying the data - at all, except for perhaps deep down in the operating system - kernel.

    -

    For raw files, pwrite/2 and pread/2 are - efficiently implemented. The file driver is called only once for - the whole operation, and the list iteration is done in the file - driver.

    -

    The options delayed_write and read_ahead to - open/2 - make the file driver cache data to reduce - the number of operating system calls. The function - create_file/2 in the recent example takes 60 seconds - without option delayed_write, which is 2.6 - times slower.

    -

    As a bad example, create_file_slow/2 - without options raw, binary, and delayed_write, - meaning it calls open(Name, [write]), needs - 1 min 20 seconds for the job, which is 3.5 times slower than - the first example, and 150 times slower than the optimized - create_file/2.

    - -

    If an error occurs when accessing an open file with module - io, - the process handling the file exits. The dead - file process can hang if a process tries to access it later. - This will be fixed in a future release.

    -
    +

    When writing data it's generally more efficient to write a list of + binaries rather than a list of integers. It is not needed to + flatten a deep list before writing. On Unix hosts, scatter output, + which writes a set of buffers in one operation, is used when + possible. In this way write(FD, [Bin1, Bin2 | Bin3]) + writes the contents of the binaries without copying the data + at all, except for perhaps deep down in the operating system + kernel.

    + +

    If an error occurs when accessing an open file with module + io, the process + handling the file exits. The dead file process can hang if a process + tries to access it later. This will be fixed in a future release. +

    +
    -- cgit v1.2.3 From c9fc20af633bbdc68206ca7d7438cd803793875f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Thu, 2 Nov 2017 17:02:16 +0100 Subject: Remove doc for sendfile/5 use_threads option The option no longer does anything; systems that lack support for non-blocking sendfile(2) will use the Erlang fallback. --- lib/kernel/doc/src/file.xml | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index 3e3c5b3bf3..58abb35428 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -1837,24 +1837,16 @@ f.txt: {person, "kalle", 25}.

    The file used must be opened using the raw flag, and the process calling sendfile must be the controlling process of the socket. See gen_tcp:controlling_process/2.

    -

    If the OS used does not support sendfile, an Erlang fallback - using - read/2 and - gen_tcp:send/2 is used.

    +

    If the OS used does not support non-blocking sendfile, an + Erlang fallback using read/2 + and gen_tcp:send/2 is + used.

    The option list can contain the following options:

    chunk_size

    The chunk size used by the Erlang fallback to send data. If using the fallback, set this to a value that comfortably fits in the systems memory. Default is 20 MB.

    - use_threads -

    Instructs the emulator to use the async thread pool for the - sendfile system call. This can be useful if the OS you are running - on does not properly support non-blocking sendfile calls. Notice that - using async threads potentially makes your system vulnerable to slow - client attacks. If set to true and no async threads are available, - the sendfile call returns {error,einval}. - Introduced in Erlang/OTP 17.0. Default is false.

    -- cgit v1.2.3 From 8e634f0063acb8009c2afb303111a2fd35aad78b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Thu, 2 Nov 2017 21:27:56 +0100 Subject: Make file:sendfile/5 follow its documented error behavior --- lib/kernel/src/file.erl | 21 ++++++++++++--------- lib/kernel/test/sendfile_SUITE.erl | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 11 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index b5a51c3410..d05199897f 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -1236,15 +1236,18 @@ sendfile(File, _Sock, _Offet, _Bytes, _Opts) when is_pid(File) -> sendfile(File, Sock, Offset, Bytes, []) -> sendfile(File, Sock, Offset, Bytes, ?MAX_CHUNK_SIZE, [], [], []); sendfile(File, Sock, Offset, Bytes, Opts) -> - ChunkSize0 = proplists:get_value(chunk_size, Opts, ?MAX_CHUNK_SIZE), - ChunkSize = if ChunkSize0 > ?MAX_CHUNK_SIZE -> - ?MAX_CHUNK_SIZE; - true -> ChunkSize0 - end, - %% Support for headers, trailers and options has been removed because the - %% Darwin and BSD API for using it does not play nice with - %% non-blocking sockets. See unix_efile.c for more info. - sendfile(File, Sock, Offset, Bytes, ChunkSize, [], [], Opts). + try proplists:get_value(chunk_size, Opts, ?MAX_CHUNK_SIZE) of + ChunkSize0 when is_integer(ChunkSize0) -> + ChunkSize = erlang:min(ChunkSize0, ?MAX_CHUNK_SIZE), + %% Support for headers, trailers and options has been removed + %% because the Darwin and BSD API for using it does not play nice + %% with non-blocking sockets. See unix_efile.c for more info. + sendfile(File, Sock, Offset, Bytes, ChunkSize, [], [], Opts); + _Other -> + {error, badarg} + catch + error:_ -> {error, badarg} + end. %% sendfile/2 -spec sendfile(Filename, Socket) -> diff --git a/lib/kernel/test/sendfile_SUITE.erl b/lib/kernel/test/sendfile_SUITE.erl index 4872ce53b7..0c0b1cbcb6 100644 --- a/lib/kernel/test/sendfile_SUITE.erl +++ b/lib/kernel/test/sendfile_SUITE.erl @@ -40,7 +40,8 @@ t_sendfile_sendduring/1, t_sendfile_recvduring/1, t_sendfile_closeduring/1, - t_sendfile_crashduring/1]). + t_sendfile_crashduring/1, + t_sendfile_arguments/1]). all() -> [t_sendfile_small, @@ -55,7 +56,8 @@ all() -> t_sendfile_sendduring, t_sendfile_recvduring, t_sendfile_closeduring, - t_sendfile_crashduring]. + t_sendfile_crashduring, + t_sendfile_arguments]. init_per_suite(Config) -> case {os:type(),os:version()} of @@ -371,6 +373,36 @@ t_sendfile_crashduring(Config) -> end end. +t_sendfile_arguments(Config) -> + Filename = proplists:get_value(small_file, Config), + + {ok, Listener} = gen_tcp:listen(0, + [{packet, 0}, {active, false}, {reuseaddr, true}]), + {ok, Port} = inet:port(Listener), + + ErrorCheck = + fun(Reason, Offset, Length, Opts) -> + {ok, Sender} = gen_tcp:connect({127, 0, 0, 1}, Port, + [{packet, 0}, {active, false}]), + {ok, Receiver} = gen_tcp:accept(Listener), + {ok, Fd} = file:open(Filename, [read, raw]), + {error, Reason} = file:sendfile(Fd, Sender, Offset, Length, Opts), + gen_tcp:close(Receiver), + gen_tcp:close(Sender), + file:close(Fd) + end, + + ErrorCheck(einval, -1, 0, []), + ErrorCheck(einval, 0, -1, []), + ErrorCheck(badarg, gurka, 0, []), + ErrorCheck(badarg, 0, gurka, []), + ErrorCheck(badarg, 0, 0, gurka), + ErrorCheck(badarg, 0, 0, [{chunk_size, gurka}]), + + gen_tcp:close(Listener), + + ok. + %% Generic sendfile server code sendfile_send(Send) -> sendfile_send({127,0,0,1},Send). -- cgit v1.2.3 From d4341cd249d728174fce580bd018e1e8b402d161 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Mon, 20 Nov 2017 15:23:24 +0100 Subject: kernel: add a resource reference counter System resources/functionality may need to be reference counted to be handled correctly when used or enabled/disabled from more than one process or application. It is easier to handle this in erlang code than in erts, so make a process that deals with the housekeeping. --- lib/kernel/src/Makefile | 1 + lib/kernel/src/kernel.app.src | 2 + lib/kernel/src/kernel.erl | 11 +++- lib/kernel/src/kernel_refc.erl | 139 +++++++++++++++++++++++++++++++++++++++ lib/kernel/test/kernel_SUITE.erl | 69 ++++++++++++++++++- 5 files changed, 218 insertions(+), 4 deletions(-) create mode 100644 lib/kernel/src/kernel_refc.erl (limited to 'lib/kernel') diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile index 5946620f0f..acfcca55e5 100644 --- a/lib/kernel/src/Makefile +++ b/lib/kernel/src/Makefile @@ -106,6 +106,7 @@ MODULES = \ inet_sctp \ kernel \ kernel_config \ + kernel_refc \ local_udp \ local_tcp \ net \ diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 080b11fc4d..3136db8267 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -57,6 +57,7 @@ inet_tcp_dist, kernel, kernel_config, + kernel_refc, local_tcp, local_udp, net, @@ -107,6 +108,7 @@ heart, init, kernel_config, + kernel_refc, kernel_sup, net_kernel, net_sup, diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl index cba57088ec..0382764b39 100644 --- a/lib/kernel/src/kernel.erl +++ b/lib/kernel/src/kernel.erl @@ -111,6 +111,13 @@ init([]) -> type => worker, modules => [kernel_config]}, + RefC = #{id => kernel_refc, + start => {kernel_refc, start_link, []}, + restart => permanent, + shutdown => 2000, + type => worker, + modules => [kernel_refc]}, + Code = #{id => code_server, start => {code, start_link, []}, restart => permanent, @@ -148,7 +155,7 @@ init([]) -> case init:get_argument(mode) of {ok, [["minimal"]]} -> - {ok, {SupFlags, [Code, File, StdError, User, Config, SafeSup]}}; + {ok, {SupFlags, [Code, File, StdError, User, Config, RefC, SafeSup]}}; _ -> Rpc = #{id => rex, start => {rpc, start_link, []}, @@ -199,7 +206,7 @@ init([]) -> {ok, {SupFlags, [Code, Rpc, Global, InetDb | DistAC] ++ [NetSup, GlGroup, File, SigSrv, - StdError, User, Config, SafeSup] ++ Timer}} + StdError, User, Config, RefC, SafeSup] ++ Timer}} end; init(safe) -> SupFlags = #{strategy => one_for_one, diff --git a/lib/kernel/src/kernel_refc.erl b/lib/kernel/src/kernel_refc.erl new file mode 100644 index 0000000000..3ad68e9ff9 --- /dev/null +++ b/lib/kernel/src/kernel_refc.erl @@ -0,0 +1,139 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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(kernel_refc). + +-behaviour(gen_server). + +%% External exports +-export([start_link/0, scheduler_wall_time/1]). +%% Internal exports +-export([init/1, handle_info/2, terminate/2]). +-export([handle_call/3, handle_cast/2, code_change/3]). + +%%%----------------------------------------------------------------- +%%% This module implements a process that handles reference counters for +%%% various erts or other kernel resources which needs reference counting. +%%% +%%% Should not be documented nor used directly by user applications. +%%%----------------------------------------------------------------- +start_link() -> + gen_server:start_link({local,kernel_refc}, kernel_refc, [], []). + +-spec scheduler_wall_time(boolean()) -> boolean(). +scheduler_wall_time(Bool) -> + gen_server:call(kernel_refc, {scheduler_wall_time, self(), Bool}). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- + +-spec init([]) -> {'ok', map()} | {'stop', term()}. + +init([]) -> + resource(scheduler_wall_time, false), + {ok, #{scheduler_wall_time=>#{}}}. + +-spec handle_call(term(), term(), State) -> {'reply', term(), State}. +handle_call({What, Who, false}, _From, State) -> + {Reply, Cnt} = do_stop(What, maps:get(What, State), Who), + {reply, Reply, State#{What:=Cnt}}; +handle_call({What, Who, true}, _From, State) -> + {Reply, Cnt} = do_start(What, maps:get(What, State), Who), + {reply, Reply, State#{What:=Cnt}}; +handle_call(_, _From, State) -> + {reply, badarg, State}. + +-spec handle_cast(term(), State) -> {'noreply', State}. +handle_cast(_, State) -> + {noreply, State}. + +-spec handle_info(term(), State) -> {'noreply', State}. +handle_info({'DOWN', _Ref, process, Pid, _}, State) -> + Cleanup = fun(Resource, Cnts) -> + cleanup(Resource, Cnts, Pid) + end, + {noreply, maps:map(Cleanup, State)}; +handle_info(_, State) -> + {noreply, State}. + +-spec terminate(term(), term()) -> 'ok'. +terminate(_Reason, _State) -> + ok. + +-spec code_change(term(), State, term()) -> {'ok', State}. +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%----------------------------------------------------------------- +%% Internal functions +%%----------------------------------------------------------------- + +do_start(Resource, Cnt, Pid) -> + case maps:get(Pid, Cnt, undefined) of + undefined -> + Ref = erlang:monitor(process, Pid), + case any(Cnt) of + true -> + {true, Cnt#{Pid=>{1, Ref}}}; + false -> + resource(Resource, true), + {false, Cnt#{Pid=>{1, Ref}}} + end; + {N, Ref} -> + {true, Cnt#{Pid=>{N+1, Ref}}} + end. + +do_stop(Resource, Cnt0, Pid) -> + case maps:get(Pid, Cnt0, undefined) of + undefined -> + {any(Cnt0), Cnt0}; + {1, Ref} -> + erlang:demonitor(Ref, [flush]), + Cnt = maps:remove(Pid, Cnt0), + case any(Cnt) of + true -> + {true, Cnt}; + false -> + resource(Resource, false), + {true, Cnt} + end; + {N, Ref} -> + {true, Cnt0#{Pid=>{N-1, Ref}}} + end. + +cleanup(Resource, Cnt0, Pid) -> + case maps:is_key(Pid, Cnt0) of + true -> + Cnt = maps:remove(Pid, Cnt0), + case any(Cnt) of + true -> + Cnt; + false -> + resource(Resource, false), + Cnt + end; + false -> + Cnt0 + end. + +any(Cnt) -> maps:size(Cnt) > 0. + +resource(scheduler_wall_time, Enable) -> + _ = erlang:system_flag(scheduler_wall_time, Enable). diff --git a/lib/kernel/test/kernel_SUITE.erl b/lib/kernel/test/kernel_SUITE.erl index da56359294..7898988dbe 100644 --- a/lib/kernel/test/kernel_SUITE.erl +++ b/lib/kernel/test/kernel_SUITE.erl @@ -30,14 +30,14 @@ -export([init_per_testcase/2, end_per_testcase/2]). %% Test cases must be exported. --export([app_test/1, appup_test/1]). +-export([app_test/1, appup_test/1, refc/1]). suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,2}}]. all() -> - [app_test, appup_test]. + [app_test, appup_test, refc]. groups() -> []. @@ -163,3 +163,68 @@ check_appup([Vsn|Vsns],Instrs,Expected) -> end; check_appup([],_,_) -> ok. + +%%% Check that refc module handles the counters as expected +refc(_Config) -> + Enable = fun(Enable) -> erlang:system_flag(scheduler_wall_time, Enable) end, + IsOn = fun() -> undefined /= erlang:statistics(scheduler_wall_time) end, + Tester = self(), + Loop = fun Loop() -> + receive + die -> normal; + {apply, Bool} -> + Res = Enable(Bool), + Tester ! {self(), Res}, + Loop() + end + end, + + %% Counter should be 0 + false = Enable(false), + + false = Enable(true), + true = Enable(true), + true = Enable(false), + true = Enable(false), + + %% Counter should be 0 + false = IsOn(), + + P1 = spawn_link(Loop), + P1 ! {apply, true}, + receive {P1, R1} -> false = R1 end, + + %% P1 has turned it on counter should be one + true = IsOn(), + true = Enable(true), + true = Enable(false), + true = IsOn(), + + P1 ! {apply, false}, + receive {P1, R2} -> true = R2 end, + false = IsOn(), + + P1 ! {apply, true}, + receive {P1, R3} -> false = R3 end, + true = IsOn(), + true = Enable(false), + + + P1 ! die, + timer:sleep(100), + false = IsOn(), + false = Enable(false), + + P2 = spawn_link(Loop), + P2 ! {apply, true}, + receive {P2, R4} -> false = R4 end, + true = IsOn(), + P2 ! {apply, true}, + receive {P2, R5} -> true = R5 end, + true = IsOn(), + + P2 ! die, + timer:sleep(100), + false = IsOn(), + + ok. -- cgit v1.2.3 From 0d9fcda74761dd0113a16b99ac4f62c80f686427 Mon Sep 17 00:00:00 2001 From: getong Date: Mon, 4 Dec 2017 03:29:01 +0000 Subject: false should be never, false not found in the source code --- lib/kernel/doc/src/net_kernel.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml index 0b94fc0fa6..cd5addcd34 100644 --- a/lib/kernel/doc/src/net_kernel.xml +++ b/lib/kernel/doc/src/net_kernel.xml @@ -56,7 +56,7 @@ $ erl -sname foobar

    Normally, connections are established automatically when another node is referenced. This functionality can be disabled by setting Kernel configuration parameter - dist_auto_connect to false, see + dist_auto_connect to never, see kernel(6). In this case, connections must be established explicitly by calling connect_node/1.

    -- cgit v1.2.3 From 1b3a894b6ef2f62974228f5904ff7fe469a4926a Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Fri, 17 Nov 2017 15:12:18 +0100 Subject: Correct distribution doc --- lib/kernel/doc/src/net_kernel.xml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml index 0b94fc0fa6..94c8fb9e20 100644 --- a/lib/kernel/doc/src/net_kernel.xml +++ b/lib/kernel/doc/src/net_kernel.xml @@ -230,7 +230,12 @@ $ erl -sname foobar

    The tuple {nodedown_reason, Reason} is included in InfoList in nodedown messages.

    -

    Reason can be any of the following:

    +

    + Reason can, depending on which + distribution module or process that is used be any term, + but for the standard TCP distribution module it is + any of the following: +

    connection_setup_failed

    The connection setup failed (after nodeup -- cgit v1.2.3 From bc98fe2a55bfb54b39b0027213a7de60c14925c5 Mon Sep 17 00:00:00 2001 From: Dimitris Zorbas Date: Sun, 26 Nov 2017 20:48:03 +0000 Subject: Add typespecs to inet:i --- lib/kernel/src/inet.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index dc20c21c77..fe91b0d33e 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -72,7 +72,7 @@ %% timer interface -export([start_timer/1, timeout/1, timeout/2, stop_timer/1]). --export_type([address_family/0, hostent/0, hostname/0, ip4_address/0, +-export_type([address_family/0, socket_protocol/0, hostent/0, hostname/0, ip4_address/0, ip6_address/0, ip_address/0, port_number/0, local_address/0, socket_address/0, returned_non_ip_address/0, socket_setopt/0, socket_getopt/0, @@ -1452,11 +1452,14 @@ fdopen(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) -> %% socket stat %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec i() -> ok. i() -> i(tcp), i(udp), i(sctp). +-spec i(socket_protocol()) -> ok. i(Proto) -> i(Proto, [port, module, recv, sent, owner, local_address, foreign_address, state, type]). +-spec i(socket_protocol(), [atom()]) -> ok. i(tcp, Fs) -> ii(tcp_sockets(), Fs, tcp); i(udp, Fs) -> -- cgit v1.2.3 From 4a3be82b5891b1aaf6f89f2ce1241986bb4d6c28 Mon Sep 17 00:00:00 2001 From: Dimitris Zorbas Date: Sun, 26 Nov 2017 20:48:44 +0000 Subject: Add documentation to :inet.i Also fix a typo. --- lib/kernel/doc/src/inet.xml | 60 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index 169a76463b..abb045b744 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -197,6 +197,9 @@ fe80::204:acff:fe17:bf38 + + + @@ -460,6 +463,61 @@ get_tcpi_sacked(Sock) -> + + + + + Displays information and statistics about sockets on the terminal + +

    + Lists all TCP, UDP and SCTP sockets, including those that the Erlang runtime system uses as well as + those created by the application. +

    +

    + The following options are available: +

    + + + port + +

    The internal index of the port.

    +
    + module + +

    The callback module of the socket.

    +
    + recv + +

    Number of bytes received by the socket.

    +
    + sent + +

    Number of bytes sent from the socket.

    +
    + owner + +

    The socket owner process.

    +
    + local_address + +

    The local address of the socket.

    +
    + foreign_address + +

    The address and port of the other end of the connection.

    +
    + state + +

    The connection state.

    +
    + type + +

    STREAM or DGRAM or SEQPACKET.

    +
    +
    + + + Convert IPv6/IPV4 address to ASCII. @@ -1214,7 +1272,7 @@ inet:setopts(Sock,[{raw,6,8,<<30:32/native>>}]),]]> For one-to-many style sockets, the special value 0 is defined to mean that the returned addresses must be without any particular association. - How different SCTP implementations interprets this varies somewhat. + How different SCTP implementations interpret this varies somewhat.

    -- cgit v1.2.3 From 2fb94bbae9b3ebf6bbd5b0ba3546450036764383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 7 Dec 2017 12:16:04 +0100 Subject: Slightly optimize reading of cooked files in list mode In general, the new NIF-based file routines are faster than the old efile driver. However, on some computers, building the entire OTP system is somewhat slower. It turns out that it is because 'erlc' cheated by turning off the IO thread pool (using '+A0') to avoid context switches between scheduler threads and threads in the IO thread pool. The new file routines perform IO on dirty IO threads, and there is (by intent) no way to force the operations to occur on scheduler threads to avoid the context switches What we can do to is to use a small (4Kb) read-ahead buffer for files opened for reading (only) in list mode (which is how the compiler opens its input files). The buffering reduces the number of context switches between scheduler threads and dirty IO threads. On my computer that seems to slightly speed up building of the entire OTP system. The buffer should do no harm. The only potential for harm I can think of is random access to a file opened in read mode, where the read-ahead buffer could slightly decrease performance. That does not seems to be a likely use case in practice, though. --- lib/kernel/src/file_io_server.erl | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/file_io_server.erl b/lib/kernel/src/file_io_server.erl index 2b35d2acfb..34d5497a4a 100644 --- a/lib/kernel/src/file_io_server.erl +++ b/lib/kernel/src/file_io_server.erl @@ -68,7 +68,8 @@ do_start(Spawn, Owner, FileName, ModeList) -> erlang:dt_restore_tag(Utag), %% process_flag(trap_exit, true), case parse_options(ModeList) of - {ReadMode, UnicodeMode, Opts} -> + {ReadMode, UnicodeMode, Opts0} -> + Opts = maybe_add_read_ahead(ReadMode, Opts0), case raw_file_io:open(FileName, [raw | Opts]) of {error, Reason} = Error -> Self ! {Ref, Error}, @@ -158,6 +159,24 @@ valid_enc({utf32,little}) -> valid_enc(_Other) -> {error,badarg}. +%% Add a small read_ahead buffer if the file is opened for reading +%% only in list mode and no read_ahead is already given. +maybe_add_read_ahead(binary, Opts) -> + Opts; +maybe_add_read_ahead(list, Opts) -> + P = fun(read_ahead) -> true; + ({read_ahead,_}) -> true; + (append) -> true; + (exclusive) -> true; + (write) -> true; + (_) -> false + end, + case lists:any(P, Opts) of + false -> + [{read_ahead, 4096}|Opts]; + true -> + Opts + end. server_loop(#state{mref = Mref} = State) -> receive -- cgit v1.2.3 From f3a74a7dfb633b21b7d06e48bf430071cdc0f779 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 8 Dec 2017 10:53:22 +0100 Subject: Update version numbers --- lib/kernel/vsn.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index cef54dd41a..106bda01ca 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 5.4 +KERNEL_VSN = 5.4.1 -- cgit v1.2.3 From 47198f179ab14cf8cfcdab0d976650cd1519b236 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 8 Dec 2017 10:57:50 +0100 Subject: Update release notes --- lib/kernel/doc/src/notes.xml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index a5316dd476..d7f224c38e 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,21 @@
  • This document describes the changes made to the Kernel application.

    +
    Kernel 5.4.1 + +
    Fixed Bugs and Malfunctions + + +

    + Refactored an internal API.

    +

    + Own Id: OTP-14784

    +
    +
    +
    + +
    +
    Kernel 5.4
    Fixed Bugs and Malfunctions -- cgit v1.2.3 From 003e5450924c4d8202e2013cac41eff58fa90584 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 12 Dec 2017 16:32:51 +0100 Subject: kernel: Try fix/skip prim_file_SUITE:large_write for valgrind by limiting the memory usage. Our valgrind test machine (pharazon) seems to get totally swamped by this test case. --- lib/kernel/test/prim_file_SUITE.erl | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl index 2f4330c217..e88d42788f 100644 --- a/lib/kernel/test/prim_file_SUITE.erl +++ b/lib/kernel/test/prim_file_SUITE.erl @@ -2108,12 +2108,25 @@ free_memory() -> {value, {buffered_memory, Buffed}} -> Buffed; false -> 0 end), - TotFree div (1024*1024) + usable_mem(TotFree) div (1024*1024) catch error : undef -> ct:fail({"os_mon not built"}) end. +usable_mem(Memory) -> + case test_server:is_valgrind() of + true -> + %% Valgrind uses extra memory for the V- and A-bits. + %% http://valgrind.org/docs/manual/mc-manual.html#mc-manual.value + %% Docs says it uses "compression to represent the V bits compactly" + %% but let's be conservative and cut usable memory in half. + Memory div 2; + false -> + Memory + end. + + %%%----------------------------------------------------------------- %%% Utilities rm_rf(Mod,Dir) -> -- cgit v1.2.3 From 7f6ac587f47bd115604c7c9d1504e66cafffd265 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 28 Nov 2017 14:17:24 +0100 Subject: Redirect system_flag(scheduler_wall_time,_) to kernel_refc --- lib/kernel/src/kernel_refc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/kernel_refc.erl b/lib/kernel/src/kernel_refc.erl index 3ad68e9ff9..05076dc885 100644 --- a/lib/kernel/src/kernel_refc.erl +++ b/lib/kernel/src/kernel_refc.erl @@ -38,7 +38,7 @@ start_link() -> -spec scheduler_wall_time(boolean()) -> boolean(). scheduler_wall_time(Bool) -> - gen_server:call(kernel_refc, {scheduler_wall_time, self(), Bool}). + gen_server:call(kernel_refc, {scheduler_wall_time, self(), Bool}, infinity). %%----------------------------------------------------------------- %% Callback functions from gen_server @@ -136,4 +136,4 @@ cleanup(Resource, Cnt0, Pid) -> any(Cnt) -> maps:size(Cnt) > 0. resource(scheduler_wall_time, Enable) -> - _ = erlang:system_flag(scheduler_wall_time, Enable). + _ = erts_internal:scheduler_wall_time(Enable). -- cgit v1.2.3 From c2d70945dce9cb09d5d7120d6e9ddf7faac8d230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Wed, 22 Nov 2017 13:19:57 +0100 Subject: Replace the libc environment with a thread-safe emulation putenv(3) and friends aren't thread-safe regardless of how you slice it; a global lock around all environment operations (like before) keeps things safe as far as our own operations go, but we have absolutely no control over what libc or a library dragged in by a driver/NIF does -- they're free to call getenv(3) or putenv(3) without honoring our lock. This commit solves this by setting up an "emulated" environment which can't be touched without going through our interfaces. Third-party libraries can still shoot themselves in the foot but benign uses of os:putenv/2 will no longer risk crashing the emulator. --- lib/kernel/src/os.erl | 76 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 29 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl index b5f19d4b99..fbc046c8f9 100644 --- a/lib/kernel/src/os.erl +++ b/lib/kernel/src/os.erl @@ -27,12 +27,13 @@ -export_type([env_var_name/0, env_var_value/0, env_var_name_value/0, command_input/0]). +-export([getenv/0, getenv/1, getenv/2, putenv/2, unsetenv/1]). + %%% BIFs --export([getenv/0, getenv/1, getenv/2, getpid/0, - perf_counter/0, perf_counter/1, - putenv/2, set_signal/2, system_time/0, system_time/1, - timestamp/0, unsetenv/1]). +-export([get_env_var/1, getpid/0, list_env_vars/0, perf_counter/0, + perf_counter/1, set_env_var/2, set_signal/2, system_time/0, + system_time/1, timestamp/0, unset_env_var/1]). -type env_var_name() :: nonempty_string(). @@ -42,29 +43,15 @@ -type command_input() :: atom() | io_lib:chars(). --spec getenv() -> [env_var_name_value()]. - -getenv() -> erlang:nif_error(undef). - --spec getenv(VarName) -> Value | false when - VarName :: env_var_name(), - Value :: env_var_value(). - -getenv(_) -> +-spec list_env_vars() -> [{env_var_name(), env_var_value()}]. +list_env_vars() -> erlang:nif_error(undef). --spec getenv(VarName, DefaultValue) -> Value when +-spec get_env_var(VarName) -> Value | false when VarName :: env_var_name(), - DefaultValue :: env_var_value(), Value :: env_var_value(). - -getenv(VarName, DefaultValue) -> - case os:getenv(VarName) of - false -> - DefaultValue; - Value -> - Value - end. +get_env_var(_VarName) -> + erlang:nif_error(undef). -spec getpid() -> Value when Value :: string(). @@ -84,11 +71,10 @@ perf_counter() -> perf_counter(Unit) -> erlang:convert_time_unit(os:perf_counter(), perf_counter, Unit). --spec putenv(VarName, Value) -> true when +-spec set_env_var(VarName, Value) -> true when VarName :: env_var_name(), Value :: env_var_value(). - -putenv(_, _) -> +set_env_var(_, _) -> erlang:nif_error(undef). -spec system_time() -> integer(). @@ -108,10 +94,9 @@ system_time(_Unit) -> timestamp() -> erlang:nif_error(undef). --spec unsetenv(VarName) -> true when +-spec unset_env_var(VarName) -> true when VarName :: env_var_name(). - -unsetenv(_) -> +unset_env_var(_) -> erlang:nif_error(undef). -spec set_signal(Signal, Option) -> 'ok' when @@ -125,6 +110,39 @@ set_signal(_Signal, _Option) -> %%% End of BIFs +-spec getenv() -> [env_var_name_value()]. +getenv() -> + [lists:flatten([Key, $=, Value]) || {Key, Value} <- os:list_env_vars() ]. + +-spec getenv(VarName) -> Value | false when + VarName :: env_var_name(), + Value :: env_var_value(). +getenv(VarName) -> + os:get_env_var(VarName). + +-spec getenv(VarName, DefaultValue) -> Value when + VarName :: env_var_name(), + DefaultValue :: env_var_value(), + Value :: env_var_value(). +getenv(VarName, DefaultValue) -> + case os:getenv(VarName) of + false -> + DefaultValue; + Value -> + Value + end. + +-spec putenv(VarName, Value) -> true when + VarName :: env_var_name(), + Value :: env_var_value(). +putenv(VarName, Value) -> + os:set_env_var(VarName, Value). + +-spec unsetenv(VarName) -> true when + VarName :: env_var_name(). +unsetenv(VarName) -> + os:unset_env_var(VarName). + -spec type() -> {Osfamily, Osname} when Osfamily :: unix | win32, Osname :: atom(). -- cgit v1.2.3 From d469368b9e14b9834017a7cf318f02950a4aadcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Fri, 1 Dec 2017 12:22:53 +0100 Subject: Disallow NULs in filename-encoded strings Previously we accepted trailing NULs, which was backwards compatible as such usage never resulted in misbehavior in the first place. The downside is that it prevented erts_native_filename_need from returning an accurate number of *actual characters*, needlessly complicating encoding-agnostic code like erts_osenv. --- lib/kernel/doc/src/file.xml | 7 ------- lib/kernel/doc/src/os.xml | 19 ++----------------- 2 files changed, 2 insertions(+), 24 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index 58abb35428..8477b0e148 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -93,13 +93,6 @@ are now rejected and will cause primitive file operations fail.

    -

    - Currently null characters at the end of the filename - will be accepted by primitive file operations. Such - filenames are however still documented as invalid. The - implementation will also change in the future and - reject such filenames. -

    diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml index 0a08e2c78a..c27182ff0b 100644 --- a/lib/kernel/doc/src/os.xml +++ b/lib/kernel/doc/src/os.xml @@ -58,17 +58,6 @@ operations to fail.

    - -

    - Currently null characters at the end of filenames, - environment variable names and values will be accepted - by the primitive operations. Such filenames, environment - variable names and values are however still documented as - invalid. The implementation will also change in the - future and reject such filenames, environment variable - names and values. -

    -
    @@ -143,12 +132,8 @@

    Previous implementation used to allow all characters as long as they were integer values greater than or equal to zero. This sometimes lead to unwanted results since null characters - (integer value zero) often are interpreted as string termination. - Current implementation still accepts null characters at the end - of Command even though the documentation - states that no null characters are allowed. This will however - be changed in the future so that no null characters at all will - be accepted.

    + (integer value zero) often are interpreted as string termination. The + current implementation rejects these.

    Examples:

    LsOut = os:cmd("ls"), % on unix platform -- cgit v1.2.3 From de0771a4b3d882161f10c1d2d03c8027327f9323 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Thu, 4 Jan 2018 16:54:22 +0100 Subject: stdlib: Let filelib:find_source() search subdirs The Design Principles states that an application can have Erlang source files one level below the "src" directory, and now filelib:find_source() by default searches one level below "src". The same applies to "esrc". That directory is only mentioned in filename(3). --- lib/kernel/doc/src/kernel_app.xml | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index e5ac031539..0762cebc94 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -4,7 +4,7 @@
    - 19962017 + 19962018 Ericsson AB. All Rights Reserved. @@ -469,8 +469,12 @@ MaxT = TickTime + TickTime / 4 ObjSuffix = string() SrcSuffix = string() -

    Specifies a list of rules for use by filelib:find_file/2 and - filelib:find_source/2. If this is set to some other value +

    Specifies a list of rules for use by + + filelib:find_file/2 + + filelib:find_source/2 + If this is set to some other value than the empty list, it replaces the default rules. Rules can be simple pairs of directory suffixes, such as {"ebin", "src"}, which are used by filelib:find_file/2, or @@ -478,6 +482,16 @@ MaxT = TickTime + TickTime / 4 file name extensions, for example [{".beam", ".erl", [{"ebin", "src"}]}, which are used by filelib:find_source/2. Both kinds of rules can be mixed in the list.

    +

    The interpretation of ObjDirSuffix and SrcDirSuffix + is as follows: if the end of the directory name where an + object is located matches ObjDirSuffix, then the + name created by replacing ObjDirSuffix with + SrcDirSuffix is expanded by calling + + filelib:wildcard/1, and the first regular + file found among the matches is the source file. +

    +
    -- cgit v1.2.3 From 55e929c4ed5cd854038c18697123ea94948ebf35 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 13 Dec 2017 11:22:45 +0100 Subject: kernel: Add os:cmd/2 with max_size option --- lib/kernel/doc/src/os.xml | 43 ++++++++++++++++++++++++++++++++++++++----- lib/kernel/src/os.erl | 37 ++++++++++++++++++++++++++----------- lib/kernel/test/os_SUITE.erl | 18 ++++++++++++++++-- 3 files changed, 80 insertions(+), 18 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml index 0e9add4161..7ce2f54542 100644 --- a/lib/kernel/doc/src/os.xml +++ b/lib/kernel/doc/src/os.xml @@ -38,17 +38,35 @@ most platforms.

    + + + + + + + +

    Options for os:cmd/2

    + + max_size + +

    The maximum size of the data returned by the os:cmd call. + See the os:cmd/2 + documentation for more details.

    +
    +
    +
    +
    +
    + + Execute a command in a shell of the target OS.

    Executes Command in a command shell of the - target OS, - captures the standard output of the command, and returns this - result as a string. This function is a replacement of - the previous function unix:cmd/1; they are equivalent on a - Unix platform.

    + target OS, captures the standard output of the command, + and returns this result as a string.

    Examples:

    LsOut = os:cmd("ls"), % on unix platform @@ -57,6 +75,21 @@ DirOut = os:cmd("dir"), % on Win32 platform called from another program (for example, os:cmd/1) can differ, compared with the standard output of the command when called directly from an OS command shell.

    +

    os:cmd/2 was added in kernel-5.5 (OTP-20.2.1). It makes it + possible to pass an options map as the second argument in order to + control the behaviour of os:cmd. The possible options are: +

    + + max_size + +

    The maximum size of the data returned by the os:cmd call. + This option is a safety feature that should be used when the command + executed can return a very large, possibly infinite, result.

    + +> os:cmd("cat /dev/zero", #{ max_size => 20 }). +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] +
    +
    diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl index 0250783632..ae188ae932 100644 --- a/lib/kernel/src/os.erl +++ b/lib/kernel/src/os.erl @@ -21,7 +21,7 @@ %% Provides a common operating system interface. --export([type/0, version/0, cmd/1, find_executable/1, find_executable/2]). +-export([type/0, version/0, cmd/1, cmd/2, find_executable/1, find_executable/2]). -include("file.hrl"). @@ -32,6 +32,11 @@ putenv/2, set_signal/2, system_time/0, system_time/1, timestamp/0, unsetenv/1]). +-type os_command() :: atom() | io_lib:chars(). +-type os_command_opts() :: #{ max_size => non_neg_integer() | infinity }. + +-export_type([os_command/0, os_command_opts/0]). + -spec getenv() -> [string()]. getenv() -> erlang:nif_error(undef). @@ -232,15 +237,21 @@ extensions() -> %% Executes the given command in the default shell for the operating system. -spec cmd(Command) -> string() when - Command :: atom() | io_lib:chars(). + Command :: os_command(). cmd(Cmd) -> + cmd(Cmd, #{ }). + +-spec cmd(Command, Options) -> string() when + Command :: os_command(), + Options :: os_command_opts(). +cmd(Cmd, Opts) -> validate(Cmd), {SpawnCmd, SpawnOpts, SpawnInput, Eot} = mk_cmd(os:type(), Cmd), Port = open_port({spawn, SpawnCmd}, [binary, stderr_to_stdout, stream, in, hide | SpawnOpts]), MonRef = erlang:monitor(port, Port), true = port_command(Port, SpawnInput), - Bytes = get_data(Port, MonRef, Eot, []), + Bytes = get_data(Port, MonRef, Eot, [], 0, maps:get(max_size, Opts, infinity)), demonitor(MonRef, [flush]), String = unicode:characters_to_list(Bytes), if %% Convert to unicode list if possible otherwise return bytes @@ -291,12 +302,13 @@ validate1([List|Rest]) when is_list(List) -> validate1([]) -> ok. -get_data(Port, MonRef, Eot, Sofar) -> +get_data(Port, MonRef, Eot, Sofar, Size, Max) -> receive {Port, {data, Bytes}} -> - case eot(Bytes, Eot) of + case eot(Bytes, Eot, Size, Max) of more -> - get_data(Port, MonRef, Eot, [Sofar,Bytes]); + get_data(Port, MonRef, Eot, [Sofar, Bytes], + Size + byte_size(Bytes), Max); Last -> catch port_close(Port), flush_until_down(Port, MonRef), @@ -307,13 +319,16 @@ get_data(Port, MonRef, Eot, Sofar) -> iolist_to_binary(Sofar) end. -eot(_Bs, <<>>) -> +eot(_Bs, <<>>, _Size, _Max) -> more; -eot(Bs, Eot) -> +eot(Bs, Eot, Size, Max) -> case binary:match(Bs, Eot) of - nomatch -> more; - {Pos, _} -> - binary:part(Bs,{0, Pos}) + nomatch when Size + byte_size(Bs) < Max -> + more; + {Pos, _} when Size + Pos < Max -> + binary:part(Bs,{0, Pos}); + _ -> + binary:part(Bs,{0, Max - Size}) end. %% When port_close returns we know that all the diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl index 53a9e168ef..079b9790f6 100644 --- a/lib/kernel/test/os_SUITE.erl +++ b/lib/kernel/test/os_SUITE.erl @@ -25,7 +25,8 @@ -export([space_in_cwd/1, quoting/1, cmd_unicode/1, space_in_name/1, bad_command/1, find_executable/1, unix_comment_in_command/1, deep_list_command/1, large_output_command/1, background_command/0, background_command/1, - message_leak/1, close_stdin/0, close_stdin/1, perf_counter_api/1]). + message_leak/1, close_stdin/0, close_stdin/1, max_size_command/1, + perf_counter_api/1]). -include_lib("common_test/include/ct.hrl"). @@ -37,7 +38,7 @@ all() -> [space_in_cwd, quoting, cmd_unicode, space_in_name, bad_command, find_executable, unix_comment_in_command, deep_list_command, large_output_command, background_command, message_leak, - close_stdin, perf_counter_api]. + close_stdin, max_size_command, perf_counter_api]. groups() -> []. @@ -312,6 +313,19 @@ close_stdin(Config) -> "-1" = os:cmd(Fds). +max_size_command(_Config) -> + + Res20 = os:cmd("cat /dev/zero", #{ max_size => 20 }), + 20 = length(Res20), + + Res0 = os:cmd("cat /dev/zero", #{ max_size => 0 }), + 0 = length(Res0), + + Res32768 = os:cmd("cat /dev/zero", #{ max_size => 32768 }), + 32768 = length(Res32768), + + ResHello = os:cmd("echo hello", #{ max_size => 20 }), + 6 = length(ResHello). %% Test that the os:perf_counter api works as expected perf_counter_api(_Config) -> -- cgit v1.2.3 From d57d91427e1ddb09cbf55cac2014a368fe717b46 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 17 Jan 2018 20:00:49 +0100 Subject: Fix slow hipe execution particularly slow erlc when compiler is hipe compiled. hipe_unified_loader:load did not patch external call sites and instead caused a double hipe mode switch per call. hipe_unified_loader:load is only used for early modules first loaded as beam and by code:atomic_load and friends. --- lib/kernel/src/hipe_unified_loader.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl index f4c7c277ed..f8199fcf71 100644 --- a/lib/kernel/src/hipe_unified_loader.erl +++ b/lib/kernel/src/hipe_unified_loader.erl @@ -236,9 +236,10 @@ load_common(Mod, Bin, Beam, Architecture) -> lists:foreach(fun({FE, DestAddress}) -> hipe_bifs:set_native_address_in_fe(FE, DestAddress) end, erase(closures_to_patch)), - ok = hipe_bifs:commit_patch_load(LoaderState), set_beam_call_traps(FunDefs), - ok; + export_funs(FunDefs), + ok = hipe_bifs:commit_patch_load(LoaderState), + ok; BeamBinary when is_binary(BeamBinary) -> %% Find all closures in the code. [] = erase(closures_to_patch), %Clean up, assertion. -- cgit v1.2.3 From 6deabadcda913b4fd7f3d209e4344f0b0f736e85 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Fri, 19 Jan 2018 14:08:02 +0100 Subject: kernel: Correct contracts and a bug in group_history --- lib/kernel/src/disk_log.erl | 4 ++-- lib/kernel/src/erl_boot_server.erl | 37 ++++++++++++++++++------------------- lib/kernel/src/group_history.erl | 4 ++-- lib/kernel/test/disk_log_SUITE.erl | 2 +- lib/kernel/test/global_SUITE.erl | 17 ++++------------- 5 files changed, 27 insertions(+), 37 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index 70cbf1c87c..99ea8dc384 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2017. All Rights Reserved. +%% Copyright Ericsson AB 1997-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. @@ -266,7 +266,7 @@ inc_wrap_file(Log) -> Size :: dlog_size(), Reason :: no_such_log | nonode | {read_only_mode, Log} | {blocked_log, Log} - | {new_size_too_small, CurrentSize :: pos_integer()} + | {new_size_too_small, Log, CurrentSize :: pos_integer()} | {badarg, size} | {file_error, file:filename(), file_error()}. change_size(Log, NewSize) -> diff --git a/lib/kernel/src/erl_boot_server.erl b/lib/kernel/src/erl_boot_server.erl index ac81cc9689..2578b74428 100644 --- a/lib/kernel/src/erl_boot_server.erl +++ b/lib/kernel/src/erl_boot_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. @@ -58,13 +58,11 @@ -define(single_addr_mask, {255, 255, 255, 255}). --type ip4_address() :: {0..255,0..255,0..255,0..255}. - --spec start(Slaves) -> {'ok', Pid} | {'error', What} when +-spec start(Slaves) -> {'ok', Pid} | {'error', Reason} when Slaves :: [Host], - Host :: atom(), + Host :: inet:ip_address() | inet:hostname(), Pid :: pid(), - What :: any(). + Reason :: {'badarg', Slaves}. start(Slaves) -> case check_arg(Slaves) of @@ -74,11 +72,11 @@ start(Slaves) -> {error, {badarg, Slaves}} end. --spec start_link(Slaves) -> {'ok', Pid} | {'error', What} when +-spec start_link(Slaves) -> {'ok', Pid} | {'error', Reason} when Slaves :: [Host], - Host :: atom(), + Host :: inet:ip_address() | inet:hostname(), Pid :: pid(), - What :: any(). + Reason :: {'badarg', Slaves}. start_link(Slaves) -> case check_arg(Slaves) of @@ -104,10 +102,10 @@ check_arg([], Result) -> check_arg(_, _Result) -> error. --spec add_slave(Slave) -> 'ok' | {'error', What} when +-spec add_slave(Slave) -> 'ok' | {'error', Reason} when Slave :: Host, - Host :: atom(), - What :: any(). + Host :: inet:ip_address() | inet:hostname(), + Reason :: {'badarg', Slave}. add_slave(Slave) -> case inet:getaddr(Slave, inet) of @@ -117,10 +115,10 @@ add_slave(Slave) -> {error, {badarg, Slave}} end. --spec delete_slave(Slave) -> 'ok' | {'error', What} when +-spec delete_slave(Slave) -> 'ok' | {'error', Reason} when Slave :: Host, - Host :: atom(), - What :: any(). + Host :: inet:ip_address() | inet:hostname(), + Reason :: {'badarg', Slave}. delete_slave(Slave) -> case inet:getaddr(Slave, inet) of @@ -130,7 +128,7 @@ delete_slave(Slave) -> {error, {badarg, Slave}} end. --spec add_subnet(Mask :: ip4_address(), Addr :: ip4_address()) -> +-spec add_subnet(Netmask :: inet:ip_address(), Addr :: inet:ip_address()) -> 'ok' | {'error', any()}. add_subnet(Mask, Addr) when is_tuple(Mask), is_tuple(Addr) -> @@ -141,14 +139,15 @@ add_subnet(Mask, Addr) when is_tuple(Mask), is_tuple(Addr) -> {error, empty_subnet} end. --spec delete_subnet(Mask :: ip4_address(), Addr :: ip4_address()) -> 'ok'. +-spec delete_subnet(Netmask :: inet:ip_address(), + Addr :: inet:ip_address()) -> 'ok'. delete_subnet(Mask, Addr) when is_tuple(Mask), is_tuple(Addr) -> gen_server:call(boot_server, {delete, {Mask, Addr}}). -spec which_slaves() -> Slaves when - Slaves :: [Host], - Host :: atom(). + Slaves :: [Slave], + Slave :: {Netmask :: inet:ip_address(), Address :: inet:ip_address()}. which_slaves() -> gen_server:call(boot_server, which). diff --git a/lib/kernel/src/group_history.erl b/lib/kernel/src/group_history.erl index 91f3663cc5..9745848992 100644 --- a/lib/kernel/src/group_history.erl +++ b/lib/kernel/src/group_history.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2017. All Rights Reserved. +%% Copyright Ericsson AB 2017-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. @@ -260,7 +260,7 @@ resize_log(Name, _OldSize, NewSize) -> ok -> show('$#erlang-history-resize-result', "ok~n", []); - {error, {new_size_too_small, _}} -> + {error, {new_size_too_small, _, _}} -> show('$#erlang-history-resize-result', "failed (new size is too small)~n", []), disable_history(); diff --git a/lib/kernel/test/disk_log_SUITE.erl b/lib/kernel/test/disk_log_SUITE.erl index fe2fc778f2..12e2521939 100644 --- a/lib/kernel/test/disk_log_SUITE.erl +++ b/lib/kernel/test/disk_log_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2017. All Rights Reserved. +%% Copyright Ericsson AB 1997-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. diff --git a/lib/kernel/test/global_SUITE.erl b/lib/kernel/test/global_SUITE.erl index 0a7f73c344..0e7b7adc47 100644 --- a/lib/kernel/test/global_SUITE.erl +++ b/lib/kernel/test/global_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-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. @@ -3470,8 +3470,8 @@ start_procs(Parent, N1, N2, N3, Config) -> Pid6 = rpc:call(N3, ?MODULE, start_proc3, [test4]), assert_pid(Pid6), yes = global:register_name(test1, Pid3), - yes = global:register_name(test2, Pid4, {global, notify_all_name}), - yes = global:register_name(test3, Pid5, {global, random_notify_name}), + yes = global:register_name(test2, Pid4, fun global:notify_all_name/3), + yes = global:register_name(test3, Pid5, fun global:random_notify_name/3), Resolve = fun(Name, Pid1, Pid2) -> Parent ! {resolve_called, Name, node()}, {Min, Max} = minmax(Pid1, Pid2), @@ -3546,7 +3546,7 @@ start_proc_basic(Name) -> end. init_proc_basic(Parent, Name) -> - X = global:register_name(Name, self(), {?MODULE, fix_basic_name}), + X = global:register_name(Name, self(), fun ?MODULE:fix_basic_name/3), Parent ! {self(),X}, loop(). @@ -3791,15 +3791,6 @@ stop() -> test_server:stop_node(Node) end, nodes()). -dbg_logs(Name) -> dbg_logs(Name, ?NODES). - -dbg_logs(Name, Nodes) -> - lists:foreach(fun(N) -> - F = lists:concat([Name, ".log.", N, ".txt"]), - ok = sys:log_to_file({global_name_server, N}, F) - end, Nodes). - - %% Tests that locally loaded nodes do not loose contact with other nodes. global_lost_nodes(Config) when is_list(Config) -> Timeout = 60, -- cgit v1.2.3 From 442c8ca0560ca0f1270c6cdea457e2de0333b580 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 22 Jan 2018 10:51:02 +0100 Subject: kernel: Ignore cores in erl_distribution_SUITE Some bash versions segfault when a unicode argument is given so we ignore cores created by nodes spawned by this testcase. --- lib/kernel/test/erl_distribution_SUITE.erl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl index bbfaa9d147..e34b4d77d2 100644 --- a/lib/kernel/test/erl_distribution_SUITE.erl +++ b/lib/kernel/test/erl_distribution_SUITE.erl @@ -95,7 +95,11 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. - +init_per_testcase(TC, Config) when TC == hostnames; + TC == nodenames -> + file:make_dir("hostnames_nodedir"), + file:write_file("hostnames_nodedir/ignore_core_files",""), + Config; init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> Config. @@ -251,7 +255,7 @@ test_node(Name, Illigal) -> end, net_kernel:monitor_nodes(true), BinCommand = unicode:characters_to_binary(Command, utf8), - Prt = open_port({spawn, BinCommand}, [stream]), + Prt = open_port({spawn, BinCommand}, [stream,{cd,"hostnames_nodedir"}]), Node = list_to_atom(Name), receive {nodeup, Node} -> -- cgit v1.2.3 From 82e5a5df18e21e4220e51d89c666d1da70f12ec1 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Fri, 26 Jan 2018 15:25:23 +0100 Subject: Implement function for IPv4-mapped IPv6 addresses --- lib/kernel/doc/src/inet.xml | 15 ++++++++++++++- lib/kernel/src/inet.erl | 13 +++++++++++-- lib/kernel/test/inet_SUITE.erl | 25 +++++++++++++++++++++++-- 3 files changed, 48 insertions(+), 5 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index abb045b744..5c56247820 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -4,7 +4,7 @@
    - 19972017 + 19972018 Ericsson AB. All Rights Reserved. @@ -581,6 +581,19 @@ get_tcpi_sacked(Sock) -> + + + Convert to and from IPv4-mapped IPv6 address. + +

    + Convert an IPv4 address to an IPv4-mapped IPv6 address + or the reverse. When converting from an IPv6 address + all but the 2 low words are ignored so this function also + works on some other types of addresses than IPv4-mapped. +

    +
    +
    + Parse an IPv4 or IPv6 address strict. diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index fe91b0d33e..fd26a88d42 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2017. All Rights Reserved. +%% Copyright Ericsson AB 1997-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. @@ -34,7 +34,8 @@ ip/1, stats/0, options/0, pushf/3, popf/1, close/1, gethostname/0, gethostname/1, parse_ipv4_address/1, parse_ipv6_address/1, parse_ipv4strict_address/1, - parse_ipv6strict_address/1, parse_address/1, parse_strict_address/1, ntoa/1]). + parse_ipv6strict_address/1, parse_address/1, parse_strict_address/1, + ntoa/1, ipv4_mapped_ipv6_address/1]). -export([connect_options/2, listen_options/2, udp_options/2, sctp_options/2]). -export([udp_module/1, tcp_module/1, tcp_module/2, sctp_module/1]). @@ -675,6 +676,14 @@ parse_address(Addr) -> parse_strict_address(Addr) -> inet_parse:strict_address(Addr). +-spec ipv4_mapped_ipv6_address(ip_address()) -> ip_address(). +ipv4_mapped_ipv6_address({D1,D2,D3,D4}) + when (D1 bor D2 bor D3 bor D4) < 256 -> + {0,0,0,0,0,16#ffff,(D1 bsl 8) bor D2,(D3 bsl 8) bor D4}; +ipv4_mapped_ipv6_address({D1,D2,D3,D4,D5,D6,D7,D8}) + when (D1 bor D2 bor D3 bor D4 bor D5 bor D6 bor D7 bor D8) < 65536 -> + {D7 bsr 8,D7 band 255,D8 bsr 8,D8 band 255}. + %% Return a list of available options options() -> [ diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl index 3b502be8b8..77ac743a6e 100644 --- a/lib/kernel/test/inet_SUITE.erl +++ b/lib/kernel/test/inet_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2017. All Rights Reserved. +%% Copyright Ericsson AB 1997-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. @@ -40,7 +40,8 @@ lookup_bad_search_option/1, getif/1, getif_ifr_name_overflow/1,getservbyname_overflow/1, getifaddrs/1, - parse_strict_address/1, simple_netns/1, simple_netns_open/1, + parse_strict_address/1, ipv4_mapped_ipv6_address/1, + simple_netns/1, simple_netns_open/1, simple_bind_to_device/1, simple_bind_to_device_open/1]). -export([get_hosts/1, get_ipv6_hosts/1, parse_hosts/1, parse_address/1, @@ -667,6 +668,26 @@ parse_strict_address(Config) when is_list(Config) -> {ok, {3089,3106,23603,50240,0,0,119,136}} = inet:parse_strict_address("c11:0c22:5c33:c440::077:0088"). +ipv4_mapped_ipv6_address(Config) when is_list(Config) -> + {D1,D2,D3,D4} = IPv4Address = + {rand:uniform(256) - 1, + rand:uniform(256) - 1, + rand:uniform(256) - 1, + rand:uniform(256) - 1}, + E7 = (D1 bsl 8) bor D2, + E8 = (D3 bsl 8) bor D4, + io:format("IPv4Address: ~p.~n", [IPv4Address]), + {0,0,0,0,0,65535,E7,E8} = inet:ipv4_mapped_ipv6_address(IPv4Address), + IPv6Address = + {rand:uniform(65536) - 1, + rand:uniform(65536) - 1, + rand:uniform(65536) - 1, + rand:uniform(65536) - 1, + rand:uniform(65536) - 1, + rand:uniform(65536) - 1, E7, E8}, + IPv4Address = inet:ipv4_mapped_ipv6_address(IPv6Address), + ok. + t_gethostnative(Config) when is_list(Config) -> %% this will result in 26 bytes sent which causes problem in Windows %% if the port-program has not assured stdin to be read in BINARY mode -- cgit v1.2.3 From 38a99af36f044459db40b76be2cc72c638eb6d98 Mon Sep 17 00:00:00 2001 From: bhuztez Date: Sun, 31 Dec 2017 14:07:49 +0800 Subject: make HiPE work on x86_64 when PIE is enabled Currently HiPE amd64 assumes the runtime system code is loaded into the low 2G of the address space. However, this is not the case when PIE is enabled, it is loaded into a random location. So trampolines are required to call BIFs, and also we have first to load the address of sse2_fnegate_mask to a regisiter before xorpd in fchs. --- lib/kernel/src/hipe_unified_loader.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/kernel') diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl index f8199fcf71..fd06f0f7d8 100644 --- a/lib/kernel/src/hipe_unified_loader.erl +++ b/lib/kernel/src/hipe_unified_loader.erl @@ -275,6 +275,7 @@ needs_trampolines(Architecture) -> arm -> true; powerpc -> true; ppc64 -> true; + amd64 -> true; _ -> false end. -- cgit v1.2.3 From 2ab931cec15917e1297aafce350439c8daa7a726 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Tue, 23 Jan 2018 11:47:58 +0100 Subject: Reword docs related to the runtime system mode The existing wording may be interpreted as saying that embedded mode eager loads all modules. This revision makes clear embedded mode only disables module auto loading. Since I was on it, I have reordered a couple of places to describe interactive first, and then embedded. It feels natural to cover first the default and positive mode (auto loads), and then its negation. --- lib/kernel/doc/src/code.xml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml index c94f612c01..bd95819636 100644 --- a/lib/kernel/doc/src/code.xml +++ b/lib/kernel/doc/src/code.xml @@ -34,26 +34,28 @@

    This module contains the interface to the Erlang code server, which deals with the loading of compiled code into a running Erlang runtime system.

    -

    The runtime system can be started in embedded or - interactive mode. Which one is decided by command-line +

    The runtime system can be started in interactive or + embedded mode. Which one is decided by the command-line flag -mode:

     % erl -mode interactive

    The modes are as follows:

    - -

    In embedded mode, all code is loaded during system startup - according to the boot script. (Code can also be loaded later - by explicitly ordering the code server to do so).

    -

    In interactive mode, which is default, only some code is loaded - during system startup, basically the modules needed by the runtime + during system startup, basically the modules needed by the runtime system. Other code is dynamically loaded when first referenced. When a call to a function in a certain module is made, and the module is not loaded, the code server searches for and tries to load the module.

    + +

    In embedded mode, modules are not auto loaded. Trying to use + a module that has not been loaded results in an error. This mode is + recommended when the boot script loads all modules, as it is + typically done in OTP releases. (Code can still be loaded later + by explicitly ordering the code server to do so).

    +

    To prevent accidentally reloading of modules affecting the Erlang runtime system, directories kernel, stdlib, -- cgit v1.2.3 From 1bba488c2fe51c3d3dae1e33c2ce4c1097fe9106 Mon Sep 17 00:00:00 2001 From: Anton Ryabkov Date: Wed, 10 Jan 2018 11:04:41 +0700 Subject: kernel: in the group added processing of 'EXIT' signal from shell in case of the group leader is waiting data from IO driver --- lib/kernel/src/group.erl | 250 ++++++++++++++++++++++++----------------------- 1 file changed, 130 insertions(+), 120 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/group.erl b/lib/kernel/src/group.erl index e1198d2587..2c0518ccad 100644 --- a/lib/kernel/src/group.erl +++ b/lib/kernel/src/group.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. @@ -114,7 +114,7 @@ server_loop(Drv, Shell, Buf0) -> {io_request,From,ReplyAs,Req} when is_pid(From) -> %% This io_request may cause a transition to a couple of %% selective receive loops elsewhere in this module. - Buf = io_request(Req, From, ReplyAs, Drv, Buf0), + Buf = io_request(Req, From, ReplyAs, Drv, Shell, Buf0), server_loop(Drv, Shell, Buf); {reply,{{From,ReplyAs},Reply}} -> io_reply(From, ReplyAs, Reply), @@ -135,7 +135,7 @@ server_loop(Drv, Shell, Buf0) -> exit(R); %% We want to throw away any term that we don't handle (standard %% practice in receive loops), but not any {Drv,_} tuples which are - %% handled in io_request/5. + %% handled in io_request/6. NotDrvTuple when (not is_tuple(NotDrvTuple)) orelse (tuple_size(NotDrvTuple) =/= 2) orelse (element(1, NotDrvTuple) =/= Drv) -> @@ -177,8 +177,8 @@ set_unicode_state(Drv,Bool) -> end. -io_request(Req, From, ReplyAs, Drv, Buf0) -> - case io_request(Req, Drv, {From,ReplyAs}, Buf0) of +io_request(Req, From, ReplyAs, Drv, Shell, Buf0) -> + case io_request(Req, Drv, Shell, {From,ReplyAs}, Buf0) of {ok,Reply,Buf} -> io_reply(From, ReplyAs, Reply), Buf; @@ -208,7 +208,7 @@ io_request(Req, From, ReplyAs, Drv, Buf0) -> %% %% These put requests have to be synchronous to the driver as otherwise %% there is no guarantee that the data has actually been printed. -io_request({put_chars,unicode,Chars}, Drv, From, Buf) -> +io_request({put_chars,unicode,Chars}, Drv, _Shell, From, Buf) -> case catch unicode:characters_to_binary(Chars,utf8) of Binary when is_binary(Binary) -> send_drv(Drv, {put_chars_sync, unicode, Binary, {From,ok}}), @@ -216,7 +216,7 @@ io_request({put_chars,unicode,Chars}, Drv, From, Buf) -> _ -> {error,{error,{put_chars, unicode,Chars}},Buf} end; -io_request({put_chars,unicode,M,F,As}, Drv, From, Buf) -> +io_request({put_chars,unicode,M,F,As}, Drv, _Shell, From, Buf) -> case catch apply(M, F, As) of Binary when is_binary(Binary) -> send_drv(Drv, {put_chars_sync, unicode, Binary, {From,ok}}), @@ -230,12 +230,12 @@ io_request({put_chars,unicode,M,F,As}, Drv, From, Buf) -> {error,{error,F},Buf} end end; -io_request({put_chars,latin1,Binary}, Drv, From, Buf) when is_binary(Binary) -> +io_request({put_chars,latin1,Binary}, Drv, _Shell, From, Buf) when is_binary(Binary) -> send_drv(Drv, {put_chars_sync, unicode, unicode:characters_to_binary(Binary,latin1), {From,ok}}), {noreply,Buf}; -io_request({put_chars,latin1,Chars}, Drv, From, Buf) -> +io_request({put_chars,latin1,Chars}, Drv, _Shell, From, Buf) -> case catch unicode:characters_to_binary(Chars,latin1) of Binary when is_binary(Binary) -> send_drv(Drv, {put_chars_sync, unicode, Binary, {From,ok}}), @@ -243,7 +243,7 @@ io_request({put_chars,latin1,Chars}, Drv, From, Buf) -> _ -> {error,{error,{put_chars,latin1,Chars}},Buf} end; -io_request({put_chars,latin1,M,F,As}, Drv, From, Buf) -> +io_request({put_chars,latin1,M,F,As}, Drv, _Shell, From, Buf) -> case catch apply(M, F, As) of Binary when is_binary(Binary) -> send_drv(Drv, {put_chars_sync, unicode, @@ -260,30 +260,30 @@ io_request({put_chars,latin1,M,F,As}, Drv, From, Buf) -> end end; -io_request({get_chars,Encoding,Prompt,N}, Drv, _From, Buf) -> - get_chars_n(Prompt, io_lib, collect_chars, N, Drv, Buf, Encoding); -io_request({get_line,Encoding,Prompt}, Drv, _From, Buf) -> - get_chars_line(Prompt, io_lib, collect_line, [], Drv, Buf, Encoding); -io_request({get_until,Encoding, Prompt,M,F,As}, Drv, _From, Buf) -> - get_chars_line(Prompt, io_lib, get_until, {M,F,As}, Drv, Buf, Encoding); -io_request({get_password,_Encoding},Drv,_From,Buf) -> - get_password_chars(Drv, Buf); -io_request({setopts,Opts}, Drv, _From, Buf) when is_list(Opts) -> +io_request({get_chars,Encoding,Prompt,N}, Drv, Shell, _From, Buf) -> + get_chars_n(Prompt, io_lib, collect_chars, N, Drv, Shell, Buf, Encoding); +io_request({get_line,Encoding,Prompt}, Drv, Shell, _From, Buf) -> + get_chars_line(Prompt, io_lib, collect_line, [], Drv, Shell, Buf, Encoding); +io_request({get_until,Encoding, Prompt,M,F,As}, Drv, Shell, _From, Buf) -> + get_chars_line(Prompt, io_lib, get_until, {M,F,As}, Drv, Shell, Buf, Encoding); +io_request({get_password,_Encoding},Drv,Shell,_From,Buf) -> + get_password_chars(Drv, Shell, Buf); +io_request({setopts,Opts}, Drv, _Shell, _From, Buf) when is_list(Opts) -> setopts(Opts, Drv, Buf); -io_request(getopts, Drv, _From, Buf) -> +io_request(getopts, Drv, _Shell, _From, Buf) -> getopts(Drv, Buf); -io_request({requests,Reqs}, Drv, From, Buf) -> - io_requests(Reqs, {ok,ok,Buf}, From, Drv); +io_request({requests,Reqs}, Drv, Shell, From, Buf) -> + io_requests(Reqs, {ok,ok,Buf}, From, Drv, Shell); %% New in R12 -io_request({get_geometry,columns},Drv,_From,Buf) -> +io_request({get_geometry,columns},Drv,_Shell,_From,Buf) -> case get_tty_geometry(Drv) of {W,_H} -> {ok,W,Buf}; _ -> {error,{error,enotsup},Buf} end; -io_request({get_geometry,rows},Drv,_From,Buf) -> +io_request({get_geometry,rows},Drv,_Shell,_From,Buf) -> case get_tty_geometry(Drv) of {_W,H} -> {ok,H,Buf}; @@ -292,40 +292,40 @@ io_request({get_geometry,rows},Drv,_From,Buf) -> end; %% BC with pre-R13 -io_request({put_chars,Chars}, Drv, From, Buf) -> - io_request({put_chars,latin1,Chars}, Drv, From, Buf); -io_request({put_chars,M,F,As}, Drv, From, Buf) -> - io_request({put_chars,latin1,M,F,As}, Drv, From, Buf); -io_request({get_chars,Prompt,N}, Drv, From, Buf) -> - io_request({get_chars,latin1,Prompt,N}, Drv, From, Buf); -io_request({get_line,Prompt}, Drv, From, Buf) -> - io_request({get_line,latin1,Prompt}, Drv, From, Buf); -io_request({get_until, Prompt,M,F,As}, Drv, From, Buf) -> - io_request({get_until,latin1, Prompt,M,F,As}, Drv, From, Buf); -io_request(get_password,Drv,From,Buf) -> - io_request({get_password,latin1},Drv,From,Buf); - - - -io_request(_, _Drv, _From, Buf) -> +io_request({put_chars,Chars}, Drv, Shell, From, Buf) -> + io_request({put_chars,latin1,Chars}, Drv, Shell, From, Buf); +io_request({put_chars,M,F,As}, Drv, Shell, From, Buf) -> + io_request({put_chars,latin1,M,F,As}, Drv, Shell, From, Buf); +io_request({get_chars,Prompt,N}, Drv, Shell, From, Buf) -> + io_request({get_chars,latin1,Prompt,N}, Drv, Shell, From, Buf); +io_request({get_line,Prompt}, Drv, Shell, From, Buf) -> + io_request({get_line,latin1,Prompt}, Drv, Shell, From, Buf); +io_request({get_until, Prompt,M,F,As}, Drv, Shell, From, Buf) -> + io_request({get_until,latin1, Prompt,M,F,As}, Drv, Shell, From, Buf); +io_request(get_password,Drv,Shell,From,Buf) -> + io_request({get_password,latin1},Drv,Shell,From,Buf); + + + +io_request(_, _Drv, _Shell, _From, Buf) -> {error,{error,request},Buf}. -%% Status = io_requests(RequestList, PrevStat, From, Drv) +%% Status = io_requests(RequestList, PrevStat, From, Drv, Shell) %% Process a list of output requests as long as %% the previous status is 'ok' or noreply. %% %% We use undefined as the From for all but the last request %% in order to discards acknowledgements from those requests. %% -io_requests([R|Rs], {noreply,Buf}, From, Drv) -> +io_requests([R|Rs], {noreply,Buf}, From, Drv, Shell) -> ReqFrom = if Rs =:= [] -> From; true -> undefined end, - io_requests(Rs, io_request(R, Drv, ReqFrom, Buf), From, Drv); -io_requests([R|Rs], {ok,ok,Buf}, From, Drv) -> + io_requests(Rs, io_request(R, Drv, Shell, ReqFrom, Buf), From, Drv, Shell); +io_requests([R|Rs], {ok,ok,Buf}, From, Drv, Shell) -> ReqFrom = if Rs =:= [] -> From; true -> undefined end, - io_requests(Rs, io_request(R, Drv, ReqFrom, Buf), From, Drv); -io_requests([_|_], Error, _From, _Drv) -> + io_requests(Rs, io_request(R, Drv, Shell, ReqFrom, Buf), From, Drv, Shell); +io_requests([_|_], Error, _From, _Drv, _Shell) -> Error; -io_requests([], Stat, _From, _) -> +io_requests([], Stat, _From, _, _Shell) -> Stat. %% io_reply(From, ReplyAs, Reply) @@ -333,7 +333,7 @@ io_requests([], Stat, _From, _) -> %% The ACK contains the return value. io_reply(undefined, _ReplyAs, _Reply) -> - %% Ignore these replies as they are generated from io_requests/4. + %% Ignore these replies as they are generated from io_requests/5. ok; io_reply(From, ReplyAs, Reply) -> From ! {io_reply,ReplyAs,Reply}, @@ -442,8 +442,8 @@ getopts(Drv,Buf) -> %% {Result,NewSaveBuffer} %% {error,What,NewSaveBuffer} -get_password_chars(Drv,Buf) -> - case get_password_line(Buf, Drv) of +get_password_chars(Drv,Shell,Buf) -> + case get_password_line(Buf, Drv, Shell) of {done, Line, Buf1} -> {ok, Line, Buf1}; interrupted -> @@ -452,59 +452,59 @@ get_password_chars(Drv,Buf) -> {exit, terminated} end. -get_chars_n(Prompt, M, F, Xa, Drv, Buf, Encoding) -> +get_chars_n(Prompt, M, F, Xa, Drv, Shell, Buf, Encoding) -> Pbs = prompt_bytes(Prompt, Encoding), case get(echo) of true -> - get_chars_loop(Pbs, M, F, Xa, Drv, Buf, start, Encoding); + get_chars_loop(Pbs, M, F, Xa, Drv, Shell, Buf, start, Encoding); false -> - get_chars_n_loop(Pbs, M, F, Xa, Drv, Buf, start, Encoding) + get_chars_n_loop(Pbs, M, F, Xa, Drv, Shell, Buf, start, Encoding) end. -get_chars_line(Prompt, M, F, Xa, Drv, Buf, Encoding) -> +get_chars_line(Prompt, M, F, Xa, Drv, Shell, Buf, Encoding) -> Pbs = prompt_bytes(Prompt, Encoding), - get_chars_loop(Pbs, M, F, Xa, Drv, Buf, start, Encoding). + get_chars_loop(Pbs, M, F, Xa, Drv, Shell, Buf, start, Encoding). -get_chars_loop(Pbs, M, F, Xa, Drv, Buf0, State, Encoding) -> +get_chars_loop(Pbs, M, F, Xa, Drv, Shell, Buf0, State, Encoding) -> Result = case get(echo) of true -> - get_line(Buf0, Pbs, Drv, Encoding); + get_line(Buf0, Pbs, Drv, Shell, Encoding); false -> % get_line_echo_off only deals with lists % and does not need encoding... - get_line_echo_off(Buf0, Pbs, Drv) + get_line_echo_off(Buf0, Pbs, Drv, Shell) end, case Result of {done,Line,Buf} -> - get_chars_apply(Pbs, M, F, Xa, Drv, Buf, State, Line, Encoding); + get_chars_apply(Pbs, M, F, Xa, Drv, Shell, Buf, State, Line, Encoding); interrupted -> {error,{error,interrupted},[]}; terminated -> {exit,terminated} end. -get_chars_apply(Pbs, M, F, Xa, Drv, Buf, State0, Line, Encoding) -> +get_chars_apply(Pbs, M, F, Xa, Drv, Shell, Buf, State0, Line, Encoding) -> case catch M:F(State0, cast(Line,get(read_mode), Encoding), Encoding, Xa) of {stop,Result,Rest} -> {ok,Result,append(Rest, Buf, Encoding)}; {'EXIT',_} -> {error,{error,err_func(M, F, Xa)},[]}; State1 -> - get_chars_loop(Pbs, M, F, Xa, Drv, Buf, State1, Encoding) + get_chars_loop(Pbs, M, F, Xa, Drv, Shell, Buf, State1, Encoding) end. -get_chars_n_loop(Pbs, M, F, Xa, Drv, Buf0, State, Encoding) -> +get_chars_n_loop(Pbs, M, F, Xa, Drv, Shell, Buf0, State, Encoding) -> try M:F(State, cast(Buf0, get(read_mode), Encoding), Encoding, Xa) of {stop,Result,Rest} -> {ok, Result, Rest}; State1 -> - case get_chars_echo_off(Pbs, Drv) of + case get_chars_echo_off(Pbs, Drv, Shell) of interrupted -> {error,{error,interrupted},[]}; terminated -> {exit,terminated}; Buf -> - get_chars_n_loop(Pbs, M, F, Xa, Drv, Buf, State1, Encoding) + get_chars_n_loop(Pbs, M, F, Xa, Drv, Shell, Buf, State1, Encoding) end catch _:_ -> {error,{error,err_func(M, F, Xa)},[]} @@ -523,24 +523,24 @@ err_func(_, F, _) -> %% {done,LineChars,RestChars} %% interrupted -get_line(Chars, Pbs, Drv, Encoding) -> +get_line(Chars, Pbs, Drv, Shell, Encoding) -> {more_chars,Cont,Rs} = edlin:start(Pbs), send_drv_reqs(Drv, Rs), - get_line1(edlin:edit_line(Chars, Cont), Drv, new_stack(get(line_buffer)), + get_line1(edlin:edit_line(Chars, Cont), Drv, Shell, new_stack(get(line_buffer)), Encoding). -get_line1({done,Line,Rest,Rs}, Drv, Ls, _Encoding) -> +get_line1({done,Line,Rest,Rs}, Drv, _Shell, Ls, _Encoding) -> send_drv_reqs(Drv, Rs), save_line_buffer(Line, get_lines(Ls)), {done,Line,Rest}; -get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls0, Encoding) +get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Shell, Ls0, Encoding) when ((Mode =:= none) and (Char =:= $\^P)) or ((Mode =:= meta_left_sq_bracket) and (Char =:= $A)) -> send_drv_reqs(Drv, Rs), case up_stack(save_line(Ls0, edlin:current_line(Cont))) of {none,_Ls} -> send_drv(Drv, beep), - get_line1(edlin:edit_line(Cs, Cont), Drv, Ls0, Encoding); + get_line1(edlin:edit_line(Cs, Cont), Drv, Shell, Ls0, Encoding); {Lcs,Ls} -> send_drv_reqs(Drv, edlin:erase_line(Cont)), {more_chars,Ncont,Nrs} = edlin:start(edlin:prompt(Cont)), @@ -548,16 +548,17 @@ get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls0, Encoding) get_line1(edlin:edit_line1(lists:sublist(Lcs, 1, length(Lcs)-1), Ncont), Drv, + Shell, Ls, Encoding) end; -get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls0, Encoding) +get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Shell, Ls0, Encoding) when ((Mode =:= none) and (Char =:= $\^N)) or ((Mode =:= meta_left_sq_bracket) and (Char =:= $B)) -> send_drv_reqs(Drv, Rs), case down_stack(save_line(Ls0, edlin:current_line(Cont))) of {none,_Ls} -> send_drv(Drv, beep), - get_line1(edlin:edit_line(Cs, Cont), Drv, Ls0, Encoding); + get_line1(edlin:edit_line(Cs, Cont), Drv, Shell, Ls0, Encoding); {Lcs,Ls} -> send_drv_reqs(Drv, edlin:erase_line(Cont)), {more_chars,Ncont,Nrs} = edlin:start(edlin:prompt(Cont)), @@ -565,6 +566,7 @@ get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls0, Encoding) get_line1(edlin:edit_line1(lists:sublist(Lcs, 1, length(Lcs)-1), Ncont), Drv, + Shell, Ls, Encoding) end; %% ^R = backward search, ^S = forward search. @@ -577,7 +579,7 @@ get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls0, Encoding) %% new modes: search, search_quit, search_found. These are added to %% the regular ones (none, meta_left_sq_bracket) and handle special %% cases of history search. -get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls, Encoding) +get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Shell, Ls, Encoding) when ((Mode =:= none) and (Char =:= $\^R)) -> send_drv_reqs(Drv, Rs), %% drop current line, move to search mode. We store the current @@ -587,8 +589,8 @@ get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls, Encoding) Pbs = prompt_bytes("(search)`': ", Encoding), {more_chars,Ncont,Nrs} = edlin:start(Pbs, search), send_drv_reqs(Drv, Nrs), - get_line1(edlin:edit_line1(Cs, Ncont), Drv, Ls, Encoding); -get_line1({expand, Before, Cs0, Cont,Rs}, Drv, Ls0, Encoding) -> + get_line1(edlin:edit_line1(Cs, Ncont), Drv, Shell, Ls, Encoding); +get_line1({expand, Before, Cs0, Cont,Rs}, Drv, Shell, Ls0, Encoding) -> send_drv_reqs(Drv, Rs), ExpandFun = get(expand_fun), {Found, Add, Matches} = ExpandFun(Before), @@ -603,37 +605,37 @@ get_line1({expand, Before, Cs0, Cont,Rs}, Drv, Ls0, Encoding) -> send_drv(Drv, {put_chars, unicode, unicode:characters_to_binary(MatchStr,unicode)}), [$\^L | Cs1] end, - get_line1(edlin:edit_line(Cs, Cont), Drv, Ls0, Encoding); -get_line1({undefined,_Char,Cs,Cont,Rs}, Drv, Ls, Encoding) -> + get_line1(edlin:edit_line(Cs, Cont), Drv, Shell, Ls0, Encoding); +get_line1({undefined,_Char,Cs,Cont,Rs}, Drv, Shell, Ls, Encoding) -> send_drv_reqs(Drv, Rs), send_drv(Drv, beep), - get_line1(edlin:edit_line(Cs, Cont), Drv, Ls, Encoding); + get_line1(edlin:edit_line(Cs, Cont), Drv, Shell, Ls, Encoding); %% The search item was found and accepted (new line entered on the exact %% result found) -get_line1({_What,Cont={line,_Prompt,_Chars,search_found},Rs}, Drv, Ls0, Encoding) -> +get_line1({_What,Cont={line,_Prompt,_Chars,search_found},Rs}, Drv, Shell, Ls0, Encoding) -> Line = edlin:current_line(Cont), %% this may create duplicate entries. Ls = save_line(new_stack(get_lines(Ls0)), Line), - get_line1({done, Line, "", Rs}, Drv, Ls, Encoding); + get_line1({done, Line, "", Rs}, Drv, Shell, Ls, Encoding); %% The search mode has been exited, but the user wants to remain in line %% editing mode wherever that was, but editing the search result. -get_line1({What,Cont={line,_Prompt,_Chars,search_quit},Rs}, Drv, Ls, Encoding) -> +get_line1({What,Cont={line,_Prompt,_Chars,search_quit},Rs}, Drv, Shell, Ls, Encoding) -> Line = edlin:current_chars(Cont), %% Load back the old prompt with the correct line number. case get(search_quit_prompt) of undefined -> % should not happen. Fallback. LsFallback = save_line(new_stack(get_lines(Ls)), Line), - get_line1({done, "\n", Line, Rs}, Drv, LsFallback, Encoding); + get_line1({done, "\n", Line, Rs}, Drv, Shell, LsFallback, Encoding); Prompt -> % redraw the line and keep going with the same stack position NCont = {line,Prompt,{lists:reverse(Line),[]},none}, send_drv_reqs(Drv, Rs), send_drv_reqs(Drv, edlin:erase_line(Cont)), send_drv_reqs(Drv, edlin:redraw_line(NCont)), - get_line1({What, NCont ,[]}, Drv, pad_stack(Ls), Encoding) + get_line1({What, NCont ,[]}, Drv, Shell, pad_stack(Ls), Encoding) end; %% Search mode is entered. get_line1({What,{line,Prompt,{RevCmd0,_Aft},search},Rs}, - Drv, Ls0, Encoding) -> + Drv, Shell, Ls0, Encoding) -> send_drv_reqs(Drv, Rs), %% Figure out search direction. ^S and ^R are returned through edlin %% whenever we received a search while being already in search mode. @@ -655,82 +657,88 @@ get_line1({What,{line,Prompt,{RevCmd0,_Aft},search},Rs}, {Ls2, {RevCmd, "': "++Line}} end, Cont = {line,Prompt,NewStack,search}, - more_data(What, Cont, Drv, Ls, Encoding); -get_line1({What,Cont0,Rs}, Drv, Ls, Encoding) -> + more_data(What, Cont, Drv, Shell, Ls, Encoding); +get_line1({What,Cont0,Rs}, Drv, Shell, Ls, Encoding) -> send_drv_reqs(Drv, Rs), - more_data(What, Cont0, Drv, Ls, Encoding). + more_data(What, Cont0, Drv, Shell, Ls, Encoding). -more_data(What, Cont0, Drv, Ls, Encoding) -> +more_data(What, Cont0, Drv, Shell, Ls, Encoding) -> receive {Drv,{data,Cs}} -> - get_line1(edlin:edit_line(Cs, Cont0), Drv, Ls, Encoding); + get_line1(edlin:edit_line(Cs, Cont0), Drv, Shell, Ls, Encoding); {Drv,eof} -> - get_line1(edlin:edit_line(eof, Cont0), Drv, Ls, Encoding); + get_line1(edlin:edit_line(eof, Cont0), Drv, Shell, Ls, Encoding); {io_request,From,ReplyAs,Req} when is_pid(From) -> {more_chars,Cont,_More} = edlin:edit_line([], Cont0), send_drv_reqs(Drv, edlin:erase_line(Cont)), - io_request(Req, From, ReplyAs, Drv, []), %WRONG!!! + io_request(Req, From, ReplyAs, Drv, Shell, []), %WRONG!!! send_drv_reqs(Drv, edlin:redraw_line(Cont)), - get_line1({more_chars,Cont,[]}, Drv, Ls, Encoding); + get_line1({more_chars,Cont,[]}, Drv, Shell, Ls, Encoding); {reply,{{From,ReplyAs},Reply}} -> %% We take care of replies from puts here as well io_reply(From, ReplyAs, Reply), - more_data(What, Cont0, Drv, Ls, Encoding); + more_data(What, Cont0, Drv, Shell, Ls, Encoding); {'EXIT',Drv,interrupt} -> interrupted; {'EXIT',Drv,_} -> - terminated + terminated; + {'EXIT',Shell,R} -> + exit(R) after get_line_timeout(What)-> - get_line1(edlin:edit_line([], Cont0), Drv, Ls, Encoding) + get_line1(edlin:edit_line([], Cont0), Drv, Shell, Ls, Encoding) end. -get_line_echo_off(Chars, Pbs, Drv) -> +get_line_echo_off(Chars, Pbs, Drv, Shell) -> send_drv_reqs(Drv, [{put_chars, unicode,Pbs}]), - get_line_echo_off1(edit_line(Chars,[]), Drv). + get_line_echo_off1(edit_line(Chars,[]), Drv, Shell). -get_line_echo_off1({Chars,[]}, Drv) -> +get_line_echo_off1({Chars,[]}, Drv, Shell) -> receive {Drv,{data,Cs}} -> - get_line_echo_off1(edit_line(Cs, Chars), Drv); + get_line_echo_off1(edit_line(Cs, Chars), Drv, Shell); {Drv,eof} -> - get_line_echo_off1(edit_line(eof, Chars), Drv); + get_line_echo_off1(edit_line(eof, Chars), Drv, Shell); {io_request,From,ReplyAs,Req} when is_pid(From) -> - io_request(Req, From, ReplyAs, Drv, []), - get_line_echo_off1({Chars,[]}, Drv); + io_request(Req, From, ReplyAs, Drv, Shell, []), + get_line_echo_off1({Chars,[]}, Drv, Shell); {reply,{{From,ReplyAs},Reply}} when From =/= undefined -> %% We take care of replies from puts here as well io_reply(From, ReplyAs, Reply), - get_line_echo_off1({Chars,[]},Drv); + get_line_echo_off1({Chars,[]},Drv, Shell); {'EXIT',Drv,interrupt} -> interrupted; {'EXIT',Drv,_} -> - terminated + terminated; + {'EXIT',Shell,R} -> + exit(R) end; -get_line_echo_off1({Chars,Rest}, _Drv) -> +get_line_echo_off1({Chars,Rest}, _Drv, _Shell) -> {done,lists:reverse(Chars),case Rest of done -> []; _ -> Rest end}. -get_chars_echo_off(Pbs, Drv) -> +get_chars_echo_off(Pbs, Drv, Shell) -> send_drv_reqs(Drv, [{put_chars, unicode,Pbs}]), - get_chars_echo_off1(Drv). + get_chars_echo_off1(Drv, Shell). -get_chars_echo_off1(Drv) -> +get_chars_echo_off1(Drv, Shell) -> receive {Drv, {data, Cs}} -> Cs; {Drv, eof} -> eof; {io_request,From,ReplyAs,Req} when is_pid(From) -> - io_request(Req, From, ReplyAs, Drv, []), - get_chars_echo_off1(Drv); + io_request(Req, From, ReplyAs, Drv, Shell, []), + get_chars_echo_off1(Drv, Shell); {reply,{{From,ReplyAs},Reply}} when From =/= undefined -> %% We take care of replies from puts here as well io_reply(From, ReplyAs, Reply), - get_chars_echo_off1(Drv); + get_chars_echo_off1(Drv, Shell); {'EXIT',Drv,interrupt} -> interrupted; {'EXIT',Drv,_} -> - terminated + terminated; + {'EXIT',Shell,R} -> + exit(R) end. %% We support line editing for the ICANON mode except the following @@ -861,30 +869,32 @@ search_down_stack(Stack, Substr) -> %% This is get_line without line editing (except for backspace) and %% without echo. -get_password_line(Chars, Drv) -> - get_password1(edit_password(Chars,[]),Drv). +get_password_line(Chars, Drv, Shell) -> + get_password1(edit_password(Chars,[]),Drv,Shell). -get_password1({Chars,[]}, Drv) -> +get_password1({Chars,[]}, Drv, Shell) -> receive {Drv,{data,Cs}} -> - get_password1(edit_password(Cs,Chars),Drv); + get_password1(edit_password(Cs,Chars),Drv,Shell); {io_request,From,ReplyAs,Req} when is_pid(From) -> %send_drv_reqs(Drv, [{delete_chars, -length(Pbs)}]), - io_request(Req, From, ReplyAs, Drv, []), %WRONG!!! + io_request(Req, From, ReplyAs, Drv, Shell, []), %WRONG!!! %% I guess the reason the above line is wrong is that Buf is %% set to []. But do we expect anything but plain output? - get_password1({Chars, []}, Drv); + get_password1({Chars, []}, Drv, Shell); {reply,{{From,ReplyAs},Reply}} -> %% We take care of replies from puts here as well io_reply(From, ReplyAs, Reply), - get_password1({Chars, []},Drv); + get_password1({Chars, []},Drv, Shell); {'EXIT',Drv,interrupt} -> interrupted; {'EXIT',Drv,_} -> - terminated + terminated; + {'EXIT',Shell,R} -> + exit(R) end; -get_password1({Chars,Rest},Drv) -> +get_password1({Chars,Rest},Drv,_Shell) -> send_drv_reqs(Drv,[{put_chars, unicode, "\n"}]), {done,lists:reverse(Chars),case Rest of done -> []; _ -> Rest end}. -- cgit v1.2.3 From 798f2151429ef7ea05d858e5ba9efc6d5cc3e10a Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Mon, 5 Feb 2018 12:33:21 +0100 Subject: kernel: Do not call erlang:get_stacktrace() --- lib/kernel/src/dist_util.erl | 4 ++-- lib/kernel/src/error_handler.erl | 4 ++-- lib/kernel/src/file.erl | 4 ++-- lib/kernel/test/code_SUITE.erl | 21 +++++++++------------ lib/kernel/test/erl_distribution_SUITE.erl | 4 ++-- lib/kernel/test/file_SUITE.erl | 4 ++-- lib/kernel/test/gen_sctp_SUITE.erl | 6 ++---- lib/kernel/test/gen_tcp_api_SUITE.erl | 4 ++-- lib/kernel/test/gen_udp_SUITE.erl | 4 ++-- lib/kernel/test/inet_res_SUITE.erl | 4 ++-- 10 files changed, 27 insertions(+), 32 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index fb9f7fd7eb..5ea0ca991f 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -539,8 +539,8 @@ do_setnode(#hs_data{other_node = Node, socket = Socket, "no table space left for node ~w ** ~n", [Node]), ?shutdown(Node); - error:Other -> - exit({Other, erlang:get_stacktrace()}) + error:Other:Stacktrace -> + exit({Other, Stacktrace}) end; _ -> error_msg("** Distribution connection error, " diff --git a/lib/kernel/src/error_handler.erl b/lib/kernel/src/error_handler.erl index 59ca8e690d..a9582c6225 100644 --- a/lib/kernel/src/error_handler.erl +++ b/lib/kernel/src/error_handler.erl @@ -106,8 +106,8 @@ crash(M, F, A) -> crash(Tuple) -> try erlang:error(undef) catch - error:undef -> - Stk = [Tuple|tl(erlang:get_stacktrace())], + error:undef:Stacktrace -> + Stk = [Tuple|tl(Stacktrace)], erlang:raise(error, undef, Stk) end. diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index d05199897f..c2df1ee288 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -1382,8 +1382,8 @@ eval_stream2({ok,Form,EndLine}, Fd, H, Last, E, Bs0) -> try erl_eval:exprs(Form, Bs0) of {value,V,Bs} -> eval_stream(Fd, H, EndLine, {V}, E, Bs) - catch Class:Reason -> - Error = {EndLine,?MODULE,{Class,Reason,erlang:get_stacktrace()}}, + catch Class:Reason:StackTrace -> + Error = {EndLine,?MODULE,{Class,Reason,StackTrace}}, eval_stream(Fd, H, EndLine, Last, [Error|E], Bs0) end; eval_stream2({error,What,EndLine}, Fd, H, Last, E, Bs) -> diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 569753155f..902196def2 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -931,37 +931,34 @@ purge_stacktrace(Config) when is_list(Config) -> code:purge(code_b_test), try code_b_test:call(fun(b) -> ok end, a) catch - error:function_clause -> + error:function_clause:Stacktrace -> code:load_file(code_b_test), - case erlang:get_stacktrace() of + case Stacktrace of [{?MODULE,_,[a],_}, {code_b_test,call,2,_}, {?MODULE,purge_stacktrace,1,_}|_] -> - false = code:purge(code_b_test), - [] = erlang:get_stacktrace() + false = code:purge(code_b_test) end end, try code_b_test:call(nofun, 2) catch - error:function_clause -> + error:function_clause:Stacktrace2 -> code:load_file(code_b_test), - case erlang:get_stacktrace() of + case Stacktrace2 of [{code_b_test,call,[nofun,2],_}, {?MODULE,purge_stacktrace,1,_}|_] -> - false = code:purge(code_b_test), - [] = erlang:get_stacktrace() + false = code:purge(code_b_test) end end, Args = [erlang,error,[badarg]], try code_b_test:call(erlang, error, [badarg,Args]) catch - error:badarg -> + error:badarg:Stacktrace3 -> code:load_file(code_b_test), - case erlang:get_stacktrace() of + case Stacktrace3 of [{code_b_test,call,Args,_}, {?MODULE,purge_stacktrace,1,_}|_] -> - false = code:purge(code_b_test), - [] = erlang:get_stacktrace() + false = code:purge(code_b_test) end end, ok. diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl index bbfaa9d147..073b391288 100644 --- a/lib/kernel/test/erl_distribution_SUITE.erl +++ b/lib/kernel/test/erl_distribution_SUITE.erl @@ -459,9 +459,9 @@ run_remote_test([FuncStr, TestNodeStr | Args]) -> 1 end catch - C:E -> + C:E:S -> io:format("Node ~p got EXCEPTION ~p:~p\nat ~p\n", - [node(), C, E, erlang:get_stacktrace()]), + [node(), C, E, S]), 2 end, io:format("Node ~p doing halt(~p).\n",[node(), Status]), diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 0cb8087a76..9a77454432 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -399,11 +399,11 @@ read_write_0(Str, {Func, ReadFun}, Options) -> io:format("~p:~p: ~p ERROR: ~ts vs~n ~w~n - ~p~n", [?MODULE, Line, Func, Str, ReadBytes, Options]), exit({error, ?LINE}); - error:What -> + error:What:Stacktrace -> io:format("~p:??: ~p ERROR: ~p from~n ~w~n ~p~n", [?MODULE, Func, What, Str, Options]), - io:format("\t~p~n", [erlang:get_stacktrace()]), + io:format("\t~p~n", [Stacktrace]), exit({error, ?LINE}) end. diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl index 620ab235a0..9dde00652c 100644 --- a/lib/kernel/test/gen_sctp_SUITE.erl +++ b/lib/kernel/test/gen_sctp_SUITE.erl @@ -1038,8 +1038,7 @@ do_from_other_process(Fun) -> Result -> Parent ! {Ref,Result} catch - Class:Reason -> - Stacktrace = erlang:get_stacktrace(), + Class:Reason:Stacktrace -> Parent ! {Ref,Class,Reason,Stacktrace} end end), @@ -1617,8 +1616,7 @@ s_start(Socket, Timeout, Parent) -> try s_loop(Socket, Timeout, Parent, Handler, gb_trees:empty()) catch - Class:Reason -> - Stacktrace = erlang:get_stacktrace(), + Class:Reason:Stacktrace -> io:format(?MODULE_STRING":socket exception ~w:~w at~n" "~p.~n", [Class,Reason,Stacktrace]), erlang:raise(Class, Reason, Stacktrace) diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl index 12d22519ce..0fe44e8a88 100644 --- a/lib/kernel/test/gen_tcp_api_SUITE.erl +++ b/lib/kernel/test/gen_tcp_api_SUITE.erl @@ -605,9 +605,9 @@ ok({ok,V}) -> V; ok(NotOk) -> try throw(not_ok) catch - Thrown -> + throw:Thrown:Stacktrace -> erlang:raise( - error, {Thrown, NotOk}, tl(erlang:get_stacktrace())) + error, {Thrown, NotOk}, tl(Stacktrace)) end. get_localaddr() -> diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index 96e495505a..6a50239c2a 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -755,9 +755,9 @@ ok({ok,V}) -> V; ok(NotOk) -> try throw(not_ok) catch - Thrown -> + throw:Thrown:Stacktrace -> erlang:raise( - error, {Thrown, NotOk}, tl(erlang:get_stacktrace())) + error, {Thrown, NotOk}, tl(Stacktrace)) end. diff --git a/lib/kernel/test/inet_res_SUITE.erl b/lib/kernel/test/inet_res_SUITE.erl index 6691ad9c06..2a5b8d0044 100644 --- a/lib/kernel/test/inet_res_SUITE.erl +++ b/lib/kernel/test/inet_res_SUITE.erl @@ -217,10 +217,10 @@ proxy_start(TC, {NS,P}) -> spawn_link( fun () -> try proxy_start(TC, NS, P, Parent, Tag) - catch C:X -> + catch C:X:Stacktrace -> io:format( "~w: ~w:~p ~p~n", - [self(),C,X,erlang:get_stacktrace()]) + [self(),C,X,Stacktrace]) end end), receive {started,Tag,Port} -> -- cgit v1.2.3 From 3162499ddc48d4ce877945775f084725ceab57c5 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Mon, 12 Feb 2018 13:38:24 +0100 Subject: Update version numbers --- lib/kernel/vsn.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 106bda01ca..91261e1d55 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 5.4.1 +KERNEL_VSN = 5.4.2 -- cgit v1.2.3 From e11c649522be8849cc21e364734be62c7783f090 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Mon, 12 Feb 2018 13:38:43 +0100 Subject: Update release notes --- lib/kernel/doc/src/notes.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index d7f224c38e..65fe9b9c07 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,26 @@

    This document describes the changes made to the Kernel application.

    +
    Kernel 5.4.2 + +
    Fixed Bugs and Malfunctions + + +

    + Add os:cmd/2 that takes an options map as the + second argument.

    +

    + Add max_size as an option to os:cmd/2 that + control the maximum size of the result that + os:cmd/2 will return.

    +

    + Own Id: OTP-14823

    +
    +
    +
    + +
    +
    Kernel 5.4.1
    Fixed Bugs and Malfunctions -- cgit v1.2.3 From ee6d1b9a61d8f2f087f0f3bf3ea2b933b5c3a93a Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Tue, 13 Feb 2018 10:14:22 +0100 Subject: Stop returning V4MAPPED addresses --- lib/kernel/doc/src/inet.xml | 4 +--- lib/kernel/doc/src/inet_res.xml | 6 ++---- lib/kernel/src/inet.erl | 4 +--- lib/kernel/src/inet_res.erl | 25 ++----------------------- 4 files changed, 6 insertions(+), 33 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index 5c56247820..9552332948 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -277,9 +277,7 @@ fe80::204:acff:fe17:bf38

    Returns a hostent record for the host with the specified hostname.

    If resolver option inet6 is true, - an IPv6 address is looked up. If that fails, - the IPv4 address is looked up and returned on - IPv6-mapped IPv4 format.

    + an IPv6 address is looked up.

    diff --git a/lib/kernel/doc/src/inet_res.xml b/lib/kernel/doc/src/inet_res.xml index 3454e3c6f9..351d86a93a 100644 --- a/lib/kernel/doc/src/inet_res.xml +++ b/lib/kernel/doc/src/inet_res.xml @@ -4,7 +4,7 @@
    - 20092015 + 20092018 Ericsson AB. All Rights Reserved. @@ -230,9 +230,7 @@ inet_dns:record_type(_) -> undefined. getbyname/2,3.

    If resolver option inet6 is true, - an IPv6 address is looked up. If that fails, - the IPv4 address is looked up and returned on - IPv6-mapped IPv4 format.

    + an IPv6 address is looked up.

    diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index fd26a88d42..4bad523dff 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -1253,9 +1253,7 @@ gethostbyname_string(Name, Type) inet -> inet_parse:ipv4_address(Name); inet6 -> - %% XXX should we really translate IPv4 addresses here - %% even if we do not know if this host can do IPv6? - inet_parse:ipv6_address(Name) + inet_parse:ipv6strict_address(Name) end of {ok,IP} -> {ok,make_hostent(Name, [IP], [], Type)}; diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl index 49aa5f8bda..58017c62fb 100644 --- a/lib/kernel/src/inet_res.erl +++ b/lib/kernel/src/inet_res.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-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. @@ -431,28 +431,7 @@ gethostbyname(Name,Family,Timeout) -> gethostbyname_tm(Name,inet,Timer) -> getbyname_tm(Name,?S_A,Timer); gethostbyname_tm(Name,inet6,Timer) -> - case getbyname_tm(Name,?S_AAAA,Timer) of - {ok,HEnt} -> {ok,HEnt}; - {error,nxdomain} -> - case getbyname_tm(Name, ?S_A,Timer) of - {ok, HEnt} -> - %% rewrite to a ipv4 only ipv6 address - {ok, - HEnt#hostent { - h_addrtype = inet6, - h_length = 16, - h_addr_list = - lists:map( - fun({A,B,C,D}) -> - {0,0,0,0,0,16#ffff,A*256+B,C*256+D} - end, HEnt#hostent.h_addr_list) - }}; - Error -> - Error - end; - Error -> - Error - end; + getbyname_tm(Name,?S_AAAA,Timer); gethostbyname_tm(_Name, _Family, _Timer) -> {error, einval}. -- cgit v1.2.3 From 54ac9157e121175615485f89dbf90fc645435d17 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 15 Feb 2018 13:44:19 +0100 Subject: kernel: Reject load of module names with slash or backslash on Windows. Purpose: Prevent tricks to get hostile code running. --- lib/kernel/src/code_server.erl | 27 +++++++++++++++++---------- lib/kernel/src/kernel.app.src | 2 +- 2 files changed, 18 insertions(+), 11 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index 418b0c50e1..f5a890cb95 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -340,8 +340,7 @@ handle_call(all_loaded, _From, S) -> {reply,all_loaded(Db),S}; handle_call({get_object_code,Mod}, _From, St) when is_atom(Mod) -> - Path = St#state.path, - case mod_to_bin(Path, Mod) of + case get_object_code(St, Mod) of {_,Bin,FName} -> {reply,{Mod,Bin,FName},St}; Error -> {reply,Error,St} end; @@ -1182,19 +1181,28 @@ load_file(Mod, From, St0) -> end, handle_pending_on_load(Action, Mod, From, St0). -load_file_1(Mod, From, #state{path=Path}=St) -> - case mod_to_bin(Path, Mod) of +load_file_1(Mod, From, St) -> + case get_object_code(St, Mod) of error -> {reply,{error,nofile},St}; {Mod,Binary,File} -> try_load_module_1(File, Mod, Binary, From, St) end. -mod_to_bin([Dir|Tail], Mod) -> - File = filename:append(Dir, atom_to_list(Mod) ++ objfile_extension()), +get_object_code(#state{path=Path}, Mod) when is_atom(Mod) -> + ModStr = atom_to_list(Mod), + case erl_prim_loader:is_basename(ModStr) of + true -> + mod_to_bin(Path, Mod, ModStr ++ objfile_extension()); + false -> + error + end. + +mod_to_bin([Dir|Tail], Mod, ModFile) -> + File = filename:append(Dir, ModFile), case erl_prim_loader:get_file(File) of error -> - mod_to_bin(Tail, Mod); + mod_to_bin(Tail, Mod, ModFile); {ok,Bin,_} -> case filename:pathtype(File) of absolute -> @@ -1203,10 +1211,9 @@ mod_to_bin([Dir|Tail], Mod) -> {Mod,Bin,absname(File)} end end; -mod_to_bin([], Mod) -> +mod_to_bin([], Mod, ModFile) -> %% At last, try also erl_prim_loader's own method - File = to_list(Mod) ++ objfile_extension(), - case erl_prim_loader:get_file(File) of + case erl_prim_loader:get_file(ModFile) of error -> error; % No more alternatives ! {ok,Bin,FName} -> diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 2a88cc7e26..0eca6fef03 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -120,6 +120,6 @@ {applications, []}, {env, [{error_logger, tty}]}, {mod, {kernel, []}}, - {runtime_dependencies, ["erts-9.1", "stdlib-3.4", "sasl-3.0"]} + {runtime_dependencies, ["erts-9.3", "stdlib-3.4", "sasl-3.0"]} ] }. -- cgit v1.2.3 From 4d547dfb2a012ba1cf8fb9dd3cdc4d9df933a37f Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 21 Feb 2018 22:05:10 +0100 Subject: erts: Optimize erlang:put/2 for immed values Do destructive write of immed value if key exists. This is an optimization at the expense of get/0 which must now copy all (mutable) key-value tuples. --- lib/kernel/test/pdict_SUITE.erl | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/test/pdict_SUITE.erl b/lib/kernel/test/pdict_SUITE.erl index d105952df9..a891451c82 100644 --- a/lib/kernel/test/pdict_SUITE.erl +++ b/lib/kernel/test/pdict_SUITE.erl @@ -33,6 +33,7 @@ init_per_group/2,end_per_group/2, mixed/1, literals/1, + destructive/1, simple/1, complicated/1, heavy/1, simple_all_keys/1, info/1]). -export([init_per_testcase/2, end_per_testcase/2]). -export([other_process/2]). @@ -52,6 +53,7 @@ suite() -> all() -> [simple, complicated, heavy, simple_all_keys, info, literals, + destructive, mixed]. groups() -> @@ -367,6 +369,36 @@ match_keys(All) -> ok. +%% Test destructive put optimization of immed values +%% does not affect get/0 or process_info. +destructive(_Config) -> + Keys = lists:seq(1,100), + [put(Key, 17) || Key <- Keys], + Get1 = get(), + {dictionary,PI1} = process_info(self(), dictionary), + + [begin + {Key, 17} = lists:keyfind(Key, 1, Get1), + {Key, 17} = lists:keyfind(Key, 1, PI1) + end + || Key <- Keys], + + [17 = put(Key, 42) || Key <- Keys], % Mutate + + Get2 = get(), + {dictionary,PI2} = process_info(self(), dictionary), + + [begin + {Key, 17} = lists:keyfind(Key, 1, Get1), + {Key, 17} = lists:keyfind(Key, 1, PI1), + {Key, 42} = lists:keyfind(Key, 1, Get2), + {Key, 42} = lists:keyfind(Key, 1, PI2) + + end + || Key <- Keys], + + ok. + %% Do random mixed put/erase to test grow/shrink %% Written for a temporary bug in gc during shrink mixed(_Config) -> -- cgit v1.2.3 From 6a4fb088b97a118bd1dcc1a544578f9fabc10c25 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Thu, 22 Feb 2018 14:46:23 +0100 Subject: Stop translating V4MAPPED addresses --- lib/kernel/src/inet_hosts.erl | 5 +---- lib/kernel/src/inet_res.erl | 3 --- 2 files changed, 1 insertion(+), 7 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/inet_hosts.erl b/lib/kernel/src/inet_hosts.erl index 0bdf00ac30..fc653bf0d3 100644 --- a/lib/kernel/src/inet_hosts.erl +++ b/lib/kernel/src/inet_hosts.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-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. @@ -72,9 +72,6 @@ gethostbyname(Name, Type, Byname, Byaddr) -> gethostbyaddr({A,B,C,D}=IP) when ?ip(A,B,C,D) -> gethostbyaddr(IP, inet); -%% ipv4 only ipv6 address -gethostbyaddr({0,0,0,0,0,16#ffff=F,G,H}) when ?ip6(0,0,0,0,0,F,G,H) -> - gethostbyaddr({G bsr 8, G band 255, H bsr 8, H band 255}); gethostbyaddr({A,B,C,D,E,F,G,H}=IP) when ?ip6(A,B,C,D,E,F,G,H) -> gethostbyaddr(IP, inet6); gethostbyaddr(Addr) when is_list(Addr) -> diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl index 58017c62fb..6454802b04 100644 --- a/lib/kernel/src/inet_res.erl +++ b/lib/kernel/src/inet_res.erl @@ -349,9 +349,6 @@ gethostbyaddr_tm({A,B,C,D} = IP, Timer) when ?ip(A,B,C,D) -> {ok, HEnt} -> {ok, HEnt}; _ -> res_gethostbyaddr(dn_in_addr_arpa(A,B,C,D), IP, Timer) end; -%% ipv4 only ipv6 address -gethostbyaddr_tm({0,0,0,0,0,16#ffff,G,H},Timer) when is_integer(G+H) -> - gethostbyaddr_tm({G div 256, G rem 256, H div 256, H rem 256},Timer); gethostbyaddr_tm({A,B,C,D,E,F,G,H} = IP, Timer) when ?ip6(A,B,C,D,E,F,G,H) -> inet_db:res_update_conf(), case inet_db:gethostbyaddr(IP) of -- cgit v1.2.3 From 6f120d4ab4fab2231f7475256bcc1eeac2bfe6e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Mon, 26 Feb 2018 10:28:26 +0100 Subject: Allow opening device files and FIFOs with file:open/2 To the best of our knowledge this was introduced since file operations on device files/FIFO:s could hang the emulator forever back when the emulator was single-threaded and lacked IO threads; a read operation could block all progress preventing the write operation it waited for from occurring. Granted, this could still happen through starving all dirty IO schedulers, but the same issue can arise with NFS files which we've always allowed. Removing this restriction also lets us remove a stat(2) call that was added to specifically allow `/dev/null`. --- lib/kernel/doc/src/file.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index 8477b0e148..1b72769ce3 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -981,8 +981,7 @@ f.txt: {person, "kalle", 25}. eisdir -

    The named file is not a regular file. It can be a - directory, a FIFO, or a device.

    +

    The named file is a directory.

    enotdir -- cgit v1.2.3 From 5e0c4f113f229b54c8177874032e4528e55bddac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Tue, 27 Feb 2018 11:58:42 +0100 Subject: Skip testing unavailable interfaces in inet_SUITE:getifaddrs This test consistently failed on Windows when an interface was enabled but unplugged, as the interface was 'up' but not 'running'. --- lib/kernel/test/inet_SUITE.erl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl index 3b502be8b8..ba0d075ef2 100644 --- a/lib/kernel/test/inet_SUITE.erl +++ b/lib/kernel/test/inet_SUITE.erl @@ -1083,11 +1083,9 @@ ifaddrs([{If,Opts}|IOs]) -> #ifopts{flags=F} = Ifopts = check_ifopts(Opts, #ifopts{name=If}), case F of {flags,Flags} -> - case lists:member(up, Flags) of - true -> - Ifopts#ifopts.addrs; - false -> - [] + case lists:member(running, Flags) of + true -> Ifopts#ifopts.addrs; + false -> [] end ++ ifaddrs(IOs); undefined -> ifaddrs(IOs) -- cgit v1.2.3 From 4baa7361afa5b10e46bf1438e560dc4b33f5b248 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Tue, 27 Feb 2018 13:35:25 +0100 Subject: kernel: Improve docs of rpc:multicall/5 --- lib/kernel/doc/src/rpc.xml | 11 ++++++----- lib/kernel/src/rpc.erl | 10 +++++----- 2 files changed, 11 insertions(+), 10 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/rpc.xml b/lib/kernel/doc/src/rpc.xml index adec2d9520..fab616e630 100644 --- a/lib/kernel/doc/src/rpc.xml +++ b/lib/kernel/doc/src/rpc.xml @@ -4,7 +4,7 @@
    - 19962016 + 19962018 Ericsson AB. All Rights Reserved. @@ -217,7 +217,7 @@ A list of the nodes that do not exist A list of the nodes where the server does not exist - A list of the nodes where the server terminatd before sending + A list of the nodes where the server terminated before sending any reply. @@ -268,8 +268,9 @@ on the specified nodes and collects the answers. It returns {ResL, BadNodes}, where BadNodes is a list - of the nodes that terminated or timed out during computation, - and ResL is a list of the return values. + of the nodes that do not exist, + and ResL is a list of the return values, + or {badrpc, Reason} for failing calls. Timeout is a time (integer) in milliseconds, or infinity.

    The following example is useful when new object code is to @@ -347,7 +348,7 @@ - Parallell evaluation of mapping a function over a + Parallel evaluation of mapping a function over a list.

    Evaluates apply(Module, Function, diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl index 0e0b7dffa3..b04aa9030b 100644 --- a/lib/kernel/src/rpc.erl +++ b/lib/kernel/src/rpc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. @@ -498,7 +498,7 @@ start_monitor(Node, Name) -> Module :: module(), Function :: atom(), Args :: [term()], - ResL :: [term()], + ResL :: [Res :: term() | {'badrpc', Reason :: term()}], BadNodes :: [node()]. multicall(M, F, A) -> @@ -509,14 +509,14 @@ multicall(M, F, A) -> Module :: module(), Function :: atom(), Args :: [term()], - ResL :: [term()], + ResL :: [Res :: term() | {'badrpc', Reason :: term()}], BadNodes :: [node()]; (Module, Function, Args, Timeout) -> {ResL, BadNodes} when Module :: module(), Function :: atom(), Args :: [term()], Timeout :: timeout(), - ResL :: [term()], + ResL :: [Res :: term() | {'badrpc', Reason :: term()}], BadNodes :: [node()]. multicall(Nodes, M, F, A) when is_list(Nodes) -> @@ -531,7 +531,7 @@ multicall(M, F, A, Timeout) -> Function :: atom(), Args :: [term()], Timeout :: timeout(), - ResL :: [term()], + ResL :: [Res :: term() | {'badrpc', Reason :: term()}], BadNodes :: [node()]. multicall(Nodes, M, F, A, infinity) -- cgit v1.2.3 From 75b0f73f72e1783d4ace976cdd2b5f23bdc3ebae Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 28 Feb 2018 10:04:53 +0100 Subject: kernel: Fix handling of os:cmd option max_size in win --- lib/kernel/src/os.erl | 8 ++++---- lib/kernel/test/os_SUITE.erl | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl index ae188ae932..a4b4f798f9 100644 --- a/lib/kernel/src/os.erl +++ b/lib/kernel/src/os.erl @@ -319,16 +319,16 @@ get_data(Port, MonRef, Eot, Sofar, Size, Max) -> iolist_to_binary(Sofar) end. -eot(_Bs, <<>>, _Size, _Max) -> +eot(Bs, <<>>, Size, Max) when Size + byte_size(Bs) < Max -> more; +eot(Bs, <<>>, Size, Max) -> + binary:part(Bs, {0, Max - Size}); eot(Bs, Eot, Size, Max) -> case binary:match(Bs, Eot) of - nomatch when Size + byte_size(Bs) < Max -> - more; {Pos, _} when Size + Pos < Max -> binary:part(Bs,{0, Pos}); _ -> - binary:part(Bs,{0, Max - Size}) + eot(Bs, <<>>, Size, Max) end. %% When port_close returns we know that all the diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl index 079b9790f6..a0bcde68db 100644 --- a/lib/kernel/test/os_SUITE.erl +++ b/lib/kernel/test/os_SUITE.erl @@ -324,8 +324,8 @@ max_size_command(_Config) -> Res32768 = os:cmd("cat /dev/zero", #{ max_size => 32768 }), 32768 = length(Res32768), - ResHello = os:cmd("echo hello", #{ max_size => 20 }), - 6 = length(ResHello). + ResHello = string:trim(os:cmd("echo hello", #{ max_size => 20 })), + 5 = length(ResHello). %% Test that the os:perf_counter api works as expected perf_counter_api(_Config) -> -- cgit v1.2.3 From 9a472f2899519234198f677269c5e70362bec351 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 23 Feb 2018 18:19:36 +0100 Subject: kernel: Remove redundant code No need to clear them as they cannot be set. --- lib/kernel/src/dist_util.erl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index 5ea0ca991f..88718ee531 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -184,9 +184,7 @@ make_this_flags(RequestType, AddFlags, RemoveFlags, OtherNode) -> Flgs0 = ?DFLAGS_THIS_DEFAULT, Flgs1 = Flgs0 bor publish_flag(RequestType, OtherNode), Flgs2 = Flgs1 bor AddFlags, - Flgs3 = Flgs2 band (bnot (?DFLAG_HIDDEN_ATOM_CACHE - bor ?DFLAG_ATOM_CACHE)), - Flgs3 band (bnot RemoveFlags). + Flgs2 band (bnot RemoveFlags). handshake_other_started(#hs_data{request_type=ReqType, add_flags=AddFlgs0, -- cgit v1.2.3 From 337919abe6ed9a97e15d5f471ef8f79d44cb8363 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 26 Feb 2018 15:36:34 +0100 Subject: erts,kernel: Add erts_internal:get_dflags/0 for kernel to ask erts about distribution flags and keep this info in one place. --- lib/kernel/src/dist_util.erl | 69 ++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 44 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index 88718ee531..638b91e153 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -143,48 +143,29 @@ publish_flag(_, OtherNode) -> 0 end. --define(DFLAGS_REMOVABLE, - (?DFLAG_DIST_HDR_ATOM_CACHE - bor ?DFLAG_HIDDEN_ATOM_CACHE - bor ?DFLAG_ATOM_CACHE)). - --define(DFLAGS_ADDABLE, - (?DFLAGS_ALL - band (bnot (?DFLAG_PUBLISHED - bor ?DFLAG_HIDDEN_ATOM_CACHE - bor ?DFLAG_ATOM_CACHE)))). - --define(DFLAGS_THIS_DEFAULT, - (?DFLAG_EXPORT_PTR_TAG - bor ?DFLAG_EXTENDED_PIDS_PORTS - bor ?DFLAG_EXTENDED_REFERENCES - bor ?DFLAG_DIST_MONITOR - bor ?DFLAG_FUN_TAGS - bor ?DFLAG_DIST_MONITOR_NAME - bor ?DFLAG_NEW_FUN_TAGS - bor ?DFLAG_BIT_BINARIES - bor ?DFLAG_NEW_FLOATS - bor ?DFLAG_UNICODE_IO - bor ?DFLAG_DIST_HDR_ATOM_CACHE - bor ?DFLAG_SMALL_ATOM_TAGS - bor ?DFLAG_UTF8_ATOMS - bor ?DFLAG_MAP_TAG - bor ?DFLAG_BIG_CREATION - bor ?DFLAG_SEND_SENDER)). - -make_this_flags(RequestType, AddFlags, RemoveFlags, OtherNode) -> - case RemoveFlags band (bnot ?DFLAGS_REMOVABLE) of + +%% Sync with dist.c +-record(erts_dflags, { + default, % flags erts prefers + mandatory, % flags erts needs + addable, % flags local dist implementation is allowed to add + rejectable % flags local dist implementation is allowed to reject +}). + +make_this_flags(RequestType, AddFlags, RejectFlags, OtherNode, + #erts_dflags{}=EDF) -> + case RejectFlags band (bnot EDF#erts_dflags.rejectable) of 0 -> ok; Rerror -> exit({"Rejecting non rejectable flags", Rerror}) end, - case AddFlags band (bnot ?DFLAGS_ADDABLE) of + case AddFlags band (bnot EDF#erts_dflags.addable) of 0 -> ok; Aerror -> exit({"Adding non addable flags", Aerror}) end, - Flgs0 = ?DFLAGS_THIS_DEFAULT, + Flgs0 = EDF#erts_dflags.default, Flgs1 = Flgs0 bor publish_flag(RequestType, OtherNode), Flgs2 = Flgs1 bor AddFlags, - Flgs2 band (bnot RemoveFlags). + Flgs2 band (bnot RejectFlags). handshake_other_started(#hs_data{request_type=ReqType, add_flags=AddFlgs0, @@ -194,7 +175,8 @@ handshake_other_started(#hs_data{request_type=ReqType, RejFlgs = convert_flags(RejFlgs0), ReqFlgs = convert_flags(ReqFlgs0), {PreOtherFlags,Node,Version} = recv_name(HSData0), - PreThisFlags = make_this_flags(ReqType, AddFlgs, RejFlgs, Node), + EDF = erts_internal:get_dflags(), + PreThisFlags = make_this_flags(ReqType, AddFlgs, RejFlgs, Node, EDF), {ThisFlags, OtherFlags} = adjust_flags(PreThisFlags, PreOtherFlags, RejFlgs), @@ -206,7 +188,7 @@ handshake_other_started(#hs_data{request_type=ReqType, add_flags=AddFlgs, reject_flags=RejFlgs, require_flags=ReqFlgs}, - check_dflags(HSData), + check_dflags(HSData, EDF), is_allowed(HSData), ?debug({"MD5 connection from ~p (V~p)~n", [Node, HSData#hs_data.other_version]}), @@ -245,12 +227,10 @@ is_allowed(#hs_data{other_node = Node, check_dflags(#hs_data{other_node = Node, other_flags = OtherFlags, other_started = OtherStarted, - require_flags = RequiredFlags} = HSData) -> - Mandatory = ((?DFLAG_EXTENDED_REFERENCES - bor ?DFLAG_EXTENDED_PIDS_PORTS - bor ?DFLAG_UTF8_ATOMS - bor ?DFLAG_NEW_FUN_TAGS) - bor RequiredFlags), + require_flags = RequiredFlags} = HSData, + #erts_dflags{}=EDF) -> + + Mandatory = (EDF#erts_dflags.mandatory bor RequiredFlags), Missing = check_mandatory(0, ?DFLAGS_ALL, Mandatory, OtherFlags, []), case Missing of @@ -408,7 +388,8 @@ handshake_we_started(#hs_data{request_type=ReqType, AddFlgs = convert_flags(AddFlgs0), RejFlgs = convert_flags(RejFlgs0), ReqFlgs = convert_flags(ReqFlgs0), - PreThisFlags = make_this_flags(ReqType, AddFlgs, RejFlgs, Node), + EDF = erts_internal:get_dflags(), + PreThisFlags = make_this_flags(ReqType, AddFlgs, RejFlgs, Node, EDF), HSData = PreHSData#hs_data{this_flags = PreThisFlags, add_flags = AddFlgs, reject_flags = RejFlgs, @@ -422,7 +403,7 @@ handshake_we_started(#hs_data{request_type=ReqType, NewHSData = HSData#hs_data{this_flags = ThisFlags, other_flags = OtherFlags, other_started = false}, - check_dflags(NewHSData), + check_dflags(NewHSData, EDF), MyChallenge = gen_challenge(), {MyCookie,HisCookie} = get_cookies(Node), send_challenge_reply(NewHSData,MyChallenge, -- cgit v1.2.3 From 7917c31757994f4f2fd344d0529de8bb193adc85 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 26 Feb 2018 15:54:44 +0100 Subject: kernel: Simplify dist flags handshake merging to a simple 'band' operation into ChosenFlags and no need to remember old 'this_flags' and 'other_flags'. --- lib/kernel/src/dist_util.erl | 34 ++++++++-------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index 638b91e153..5871e7e8ee 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -116,22 +116,8 @@ dflag2str(_) -> "UNKNOWN". -remove_flag(Flag, Flags) -> - case Flags band Flag of - 0 -> - Flags; - _ -> - Flags - Flag - end. - -adjust_flags(ThisFlags, OtherFlags, RejectFlags) -> - case (?DFLAG_PUBLISHED band ThisFlags) band OtherFlags of - 0 -> - {remove_flag(?DFLAG_PUBLISHED, ThisFlags), - remove_flag(?DFLAG_PUBLISHED, OtherFlags)}; - _ -> - {ThisFlags, OtherFlags band (bnot RejectFlags)} - end. +adjust_flags(ThisFlags, OtherFlags) -> + ThisFlags band OtherFlags. publish_flag(hidden, _) -> 0; @@ -177,11 +163,9 @@ handshake_other_started(#hs_data{request_type=ReqType, {PreOtherFlags,Node,Version} = recv_name(HSData0), EDF = erts_internal:get_dflags(), PreThisFlags = make_this_flags(ReqType, AddFlgs, RejFlgs, Node, EDF), - {ThisFlags, OtherFlags} = adjust_flags(PreThisFlags, - PreOtherFlags, - RejFlgs), - HSData = HSData0#hs_data{this_flags=ThisFlags, - other_flags=OtherFlags, + ChosenFlags = adjust_flags(PreThisFlags, PreOtherFlags), + HSData = HSData0#hs_data{this_flags=ChosenFlags, + other_flags=ChosenFlags, other_version=Version, other_node=Node, other_started=true, @@ -397,11 +381,9 @@ handshake_we_started(#hs_data{request_type=ReqType, send_name(HSData), recv_status(HSData), {PreOtherFlags,ChallengeA} = recv_challenge(HSData), - {ThisFlags,OtherFlags} = adjust_flags(PreThisFlags, - PreOtherFlags, - RejFlgs), - NewHSData = HSData#hs_data{this_flags = ThisFlags, - other_flags = OtherFlags, + ChosenFlags = adjust_flags(PreThisFlags, PreOtherFlags), + NewHSData = HSData#hs_data{this_flags = ChosenFlags, + other_flags = ChosenFlags, other_started = false}, check_dflags(NewHSData, EDF), MyChallenge = gen_challenge(), -- cgit v1.2.3 From 2709c93ede0be977ac13543d0fabe8ff3c27f4cc Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 26 Feb 2018 16:59:39 +0100 Subject: kernel: Remove DFLAGS_ALL from dist.hrl and remove use in dist_util:check_mandatory --- lib/kernel/include/dist.hrl | 21 --------------------- lib/kernel/src/dist_util.erl | 20 +++++++++----------- 2 files changed, 9 insertions(+), 32 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/include/dist.hrl b/lib/kernel/include/dist.hrl index db4a5eaebc..986aca6c26 100644 --- a/lib/kernel/include/dist.hrl +++ b/lib/kernel/include/dist.hrl @@ -49,24 +49,3 @@ %% Also update dflag2str() in ../src/dist_util.erl %% when adding flags... - --define(DFLAGS_ALL, - (?DFLAG_PUBLISHED - bor ?DFLAG_ATOM_CACHE - bor ?DFLAG_EXTENDED_REFERENCES - bor ?DFLAG_DIST_MONITOR - bor ?DFLAG_FUN_TAGS - bor ?DFLAG_DIST_MONITOR_NAME - bor ?DFLAG_HIDDEN_ATOM_CACHE - bor ?DFLAG_NEW_FUN_TAGS - bor ?DFLAG_EXTENDED_PIDS_PORTS - bor ?DFLAG_EXPORT_PTR_TAG - bor ?DFLAG_BIT_BINARIES - bor ?DFLAG_NEW_FLOATS - bor ?DFLAG_UNICODE_IO - bor ?DFLAG_DIST_HDR_ATOM_CACHE - bor ?DFLAG_SMALL_ATOM_TAGS - bor ?DFLAG_UTF8_ATOMS - bor ?DFLAG_MAP_TAG - bor ?DFLAG_BIG_CREATION - bor ?DFLAG_SEND_SENDER)). diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index 5871e7e8ee..5614b80802 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -215,8 +215,7 @@ check_dflags(#hs_data{other_node = Node, #erts_dflags{}=EDF) -> Mandatory = (EDF#erts_dflags.mandatory bor RequiredFlags), - Missing = check_mandatory(0, ?DFLAGS_ALL, Mandatory, - OtherFlags, []), + Missing = check_mandatory(Mandatory, OtherFlags, []), case Missing of [] -> ok; @@ -236,21 +235,20 @@ check_dflags(#hs_data{other_node = Node, ?shutdown2(Node, {check_dflags_failed, Missing}) end. -check_mandatory(_Bit, 0, _Mandatory, _OtherFlags, Missing) -> +check_mandatory(0, _OtherFlags, Missing) -> Missing; -check_mandatory(Bit, Left, Mandatory, OtherFlags, Missing) -> - DFlag = (1 bsl Bit), - NewLeft = Left band (bnot DFlag), - NewMissing = case {DFlag band Mandatory, - DFlag band OtherFlags} of - {DFlag, 0} -> +check_mandatory(Mandatory, OtherFlags, Missing) -> + Left = Mandatory band (Mandatory - 1), % clear lowest set bit + DFlag = Mandatory bxor Left, % only lowest set bit + NewMissing = case DFlag band OtherFlags of + 0 -> %% Mandatory and missing... [dflag2str(DFlag) | Missing]; _ -> - %% Not mandatory or present... + %% Mandatory and present... Missing end, - check_mandatory(Bit+1, NewLeft, Mandatory, OtherFlags, NewMissing). + check_mandatory(Left, OtherFlags, NewMissing). %% No nodedown will be sent if we fail before this process has -- cgit v1.2.3 From 7fb3ed7d2731050186eb5224fe8e6050e4909341 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 26 Feb 2018 20:40:51 +0100 Subject: erts,kernel: Add dist_util:strict_order_flags/0 to replace DFLAGS_STRICT_ORDER_DELIVERY and remove that compile time dependency. --- lib/kernel/include/dist.hrl | 5 ----- lib/kernel/src/dist_util.erl | 9 ++++++++- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/include/dist.hrl b/lib/kernel/include/dist.hrl index 986aca6c26..b7c35712a6 100644 --- a/lib/kernel/include/dist.hrl +++ b/lib/kernel/include/dist.hrl @@ -42,10 +42,5 @@ -define(DFLAG_BIG_CREATION, 16#40000). -define(DFLAG_SEND_SENDER, 16#80000). -%% DFLAGs that require strict ordering or:ed together... --define(DFLAGS_STRICT_ORDER_DELIVERY, - ?DFLAG_DIST_HDR_ATOM_CACHE). - - %% Also update dflag2str() in ../src/dist_util.erl %% when adding flags... diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index 5614b80802..3927b64b06 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -27,6 +27,7 @@ %%-compile(export_all). -export([handshake_we_started/1, handshake_other_started/1, + strict_order_flags/0, start_timer/1, setup_timer/2, reset_timer/1, cancel_timer/1, shutdown/3, shutdown/4]). @@ -135,9 +136,15 @@ publish_flag(_, OtherNode) -> default, % flags erts prefers mandatory, % flags erts needs addable, % flags local dist implementation is allowed to add - rejectable % flags local dist implementation is allowed to reject + rejectable, % flags local dist implementation is allowed to reject + strict_order % flags for features needing strict order delivery }). +-spec strict_order_flags() -> integer(). +strict_order_flags() -> + EDF = erts_internal:get_dflags(), + EDF#erts_dflags.strict_order. + make_this_flags(RequestType, AddFlags, RejectFlags, OtherNode, #erts_dflags{}=EDF) -> case RejectFlags band (bnot EDF#erts_dflags.rejectable) of -- cgit v1.2.3 From 3e580e039a8eb2467c638503bc526116fe829fec Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 2 Mar 2018 16:56:13 +0100 Subject: kernel,stdlib: Remove obsolete use of send with 'noconnect' as workarounds to avoid blocking auto-connect, which is now asynchronous in OTP-21. Also changed old catch to more efficient try-catch. --- lib/kernel/src/net_kernel.erl | 8 ++++---- lib/kernel/src/rpc.erl | 5 +---- 2 files changed, 5 insertions(+), 8 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index cdb10a7b12..f38989d103 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -1777,16 +1777,16 @@ async_reply({reply, Msg, State}, From) -> async_gen_server_reply(From, Msg) -> {Pid, Tag} = From, M = {Tag, Msg}, - case catch erlang:send(Pid, M, [nosuspend, noconnect]) of + try erlang:send(Pid, M, [nosuspend, noconnect]) of ok -> ok; nosuspend -> _ = spawn(fun() -> catch erlang:send(Pid, M, [noconnect]) end), ok; noconnect -> - ok; % The gen module takes care of this case. - {'EXIT', _} -> - ok + ok % The gen module takes care of this case. + catch + _:_ -> ok end. call_owner(Owner, Msg) -> diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl index 0e0b7dffa3..917e725fd6 100644 --- a/lib/kernel/src/rpc.erl +++ b/lib/kernel/src/rpc.erl @@ -418,10 +418,7 @@ abcast(Name, Mess) -> abcast([Node|Tail], Name, Mess) -> Dest = {Name,Node}, - case catch erlang:send(Dest, Mess, [noconnect]) of - noconnect -> spawn(erlang, send, [Dest,Mess]), ok; - _ -> ok - end, + try erlang:send(Dest, Mess) catch error:_ -> ok end, abcast(Tail, Name, Mess); abcast([], _,_) -> abcast. -- cgit v1.2.3 From 3e0cecc541fc9cd85b4c5878e58a7ec4a3142368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Muska=C5=82a?= Date: Wed, 7 Mar 2018 10:56:23 +0100 Subject: Short-circuit code:ensure_loaded for already-loaded modules This checks if the module is already loaded using erlang:module_loaded before calling the code server. This should improve performance of the call significantly since the case where module is already loaded is the common one. The change shouldn't cause any problems. It's worth noting that code:ensure_modules_loaded already does a similar check. --- lib/kernel/src/code.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index 9969021a6c..f143a49d2f 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -149,8 +149,11 @@ load_file(Mod) when is_atom(Mod) -> -spec ensure_loaded(Module) -> {module, Module} | {error, What} when Module :: module(), What :: embedded | badfile | nofile | on_load_failure. -ensure_loaded(Mod) when is_atom(Mod) -> - call({ensure_loaded,Mod}). +ensure_loaded(Mod) when is_atom(Mod) -> + case erlang:module_loaded(Mod) of + true -> {module, Mod}; + false -> call({ensure_loaded,Mod}) + end. %% XXX File as an atom is allowed only for backwards compatibility. -spec load_abs(Filename) -> load_ret() when -- cgit v1.2.3 From db5a9ef5e49f55338369f088bbef1d492f8c3c0c Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 9 Mar 2018 10:59:22 +0100 Subject: Update version numbers --- lib/kernel/vsn.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 91261e1d55..60a1b0bff8 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 5.4.2 +KERNEL_VSN = 5.4.3 -- cgit v1.2.3 From 7c7ba7191a3932e737fa81fd0166df1c637d4b31 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 9 Mar 2018 11:01:07 +0100 Subject: Update release notes --- lib/kernel/doc/src/notes.xml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 65fe9b9c07..09844f1502 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,34 @@

    This document describes the changes made to the Kernel application.

    +
    Kernel 5.4.3 + +
    Fixed Bugs and Malfunctions + + +

    Correct a few contracts.

    +

    + Own Id: OTP-14889

    +
    + +

    + Reject loading modules with names containing directory + separators ('/' or '\' on Windows).

    +

    + Own Id: OTP-14933 Aux Id: ERL-564, PR-1716

    +
    + +

    + Fix bug in handling of os:cmd/2 option max_size on + windows.

    +

    + Own Id: OTP-14940

    +
    +
    +
    + +
    +
    Kernel 5.4.2
    Fixed Bugs and Malfunctions -- cgit v1.2.3 From 33d689571257102a64eb242805fd49efda32d8d5 Mon Sep 17 00:00:00 2001 From: Pierre Fenoll Date: Fri, 16 Mar 2018 09:23:57 +0100 Subject: more-typos: s%follwing%following%g --- lib/kernel/src/disk_log_1.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/disk_log_1.erl b/lib/kernel/src/disk_log_1.erl index 93856aa7b3..b456b53d20 100644 --- a/lib/kernel/src/disk_log_1.erl +++ b/lib/kernel/src/disk_log_1.erl @@ -630,7 +630,7 @@ is_head(Bin) when is_binary(Bin) -> %% Writes MaxB bytes on each file. %% Creates a file called Name.idx in the Dir. This %% file contains the last written FileName as one byte, and -%% follwing that, the sizes of each file (size 0 number of items). +%% following that, the sizes of each file (size 0 number of items). %% On startup, this file is read, and the next available %% filename is used as first log file. %% Reports can be browsed with Report Browser Tool (rb), or -- cgit v1.2.3 From f14a5306622994a6b49b25c63ec882a1551398ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Mon, 19 Mar 2018 13:18:08 +0100 Subject: Fix file:change_group/change_owner It wasn't possible to change group/owner separately, and our test suite lacked coverage for that. ERL-589 --- lib/kernel/test/file_SUITE.erl | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 9a77454432..eea9e43dd3 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -56,7 +56,8 @@ open1/1, old_modes/1, new_modes/1, path_open/1, open_errors/1]). -export([ file_info_basic_file/1, file_info_basic_directory/1, - file_info_bad/1, file_info_times/1, file_write_file_info/1]). + file_info_bad/1, file_info_times/1, file_write_file_info/1, + file_wfi_helpers/1]). -export([rename/1, access/1, truncate/1, datasync/1, sync/1, read_write/1, pread_write/1, append/1, exclusive/1]). -export([ e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]). @@ -152,7 +153,8 @@ groups() -> {pos, [], [pos1, pos2, pos3]}, {file_info, [], [file_info_basic_file, file_info_basic_directory, - file_info_bad, file_info_times, file_write_file_info]}, + file_info_bad, file_info_times, file_write_file_info, + file_wfi_helpers]}, {consult, [], [consult1, path_consult]}, {eval, [], [eval1, path_eval]}, {script, [], [script1, path_script]}, @@ -1608,6 +1610,39 @@ file_write_file_info(Config) when is_list(Config) -> [] = flush(), ok. +file_wfi_helpers(Config) when is_list(Config) -> + RootDir = get_good_directory(Config), + io:format("RootDir = ~p", [RootDir]), + + Name = filename:join(RootDir, + atom_to_list(?MODULE) ++ "_wfi_helpers"), + + ok = ?FILE_MODULE:write_file(Name, "hello again"), + NewTime = {{1997, 02, 15}, {13, 18, 20}}, + ok = ?FILE_MODULE:change_time(Name, NewTime, NewTime), + + {ok, #file_info{atime=NewActAtime, mtime=NewTime}} = + ?FILE_MODULE:read_file_info(Name), + + NewFilteredAtime = filter_atime(NewTime, Config), + NewFilteredAtime = filter_atime(NewActAtime, Config), + + %% Make the file unwritable + ok = ?FILE_MODULE:change_mode(Name, 8#400), + {error, eacces} = ?FILE_MODULE:write_file(Name, "hello again"), + + %% ... and writable again + ok = ?FILE_MODULE:change_mode(Name, 8#600), + ok = ?FILE_MODULE:write_file(Name, "hello again"), + + %% We have no idea which users will work, so all we can do is to check + %% that it returns enoent instead of crashing. + {error, enoent} = ?FILE_MODULE:change_group("bogus file name", 0), + {error, enoent} = ?FILE_MODULE:change_owner("bogus file name", 0), + + [] = flush(), + ok. + %% Returns a directory on a file system that has correct file times. get_good_directory(Config) -> -- cgit v1.2.3 From 4bc282d812cc2c49aa3e2d073e96c720f16aa270 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 7 Mar 2018 01:17:21 +0100 Subject: Implementation of true asynchronous signaling between processes Communication between Erlang processes has conceptually always been performed through asynchronous signaling. The runtime system implementation has however previously preformed most operation synchronously. In a system with only one true thread of execution, this is not problematic (often the opposite). In a system with multiple threads of execution (as current runtime system implementation with SMP support) it becomes problematic. This since it often involves locking of structures when updating them which in turn cause resource contention. Utilizing true asynchronous communication often avoids these resource contention issues. The case that triggered this change was contention on the link lock due to frequent updates of the monitor trees during communication with a frequently used server. The signal order delivery guarantees of the language makes it hard to change the implementation of only some signals to use true asynchronous signaling. Therefore the implementations of (almost) all signals have been changed. Currently the following signals have been implemented as true asynchronous signals: - Message signals - Exit signals - Monitor signals - Demonitor signals - Monitor triggered signals (DOWN, CHANGE, etc) - Link signals - Unlink signals - Group leader signals All of the above already defined as asynchronous signals in the language. The implementation of messages signals was quite asynchronous to begin with, but had quite strict delivery constraints due to the ordering guarantees of signals between a pair of processes. The previously used message queue partitioned into two halves has been replaced by a more general signal queue partitioned into three parts that service all kinds of signals. More details regarding the signal queue can be found in comments in the erl_proc_sig_queue.h file. The monitor and link implementations have also been completely replaced in order to fit the new asynchronous signaling implementation as good as possible. More details regarding the new monitor and link implementations can be found in the erl_monitor_link.h file. --- lib/kernel/src/erts_debug.erl | 17 +------- lib/kernel/src/net_kernel.erl | 8 ++-- lib/kernel/test/erl_distribution_SUITE.erl | 15 ++++--- lib/kernel/test/init_SUITE.erl | 67 ++++++++++++++++++------------ 4 files changed, 53 insertions(+), 54 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl index 3456c8511e..6f248626ca 100644 --- a/lib/kernel/src/erts_debug.erl +++ b/lib/kernel/src/erts_debug.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-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. @@ -32,8 +32,7 @@ %%% BIFs -export([breakpoint/2, disassemble/1, display/1, dist_ext_to_term/2, - dump_monitors/1, dump_links/1, flat_size/1, - get_internal_state/1, instructions/0, + flat_size/1, get_internal_state/1, instructions/0, map_info/1, same/2, set_internal_state/2, size_shared/1, copy_shared/1, dirty_cpu/2, dirty_io/2, dirty/3, lcnt_control/1, lcnt_control/2, lcnt_collect/0, lcnt_clear/0]). @@ -70,18 +69,6 @@ display(_) -> dist_ext_to_term(_, _) -> erlang:nif_error(undef). --spec dump_monitors(Id) -> true when - Id :: pid() | atom(). - -dump_monitors(_) -> - erlang:nif_error(undef). - --spec dump_links(Id) -> true when - Id :: pid() | port() | atom(). - -dump_links(_) -> - erlang:nif_error(undef). - -spec flat_size(Term) -> non_neg_integer() when Term :: term(). diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index f38989d103..669adefdf8 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. @@ -222,8 +222,7 @@ get_net_ticktime() -> Error :: error | {error, term()}. monitor_nodes(Flag) -> case catch process_flag(monitor_nodes, Flag) of - true -> ok; - false -> ok; + N when is_integer(N) -> ok; _ -> mk_monitor_nodes_error(Flag, []) end. @@ -236,8 +235,7 @@ monitor_nodes(Flag) -> Error :: error | {error, term()}. monitor_nodes(Flag, Opts) -> case catch process_flag({monitor_nodes, Opts}, Flag) of - true -> ok; - false -> ok; + N when is_integer(N) -> ok; _ -> mk_monitor_nodes_error(Flag, Opts) end. diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl index f6791adf86..0470f09f29 100644 --- a/lib/kernel/test/erl_distribution_SUITE.erl +++ b/lib/kernel/test/erl_distribution_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2017. All Rights Reserved. +%% Copyright Ericsson AB 1997-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. @@ -1144,17 +1144,16 @@ monitor_nodes_otp_6481_test(Config, TestType) when is_list(Config) -> TestMonNodeState = monitor_node_state(), %% io:format("~p~n", [TestMonNodeState]), TestMonNodeState = - MonNodeState + case TestType of + nodedown -> []; + nodeup -> [{self(), []}] + end + ++ lists:map(fun (_) -> {MN, []} end, Seq) ++ case TestType of nodedown -> [{self(), []}]; nodeup -> [] end - ++ lists:map(fun (_) -> {MN, []} end, Seq) - ++ case TestType of - nodedown -> []; - nodeup -> [{self(), []}] - end, - + ++ MonNodeState, {ok, Node} = start_node(Name, "", this), receive {nodeup, Node} -> ok end, diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl index 2b59eb2bfe..c8415b34e5 100644 --- a/lib/kernel/test/init_SUITE.erl +++ b/lib/kernel/test/init_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. @@ -365,7 +365,9 @@ restart(Config) when is_list(Config) -> %% Ok, the node is up, now the real test test begins. erlang:monitor_node(Node, true), SysProcs0 = rpc:call(Node, ?MODULE, find_system_processes, []), - [InitPid, PurgerPid, LitCollectorPid, DirtyCodePid] = SysProcs0, + io:format("SysProcs0=~p~n", [SysProcs0]), + [InitPid, PurgerPid, LitCollectorPid, + DirtySigNPid, DirtySigHPid, DirtySigMPid] = SysProcs0, InitPid = rpc:call(Node, erlang, whereis, [init]), PurgerPid = rpc:call(Node, erlang, whereis, [erts_code_purger]), Procs = rpc:call(Node, erlang, processes, []), @@ -381,7 +383,9 @@ restart(Config) when is_list(Config) -> ok = wait_restart(30, Node), SysProcs1 = rpc:call(Node, ?MODULE, find_system_processes, []), - [InitPid1, PurgerPid1, LitCollectorPid1, DirtyCodePid1] = SysProcs1, + io:format("SysProcs1=~p~n", [SysProcs1]), + [InitPid1, PurgerPid1, LitCollectorPid1, + DirtySigNPid1, DirtySigHPid1, DirtySigMPid1] = SysProcs1, %% Still the same init process! InitPid1 = rpc:call(Node, erlang, whereis, [init]), @@ -394,20 +398,18 @@ restart(Config) when is_list(Config) -> PurgerP = pid_to_list(PurgerPid1), %% and same literal area collector process! - case LitCollectorPid of - undefined -> undefined = LitCollectorPid1; - _ -> - LitCollectorP = pid_to_list(LitCollectorPid), - LitCollectorP = pid_to_list(LitCollectorPid1) - end, - - %% and same dirty process code checker process! - case DirtyCodePid of - undefined -> undefined = DirtyCodePid1; - _ -> - DirtyCodeP = pid_to_list(DirtyCodePid), - DirtyCodeP = pid_to_list(DirtyCodePid1) - end, + LitCollectorP = pid_to_list(LitCollectorPid), + LitCollectorP = pid_to_list(LitCollectorPid1), + + %% and same normal dirty signal handler process! + DirtySigNP = pid_to_list(DirtySigNPid), + DirtySigNP = pid_to_list(DirtySigNPid1), + %% and same high dirty signal handler process! + DirtySigHP = pid_to_list(DirtySigHPid), + DirtySigHP = pid_to_list(DirtySigHPid1), + %% and same max dirty signal handler process! + DirtySigMP = pid_to_list(DirtySigMPid), + DirtySigMP = pid_to_list(DirtySigMPid1), NewProcs0 = rpc:call(Node, erlang, processes, []), NewProcs = NewProcs0 -- SysProcs1, @@ -433,7 +435,9 @@ restart(Config) when is_list(Config) -> -record(sys_procs, {init, code_purger, literal_collector, - dirty_proc_checker}). + dirty_sig_handler_normal, + dirty_sig_handler_high, + dirty_sig_handler_max}). find_system_processes() -> find_system_procs(processes(), #sys_procs{}). @@ -442,21 +446,32 @@ find_system_procs([], SysProcs) -> [SysProcs#sys_procs.init, SysProcs#sys_procs.code_purger, SysProcs#sys_procs.literal_collector, - SysProcs#sys_procs.dirty_proc_checker]; + SysProcs#sys_procs.dirty_sig_handler_normal, + SysProcs#sys_procs.dirty_sig_handler_high, + SysProcs#sys_procs.dirty_sig_handler_max]; find_system_procs([P|Ps], SysProcs) -> - case process_info(P, initial_call) of - {initial_call,{otp_ring0,start,2}} -> + case process_info(P, [initial_call, priority]) of + [{initial_call,{otp_ring0,start,2}},_] -> undefined = SysProcs#sys_procs.init, find_system_procs(Ps, SysProcs#sys_procs{init = P}); - {initial_call,{erts_code_purger,start,0}} -> + [{initial_call,{erts_code_purger,start,0}},_] -> undefined = SysProcs#sys_procs.code_purger, find_system_procs(Ps, SysProcs#sys_procs{code_purger = P}); - {initial_call,{erts_literal_area_collector,start,0}} -> + [{initial_call,{erts_literal_area_collector,start,0}},_] -> undefined = SysProcs#sys_procs.literal_collector, find_system_procs(Ps, SysProcs#sys_procs{literal_collector = P}); - {initial_call,{erts_dirty_process_code_checker,start,0}} -> - undefined = SysProcs#sys_procs.dirty_proc_checker, - find_system_procs(Ps, SysProcs#sys_procs{dirty_proc_checker = P}); + [{initial_call,{erts_dirty_process_signal_handler,start,0}}, + {priority,normal}] -> + undefined = SysProcs#sys_procs.dirty_sig_handler_normal, + find_system_procs(Ps, SysProcs#sys_procs{dirty_sig_handler_normal = P}); + [{initial_call,{erts_dirty_process_signal_handler,start,0}}, + {priority,high}] -> + undefined = SysProcs#sys_procs.dirty_sig_handler_high, + find_system_procs(Ps, SysProcs#sys_procs{dirty_sig_handler_high = P}); + [{initial_call,{erts_dirty_process_signal_handler,start,0}}, + {priority,max}] -> + undefined = SysProcs#sys_procs.dirty_sig_handler_max, + find_system_procs(Ps, SysProcs#sys_procs{dirty_sig_handler_max = P}); _ -> find_system_procs(Ps, SysProcs) end. -- cgit v1.2.3 From cc62cd1d6840c0f4c02d70c4fba06827b8ef765b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 21 Mar 2018 19:45:43 +0100 Subject: kernel: Restore x-permission of test directories just to make it easier to do "rm -rf" --- lib/kernel/test/file_SUITE.erl | 6 +++--- lib/kernel/test/prim_file_SUITE.erl | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index eea9e43dd3..ff93f25e25 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -2212,7 +2212,7 @@ e_delete(Config) when is_list(Config) -> Base, #file_info {mode=0}), {error, eacces} = ?FILE_MODULE:delete(Afile), ?FILE_MODULE:write_file_info( - Base, #file_info {mode=8#600}) + Base, #file_info {mode=8#700}) end, [] = flush(), @@ -2343,7 +2343,7 @@ e_make_dir(Config) when is_list(Config) -> ?FILE_MODULE:write_file_info(Base, #file_info {mode=0}), {error, eacces} = ?FILE_MODULE:make_dir(filename:join(Base, "xxxx")), ?FILE_MODULE:write_file_info( - Base, #file_info {mode=8#600}) + Base, #file_info {mode=8#700}) end, ok. @@ -2389,7 +2389,7 @@ e_del_dir(Config) when is_list(Config) -> ok = ?FILE_MODULE:make_dir(ADirectory), ?FILE_MODULE:write_file_info( Base, #file_info {mode=0}), {error, eacces} = ?FILE_MODULE:del_dir(ADirectory), - ?FILE_MODULE:write_file_info( Base, #file_info {mode=8#600}) + ?FILE_MODULE:write_file_info( Base, #file_info {mode=8#700}) end, [] = flush(), ok. diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl index ab62f4dc34..5bb230d1c4 100644 --- a/lib/kernel/test/prim_file_SUITE.erl +++ b/lib/kernel/test/prim_file_SUITE.erl @@ -1306,7 +1306,7 @@ e_delete(Config) when is_list(Config) -> Base, #file_info {mode=0}), {error, eacces} = ?PRIM_FILE:delete(Afile), ?PRIM_FILE:write_file_info( - Base, #file_info {mode=8#600}) + Base, #file_info {mode=8#700}) end, ok. @@ -1442,7 +1442,7 @@ e_make_dir(Config) when is_list(Config) -> ?PRIM_FILE:write_file_info(Base, #file_info {mode=0}), {error, eacces} = ?PRIM_FILE:make_dir(filename:join(Base, "xxxx")), - ?PRIM_FILE:write_file_info(Base, #file_info {mode=8#600}) + ?PRIM_FILE:write_file_info(Base, #file_info {mode=8#700}) end, ok. @@ -1492,7 +1492,7 @@ e_del_dir(Config) when is_list(Config) -> ?PRIM_FILE:write_file_info(Base, #file_info {mode=0}), {error, eacces} = ?PRIM_FILE:del_dir(ADirectory), ?PRIM_FILE:write_file_info( - Base, #file_info {mode=8#600}) + Base, #file_info {mode=8#700}) end, ok. -- cgit v1.2.3 From 06ed628dfd013010dd6e182508c1137b9f4ba09b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Mon, 26 Feb 2018 12:49:57 +0100 Subject: Lift the type restrictions on seq_trace token labels OTP-14899 --- lib/kernel/doc/src/seq_trace.xml | 9 ++- lib/kernel/include/dist.hrl | 1 + lib/kernel/src/dist_util.erl | 2 + lib/kernel/src/seq_trace.erl | 6 +- lib/kernel/test/seq_trace_SUITE.erl | 109 ++++++++++++++++++++++++++++++++---- 5 files changed, 108 insertions(+), 19 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/seq_trace.xml b/lib/kernel/doc/src/seq_trace.xml index 197851021f..69eb12a8a0 100644 --- a/lib/kernel/doc/src/seq_trace.xml +++ b/lib/kernel/doc/src/seq_trace.xml @@ -80,13 +80,18 @@ seq_trace:set_token(OldToken), % activate the trace token again

    Sets the individual Component of the trace token to Val. Returns the previous value of the component.

    - set_token(label, Integer) + set_token(label, Label) -

    The label component is an integer which +

    The label component is a term which identifies all events belonging to the same sequential trace. If several sequential traces can be active simultaneously, label is used to identify the separate traces. Default is 0.

    + +

    Labels were restricted to small signed integers (28 bits) + prior to OTP 21. The trace token will be silenty dropped if it + crosses over to a node that does not support the label.

    +
    set_token(serial, SerialValue) diff --git a/lib/kernel/include/dist.hrl b/lib/kernel/include/dist.hrl index b7c35712a6..6baaa35d72 100644 --- a/lib/kernel/include/dist.hrl +++ b/lib/kernel/include/dist.hrl @@ -41,6 +41,7 @@ -define(DFLAG_MAP_TAG, 16#20000). -define(DFLAG_BIG_CREATION, 16#40000). -define(DFLAG_SEND_SENDER, 16#80000). +-define(DFLAG_BIG_SEQTRACE_LABELS, 16#100000). %% Also update dflag2str() in ../src/dist_util.erl %% when adding flags... diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index f7a84c14b4..781397e1ee 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -113,6 +113,8 @@ dflag2str(?DFLAG_BIG_CREATION) -> "BIG_CREATION"; dflag2str(?DFLAG_SEND_SENDER) -> "SEND_SENDER"; +dflag2str(?DFLAG_BIG_SEQTRACE_LABELS) -> + "BIG_SEQTRACE_LABELS"; dflag2str(_) -> "UNKNOWN". diff --git a/lib/kernel/src/seq_trace.erl b/lib/kernel/src/seq_trace.erl index cc0c10909b..8d7aba0f27 100644 --- a/lib/kernel/src/seq_trace.erl +++ b/lib/kernel/src/seq_trace.erl @@ -41,7 +41,7 @@ -type flag() :: 'send' | 'receive' | 'print' | 'timestamp' | 'monotonic_timestamp' | 'strict_monotonic_timestamp'. -type component() :: 'label' | 'serial' | flag(). --type value() :: (Integer :: non_neg_integer()) +-type value() :: (Label :: term()) | {Previous :: non_neg_integer(), Current :: non_neg_integer()} | (Bool :: boolean()). @@ -59,10 +59,6 @@ set_token({Flags,Label,Serial,_From,Lastcnt}) -> F = decode_flags(Flags), set_token2([{label,Label},{serial,{Lastcnt, Serial}} | F]). -%% We limit the label type to always be a small integer because erl_interface -%% expects that, the BIF can however "unofficially" handle atoms as well, and -%% atoms can be used if only Erlang nodes are involved - -spec set_token(Component, Val) -> {Component, OldVal} when Component :: component(), Val :: value(), diff --git a/lib/kernel/test/seq_trace_SUITE.erl b/lib/kernel/test/seq_trace_SUITE.erl index be23a1933f..aae8a83304 100644 --- a/lib/kernel/test/seq_trace_SUITE.erl +++ b/lib/kernel/test/seq_trace_SUITE.erl @@ -25,7 +25,7 @@ -export([token_set_get/1, tracer_set_get/1, print/1, send/1, distributed_send/1, recv/1, distributed_recv/1, trace_exit/1, distributed_exit/1, call/1, port/1, - match_set_seq_token/1, gc_seq_token/1]). + match_set_seq_token/1, gc_seq_token/1, label_capability_mismatch/1]). %% internal exports -export([simple_tracer/2, one_time_receiver/0, one_time_receiver/1, @@ -47,7 +47,7 @@ all() -> [token_set_get, tracer_set_get, print, send, distributed_send, recv, distributed_recv, trace_exit, distributed_exit, call, port, match_set_seq_token, - gc_seq_token]. + gc_seq_token, label_capability_mismatch]. groups() -> []. @@ -90,8 +90,8 @@ do_token_set_get(TsType) -> %% Test that initial seq_trace is disabled [] = seq_trace:get_token(), %% Test setting and reading the different fields - 0 = seq_trace:set_token(label,17), - {label,17} = seq_trace:get_token(label), + 0 = seq_trace:set_token(label,{my_label,1}), + {label,{my_label,1}} = seq_trace:get_token(label), false = seq_trace:set_token(print,true), {print,true} = seq_trace:get_token(print), false = seq_trace:set_token(send,true), @@ -101,12 +101,12 @@ do_token_set_get(TsType) -> false = seq_trace:set_token(TsType,true), {TsType,true} = seq_trace:get_token(TsType), %% Check the whole token - {Flags,17,0,Self,0} = seq_trace:get_token(), % all flags are set + {Flags,{my_label,1},0,Self,0} = seq_trace:get_token(), % all flags are set %% Test setting and reading the 'serial' field {0,0} = seq_trace:set_token(serial,{3,5}), {serial,{3,5}} = seq_trace:get_token(serial), %% Check the whole token, test that a whole token can be set and get - {Flags,17,5,Self,3} = seq_trace:get_token(), + {Flags,{my_label,1},5,Self,3} = seq_trace:get_token(), seq_trace:set_token({Flags,19,7,Self,5}), {Flags,19,7,Self,5} = seq_trace:get_token(), %% Check that receive timeout does not reset token @@ -166,11 +166,13 @@ do_send(TsType) -> seq_trace:reset_trace(), start_tracer(), Receiver = spawn(?MODULE,one_time_receiver,[]), + Label = make_ref(), + seq_trace:set_token(label,Label), set_token_flags([send, TsType]), Receiver ! send, Self = self(), seq_trace:reset_trace(), - [{0,{send,_,Self,Receiver,send}, Ts}] = stop_tracer(1), + [{Label,{send,_,Self,Receiver,send}, Ts}] = stop_tracer(1), check_ts(TsType, Ts). distributed_send(Config) when is_list(Config) -> @@ -184,14 +186,19 @@ do_distributed_send(TsType) -> seq_trace:reset_trace(), start_tracer(), Receiver = spawn(Node,?MODULE,one_time_receiver,[]), + + %% Make sure complex labels survive the trip. + Label = make_ref(), + seq_trace:set_token(label,Label), set_token_flags([send,TsType]), + Receiver ! send, Self = self(), seq_trace:reset_trace(), stop_node(Node), - [{0,{send,_,Self,Receiver,send}, Ts}] = stop_tracer(1), + [{Label,{send,_,Self,Receiver,send}, Ts}] = stop_tracer(1), check_ts(TsType, Ts). - + recv(Config) when is_list(Config) -> lists:foreach(fun do_recv/1, ?TIMESTAMP_MODES). @@ -220,7 +227,12 @@ do_distributed_recv(TsType) -> seq_trace:reset_trace(), rpc:call(Node,?MODULE,start_tracer,[]), Receiver = spawn(Node,?MODULE,one_time_receiver,[]), + + %% Make sure complex labels survive the trip. + Label = make_ref(), + seq_trace:set_token(label,Label), set_token_flags(['receive',TsType]), + Receiver ! 'receive', %% let the other process receive the message: receive after 1 -> ok end, @@ -229,7 +241,7 @@ do_distributed_recv(TsType) -> Result = rpc:call(Node,?MODULE,stop_tracer,[1]), stop_node(Node), ok = io:format("~p~n",[Result]), - [{0,{'receive',_,Self,Receiver,'receive'}, Ts}] = Result, + [{Label,{'receive',_,Self,Receiver,'receive'}, Ts}] = Result, check_ts(TsType, Ts). trace_exit(Config) when is_list(Config) -> @@ -240,7 +252,12 @@ do_trace_exit(TsType) -> start_tracer(), Receiver = spawn_link(?MODULE, one_time_receiver, [exit]), process_flag(trap_exit, true), + + %% Make sure complex labels survive the trip. + Label = make_ref(), + seq_trace:set_token(label,Label), set_token_flags([send, TsType]), + Receiver ! {before, exit}, %% let the other process receive the message: receive @@ -254,8 +271,8 @@ do_trace_exit(TsType) -> Result = stop_tracer(2), seq_trace:reset_trace(), ok = io:format("~p~n", [Result]), - [{0, {send, {0,1}, Self, Receiver, {before, exit}}, Ts0}, - {0, {send, {1,2}, Receiver, Self, + [{Label, {send, {0,1}, Self, Receiver, {before, exit}}, Ts0}, + {Label, {send, {1,2}, Receiver, Self, {'EXIT', Receiver, {exit, {before, exit}}}}, Ts1}] = Result, check_ts(TsType, Ts0), check_ts(TsType, Ts1). @@ -291,6 +308,74 @@ do_distributed_exit(TsType) -> {'EXIT', Receiver, {exit, {before, exit}}}}, Ts}] = Result, check_ts(TsType, Ts). +label_capability_mismatch(Config) when is_list(Config) -> + Releases = ["20_latest"], + Available = [Rel || Rel <- Releases, test_server:is_release_available(Rel)], + case Available of + [] -> {skipped, "No incompatible releases available"}; + _ -> + lists:foreach(fun do_incompatible_labels/1, Available), + lists:foreach(fun do_compatible_labels/1, Available), + ok + end. + +do_incompatible_labels(Rel) -> + Cookie = atom_to_list(erlang:get_cookie()), + {ok, Node} = test_server:start_node( + list_to_atom(atom_to_list(?MODULE)++"_"++Rel), peer, + [{args, " -setcookie "++Cookie}, {erl, [{release, Rel}]}]), + + {_,Dir} = code:is_loaded(?MODULE), + Mdir = filename:dirname(Dir), + true = rpc:call(Node,code,add_patha,[Mdir]), + seq_trace:reset_trace(), + rpc:call(Node,?MODULE,start_tracer,[]), + Receiver = spawn(Node,?MODULE,one_time_receiver,[]), + + %% This node does not support arbitrary labels, so it must fail with a + %% timeout as the token is dropped silently. + seq_trace:set_token(label,make_ref()), + seq_trace:set_token('receive',true), + + Receiver ! 'receive', + %% let the other process receive the message: + receive after 10 -> ok end, + seq_trace:reset_trace(), + + {error,timeout} = rpc:call(Node,?MODULE,stop_tracer,[1]), + stop_node(Node), + ok. + +do_compatible_labels(Rel) -> + Cookie = atom_to_list(erlang:get_cookie()), + {ok, Node} = test_server:start_node( + list_to_atom(atom_to_list(?MODULE)++"_"++Rel), peer, + [{args, " -setcookie "++Cookie}, {erl, [{release, Rel}]}]), + + {_,Dir} = code:is_loaded(?MODULE), + Mdir = filename:dirname(Dir), + true = rpc:call(Node,code,add_patha,[Mdir]), + seq_trace:reset_trace(), + rpc:call(Node,?MODULE,start_tracer,[]), + Receiver = spawn(Node,?MODULE,one_time_receiver,[]), + + %% This node does not support arbitrary labels, but small integers should + %% still work. + Label = 1234, + seq_trace:set_token(label,Label), + seq_trace:set_token('receive',true), + + Receiver ! 'receive', + %% let the other process receive the message: + receive after 10 -> ok end, + Self = self(), + seq_trace:reset_trace(), + Result = rpc:call(Node,?MODULE,stop_tracer,[1]), + stop_node(Node), + ok = io:format("~p~n",[Result]), + [{Label,{'receive',_,Self,Receiver,'receive'}, _}] = Result, + ok. + call(doc) -> "Tests special forms {is_seq_trace} and {get_seq_token} " "in trace match specs."; -- cgit v1.2.3 From e1aa6fab326bdb443f14a08727a33fa52cbb573a Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Fri, 23 Mar 2018 14:43:48 +0100 Subject: Update types for posix error codes I have read the man pages for most socket and file operations on recent Linux, FreeBSD, OpenBSD and Solaris 10 and noted the possible error codes. Which error codes that are possible for file operations have been updated in file:posix/0. Error codes for socket operations in inet:posix/0. The latter refers to the former so it is a superset, assuming that e.g sendfile and AF_UNIX socket operations could cause socket operations to return any file error code. That is not entirely true, but could be, especially in the future. Added to file:posix/0 are: ebadmsg edeadlk edeadlock eftype emultihop enobufs enolck enolink enosr enostr enosys eopnotsupp eoverflow erange etxtbsy Added to inet:posix/0 are all but: exbadport exbadseq file:posix() These are still possible according to erl_posix_str.c, but are not in file:posix/0 nor in inet:posix/0, and many of them are not file nor inet related, but some might be: e2big eadv ealign ebade ebadfd ebadr ebadrpc ebadrqc ebadslt ebfont echild echrng ecomm edirty edom edotdot eduppkg eidrm einit eisnam elbin el2hlt el2nsync el3hlt el3rst elibacc elibbad elibexec elibmax elibscn elnrng enavail enet enoano enocsi enodata enoexec enonet enosym enotempty enotnam enotuniq eproclim eprocunavail eprogmismatch eprogunavail erefused eremchg eremdev eremote eremoteio eremoterelease erpcmismatch erremote eshutdown esrmnt esuccesss etime etoomanyrefs euclean eunatch eusers eversion exfull sysnotready vernotsupported ediscon enomore ecancelled einvalidproctable einvalidprovider eproviderfailedinit syscallfailure service_not_found type_not_found e_no_more e_cancelled unknown --- lib/kernel/src/file.erl | 29 ++++++++++++++++++++--------- lib/kernel/src/inet.erl | 17 +++++++++++++++-- 2 files changed, 35 insertions(+), 11 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index 933f2d5f65..7a38df3c5e 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. @@ -101,14 +101,25 @@ -type deep_list() :: [char() | atom() | deep_list()]. -type name() :: string() | atom() | deep_list(). -type name_all() :: string() | atom() | deep_list() | (RawFilename :: binary()). --type posix() :: 'eacces' | 'eagain' | 'ebadf' | 'ebusy' | 'edquot' - | 'eexist' | 'efault' | 'efbig' | 'eintr' | 'einval' - | 'eio' | 'eisdir' | 'eloop' | 'emfile' | 'emlink' - | 'enametoolong' - | 'enfile' | 'enodev' | 'enoent' | 'enomem' | 'enospc' - | 'enotblk' | 'enotdir' | 'enotsup' | 'enxio' | 'eperm' - | 'epipe' | 'erofs' | 'espipe' | 'esrch' | 'estale' - | 'exdev'. +-type posix() :: + 'eacces' | 'eagain' | + 'ebadf' | 'ebadmsg' | 'ebusy' | + 'edeadlk' | 'edeadlock' | 'edquot' | + 'eexist' | + 'efault' | 'efbig' | 'eftype' | + 'eintr' | 'einval' | 'eio' | 'eisdir' | + 'eloop' | + 'emfile' | 'emlink' | 'emultihop' | + 'enametoolong' | 'enfile' | + 'enobufs' | 'enodev' | 'enolck' | 'enolink' | 'enoent' | + 'enomem' | 'enospc' | 'enosr' | 'enostr' | 'enosys' | + 'enotblk' | 'enotdir' | 'enotsup' | 'enxio' | + 'eopnotsupp' | 'eoverflow' | + 'eperm' | 'epipe' | + 'erange' | 'erofs' | + 'espipe' | 'esrch' | 'estale' | + 'etxtbsy' | + 'exdev'. -type date_time() :: calendar:datetime(). -type posix_file_advise() :: 'normal' | 'sequential' | 'random' | 'no_reuse' | 'will_need' | 'dont_need'. diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index dc20c21c77..d9ad56a71d 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2017. All Rights Reserved. +%% Copyright Ericsson AB 1997-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. @@ -104,7 +104,20 @@ {local, binary()} | {unspec, <<>>} | {undefined, any()}. --type posix() :: exbadport | exbadseq | file:posix(). +-type posix() :: + 'eaddrinuse' | 'eaddrnotavail' | 'eafnosupport' | 'ealready' | + 'econnaborted' | 'econnrefused' | 'econnreset' | + 'edestaddrreq' | + 'ehostdown' | 'ehostunreach' | + 'einprogress' | 'eisconn' | + 'emsgsize' | + 'enetdown' | 'enetunreach' | + 'enopkg' | 'enoprotoopt' | 'enotconn' | 'enotty' | 'enotsock' | + 'eproto' | 'eprotonosupport' | 'eprototype' | + 'esocktnosupport' | + 'etimedout' | + 'ewouldblock' | + 'exbadport' | 'exbadseq' | file:posix(). -type socket() :: port(). -type socket_setopt() :: -- cgit v1.2.3 From a91f4640bc4554f9a746afa65bacda0e1a60afc0 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 6 Apr 2018 16:40:56 +0200 Subject: erl_docgen: Add ghlink step for all non-generated doc xml files In order to get line numbers into the ghlink we have to add a post processing step for all xml files. --- lib/kernel/doc/src/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile index 0759f362d4..2413541082 100644 --- a/lib/kernel/doc/src/Makefile +++ b/lib/kernel/doc/src/Makefile @@ -137,16 +137,16 @@ clean clean_docs: rm -f errs core *~ $(SPECDIR)/specs_erl_prim_loader_stub.xml: - escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ + $(gen_verbose)escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ -o$(dir $@) -module erl_prim_loader_stub $(SPECDIR)/specs_erlang_stub.xml: - escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ + $(gen_verbose)escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ -o$(dir $@) -module erlang_stub $(SPECDIR)/specs_init_stub.xml: - escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ + $(gen_verbose)escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ -o$(dir $@) -module init_stub $(SPECDIR)/specs_zlib_stub.xml: - escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ + $(gen_verbose)escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ -o$(dir $@) -module zlib_stub # ---------------------------------------------------- -- cgit v1.2.3 From e5e2fd334b9fec2bedab9ee6987b96ca2aec1f87 Mon Sep 17 00:00:00 2001 From: Frank Hunleth Date: Fri, 16 Mar 2018 10:11:09 -0400 Subject: kernel: Update note about heart rebooting on NTP updates The timestamp code in heart uses monotonic time so it is immune to NTP changes. --- lib/kernel/doc/src/heart.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/heart.xml b/lib/kernel/doc/src/heart.xml index 5b5b71e521..46c7ce60b6 100644 --- a/lib/kernel/doc/src/heart.xml +++ b/lib/kernel/doc/src/heart.xml @@ -59,8 +59,9 @@
     % erl -heart -env HEART_BEAT_TIMEOUT 30 ...

    The value (in seconds) must be in the range 10 < X <= 65535.

    -

    Notice that if the system clock is adjusted with - more than HEART_BEAT_TIMEOUT seconds, heart +

    When running on OSs lacking support for monotonic time, + heart is susceptible to system clock adjustments of more than + HEART_BEAT_TIMEOUT seconds. When this happens, heart times out and tries to reboot the system. This can occur, for example, if the system clock is adjusted automatically by use of the Network Time Protocol (NTP).

    -- cgit v1.2.3 From a2d184760b3b21a55b663f15f1989a92d281c432 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 10 Apr 2018 15:21:08 +0200 Subject: Set infinity timeout for auth requests --- lib/kernel/src/auth.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/auth.erl b/lib/kernel/src/auth.erl index 40feee6bf0..a2116d8e8a 100644 --- a/lib/kernel/src/auth.erl +++ b/lib/kernel/src/auth.erl @@ -107,7 +107,7 @@ get_cookie() -> get_cookie(_Node) when node() =:= nonode@nohost -> nocookie; get_cookie(Node) -> - gen_server:call(auth, {get_cookie, Node}). + gen_server:call(auth, {get_cookie, Node}, infinity). -spec set_cookie(Cookie :: cookie()) -> 'true'. @@ -119,12 +119,12 @@ set_cookie(Cookie) -> set_cookie(_Node, _Cookie) when node() =:= nonode@nohost -> erlang:error(distribution_not_started); set_cookie(Node, Cookie) -> - gen_server:call(auth, {set_cookie, Node, Cookie}). + gen_server:call(auth, {set_cookie, Node, Cookie}, infinity). -spec sync_cookie() -> any(). sync_cookie() -> - gen_server:call(auth, sync_cookie). + gen_server:call(auth, sync_cookie, infinity). -spec print(Node :: node(), Format :: string(), Args :: [_]) -> 'ok'. -- cgit v1.2.3 From bf0b86ea8e71d05331ef36c472f4dee646b22c38 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 12 Apr 2018 11:54:47 +0200 Subject: erts: Add erts_debug:lc_graph/0 Run debug VM or config with --enable-lock-checking. Exercise VM and then run erts_debug:lc_graph(). to create a file "lc_graph." in current working directory. --- lib/kernel/src/erts_debug.erl | 90 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl index 6f248626ca..1270de4144 100644 --- a/lib/kernel/src/erts_debug.erl +++ b/lib/kernel/src/erts_debug.erl @@ -35,7 +35,8 @@ flat_size/1, get_internal_state/1, instructions/0, map_info/1, same/2, set_internal_state/2, size_shared/1, copy_shared/1, dirty_cpu/2, dirty_io/2, dirty/3, - lcnt_control/1, lcnt_control/2, lcnt_collect/0, lcnt_clear/0]). + lcnt_control/1, lcnt_control/2, lcnt_collect/0, lcnt_clear/0, + lc_graph/0, lc_graph_to_dot/2, lc_graph_merge/2]). -spec breakpoint(MFA, Flag) -> non_neg_integer() when MFA :: {Module :: module(), @@ -407,3 +408,90 @@ cont_dis(_, {_,_,_}, _) -> ok. map_info(_) -> erlang:nif_error(undef). + +%% Create file "lc_graph." with all actual lock dependencies +%% recorded so far by the VM. +%% Needs debug VM or --enable-lock-checking config, returns 'notsup' otherwise. +lc_graph() -> + erts_debug:set_internal_state(available_internal_state, true), + erts_debug:get_internal_state(lc_graph). + +%% Convert "lc_graph." file to https://www.graphviz.org dot format. +lc_graph_to_dot(OutFile, InFile) -> + {ok, [LL0]} = file:consult(InFile), + + [{"NO LOCK",0} | LL] = LL0, + Map = maps:from_list([{Id, Name} || {Name, Id, _, _} <- LL]), + + case file:open(OutFile, [exclusive]) of + {ok, Out} -> + ok = file:write(Out, "digraph G {\n"), + + [dot_print_lock(Out, Lck, Map) || Lck <- LL], + + ok = file:write(Out, "}\n"), + ok = file:close(Out); + + {error,eexist} -> + {"File already exists", OutFile} + end. + +dot_print_lock(Out, {_Name, Id, Lst, _}, Map) -> + [dot_print_edge(Out, From, Id, Map) || From <- Lst], + ok. + +dot_print_edge(_, 0, _, _) -> + ignore; % "NO LOCK" +dot_print_edge(Out, From, To, Map) -> + io:format(Out, "~p -> ~p;\n", [maps:get(From,Map), maps:get(To,Map)]). + + +%% Merge several "lc_graph" files into one file. +lc_graph_merge(OutFile, InFiles) -> + LLs = lists:map(fun(InFile) -> + {ok, [LL]} = file:consult(InFile), + LL + end, + InFiles), + + Res = lists:foldl(fun(A, B) -> lcg_merge(A, B) end, + hd(LLs), + tl(LLs)), + case file:open(OutFile, [exclusive]) of + {ok, Out} -> + try + lcg_print(Out, Res) + after + file:close(Out) + end, + ok; + {error, eexist} -> + {"File already exists", OutFile} + end. + +lcg_merge(A, B) -> + lists:zipwith(fun(LA, LB) -> lcg_merge_locks(LA, LB) end, + A, B). + +lcg_merge_locks(L, L) -> + L; +lcg_merge_locks({Name, Id, DA, IA}, {Name, Id, DB, IB}) -> + Direct = lists:umerge(DA, DB), + Indirect = lists:umerge(IA, IB), + {Name, Id, Direct, Indirect -- Direct}. + + +lcg_print(Out, LL) -> + io:format(Out, "[", []), + lcg_print_locks(Out, LL), + io:format(Out, "].\n", []), + ok. + +lcg_print_locks(Out, [{_,_}=NoLock | Rest]) -> + io:format(Out, "~p,\n", [NoLock]), + lcg_print_locks(Out, Rest); +lcg_print_locks(Out, [LastLock]) -> + io:format(Out, "~w", [LastLock]); +lcg_print_locks(Out, [Lock | Rest]) -> + io:format(Out, "~w,\n", [Lock]), + lcg_print_locks(Out, Rest). -- cgit v1.2.3 From 6915407d64fca30896929bbc717408595a63d45b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 13 Apr 2018 14:47:55 +0200 Subject: Add zzz_SUITE with erts_debug:lc_graph last in tests for erts, stdlib, kernel and runtime_tools. --- lib/kernel/test/Makefile | 3 ++- lib/kernel/test/zzz_SUITE.erl | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 lib/kernel/test/zzz_SUITE.erl (limited to 'lib/kernel') diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile index efe3a68531..03b6355056 100644 --- a/lib/kernel/test/Makefile +++ b/lib/kernel/test/Makefile @@ -80,7 +80,8 @@ MODULES= \ loose_node \ sendfile_SUITE \ standard_error_SUITE \ - multi_load_SUITE + multi_load_SUITE \ + zzz_SUITE APP_FILES = \ appinc.app \ diff --git a/lib/kernel/test/zzz_SUITE.erl b/lib/kernel/test/zzz_SUITE.erl new file mode 100644 index 0000000000..59c7fd7404 --- /dev/null +++ b/lib/kernel/test/zzz_SUITE.erl @@ -0,0 +1,37 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-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(zzz_SUITE). + +%% The sole purpose of this test suite is for things we want to run last +%% before the VM terminates. + +-export([all/0]). + +-export([lc_graph/1]). + + +all() -> + [lc_graph]. + +lc_graph(_Config) -> + %% Create "lc_graph" file in current working dir + %% if lock checker is enabled. + erts_debug:lc_graph(), + ok. -- cgit v1.2.3 From 23f4279f49ce84c8d0e96a4d4a1478b2ee9ec190 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Thu, 19 Apr 2018 11:44:46 +0200 Subject: Improve node allowed check * Move the node name check earlier * Check while still a list * Export allowed check function * Make allowed check function handle atom and list mixes --- lib/kernel/src/dist_util.erl | 75 +++++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 28 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index 781397e1ee..b325fc5456 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2017. All Rights Reserved. +%% Copyright Ericsson AB 1999-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. @@ -30,6 +30,7 @@ strict_order_flags/0, start_timer/1, setup_timer/2, reset_timer/1, cancel_timer/1, + is_allowed/2, shutdown/3, shutdown/4]). -import(error_logger,[error_msg/2]). @@ -182,7 +183,6 @@ handshake_other_started(#hs_data{request_type=ReqType, reject_flags=RejFlgs, require_flags=ReqFlgs}, check_dflags(HSData, EDF), - is_allowed(HSData), ?debug({"MD5 connection from ~p (V~p)~n", [Node, HSData#hs_data.other_version]}), mark_pending(HSData), @@ -199,21 +199,6 @@ handshake_other_started(OldHsData) when element(1,OldHsData) =:= hs_data -> handshake_other_started(convert_old_hsdata(OldHsData)). -%% -%% check if connecting node is allowed to connect -%% with allow-node-scheme -%% -is_allowed(#hs_data{other_node = Node, - allowed = Allowed} = HSData) -> - case lists:member(Node, Allowed) of - false when Allowed =/= [] -> - send_status(HSData, not_allowed), - error_msg("** Connection attempt from " - "disallowed node ~w ** ~n", [Node]), - ?shutdown2(Node, {is_allowed, not_allowed}); - _ -> true - end. - %% %% Check mandatory flags... %% @@ -642,24 +627,58 @@ send_challenge_ack(#hs_data{socket = Socket, f_send = FSend}, %% tcp_drv.c which used it to detect simultaneous connection %% attempts). %% -recv_name(#hs_data{socket = Socket, f_recv = Recv}) -> +recv_name(#hs_data{socket = Socket, f_recv = Recv} = HSData) -> case Recv(Socket, 0, infinity) of - {ok,Data} -> - get_name(Data); + {ok, + [$n,VersionA, VersionB, Flag1, Flag2, Flag3, Flag4 + | OtherNode] = Data} -> + case is_valid_name(OtherNode) of + true -> + Flags = ?u32(Flag1, Flag2, Flag3, Flag4), + Version = ?u16(VersionA,VersionB), + is_allowed(HSData, Flags, OtherNode, Version); + false -> + ?shutdown(Data) + end; _ -> ?shutdown(no_node) end. -get_name([$n,VersionA, VersionB, Flag1, Flag2, Flag3, Flag4 | OtherNode] = Data) -> - case is_valid_name(OtherNode) of +%% +%% check if connecting node is allowed to connect +%% with allow-node-scheme +%% +is_allowed(#hs_data{allowed = []}, Flags, Node, Version) -> + {Flags,list_to_atom(Node),Version}; +is_allowed(#hs_data{allowed = Allowed} = HSData, Flags, Node, Version) -> + case is_allowed(Node, Allowed) of true -> - {?u32(Flag1, Flag2, Flag3, Flag4), list_to_atom(OtherNode), - ?u16(VersionA,VersionB)}; + {Flags,list_to_atom(Node),Version}; false -> - ?shutdown(Data) - end; -get_name(Data) -> - ?shutdown(Data). + send_status(HSData#hs_data{other_node = Node}, not_allowed), + error_msg("** Connection attempt from " + "disallowed node ~s ** ~n", [Node]), + ?shutdown2(Node, {is_allowed, not_allowed}) + end. + +is_allowed(_Node, []) -> + false; +is_allowed(Node, [AllowedNode|Allowed]) -> + case is_nodename_equal(Node, AllowedNode) of + true -> + true; + false -> + is_allowed(Node, Allowed) + end. + +is_nodename_equal(A, B) when is_atom(A), is_atom(B) -> + A =:= B; +is_nodename_equal(A, B) when is_atom(A) -> + is_nodename_equal(atom_to_list(A), B); +is_nodename_equal(A, B) when is_atom(B) -> + is_nodename_equal(A, atom_to_list(B)); +is_nodename_equal(A, B) when is_list(A), is_list(B) -> + A =:= B. is_valid_name(OtherNodeName) -> case string:lexemes(OtherNodeName,"@") of -- cgit v1.2.3 From 4c4d861792d79ac7773548c089b7a93bc2c72a51 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Fri, 20 Apr 2018 16:52:15 +0200 Subject: Open for host and node allow list --- lib/kernel/src/dist_util.erl | 81 ++++++++++++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 22 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index b325fc5456..402d474c6d 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -30,7 +30,7 @@ strict_order_flags/0, start_timer/1, setup_timer/2, reset_timer/1, cancel_timer/1, - is_allowed/2, + is_node_name/1, split_node/1, is_allowed/2, shutdown/3, shutdown/4]). -import(error_logger,[error_msg/2]). @@ -632,7 +632,7 @@ recv_name(#hs_data{socket = Socket, f_recv = Recv} = HSData) -> {ok, [$n,VersionA, VersionB, Flag1, Flag2, Flag3, Flag4 | OtherNode] = Data} -> - case is_valid_name(OtherNode) of + case is_node_name(OtherNode) of true -> Flags = ?u32(Flag1, Flag2, Flag3, Flag4), Version = ?u16(VersionA,VersionB), @@ -644,6 +644,31 @@ recv_name(#hs_data{socket = Socket, f_recv = Recv} = HSData) -> ?shutdown(no_node) end. +is_node_name(OtherNodeName) -> + case string:split(OtherNodeName, "@") of + [Name,Host] -> + (not string:is_empty(Name)) + andalso (not string:is_empty(Host)); + _ -> + false + end. + +split_node(Node) -> + case string:split(listify_node(Node), "@") of + [Name,Host] = Split -> + case + (not string:is_empty(Name)) + andalso (not string:is_empty(Host)) + of + true -> + {Name,Host}; + false -> + Split + end; + Split -> + Split + end. + %% %% check if connecting node is allowed to connect %% with allow-node-scheme @@ -661,32 +686,44 @@ is_allowed(#hs_data{allowed = Allowed} = HSData, Flags, Node, Version) -> ?shutdown2(Node, {is_allowed, not_allowed}) end. +%% Allow Node on Allowed node list, and also if host part +%% of Node matches Allowed list item. The Allowed list +%% contains node names or host names. +%% is_allowed(_Node, []) -> false; +is_allowed(Node, [Node|_Allowed]) when is_atom(Node) -> + true; is_allowed(Node, [AllowedNode|Allowed]) -> - case is_nodename_equal(Node, AllowedNode) of - true -> - true; - false -> + case split_node(AllowedNode) of + {AllowedName,AllowedHost} -> + %% Allowed node name + case split_node(Node) of + {AllowedName,AllowedHost} -> + true; + _ -> + is_allowed(Node, Allowed) + end; + [AllowedHost] -> + %% Allowed host name + case split_node(Node) of + {_,AllowedHost} -> + %% Matching Host part + true; + [AllowedHost] -> + %% Host matches Host + true; + _ -> + is_allowed(Node, Allowed) + end; + _ -> is_allowed(Node, Allowed) end. -is_nodename_equal(A, B) when is_atom(A), is_atom(B) -> - A =:= B; -is_nodename_equal(A, B) when is_atom(A) -> - is_nodename_equal(atom_to_list(A), B); -is_nodename_equal(A, B) when is_atom(B) -> - is_nodename_equal(A, atom_to_list(B)); -is_nodename_equal(A, B) when is_list(A), is_list(B) -> - A =:= B. - -is_valid_name(OtherNodeName) -> - case string:lexemes(OtherNodeName,"@") of - [_OtherNodeName,_OtherNodeHost] -> - true; - _else -> - false - end. +listify_node(Atom) when is_atom(Atom) -> + atom_to_list(Atom); +listify_node(Node) when is_list(Node) -> + Node. publish_type(Flags) -> case Flags band ?DFLAG_PUBLISHED of -- cgit v1.2.3 From 05b1c13e36ec49891e89593b25e668e983a4fc41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Tue, 24 Apr 2018 13:30:00 +0200 Subject: heart: Use ntohs instead of manual conversion Multiplying a signed char by 256 is undefined behavior and caused problems on some platforms when the length was long enough. We could cast it to an unsigned int to make it work, but it's best not to reinvent the wheel. Fixes OTP-15034 --- lib/kernel/test/heart_SUITE.erl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/heart_SUITE.erl b/lib/kernel/test/heart_SUITE.erl index 83efbb4c35..02954aa54c 100644 --- a/lib/kernel/test/heart_SUITE.erl +++ b/lib/kernel/test/heart_SUITE.erl @@ -352,9 +352,16 @@ clear_cmd(Config) when is_list(Config) -> get_cmd(suite) -> []; get_cmd(Config) when is_list(Config) -> {ok, Node} = start_check(slave, heart_test), - Cmd = "test", - ok = rpc:call(Node, heart, set_cmd, [Cmd]), - {ok, Cmd} = rpc:call(Node, heart, get_cmd, []), + + ShortCmd = "test", + ok = rpc:call(Node, heart, set_cmd, [ShortCmd]), + {ok, ShortCmd} = rpc:call(Node, heart, get_cmd, []), + + %% This would hang prior to OTP-15024 being fixed. + LongCmd = [$a || _ <- lists:seq(1, 160)], + ok = rpc:call(Node, heart, set_cmd, [LongCmd]), + {ok, LongCmd} = rpc:call(Node, heart, get_cmd, []), + stop_node(Node), ok. -- cgit v1.2.3 From 0deea4a8f369013ec00e231d0c2c37e4ab3f0ba1 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 24 Apr 2018 14:26:31 +0200 Subject: Add logger --- lib/kernel/doc/src/Makefile | 21 +- lib/kernel/doc/src/book.xml | 3 + lib/kernel/doc/src/introduction_chapter.xml | 64 + lib/kernel/doc/src/logger.xml | 478 +++++++ lib/kernel/doc/src/logger_arch.png | Bin 0 -> 31459 bytes lib/kernel/doc/src/logger_chapter.xml | 641 +++++++++ lib/kernel/doc/src/logger_filters.xml | 191 +++ lib/kernel/doc/src/logger_formatter.xml | 149 ++ lib/kernel/doc/src/logger_std_h.xml | 81 ++ lib/kernel/doc/src/part.xml | 40 + lib/kernel/doc/src/ref_man.xml | 4 + lib/kernel/doc/src/specs.xml | 4 + lib/kernel/include/logger.hrl | 49 + lib/kernel/src/Makefile | 29 +- lib/kernel/src/kernel.app.src | 15 +- lib/kernel/src/logger.erl | 799 +++++++++++ lib/kernel/src/logger_backend.erl | 133 ++ lib/kernel/src/logger_config.erl | 151 +++ lib/kernel/src/logger_disk_log_h.erl | 694 ++++++++++ lib/kernel/src/logger_filters.erl | 123 ++ lib/kernel/src/logger_formatter.erl | 255 ++++ lib/kernel/src/logger_h_common.erl | 305 +++++ lib/kernel/src/logger_h_common.hrl | 262 ++++ lib/kernel/src/logger_internal.hrl | 96 ++ lib/kernel/src/logger_server.erl | 440 ++++++ lib/kernel/src/logger_simple.erl | 238 ++++ lib/kernel/src/logger_std_h.erl | 799 +++++++++++ lib/kernel/src/logger_sup.erl | 53 + lib/kernel/test/Makefile | 14 +- lib/kernel/test/kernel.spec | 1 + lib/kernel/test/logger.cover | 14 + lib/kernel/test/logger.spec | 11 + lib/kernel/test/logger_SUITE.erl | 828 ++++++++++++ lib/kernel/test/logger_bench_SUITE.erl | 500 +++++++ lib/kernel/test/logger_bench_SUITE_data/Emakefile | 1 + .../test/logger_bench_SUITE_data/lager_helper.erl | 73 + lib/kernel/test/logger_disk_log_h_SUITE.erl | 1417 ++++++++++++++++++++ lib/kernel/test/logger_env_var_SUITE.erl | 451 +++++++ lib/kernel/test/logger_filters_SUITE.erl | 214 +++ lib/kernel/test/logger_formatter_SUITE.erl | 501 +++++++ lib/kernel/test/logger_legacy_SUITE.erl | 282 ++++ lib/kernel/test/logger_simple_SUITE.erl | 247 ++++ lib/kernel/test/logger_std_h_SUITE.erl | 1396 +++++++++++++++++++ 43 files changed, 12056 insertions(+), 11 deletions(-) create mode 100644 lib/kernel/doc/src/introduction_chapter.xml create mode 100644 lib/kernel/doc/src/logger.xml create mode 100644 lib/kernel/doc/src/logger_arch.png create mode 100644 lib/kernel/doc/src/logger_chapter.xml create mode 100644 lib/kernel/doc/src/logger_filters.xml create mode 100644 lib/kernel/doc/src/logger_formatter.xml create mode 100644 lib/kernel/doc/src/logger_std_h.xml create mode 100644 lib/kernel/doc/src/part.xml create mode 100644 lib/kernel/include/logger.hrl create mode 100644 lib/kernel/src/logger.erl create mode 100644 lib/kernel/src/logger_backend.erl create mode 100644 lib/kernel/src/logger_config.erl create mode 100644 lib/kernel/src/logger_disk_log_h.erl create mode 100644 lib/kernel/src/logger_filters.erl create mode 100644 lib/kernel/src/logger_formatter.erl create mode 100644 lib/kernel/src/logger_h_common.erl create mode 100644 lib/kernel/src/logger_h_common.hrl create mode 100644 lib/kernel/src/logger_internal.hrl create mode 100644 lib/kernel/src/logger_server.erl create mode 100644 lib/kernel/src/logger_simple.erl create mode 100644 lib/kernel/src/logger_std_h.erl create mode 100644 lib/kernel/src/logger_sup.erl create mode 100644 lib/kernel/test/logger.cover create mode 100644 lib/kernel/test/logger.spec create mode 100644 lib/kernel/test/logger_SUITE.erl create mode 100644 lib/kernel/test/logger_bench_SUITE.erl create mode 100644 lib/kernel/test/logger_bench_SUITE_data/Emakefile create mode 100644 lib/kernel/test/logger_bench_SUITE_data/lager_helper.erl create mode 100644 lib/kernel/test/logger_disk_log_h_SUITE.erl create mode 100644 lib/kernel/test/logger_env_var_SUITE.erl create mode 100644 lib/kernel/test/logger_filters_SUITE.erl create mode 100644 lib/kernel/test/logger_formatter_SUITE.erl create mode 100644 lib/kernel/test/logger_legacy_SUITE.erl create mode 100644 lib/kernel/test/logger_simple_SUITE.erl create mode 100644 lib/kernel/test/logger_std_h_SUITE.erl (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile index 2413541082..10bdf46119 100644 --- a/lib/kernel/doc/src/Makefile +++ b/lib/kernel/doc/src/Makefile @@ -56,6 +56,10 @@ XML_REF3_FILES = application.xml \ inet.xml \ inet_res.xml \ init_stub.xml \ + logger.xml \ + logger_std_h.xml \ + logger_filters.xml \ + logger_formatter.xml \ net_adm.xml \ net_kernel.xml \ os.xml \ @@ -70,11 +74,17 @@ XML_REF4_FILES = app.xml config.xml XML_REF6_FILES = kernel_app.xml -XML_PART_FILES = -XML_CHAPTER_FILES = notes.xml +XML_PART_FILES = part.xml +XML_CHAPTER_FILES = \ + notes.xml \ + introduction_chapter.xml \ + logger_chapter.xml BOOK_FILES = book.xml +IMAGE_FILES = \ + logger_arch.png + XML_FILES = \ $(BOOK_FILES) $(XML_CHAPTER_FILES) \ $(XML_PART_FILES) $(XML_REF3_FILES) $(XML_REF4_FILES)\ @@ -111,7 +121,7 @@ SPECS_FLAGS = -I../../include # ---------------------------------------------------- # Targets # ---------------------------------------------------- -$(HTMLDIR)/%.gif: %.gif +$(HTMLDIR)/%: % $(INSTALL_DATA) $< $@ docs: man pdf html @@ -120,11 +130,12 @@ $(TOP_PDF_FILE): $(XML_FILES) pdf: $(TOP_PDF_FILE) -html: gifs $(HTML_REF_MAN_FILE) +html: images $(HTML_REF_MAN_FILE) man: $(MAN3_FILES) $(MAN4_FILES) $(MAN6_FILES) -gifs: $(GIF_FILES:%=$(HTMLDIR)/%) +images: $(IMAGE_FILES:%=$(HTMLDIR)/%) + debug opt: clean clean_docs: diff --git a/lib/kernel/doc/src/book.xml b/lib/kernel/doc/src/book.xml index 81a87d126d..0b69b547e7 100644 --- a/lib/kernel/doc/src/book.xml +++ b/lib/kernel/doc/src/book.xml @@ -34,6 +34,9 @@ + + + diff --git a/lib/kernel/doc/src/introduction_chapter.xml b/lib/kernel/doc/src/introduction_chapter.xml new file mode 100644 index 0000000000..6e6990ddda --- /dev/null +++ b/lib/kernel/doc/src/introduction_chapter.xml @@ -0,0 +1,64 @@ + + + + +
    + + 2017 + Ericsson AB. 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. + + + + Introduction + + + + + + + + introduction.xml +
    + +
    + Scope +

    The Kernel application has all the code necessary to run + the Erlang runtime system: file servers, code servers, + and so on.

    +

    The Kernel application is the first application started. It is + mandatory in the sense that the minimal system based on + Erlang/OTP consists of Kernel and STDLIB. Kernel + contains the following functional areas:

    + + Start, stop, supervision, configuration, and distribution of applications + Code loading + Logging + Error logging + Global name service + Supervision of Erlang/OTP + Communication with sockets + Operating system interface + +
    + +
    + Prerequisites +

    It is assumed that the reader is familiar with the Erlang programming + language.

    +
    +
    + + diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml new file mode 100644 index 0000000000..66e6e5c689 --- /dev/null +++ b/lib/kernel/doc/src/logger.xml @@ -0,0 +1,478 @@ + + + + +
    + + 2017 + Ericsson AB. 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. + + + + logger + + + + + + + A + logger.xml +
    + logger + API module for the logger application. + + + + + + + + + +

    The severity level for the message to be logged.

    +
    +
    + + + +

    +
    +
    + + + +

    +
    +
    + + + +

    +
    +
    + + + +

    Metadata associated with the message to be logged.

    +
    +
    + + + +

    +
    +
    + + + +

    +
    +
    + + + +

    +
    +
    + + + +

    +
    +
    + + + +

    +
    +
    +
    + +
    + Macros +

    The following macros are defined:

    + + + ?LOG_EMERGENCY(StringOrReport[,Metadata]) + ?LOG_EMERGENCY(FunOrFormat,Args[,Metadata]) + ?LOG_ALERT(StringOrReport[,Metadata]) + ?LOG_ALERT(FunOrFormat,Args[,Metadata]) + ?LOG_CRITICAL(StringOrReport[,Metadata]) + ?LOG_CRITICAL(FunOrFormat,Args[,Metadata]) + ?LOG_ERROR(StringOrReport[,Metadata]) + ?LOG_ERROR(FunOrFormat,Args[,Metadata]) + ?LOG_WARNING(StringOrReport[,Metadata]) + ?LOG_WARNING(FunOrFormat,Args[,Metadata]) + ?LOG_NOTICE(StringOrReport[,Metadata]) + ?LOG_NOTICE(FunOrFormat,Args[,Metadata]) + ?LOG_INFO(StringOrReport[,Metadata]) + ?LOG_INFO(FunOrFormat,Args[,Metadata]) + ?LOG_DEBUG(StringOrReport[,Metadata]) + ?LOG_DEBUG(FunOrFormat,Args[,Metadata]) + + +

    All macros expand to a call to logger, where Level is + taken from the macro name, and the following metadata is added, + or merged with the given Metadata:

    + + +#{mfa=>{?MODULE,?FUNCTION_NAME,?FUNCTION_ARITY}, + file=>?FILE, + line=>?LINE} + + +

    The call is wrapped in a case statement and will be evaluated + only if Level is equal to or below the configured log + level.

    +
    + + + + emergency(StringOrReport[,Metadata]) + emergency(Format,Args[,Metadata]) + emergency(Fun,FunArgs[,Metadata]) + Logs the given message as level emergency. + +

    Equivalent to + log(emergency,...).

    +
    +
    + + + alert(StringOrReport[,Metadata]) + alert(Format,Args[,Metadata]) + alert(Fun,FunArgs[,Metadata]) + Logs the given message as level alert. + +

    Equivalent to + log(alert,...).

    +
    +
    + + + critical(StringOrReport[,Metadata]) + critical(Format,Args[,Metadata]) + critical(Fun,FunArgs[,Metadata]) + Logs the given message as level critical. + +

    Equivalent to + log(critical,...).

    +
    +
    + + + error(StringOrReport[,Metadata]) + error(Format,Args[,Metadata]) + error(Fun,FunArgs[,Metadata]) + Logs the given message as level error. + +

    Equivalent to + log(error,...).

    +
    +
    + + + warning(StringOrReport[,Metadata]) + warning(Format,Args[,Metadata]) + warning(Fun,FunArgs[,Metadata]) + Logs the given message as level warning. + +

    Equivalent to + log(warning,...).

    +
    +
    + + + notice(StringOrReport[,Metadata]) + notice(Format,Args[,Metadata]) + notice(Fun,FunArgs[,Metadata]) + Logs the given message as level notice. + +

    Equivalent to + log(notice,...).

    +
    +
    + + + info(StringOrReport[,Metadata]) + info(Format,Args[,Metadata]) + info(Fun,FunArgs[,Metadata]) + Logs the given message as level info. + +

    Equivalent to + log(info,...).

    +
    +
    + + + debug(StringOrReport[,Metadata]) + debug(Format,Args[,Metadata]) + debug(Fun,FunArgs[,Metadata]) + Logs the given message as level debug. + +

    Equivalent to + log(debug,...).

    +
    +
    + + + + + + + + + Logs the given message. + + + + + + + + +

    Log the given message.

    +
    +
    + + + + Lookup the current configuration for logger. + +

    Lookup the current configuration for logger.

    +
    +
    + + + + Lookup the current configuration for the given handler. + +

    Lookup the current configuration for the given handler.

    +
    +
    + + + + Get information about all logger configurations + +

    Same as logger:i(term)

    +
    +
    + + + + + + Get information about all logger configurations + +

    The logger:i/1 function can be used to get all + current logger configuration. The way that the information + is returned depends on the Action

    + + string + Return the pretty printed current logger configuration + as iodata. + term + Return the current logger configuration as a term. The + format of this term may change inbetween releases. For a + stable format use + logger:get_handler_config/1 + and + logger:get_logger_config/0. + The same as calling logger:i(). + print + Pretty print all the current logger configuration to + standard out. Example: + logger:i(). +Current logger configuration: + Level: info + FilterDefault: log + Filters: + Handlers: + Id: logger_std_h + Module: logger_std_h + Level: info + Formatter: + Module: logger_formatter + Config: #{template => [{logger_formatter,header},"\n",msg,"\n"], + legacy_header => true} + Filter Default: stop + Filters: + Id: stop_progress + Fun: fun logger_filters:progress/2 + Config: stop + Id: remote_gl + Fun: fun logger_filters:remote_gl/2 + Config: stop + Id: domain + Fun: fun logger_filters:domain/2 + Config: {log,prefix_of,[beam,erlang,otp,sasl]} + Id: no_domain + Fun: fun logger_filters:domain/2 + Config: {log,no_domain,[]} + Handler Config: + logger_std_h: #{type => standard_io} + Level set per module: + Module: my_module + Level: debug]]> + + +
    +
    + + + + Add a filter to the logger. + +

    Add a filter to the logger.

    +
    +
    + + + + Add a filter to the specified handler. + +

    Add a filter to the specified handler.

    +
    +
    + + + + Remove a filter from the logger. + +

    Remove the filter with the specified identity from the logger.

    +
    +
    + + + + Remove a filter from the specified handler. + +

    Remove the filter with the specified identity from the given handler.

    +
    +
    + + + + Add a handler with the given configuration. + +

    Add a handler with the given configuration.

    +
    +
    + + + + Remove the handler with the specified identity. + +

    Remove the handler with the specified identity.

    +
    +
    + + + + Set the log level for the specified module. + +

    Set the log level for the specified module.

    +

    To change the logging level globally, use + logger:set_logger_config(level, Level). +

    +
    +
    + + + + Remove a module specific log setting. + +

    Remove a module specific log setting. After this, the + global log level is used for the specified module.

    +
    +
    + + + + + Add or update configuration data for the logger. + +

    Add or update configuration data for the logger.

    +
    +
    + + + + + Add or update configuration data for the specified + handler. + +

    Add or update configuration data for the specified + handler.

    +
    +
    + + + + Compare the severity of two log levels. + +

    Compare the severity of two log levels. Returns gt + if Level1 is more severe than + Level2, lt if Level1 is less severe, + and eq if the levels are equal.

    +
    +
    + + + + Set metadata to use when logging from current process. + +

    Set metadata which logger automatically inserts it + in all log events produced on the current + process. Subsequent calls will overwrite previous data set + by this function.

    +

    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.

    +
    +
    + + + + Retrieve data set with set_process_metadata/1. + +

    Retrieve data set + with + set_process_metadata/1.

    +
    +
    + + + + Delete data set with set_process_metadata/1. + +

    Delete data set + with + set_process_metadata/1.

    +
    +
    + +
    + +
    + + diff --git a/lib/kernel/doc/src/logger_arch.png b/lib/kernel/doc/src/logger_arch.png new file mode 100644 index 0000000000..727609a6ef Binary files /dev/null and b/lib/kernel/doc/src/logger_arch.png differ diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml new file mode 100644 index 0000000000..2a325453da --- /dev/null +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -0,0 +1,641 @@ + + + + +
    + + 2017 + Ericsson AB. 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. + + + + Logging + + + + + logger_chapter.xml +
    + +
    + Overview +

    Erlang/OTP provides a standard API for logging. The backend of + this API can be used as is, or it can be customized to suite + specific needs.

    +

    It consists of two parts - the logger part and the + handler part. The logger will forward log events to one + or more handler(s).

    + + + Conceptual overview + + +

    Filters can be added to the logger and to each + handler. The filters decide if an event is to be forwarded or + not, and they can also modify all parts of the log event.

    + +

    A formatter can be set for each handler. The formatter + does the final formatting of the log event, including the log + message itself, and possibly a timestamp, header and other + metadata.

    + +

    In accordance with the Syslog protocol, RFC-5424, eight + severity levels can be specified:

    + + + + Level + Integer + Description + + + emergency + 0 + system is unusable + + + alert + 1 + action must be taken immediately + + + critical + 2 + critical contidions + + + error + 3 + error conditions + + + warning + 4 + warning conditions + + + notice + 5 + normal but significant conditions + + + info + 6 + informational messages + + + debug + 7 + debug-level messages + + Severity levels +
    + +

    A log event is allowed by Logger if the integer value of + its Level is less than or equal to the currently + configured log level. The log level can be configured globally, + or to allow more verbose logging from a specific part of the + system, per module.

    + +
    + Customizable parts + + + Handler + +

    A handler is defined as a module exporting the following + function:

    + + log(Log, Config) -> ok + +

    A handler is called by the logger backend after filtering on + logger level and on handler level for the handler which is + about to be called. The function call is done on the client + process, and it is up to the handler implementation if other + processes are to be involved or not.

    + +

    Multiple instances of the same handler can be + added. Configuration is per instance.

    + +
    + + Filter + +

    Filters can be set on the logger or on a handler. Logger + filters are applied first, and if passed, the handler filters + for each handler are applied. The handler plugin is only + called if all handler filters for the handler in question also + pass.

    + +

    A filter is specified as:

    + + {fun((Log,Extra) -> Log | stop | ignore), Extra} + +

    The configuration parameter filter_default + specifies the behavior if all filters return ignore. + filter_default is by default set to log.

    + +

    The Extra parameter may contain any data that the + filter needs.

    +
    + + Formatter + +

    A formatter is defined as a module exporting the following + function:

    + + format(Log,Extra) -> string() + +

    The formatter plugin is called by each handler, and the + returned string can be printed to the handler's destination + (stdout, file, ...).

    +
    + +
    +
    + +
    + Built-in handlers + + + logger_std_h + +

    This is the default handler used by OTP. Multiple instances + can be started, and each instance will write log events to a + given destination, console or file. Filters can be used for + selecting which event to send to which handler instance.

    +
    + + logger_disk_log_h + +

    This handler behaves much like logger_std_h, except it uses + disk_log as its + destination.

    +
    + + error_logger + +

    This handler is to be used for backwards compatibility + only. It is not started by default, but will be automatically + started the first time an event handler is added + with + error_logger:add_report_handler/1,2.

    + +

    No built-in event handlers exist.

    +
    +
    +
    + +
    + Built-in filters + + + logger_filters:domain/2 + +

    This filter provides a way of filtering log events based on a + domain field Metadata. See + + logger_filters:domain/2

    +
    + + logger_filters:level/2 + +

    This filter provides a way of filtering log events based + on the log level. See + logger_filters:domain/2

    +
    + + logger_filters:progress/2 + +

    This filter matches all progress reports + from supervisor and application_controller. + See + logger_filters:progress/2

    +
    + + logger_filters:remote_gl/2 + +

    This filter matches all events originating from a process + that has its group leader on a remote node. + See + logger_filters:remote_gl/2

    +
    +
    +
    + +
    + Default formatter + +

    The default formatter is logger_formatter. + See + logger_formatter:format/2.

    +
    +
    + +
    + Configuration + +
    + Application environment variables +

    See Kernel(6) for + information about the application environment variables that can + be used for configuring logger.

    +
    + +
    + Logger configuration + + + level + +

    Specifies the severity level to log.

    +
    + filters + +

    Logger filters are added or removed with + + logger:add_logger_filter/2 and + + logger:remove_logger_filter/1, + respectively.

    +

    See Filter for more + information.

    +

    By default, no filters exist.

    +
    + filter_default = log | stop + +

    Specifies what to do with an event if all filters + return ignore.

    +

    Default is log.

    +
    + handlers + +

    Handlers are added or removed with + + logger:add_handler/3 and + + logger:remove_handler/1, + respectively.

    +

    See Handler for more + information.

    +
    +
    +
    + +
    + Handler configuration + + level + +

    Specifies the severity level to log.

    +
    + filters + +

    Handler filters can be specified when adding the handler, + or added or removed later with + + logger:add_handler_filter/3 and + + logger:remove_handler_filter/2, + respectively.

    +

    See Filter for more + information.

    +

    By default, no filters exist.

    +
    + filter_default = log | stop + +

    Specifies what to do with an event if all filters + return ignore.

    +

    Default is log.

    +
    + depth = pos_integer() | unlimited + +

    Specifies if the depth of terms in the log events shall + be limited by using control characters ~P + and ~W instead of ~p and ~w, + respectively. See + io:format.

    +
    + max_size = pos_integer() | unlimited + +

    Specifies if the size of a log event shall be limited by + truncating the formatted string.

    +
    + formatter = {Module::module(),Extra::term()} + +

    See Formatter for more + information.

    +

    The default module is + logger_formatter, and Extra is + it's configuration map.

    +
    +
    + +

    Note that level and filters are obeyed by + Logger itself before forwarding the log events to each + handler, while depth, max_size + and formatter are left to the handler + implementation. All Logger's built-in handlers do apply these + configuration parameters before printing.

    +
    + +
    + +
    + + Backwards compatibility with error_logger +

    Logger provides backwards compatibility with the old + error_logger in the following ways:

    + + + Legacy event handlers + +

    To use event handlers written for error_logger, just + add your event handler with

    + +error_logger:add_report_handler/1,2. + +

    This will automatically start the error_logger + event manager, and add error_logger as a + handler to logger, with configuration

    + +#{level=>info, + filter_default=>log, + filters=>[]}. + +

    Note that this handler will ignore events that do not + originate from the old error_logger API, or from + within OTP. This means that if your code uses the logger API + for logging, then your log events will be discarded by this + handler.

    +

    Also note that error_logger is not overload + protected.

    +
    + Logger API + +

    The old error_logger API still exists, but should + only be used by legacy code. It will be removed in a later + release.

    +
    + Output format + +

    To get log events on the same format as produced + by error_logger_tty_h and error_logger_file_h, + use the default formatter, logger_formatter, with + configuration parameter legacy_header=>true. This is + also the default.

    +
    + Default format of log events from OTP + +

    By default, all log events originating from within OTP, + except the former so called "SASL reports", look the same as + before.

    +
    + SASL reports + +

    By SASL reports we mean supervisor reports, crash reports + and progress reports.

    +

    In earlier releases, these reports were only logged when + the SASL application was running, and they were printed + trough specific event handlers + named sasl_report_tty_h + and sasl_report_file_h.

    +

    The destination of these log events were configured by + environment variables for the SASL application.

    +

    Due to the specific event handlers, the output format + slightly differed from other log events.

    +

    As of OTP-21, the concept of SASL reports is removed, + meaning that the default behavior is as follows:

    + + Supervisor reports, crash reports and progress reports + are no longer connected to the SASL application. + Supervisor reports and crash reports are logged by + default. + Progress reports are not logged by default, but can be + enabled with the kernel environment + variable logger_log_progress. + The output format is the same for all log + events. + +

    If the old behavior is preferred, the kernel environment + variable logger_sasl_compatible can be set + to true. The old SASL environment variables can then + be used as before, and the SASL reports will only be printed + if the SASL application is running - through a second log + handler named sasl_h.

    +

    All SASL reports have a metadata + field domain=>[beam,erlang,otp,sasl], which can be + used, for example, by filters to to stop or allow the + events.

    +
    +
    +
    + + +
    + Error handling +

    Log data is expected to be either a format string and + arguments, a string (unicode:chardata), or a report (map or + key-value list) which can be converted to a format string and + arguments by the handler. A default report callback should be + included in the log event's metadata, which can be used for + converting the report to a format string and arguments. The + handler might also do a custom conversion if the default format + is not desired.

    +

    logger does, to a certain extent, check its input data + before forwarding a log event to the handlers, but it does not + evaluate conversion funs or check the validity of format strings + and arguments. This means that any filter or handler must be + careful when formatting the data of a log event, making sure + that it does not crash due to bad input data or faulty + callbacks.

    +

    If a filter or handler still crashes, logger will remove the + filter or handler in question from the configuration, and then + print a short error message on the console. A debug event + containing the crash reason and other details is also issued, + and can be seen if a handler is installed which logs on debug + level.

    +
    + +
    + Example: add a handler to log debug events to file +

    When starting an erlang node, the default behavior is that all + log events with level info and above are logged to the + console. In order to also log debug events, you can either + change the global log level to debug or add a separate + handler to take care of this. In this example we will add a new + handler which prints the debug events to a separate file.

    +

    First, we add an instance of logger_std_h with + type {file,File}, and we set the handler's level + to debug:

    +
    +1> Config = #{level=>debug,logger_std_h=>#{type=>{file,"./debug.log"}}}.
    +#{logger_std_h => #{type => {file,"./debug.log"}},
    +  level => debug}
    +2> logger:add_handler(debug_handler,logger_std_h,Config).
    +ok
    +

    By default, the handler receives all events, so we need to add a filter + to stop all non-debug events:

    +
    +3> Fun = fun(#{level:=debug}=Log,_) -> Log; (_,_) -> stop end.
    +#Fun<erl_eval.12.98642416>
    +4> logger:add_handler_filter(debug_handler,allow_debug,{Fun,[]}).
    +ok
    +

    And finally, we need to make sure that the logger itself allows + debug events. This can either be done by setting the global + logger level:

    +
    +5> logger:set_logger_config(level,debug).
    +ok
    +

    Or by allowing debug events from one or a few modules only:

    +
    +6> logger:set_module_level(mymodule,debug).
    +ok
    + +
    + +
    + Example: implement a handler +

    The only requirement that a handler MUST fulfill is to export + the following function:

    + log(logger:log(),logger:config()) ->ok +

    It may also implement the following callbacks:

    + +adding_handler(logger:handler_id(),logger:config()) -> {ok,logger:config()} | {error,term()} +removing_handler(logger:handler_id()) -> ok +changing_config(logger:handler_id(),logger:config(),logger:config()) -> {ok,logger:config()} | {error,term()} + +

    When logger:add_handler(Id,Module,Config) is called, logger + will first call Module:adding_handler(Id,Config), and if it + returns {ok,NewConfig} the NewConfig is written to the + configuration database. After this, the handler may receive log + events as calls to Module:log/2.

    +

    A handler can be removed by calling + logger:remove_handler(Id). logger will call + Module:removing_handler(Id), and then remove the handler's + configuration from the configuration database.

    +

    When logger:set_handler_config is called, logger calls + Module:changing_config(Id,OldConfig,NewConfig). If this function + returns ok, the NewConfig is written to the configuration + database.

    + +

    A simple handler which prints to the console could be + implemented as follows:

    + +-module(myhandler). +-export([log/2]). + +log(#{msg:={report,R}},_) -> + io:format("~p~n",[R]); +log(#{msg:={string,S}},_) -> + io:put_chars(S); +log(#{msg:={F,A}},_) -> + io:format(F,A). + + +

    A simple handler which prints to file could be implemented like + this:

    + +-module(myhandler). +-export([adding_handler/2, removing_handler/1, log/2]). +-export([init/1, handle_call/3, handle_cast/2, terminate/2]). + +adding_handler(Id,Config) -> + {ok,Fd} = file:open(File,[append,{encoding,utf8}]), + {ok,Config#{myhandler_fd=>Fd}}. + +removing_handler(Id,#{myhandler_fd:=Fd}) -> + _ = file:close(Fd), + ok. + +log(#{msg:={report,R}},#{myhandler_fd:=Fd}) -> + io:format(Fd,"~p~n",[R]); +log(#{msg:={string,S}},#{myhandler_fd:=Fd}) -> + io:put_chars(Fd,S); +log(#{msg:={F,A}},#{myhandler_fd:=Fd}) -> + io:format(Fd,F,A). + + +

    Note that none of the above handlers have any overload + protection, and all log events are printed directly from the + client process. Neither do the handlers use the formatter or + in any way add time or other metadata to the printed events.

    + +

    For examples of overload protection, please refer to the + implementation + of logger_std_h + and logger_disk_log_h + .

    + +

    Below is a simpler example of a handler which logs through one + single process, and uses the default formatter to gain a common + look of the log events.

    +

    It also uses the metadata field report_cb, if it exists, + to print reports in the way the event issuer suggests. The + formatter will normally do this, but if the handler either has + an own default (as in this example) or if the + given report_cb should not be used at all, then the + handler must take care of this itself.

    + +-module(myhandler). +-export([adding_handler/2, removing_handler/1, log/2]). +-export([init/1, handle_call/3, handle_cast/2, terminate/2]). + +adding_handler(Id,Config) -> + {ok,Pid} = gen_server:start(?MODULE,Config), + {ok,Config#{myhandler_pid=>Pid}}. + +removing_handler(Id,#{myhandler_pid:=Pid}) -> + gen_server:stop(Pid). + +log(Log,#{myhandler_pid:=Pid} = Config) -> + gen_server:cast(Pid,{log,Log,Config}). + +init(#{myhandler_file:=File}) -> + {ok,Fd} = file:open(File,[append,{encoding,utf8}]), + {ok,#{file=>File,fd=>Fd}}. + +handle_call(_,_,State) -> + {reply,{error,bad_request},State}. + +handle_cast({log,Log,Config},#{fd:=Fd} = State) -> + do_log(Fd,Log,Config), + {noreply,State}. + +terminate(Reason,#{fd:=Fd}) -> + _ = file:close(Fd), + ok. + +do_log(Fd,#{msg:={report,R}} = Log, Config) -> + Fun = maps:get(report_cb,Config,fun my_report_cb/1, + {F,A} = Fun(R), + do_log(Fd,Log#{msg=>{F,A},Config); +do_log(Fd,Log,#{formatter:={FModule,FConfig}}) -> + String = FModule:format(Log,FConfig), + io:put_chars(Fd,String). + +my_report_cb(R) -> + {"~p",[R]}. + +
    + + +
    + See Also +

    error_logger(3), + SASL(6)

    +
    +
    diff --git a/lib/kernel/doc/src/logger_filters.xml b/lib/kernel/doc/src/logger_filters.xml new file mode 100644 index 0000000000..d742391e35 --- /dev/null +++ b/lib/kernel/doc/src/logger_filters.xml @@ -0,0 +1,191 @@ + + + + +
    + + 2018 + Ericsson AB. 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. + + + + logger_filters + + + + + + + A + logger_filters.xml +
    + logger_filters + Filters to use with logger. + + +

    Filters to use with logger. All functions exported from this + module can be used as logger or handler + filters. See + logger:add_logger_filter/2 + and + logger:add_handler_filter/3 + for more information about how filters are added.

    +
    + + + + + Filter log events based on the domain field in metadata. + +

    This filter provides a way of filtering log events based on a + domain field Metadata.

    + +

    The Extra parameter is specified when + adding the filter + via + logger:add_logger_filter/2 + or + logger:add_handler_filter/3.

    + +

    The filter compares the value of the domain field + in the log event's metadata (Domain) + to MatchDomain as follows:

    + + + Compare = starts_with +

    The filter matches if MatchDomain is a prefix + of Domain.

    + Compare = prefix_of +

    The filter matches if Domain is a prefix + of MatchDomain.

    + Compare = equals +

    The filter matches if Domain is equal + to MatchDomain.

    + Compare = no_domain +

    The filter matches if there is no domain field in + metadata. In this case MatchDomain shall + be [].

    +
    + +

    If the filter matches and Action = + log, the log event is allowed. If the filter matches + and Action = stop, the log event is + stopped.

    + +

    If the filter does not match, it returns ignore, + meaning that other filters, or the value of the + configuration parameter filter_default, will decide + if the event is allowed or not.

    + +

    Log events that do not contain any domain field, will + only match when Compare = no_domain.

    + +

    Example: stop all events with + domain [beam,erlang,otp,sasl|_]

    + + +logger:set_handler_config(h1,filter_default,log). % this is the default +Filter = {fun logger_filters:domain/2,{stop,starts_with,[beam,erlang,otp,sasl]}}. +logger:add_handler_filter(h1,no_sasl,Filter). +ok +
    +
    + + + + Filter log events based on the log level. + +

    This filter provides a way of filtering log events based + on the log level. It matches log events by comparing the + log level with a predefined MatchLevel

    + +

    The Extra parameter is specified when + adding the filter + via + logger:add_logger_filter/2 + or + logger:add_handler_filter/3.

    + +

    The filter compares the value of the event's log level + (Level) to MatchLevel by + calling + logger:compare_levels(Level,MatchLevel) -> CmpRet. It + matches the event if:

    + + + CmpRet = eq and Operator = + eq | lteq | gteq + CmpRet = lt and Operator = + lt | lteq | neq + CmpRet = gt and Operator = + gt | gteq | neq + + +

    If the filter matches and Action = + log, the log event is allowed. If the filter matches + and Action = stop, the log event is + stopped.

    + +

    If the filter does not match, it returns ignore, + meaning that other filters, or the value of the + configuration parameter filter_default, will decide + if the event is allowed or not.

    + +

    Example: only allow debug level log events

    + + +logger:set_handler_config(h1,filter_default,stop). +Filter = {fun logger_filters:level/2,{log,eq,debug}}. +logger:add_handler_filter(h1,debug_only,Filter). +ok +
    +
    + + + + Filter progress reports from supervisor and application_controller. + +

    This filter matches all progress reports + from supervisor and application_controller.

    + +

    If Extra = log, the progress reports + are allowed. If Extra = stop, the + progress reports are stopped.

    + +

    The filter returns ignore for all other log events.

    +
    +
    + + + + Filter events with group leader on remote node. + +

    This filter matches all events originating from a process + that has its group leader on a remote node.

    + +

    If Extra = log, the matching events + are allowed. If Extra = stop, the + matching events are stopped.

    + +

    The filter returns ignore for all other log events.

    +
    +
    + +
    + +
    + + diff --git a/lib/kernel/doc/src/logger_formatter.xml b/lib/kernel/doc/src/logger_formatter.xml new file mode 100644 index 0000000000..213a592e47 --- /dev/null +++ b/lib/kernel/doc/src/logger_formatter.xml @@ -0,0 +1,149 @@ + + + + +
    + + 2017 + Ericsson AB. 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. + + + + logger_formatter + + + + + + + A + logger_formatter.xml +
    + logger_formatter + Default formatter for the Logger application. + + +

    Default formatter for the Logger application.

    +
    + + + + + + + + + + + + + Formats the given message. + +

    Formats the given message.

    +

    The template is a list of atoms, tuples and strings. Atoms + can be level or msg, 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 {key1,key2} will be replaced by the value of + the key2 field in this nested map (the value vill be + converted to a string):

    + + +#{key1=>#{key2=>my_value, + ...}, + ...} + + +

    Strings are printed literally.

    + +

    depth 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 + io:format/2 + in STDLIB.

    + +

    max_size is a positive integer representing the + maximum size a string returned from this formatter can + have. If the formatted string is longer, it will be + truncated.

    + +

    utc is a boolean. If set to true, all dates are + displayed in Universal Coordinated Time. Default + is false.

    + +

    report_cb must be a function with arity 1, + returning {Format,Args}. This function will replace + any report_cb found in metadata.

    + +

    If single_line=true, all newlines in the message are + replaced with ", ", and whitespaces following directly + after newlines are removed. Note that newlines added by the + formatter template are not replaced.

    + +

    If legacy_header=true a header field is added to + logger_formatter's part of Metadata. The value of + this field is a string similar to the header created by the + old error_logger event handlers. It can be included + in the log event by adding the + tuple {logger_formatter,header} to the template.

    + +

    The default template when legacy_header=true is

    + + [{logger_formatter,header},"\n",msg,"\n"] + +

    which will cause log entries like this:

    + + =ERROR REPORT==== 29-Dec-2017::13:30:51.245123 === + process: <0.74.0> + exit_reason: "Something went wrong" + +

    Note that all eight levels might occur here, not + only ERROR, WARNING or INFO. And also + that micro seconds are added at the end of the + timestamp.

    + +

    The default template when single_line=true is

    + + [time," ",level,": ",msg,"\n"] + +

    which will cause log entries like this:

    + + 2017-12-29 13:31:49.640317 error: process: <0.74.0>, exit_reason: "Something went wrong" + +

    The default template when both legacy_header and + single_line are set to false is:

    + + [time," ",level,":\n",msg,"\n"] + +

    which will cause log entries like this:

    + + 2017-12-29 13:32:25.191925 error: + process: <0.74.0> + exit_reason: "Something went wrong" + +
    +
    + +
    + +
    + + diff --git a/lib/kernel/doc/src/logger_std_h.xml b/lib/kernel/doc/src/logger_std_h.xml new file mode 100644 index 0000000000..2a368b16eb --- /dev/null +++ b/lib/kernel/doc/src/logger_std_h.xml @@ -0,0 +1,81 @@ + + + + +
    + + 2017 + Ericsson AB. 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. + + + + logger_std_h + + + + + + + A + logger_std_h.xml +
    + logger_std_h + Default handler for the Logger application. + + +

    This is the default handler for the Logger + application. Multiple instances of this handler can be added to + logger, and each instance will print logs to standard_io, + standard_error or to a file.

    + +

    To add a new instance, + use logger:add_handler/3 + .

    + +

    The handler configuration may contain the following keys, + associated with values as described:

    + + filters + +

    A list of {Id,{Fun,Args}}, each representing a filter + that may selct or modify log events to forward to this + handler.

    + filter_default + +

    The atom log or stop, specifying what to + do with a log event if all filters + return ignore.

    + formatter + +

    {Module,Extra}, + where Module:format(Log,Extra) will be called by + the handler to produce the string that will be printed to + the handler's destination.

    + level + +

    The level of log events that logger shall forward to + this handler. Log events of the specified, or more severe + levels, are forwarded.

    +
    + +
    + + + +
    + + diff --git a/lib/kernel/doc/src/part.xml b/lib/kernel/doc/src/part.xml new file mode 100644 index 0000000000..68eb4530e2 --- /dev/null +++ b/lib/kernel/doc/src/part.xml @@ -0,0 +1,40 @@ + + + + +
    + + 19962017 + Ericsson AB. 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. + + + + Logger User's Guide + OTP Team + + 2017-12-01 + 0.1 + part.xml +
    + +

    The System Architecture Support Libraries SASL application + provides support for alarm handling, release handling, and + related functions.

    +
    + + +
    + diff --git a/lib/kernel/doc/src/ref_man.xml b/lib/kernel/doc/src/ref_man.xml index 5cd77e0f6f..7f4d5b384f 100644 --- a/lib/kernel/doc/src/ref_man.xml +++ b/lib/kernel/doc/src/ref_man.xml @@ -52,6 +52,10 @@ + + + + diff --git a/lib/kernel/doc/src/specs.xml b/lib/kernel/doc/src/specs.xml index 29d52f23bb..a67e26ef16 100644 --- a/lib/kernel/doc/src/specs.xml +++ b/lib/kernel/doc/src/specs.xml @@ -20,6 +20,10 @@ + + + + diff --git a/lib/kernel/include/logger.hrl b/lib/kernel/include/logger.hrl new file mode 100644 index 0000000000..2143ccd297 --- /dev/null +++ b/lib/kernel/include/logger.hrl @@ -0,0 +1,49 @@ +-ifndef(LOGGER_HRL). +-define(LOGGER_HRL,true). +-define(LOG_EMERGENCY(A),?DO_LOG(emergency,[A])). +-define(LOG_EMERGENCY(A,B),?DO_LOG(emergency,[A,B])). +-define(LOG_EMERGENCY(A,B,C),?DO_LOG(emergency,[A,B,C])). + +-define(LOG_ALERT(A),?DO_LOG(alert,[A])). +-define(LOG_ALERT(A,B),?DO_LOG(alert,[A,B])). +-define(LOG_ALERT(A,B,C),?DO_LOG(alert,[A,B,C])). + +-define(LOG_CRITICAL(A),?DO_LOG(critical,[A])). +-define(LOG_CRITICAL(A,B),?DO_LOG(critical,[A,B])). +-define(LOG_CRITICAL(A,B,C),?DO_LOG(critical,[A,B,C])). + +-define(LOG_ERROR(A),?DO_LOG(error,[A])). +-define(LOG_ERROR(A,B),?DO_LOG(error,[A,B])). +-define(LOG_ERROR(A,B,C),?DO_LOG(error,[A,B,C])). + +-define(LOG_WARNING(A),?DO_LOG(warning,[A])). +-define(LOG_WARNING(A,B),?DO_LOG(warning,[A,B])). +-define(LOG_WARNING(A,B,C),?DO_LOG(warning,[A,B,C])). + +-define(LOG_NOTICE(A),?DO_LOG(notice,[A])). +-define(LOG_NOTICE(A,B),?DO_LOG(notice,[A,B])). +-define(LOG_NOTICE(A,B,C),?DO_LOG(notice,[A,B,C])). + +-define(LOG_INFO(A),?DO_LOG(info,[A])). +-define(LOG_INFO(A,B),?DO_LOG(info,[A,B])). +-define(LOG_INFO(A,B,C),?DO_LOG(info,[A,B,C])). + +-define(LOG_DEBUG(A),?DO_LOG(debug,[A])). +-define(LOG_DEBUG(A,B),?DO_LOG(debug,[A,B])). +-define(LOG_DEBUG(A,B,C),?DO_LOG(debug,[A,B,C])). + +-define(LOCATION,#{mfa=>{?MODULE,?FUNCTION_NAME,?FUNCTION_ARITY}, + line=>?LINE, + file=>?FILE}). + +%%%----------------------------------------------------------------- +%%% Internal, i.e. not intended for direct use in code - use above +%%% macros instead! +-define(DO_LOG(Level,Args), + case logger:allow(Level,?MODULE) of + true -> + apply(logger,macro_log,[?LOCATION,Level|Args]); + false -> + ok + end). +-endif. diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile index 0bc9f121a0..702845512c 100644 --- a/lib/kernel/src/Makefile +++ b/lib/kernel/src/Makefile @@ -109,6 +109,17 @@ MODULES = \ kernel_refc \ local_udp \ local_tcp \ + logger \ + logger_backend \ + logger_config \ + logger_std_h \ + logger_disk_log_h \ + logger_h_common \ + logger_filters \ + logger_formatter \ + logger_server \ + logger_simple \ + logger_sup \ net \ net_adm \ net_kernel \ @@ -132,13 +143,14 @@ MODULES = \ HRL_FILES= ../include/file.hrl ../include/inet.hrl ../include/inet_sctp.hrl \ ../include/dist.hrl ../include/dist_util.hrl \ - ../include/net_address.hrl + ../include/net_address.hrl ../include/logger.hrl INTERNAL_HRL_FILES= application_master.hrl disk_log.hrl \ erl_epmd.hrl hipe_ext_format.hrl \ inet_dns.hrl inet_res.hrl \ inet_boot.hrl inet_config.hrl inet_int.hrl \ - inet_dns_record_adts.hrl + inet_dns_record_adts.hrl \ + logger_internal.hrl logger_h_common.hrl ERL_FILES= $(MODULES:%=%.erl) @@ -223,7 +235,7 @@ release_docs_spec: # Include dependencies -- list below added by Kostis Sagonas -$(EBIN)/application_controller.beam: application_master.hrl +$(EBIN)/application_controller.beam: application_master.hrl ../include/logger.hrl $(EBIN)/application_master.beam: application_master.hrl $(EBIN)/auth.beam: ../include/file.hrl $(EBIN)/code.beam: ../include/file.hrl @@ -234,6 +246,7 @@ $(EBIN)/disk_log_server.beam: disk_log.hrl $(EBIN)/dist_util.beam: ../include/dist_util.hrl ../include/dist.hrl $(EBIN)/erl_boot_server.beam: inet_boot.hrl $(EBIN)/erl_epmd.beam: inet_int.hrl erl_epmd.hrl +$(EBIN)/error_logger.beam: logger_internal.hrl ../include/logger.hrl $(EBIN)/file.beam: ../include/file.hrl file_int.hrl $(EBIN)/file_io_server.beam: ../include/file.hrl file_int.hrl $(EBIN)/gen_tcp.beam: inet_int.hrl @@ -259,6 +272,16 @@ $(EBIN)/inet_udp.beam: inet_int.hrl $(EBIN)/inet_sctp.beam: inet_int.hrl ../include/inet_sctp.hrl $(EBIN)/local_udp.beam: inet_int.hrl $(EBIN)/local_tcp.beam: inet_int.hrl +$(EBIN)/logger.beam: logger_internal.hrl ../include/logger.hrl +$(EBIN)/logger_backend.beam: logger_internal.hrl ../include/logger.hrl +$(EBIN)/logger_config.beam: logger_internal.hrl ../include/logger.hrl +$(EBIN)/logger_disk_log_h.beam: logger_h_common.hrl logger_internal.hrl ../include/logger.hrl ../include/file.hrl +$(EBIN)/logger_filters.beam: logger_internal.hrl ../include/logger.hrl +$(EBIN)/logger_formatter.beam: logger_internal.hrl ../include/logger.hrl +$(EBIN)/logger_server.beam: logger_internal.hrl ../include/logger.hrl +$(EBIN)/logger_simple.beam: logger_internal.hrl ../include/logger.hrl +$(EBIN)/logger_std_h.beam: logger_h_common.hrl logger_internal.hrl ../include/logger.hrl ../include/file.hrl +$(EBIN)/logger_h_common.beam: logger_h_common.hrl logger_internal.hrl ../include/logger.hrl $(EBIN)/net_kernel.beam: ../include/net_address.hrl $(EBIN)/os.beam: ../include/file.hrl $(EBIN)/ram_file.beam: ../include/file.hrl diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 82a3571da9..afffcd156e 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -60,6 +60,17 @@ kernel_refc, local_tcp, local_udp, + logger, + logger_backend, + logger_config, + logger_disk_log_h, + logger_filters, + logger_formatter, + logger_h_common, + logger_server, + logger_simple, + logger_std_h, + logger_sup, net, net_adm, net_kernel, @@ -117,6 +128,8 @@ kernel_config, kernel_refc, kernel_sup, + logger, + logger_sup, net_kernel, net_sup, rex, @@ -127,7 +140,7 @@ inet_db, pg2]}, {applications, []}, - {env, [{error_logger, tty}]}, + {env, []}, {mod, {kernel, []}}, {runtime_dependencies, ["erts-10.0", "stdlib-3.5", "sasl-3.0"]} ] diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl new file mode 100644 index 0000000000..7606912da4 --- /dev/null +++ b/lib/kernel/src/logger.erl @@ -0,0 +1,799 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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). + +%% Log interface +-export([emergency/1,emergency/2,emergency/3, + alert/1,alert/2,alert/3, + critical/1,critical/2,critical/3, + error/1,error/2,error/3, + warning/1,warning/2,warning/3, + notice/1,notice/2,notice/3, + info/1,info/2,info/3, + debug/1,debug/2,debug/3]). +-export([log/2,log/3,log/4]). + +%% Called by macro +-export([allow/2,macro_log/3,macro_log/4,macro_log/5,add_default_metadata/1]). + +%% Configuration +-export([add_handler/3, remove_handler/1, + add_logger_filter/2, add_handler_filter/3, + remove_logger_filter/1, remove_handler_filter/2, + 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]). + +%% Misc +-export([compare_levels/2]). +-export([set_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 +-export([format_report/1, format_otp_report/1]). + +-export([internal_log/2,filter_stacktrace/2]). + +-include("logger_internal.hrl"). +-include("logger.hrl"). + +%%%----------------------------------------------------------------- +%%% Types +-type log() :: #{level=>level(), + msg=>{io:format(),[term()]} | + {report,report()} | + {string,unicode:chardata()}, + 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 handler_id() :: atom(). +-type filter_id() :: atom(). +-type filter() :: {fun((log(),term()) -> filter_return()),term()}. +-type filter_return() :: stop | ignore | log(). +-type config() :: map(). + +-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]). + +%%%----------------------------------------------------------------- +%%% API +emergency(X) -> + log(emergency,X). +emergency(X,Y) -> + log(emergency,X,Y). +emergency(X,Y,Z) -> + log(emergency,X,Y,Z). + +alert(X) -> + log(alert,X). +alert(X,Y) -> + log(alert,X,Y). +alert(X,Y,Z) -> + log(alert,X,Y,Z). + +critical(X) -> + log(critical,X). +critical(X,Y) -> + log(critical,X,Y). +critical(X,Y,Z) -> + log(critical,X,Y,Z). + +error(X) -> + log(error,X). +error(X,Y) -> + log(error,X,Y). +error(X,Y,Z) -> + log(error,X,Y,Z). + +warning(X) -> + log(warning,X). +warning(X,Y) -> + log(warning,X,Y). +warning(X,Y,Z) -> + log(warning,X,Y,Z). + +notice(X) -> + log(notice,X). +notice(X,Y) -> + log(notice,X,Y). +notice(X,Y,Z) -> + log(notice,X,Y,Z). + +info(X) -> + log(info,X). +info(X,Y) -> + log(info,X,Y). +info(X,Y,Z) -> + log(info,X,Y,Z). + +debug(X) -> + log(debug,X). +debug(X,Y) -> + log(debug,X,Y). +debug(X,Y,Z) -> + log(debug,X,Y,Z). + +-spec log(Level,StringOrReport) -> ok when + Level :: level(), + StringOrReport :: unicode:chardata() | report(). +log(Level, StringOrReport) -> + do_log(Level,StringOrReport,#{}). + +-spec log(Level,StringOrReport,Metadata) -> ok when + Level :: level(), + StringOrReport :: unicode:chardata() | report(), + Metadata :: metadata(); + (Level,Format,Args) -> ok when + Level :: level(), + Format :: io:format(), + Args ::[term()]; + (Level,Fun,FunArgs) -> ok when + Level :: level(), + Fun :: msg_fun(), + FunArgs :: term(). +log(Level, StringOrReport, Metadata) + when is_map(Metadata), not is_function(StringOrReport) -> + do_log(Level,StringOrReport,Metadata); +log(Level, FunOrFormat, Args) -> + do_log(Level,{FunOrFormat,Args},#{}). + +-spec log(Level,Format, Args, Metadata) -> ok when + Level :: level(), + Format :: io:format(), + Args :: [term()], + Metadata :: metadata(); + (Level,Fun,FunArgs,Metadata) -> ok when + Level :: level(), + Fun :: msg_fun(), + FunArgs :: term(), + Metadata :: metadata(). +log(Level, FunOrFormat, Args, Metadata) -> + do_log(Level,{FunOrFormat,Args},Metadata). + +-spec allow(Level,Module) -> boolean() when + Level :: level(), + Module :: module(). +allow(Level,Module) when ?IS_LEVEL(Level), is_atom(Module) -> + logger_config:allow(?LOGGER_TABLE,Level,Module). + + +-spec macro_log(Location,Level,StringOrReport) -> ok when + Location :: map(), + 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(), + Level :: level(), + StringOrReport :: unicode:chardata() | report(), + Meta :: metadata(); + (Location,Level,Format,Args) -> ok when + Location :: map(), + Level :: level(), + Format :: io:format(), + Args ::[term()]; + (Location,Level,Fun,FunArgs) -> ok when + Location :: map(), + Level :: level(), + Fun :: msg_fun(), + FunArgs :: term(). +macro_log(Location,Level,StringOrReport,Meta) + when is_map(Meta), not is_function(StringOrReport) -> + log_allowed(Location,Level,StringOrReport,Meta); +macro_log(Location,Level,FunOrFormat,Args) -> + log_allowed(Location,Level,{FunOrFormat,Args},#{}). + +-spec macro_log(Location,Level,Format,Args,Meta) -> ok when + Location :: map(), + Level :: level(), + Format :: io:format(), + Args ::[term()], + Meta :: metadata(); + (Location,Level,Fun,FunArgs,Meta) -> ok when + Location :: map(), + Level :: level(), + Fun :: msg_fun(), + FunArgs :: term(), + Meta :: metadata(). +macro_log(Location,Level,FunOrFormat,Args,Meta) -> + log_allowed(Location,Level,{FunOrFormat,Args},Meta). + +-spec format_otp_report(Report) -> FormatArgs when + Report :: report(), + FormatArgs :: {io:format(),[term()]}. +format_otp_report(#{label:=_,report:=Report}) -> + format_report(Report); +format_otp_report(Report) -> + format_report(Report). + +-spec format_report(Report) -> FormatArgs when + Report :: report(), + FormatArgs :: {io:format(),[term()]}. +format_report(Report) when is_map(Report) -> + format_report(maps:to_list(Report)); +format_report(Report) when is_list(Report) -> + case lists:flatten(Report) of + [] -> + {"~tp",[[]]}; + FlatList -> + case string_p1(FlatList) of + true -> + {"~ts",[FlatList]}; + false -> + format_term_list(Report,[],[]) + end + end; +format_report(Report) -> + {"~tp",[Report]}. + +format_term_list([{Tag,Data}|T],Format,Args) -> + PorS = case string_p(Data) of + true -> "s"; + false -> "p" + end, + format_term_list(T,[" ~tp: ~t"++PorS|Format],[Data,Tag|Args]); +format_term_list([Data|T],Format,Args) -> + format_term_list(T,[" ~tp"|Format],[Data|Args]); +format_term_list([],Format,Args) -> + {lists:flatten(lists:join($\n,lists:reverse(Format))),lists:reverse(Args)}. + +string_p(List) when is_list(List) -> + string_p1(lists:flatten(List)); +string_p(_) -> + false. + +string_p1([]) -> + false; +string_p1(FlatList) -> + io_lib:printable_unicode_list(FlatList). + +internal_log(Level,Term) when is_atom(Level) -> + erlang:display_string("Logger - "++ atom_to_list(Level) ++ ": "), + erlang:display(Term). + +%%%----------------------------------------------------------------- +%%% Configuration +-spec add_logger_filter(FilterId,Filter) -> ok | {error,term()} when + FilterId :: filter_id(), + Filter :: filter(). +add_logger_filter(FilterId,Filter) -> + logger_server:add_filter(logger,{FilterId,Filter}). + +-spec add_handler_filter(HandlerId,FilterId,Filter) -> ok | {error,term()} when + HandlerId :: handler_id(), + FilterId :: filter_id(), + Filter :: filter(). +add_handler_filter(HandlerId,FilterId,Filter) -> + logger_server:add_filter(HandlerId,{FilterId,Filter}). + + +-spec remove_logger_filter(FilterId) -> ok | {error,term()} when + FilterId :: filter_id(). +remove_logger_filter(FilterId) -> + logger_server:remove_filter(logger,FilterId). + +-spec remove_handler_filter(HandlerId,FilterId) -> ok | {error,term()} when + HandlerId :: handler_id(), + FilterId :: filter_id(). +remove_handler_filter(HandlerId,FilterId) -> + logger_server:remove_filter(HandlerId,FilterId). + +-spec add_handler(HandlerId,Module,Config) -> ok | {error,term()} when + HandlerId :: handler_id(), + Module :: module(), + Config :: config(). +add_handler(HandlerId,Module,Config) -> + logger_server:add_handler(HandlerId,Module,Config). + +-spec remove_handler(HandlerId) -> ok | {error,term()} when + HandlerId :: handler_id(). +remove_handler(HandlerId) -> + logger_server:remove_handler(HandlerId). + +-spec set_logger_config(Key,Value) -> ok | {error,term()} when + Key :: atom(), + Value :: term(). +set_logger_config(Key,Value) -> + logger_server:set_config(logger,Key,Value). + +-spec set_logger_config(Config) -> ok | {error,term()} when + Config :: config(). +set_logger_config(Config) -> + logger_server:set_config(logger,Config). + +-spec set_handler_config(HandlerId,Key,Value) -> ok | {error,term()} when + HandlerId :: handler_id(), + Key :: atom(), + Value :: term(). +set_handler_config(HandlerId,Key,Value) -> + logger_server:set_config(HandlerId,Key,Value). + +-spec set_handler_config(HandlerId,Config) -> ok | {error,term()} when + HandlerId :: handler_id(), + Config :: config(). +set_handler_config(HandlerId,Config) -> + logger_server:set_config(HandlerId,Config). + +-spec get_logger_config() -> {ok,Config} when + Config :: config(). +get_logger_config() -> + logger_config:get(?LOGGER_TABLE,logger). + +-spec get_handler_config(HandlerId) -> {ok,{Module,Config}} | {error,term()} when + HandlerId :: handler_id(), + Module :: module(), + Config :: config(). +get_handler_config(HandlerId) -> + logger_config:get(?LOGGER_TABLE,HandlerId). + +-spec set_module_level(Module,Level) -> ok | {error,term()} when + Module :: module(), + Level :: level(). +set_module_level(Module,Level) -> + logger_server:set_module_level(Module,Level). + +-spec reset_module_level(Module) -> ok | {error,term()} when + Module :: module(). +reset_module_level(Module) -> + logger_server:reset_module_level(Module). + +%%%----------------------------------------------------------------- +%%% Misc +-spec compare_levels(Level1,Level2) -> eq | gt | lt when + Level1 :: level(), + Level2 :: level(). +compare_levels(Level,Level) when ?IS_LEVEL(Level) -> + eq; +compare_levels(Level1,Level2) when ?IS_LEVEL(Level1), ?IS_LEVEL(Level2) -> + Int1 = logger_config:level_to_int(Level1), + Int2 = logger_config:level_to_int(Level2), + if Int1 < Int2 -> gt; + true -> lt + end; +compare_levels(Level1,Level2) -> + erlang:error(badarg,[Level1,Level2]). + +-spec set_process_metadata(Meta) -> ok when + Meta :: metadata(). +set_process_metadata(Meta) when is_map(Meta) -> + _ = put(?LOGGER_META_KEY,Meta), + ok; +set_process_metadata(Meta) -> + erlang:error(badarg,[Meta]). + +-spec get_process_metadata() -> Meta | undefined when + Meta :: metadata(). +get_process_metadata() -> + get(?LOGGER_META_KEY). + +-spec unset_process_metadata() -> ok. +unset_process_metadata() -> + _ = erase(?LOGGER_META_KEY), + ok. + +-spec i() -> #{logger=>config(), + handlers=>[{handler_id(),module(),config()}], + module_levels=>[{module(),level()}]}. +i() -> + i(term). + +-spec i(term) -> #{logger=>config(), + handlers=>[{handler_id(),module(),config()}], + module_levels=>[{module(),level()}]}; + (print) -> ok; + (string) -> iolist(). +i(_Action = print) -> + io:put_chars(i(string)); +i(_Action = string) -> + #{logger := #{level := Level, handlers := Handlers, + filters := Filters, filter_default := FilterDefault}, + handlers := HandlerConfigs, + module_levels := Modules} = i(term), + [io_lib:format("Current logger configuration:~n", []), + io_lib:format(" Level: ~p~n",[Level]), + io_lib:format(" Filter Default: ~p~n", [FilterDefault]), + io_lib:format(" Filters: ~n", []), + print_filters(4, Filters), + io_lib:format(" Handlers: ~n", []), + print_handlers([C || {Id, _, _} = C <- HandlerConfigs, + lists:member(Id, Handlers)]), + io_lib:format(" Level set per module: ~n", []), + print_module_levels(Modules) + ]; +i(_Action = term) -> + {Logger, Handlers, Modules} = logger_config:get(tid()), + #{logger=>Logger, + handlers=>Handlers, + module_levels=>Modules}. + +print_filters(Indent, {Id, {Fun, Config}}) -> + io_lib:format("~sId: ~p~n" + "~s Fun: ~p~n" + "~s Config: ~p~n",[Indent, Id, Indent, Fun, Indent, Config]); +print_filters(Indent, Filters) -> + IndentStr = io_lib:format("~.*s",[Indent, ""]), + lists:map(fun(Filter) ->print_filters(IndentStr, Filter) end, Filters). + + +print_handlers({Id,Module, + #{level := Level, + filters := Filters, filter_default := FilterDefault, + formatter := {FormatterModule,FormatterConfig}} = Config}) -> + MyKeys = [filter_default, filters, formatter, level, id], + UnhandledConfig = maps:filter(fun(Key, _) -> + not lists:member(Key, MyKeys) + end, Config), + Unhandled = lists:map(fun({Key, Value}) -> + io_lib:format(" ~p: ~p~n",[Key, Value]) + end, maps:to_list(UnhandledConfig)), + io_lib:format(" Id: ~p~n" + " Module: ~p~n" + " Level: ~p~n" + " Formatter:~n" + " Module: ~p~n" + " Config: ~p~n" + " Filter Default: ~p~n" + " Filters:~n~s" + " Handler Config:~n" + "~s" + "",[Id, Module, Level, FormatterModule, FormatterConfig, + FilterDefault, print_filters(8, Filters), Unhandled]); +print_handlers(Handlers) -> + lists:map(fun print_handlers/1, Handlers). + +print_module_levels({Module,Level}) -> + io_lib:format(" Module: ~p~n" + " Level: ~p~n", + [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 + 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 + end. + +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 + {ok, tty} -> + {ok, tty}; + {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 + {ok, Bad} -> + {error,{bad_config, {kernel, {logger_dest, 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 + end. + +get_logger_filters() -> + case application:get_env(kernel, logger_sasl_compatible, false) of + true -> + ?DEFAULT_HANDLER_FILTERS([beam,erlang,otp]); + false -> + Extra = + case application:get_env(kernel, logger_log_progress, false) of + true -> + []; + false -> + [{stop_progress, + {fun logger_filters:progress/2,stop}}] + end, + 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}}. + +%%%----------------------------------------------------------------- +-spec limit_term(term()) -> term(). + +limit_term(Term) -> + case get_format_depth() of + unlimited -> Term; + D -> io_lib:limit_term(Term, D) + end. + +-spec get_format_depth() -> 'unlimited' | pos_integer(). + +get_format_depth() -> + Depth = + case application:get_env(kernel, logger_format_depth) of + {ok, D} when is_integer(D) -> + D; + undefined -> + case application:get_env(kernel, error_logger_format_depth) of + {ok, D} when is_integer(D) -> + D; + undefined -> + unlimited + end + end, + max(10, Depth). + +-spec get_max_size() -> 'unlimited' | pos_integer(). + +get_max_size() -> + case application:get_env(kernel, logger_max_size) of + {ok, Size} when is_integer(Size) -> + max(50, Size); + undefined -> + unlimited + end. + +-spec get_utc_config() -> boolean(). + +get_utc_config() -> + %% Kernel's logger_utc configuration overrides SASL utc_log, which + %% in turn overrides stdlib config - in order to have uniform + %% timestamps in log messages + case application:get_env(kernel, logger_utc) of + {ok, Val} -> Val; + undefined -> + case application:get_env(sasl, utc_log) of + {ok, Val} -> Val; + undefined -> + case application:get_env(stdlib, utc_log) of + {ok, Val} -> Val; + undefined -> false + end + end + end. + +%%%----------------------------------------------------------------- +%%% Internal +do_log(warning,Msg,Meta) -> + do_log_1(error_logger:warning_map(),Msg,Meta); +do_log(Level,Msg,Meta) -> + do_log_1(Level,Msg,Meta). + +do_log_1(Level,Msg,#{mfa:={Module,_,_}}=Meta) -> + case logger_config:allow(?LOGGER_TABLE,Level,Module) of + true -> + log_allowed(#{},Level,Msg,Meta); + false -> + ok + end; +do_log_1(Level,Msg,Meta) -> + case logger_config:allow(?LOGGER_TABLE,Level) of + true -> + log_allowed(#{},Level,Msg,Meta); + false -> + ok + end. + +-spec log_allowed(Location,Level,Msg,Meta) -> ok when + Location :: map(), + Level :: level(), + Msg :: {msg_fun(),term()} | + {io:format(),[term()]} | + report() | + unicode:chardata(), + Meta :: metadata(). +log_allowed(Location,Level,{Fun,FunArgs},Meta) when is_function(Fun,1) -> + try Fun(FunArgs) of + Msg={Format,Args} when is_list(Format), is_list(Args) -> + log_allowed(Location,Level,Msg,Meta); + Report when ?IS_REPORT(Report) -> + log_allowed(Location,Level,Report,Meta); + String when ?IS_STRING(String) -> + log_allowed(Location,Level,String,Meta); + Other -> + log_allowed(Location,Level, + {"LAZY_FUN ERROR: ~tp; Returned: ~tp", + [{Fun,FunArgs},Other]}, + Meta) + catch C:R -> + log_allowed(Location,Level, + {"LAZY_FUN CRASH: ~tp; Reason: ~tp", + [{Fun,FunArgs},{C,R}]}, + Meta) + end; +log_allowed(Location,Level,Msg,Meta0) when is_map(Meta0) -> + %% Metadata priorities are: + %% Location (added in API macros) - will be overwritten by process + %% metadata (set by set_process_metadata/1), which in turn will be + %% overwritten by the metadata given as argument in the log call + %% (function or macro). + Meta = add_default_metadata( + maps:merge(Location,maps:merge(proc_meta(),Meta0))), + case node(maps:get(gl,Meta)) of + Node when Node=/=node() -> + log_remote(Node,Level,Msg,Meta), + do_log_allowed(Level,Msg,Meta); + _ -> + do_log_allowed(Level,Msg,Meta) + end. + +do_log_allowed(Level,{Format,Args}=Msg,Meta) + when ?IS_LEVEL(Level), + is_list(Format), + is_list(Args), + is_map(Meta) -> + logger_backend:log_allowed(#{level=>Level,msg=>Msg,meta=>Meta},tid()); +do_log_allowed(Level,Report,Meta) + when ?IS_LEVEL(Level), + ?IS_REPORT(Report), + is_map(Meta) -> + logger_backend:log_allowed(#{level=>Level,msg=>{report,Report},meta=>Meta}, + tid()); +do_log_allowed(Level,String,Meta) + when ?IS_LEVEL(Level), + ?IS_STRING(String), + is_map(Meta) -> + logger_backend:log_allowed(#{level=>Level,msg=>{string,String},meta=>Meta}, + tid()). +tid() -> + ets:whereis(?LOGGER_TABLE). + +log_remote(Node,Level,{Format,Args},Meta) -> + log_remote(Node,{log,Level,Format,Args,Meta}); +log_remote(Node,Level,Msg,Meta) -> + log_remote(Node,{log,Level,Msg,Meta}). + +log_remote(Node,Request) -> + {logger,Node} ! Request, + ok. + +add_default_metadata(Meta) -> + add_default_metadata([pid,gl,time],Meta). + +add_default_metadata([Key|Keys],Meta) -> + case maps:is_key(Key,Meta) of + true -> + add_default_metadata(Keys,Meta); + false -> + add_default_metadata(Keys,Meta#{Key=>default(Key)}) + end; +add_default_metadata([],Meta) -> + Meta. + +proc_meta() -> + case get_process_metadata() of + ProcMeta when is_map(ProcMeta) -> ProcMeta; + _ -> #{} + end. + +default(pid) -> self(); +default(gl) -> group_leader(); +default(time) -> erlang:monotonic_time(microsecond). + +%% Remove everything upto and including this module from the stacktrace +filter_stacktrace(Module,[{Module,_,_,_}|_]) -> + []; +filter_stacktrace(Module,[H|T]) -> + [H|filter_stacktrace(Module,T)]; +filter_stacktrace(_,[]) -> + []. diff --git a/lib/kernel/src/logger_backend.erl b/lib/kernel/src/logger_backend.erl new file mode 100644 index 0000000000..d9f5aa6faf --- /dev/null +++ b/lib/kernel/src/logger_backend.erl @@ -0,0 +1,133 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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_backend). + +-export([log_allowed/2]). + +-include("logger_internal.hrl"). + +-define(OWN_KEYS,[level,filters,filter_default,handlers]). + +%%%----------------------------------------------------------------- +%%% The default logger backend +log_allowed(Log, Tid) -> + {ok,Config} = logger_config:get(Tid,logger), + Filters = maps:get(filters,Config,[]), + case apply_filters(logger,Log,Filters,Config) of + stop -> + ok; + Log1 -> + Handlers = maps:get(handlers,Config,[]), + call_handlers(Log1,Handlers,Tid) + end, + ok. + +call_handlers(#{level:=Level}=Log,[Id|Handlers],Tid) -> + case logger_config:get(Tid,Id,Level) of + {ok,{Module,Config}} -> + Filters = maps:get(filters,Config,[]), + case apply_filters(Id,Log,Filters,Config) of + stop -> + ok; + Log1 -> + Config1 = maps:without(?OWN_KEYS,Config), + try Module:log(Log1,Config1) + catch C:R:S -> + case logger:remove_handler(Id) of + ok -> + logger:internal_log( + error,{removed_failing_handler,Id}), + ?LOG_INTERNAL( + debug, + [{logger,removed_failing_handler}, + {handler,{Id,Module}}, + {log,Log1}, + {config,Config1}, + {reason,{C,R,filter_stacktrace(S)}}]); + {error,{not_found,_}} -> + %% Probably already removed by other client + %% Don't report again + ok; + {error,Reason} -> + ?LOG_INTERNAL( + debug, + [{logger,remove_handler_failed}, + {reason,Reason}]) + end + end + end; + _ -> + ok + end, + call_handlers(Log,Handlers,Tid); +call_handlers(_Log,[],_Tid) -> + ok. + +apply_filters(Owner,Log,Filters,Config) -> + case do_apply_filters(Owner,Log,Filters,ignore) of + stop -> + stop; + ignore -> + case maps:get(filter_default,Config) of + log -> + Log; + stop -> + stop + end; + Log1 -> + Log1 + end. + +do_apply_filters(Owner,Log,[{_Id,{FilterFun,FilterArgs}}=Filter|Filters],State) -> + try FilterFun(Log,FilterArgs) of + stop -> + stop; + ignore -> + do_apply_filters(Owner,Log,Filters,State); + Log1=#{level:=Level,msg:=Msg,meta:=Meta} + when is_atom(Level), ?IS_MSG(Msg), is_map(Meta) -> + do_apply_filters(Owner,Log1,Filters,log); + Bad -> + handle_filter_failed(Filter,Owner,Log,{bad_return_value,Bad}) + catch C:R:S -> + handle_filter_failed(Filter,Owner,Log,{C,R,filter_stacktrace(S)}) + end; +do_apply_filters(_Owner,_Log,[],ignore) -> + ignore; +do_apply_filters(_Owner,Log,[],log) -> + Log. + +handle_filter_failed({Id,_}=Filter,Owner,Log,Reason) -> + case logger_server:remove_filter(Owner,Id) of + ok -> + logger:internal_log(error,{removed_failing_filter,Id}), + ?LOG_INTERNAL(debug, + [{logger,removed_failing_filter}, + {filter,Filter}, + {owner,Owner}, + {log,Log}, + {reason,Reason}]); + _ -> + ok + end, + ignore. + +filter_stacktrace(Stacktrace) -> + logger:filter_stacktrace(?MODULE,Stacktrace). diff --git a/lib/kernel/src/logger_config.erl b/lib/kernel/src/logger_config.erl new file mode 100644 index 0000000000..799aea9617 --- /dev/null +++ b/lib/kernel/src/logger_config.erl @@ -0,0 +1,151 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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_config). + +-export([new/1,delete/2, + exist/2, + allow/2,allow/3, + get/2, get/3, get/1, + create/3, create/4, set/3, + set_module_level/3,reset_module_level/2, + cache_module_level/2, + level_to_int/1]). + +-include("logger_internal.hrl"). + +new(Name) -> + _ = ets:new(Name,[set,protected,named_table]), + ets:whereis(Name). + +delete(Tid,Id) -> + ets:delete(Tid,table_key(Id)). + +allow(Tid,Level,Module) -> + LevelInt = level_to_int(Level), + case ets:lookup(Tid,Module) of + [{Module,{ModLevel,cached}}] when is_integer(ModLevel), + LevelInt =< ModLevel -> + true; + [{Module,ModLevel}] when is_integer(ModLevel), + LevelInt =< ModLevel -> + true; + [] -> + logger_server:cache_module_level(Module), + allow(Tid,Level); + _ -> + false + end. + +allow(Tid,Level) -> + GlobalLevelInt = ets:lookup_element(Tid,?LOGGER_KEY,2), + level_to_int(Level) =< GlobalLevelInt. + +exist(Tid,What) -> + ets:member(Tid,table_key(What)). + +get(Tid,What) -> + case ets:lookup(Tid,table_key(What)) of + [{_,_,Config}] -> + {ok,Config}; + [{_,_,Config,Module}] -> + {ok,{Module,Config}}; + [] -> + {error,{not_found,What}} + end. + +get(Tid,What,Level) -> + MS = [{{table_key(What),'$1','$2'}, % logger config + [{'>=','$1',level_to_int(Level)}], + ['$2']}, + {{table_key(What),'$1','$2','$3'}, % handler config + [{'>=','$1',level_to_int(Level)}], + [{{'$3','$2'}}]}], + case ets:select(Tid,MS) of + [] -> error; + [Data] -> {ok,Data} + end. + +create(Tid,What,Module,Config) -> + LevelInt = level_to_int(maps:get(level,Config)), + ets:insert(Tid,{table_key(What),LevelInt,Config,Module}). +create(Tid,What,Config) -> + LevelInt = level_to_int(maps:get(level,Config)), + ets:insert(Tid,{table_key(What),LevelInt,Config}). + +set(Tid,What,Config) -> + LevelInt = level_to_int(maps:get(level,Config)), + %% Should do this only if the level has actually changed. Possibly + %% overwrite instead of delete? + case What of + logger -> + _ = ets:select_delete(Tid,[{{'_',{'$1',cached}}, + [{'=/=','$1',LevelInt}], + [true]}]), + ok; + _ -> + ok + end, + ets:update_element(Tid,table_key(What),[{2,LevelInt},{3,Config}]), + ok. + +set_module_level(Tid,Module,Level) -> + ets:insert(Tid,{Module,level_to_int(Level)}), + ok. + +reset_module_level(Tid,Module) -> + ets:delete(Tid,Module), % should possibley overwrite instead of delete? + ok. + +cache_module_level(Tid,Module) -> + GlobalLevelInt = ets:lookup_element(Tid,?LOGGER_KEY,2), + ets:insert_new(Tid,{Module,{GlobalLevelInt,cached}}), + ok. + +get(Tid) -> + {ok,Logger} = get(Tid,logger), + HMS = [{{table_key('$1'),'_','$2','$3'},[],[{{'$1','$3','$2'}}]}], + Handlers = ets:select(Tid,HMS), + MMS = [{{'$1','$2'},[{is_atom,'$1'},{is_integer,'$2'}],[{{'$1','$2'}}]}], + Modules = ets:select(Tid,MMS), + {Logger,Handlers,[{M,int_to_level(L)} || {M,L} <- Modules]}. + +level_to_int(emergency) -> ?EMERGENCY; +level_to_int(alert) -> ?ALERT; +level_to_int(critical) -> ?CRITICAL; +level_to_int(error) -> ?ERROR; +level_to_int(warning) -> ?WARNING; +level_to_int(notice) -> ?NOTICE; +level_to_int(info) -> ?INFO; +level_to_int(debug) -> ?DEBUG. + +int_to_level(?EMERGENCY) -> emergency; +int_to_level(?ALERT) -> alert; +int_to_level(?CRITICAL) -> critical; +int_to_level(?ERROR) -> error; +int_to_level(?WARNING) -> warning; +int_to_level(?NOTICE) -> notice; +int_to_level(?INFO) -> info; +int_to_level(?DEBUG) -> debug. + +%%%----------------------------------------------------------------- +%%% Internal + +table_key(logger) -> ?LOGGER_KEY; +table_key(HandlerId) -> {?HANDLER_KEY,HandlerId}. diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl new file mode 100644 index 0000000000..3b71f936d8 --- /dev/null +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -0,0 +1,694 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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_disk_log_h). + +-behaviour(gen_server). + +-include("logger.hrl"). +-include("logger_internal.hrl"). +-include("logger_h_common.hrl"). + +%%% API +-export([start_link/3, info/1, disk_log_sync/1, reset/1]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +%% logger callbacks +-export([log/2, + adding_handler/2, removing_handler/1, + changing_config/3, swap_buffer/2]). + +%%%=================================================================== +%%% API +%%%=================================================================== + +%%%----------------------------------------------------------------- +%%% Start a disk_log handler process and link to caller. +%%% This function is called by the kernel supervisor when this +%%% handler process gets added (as a result of calling add/3). +-spec start_link(Name, Config, HandlerState) -> {ok,Pid} | {error,Reason} when + Name :: atom(), + Config :: logger:config(), + HandlerState :: map(), + Pid :: pid(), + Reason :: term(). + +start_link(Name, Config, HandlerState) -> + proc_lib:start_link(?MODULE,init,[[Name,Config,HandlerState]]). + +%%%----------------------------------------------------------------- +%%% +-spec disk_log_sync(Name) -> ok | {error,Reason} when + Name :: atom(), + Reason :: handler_busy | {badarg,term()}. + +disk_log_sync(Name) when is_atom(Name) -> + try + gen_server:call(Name, disk_log_sync, ?DEFAULT_CALL_TIMEOUT) + catch + _:{timeout,_} -> {error,handler_busy} + end; +disk_log_sync(Name) -> + {error,{badarg,{disk_log_sync,[Name]}}}. + +%%%----------------------------------------------------------------- +%%% +-spec info(Name) -> Info | {error,Reason} when + Name :: atom(), + Info :: term(), + Reason :: handler_busy | {badarg,term()}. + +info(Name) when is_atom(Name) -> + try + gen_server:call(Name, info, ?DEFAULT_CALL_TIMEOUT) + catch + _:{timeout,_} -> {error,handler_busy} + end; +info(Name) -> + {error,{badarg,{info,[Name]}}}. + +%%%----------------------------------------------------------------- +%%% +-spec reset(Name) -> ok | {error,Reason} when + Name :: atom(), + Reason :: handler_busy | {badarg,term()}. + +reset(Name) when is_atom(Name) -> + try + gen_server:call(Name, reset, ?DEFAULT_CALL_TIMEOUT) + catch + _:{timeout,_} -> {error,handler_busy} + end; +reset(Name) -> + {error,{badarg,{reset,[Name]}}}. + + +%%%=================================================================== +%%% logger callbacks +%%%=================================================================== + +%%%----------------------------------------------------------------- +%%% Handler being added +adding_handler(Name, Config) -> + case check_config(adding, Name, Config) of + {ok, Config1} -> + %% create initial handler state by merging defaults with config + HConfig = maps:get(?MODULE, Config1, #{}), + HState = maps:merge(get_init_state(), HConfig), + case logger_h_common:overload_levels_ok(HState) of + true -> + case start(Name, Config1, HState) of + ok -> + %% Make sure wait_for_buffer is not stored, so we + %% won't hang and wait for buffer on a restart + {ok, maps:remove(wait_for_buffer,Config1)}; + Error -> + Error + end; + false -> + #{toggle_sync_qlen := TSQL, + drop_new_reqs_qlen := DNRQL, + flush_reqs_qlen := FRQL} = HState, + {error,{invalid_levels,{TSQL,DNRQL,FRQL}}} + end; + Error -> + Error + end. + +%%%----------------------------------------------------------------- +%%% Updating handler config +changing_config(Name, + OldConfig=#{id:=Id, disk_log_opts:=DLOpts}, + NewConfig=#{id:=Id, disk_log_opts:=DLOpts}) -> + case check_config(changing, Name, NewConfig) of + Result = {ok,NewConfig1} -> + try gen_server:call(Name, {change_config,OldConfig,NewConfig1}, + ?DEFAULT_CALL_TIMEOUT) of + ok -> Result; + HError -> HError + catch + _:{timeout,_} -> {error,handler_busy} + end; + Error -> + Error + end; +changing_config(_Name, OldConfig, NewConfig) -> + {error,{illegal_config_change,OldConfig,NewConfig}}. + +check_config(adding, Name, Config0) -> + %% Merge in defaults on top level + Config = maps:merge(#{id => Name}, Config0), + %% Merge in defaults on handler level + LogOpts0 = maps:get(disk_log_opts, Config, #{}), + LogOpts = merge_default_logopts(Name, LogOpts0), + case check_log_opts(maps:to_list(LogOpts)) of + ok -> + MyConfig = maps:get(?MODULE, Config, #{}), + case check_my_config(maps:to_list(MyConfig)) of + ok -> + {ok,Config#{disk_log_opts=>LogOpts, + ?MODULE=>MyConfig}}; + Error -> + Error + end; + Error -> + Error + end; +check_config(changing, _Name, Config) -> + MyConfig = maps:get(?MODULE, Config, #{}), + case check_my_config(maps:to_list(MyConfig)) of + ok -> {ok,Config}; + Error -> Error + end. + +merge_default_logopts(Name, LogOpts) -> + Type = maps:get(type, LogOpts, wrap), + {DefaultNoFiles,DefaultNoBytes} = + case Type of + halt -> {undefined,infinity}; + _wrap -> {10,1048576} + end, + {ok,Dir} = file:get_cwd(), + Default = #{file => filename:join(Dir,Name), + max_no_files => DefaultNoFiles, + max_no_bytes => DefaultNoBytes, + type => Type}, + maps:merge(Default,LogOpts). + +check_log_opts([{file,File}|Opts]) when is_list(File) -> + check_log_opts(Opts); +check_log_opts([{max_no_files,undefined}|Opts]) -> + check_log_opts(Opts); +check_log_opts([{max_no_files,N}|Opts]) when is_integer(N), N>0 -> + check_log_opts(Opts); +check_log_opts([{max_no_bytes,infinity}|Opts]) -> + check_log_opts(Opts); +check_log_opts([{max_no_bytes,N}|Opts]) when is_integer(N), N>0 -> + check_log_opts(Opts); +check_log_opts([{type,Type}|Opts]) when Type==wrap; Type==halt -> + check_log_opts(Opts); +check_log_opts([Invalid|_]) -> + {error,{invalid_config,disk_log_opt,Invalid}}; +check_log_opts([]) -> + ok. + +check_my_config([Other | Config]) -> + case logger_h_common:check_common_config(Other) of + valid -> + check_my_config(Config); + invalid -> + {error,{invalid_config,?MODULE,Other}} + end; +check_my_config([]) -> + ok. + +%%%----------------------------------------------------------------- +%%% Handler being removed +removing_handler(Name) -> + stop(Name). + +%%%----------------------------------------------------------------- +%%% Get buffer when swapping from simple handler +swap_buffer(Name,Buffer) -> + case whereis(Name) of + undefined -> + ok; + _ -> + Name ! {buffer,Buffer} + end. + +%%%----------------------------------------------------------------- +%%% Log a string or report +-spec log(Log, Config) -> ok | dropped when + Log :: logger:log(), + Config :: logger:config(). + +log(Log,Config=#{id:=Name}) -> + %% if the handler has crashed, we must drop this request + %% and hope the handler restarts so we can try again + true = is_pid(whereis(Name)), + Bin = logger_h_common:log_to_binary(Log,Config), + logger_h_common:call_cast_or_drop(Name, Bin). + + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== + +init([Name, Config = #{disk_log_opts := LogOpts}, + State = #{dl_sync_int := DLSyncInt}]) -> + register(Name, self()), + process_flag(trap_exit, true), + process_flag(message_queue_data, off_heap), + + ?init_test_hooks(), + ?start_observation(Name), + + case open_disk_log(Name, LogOpts) of + ok -> + catch ets:new(Name, [public, named_table]), + ?set_mode(Name, async), + proc_lib:init_ack({ok,self()}), + T0 = ?timestamp(), + State1 = + ?merge_with_stats(State#{id => Name, + mode => async, + dl_sync => DLSyncInt, + log_opts => LogOpts, + last_qlen => 0, + last_log_ts => T0, + burst_win_ts => T0, + burst_msg_count => 0, + prev_log_result => ok, + prev_sync_result => ok, + prev_disk_log_info => undefined}), + gen_server:cast(self(), {repeated_disk_log_sync,T0}), + enter_loop(Config, State1); + Error -> + logger_h_common:error_notify({open_disk_log,Name,Error}), + proc_lib:init_ack(Error) + end. + +enter_loop(#{wait_for_buffer:=true}=Config,State) -> + State1 = + receive + {buffer,Buffer} -> + lists:foldl( + fun(Log,S) -> + Bin = logger_h_common:log_to_binary(Log,Config), + {_,S1} = do_log(Bin,cast,S), + S1 + end, + State, + Buffer) + end, + gen_server:enter_loop(?MODULE,[],State1); +enter_loop(_Config,State) -> + gen_server:enter_loop(?MODULE,[],State). + +%% This is the synchronous log request. +handle_call({log, Bin}, _From, State) -> + {Result,State1} = do_log(Bin, call, State), + %% Result == ok | dropped + {reply, Result, State1}; + +handle_call(disk_log_sync, _From, State = #{id := Name}) -> + State1 = #{prev_sync_result := Result} = disk_log_sync(Name, State), + {reply, Result, State1}; + +handle_call({change_config,_OldConfig,NewConfig}, _From, + State = #{filesync_repeat_interval := FSyncInt0, + last_log_ts := LastLogTS}) -> + HConfig = maps:get(?MODULE, NewConfig, #{}), + State1 = #{toggle_sync_qlen := TSQL, + drop_new_reqs_qlen := DNRQL, + flush_reqs_qlen := FRQL} = maps:merge(State, HConfig), + case logger_h_common:overload_levels_ok(State1) of + true -> + _ = + case maps:get(filesync_repeat_interval, HConfig, undefined) of + undefined -> + ok; + no_repeat -> + _ = logger_h_common:cancel_timer(maps:get(rep_sync_tref, + State, + undefined)); + FSyncInt0 -> + ok; + _FSyncInt1 -> + _ = logger_h_common:cancel_timer(maps:get(rep_sync_tref, + State, + undefined)), + _ = gen_server:cast(self(), {repeated_disk_log_sync, + LastLogTS}) + end, + {reply, ok, State1}; + false -> + {reply, {error,{invalid_levels,{TSQL,DNRQL,FRQL}}}, State} + end; + +handle_call(info, _From, State) -> + {reply, State, State}; + +handle_call(reset, _From, State) -> + State1 = ?merge_with_stats(State), + {reply, ok, State1#{last_qlen => 0, + last_log_ts => ?timestamp(), + prev_log_result => ok, + prev_sync_result => ok, + prev_disk_log_info => undefined}}; + +handle_call(stop, _From, State) -> + {stop, {shutdown,stopped}, ok, State}. + + +%% This is the asynchronous log request. +handle_cast({log, Bin}, State) -> + {_,State1} = do_log(Bin, cast, State), + {noreply, State1}; + +%% If FILESYNC_REPEAT_INTERVAL is set to a millisec value, this +%% clause gets called repeatedly by the handler. In order to +%% guarantee that a filesync *always* happens after the last log +%% request, the repeat operation must be active! +handle_cast({repeated_disk_log_sync,LastLogTS0}, + State = #{id := Name, + filesync_repeat_interval := FSyncInt, + last_log_ts := LastLogTS1}) -> + State1 = + if is_integer(FSyncInt) -> + %% only do filesync if something has been + %% written since last time we checked + NewState = if LastLogTS1 == LastLogTS0 -> + State; + true -> + disk_log_sync(Name, State) + end, + {ok,TRef} = + timer:apply_after(FSyncInt, gen_server,cast, + [self(), + {repeated_disk_log_sync,LastLogTS1}]), + NewState#{rep_sync_tref => TRef}; + true -> + State + end, + {noreply,State1}. + +%% The disk log owner must handle status messages from disk_log. +handle_info({disk_log, _Node, _Log, {wrap,_NoLostItems}}, State) -> + {noreply, State}; +handle_info({disk_log, _Node, Log, Info = {truncated,_NoLostItems}}, + State = #{id := Name, prev_disk_log_info := PrevInfo}) -> + error_notify_new(Info, PrevInfo, {disk_log,Name,Log,Info}), + {noreply, State#{prev_disk_log_info => Info}}; +handle_info({disk_log, _Node, Log, Info = {blocked_log,_Items}}, + State = #{id := Name, prev_disk_log_info := PrevInfo}) -> + error_notify_new(Info, PrevInfo, {disk_log,Name,Log,Info}), + {noreply, State#{prev_disk_log_info => Info}}; +handle_info({disk_log, _Node, Log, full}, + State = #{id := Name, prev_disk_log_info := PrevInfo}) -> + error_notify_new(full, PrevInfo, {disk_log,Name,Log,full}), + {noreply, State#{prev_disk_log_info => full}}; +handle_info({disk_log, _Node, Log, Info = {error_status,_Status}}, + State = #{id := Name, prev_disk_log_info := PrevInfo}) -> + error_notify_new(Info, PrevInfo, {disk_log,Name,Log,Info}), + {noreply, State#{prev_disk_log_info => Info}}; + +handle_info({'EXIT',_Pid,_Why}, State = #{id := _Name}) -> + {noreply, State}; + +handle_info(_, State) -> + {noreply, State}. + +terminate(Reason, State = #{id := Name}) -> + _ = logger_h_common:cancel_timer(maps:get(rep_sync_tref, State, + undefined)), + _ = close_disk_log(Name, normal), + logger_h_common:stop_or_restart(Name, Reason, State). + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%%----------------------------------------------------------------- +%%% Internal functions + +%%%----------------------------------------------------------------- +%%% +get_init_state() -> + #{toggle_sync_qlen => ?TOGGLE_SYNC_QLEN, + drop_new_reqs_qlen => ?DROP_NEW_REQS_QLEN, + flush_reqs_qlen => ?FLUSH_REQS_QLEN, + enable_burst_limit => ?ENABLE_BURST_LIMIT, + burst_limit_size => ?BURST_LIMIT_SIZE, + burst_window_time => ?BURST_WINDOW_TIME, + enable_kill_overloaded => ?ENABLE_KILL_OVERLOADED, + handler_overloaded_qlen => ?HANDLER_OVERLOADED_QLEN, + handler_overloaded_mem => ?HANDLER_OVERLOADED_MEM, + handler_restart_after => ?HANDLER_RESTART_AFTER, + dl_sync_int => ?CONTROLLER_SYNC_INTERVAL, + filesync_ok_qlen => ?FILESYNC_OK_QLEN, + filesync_repeat_interval => ?FILESYNC_REPEAT_INTERVAL}. + +%%%----------------------------------------------------------------- +%%% Add a disk_log handler to the logger. +%%% This starts a dedicated handler process which should always +%%% exist if the handler is registered with logger (and should not +%%% exist if the handler is not registered). +%%% +%%% Config is the logger:config() map containing a sub map with any of +%%% the following associations: +%%% +%%% Config = #{disk_log_opts => #{file => file:filename(), +%%% max_no_bytes => integer(), +%%% max_no_files => integer(), +%%% type => wrap | halt}}. +%%% +%%% This map will be merged with the logger configuration data for +%%% the disk_log LogName. If type == halt, then max_no_files is +%%% ignored. +%%% +%%% Handler specific config should be provided with a sub map associated +%%% with a key named the same as this module, e.g: +%%% +%%% Config = #{logger_disk_log_h => #{toggle_sync_qlen => 50} +%%% +%%% The disk_log handler process is linked to logger_sup, which is +%%% part of the kernel application's supervision tree. +start(Name, Config, HandlerState) -> + LoggerDLH = + #{id => Name, + start => {?MODULE, start_link, [Name,Config,HandlerState]}, + restart => temporary, + shutdown => 2000, + type => worker, + modules => [?MODULE]}, + case supervisor:start_child(logger_sup, LoggerDLH) of + {ok,_} -> + ok; + Error -> + Error + end. + +%%%----------------------------------------------------------------- +%%% Stop and remove the handler. +stop(Name) -> + case whereis(Name) of + undefined -> + ok; + _ -> + %% We don't want to do supervisor:terminate_child here + %% since we need to distinguish this explicit stop from a + %% system termination in order to avoid circular attempts + %% at removing the handler (implying deadlocks and + %% timeouts). + _ = gen_server:call(Name,stop), + _ = supervisor:delete_child(logger_sup, Name), + ok + end. + +%%%----------------------------------------------------------------- +%%% Logging and overload control. +-define(update_dl_sync(C, Interval), + if C == 0 -> Interval; + true -> C-1 end). + +%% check for overload between every request (and set Mode to async, +%% sync or drop accordingly), but never flush the whole mailbox +%% before LogWindowSize requests have been handled +do_log(Bin, CallOrCast, State = #{id:=Name, mode := _Mode0}) -> + T1 = ?timestamp(), + + %% check if the handler is getting overloaded, or if it's + %% recovering from overload (the check must be done for each + %% request to react quickly to large bursts of requests and + %% to ensure that the handler can never end up in drop mode + %% with an empty mailbox, which would stop operation) + {Mode1,QLen,Mem,State1} = logger_h_common:check_load(State), + + %% kill the handler if it can't keep up with the load + logger_h_common:kill_if_choked(Name, QLen, Mem, State), + + if Mode1 == flush -> + flush(Name, QLen, T1, State1); + true -> + write(Name, Mode1, T1, Bin, CallOrCast, State1) + end. + +%% this function is called by do_log/3 after an overload check +%% has been performed, where QLen > FlushQLen +flush(Name, _QLen0, T1, State=#{last_log_ts := _T0}) -> + %% flush messages in the mailbox (a limited number in + %% order to not cause long delays) + _NewFlushed = logger_h_common:flush_log_requests(?FLUSH_MAX_N), + + %% because of the receive loop when flushing messages, the + %% handler will be scheduled out often and the mailbox could + %% grow very large, so we'd better check the queue again here + {_,_QLen1} = process_info(self(), message_queue_len), + ?observe(Name,{max_qlen,_QLen1}), + + %% Add 1 for the current log request + ?observe(Name,{flushed,_NewFlushed+1}), + + State1 = ?update_max_time(?diff_time(T1,_T0),State), + {dropped,?update_other(flushed,FLUSHED,_NewFlushed, + State1#{mode => ?set_mode(Name,async), + last_qlen => 0, + last_log_ts => T1})}. + +%% this function is called to write to disk_log +write(Name, Mode, T1, Bin, _CallOrCast, + State = #{dl_sync := DLSync, + dl_sync_int := DLSyncInt, + last_qlen := LastQLen, + last_log_ts := T0}) -> + %% check if we need to limit the number of writes + %% during a burst of log requests + {DoWrite,BurstWinT,BurstMsgCount} = logger_h_common:limit_burst(State), + + %% only send a synhrounous request to the disk_log process + %% every DLSyncInt time, to give the handler time between + %% writes so it can keep up with incoming messages + {Status,LastQLen1,State1} = + if DoWrite, DLSync == 0 -> + ?observe(Name,{_CallOrCast,1}), + NewState = disk_log_write(Name, Bin, State), + {ok, element(2,process_info(self(),message_queue_len)), + NewState}; + DoWrite -> + ?observe(Name,{_CallOrCast,1}), + NewState = disk_log_write(Name, Bin, State), + {ok, LastQLen, NewState}; + not DoWrite -> + ?observe(Name,{flushed,1}), + {dropped, LastQLen, State} + end, + + %% Check if the time since the previous log request is long enough - + %% and the queue length small enough - to assume the mailbox has + %% been emptied, and if so, do filesync operation and reset mode to + %% async. Note that this is the best we can do to detect an idle + %% handler without setting a timer after each log call/cast. If the + %% time between two consecutive log requests is fast and no new + %% request comes in after the last one, idle state won't be detected! + Time = ?diff_time(T1,T0), + {Mode1,BurstMsgCount1,State2} = + if (LastQLen1 < ?FILESYNC_OK_QLEN) andalso + (Time > ?IDLE_DETECT_TIME_USEC) -> + {?change_mode(Name,Mode,async), 0, disk_log_sync(Name,State1)}; + true -> + {Mode, BurstMsgCount,State1} + end, + + State3 = + ?update_calls_or_casts(_CallOrCast,1,State2), + State4 = + ?update_max_time(Time, + State3#{mode => Mode1, + last_qlen := LastQLen1, + last_log_ts => T1, + burst_win_ts => BurstWinT, + burst_msg_count => BurstMsgCount1, + dl_sync => ?update_dl_sync(DLSync,DLSyncInt)}), + {Status,State4}. + + +open_disk_log(Name, LogOpts) -> + #{file := File, + max_no_bytes := MaxNoBytes, + max_no_files := MaxNoFiles, + type := Type} = LogOpts, + case filelib:ensure_dir(File) of + ok -> + Size = + if Type == halt -> MaxNoBytes; + Type == wrap -> {MaxNoBytes,MaxNoFiles} + end, + Opts = [{name, Name}, + {file, File}, + {size, Size}, + {type, Type}, + {linkto, self()}, + {repair, false}, + {format, external}, + {notify, true}, + {quiet, true}, + {mode, read_write}], + case disk_log:open(Opts) of + {ok,Name} -> + ok; + Error = {error,_Reason} -> + Error + end; + Error -> + Error + end. + +close_disk_log(Name, _) -> + _ = ?disk_log_sync(Name), + _ = disk_log:lclose(Name), + ok. + +disk_log_write(Name, Bin, State) -> + Result = + case ?disk_log_blog(Name, Bin) of + ok -> + ok; + LogError -> + _ = case maps:get(prev_log_result, State) of + LogError -> + %% don't report same error twice + ok; + _ -> + LogOpts = maps:get(log_opts, State), + logger_h_common:error_notify({Name,log, + LogOpts, + LogError}) + end, + LogError + end, + State#{prev_log_result => Result}. + +disk_log_sync(Name, State) -> + Result = + case ?disk_log_sync(Name) of + ok -> + ok; + SyncError -> + _ = case maps:get(prev_sync_result, State) of + SyncError -> + %% don't report same error twice + ok; + _ -> + LogOpts = maps:get(log_opts, State), + logger_h_common:error_notify({Name,sync, + LogOpts, + SyncError}) + end, + SyncError + end, + State#{prev_sync_result => Result}. + +error_notify_new(Info,Info, _Term) -> + ok; +error_notify_new(_Info0,_Info1, Term) -> + logger_h_common:error_notify(Term). diff --git a/lib/kernel/src/logger_filters.erl b/lib/kernel/src/logger_filters.erl new file mode 100644 index 0000000000..85928f0fd6 --- /dev/null +++ b/lib/kernel/src/logger_filters.erl @@ -0,0 +1,123 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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_filters). + +-export([domain/2, + level/2, + progress/2, + remote_gl/2]). + +-include("logger_internal.hrl"). +-define(IS_ACTION(A), (A==log orelse A==stop)). + +-spec domain(Log,Extra) -> logger:filter_return() when + Log :: logger:log(), + Extra :: {Action,Compare,MatchDomain}, + Action :: log | stop, + Compare :: prefix_of | starts_with | equals | no_domain, + MatchDomain :: list(atom()). +domain(#{meta:=Meta}=Log,{Action,Compare,MatchDomain}) + when ?IS_ACTION(Action) andalso + (Compare==prefix_of orelse + Compare==starts_with orelse + Compare==equals orelse + Compare==no_domain) andalso + is_list(MatchDomain) -> + filter_domain(Compare,Meta,MatchDomain,on_match(Action,Log)); +domain(Log,Extra) -> + erlang:error(badarg,[Log,Extra]). + +-spec level(Log,Extra) -> logger:filter_return() when + Log :: logger:log(), + Extra :: {Action,Operator,MatchLevel}, + Action :: log | stop, + Operator :: neq | eq | lt | gt | lteq | gteq, + MatchLevel :: logger:level(). +level(#{level:=L1}=Log,{Action,Op,L2}) + when ?IS_ACTION(Action) andalso + (Op==neq orelse + Op==eq orelse + Op==lt orelse + Op==gt orelse + Op==lteq orelse + Op==gteq) andalso + ?IS_LEVEL(L2) -> + filter_level(Op,L1,L2,on_match(Action,Log)); +level(Log,Extra) -> + erlang:error(badarg,[Log,Extra]). + +-spec progress(Log,Extra) -> logger:filter_return() when + Log :: logger:log(), + Extra :: log | stop. +progress(Log,Action) when ?IS_ACTION(Action) -> + filter_progress(Log,on_match(Action,Log)); +progress(Log,Action) -> + erlang:error(badarg,[Log,Action]). + +-spec remote_gl(Log,Extra) -> logger:filter_return() when + Log :: logger:log(), + Extra :: log | stop. +remote_gl(Log,Action) when ?IS_ACTION(Action) -> + filter_remote_gl(Log,on_match(Action,Log)); +remote_gl(Log,Action) -> + erlang:error(badarg,[Log,Action]). + +%%%----------------------------------------------------------------- +%%% Internal +filter_domain(prefix_of,#{domain:=Domain},MatchDomain,OnMatch) -> + is_prefix(Domain,MatchDomain,OnMatch); +filter_domain(starts_with,#{domain:=Domain},MatchDomain,OnMatch) -> + is_prefix(MatchDomain,Domain,OnMatch); +filter_domain(equals,#{domain:=Domain},Domain,OnMatch) -> + OnMatch; +filter_domain(Action,Meta,_,OnMatch) -> + case maps:is_key(domain,Meta) of + false when Action==no_domain -> OnMatch; + _ -> ignore + end. + +is_prefix(D1,D2,OnMatch) when is_list(D1), is_list(D2) -> + case lists:prefix(D1,D2) of + true -> OnMatch; + false -> ignore + end; +is_prefix(_,_,_) -> + ignore. + +filter_level(Op,L1,L2,OnMatch) -> + case logger:compare_levels(L1,L2) of + eq when Op==eq; Op==lteq; Op==gteq -> OnMatch; + lt when Op==lt; Op==lteq; Op==neq -> OnMatch; + gt when Op==gt; Op==gteq; Op==neq -> OnMatch; + _ -> ignore + end. + +filter_progress(#{msg:={report,#{label:={_,progress}}}},OnMatch) -> + OnMatch; +filter_progress(_,_) -> + ignore. + +filter_remote_gl(#{meta:=#{gl:=GL}},OnMatch) when node(GL)=/=node() -> + OnMatch; +filter_remote_gl(_,_) -> + ignore. + +on_match(log,Log) -> Log; +on_match(stop,_) -> stop. diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl new file mode 100644 index 0000000000..91283ab299 --- /dev/null +++ b/lib/kernel/src/logger_formatter.erl @@ -0,0 +1,255 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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_formatter). + +-export([format/2]). + +-include("logger_internal.hrl"). + +%%%----------------------------------------------------------------- +%%% Types +-type template() :: [atom()|tuple()|string()]. + +%%%----------------------------------------------------------------- +%%% API +-spec format(Log,Config) -> String when + Log :: logger:log(), + Config :: #{single_line=>boolean(), + legacy_header=>boolean(), + report_cb=>fun((logger:report()) -> {io:format(),[term()]}), + max_size=>pos_integer() | unlimited, + depth=>pos_integer() | unlimited, + template=>template(), + utc=>boolean()}, + String :: string(). +format(#{level:=Level,msg:=Msg0,meta:=Meta},Config0) + when is_map(Config0) -> + Config = add_default_config(Config0), + Meta1 = maybe_add_legacy_header(Level,Meta,Config), + MsgStr0 = format_msg(Msg0,Meta1,Config), + MsgStr = + case maps:get(single_line,Config) of + true -> + %% Trim leading and trailing whitespaces, and replace + %% newlines with ", " + re:replace(string:trim(MsgStr0),",?\r?\n\s*",", ", + [{return,list},global]); + _false2 -> + MsgStr0 + end, + String = do_format(Level,MsgStr,Meta1,maps:get(template,Config),Config), + truncate(String,maps:get(max_size,Config)). + +do_format(Level,Msg,Data,[level|Format],Config) -> + [to_string(level,Level,Config)|do_format(Level,Msg,Data,Format,Config)]; +do_format(Level,Msg,Data,[msg|Format],Config) -> + [Msg|do_format(Level,Msg,Data,Format,Config)]; +do_format(Level,Msg,Data,[Key|Format],Config) when is_atom(Key); is_tuple(Key) -> + Value = value(Key,Data), + [to_string(Key,Value,Config)|do_format(Level,Msg,Data,Format,Config)]; +do_format(Level,Msg,Data,[Str|Format],Config) -> + [Str|do_format(Level,Msg,Data,Format,Config)]; +do_format(_Level,_Msg,_Data,[],_Config) -> + []. + +value(Key,Meta) when is_atom(Key), is_map(Meta) -> + maps:get(Key,Meta,""); +value(Key,_) when is_atom(Key) -> + ""; +value(Keys,Meta) when is_tuple(Keys) -> + value(tuple_to_list(Keys),Meta); +value([Key|Keys],Meta) -> + value(Keys,value(Key,Meta)); +value([],Value) -> + Value. + +to_string(time,Time,Config) -> + format_time(Time,Config); +to_string(mfa,MFA,_Config) -> + format_mfa(MFA); +to_string(_,Value,_Config) -> + to_string(Value). + +to_string(X) when is_atom(X) -> + atom_to_list(X); +to_string(X) when is_integer(X) -> + integer_to_list(X); +to_string(X) when is_pid(X) -> + pid_to_list(X); +to_string(X) when is_reference(X) -> + ref_to_list(X); +to_string(X) when is_list(X) -> + case io_lib:printable_unicode_list(lists:flatten(X)) of + true -> X; + _ -> io_lib:format("~tp",[X]) + end; +to_string(X) -> + io_lib:format("~tp",[X]). + +format_msg({string,Chardata},Meta,Config) -> + try unicode:characters_to_list(Chardata) + catch _:_ -> format_msg({"INVALID STRING: ~tp",[Chardata]},Meta,Config) + end; +format_msg({report,_}=Msg,Meta,#{report_cb:=Fun}=Config) when is_function(Fun,1) -> + format_msg(Msg,Meta#{report_cb=>Fun},maps:remove(report_cb,Config)); +format_msg({report,Report},#{report_cb:=Fun}=Meta,Config) when is_function(Fun,1) -> + try Fun(Report) of + {Format,Args} when is_list(Format), is_list(Args) -> + format_msg({Format,Args},maps:remove(report_cb,Meta),Config); + Other -> + format_msg({"REPORT_CB ERROR: ~tp; Returned: ~tp", + [Report,Other]},Meta,Config) + catch C:R -> + format_msg({"REPORT_CB CRASH: ~tp; Reason: ~tp", + [Report,{C,R}]},Meta,Config) + end; +format_msg({report,Report},Meta,Config) -> + format_msg({report,Report}, + Meta#{report_cb=>fun logger:format_report/1}, + Config); +format_msg(Msg,_Meta,Config) -> + limit_depth(Msg, maps:get(depth,Config)). + +limit_depth(Msg,false) -> + Depth = logger:get_format_depth(), + limit_depth(Msg,Depth); +limit_depth({Format,Args},unlimited) -> + try io_lib:format(Format,Args) + catch _:_ -> + io_lib:format("FORMAT ERROR: ~tp - ~tp",[Format,Args]) + end; +limit_depth({Format0,Args},Depth) -> + try + Format1 = io_lib:scan_format(Format0, Args), + Format = limit_format(Format1, Depth), + io_lib:build_text(Format) + catch _:_ -> + limit_depth({"FORMAT ERROR: ~tp - ~tp",[Format0,Args]},Depth) + end. + +limit_format([#{control_char:=C0}=M0|T], Depth) when C0 =:= $p; + C0 =:= $w -> + C = C0 - ($a - $A), %To uppercase. + #{args:=Args} = M0, + M = M0#{control_char:=C,args:=Args++[Depth]}, + [M|limit_format(T, Depth)]; +limit_format([H|T], Depth) -> + [H|limit_format(T, Depth)]; +limit_format([], _) -> + []. + +truncate(String,unlimited) -> + String; +truncate(String,false) -> + Size = logger:get_max_size(), + truncate(String,Size); +truncate(String,Size) -> + Length = string:length(String), + if Length>Size -> + string:slice(String,0,Size-3)++"..."; + true -> + String + end. + +format_time(Timestamp,Config) when is_integer(Timestamp) -> + {Date,Time,Micro} = timestamp_to_datetimemicro(Timestamp,Config), + format_time(Date,Time,Micro); +format_time(Other,_Config) -> + %% E.g. a string + to_string(Other). + +format_time({Y,M,D},{H,Min,S},Micro) -> + io_lib:format("~4w-~2..0w-~2..0w ~2w:~2..0w:~2..0w.~6..0w", + [Y,M,D,H,Min,S,Micro]). + +%% Assuming this is monotonic time in microseconds +timestamp_to_datetimemicro(Timestamp,Config) when is_integer(Timestamp) -> + SysTime = Timestamp + erlang:time_offset(microsecond), + Micro = SysTime rem 1000000, + Sec = SysTime div 1000000, + UniversalTime = erlang:posixtime_to_universaltime(Sec), + {Date,Time} = + case Config of + #{utc:=true} -> UniversalTime; + _ -> erlang:universaltime_to_localtime(UniversalTime) + end, + {Date,Time,Micro}. + +format_mfa({M,F,A}) when is_atom(M), is_atom(F), is_integer(A) -> + atom_to_list(M)++":"++atom_to_list(F)++"/"++integer_to_list(A); +format_mfa({M,F,A}) when is_atom(M), is_atom(F), is_list(A) -> + format_mfa({M,F,length(A)}); +format_mfa(MFA) -> + to_string(MFA). + +maybe_add_legacy_header(Level, + #{time:=Timestamp}=Meta, + #{legacy_header:=true}=Config) -> + #{title:=Title}=MyMeta = add_legacy_title(Level,maps:get(?MODULE,Meta,#{})), + {{Y,Mo,D},{H,Mi,S},Micro} = timestamp_to_datetimemicro(Timestamp,Config), + Header = io_lib:format("=~ts==== ~w-~s-~4w::~2..0w:~2..0w:~2..0w.~6..0w ~s===", + [Title,D,month(Mo),Y,H,Mi,S,Micro,utcstr(Config)]), + Meta#{?MODULE=>MyMeta#{header=>Header}}; +maybe_add_legacy_header(_,Meta,_) -> + Meta. + +add_legacy_title(_Level,#{title:=_}=MyMeta) -> + MyMeta; +add_legacy_title(Level,MyMeta) -> + Title = string:uppercase(atom_to_list(Level)) ++ " REPORT", + MyMeta#{title=>Title}. + +month(1) -> "Jan"; +month(2) -> "Feb"; +month(3) -> "Mar"; +month(4) -> "Apr"; +month(5) -> "May"; +month(6) -> "Jun"; +month(7) -> "Jul"; +month(8) -> "Aug"; +month(9) -> "Sep"; +month(10) -> "Oct"; +month(11) -> "Nov"; +month(12) -> "Dec". + +utcstr(#{utc:=true}) -> "UTC "; +utcstr(_) -> "". + +add_default_config(#{utc:=_}=Config) -> + Default = + #{legacy_header=>false, + single_line=>false, + max_size=>false, + depth=>false}, + add_default_template(maps:merge(Default,Config)); +add_default_config(Config) -> + add_default_config(Config#{utc=>logger:get_utc_config()}). + +add_default_template(#{template:=_}=Config) -> + Config; +add_default_template(Config) -> + Config#{template=>default_template(Config)}. + +default_template(#{legacy_header:=true}) -> + ?DEFAULT_FORMAT_TEMPLATE_HEADER; +default_template(#{single_line:=true}) -> + ?DEFAULT_FORMAT_TEMPLATE_SINGLE; +default_template(_) -> + ?DEFAULT_FORMAT_TEMPLATE. diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl new file mode 100644 index 0000000000..7a5082fb84 --- /dev/null +++ b/lib/kernel/src/logger_h_common.erl @@ -0,0 +1,305 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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_h_common). + +-include("logger_h_common.hrl"). +-include("logger_internal.hrl"). + +-export([log_to_binary/2, + check_common_config/1, + call_cast_or_drop/2, + check_load/1, + limit_burst/1, + kill_if_choked/4, + flush_log_requests/0, + flush_log_requests/1, + handler_exit/2, + cancel_timer/1, + stop_or_restart/3, + overload_levels_ok/1, + error_notify/1, + info_notify/1]). + +%%%----------------------------------------------------------------- +%%% Covert log data on any form to binary +-spec log_to_binary(Log,Config) -> LogString when + Log :: logger:log(), + Config :: logger:config(), + LogString :: binary(). +log_to_binary(#{msg:={report,_},meta:=#{report_cb:=_}}=Log,Config) -> + do_log_to_binary(Log,Config); +log_to_binary(#{msg:={report,_},meta:=Meta}=Log,Config) -> + DefaultReportCb = fun logger:format_otp_report/1, + do_log_to_binary(Log#{meta=>Meta#{report_cb=>DefaultReportCb}},Config); +log_to_binary(Log,Config) -> + do_log_to_binary(Log,Config). + +do_log_to_binary(Log,Config) -> + {Formatter,FormatterConfig} = maps:get(formatter,Config), + String = try_format(Log,Formatter,FormatterConfig), + try unicode:characters_to_binary(String) + catch _:_ -> + ?LOG_INTERNAL(debug,[{formatter_error,Formatter}, + {config,FormatterConfig}, + {log,Log}, + {bad_return_value,String}]), + <<"FORMATTER ERROR: bad_return_value">> + end. + +try_format(Log,Formatter,FormatterConfig) -> + try Formatter:format(Log,FormatterConfig) + catch + C:R:S -> + ?LOG_INTERNAL(debug,[{formatter_crashed,Formatter}, + {config,FormatterConfig}, + {log,Log}, + {reason, + {C,R,logger:filter_stacktrace(?MODULE,S)}}]), + case {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG} of + {Formatter,FormatterConfig} -> + "DEFAULT FORMATTER CRASHED"; + {DefaultFormatter,DefaultConfig} -> + try_format(Log#{msg=>{"FORMATTER CRASH: ~tp", + [maps:get(msg,Log)]}}, + DefaultFormatter,DefaultConfig) + end + end. + +%%%----------------------------------------------------------------- +%%% Check that the configuration term is valid +check_common_config({toggle_sync_qlen,N}) when is_integer(N) -> + valid; +check_common_config({drop_new_reqs_qlen,N}) when is_integer(N) -> + valid; +check_common_config({flush_reqs_qlen,N}) when is_integer(N) -> + valid; +check_common_config({enable_burst_limit,Bool}) when Bool == true; + Bool == false -> + valid; +check_common_config({burst_limit_size,N}) when is_integer(N) -> + valid; +check_common_config({burst_window_time,N}) when is_integer(N) -> + valid; +check_common_config({enable_kill_overloaded,Bool}) when Bool == true; + Bool == false -> + valid; +check_common_config({handler_overloaded_qlen,N}) when is_integer(N) -> + valid; +check_common_config({handler_overloaded_mem,N}) when is_integer(N) -> + valid; +check_common_config({handler_restart_after,NorA}) when is_integer(NorA); + NorA == never -> + valid; +check_common_config({filesync_repeat_interval,NorA}) when is_integer(NorA); + NorA == no_repeat -> + valid; +check_common_config(_) -> + invalid. + + +%%%----------------------------------------------------------------- +%%% Overload Protection +call_cast_or_drop(Name, Bin) -> + %% If the handler process is getting overloaded, the log request + %% will be synchronous instead of asynchronous (slows down the + %% logging tempo of a process doing lots of logging. If the + %% handler is choked, drop mode is set and no request will be sent. + try ?get_mode(Name) of + async -> + gen_server:cast(Name, {log,Bin}); + sync -> + try gen_server:call(Name, {log,Bin}, ?DEFAULT_CALL_TIMEOUT) of + %% if return value from call == dropped, the + %% message has been flushed by handler and should + %% therefore not be counted as dropped in stats + ok -> ok; + dropped -> ok + catch + _:{timeout,_} -> + %%! --- Tue Mar 6 23:10:48 2018 --- peppe was here! + erlang:display('CLIENT_TIMEOUT'), + %% exit('CLIENT_TIMEOUT'), + + ?observe(Name,{dropped,1}) + end; + drop -> ?observe(Name,{dropped,1}) + catch + %% if the ETS table doesn't exist (maybe because of a + %% handler restart), we can only drop the request + _:_ -> ?observe(Name,{dropped,1}) + end, + ok. + +handler_exit(_Name, Reason) -> + exit(Reason). + +check_load(State = #{id:=Name, mode := Mode, + toggle_sync_qlen := ToggleSyncQLen, + drop_new_reqs_qlen := DropNewQLen, + flush_reqs_qlen := FlushQLen}) -> + {_,Mem} = process_info(self(), memory), + ?observe(Name,{max_mem,Mem}), + %% make sure the handler process doesn't get scheduled + %% out between the message_queue_len check below and the + %% action that follows (flush or write). + {_,QLen} = process_info(self(), message_queue_len), + ?observe(Name,{max_qlen,QLen}), + + {Mode1,_NewDrops,_NewFlushes} = + if + QLen >= FlushQLen -> + {flush, 0,1}; + QLen >= DropNewQLen -> + %% Note that drop mode will force log requests to + %% be dropped on the client side (never sent get to + %% the handler). + IncDrops = if Mode == drop -> 0; true -> 1 end, + {?change_mode(Name, Mode, drop), IncDrops,0}; + QLen >= ToggleSyncQLen -> + {?change_mode(Name, Mode, sync), 0,0}; + true -> + {?change_mode(Name, Mode, async), 0,0} + end, + State1 = ?update_other(drops,DROPS,_NewDrops,State), + {Mode1, QLen, Mem, + ?update_other(flushes,FLUSHES,_NewFlushes, + State1#{last_qlen => QLen})}. + +limit_burst(#{enable_burst_limit := false}) -> + {true,0,0}; +limit_burst(#{burst_win_ts := BurstWinT0, + burst_msg_count := BurstMsgCount, + burst_window_time := BurstWinTime, + burst_limit_size := BurstLimitSz}) -> + if (BurstMsgCount >= BurstLimitSz) -> + %% the limit for allowed messages has been reached + BurstWinT1 = ?timestamp(), + case ?diff_time(BurstWinT1,BurstWinT0) of + BurstCheckTime when BurstCheckTime < (BurstWinTime*1000) -> + %% we're still within the burst time frame + {false,BurstWinT0,BurstMsgCount}; + _BurstCheckTime -> + %% burst time frame passed, reset counters + {true,BurstWinT1,0} + end; + true -> + %% the limit for allowed messages not yet reached + {true,BurstWinT0,BurstMsgCount+1} + end. + +kill_if_choked(Name, QLen, Mem, + #{enable_kill_overloaded := KillIfOL, + handler_overloaded_qlen := HOLQLen, + handler_overloaded_mem := HOLMem}) -> + if KillIfOL andalso + ((QLen > HOLQLen) orelse (Mem > HOLMem)) -> + handler_exit(Name, {shutdown,{overloaded,Name,QLen,Mem}}); + true -> + ok + end. + +flush_log_requests() -> + flush_log_requests(-1). + +flush_log_requests(Limit) -> + process_flag(priority, high), + Flushed = flush_log_requests(0, Limit), + process_flag(priority, normal), + Flushed. + +flush_log_requests(Limit, Limit) -> + Limit; +flush_log_requests(N, Limit) -> + %% flush log requests but leave other requests, such as + %% file/disk_log_sync, info and change_config, so that these + %% have a chance to be processed even under heavy load + receive + {'$gen_cast',{log,_}} -> + flush_log_requests(N+1, Limit); + {'$gen_call',{Pid,MRef},{log,_}} -> + Pid ! {MRef, dropped}, + flush_log_requests(N+1, Limit) + after + 0 -> N + end. + +cancel_timer(TRef) when is_atom(TRef) -> ok; +cancel_timer(TRef) -> timer:cancel(TRef). + + +stop_or_restart(Name, {shutdown,Reason={overloaded,_Name,_QLen,_Mem}}, + #{handler_restart_after := RestartAfter}) -> + %% If we're terminating because of an overload situation (see + %% logger_h_common:kill_if_choked/4), we need to remove the handler + %% and set a restart timer. A separate process must perform this + %% in order to avoid deadlock. + HandlerPid = self(), + RemoveAndRestart = + fun() -> + MRef = erlang:monitor(process, HandlerPid), + receive + {'DOWN',MRef,_,_,_} -> + ok + after 30000 -> + error_notify(Reason), + exit(HandlerPid, kill) + end, + case logger:get_handler_config(Name) of + {ok,{HMod,HConfig}} when is_integer(RestartAfter) -> + _ = logger:remove_handler(Name), + _ = timer:apply_after(RestartAfter, logger, add_handler, + [Name,HMod,HConfig]); + {ok,_} -> + _ = logger:remove_handler(Name); + {error,CfgReason} when is_integer(RestartAfter) -> + error_notify({Name,restart_impossible,CfgReason}); + {error,_} -> + ok + end + end, + spawn(RemoveAndRestart), + ok; + +stop_or_restart(Name, shutdown, _State) -> + %% Probably terminated by supervisor. Remove the handler to avoid + %% error printouts due to failing handler. + _ = case logger:get_handler_config(Name) of + {ok,_} -> + %% Spawning to avoid deadlock + spawn(logger,remove_handler,[Name]); + _ -> + ok + end, + ok; + +stop_or_restart(_Name, _Reason, _State) -> + ok. + +overload_levels_ok(HandlerConfig) -> + TSQL = maps:get(toggle_sync_qlen, HandlerConfig, ?TOGGLE_SYNC_QLEN), + DNRQL = maps:get(drop_new_reqs_qlen, HandlerConfig, ?DROP_NEW_REQS_QLEN), + FRQL = maps:get(flush_reqs_qlen, HandlerConfig, ?FLUSH_REQS_QLEN), + (TSQL < DNRQL) andalso (DNRQL < FRQL). + +error_notify(Term) -> + ?internal_log(error, Term). + +info_notify(Term) -> + ?internal_log(info, Term). diff --git a/lib/kernel/src/logger_h_common.hrl b/lib/kernel/src/logger_h_common.hrl new file mode 100644 index 0000000000..f110bbaf97 --- /dev/null +++ b/lib/kernel/src/logger_h_common.hrl @@ -0,0 +1,262 @@ + +%%%----------------------------------------------------------------- +%%% Overload protection configuration + +%%! *** NOTE *** +%%! It's important that: +%%! TOGGLE_SYNC_QLEN < DROP_NEW_REQS_QLEN < FLUSH_REQS_QLEN +%%! and that DROP_NEW_REQS_QLEN >= 2. +%%! Otherwise the handler could end up in drop mode with no new +%%! log requests to process. This would cause all future requests +%%! to be dropped (no switch to async mode would ever take place). + +%% This specifies the message_queue_len value where the log +%% requests switch from asynchronous casts to synchronous calls. +-define(TOGGLE_SYNC_QLEN, 10). +%% Above this message_queue_len, log requests will be dropped, +%% i.e. no log requests get sent to the handler process. +-define(DROP_NEW_REQS_QLEN, 200). +%% Above this message_queue_len, the handler process will flush +%% its mailbox and only leave this number of messages in it. +-define(FLUSH_REQS_QLEN, 1000). + +%% Never flush more than this number of messages in one go, +%% or the handler will be unresponsive for seconds (keep this +%% number as large as possible or the mailbox could grow large). +-define(FLUSH_MAX_N, 5000). + +%% BURST_LIMIT is the max number of log requests allowed to be +%% written within a BURST_WINDOW_TIME time frame. +-define(ENABLE_BURST_LIMIT, true). +-define(BURST_LIMIT_SIZE, 500). +-define(BURST_WINDOW_TIME, 1000). + +%% This enables/disables the feature to automatically get the +%% handler terminated if it gets too loaded (and can't keep up). +-define(ENABLE_KILL_OVERLOADED, false). +%% If the message_queue_len goes above this size even after +%% flushing has been performed, the handler is terminated. +-define(HANDLER_OVERLOADED_QLEN, 20000). +%% If the memory usage exceeds this level +-define(HANDLER_OVERLOADED_MEM, 3000000). + +%% This is the default time that the handler will wait before +%% restarting and accepting new requests. The value 'never' +%% disables restarts. +-define(HANDLER_RESTART_AFTER, 5000). +%%-define(HANDLER_RESTART_AFTER, never). + +%% The handler sends asynchronous write requests to the process +%% controlling the i/o device, but every once in this interval +%% will the write request be synchronous, so that the i/o device +%% process doesn't get overloaded. This gives the handler time +%% to keep up with its mailbox in overload situations, even if +%% the i/o is slow. +-define(CONTROLLER_SYNC_INTERVAL, 20). +%% The handler will not perform a file sync operation if the +%% mailbox size is greater than this number. This is to ensure +%% the handler process doesn't get overloaded while waiting for +%% an expensive file sync operation to finish. +-define(FILESYNC_OK_QLEN, 2). +%% Do a file/disk_log sync operation every integer() millisec +%% (if necessary) or set to 'no_repeat' to only do file sync when +%% the handler is idle. Note that file sync is not guaranteed to +%% happen automatically if this operation is disabled. +-define(FILESYNC_REPEAT_INTERVAL, 5000). +%%-define(FILESYNC_REPEAT_INTERVAL, no_repeat). + +%% This is the time after last message received that we think/hope +%% that the handler has an empty mailbox (no new log request has +%% come in). +-define(IDLE_DETECT_TIME_MSEC, 100). +-define(IDLE_DETECT_TIME_USEC, 100000). + +%% Default disk log option values +-define(DISK_LOG_TYPE, wrap). +-define(DISK_LOG_MAX_NO_FILES, 10). +-define(DISK_LOG_MAX_NO_BYTES, 1048576). + +%%%----------------------------------------------------------------- +%%% Overload protection macros + +-define(timestamp(), erlang:monotonic_time(microsecond)). + +-define(get_mode(HandlerName), + case ets:lookup(HandlerName, mode) of + [{mode,sync}] -> + case whereis(HandlerName)==self() of + true -> async; + _ -> sync + end; + [{mode,M}] -> M; + _ -> async + end). + +-define(set_mode(HandlerName, M), + begin ets:insert(HandlerName, {mode,M}), M end). + +-define(change_mode(HandlerName, M0, M1), + if M0 == M1 -> + M0; + true -> + ets:insert(HandlerName, {mode,M1}), + M1 + end). + +-define(min(X1, X2), + if X2 == undefined -> X1; + X2 < X1 -> X2; + true -> X1 + end). + +-define(max(X1, X2), + if + X2 == undefined -> X1; + X2 > X1 -> X2; + true -> X1 + end). + +-define(diff_time(OS_T1, OS_T0), OS_T1-OS_T0). + +%%%----------------------------------------------------------------- +%%% The test hook macros make it possible to observe and manipulate +%%% internal handler functionality. When enabled, these macros will +%%% slow down execution and therefore should not be include in code +%%% to be officially released. + +%% -define(TEST_HOOKS, true). +-ifdef(TEST_HOOKS). + -define(TEST_HOOKS_TAB, logger_h_test_hooks). + + -define(init_test_hooks(), + _ = case ets:whereis(?TEST_HOOKS_TAB) of + undefined -> ets:new(?TEST_HOOKS_TAB, [named_table,public]); + _ -> ok + end, + ets:insert(?TEST_HOOKS_TAB, {internal_log,{logger,internal_log}}), + ets:insert(?TEST_HOOKS_TAB, {file_write,ok}), + ets:insert(?TEST_HOOKS_TAB, {file_datasync,ok}), + ets:insert(?TEST_HOOKS_TAB, {disk_log_blog,ok}), + ets:insert(?TEST_HOOKS_TAB, {disk_log_sync,ok})). + + -define(set_internal_log(MOD_FUNC), + ets:insert(?TEST_HOOKS_TAB, {internal_log,MOD_FUNC})). + + -define(set_result(OPERATION, RESULT), + ets:insert(?TEST_HOOKS_TAB, {OPERATION,RESULT})). + + -define(set_defaults(), + ets:insert(?TEST_HOOKS_TAB, {internal_log,{logger,internal_log}}), + ets:insert(?TEST_HOOKS_TAB, {file_write,ok}), + ets:insert(?TEST_HOOKS_TAB, {file_datasync,ok}), + ets:insert(?TEST_HOOKS_TAB, {disk_log_blog,ok}), + ets:insert(?TEST_HOOKS_TAB, {disk_log_sync,ok})). + + -define(internal_log(TYPE, TERM), + try ets:lookup(?TEST_HOOKS_TAB, internal_log) of + [{_,{LMOD,LFUNC}}] -> apply(LMOD, LFUNC, [TYPE,TERM]); + _ -> logger:internal_log(TYPE, TERM) + catch _:_ -> logger:internal_log(TYPE, TERM) end). + + -define(file_write(DEVICE, DATA), + try ets:lookup(?TEST_HOOKS_TAB, file_write) of + [{_,ok}] -> file:write(DEVICE, DATA); + [{_,ERROR}] -> ERROR + catch _:_ -> file:write(DEVICE, DATA) end). + + -define(file_datasync(DEVICE), + try ets:lookup(?TEST_HOOKS_TAB, file_datasync) of + [{_,ok}] -> file:datasync(DEVICE); + [{_,ERROR}] -> ERROR + catch _:_ -> file:datasync(DEVICE) end). + + -define(disk_log_blog(LOG, DATA), + try ets:lookup(?TEST_HOOKS_TAB, disk_log_blog) of + [{_,ok}] -> disk_log:blog(LOG, DATA); + [{_,ERROR}] -> ERROR + catch _:_ -> disk_log:blog(LOG, DATA) end). + + -define(disk_log_sync(LOG), + try ets:lookup(?TEST_HOOKS_TAB, disk_log_sync) of + [{_,ok}] -> disk_log:sync(LOG); + [{_,ERROR}] -> ERROR + catch _:_ -> disk_log:sync(LOG) end). + + -define(DEFAULT_CALL_TIMEOUT, 5000). + +-else. % DEFAULTS! + -define(TEST_HOOKS_TAB, undefined). + -define(init_test_hooks(), ok). + -define(set_internal_log(_MOD_FUNC), ok). + -define(set_result(_OPERATION, _RESULT), ok). + -define(set_defaults(), ok). + -define(internal_log(TYPE, TERM), logger:internal_log(TYPE, TERM)). + -define(file_write(DEVICE, DATA), file:write(DEVICE, DATA)). + -define(file_datasync(DEVICE), file:datasync(DEVICE)). + -define(disk_log_blog(LOG, DATA), disk_log:blog(LOG, DATA)). + -define(disk_log_sync(LOG), disk_log:sync(LOG)). + -define(DEFAULT_CALL_TIMEOUT, ?DEFAULT_LOGGER_CALL_TIMEOUT-2000). +-endif. + +%%%----------------------------------------------------------------- +%%% These macros enable statistics counters in the state of the +%%% handler which is useful for analysing the overload protection +%%% behaviour. These counters should not be included in code to be +%%% officially released (as some counters will grow very large +%%% over time). + +%%-define(SAVE_STATS, true). +-ifdef(SAVE_STATS). + -define(merge_with_stats(STATE), + STATE#{flushes => 0, flushed => 0, drops => 0, + casts => 0, calls => 0, + max_qlen => 0, max_time => 0}). + + -define(update_max_qlen(QLEN, STATE), + begin #{max_qlen := QLEN0} = STATE, + STATE#{max_qlen => ?max(QLEN0,QLEN)} end). + + -define(update_calls_or_casts(CALL_OR_CAST, INC, STATE), + case CALL_OR_CAST of + cast -> + #{casts := CASTS0} = STATE, + STATE#{casts => CASTS0+INC}; + call -> + #{calls := CALLS0} = STATE, + STATE#{calls => CALLS0+INC} + end). + + -define(update_max_time(TIME, STATE), + begin #{max_time := TIME0} = STATE, + STATE#{max_time => ?max(TIME0,TIME)} end). + + -define(update_other(OTHER, VAR, INCVAL, STATE), + begin #{OTHER := VAR} = STATE, + STATE#{OTHER => VAR+INCVAL} end). + +-else. % DEFAULT! + -define(merge_with_stats(STATE), STATE). + -define(update_max_qlen(_QLEN, STATE), STATE). + -define(update_calls_or_casts(_CALL_OR_CAST, _INC, STATE), STATE). + -define(update_max_time(_TIME, STATE), STATE). + -define(update_other(_OTHER, _VAR, _INCVAL, STATE), STATE). +-endif. + +%%%----------------------------------------------------------------- +%%% These macros enable callbacks that make it possible to analyse +%%% the overload protection behaviour from outside the handler +%%% process (including dropped requests on the client side). +%%% An external callback module (?OBSERVER_MOD) is required which +%%% is not part of the kernel application. For this reason, these +%%% callbacks should not be included in code to be officially released. + +%%-define(OBSERVER_MOD, logger_test). +-ifdef(OBSERVER_MOD). + -define(start_observation(NAME), ?OBSERVER:start_observation(NAME)). + -define(observe(NAME,EVENT), ?OBSERVER:observe(NAME,EVENT)). + +-else. % DEFAULT! + -define(start_observation(_NAME), ok). + -define(observe(_NAME,_EVENT), ok). +-endif. +%%! <--- diff --git a/lib/kernel/src/logger_internal.hrl b/lib/kernel/src/logger_internal.hrl new file mode 100644 index 0000000000..8fcaba8de6 --- /dev/null +++ b/lib/kernel/src/logger_internal.hrl @@ -0,0 +1,96 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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% +%% +-include_lib("kernel/include/logger.hrl"). +-define(LOGGER_TABLE,logger). +-define(LOGGER_KEY,'$logger_config$'). +-define(HANDLER_KEY,'$handler_config$'). +-define(LOGGER_META_KEY,'$logger_metadata$'). +-define(STANDARD_HANDLER, logger_std_h). +-define(DEFAULT_LOGGER_CALL_TIMEOUT, 10000). +-define(DEFAULT_HANDLER_FILTERS, + ?DEFAULT_HANDLER_FILTERS([beam,erlang,otp])). +-define(DEFAULT_HANDLER_FILTERS(Domain), + [{remote_gl,{fun logger_filters:remote_gl/2,stop}}, + {domain,{fun logger_filters:domain/2,{log,prefix_of,Domain}}}, + {no_domain,{fun logger_filters:domain/2,{log,no_domain,[]}}}]). +-define(DEFAULT_FORMATTER,logger_formatter). +-define(DEFAULT_FORMAT_CONFIG,#{legacy_header=>true, + template=>?DEFAULT_FORMAT_TEMPLATE_HEADER}). +-define(DEFAULT_FORMAT_TEMPLATE_HEADER, + [{logger_formatter,header},"\n",msg,"\n"]). +-define(DEFAULT_FORMAT_TEMPLATE_SINGLE, + [time," ",level,": ",msg,"\n"]). +-define(DEFAULT_FORMAT_TEMPLATE, + [time," ",level,":\n",msg,"\n"]). + +-define(LOG_INTERNAL(Level,Report), + case logger:allow(Level,?MODULE) of + true -> + _ = spawn(logger,macro_log,[?LOCATION,Level,Report, + logger:add_default_metadata(#{})]), + ok; + false -> + ok + end). + +%%%----------------------------------------------------------------- +%%% Levels +%%% Using same as syslog +-define(LEVELS,[emergency, + alert, + critical, + error, + warning, + notice, + info, + debug]). +-define(EMERGENCY,0). +-define(ALERT,1). +-define(CRITICAL,2). +-define(ERROR,3). +-define(WARNING,4). +-define(NOTICE,5). +-define(INFO,6). +-define(DEBUG,7). + +-define(IS_LEVEL(L), + (L=:=emergency orelse + L=:=alert orelse + L=:=critical orelse + L=:=error orelse + L=:=warning orelse + L=:=notice orelse + L=:=info orelse + L=:=debug)). + +-define(IS_MSG(Msg), + ((is_tuple(Msg) andalso tuple_size(Msg)==2) + andalso + (is_list(element(1,Msg)) andalso is_list(element(2,Msg))) + orelse + (element(1,Msg)==report andalso ?IS_REPORT(element(2,Msg))) + orelse + (element(1,Msg)==string andalso ?IS_STRING(element(2,Msg))))). + +-define(IS_REPORT(Report), + (is_map(Report) orelse (is_list(Report) andalso is_tuple(hd(Report))))). + +-define(IS_STRING(String), + (is_list(String) orelse is_binary(String))). diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl new file mode 100644 index 0000000000..6ef3b8582a --- /dev/null +++ b/lib/kernel/src/logger_server.erl @@ -0,0 +1,440 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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_server). + +-behaviour(gen_server). + +%% API +-export([start_link/0, + add_handler/3, remove_handler/1, + add_filter/2, remove_filter/2, + set_module_level/2, reset_module_level/1, + cache_module_level/1, + set_config/2, set_config/3]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2]). + +-include("logger_internal.hrl"). + +-define(SERVER, logger). + +-record(state, {tid}). + +%%%=================================================================== +%%% API +%%%=================================================================== + +start_link() -> + gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). + +add_handler(Id,Module,Config0) -> + case sanity_check(logger,handlers,[Id]) of + ok -> + try check_mod(Module) of + ok -> + case sanity_check(Id,Config0) of + ok -> + Default = default_config(Id), + Config = maps:merge(Default,Config0), + call({add_handler,Id,Module,Config}); + Error -> + Error + end + catch throw:Error -> + {error,Error} + end; + Error -> + Error + end. + +remove_handler(HandlerId) -> + call({remove_handler,HandlerId}). + +add_filter(Owner,Filter) -> + case sanity_check(Owner,filters,[Filter]) of + ok -> call({add_filter,Owner,Filter}); + Error -> Error + end. + +remove_filter(Owner,FilterId) -> + call({remove_filter,Owner,FilterId}). + +set_module_level(Module,Level) when is_atom(Module) -> + case sanity_check(logger,level,Level) of + ok -> call({set_module_level,Module,Level}); + Error -> Error + end; +set_module_level(Module,_) -> + {error,{not_a_module,Module}}. + +reset_module_level(Module) when is_atom(Module) -> + call({reset_module_level,Module}); +reset_module_level(Module) -> + {error,{not_a_module,Module}}. + +cache_module_level(Module) -> + gen_server:cast(?SERVER,{cache_module_level,Module}). + + +set_config(Owner,Key,Value) -> + case sanity_check(Owner,Key,Value) of + ok -> call({update_config,Owner,#{Key=>Value}}); + Error -> Error + end. + +set_config(Owner,Config0) -> + case sanity_check(Owner,Config0) of + ok -> + Config = maps:merge(default_config(Owner),Config0), + call({set_config,Owner,Config}); + Error -> + Error + end. + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== + +init([]) -> + process_flag(trap_exit, true), + Tid = logger_config:new(?LOGGER_TABLE), + LoggerConfig = maps:merge(default_config(logger), + #{handlers=>[logger_simple]}), + logger_config:create(Tid,logger,LoggerConfig), + SimpleConfig0 = maps:merge(default_config(logger_simple), + #{filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS, + logger_simple=>#{buffer=>true}}), + %% If this fails, then the node should crash + {ok,SimpleConfig} = + logger_simple:adding_handler(logger_simple,SimpleConfig0), + logger_config:create(Tid,logger_simple,logger_simple,SimpleConfig), + {ok, #state{tid=Tid}}. + +handle_call({add_handler,Id,Module,HConfig}, _From, #state{tid=Tid}=State) -> + Reply = + case logger_config:exist(Tid,Id) of + true -> + {error,{already_exist,Id}}; + false -> + %% inform the handler + case call_h(Module,adding_handler,[Id,HConfig],{ok,HConfig}) of + {ok,HConfig1} -> + 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; + {error,HReason} -> + {error,{handler_not_added,HReason}} + end + end, + {reply,Reply,State}; +handle_call({remove_handler,HandlerId}, _From, #state{tid=Tid}=State) -> + Reply = + case logger_config:get(Tid,HandlerId) of + {ok,{Module,_}} -> + {ok,Config} = do_get_config(Tid,logger), + Handlers0 = maps:get(handlers,Config,[]), + Handlers = lists:delete(HandlerId,Handlers0), + %% inform the handler + _ = call_h(Module,removing_handler,[HandlerId],ok), + do_set_config(Tid,logger,Config#{handlers=>Handlers}), + logger_config:delete(Tid,HandlerId), + ok; + _ -> + {error,{not_found,HandlerId}} + end, + {reply,Reply,State}; +handle_call({add_filter,Id,Filter}, _From,#state{tid=Tid}=State) -> + Reply = do_add_filter(Tid,Id,Filter), + {reply,Reply,State}; +handle_call({remove_filter,Id,FilterId}, _From, #state{tid=Tid}=State) -> + Reply = do_remove_filter(Tid,Id,FilterId), + {reply,Reply,State}; +handle_call({update_config,Id,NewConfig}, _From, #state{tid=Tid}=State) -> + Reply = + case logger_config:get(Tid,Id) of + {ok,{Module,OldConfig}} -> + Config = maps:merge(OldConfig,NewConfig), + case call_h(Module,changing_config,[Id,OldConfig,Config], + {ok,Config}) of + {ok,Config1} -> + do_set_config(Tid,Id,Config1); + Error -> + Error + end; + {ok,OldConfig} -> + Config = maps:merge(OldConfig,NewConfig), + do_set_config(Tid,Id,Config); + Error -> + Error + end, + {reply,Reply,State}; +handle_call({set_config,logger,Config}, _From, #state{tid=Tid}=State) -> + Reply = do_set_config(Tid,logger,Config), + {reply,Reply,State}; +handle_call({set_config,HandlerId,Config}, _From, #state{tid=Tid}=State) -> + Reply = + case logger_config:get(Tid,HandlerId) of + {ok,{Module,OldConfig}} -> + case call_h(Module,changing_config,[HandlerId,OldConfig,Config], + {ok,Config}) of + {ok,Config1} -> + do_set_config(Tid,HandlerId,Config1); + Error -> + Error + end; + _ -> + {error,{not_found,HandlerId}} + end, + {reply,Reply,State}; +handle_call({set_module_level,Module,Level}, _From, #state{tid=Tid}=State) -> + Reply = logger_config:set_module_level(Tid,Module,Level), + {reply,Reply,State}; +handle_call({reset_module_level,Module}, _From, #state{tid=Tid}=State) -> + Reply = logger_config:reset_module_level(Tid,Module), + {reply,Reply,State}. + +handle_cast({cache_module_level,Module}, #state{tid=Tid}=State) -> + logger_config:cache_module_level(Tid,Module), + {noreply, State}. + +%% Interface for those who can't call the API - e.g. the emulator, or +%% places related to code loading. +%% +%% This can also be log events from remote nodes which are sent from +%% logger.erl when the group leader of the client process is on a +%% same node as the client process itself. +handle_info({log,Level,Format,Args,Meta}, State) -> + logger:log(Level,Format,Args,Meta), + {noreply, State}; +handle_info({log,Level,Report,Meta}, State) -> + logger:log(Level,Report,Meta), + {noreply, State}; +handle_info({Ref,_Reply},State) when is_reference(Ref) -> + %% Assuming this is a timed-out gen_server reply - ignoring + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +call(Request) -> + case whereis(?SERVER) of + Pid when Pid==self() -> + {error,{attempting_syncronous_call_to_self,Request}}; + _ -> + gen_server:call(?SERVER,Request,?DEFAULT_LOGGER_CALL_TIMEOUT) + end. + +do_add_filter(Tid,Id,{FId,_} = Filter) -> + case do_get_config(Tid,Id) of + {ok,Config} -> + Filters = maps:get(filters,Config,[]), + case lists:keymember(FId,1,Filters) of + true -> + {error,{already_exist,FId}}; + false -> + do_set_config(Tid,Id,Config#{filters=>[Filter|Filters]}) + end; + Error -> + Error + end. + +do_remove_filter(Tid,Id,FilterId) -> + case do_get_config(Tid,Id) of + {ok,Config} -> + Filters0 = maps:get(filters,Config,[]), + case lists:keytake(FilterId,1,Filters0) of + {value,_,Filters} -> + do_set_config(Tid,Id,Config#{filters=>Filters}); + false -> + {error,{not_found,FilterId}} + end; + Error -> + Error + end. + +do_get_config(Tid,Id) -> + case logger_config:get(Tid,Id) of + {ok,{_,Config}} -> + {ok,Config}; + {ok,Config} -> + {ok,Config}; + Error -> + Error + end. + +do_set_config(Tid,Id,Config) -> + logger_config:set(Tid,Id,Config), + ok. + +default_config(logger) -> + #{level=>info, + filters=>[], + filter_default=>log, + handlers=>[]}; +default_config(_) -> + #{level=>info, + filters=>[], + filter_default=>log, + formatter=>{?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}}. + +sanity_check(Owner,Key,Value) -> + sanity_check_1(Owner,[{Key,Value}]). + +sanity_check(HandlerId,Config) when is_map(Config) -> + sanity_check_1(HandlerId,maps:to_list(Config)); +sanity_check(_,Config) -> + {error,{invalid_handler_config,Config}}. + +sanity_check_1(Owner,Config) when is_list(Config) -> + try + Type = get_type(Owner), + check_config(Type,Config) + catch throw:Error -> {error,Error} + end. + +get_type(logger) -> + logger; +get_type(Id) -> + check_id(Id), + handler. + +check_config(Owner,[{level,Level}|Config]) -> + check_level(Level), + check_config(Owner,Config); +check_config(logger,[{handlers,Handlers}|Config]) -> + check_handlers(Handlers), + check_config(logger,Config); +check_config(Owner,[{filters,Filters}|Config]) -> + check_filters(Filters), + check_config(Owner,Config); +check_config(Owner,[{filter_default,FD}|Config]) -> + check_filter_default(FD), + check_config(Owner,Config); +check_config(handler,[{formatter,Formatter}|Config]) -> + check_formatter(Formatter), + check_config(handler,Config); +check_config(logger,[C|_]) -> + throw({invalid_logger_config,C}); +check_config(handler,[{_,_}|Config]) -> + %% Arbitrary config elements are allowed for handlers + check_config(handler,Config); +check_config(_,[]) -> + ok. + +check_id(Id) when is_atom(Id) -> + ok; +check_id(Id) -> + throw({invalid_id,Id}). + +check_mod(Mod) when is_atom(Mod) -> + ok; +check_mod(Mod) -> + throw({invalid_module,Mod}). + +check_level({LevelInt,cached}) when LevelInt>=?EMERGENCY, LevelInt= + ok; +check_level(Level) -> + case lists:member(Level,?LEVELS) of + true -> + ok; + false -> + throw({invalid_level,Level}) + end. + +check_handlers([Id|Handlers]) -> + check_id(Id), + check_handlers(Handlers); +check_handlers([]) -> + ok; +check_handlers(Handlers) -> + throw({invalid_handlers,Handlers}). + +check_filters([{Id,{Fun,_Args}}|Filters]) when is_atom(Id), is_function(Fun,2) -> + check_filters(Filters); +check_filters([Filter|_]) -> + throw({invalid_filter,Filter}); +check_filters([]) -> + ok; +check_filters(Filters) -> + throw({invalid_filters,Filters}). + +check_filter_default(FD) when FD==stop; FD==log -> + ok; +check_filter_default(FD) -> + throw({invalid_filter_default,FD}). + +check_formatter({logger_formatter,Config}) when is_map(Config) -> + check_logger_formatter_config(maps:to_list(Config)); +check_formatter({logger_formatter,Config}) -> + throw({invalid_formatter_config,Config}); +check_formatter({Mod,_}) -> + %% no knowledge of other formatters + check_mod(Mod); +check_formatter(Formatter) -> + throw({invalid_formatter,Formatter}). + + +check_logger_formatter_config([{template,T}|Config]) when is_list(T) -> + case lists:all(fun(X) when is_atom(X) -> true; + (X) when is_tuple(X), is_atom(element(1,X)) -> true; + (X) when is_list(X) -> io_lib:printable_unicode_list(X); + (_) -> false + end, + T) of + true -> + check_logger_formatter_config(Config); + false -> + throw({invalid_formatter_template,T}) + end; +check_logger_formatter_config([{legacy_header,LH}|Config]) when is_boolean(LH) -> + check_logger_formatter_config(Config); +check_logger_formatter_config([{single_line,SL}|Config]) when is_boolean(SL) -> + check_logger_formatter_config(Config); +check_logger_formatter_config([{utc,Utc}|Config]) when is_boolean(Utc) -> + check_logger_formatter_config(Config); +check_logger_formatter_config([C|_]) -> + throw({invalid_formatter_config,C}); +check_logger_formatter_config([]) -> + ok. + +call_h(Module, Function, Args, DefRet) -> + %% Not calling code:ensure_loaded + erlang:function_exported here, + %% since in some rare terminal cases, the code_server might not + %% exist and we'll get a deadlock in removing the handler. + try apply(Module, Function, Args) + catch + C:R:S -> + case {C,R,S} of + {error,undef,[{Module,Function,Args,_}|_]} -> + DefRet; + _ -> + {error,{callback_crashed, + {C,R,logger:filter_stacktrace(?MODULE,S)}}} + end + end. diff --git a/lib/kernel/src/logger_simple.erl b/lib/kernel/src/logger_simple.erl new file mode 100644 index 0000000000..61387ac7c9 --- /dev/null +++ b/lib/kernel/src/logger_simple.erl @@ -0,0 +1,238 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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_simple). + +-export([adding_handler/2, removing_handler/1, 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, + [link,monitor,{message_queue_data,off_heap}]), + receive + {'DOWN',Ref,process,Pid,Reason} -> + {error,Reason}; + {Pid,started} -> + erlang:demonitor(Ref), + {ok,Config} + end; + _ -> + {error,{handler_process_name_already_exists,?MODULE}} + end. + +removing_handler(?MODULE) -> + case whereis(?MODULE) of + undefined -> + ok; + Pid -> + Ref = erlang:monitor(process,Pid), + unlink(Pid), + Pid ! stop, + receive {'DOWN',Ref,process,Pid,_} -> + ok + end + end. + +log(#{meta:=#{error_logger:=#{tag:=info_report,type:=Type}}},_Config) + when Type=/=std_info -> + %% Skip info reports that are not 'std_info' (ref simple logger in + %% error_logger) + ok; +log(#{msg:=_,meta:=#{time:=_}}=Log,_Config) -> + _ = case whereis(?MODULE) of + undefined -> + %% Is the node on the way down? Real emergency? + %% Log directly from client just to get it out + do_log( + #{level=>error, + msg=>{report,{error,simple_handler_process_dead}}, + meta=>#{time=>erlang:monotonic_time(microsecond)}}), + do_log(Log); + _ -> + ?MODULE ! {log,Log} + end, + ok; +log(_,_) -> + %% Unexpected log. + %% We don't want to crash the simple logger, so ignore this. + ok. + +%%%----------------------------------------------------------------- +%%% Process +init(Starter,Config) -> + 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,Timeout) -> + receive + stop -> + ok; + {get_buffer,From} -> + loop(Buffer#{send_to=>From},0); + {log,#{msg:=_,meta:=#{time:=_}}=Log} -> + do_log(Log), + loop(update_buffer(Buffer,Log),Timeout); + _ -> + %% 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) + 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]}. + +drop_msg(0) -> + []; +drop_msg(N) -> + [#{level=>info, + msg=>{"Simple handler buffer full, dropped ~w messages",[N]}, + meta=>#{time=>erlang:monotonic_time(microsecond)}}]. + +%%%----------------------------------------------------------------- +%%% Internal + +%% Can't do io_lib:format + +do_log(#{msg:={report,Report}, + meta:=#{time:=T,error_logger:=#{type:=Type}}}) -> + display_date(T), + display_report(Type,Report); +do_log(#{msg:=Msg,meta:=#{time:=T}}) -> + %% Maybe buffer event - if next handler shall be able to + %% re-display it + display_date(T), + display(Msg). + +display_date(Timestamp0) when is_integer(Timestamp0) -> + Timestamp = Timestamp0 + erlang:time_offset(microsecond), + Micro = Timestamp rem 1000000, + Sec = Timestamp div 1000000, + {{Y,Mo,D},{H,Mi,S}} = erlang:universaltime_to_localtime( + erlang:posixtime_to_universaltime(Sec)), + erlang:display_string( + integer_to_list(Y) ++ "-" ++ + pad(Mo,2) ++ "-" ++ + pad(D,2) ++ " " ++ + pad(H,2) ++ ":" ++ + pad(Mi,2) ++ ":" ++ + pad(S,2) ++ "." ++ + pad(Micro,6) ++ " "). + +pad(Int,Size) when is_integer(Int) -> + pad(integer_to_list(Int),Size); +pad(Str,Size) when length(Str)==Size -> + Str; +pad(Str,Size) -> + pad([$0|Str],Size). + +display({string,Chardata}) -> + try unicode:characters_to_list(Chardata) of + String -> erlang:display_string(String), erlang:display_string("\n") + catch _:_ -> erlang:display(Chardata) + end; +display({report,Report}) when is_map(Report) -> + display_report(maps:to_list(Report)); +display({report,Report}) -> + display_report(Report); +display({F, A}) when is_list(F), is_list(A) -> + erlang:display_string(F ++ "\n"), + [begin + erlang:display_string("\t"), + erlang:display(Arg) + end || Arg <- A], + ok. + +display_report(Atom, A) when is_atom(Atom) -> + %% The widest atom seems to be 'supervisor_report' at 17. + ColumnWidth = 20, + AtomString = atom_to_list(Atom), + AtomLength = length(AtomString), + Padding = lists:duplicate(ColumnWidth - AtomLength, $\s), + erlang:display_string(AtomString ++ Padding), + display_report(A); +display_report(F, A) -> + erlang:display({F, A}). + +display_report([A, []]) -> + %% Special case for crash reports when process has no links + display_report(A); +display_report(A = [_|_]) -> + case lists:all(fun({Key,_Value}) -> is_atom(Key); (_) -> false end, A) of + true -> + erlang:display_string("\n"), + lists:foreach( + fun({Key, Value}) -> + erlang:display_string( + " " ++ + atom_to_list(Key) ++ + ": "), + erlang:display(Value) + end, A); + false -> + erlang:display(A) + end; +display_report(A) -> + erlang:display(A). diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl new file mode 100644 index 0000000000..cbc9db372c --- /dev/null +++ b/lib/kernel/src/logger_std_h.erl @@ -0,0 +1,799 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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_std_h). + +-behaviour(gen_server). + +-include("logger.hrl"). +-include("logger_internal.hrl"). +-include("logger_h_common.hrl"). + +-include_lib("kernel/include/file.hrl"). + +%% API +-export([start_link/3, info/1, filesync/1, reset/1]). + +%% gen_server and proc_lib callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +%% logger callbacks +-export([log/2, adding_handler/2, removing_handler/1, + changing_config/3, swap_buffer/2]). + +%%%=================================================================== +%%% API +%%%=================================================================== + +%%%----------------------------------------------------------------- +%%% Start a standard handler process and link to caller. +%%% This function is called by the kernel supervisor when this +%%% handler process gets added +-spec start_link(Name, Config, HandlerState) -> {ok,Pid} | {error,Reason} when + Name :: atom(), + Config :: logger:config(), + HandlerState :: map(), + Pid :: pid(), + Reason :: term(). + +start_link(Name, Config, HandlerState) -> + proc_lib:start_link(?MODULE,init,[[Name,Config,HandlerState]]). + +%%%----------------------------------------------------------------- +%%% +-spec filesync(Name) -> ok | {error,Reason} when + Name :: atom(), + Reason :: handler_busy | {badarg,term()}. + +filesync(Name) when is_atom(Name) -> + try + gen_server:call(Name, filesync, ?DEFAULT_CALL_TIMEOUT) + catch + _:{timeout,_} -> {error,handler_busy} + end; +filesync(Name) -> + {error,{badarg,{filesync,[Name]}}}. + +%%%----------------------------------------------------------------- +%%% +-spec info(Name) -> Info | {error,Reason} when + Name :: atom(), + Info :: term(), + Reason :: handler_busy | {badarg,term()}. + +info(Name) when is_atom(Name) -> + try + gen_server:call(Name, info, ?DEFAULT_CALL_TIMEOUT) + catch + _:{timeout,_} -> {error,handler_busy} + end; +info(Name) -> + {error,{badarg,{info,[Name]}}}. + +%%%----------------------------------------------------------------- +%%% +-spec reset(Name) -> ok | {error,Reason} when + Name :: atom(), + Reason :: handler_busy | {badarg,term()}. + +reset(Name) when is_atom(Name) -> + try + gen_server:call(Name, reset, ?DEFAULT_CALL_TIMEOUT) + catch + _:{timeout,_} -> {error,handler_busy} + end; +reset(Name) -> + {error,{badarg,{reset,[Name]}}}. + + +%%%=================================================================== +%%% logger callbacks +%%%=================================================================== + +%%%----------------------------------------------------------------- +%%% Handler being added +adding_handler(Name, Config) -> + case check_config(adding, Name, Config) of + {ok, Config1} -> + %% create initial handler state by merging defaults with config + HConfig = maps:get(?MODULE, Config1, #{}), + HState = maps:merge(get_init_state(), HConfig), + case logger_h_common:overload_levels_ok(HState) of + true -> + case start(Name, Config1, HState) of + ok -> + %% Make sure wait_for_buffer is not stored, so we + %% won't hang and wait for buffer on a restart + {ok, maps:remove(wait_for_buffer,Config1)}; + Error -> + Error + end; + false -> + #{toggle_sync_qlen := TSQL, + drop_new_reqs_qlen := DNRQL, + flush_reqs_qlen := FRQL} = HState, + {error,{invalid_levels,{TSQL,DNRQL,FRQL}}} + end; + Error -> + Error + end. + +%%%----------------------------------------------------------------- +%%% Updating handler config +changing_config(Name, + OldConfig=#{id:=Id, ?MODULE:=#{type:=Type}}, + NewConfig=#{id:=Id}) -> + MyConfig = maps:get(?MODULE, NewConfig, #{}), + case maps:get(type, MyConfig, Type) of + Type -> + MyConfig1 = MyConfig#{type=>Type}, + changing_config1(Name, OldConfig, + NewConfig#{?MODULE=>MyConfig1}); + _ -> + {error,{illegal_config_change,OldConfig,NewConfig}} + end; +changing_config(_Name, OldConfig, NewConfig) -> + {error,{illegal_config_change,OldConfig,NewConfig}}. + +changing_config1(Name, OldConfig, NewConfig) -> + case check_config(changing, Name, NewConfig) of + Result = {ok,NewConfig1} -> + try gen_server:call(Name, {change_config,OldConfig,NewConfig1}, + ?DEFAULT_CALL_TIMEOUT) of + ok -> Result; + HError -> HError + catch + _:{timeout,_} -> {error,handler_busy} + end; + Error -> + Error + end. + +check_config(adding, Name, Config0) -> + %% Merge in defaults on top level + Config = maps:merge(#{id => Name}, Config0), + %% Merge in defaults on handler level + MyConfig0 = maps:get(?MODULE, Config, #{}), + MyConfig = maps:merge(#{type => standard_io}, + MyConfig0), + case check_my_config(maps:to_list(MyConfig)) of + ok -> + {ok,Config#{?MODULE=>MyConfig}}; + Error -> + Error + end; +check_config(changing, _Name, Config) -> + MyConfig = maps:get(?MODULE, Config, #{}), + case check_my_config(maps:to_list(MyConfig)) of + ok -> {ok,Config}; + Error -> Error + end. + +check_my_config([{type,Type} | Config]) when Type == standard_io; + Type == standard_error -> + check_my_config(Config); +check_my_config([{type,{file,File}} | Config]) when is_list(File) -> + check_my_config(Config); +check_my_config([{type,{file,File,Modes}} | Config]) when is_list(File), + is_list(Modes) -> + check_my_config(Config); +check_my_config([Other | Config]) -> + case logger_h_common:check_common_config(Other) of + valid -> + check_my_config(Config); + invalid -> + {error,{invalid_config,?MODULE,Other}} + end; +check_my_config([]) -> + ok. + + +%%%----------------------------------------------------------------- +%%% Handler being removed +removing_handler(Name) -> + stop(Name). + +%%%----------------------------------------------------------------- +%%% Get buffer when swapping from simple handler +swap_buffer(Name,Buffer) -> + case whereis(Name) of + undefined -> + ok; + _ -> + Name ! {buffer,Buffer} + end. + +%%%----------------------------------------------------------------- +%%% Log a string or report +-spec log(Log, Config) -> ok | dropped when + Log :: logger:log(), + Config :: logger:config(). + +log(Log,Config=#{id:=Name}) -> + %% if the handler has crashed, we must drop this request + %% and hope the handler restarts so we can try again + true = is_pid(whereis(Name)), + Bin = logger_h_common:log_to_binary(Log,Config), + logger_h_common:call_cast_or_drop(Name, Bin). + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== + +init([Name, Config, + State0 = #{type := Type, file_ctrl_sync_int := FileCtrlSyncInt}]) -> + register(Name, self()), + process_flag(trap_exit, true), + process_flag(message_queue_data, off_heap), + + ?init_test_hooks(), + ?start_observation(Name), + + case do_init(Name, Type) of + {ok,InitState} -> + catch ets:new(Name, [public, named_table]), + ?set_mode(Name, async), + State = maps:merge(State0, InitState), + T0 = ?timestamp(), + State1 = + ?merge_with_stats(State#{mode => async, + file_ctrl_sync => FileCtrlSyncInt, + last_qlen => 0, + last_log_ts => T0, + burst_win_ts => T0, + burst_msg_count => 0}), + proc_lib:init_ack({ok,self()}), + gen_server:cast(self(), {repeated_filesync,T0}), + enter_loop(Config, State1); + Error -> + logger_h_common:error_notify({init_handler,Name,Error}), + proc_lib:init_ack(Error) + end. + +do_init(Name, Std) when Std=:=standard_io; Std=:=standard_error -> + case open_log_file(Name, Std) of + {ok,FileCtrlPid} -> + {ok,#{id=>Name,type=>Std,file_ctrl_pid=>FileCtrlPid}}; + Error -> + Error + end; +do_init(Name, FileInfo) when is_tuple(FileInfo) -> + case open_log_file(Name, FileInfo) of + {ok,FileCtrlPid} -> + {ok,#{id=>Name,type=>FileInfo,file_ctrl_pid=>FileCtrlPid}}; + Error -> + Error + end. + +enter_loop(#{wait_for_buffer:=true}=Config,State) -> + State1 = + receive + {buffer,Buffer} -> + lists:foldl( + fun(Log,S) -> + Bin = logger_h_common:log_to_binary(Log,Config), + {_,S1} = do_log(Bin,cast,S), + S1 + end, + State, + Buffer) + end, + gen_server:enter_loop(?MODULE,[],State1); +enter_loop(_Config,State) -> + gen_server:enter_loop(?MODULE,[],State). + +%% This is the synchronous log request. +handle_call({log, Bin}, _From, State) -> + {Result,State1} = do_log(Bin, call, State), + %% Result == ok | dropped + {reply,Result, State1}; + +handle_call(filesync, _From, State = #{type := Type, + file_ctrl_pid := FileCtrlPid}) -> + if is_atom(Type) -> + {reply, ok, State}; + true -> + {reply, file_ctrl_filesync_sync(FileCtrlPid), State} + end; + +handle_call({change_config,_OldConfig,NewConfig}, _From, + State = #{filesync_repeat_interval := FSyncInt0, + last_log_ts := LastLogTS}) -> + HConfig = maps:get(?MODULE, NewConfig, #{}), + State1 = maps:merge(State, HConfig), + case logger_h_common:overload_levels_ok(State1) of + true -> + _ = + case maps:get(filesync_repeat_interval, HConfig, undefined) of + undefined -> + ok; + no_repeat -> + _ = logger_h_common:cancel_timer(maps:get(rep_sync_tref, + State, + undefined)); + FSyncInt0 -> + ok; + _FSyncInt1 -> + _ = logger_h_common:cancel_timer(maps:get(rep_sync_tref, + State, + undefined)), + gen_server:cast(self(), {repeated_filesync, + LastLogTS}) + end, + {reply, ok, State1}; + false -> + #{toggle_sync_qlen := TSQL, + drop_new_reqs_qlen := DNRQL, + flush_reqs_qlen := FRQL} = State1, + {reply, {error,{invalid_levels,{TSQL,DNRQL,FRQL}}}, State} + end; + +handle_call(info, _From, State) -> + {reply, State, State}; + +handle_call(reset, _From, State) -> + State1 = ?merge_with_stats(State), + {reply, ok, State1#{last_qlen => 0, + last_log_ts => ?timestamp()}}; + +handle_call(stop, _From, State) -> + {stop, {shutdown,stopped}, ok, State}. + +%% This is the asynchronous log request. +handle_cast({log, Bin}, State) -> + {_,State1} = do_log(Bin, cast, State), + {noreply, State1}; + +%% If FILESYNC_REPEAT_INTERVAL is set to a millisec value, this +%% clause gets called repeatedly by the handler. In order to +%% guarantee that a filesync *always* happens after the last log +%% request, the repeat operation must be active! +handle_cast({repeated_filesync,LastLogTS0}, + State = #{type := Type, + file_ctrl_pid := FileCtrlPid, + filesync_repeat_interval := FSyncInt, + last_log_ts := LastLogTS1}) -> + State1 = + if not is_atom(Type), is_integer(FSyncInt) -> + %% only do filesync if something has been + %% written since last time we checked + if LastLogTS1 == LastLogTS0 -> + ok; + true -> + file_ctrl_filesync_async(FileCtrlPid) + end, + {ok,TRef} = + timer:apply_after(FSyncInt, gen_server,cast, + [self(),{repeated_filesync,LastLogTS1}]), + State#{rep_sync_tref => TRef}; + true -> + State + end, + {noreply,State1}. + +handle_info({'EXIT',Pid,Why}, State = #{id := Name, type := FileInfo}) -> + case maps:get(file_ctrl_pid, State, undefined) of + Pid -> + %% file error, terminate handler + logger_h_common:handler_exit(Name, + {error,{write_failed,FileInfo,Why}}); + _Other -> + %% ignore EXIT + ok + end, + {noreply, State}; + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(Reason, State = #{id:=Name, file_ctrl_pid:=FWPid, + type:=_FileInfo}) -> + _ = logger_h_common:cancel_timer(maps:get(rep_sync_tref, State, + undefined)), + case is_process_alive(FWPid) of + true -> + unlink(FWPid), + _ = file_ctrl_stop(FWPid), + MRef = erlang:monitor(process, FWPid), + receive + {'DOWN',MRef,_,_,_} -> + ok + after + ?DEFAULT_CALL_TIMEOUT -> + exit(FWPid, kill) + end; + false -> + ok + end, + logger_h_common:stop_or_restart(Name, Reason, State). + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== + +%%%----------------------------------------------------------------- +%%% +get_init_state() -> + #{toggle_sync_qlen => ?TOGGLE_SYNC_QLEN, + drop_new_reqs_qlen => ?DROP_NEW_REQS_QLEN, + flush_reqs_qlen => ?FLUSH_REQS_QLEN, + enable_burst_limit => ?ENABLE_BURST_LIMIT, + burst_limit_size => ?BURST_LIMIT_SIZE, + burst_window_time => ?BURST_WINDOW_TIME, + enable_kill_overloaded => ?ENABLE_KILL_OVERLOADED, + handler_overloaded_qlen => ?HANDLER_OVERLOADED_QLEN, + handler_overloaded_mem => ?HANDLER_OVERLOADED_MEM, + handler_restart_after => ?HANDLER_RESTART_AFTER, + file_ctrl_sync_int => ?CONTROLLER_SYNC_INTERVAL, + filesync_ok_qlen => ?FILESYNC_OK_QLEN, + filesync_repeat_interval => ?FILESYNC_REPEAT_INTERVAL}. + +%%%----------------------------------------------------------------- +%%% Add a standard handler to the logger. +%%% This starts a dedicated handler process which should always +%%% exist if the handler is registered with logger (and should not +%%% exist if the handler is not registered). +%%% +%%% Handler specific config should be provided with a sub map associated +%%% with a key named the same as this module, e.g: +%%% +%%% Config = #{logger_std_h => #{toggle_sync_qlen => 50} +%%% +%%% The standard handler process is linked to logger_sup, which is +%%% part of the kernel application's supervision tree. +start(Name, Config, HandlerState) -> + LoggerStdH = + #{id => Name, + start => {?MODULE, start_link, [Name,Config,HandlerState]}, + restart => temporary, + shutdown => 2000, + type => worker, + modules => [?MODULE]}, + case supervisor:start_child(logger_sup, LoggerStdH) of + {ok,_Pid} -> + ok; + Error -> + Error + end. + +%%%----------------------------------------------------------------- +%%% Stop and remove the handler. +stop(Name) -> + case whereis(Name) of + undefined -> + ok; + _ -> + %% We don't want to do supervisor:terminate_child here + %% since we need to distinguish this explicit stop from a + %% system termination in order to avoid circular attempts + %% at removing the handler (implying deadlocks and + %% timeouts). + _ = gen_server:call(Name,stop), + _ = supervisor:delete_child(logger_sup, Name), + ok + end. + +%%%----------------------------------------------------------------- +%%% Logging and overload control. +-define(update_file_ctrl_sync(C, Interval), + if C == 0 -> Interval; + true -> C-1 end). + +%% check for overload between every request (and set Mode to async, +%% sync or drop accordingly), but never flush the whole mailbox +%% before LogWindowSize requests have been handled +do_log(Bin, CallOrCast, State = #{id:=Name}) -> + T1 = ?timestamp(), + + %% check if the handler is getting overloaded, or if it's + %% recovering from overload (the check must be done for each + %% request to react quickly to large bursts of requests and + %% to ensure that the handler can never end up in drop mode + %% with an empty mailbox, which would stop operation) + {Mode1,QLen,Mem,State1} = logger_h_common:check_load(State), + + %% kill the handler if it can't keep up with the load + logger_h_common:kill_if_choked(Name, QLen, Mem, State), + + if Mode1 == flush -> + flush(Name, QLen, T1, State1); + true -> + write(Name, Mode1, T1, Bin, CallOrCast, State1) + end. + +%% this clause is called by do_log/3 after an overload check +%% has been performed, where QLen > FlushQLen +flush(Name, _QLen0, T1, State=#{last_log_ts := _T0}) -> + %% flush messages in the mailbox (a limited number in + %% order to not cause long delays) + _NewFlushed = logger_h_common:flush_log_requests(?FLUSH_MAX_N), + + %% because of the receive loop when flushing messages, the + %% handler will be scheduled out often and the mailbox could + %% grow very large, so we'd better check the queue again here + {_,_QLen1} = process_info(self(), message_queue_len), + ?observe(Name,{max_qlen,_QLen1}), + + %% Add 1 for the current log request + ?observe(Name,{flushed,_NewFlushed+1}), + + State1 = ?update_max_time(?diff_time(T1,_T0),State), + {dropped,?update_other(flushed,FLUSHED,_NewFlushed, + State1#{mode => ?set_mode(Name,async), + last_qlen => 0, + last_log_ts => T1})}. + +%% this clause is called to write to file +write(Name, Mode, T1, Bin, _CallOrCast, + State = #{file_ctrl_pid := FileCtrlPid, + file_ctrl_sync := FileCtrlSync, + last_qlen := LastQLen, + last_log_ts := T0, + file_ctrl_sync_int := FileCtrlSyncInt}) -> + %% check if we need to limit the number of writes + %% during a burst of log requests + {DoWrite,BurstWinT,BurstMsgCount} = logger_h_common:limit_burst(State), + + %% only send a synhrounous request to the file controller process + %% every FileCtrlSyncInt time, to give the handler time between + %% file writes so it can keep up with incoming messages + {Result,LastQLen1} = + if DoWrite, FileCtrlSync == 0 -> + ?observe(Name,{_CallOrCast,1}), + file_write_sync(FileCtrlPid, Bin, false), + {ok,element(2, process_info(self(), message_queue_len))}; + DoWrite -> + ?observe(Name,{_CallOrCast,1}), + file_write_async(FileCtrlPid, Bin), + {ok,LastQLen}; + not DoWrite -> + ?observe(Name,{flushed,1}), + {dropped,LastQLen} + end, + + %% Check if the time since the previous log request is long enough - + %% and the queue length small enough - to assume the mailbox has + %% been emptied, and if so, do filesync operation and reset mode to + %% async. Note that this is the best we can do to detect an idle + %% handler without setting a timer after each log call/cast. If the + %% time between two consecutive log requests is fast and no new + %% request comes in after the last one, idle state won't be detected! + Time = ?diff_time(T1,T0), + {Mode1,BurstMsgCount1} = + if (LastQLen1 < ?FILESYNC_OK_QLEN) andalso + (Time > ?IDLE_DETECT_TIME_USEC) -> + %% do filesync if necessary + case maps:get(type, State) of + Std when is_atom(Std) -> + ok; + _File -> + file_ctrl_filesync_async(FileCtrlPid) + end, + {?change_mode(Name, Mode, async),0}; + true -> + {Mode,BurstMsgCount} + end, + State1 = + ?update_calls_or_casts(_CallOrCast,1,State), + State2 = + ?update_max_time(Time, + State1#{mode => Mode1, + last_qlen := LastQLen1, + last_log_ts => T1, + burst_win_ts => BurstWinT, + burst_msg_count => BurstMsgCount1, + file_ctrl_sync => + ?update_file_ctrl_sync(FileCtrlSync, + FileCtrlSyncInt)}), + {Result,State2}. + +open_log_file(HandlerName, FileInfo) -> + case file_ctrl_start(HandlerName, FileInfo) of + OK = {ok,_FileCtrlPid} -> OK; + Error -> Error + end. + +do_open_log_file({file,File}) -> + do_open_log_file({file,File,[raw,append,delayed_write]}); + +do_open_log_file({file,File,[]}) -> + do_open_log_file({file,File,[raw,append,delayed_write]}); + +do_open_log_file({file,File,Modes}) -> + try + case filelib:ensure_dir(File) of + ok -> + file:open(File, Modes); + Error -> + Error + end + catch + _:Reason -> {error,Reason} + end. + +close_log_file(Std) when Std == standard_io; Std == standard_error -> + ok; +close_log_file(Fd) -> + _ = file:datasync(Fd), + _ = file:close(Fd). + +%%%----------------------------------------------------------------- +%%% File control process + +file_ctrl_start(HandlerName, FileInfo) -> + Starter = self(), + FileCtrlPid = + spawn_link(fun() -> + file_ctrl_init(HandlerName, FileInfo, Starter) + end), + receive + {FileCtrlPid,ok} -> + {ok,FileCtrlPid}; + {FileCtrlPid,Error} -> + Error + after + ?DEFAULT_CALL_TIMEOUT -> + {error,file_ctrl_process_not_started} + end. + +file_ctrl_stop(Pid) -> + Pid ! stop. + +file_write_async(Pid, Bin) -> + Pid ! {log,Bin}, + ok. + +file_write_sync(Pid, Bin, FileSync) -> + case file_ctrl_call(Pid, {log,self(),Bin,FileSync}) of + {error,Reason} -> + {error,{write_failed,Bin,Reason}}; + Result -> + Result + end. + +file_ctrl_filesync_async(Pid) -> + Pid ! filesync, + ok. + +file_ctrl_filesync_sync(Pid) -> + file_ctrl_call(Pid, {filesync,self()}). + +file_ctrl_call(Pid, Msg) -> + MRef = monitor(process, Pid), + Pid ! {Msg,MRef}, + receive + {MRef,Result} -> + demonitor(MRef, [flush]), + Result; + {'DOWN',MRef,_Type,_Object,Reason} -> + {error,Reason} + after + ?DEFAULT_CALL_TIMEOUT -> + {error,{no_response,Pid}} + end. + +file_ctrl_init(HandlerName, FileInfo, Starter) when is_tuple(FileInfo) -> + process_flag(message_queue_data, off_heap), + FileName = element(2, FileInfo), + case do_open_log_file(FileInfo) of + {ok,Fd} -> + Starter ! {self(),ok}, + file_ctrl_loop(Fd, file, FileName, false, ok, ok, HandlerName); + {error,Reason} -> + Starter ! {self(),{error,{open_failed,FileName,Reason}}} + end; +file_ctrl_init(HandlerName, StdDev, Starter) -> + Starter ! {self(),ok}, + file_ctrl_loop(StdDev, standard_io, StdDev, false, ok, ok, HandlerName). + +file_ctrl_loop(Fd, Type, DevName, Synced, + PrevWriteResult, PrevSyncResult, HandlerName) -> + receive + %% asynchronous request + {log,Bin} -> + Result = if Type == file -> + write_to_dev(Fd, Bin, DevName, + PrevWriteResult, HandlerName); + true -> + io:put_chars(Fd, Bin) + end, + file_ctrl_loop(Fd, Type, DevName, false, + Result, PrevSyncResult, HandlerName); + + %% synchronous request + {{log,From,Bin,FileSync},MRef} -> + if Type == file -> + %% check that file hasn't been deleted + CheckFile = + fun() -> {ok,_} = file:read_file_info(DevName) end, + spawn_link(CheckFile), + WResult = write_to_dev(Fd, Bin, DevName, + PrevWriteResult, HandlerName), + {Synced1,SResult} = + if not FileSync -> + {false,PrevSyncResult}; + true -> + case sync_dev(Fd, DevName, + PrevSyncResult, HandlerName) of + ok -> {true,ok}; + Error -> {false,Error} + end + end, + From ! {MRef,ok}, + file_ctrl_loop(Fd, Type, DevName, Synced1, + WResult, SResult, HandlerName); + true -> + _ = io:put_chars(Fd, Bin), + From ! {MRef,ok}, + file_ctrl_loop(Fd, Type, DevName, false, + ok, PrevSyncResult, HandlerName) + end; + + filesync when not Synced -> + Result = sync_dev(Fd, DevName, PrevSyncResult, HandlerName), + file_ctrl_loop(Fd, Type, DevName, true, + PrevWriteResult, Result, HandlerName); + + filesync -> + file_ctrl_loop(Fd, Type, DevName, true, + PrevWriteResult, PrevSyncResult, HandlerName); + + {{filesync,From},MRef} -> + Result = if not Synced -> + sync_dev(Fd, DevName, PrevSyncResult, HandlerName); + true -> + ok + end, + From ! {MRef,ok}, + file_ctrl_loop(Fd, Type, DevName, true, + PrevWriteResult, Result, HandlerName); + + stop -> + _ = close_log_file(Fd), + stopped + end. + +write_to_dev(Fd, Bin, FileName, PrevWriteResult, HandlerName) -> + case ?file_write(Fd, Bin) of + ok -> + ok; + PrevWriteResult -> + %% don't report same error twice + PrevWriteResult; + Error -> + logger_h_common:error_notify({HandlerName,write,FileName,Error}), + Error + end. + +sync_dev(Fd, DevName, PrevSyncResult, HandlerName) -> + case ?file_datasync(Fd) of + ok -> + ok; + PrevSyncResult -> + %% don't report same error twice + PrevSyncResult; + Error -> + logger_h_common:error_notify({HandlerName,filesync,DevName,Error}), + Error + end. + diff --git a/lib/kernel/src/logger_sup.erl b/lib/kernel/src/logger_sup.erl new file mode 100644 index 0000000000..4e4de94d5c --- /dev/null +++ b/lib/kernel/src/logger_sup.erl @@ -0,0 +1,53 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +-define(SERVER, ?MODULE). + +%%%=================================================================== +%%% API functions +%%%=================================================================== + +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +%%%=================================================================== +%%% Supervisor callbacks +%%%=================================================================== + +init([]) -> + + SupFlags = #{strategy => one_for_one, + intensity => 1, + period => 5}, + + {ok, {SupFlags, []}}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile index 03b6355056..8599a3d814 100644 --- a/lib/kernel/test/Makefile +++ b/lib/kernel/test/Makefile @@ -70,6 +70,15 @@ MODULES= \ interactive_shell_SUITE \ init_SUITE \ kernel_config_SUITE \ + logger_SUITE \ + logger_bench_SUITE \ + logger_disk_log_h_SUITE \ + logger_env_var_SUITE \ + logger_filters_SUITE \ + logger_formatter_SUITE \ + logger_legacy_SUITE \ + logger_simple_SUITE \ + logger_std_h_SUITE \ os_SUITE \ pg2_SUITE \ seq_trace_SUITE \ @@ -102,7 +111,7 @@ TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) INSTALL_PROGS= $(TARGET_FILES) EMAKEFILE=Emakefile -COVERFILE=kernel.cover +COVERFILE=kernel.cover logger.cover # ---------------------------------------------------- # Release directory specification @@ -149,7 +158,8 @@ release_tests_spec: make_emakefile $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)" $(INSTALL_DATA) $(APP_FILES) "$(RELSYSDIR)" - $(INSTALL_DATA) kernel.spec kernel_smoke.spec kernel_bench.spec \ + $(INSTALL_DATA) \ + kernel.spec kernel_smoke.spec kernel_bench.spec logger.spec \ $(EMAKEFILE) $(COVERFILE) "$(RELSYSDIR)" chmod -R u+w "$(RELSYSDIR)" @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) diff --git a/lib/kernel/test/kernel.spec b/lib/kernel/test/kernel.spec index 62afc9f97b..86d2155828 100644 --- a/lib/kernel/test/kernel.spec +++ b/lib/kernel/test/kernel.spec @@ -2,3 +2,4 @@ {config, "../test_server/ts.unix.config"}. {suites,"../kernel_test", all}. +{skip_suites,"../kernel_test",[logger_bench_SUITE],"Not ready"}. diff --git a/lib/kernel/test/logger.cover b/lib/kernel/test/logger.cover new file mode 100644 index 0000000000..b30bcfe920 --- /dev/null +++ b/lib/kernel/test/logger.cover @@ -0,0 +1,14 @@ +%% -*- erlang -*- +{incl_mods,[error_logger, + logger, + logger_backend, + logger_config, + logger_disk_log_h, + logger_h_common, + logger_filters, + logger_formatter, + logger_server, + logger_simple, + logger_std_h, + logger_sup]}. + diff --git a/lib/kernel/test/logger.spec b/lib/kernel/test/logger.spec new file mode 100644 index 0000000000..cd76a754a4 --- /dev/null +++ b/lib/kernel/test/logger.spec @@ -0,0 +1,11 @@ +%% -*-erlang-*- +{suites,"../kernel_test", [error_logger_SUITE, + error_logger_warn_SUITE, + logger_SUITE, + logger_disk_log_h_SUITE, + logger_env_var_SUITE, + logger_filters_SUITE, + logger_formatter_SUITE, + logger_legacy_SUITE, + logger_simple_SUITE, + logger_std_h_SUITE]}. diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl new file mode 100644 index 0000000000..0edce3e34c --- /dev/null +++ b/lib/kernel/test/logger_SUITE.erl @@ -0,0 +1,828 @@ +%% +%% %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_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("kernel/include/logger.hrl"). +-include_lib("kernel/src/logger_internal.hrl"). + +-define(str,"Log from "++atom_to_list(?FUNCTION_NAME)++ + ":"++integer_to_list(?LINE)). +-define(map_rep,#{function=>?FUNCTION_NAME, line=>?LINE}). +-define(keyval_rep,[{function,?FUNCTION_NAME}, {line,?LINE}]). + +-define(MY_LOC(N),#{mfa=>{?MODULE,?FUNCTION_NAME,?FUNCTION_ARITY}, + file=>?FILE, line=>?LINE-N}). + +-define(TRY(X), my_try(fun() -> X end)). + + +suite() -> + [{timetrap,{seconds,30}}]. + +init_per_suite(Config) -> + case logger:get_handler_config(logger_std_h) of + {ok,StdH} -> + ok = logger:remove_handler(logger_std_h), + [{logger_std_h,StdH}|Config]; + _ -> + Config + end. + +end_per_suite(Config) -> + case ?config(logger_std_h,Config) of + {HMod,HConfig} -> + ok = logger:add_handler(logger_std_h,HMod,HConfig); + _ -> + ok + end. + +init_per_group(_Group, Config) -> + Config. + +end_per_group(_Group, _Config) -> + ok. + +init_per_testcase(_TestCase, Config) -> + {ok,LC} = logger:get_logger_config(), + [{logger_config,LC}|Config]. + +end_per_testcase(Case, Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +groups() -> + []. + +all() -> + [start_stop, + add_remove_handler, + multiple_handlers, + add_remove_filter, + change_config, + set_formatter, + log_all_levels_api, + macros, + set_level, + set_level_module, + cache_level_module, + format_report, + filter_failed, + handler_failed, + config_sanity_check, + log_failed, + emulator, + via_logger_process, + other_node, + compare_levels, + process_metadata]. + +start_stop(_Config) -> + S = whereis(logger), + true = is_pid(S), + ok. + +add_remove_handler(_Config) -> + register(callback_receiver,self()), + {ok,#{handlers:=Hs0}} = logger:get_logger_config(), + {error,{not_found,h1}} = logger:get_handler_config(h1), + ok = logger:add_handler(h1,?MODULE,#{}), + [add] = test_server:messages_get(), + {ok,#{handlers:=Hs}} = logger:get_logger_config(), + [h1|Hs0] = Hs, + {ok,{?MODULE,#{level:=info,filters:=[],filter_default:=log}}} = % defaults + logger:get_handler_config(h1), + ok = logger:set_handler_config(h1,filter_default,stop), + [changing_config] = test_server:messages_get(), + ?LOG_INFO("hello",[]), + ok = check_no_log(), + ok = logger:set_handler_config(h1,filter_default,log), + [changing_config] = test_server:messages_get(), + {ok,{?MODULE,#{filter_default:=log}}} = logger:get_handler_config(h1), + ?LOG_INFO("hello",[]), + ok = check_logged(info,"hello",[],?MY_LOC(1)), + ok = logger:remove_handler(h1), + [remove] = test_server:messages_get(), + {ok,#{handlers:=Hs0}} = logger:get_logger_config(), + {error,{not_found,h1}} = logger:get_handler_config(h1), + {error,{not_found,h1}} = logger:remove_handler(h1), + logger:info("hello",[]), + ok = check_no_log(), + ok. + +add_remove_handler(cleanup,_Config) -> + logger:remove_handler(h1), + ok. + +multiple_handlers(_Config) -> + ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log}), + ok = logger:add_handler(h2,?MODULE,#{level=>error,filter_default=>log}), + ?LOG_ERROR("hello",[]), + ok = check_logged(error,"hello",[],?MY_LOC(1)), + ok = check_logged(error,"hello",[],?MY_LOC(2)), + ?LOG_INFO("hello",[]), + ok = check_logged(info,"hello",[],?MY_LOC(1)), + ok = check_no_log(), + ok. + +multiple_handlers(cleanup,_Config) -> + logger:remove_handler(h1), + logger:remove_handler(h2), + ok. + +add_remove_filter(_Config) -> + ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log}), + LF = {fun(Log,_) -> Log#{level=>error} end, []}, + ok = logger:add_logger_filter(lf,LF), + {error,{already_exist,lf}} = logger:add_logger_filter(lf,LF), + {error,{already_exist,lf}} = logger:add_logger_filter(lf,{fun(Log,_) -> + Log + end, []}), + ?LOG_INFO("hello",[]), + ok = check_logged(error,"hello",[],?MY_LOC(1)), + ok = check_no_log(), + + ok = logger:add_handler(h2,?MODULE,#{level=>info,filter_default=>log}), + HF = {fun(#{level:=error}=Log,_) -> + Log#{level=>mylevel}; + (_,_) -> + ignore + end, + []}, + ok = logger:add_handler_filter(h1,hf,HF), + {error,{already_exist,hf}} = logger:add_handler_filter(h1,hf,HF), + {error,{already_exist,hf}} = logger:add_handler_filter(h1,hf,{fun(Log,_) -> + Log + end, []}), + ?LOG_INFO("hello",[]), + ok = check_logged(mylevel,"hello",[],?MY_LOC(1)), + ok = check_logged(error,"hello",[],?MY_LOC(2)), + + ok = logger:remove_logger_filter(lf), + {error,{not_found,lf}} = logger:remove_logger_filter(lf), + + ?LOG_INFO("hello",[]), + ok = check_logged(info,"hello",[],?MY_LOC(1)), + ok = check_logged(info,"hello",[],?MY_LOC(2)), + + ?LOG_ERROR("hello",[]), + ok = check_logged(mylevel,"hello",[],?MY_LOC(1)), + ok = check_logged(error,"hello",[],?MY_LOC(2)), + + ok = logger:remove_handler_filter(h1,hf), + {error,{not_found,hf}} = logger:remove_handler_filter(h1,hf), + ?LOG_INFO("hello",[]), + ok = check_logged(info,"hello",[],?MY_LOC(1)), + ok = check_logged(info,"hello",[],?MY_LOC(2)), + + ?LOG_ERROR("hello",[]), + ok = check_logged(error,"hello",[],?MY_LOC(1)), + ok = check_logged(error,"hello",[],?MY_LOC(2)), + ok. + +add_remove_filter(cleanup,_Config) -> + logger:remove_logger_filter(lf), + logger:remove_handler(h1), + logger:remove_handler(h2), + ok. + +change_config(_Config) -> + %% Overwrite handler config - check that defaults are added + ok = logger:add_handler(h1,?MODULE,#{level=>debug,custom=>custom}), + {ok,{?MODULE,#{level:=debug,filter_default:=log,custom:=custom}}} = + logger:get_handler_config(h1), + register(callback_receiver,self()), + ok = logger:set_handler_config(h1,#{filter_default=>stop}), + [changing_config] = test_server:messages_get(), + {ok,{?MODULE,#{level:=info,filter_default:=stop}=C2}} = + logger:get_handler_config(h1), + false = maps:is_key(custom,C2), + {error,fail} = logger:set_handler_config(h1,#{fail=>true}), + {error,{attempting_syncronous_call_to_self,_}} = + logger:set_handler_config( + h1,#{call=>fun() -> logger:set_module_level(?MODULE,debug) end}), + {ok,{?MODULE,C2}} = logger:get_handler_config(h1), + + %% Change one key only + {error,fail} = logger:set_handler_config(h1,fail,true), + ok = logger:set_handler_config(h1,custom,custom), + [changing_config] = test_server:messages_get(), + {ok,{?MODULE,#{custom:=custom}=C3}} = logger:get_handler_config(h1), + C2 = maps:remove(custom,C3), + + %% Overwrite logger config - check that defaults are added + {ok,LConfig} = logger:get_logger_config(), + ok = logger:set_logger_config(#{filter_default=>stop}), + {ok,#{level:=info,filters:=[],handlers:=[],filter_default:=stop}=LC1} = + logger:get_logger_config(), + 4 = maps:size(LC1), + + %% Change one key only + ok = logger:set_logger_config(handlers,[h1]), + {ok,#{level:=info,filters:=[],handlers:=[h1],filter_default:=stop}} = + logger:get_logger_config(), + + %% Cleanup + ok = logger:set_logger_config(LConfig), + [] = test_server:messages_get(), + + ok. + +change_config(cleanup,Config) -> + logger:remove_handler(h1), + LC = ?config(logger_config,Config), + logger:set_logger_config(LC), + ok. + +set_formatter(_Config) -> + {error,{not_found,h1}}=logger:set_handler_config(h1,formatter,{?MODULE,[]}), + ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log}), + ok = logger:set_handler_config(h1,formatter,{?MODULE,[]}), + logger:info("hello",[]), + receive + {_Log,#{formatter:={?MODULE,[]}}} -> + ok + after 500 -> + ct:fail({timeout,no_log,process_info(self(),messages)}) + end, + ok. + +set_formatter(cleanup,_Config) -> + logger:remove_handler(h1), + ok. + +log_all_levels_api(_Config) -> + ok = logger:set_logger_config(level,debug), + ok = logger:add_handler(h1,?MODULE,#{level=>debug,filter_default=>log}), + test_api(emergency), + test_api(alert), + test_api(critical), + test_api(error), + test_api(warning), + test_api(notice), + test_api(info), + test_api(debug), + test_log_function(emergency), + ok. + +log_all_levels_api(cleanup,_Config) -> + logger:remove_handler(h1), + logger:set_logger_config(level,info), + ok. + +macros(_Config) -> + ok = logger:set_module_level(?MODULE,debug), + ok = logger:add_handler(h1,?MODULE,#{level=>debug,filter_default=>log}), + test_macros(emergency), + ok. + +macros(cleanup,_Config) -> + logger:remove_handler(h1), + logger:reset_module_level(?MODULE), + ok. + +set_level(_Config) -> + ok = logger:add_handler(h1,?MODULE,#{level=>debug,filter_default=>log}), + logger:debug(?map_rep), + ok = check_no_log(), + logger:info(M1=?map_rep), + ok = check_logged(info,M1,#{}), + ok = logger:set_logger_config(level,debug), + logger:debug(M2=?map_rep), + ok = check_logged(debug,M2,#{}), + ok. + +set_level(cleanup,_Config) -> + logger:remove_handler(h1), + logger:set_logger_config(level,info), + ok. + +set_level_module(_Config) -> + ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log}), + {error,{invalid_level,bad}} = logger:set_module_level(?MODULE,bad), + {error,{not_a_module,{bad}}} = logger:set_module_level({bad},warning), + ok = logger:set_module_level(?MODULE,warning), + logger:info(?map_rep,?MY_LOC(0)), + ok = check_no_log(), + logger:warning(M1=?map_rep,?MY_LOC(0)), + ok = check_logged(warning,M1,?MY_LOC(1)), + ok = logger:set_module_level(?MODULE,info), + logger:info(M2=?map_rep,?MY_LOC(0)), + ok = check_logged(info,M2,?MY_LOC(1)), + + {error,{not_a_module,{bad}}} = logger:reset_module_level({bad}), + ok = logger:reset_module_level(?MODULE), + + ok. + +set_level_module(cleanup,_Config) -> + logger:remove_handler(h1), + logger:reset_module_level(?MODULE), + ok. + +cache_level_module(_Config) -> + ok = logger:reset_module_level(?MODULE), + [] = ets:lookup(logger,?MODULE), %dirty - add API in logger_config? + ?LOG_INFO(?map_rep), + %% Caching is done asynchronously, so wait a bit for the update + timer:sleep(100), + [_] = ets:lookup(logger,?MODULE), %dirty - add API in logger_config? + ok = logger:reset_module_level(?MODULE), + [] = ets:lookup(logger,?MODULE), %dirty - add API in logger_config? + ok. + +cache_level_module(cleanup,_Config) -> + logger:reset_module_level(?MODULE), + ok. + +format_report(_Config) -> + {"~ts",["string"]} = logger:format_report("string"), + {"~tp",[term]} = logger:format_report(term), + {"~tp",[[]]} = logger:format_report([]), + {" ~tp: ~tp",[key,value]} = logger:format_report([{key,value}]), + KeyVals = [{key1,value1},{key2,"value2"},{key3,[]}], + KeyValRes = + {" ~tp: ~tp\n ~tp: ~ts\n ~tp: ~tp", + [key1,value1,key2,"value2",key3,[]]} = + logger:format_report(KeyVals), + KeyValRes = logger:format_report(maps:from_list(KeyVals)), + KeyValRes = logger:format_otp_report(#{label=>{?MODULE,test},report=>KeyVals}), + {" ~tp: ~tp\n ~tp: ~tp", + [label,{?MODULE,test},report,KeyVals]} = + logger:format_report(#{label=>{?MODULE,test},report=>KeyVals}), + + {" ~tp: ~tp\n ~tp",[key1,value1,term]} = + logger:format_report([{key1,value1},term]), + + {" ~tp: ~tp\n ~tp",[key1,value1,[]]} = + logger:format_report([{key1,value1},[]]), + + {"~tp",[[]]} = logger:format_report([[],[],[]]), + + ok. + +filter_failed(_Config) -> + ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log}), + + %% Logger filters + {error,{invalid_filter,_}} = + logger:add_logger_filter(lf,{fun(_) -> ok end,args}), + ok = logger:add_logger_filter(lf,{fun(_,_) -> a=b end,args}), + {ok,#{filters:=[_]}} = logger:get_logger_config(), + ok = logger:info(M1=?map_rep), + ok = check_logged(info,M1,#{}), + {error,{not_found,lf}} = logger:remove_logger_filter(lf), + + ok = logger:add_logger_filter(lf,{fun(_,_) -> faulty_return end,args}), + {ok,#{filters:=[_]}} = logger:get_logger_config(), + ok = logger:info(M2=?map_rep), + ok = check_logged(info,M2,#{}), + {error,{not_found,lf}} = logger:remove_logger_filter(lf), + + %% Handler filters + {error,{not_found,h0}} = + logger:add_handler_filter(h0,hf,{fun(_,_) -> ignore end,args}), + {error,{not_found,h0}} = logger:remove_handler_filter(h0,hf), + {error,{invalid_filter,_}} = + logger:add_handler_filter(h1,hf,{fun(_) -> ok end,args}), + ok = logger:add_handler_filter(h1,hf,{fun(_,_) -> a=b end,args}), + {ok,{?MODULE,#{filters:=[_]}}} = logger:get_handler_config(h1), + ok = logger:info(M3=?map_rep), + ok = check_logged(info,M3,#{}), + {error,{not_found,hf}} = logger:remove_handler_filter(h1,hf), + + ok = logger:add_handler_filter(h1,hf,{fun(_,_) -> faulty_return end,args}), + {ok,{?MODULE,#{filters:=[_]}}} = logger:get_handler_config(h1), + ok = logger:info(M4=?map_rep), + ok = check_logged(info,M4,#{}), + {error,{not_found,hf}} = logger:remove_handler_filter(h1,hf), + + ok. + +filter_failed(cleanup,_Config) -> + logger:remove_handler(h1), + ok. + +handler_failed(_Config) -> + {error,{invalid_id,1}} = logger:add_handler(1,?MODULE,#{}), + {error,{invalid_module,"nomodule"}} = logger:add_handler(h1,"nomodule",#{}), + {error,{invalid_handler_config,bad}} = logger:add_handler(h1,?MODULE,bad), + {error,{invalid_filters,false}} = + logger:add_handler(h1,?MODULE,#{filters=>false}), + {error,{invalid_filter_default,true}} = + 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}), + logger:info(?map_rep), + check_no_log(), + #{logger:=#{handlers:=Ids1}, + handlers:=H1} = logger:i(), + false = lists:member(h1,Ids1), + false = lists:keymember(h1,1,H1), + {error,{not_found,h1}} = logger:remove_handler(h1), + + ok = logger:add_handler(h2,?MODULE,#{filter_default=>log,crash=>true}), + {error,{already_exist,h2}} = logger:add_handler(h2,othermodule,#{}), + + logger:info(?map_rep), + check_no_log(), + #{logger:=#{handlers:=Ids2}, + handlers:=H2} = logger:i(), + false = lists:member(h2,Ids2), + false = lists:keymember(h2,1,H2), + {error,{not_found,h2}} = logger:remove_handler(h2), + + ok. + +handler_failed(cleanup,_Config) -> + logger:remove_handler(h1), + logger:remove_handler(h2), + ok. + +config_sanity_check(_Config) -> + %% Logger config + {error,{invalid_filter_default,bad}} = + logger:set_logger_config(filter_default,bad), + {error,{invalid_level,bad}} = logger:set_logger_config(level,bad), + {error,{invalid_handlers,bad}} = logger:set_logger_config(handlers,bad), + {error,{invalid_id,{bad,bad}}} = + logger:set_logger_config(handlers,[{bad,bad}]), + {error,{invalid_id,"bad"}} = logger:set_logger_config(handlers,["bad"]), + {error,{invalid_filters,bad}} = logger:set_logger_config(filters,bad), + {error,{invalid_filter,bad}} = logger:set_logger_config(filters,[bad]), + {error,{invalid_filter,{_,_}}} = + logger:set_logger_config(filters,[{id,bad}]), + {error,{invalid_filter,{_,{_,_}}}} = + logger:set_logger_config(filters,[{id,{bad,args}}]), + {error,{invalid_filter,{_,{_,_}}}} = + logger:set_logger_config(filters,[{id,{fun() -> ok end,args}}]), + {error,{invalid_logger_config,{bad,bad}}} = + logger:set_logger_config(bad,bad), + + %% Handler config + {error,{not_found,h1}} = logger:set_handler_config(h1,a,b), + ok = logger:add_handler(h1,?MODULE,#{}), + {error,{invalid_filter_default,bad}} = + logger:set_handler_config(h1,filter_default,bad), + {error,{invalid_level,bad}} = logger:set_handler_config(h1,level,bad), + {error,{invalid_filters,bad}} = logger:set_handler_config(h1,filters,bad), + {error,{invalid_filter,bad}} = logger:set_handler_config(h1,filters,[bad]), + {error,{invalid_filter,{_,_}}} = + logger:set_handler_config(h1,filters,[{id,bad}]), + {error,{invalid_filter,{_,{_,_}}}} = + logger:set_handler_config(h1,filters,[{id,{bad,args}}]), + {error,{invalid_filter,{_,{_,_}}}} = + logger:set_handler_config(h1,filters,[{id,{fun() -> ok end,args}}]), + {error,{invalid_formatter,bad}} = + logger:set_handler_config(h1,formatter,bad), + {error,{invalid_module,{bad}}} = + logger:set_handler_config(h1,formatter,{{bad},cfg}), + {error,{invalid_formatter_config,bad}} = + logger:set_handler_config(h1,formatter,{logger_formatter,bad}), + {error,{invalid_formatter_config,{bad,bad}}} = + logger:set_handler_config(h1,formatter,{logger_formatter,#{bad=>bad}}), + {error,{invalid_formatter_config,{template,bad}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{template=>bad}}), + {error,{invalid_formatter_template,[1]}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{template=>[1]}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{template=>[]}}), + {error,{invalid_formatter_config,{single_line,bad}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{single_line=>bad}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{single_line=>true}}), + {error,{invalid_formatter_config,{legacy_header,bad}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{legacy_header=>bad}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{legacy_header=>true}}), + ok = logger:set_handler_config(h1,custom,custom), + ok. + +config_sanity_check(cleanup,_Config) -> + logger:remove_handler(h1), + ok. + +log_failed(_Config) -> + ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log}), + {error,function_clause} = ?TRY(logger:log(bad,?map_rep)), + {error,function_clause} = ?TRY(logger:log(info,?map_rep,bad)), + {error,function_clause} = ?TRY(logger:log(info,fun() -> ?map_rep end,bad)), + {error,function_clause} = ?TRY(logger:log(info,fun() -> ?map_rep end,bad,#{})), + {error,function_clause} = ?TRY(logger:log(info,bad,bad,bad)), + {error,function_clause} = ?TRY(logger:log(info,bad,bad,#{})), + check_no_log(), + ok = logger:log(info,M1=?str,#{}), + check_logged(info,M1,#{}), + ok = logger:log(info,M2=?map_rep,#{}), + check_logged(info,M2,#{}), + ok = logger:log(info,M3=?keyval_rep,#{}), + check_logged(info,M3,#{}), + + %% Should we check report input more thoroughly? + ok = logger:log(info,M4=?keyval_rep++[other,stuff,in,list],#{}), + check_logged(info,M4,#{}), + + %% This might break a handler since it is assumed to be a format + %% string and args, so it depends how the handler protects itself + %% against something like io_lib:format("ok","ok") + ok = logger:log(info,"ok","ok",#{}), + check_logged(info,"ok","ok",#{}), + + ok. + +log_failed(cleanup,_Config) -> + logger:remove_handler(h1), + ok. + +emulator(_Config) -> + ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log, + tc_proc=>self()}), + Msg = "Error in process ~p on node ~p with exit value:~n~p~n", + Error = {badmatch,4}, + Stack = [{module, function, 2, []}], + Pid = spawn(?MODULE, generate_error, [Error, Stack]), + check_logged(error, Msg, [Pid, node(), {Error, Stack}], + #{gl=>group_leader(), + error_logger=>#{tag=>error,emulator=>true}}), + ok. + +emulator(cleanup, _Config) -> + logger:remove_handler(h1), + ok. + +generate_error(Error, Stack) -> + erlang:raise(error, Error, Stack). + +via_logger_process(Config) -> + ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log, + tc_proc=>self()}), + + %% Explicitly send a message to the logger process + %% This is used by code_server, erl_prim_loader, init, prim_file, ... + Msg = ?str, + logger ! {log,error,Msg,[],#{}}, + check_logged(error, Msg, [], #{}), + + case os:type() of + {win32,_} -> + %% Skip this part on windows - cant change file mode" + ok; + _ -> + %% This should trigger the same thing from erl_prim_loader + Dir = filename:join(?config(priv_dir,Config),"dummydir"), + ok = file:make_dir(Dir), + ok = file:change_mode(Dir,8#0222), + error = erl_prim_loader:list_dir(Dir), + check_logged(error, + #{report=>"File operation error: eacces. Target: " ++ + Dir ++". Function: list_dir. "}, + #{pid=>self(), + gl=>group_leader(), + error_logger=>#{tag=>error_report, + type=>std_error}}), + ok + end. + +via_logger_process(cleanup, Config) -> + Dir = filename:join(?config(priv_dir,Config),"dummydir"), + _ = file:change_mode(Dir,8#0664), + _ = file:del_dir(Dir), + logger:remove_handler(h1), + ok. + +other_node(_Config) -> + ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log, + tc_proc=>self()}), + {ok,Node} = test_server:start_node(?FUNCTION_NAME,slave,[]), + rpc:call(Node,logger,error,[Msg=?str,#{}]), + check_logged(error,Msg,#{}), + ok. + +other_node(cleanup,_Config) -> + Nodes = nodes(), + [test_server:stop_node(Node) || Node <- Nodes], + logger:remove_handler(h1), + ok. + +compare_levels(_Config) -> + Levels = [emergency,alert,critical,error,warning,notice,info,debug], + ok = compare(Levels), + {error,badarg} = ?TRY(logger:compare_levels(bad,bad)), + {error,badarg} = ?TRY(logger:compare_levels({bad},info)), + {error,badarg} = ?TRY(logger:compare_levels(info,"bad")), + ok. + +compare([L|Rest]) -> + eq = logger:compare_levels(L,L), + [gt = logger:compare_levels(L,L1) || L1 <- Rest], + [lt = logger:compare_levels(L1,L) || L1 <- Rest], + compare(Rest); +compare([]) -> + ok. + +process_metadata(_Config) -> + undefined = logger:get_process_metadata(), + {error,badarg} = ?TRY(logger:set_process_metadata(bad)), + ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log}), + Time = erlang:monotonic_time(microsecond), + ProcMeta = #{time=>Time,line=>0,custom=>proc}, + ok = logger:set_process_metadata(ProcMeta), + S1 = ?str, + ?LOG_INFO(S1,#{custom=>macro}), + check_logged(info,S1,#{time=>Time,line=>0,custom=>macro}), + + Time2 = erlang:monotonic_time(microsecond), + S2 = ?str, + ?LOG_INFO(S2,#{time=>Time2,line=>1,custom=>macro}), + check_logged(info,S2,#{time=>Time2,line=>1,custom=>macro}), + + logger:info(S3=?str,#{custom=>func}), + check_logged(info,S3,#{time=>Time,line=>0,custom=>func}), + + ProcMeta = logger:get_process_metadata(), + ok = logger:unset_process_metadata(), + undefined = logger:get_process_metadata(), + + ok. + +process_metadata(cleanup,_Config) -> + logger:remove_handler(h1), + ok. + +%%%----------------------------------------------------------------- +%%% Internal +check_logged(Level,Format,Args,Meta) -> + do_check_logged(Level,{Format,Args},Meta). + +check_logged(Level,Msg,Meta) when ?IS_REPORT(Msg) -> + do_check_logged(Level,{report,Msg},Meta); +check_logged(Level,Msg,Meta) when ?IS_STRING(Msg) -> + do_check_logged(Level,{string,Msg},Meta). + +do_check_logged(Level,Msg0,Meta0) -> + receive + {#{level:=Level,msg:=Msg,meta:=Meta},_} -> + check_msg(Msg0,Msg), + check_maps(Meta0,Meta,meta) + after 500 -> + ct:fail({timeout,no_log,process_info(self(),messages)}) + end. + +check_no_log() -> + receive + X -> ct:fail({got_unexpected_log,X}) + after 500 -> + ok + end. + +check_msg(Msg,Msg) -> + ok; +check_msg({report,Expected},{report,Got}) when is_map(Expected), is_map(Got) -> + check_maps(Expected,Got,msg); +check_msg(Expected,Got) -> + ct:fail({unexpected,msg,Expected,Got}). + +check_maps(Expected,Got,What) -> + case maps:merge(Got,Expected) of + Got -> + ok; + _ -> + ct:fail({unexpected,What,Expected,Got}) + end. + +%% Handler +adding_handler(_Id,Config) -> + maybe_send(add), + {ok,Config}. +removing_handler(_Id) -> + maybe_send(remove), + ok. +changing_config(_Id,_Old,#{call:=Fun}) -> + Fun(); +changing_config(_Id,_Old,#{fail:=true}) -> + {error,fail}; +changing_config(_Id,_Old,Config) -> + maybe_send(changing_config), + {ok,Config}. + +maybe_send(Msg) -> + case whereis(callback_receiver) of + undefined -> ok; + Pid -> Pid ! Msg + end. + +log(_Log,#{crash:=true}) -> + a=b; +log(Log,Config) -> + TcProc = maps:get(tc_proc,Config,self()), + TcProc ! {Log,Config}, + ok. + +test_api(Level) -> + logger:Level(#{Level=>rep}), + ok = check_logged(Level,#{Level=>rep},#{}), + logger:Level(#{Level=>rep},#{my=>meta}), + ok = check_logged(Level,#{Level=>rep},#{my=>meta}), + logger:Level("~w: ~w",[Level,fa]), + ok = check_logged(Level,"~w: ~w",[Level,fa],#{}), + logger:Level("~w: ~w ~w",[Level,fa,meta],#{my=>meta}), + ok = check_logged(Level,"~w: ~w ~w",[Level,fa,meta],#{my=>meta}), + logger:Level(fun(x) -> {"~w: ~w ~w",[Level,fun_to_fa,meta]} end,x, + #{my=>meta}), + ok = check_logged(Level,"~w: ~w ~w",[Level,fun_to_fa,meta],#{my=>meta}), + logger:Level(fun(x) -> #{Level=>fun_to_r,meta=>true} end,x, + #{my=>meta}), + ok = check_logged(Level,#{Level=>fun_to_r,meta=>true},#{my=>meta}), + logger:Level(fun(x) -> <<"fun_to_s">> end,x,#{}), + ok = check_logged(Level,<<"fun_to_s">>,#{}), + logger:Level(F1=fun(x) -> {fun_to_bad} end,x,#{}), + ok = check_logged(Level,"LAZY_FUN ERROR: ~tp; Returned: ~tp", + [{F1,x},{fun_to_bad}],#{}), + logger:Level(F2=fun(x) -> erlang:error(fun_that_crashes) end,x,#{}), + ok = check_logged(Level,"LAZY_FUN CRASH: ~tp; Reason: ~tp", + [{F2,x},{error,fun_that_crashes}],#{}), + ok. + +test_log_function(Level) -> + logger:log(Level,#{Level=>rep}), + ok = check_logged(Level,#{Level=>rep},#{}), + logger:log(Level,#{Level=>rep},#{my=>meta}), + ok = check_logged(Level,#{Level=>rep},#{my=>meta}), + logger:log(Level,"~w: ~w",[Level,fa]), + ok = check_logged(Level,"~w: ~w",[Level,fa],#{}), + logger:log(Level,"~w: ~w ~w",[Level,fa,meta],#{my=>meta}), + ok = check_logged(Level,"~w: ~w ~w",[Level,fa,meta],#{my=>meta}), + logger:log(Level,fun(x) -> {"~w: ~w ~w",[Level,fun_to_fa,meta]} end, + x, #{my=>meta}), + ok = check_logged(Level,"~w: ~w ~w",[Level,fun_to_fa,meta],#{my=>meta}), + logger:log(Level,fun(x) -> #{Level=>fun_to_r,meta=>true} end, + x, #{my=>meta}), + ok = check_logged(Level,#{Level=>fun_to_r,meta=>true},#{my=>meta}), + logger:log(Level,fun(x) -> <<"fun_to_s">> end,x,#{}), + ok = check_logged(Level,<<"fun_to_s">>,#{}), + logger:log(Level,F1=fun(x) -> {fun_to_bad} end,x,#{}), + ok = check_logged(Level,"LAZY_FUN ERROR: ~tp; Returned: ~tp", + [{F1,x},{fun_to_bad}],#{}), + logger:log(Level,F2=fun(x) -> erlang:error(fun_that_crashes) end,x,#{}), + ok = check_logged(Level,"LAZY_FUN CRASH: ~tp; Reason: ~tp", + [{F2,x},{error,fun_that_crashes}],#{}), + ok. + +test_macros(emergency=Level) -> + ?LOG_EMERGENCY(#{Level=>rep}), + ok = check_logged(Level,#{Level=>rep},?MY_LOC(1)), + ?LOG_EMERGENCY(#{Level=>rep},#{my=>meta}), + ok = check_logged(Level,#{Level=>rep},(?MY_LOC(1))#{my=>meta}), + ?LOG_EMERGENCY("~w: ~w",[Level,fa]), + ok = check_logged(Level,"~w: ~w",[Level,fa],?MY_LOC(1)), + ?LOG_EMERGENCY("~w: ~w ~w",[Level,fa,meta],#{my=>meta}), + ok = check_logged(Level,"~w: ~w ~w",[Level,fa,meta],(?MY_LOC(1))#{my=>meta}), + ?LOG_EMERGENCY(fun(x) -> {"~w: ~w ~w",[Level,fun_to_fa,meta]} end, + x, #{my=>meta}), + ok = check_logged(Level,"~w: ~w ~w",[Level,fun_to_fa,meta], + (?MY_LOC(3))#{my=>meta}), + ?LOG_EMERGENCY(fun(x) -> #{Level=>fun_to_r,meta=>true} end, x, #{my=>meta}), + ok = check_logged(Level,#{Level=>fun_to_r,meta=>true}, + (?MY_LOC(2))#{my=>meta}), + ?LOG_EMERGENCY(fun(x) -> <<"fun_to_s">> end,x,#{}), + ok = check_logged(Level,<<"fun_to_s">>,?MY_LOC(1)), + F1=fun(x) -> {fun_to_bad} end, + ?LOG_EMERGENCY(F1,x,#{}), + ok = check_logged(Level,"LAZY_FUN ERROR: ~tp; Returned: ~tp", + [{F1,x},{fun_to_bad}],#{}), + F2=fun(x) -> erlang:error(fun_that_crashes) end, + ?LOG_EMERGENCY(F2,x,#{}), + ok = check_logged(Level,"LAZY_FUN CRASH: ~tp; Reason: ~tp", + [{F2,x},{error,fun_that_crashes}],#{}), + ok. + +%%%----------------------------------------------------------------- +%%% Called by macro ?TRY(X) +my_try(Fun) -> + try Fun() catch C:R -> {C,R} end. diff --git a/lib/kernel/test/logger_bench_SUITE.erl b/lib/kernel/test/logger_bench_SUITE.erl new file mode 100644 index 0000000000..d47122ea9d --- /dev/null +++ b/lib/kernel/test/logger_bench_SUITE.erl @@ -0,0 +1,500 @@ +%% +%% %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_bench_SUITE). + +-compile(export_all). + +%%%----------------------------------------------------------------- +%%% To include lager tests, add paths to lager and goldrush +%%% (goldrush is a dependency inside the lager repo) +%%% +%%% To print data to .csv files, add the following to a config file: +%%% {print_csv,[{console_handler,[{path,"/some/dir/"}]}]}. +%%% +%%%----------------------------------------------------------------- + +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). +-include_lib("kernel/include/logger.hrl"). +-include_lib("kernel/src/logger_internal.hrl"). + +-define(msg,lists:flatten(string:pad("Log from "++atom_to_list(?FUNCTION_NAME)++ + ":"++integer_to_list(?LINE), + 80,trailing,$*))). +-define(meta,#{mfa=>{?MODULE,?FUNCTION_NAME,?FUNCTION_ARITY}, + pid=>self()}). + +-define(NO_COMPARE,[profile]). + +-define(TIMES,100000). + +suite() -> + [{timetrap,{seconds,120}}]. + +init_per_suite(Config) -> + DataDir = ?config(data_dir,Config), + have_lager() andalso make(DataDir), + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(Group, Config) -> + H = remove_all_handlers(), + do_init_per_group(Group), + [{handlers,H}|Config]. + +do_init_per_group(minimal_handler) -> + ok = logger:add_handler(?MODULE,?MODULE,#{level=>error,filter_default=>log}); +do_init_per_group(console_handler) -> + ok = logger:add_handler(?MODULE,logger_std_h, + #{filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS, + logger_std_h=>#{type=>standard_io, + toggle_sync_qlen => ?TIMES+1, + drop_new_reqs_qlen => ?TIMES+2, + flush_reqs_qlen => ?TIMES+3, + enable_burst_limit => false}}), + have_lager() andalso lager_helper:start(), + ok. + +end_per_group(Group, Config) -> + case ?config(saved_config,Config) of + {_,[{bench,Bench}]} -> + print_compare_chart(Group,Bench); + _ -> + ok + end, + add_all_handlers(?config(handlers,Config)), + do_end_per_group(Group). + +do_end_per_group(minimal_handler) -> + ok = logger:remove_handler(?MODULE); +do_end_per_group(console_handler) -> + ok = logger:remove_handler(?MODULE), + have_lager() andalso lager_helper:stop(), + ok. + +init_per_testcase(_TestCase, Config) -> + Config. + +end_per_testcase(Case, Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + wait_for_handlers(), + ok. + +wait_for_handlers() -> + wait_for_handler(?MODULE), + wait_for_handler(lager_event). + +wait_for_handler(Handler) -> + case whereis(Handler) of + undefined -> + io:format("~p: noproc1",[Handler]), + ok; + Pid -> + case process_info(Pid,message_queue_len) of + {_,0} -> + io:format("~p: queue=~p",[Handler,0]), + ok; + {_,Q} -> + io:format("~p: queue=~p",[Handler,Q]), + timer:sleep(2000), + wait_for_handler(Handler); + undefined -> + io:format("~p: noproc2",[Handler]), + ok + end + end. + +groups() -> + [{minimal_handler,[],[log, + log_drop, + log_drop_by_handler, + macro, + macro_drop, + macro_drop_by_handler, + error_logger, + error_logger_drop, + error_logger_drop_by_handler + ]}, + {console_handler,[],[%profile, + log, + log_drop, + log_drop_by_handler, + %% log_handler_complete, + macro, + macro_drop, + macro_drop_by_handler, + %% macro_handler_complete, + error_logger, + error_logger_drop, + error_logger_drop_by_handler%% , + %% error_logger_handler_complete + ] ++ lager_cases()} + ]. + +lager_cases() -> + case have_lager() of + true -> + [lager_log, + lager_log_drop, + lager_log_drop_by_handler, + %% lager_log_handler_complete, + lager_parsetrans, + lager_parsetrans_drop, + lager_parsetrans_drop_by_handler%% , + %% lager_parsetrans_handler_complete + ]; + false -> + [] + end. + + +all() -> + [{group,minimal_handler}, + {group,console_handler} + ]. + +log(Config) -> + Times = ?TIMES, + run_benchmark(Config,?FUNCTION_NAME,fun do_log_func/2, [error,?msg], Times). + +log_drop(Config) -> + Times = ?TIMES*100, + ok = logger:set_logger_config(level,error), + run_benchmark(Config,?FUNCTION_NAME,fun do_log_func/2, [info,?msg], Times). + +log_drop(cleanup,_Config) -> + ok = logger:set_logger_config(level,info). + +log_drop_by_handler(Config) -> + Times = ?TIMES, + %% just ensure correct levels + ok = logger:set_logger_config(level,info), + ok = logger:set_handler_config(?MODULE,level,error), + run_benchmark(Config,?FUNCTION_NAME,fun do_log_func/2, [info,?msg], Times). + +log_handler_complete(Config) -> + ok = logger:set_handler_config(?MODULE,formatter, + {?MODULE,?DEFAULT_FORMAT_CONFIG}), + handler_complete(Config, ?FUNCTION_NAME, fun do_log_func/2, [error,?msg]). + +log_handler_complete(cleanup,_Config) -> + ok=logger:set_handler_config(?MODULE,formatter, + {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}). + +macro(Config) -> + Times = ?TIMES, + run_benchmark(Config,?FUNCTION_NAME,fun do_log_macro/2,[error,?msg], Times). + +macro_drop(Config) -> + Times = ?TIMES*100, + ok = logger:set_logger_config(level,error), + run_benchmark(Config,?FUNCTION_NAME,fun do_log_macro/2,[info,?msg], Times). + +macro_drop(cleanup,_Config) -> + ok = logger:set_logger_config(level,info). + +macro_drop_by_handler(Config) -> + Times = ?TIMES, + %% just ensure correct levels + ok = logger:set_logger_config(level,info), + ok = logger:set_handler_config(?MODULE,level,error), + run_benchmark(Config,?FUNCTION_NAME,fun do_log_macro/2, [info,?msg], Times). + +macro_handler_complete(Config) -> + ok = logger:set_handler_config(?MODULE,formatter, + {?MODULE,?DEFAULT_FORMAT_CONFIG}), + handler_complete(Config, ?FUNCTION_NAME, fun do_log_macro/2, [error,?msg]). + +macro_handler_complete(cleanup,_Config) -> + ok=logger:set_handler_config(?MODULE,formatter, + {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}). + +error_logger(Config) -> + Times = ?TIMES, + run_benchmark(Config,?FUNCTION_NAME,fun do_error_logger/2, [error,?msg], Times). + +error_logger_drop(Config) -> + Times = ?TIMES*100, + ok = logger:set_logger_config(level,error), + run_benchmark(Config,?FUNCTION_NAME,fun do_error_logger/2, [info,?msg], Times). + +error_logger_drop(cleanup,_Config) -> + ok = logger:set_logger_config(level,info). + +error_logger_drop_by_handler(Config) -> + Times = ?TIMES, + %% just ensure correct levels + ok = logger:set_logger_config(level,info), + ok = logger:set_handler_config(?MODULE,level,error), + run_benchmark(Config,?FUNCTION_NAME,fun do_log_func/2, [info,?msg], Times). + +error_logger_handler_complete(Config) -> + ok = logger:set_handler_config(?MODULE,formatter, + {?MODULE,?DEFAULT_FORMAT_CONFIG}), + handler_complete(Config, ?FUNCTION_NAME, fun do_error_logger/2, [error,?msg]). + +error_logger_handler_complete(cleanup,_Config) -> + ok=logger:set_handler_config(?MODULE,formatter, + {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}). + +lager_log(Config) -> + Times = ?TIMES, + run_benchmark(Config,?FUNCTION_NAME,fun lager_helper:do_func/2, [error,?msg], Times). + +lager_log_drop(Config) -> + Times = ?TIMES*100, + run_benchmark(Config,?FUNCTION_NAME,fun lager_helper:do_func/2, [info,?msg], Times). + +lager_log_drop_by_handler(Config) -> + %% This concept does not exist, so doing same as lager_log_drop/1 + Times = ?TIMES, + run_benchmark(Config,?FUNCTION_NAME,fun lager_helper:do_func/2, [info,?msg], Times). + +lager_log_handler_complete(Config) -> + handler_complete(Config, ?FUNCTION_NAME, fun lager_helper:do_func/2, [error,?msg]). + +lager_parsetrans(Config) -> + Times = ?TIMES, + run_benchmark(Config,?FUNCTION_NAME,fun lager_helper:do_parsetrans/2, [error,?msg], Times). + +lager_parsetrans_drop(Config) -> + Times = ?TIMES*100, + run_benchmark(Config,?FUNCTION_NAME,fun lager_helper:do_parsetrans/2, [info,?msg], Times). + +lager_parsetrans_drop_by_handler(Config) -> + %% This concept does not exist, so doing same as lager_parsetrans_drop/1 + Times = ?TIMES, + run_benchmark(Config,?FUNCTION_NAME,fun lager_helper:do_parsetrans/2, [info,?msg], Times). + +lager_parsetrans_handler_complete(Config) -> + handler_complete(Config, ?FUNCTION_NAME, fun lager_helper:do_parsetrans/2, [error,?msg]). + + +profile(Config) -> + Times = ?TIMES, + %% fprof:apply(fun repeated_apply/3,[fun lager_helper:do_func/2,[error,?msg],Times]), + fprof:apply(fun repeated_apply/3,[fun do_log_func/2,[error,?msg],Times]), + ok = fprof:profile(), + ok = fprof:analyse(dest,"../fprof.analyse"), + ok. + +%%%----------------------------------------------------------------- +%%% Internal +%% Handler +log(_Log,_Config) -> + ok. + +format(Log=#{meta:=#{pid:=Pid}},Config) when is_pid(Pid) -> + String = ?DEFAULT_FORMATTER:format(Log,Config), + Pid ! done, + String; +format(Log=#{meta:=#{pid:=PidStr}},Config) when is_list(PidStr) -> + String = ?DEFAULT_FORMATTER:format(Log,Config), + list_to_pid(PidStr) ! done, + String. + +handler_complete(Config, TC, Fun, Args) -> + Times = ?TIMES, + Start = os:perf_counter(microsecond), + repeated_apply(Fun, Args, Times), + MSecs = wait_for_done(Start,Times), + calc_and_report(Config,TC,MSecs,Times). + +wait_for_done(Start,0) -> + os:perf_counter(microsecond) - Start; +wait_for_done(Start,N) -> + receive + done -> + wait_for_done(Start,N-1) + after 20000 -> + ct:fail("missing " ++ integer_to_list(N) ++ " replys") + end. + +%%%----------------------------------------------------------------- +%%% Benchmark stuff +run_benchmark(Config,Tag,Fun,Args,Times) -> + _ = erlang:apply(Fun, Args), % apply once to ensure level is cached + MSecs = measure_repeated_op(Fun, Args, Times), + %% fprof:profile(), + %% fprof:analyse(dest,"../"++atom_to_list(Tag)++".prof"), + calc_and_report(Config,Tag,MSecs,Times). + +measure_repeated_op(Fun, Args, Times) -> + Start = os:perf_counter(microsecond), + %% fprof:apply(fun repeated_apply/3, [Fun, Args, Times]), + repeated_apply(Fun, Args, Times), + os:perf_counter(microsecond) - Start. + +repeated_apply(_F, _Args, Times) when Times =< 0 -> + ok; +repeated_apply(F, Args, Times) -> + erlang:apply(F, Args), + repeated_apply(F, Args, Times - 1). + +calc_and_report(Config,Tag,MSecs,Times) -> + IOPS = trunc(Times * (1000000 / MSecs)), + ct_event:notify(#event{ name = benchmark_data, data = [{value,IOPS}] }), + ct:print("~p:~n~p IOPS, ~p us", [Tag, IOPS, MSecs]), + ct:comment("~p IOPS, ~p us", [IOPS, MSecs]), + Bench = case ?config(saved_config,Config) of + {_,[{bench,B}]} -> B; + undefined -> [] + end, + {save_config,[{bench,[{Tag,IOPS,MSecs}|Bench]}]}. + +remove_all_handlers() -> + #{handlers:=Hs} = logger:i(), + [logger:remove_handler(Id) || {Id,_,_} <- Hs], + Hs. + +add_all_handlers(Hs) -> + [logger:add_handler(Id,Mod,Config) || {Id,Mod,Config} <- Hs], + ok. + +%%%----------------------------------------------------------------- +%%% Call logger in different ways +do_log_func(Level,Msg) -> + logger:Level(Msg,[],?meta). + +do_log_macro(error,Msg) -> + ?LOG_ERROR(Msg,[]); +do_log_macro(info,Msg) -> + ?LOG_INFO(Msg,[]); +do_log_macro(debug,Msg) -> + ?LOG_DEBUG(Msg,[]). + +do_error_logger(error,Msg) -> + error_logger:error_msg(Msg,[]); +do_error_logger(info,Msg) -> + error_logger:info_msg(Msg,[]). + +%%%----------------------------------------------------------------- +%%% +print_compare_chart(Group,Bench) -> + io:format("~-20s~12s~12s~12s~12s", + ["Microseconds:","Log","Drop","HDrop","Complete"]), + io:format(user,"~-20s~12s~12s~12s~12s~n", + ["Microseconds:","Log","Drop","HDrop","Complete"]), + {Log,Drop,HDrop,Comp} = sort_bench(Bench,[],[],[],[]), + print_compare_chart(Log,Drop,HDrop,Comp), + io:format(user,"~n",[]), + maybe_print_csv_files(Group, + [{log,Log},{drop,Drop},{hdrop,HDrop},{comp,Comp}]). + +print_compare_chart([{What,LIOPS,LMSecs}|Log], + [{What,DIOPS,DMSecs}|Drop], + [{What,HIOPS,HMSecs}|HDrop], + [{What,CIOPS,CMSecs}|Comp]) -> + io:format("~-20w~12w~12w~12w~12w",[What,LMSecs,DMSecs,HMSecs,CMSecs]), + io:format(user,"~-20w~12w~12w~12w~12w~n",[What,LMSecs,DMSecs,HMSecs,CMSecs]), + print_compare_chart(Log,Drop,HDrop,Comp); +print_compare_chart([{What,LIOPS,LMSecs}|Log], + [{What,DIOPS,DMSecs}|Drop], + [{What,HIOPS,HMSecs}|HDrop], + []=Comp) -> + io:format("~-20w~12w~12w~12w",[What,LMSecs,DMSecs,HMSecs]), + io:format(user,"~-20w~12w~12w~12w~n",[What,LMSecs,DMSecs,HMSecs]), + print_compare_chart(Log,Drop,HDrop,Comp); +print_compare_chart([],[],[],[]) -> + ok; +print_compare_chart(Log,Drop,HDrop,Comp) -> + ct:fail({Log,Drop,HDrop,Comp}). + +sort_bench([{TC,IOPS,MSecs}|Bench],Log,Drop,HDrop,Comp) -> + case lists:member(TC,?NO_COMPARE) of + true -> + sort_bench(Bench,Log,Drop,HDrop,Comp); + false -> + TCStr = atom_to_list(TC), + {What,Type} = + case re:run(TCStr,"(.*)_(drop.*)", + [{capture,all_but_first,list}]) of + {match,[WhatStr,TypeStr]} -> + {list_to_atom(WhatStr),list_to_atom(TypeStr)}; + nomatch -> + case re:run(TCStr,"(.*)_(handler_complete.*)", + [{capture,all_but_first,list}]) of + {match,[WhatStr,TypeStr]} -> + {list_to_atom(WhatStr),list_to_atom(TypeStr)}; + nomatch -> + {TC,log} + end + end, + case Type of + log -> + sort_bench(Bench,[{What,IOPS,MSecs}|Log],Drop,HDrop,Comp); + drop -> + sort_bench(Bench,Log,[{What,IOPS,MSecs}|Drop],HDrop,Comp); + drop_by_handler -> + sort_bench(Bench,Log,Drop,[{What,IOPS,MSecs}|HDrop],Comp); + handler_complete -> + sort_bench(Bench,Log,Drop,HDrop,[{What,IOPS,MSecs}|Comp]) + end + end; +sort_bench([],Log,Drop,HDrop,Comp) -> + {lists:keysort(1,Log), + lists:keysort(1,Drop), + lists:keysort(1,HDrop), + lists:keysort(1,Comp)}. + +maybe_print_csv_files(Group,Data) -> + case ct:get_config({print_csv,Group}) of + undefined -> + ok; + Cfg -> + Path = proplists:get_value(path,Cfg,".."), + Files = [begin + File = filename:join(Path,F)++".csv", + case filelib:is_regular(File) of + true -> + {ok,Fd} = file:open(File,[append]), + Fd; + false -> + {ok,Fd} = file:open(File,[write]), + ok = file:write(Fd, + "error_logger,lager_log," + "lager_parsetrans,logger_log," + "logger_macro\n"), + Fd + end + end || {F,_} <- Data], + [print_csv_file(F,D) || {F,D} <- lists:zip(Files,Data)], + [file:close(Fd) || Fd <- Files], + ok + end. + +print_csv_file(Fd,{_,Data}) -> + AllIOPS = [integer_to_list(IOPS) || {_,IOPS,_} <- Data], + ok = file:write(Fd,lists:join(",",AllIOPS)++"\n"). + +have_lager() -> + code:ensure_loaded(lager) == {module,lager}. + +make(Dir) -> + {ok,Cwd} = file:get_cwd(), + ok = file:set_cwd(Dir), + up_to_date = make:all([load]), + ok = file:set_cwd(Cwd), + code:add_path(Dir). diff --git a/lib/kernel/test/logger_bench_SUITE_data/Emakefile b/lib/kernel/test/logger_bench_SUITE_data/Emakefile new file mode 100644 index 0000000000..85c82bdaab --- /dev/null +++ b/lib/kernel/test/logger_bench_SUITE_data/Emakefile @@ -0,0 +1 @@ +{['lager_helper'],[{outdir,"."},debug_info,{i,"/home/uabshan/Work/git/otp/lib/kernel/src"},{i,"/home/uabshan/Work/git/otp/lib/kernel/include"}]}. diff --git a/lib/kernel/test/logger_bench_SUITE_data/lager_helper.erl b/lib/kernel/test/logger_bench_SUITE_data/lager_helper.erl new file mode 100644 index 0000000000..296ced4276 --- /dev/null +++ b/lib/kernel/test/logger_bench_SUITE_data/lager_helper.erl @@ -0,0 +1,73 @@ +-module(lager_helper). + +-compile(export_all). +-compile({parse_transform,lager_transform}). + +-include_lib("kernel/src/logger_internal.hrl"). + +start() -> + application:load(lager), + application:set_env(lager, error_logger_redirect, false), + application:set_env(lager, async_threshold, 100010), + application:set_env(lager, async_threshold_window, 100), + application:set_env(lager,handlers,[{?MODULE,[{level,error}]}]), + lager:start(). + +stop() -> + application:stop(lager). + +do_func(Level,Msg) -> + lager:log(Level,[{pid,self()}],Msg,[]). + +do_parsetrans(error,Msg) -> + lager:error(Msg,[]); +do_parsetrans(info,Msg) -> + lager:info(Msg,[]). + +%%%----------------------------------------------------------------- +%%% Dummy handler for lager +-record(state, {level :: {'mask', integer()}, + formatter :: atom(), + format_config :: any()}). +init(Opts) -> + Level = proplists:get_value(level,Opts,info), + Formatter = proplists:get_value(formatter,Opts,logger_bench_SUITE), + FormatConfig = proplists:get_value(format_config,Opts,?DEFAULT_FORMAT_CONFIG), + {ok,#state{level=lager_util:config_to_mask(Level), + formatter=Formatter, + format_config=FormatConfig}}. + +handle_call(get_loglevel, #state{level=Level} = State) -> + {ok, Level, State}; +handle_call({set_loglevel, Level}, State) -> + try lager_util:config_to_mask(Level) of + Levels -> + {ok, ok, State#state{level=Levels}} + catch + _:_ -> + {ok, {error, bad_log_level}, State} + end; +handle_call(_Request, State) -> + {ok, ok, State}. + +handle_event({log, Message}, + #state{level=L,formatter=Formatter,format_config=FormatConfig} = State) -> + case lager_util:is_loggable(Message, L, ?MODULE) of + true -> + Metadata = + case maps:from_list(lager_msg:metadata(Message)) of + Meta = #{pid:=Pid} when is_pid(Pid) -> + Meta; + Meta = #{pid:=PidStr} when is_list(PidStr) -> + Meta + end, + Log = #{level=>lager_msg:severity(Message), + msg=>{report,lager_msg:message(Message)}, + meta=>Metadata}, + io:put_chars(user, Formatter:format(Log,FormatConfig)), + {ok, State}; + false -> + {ok, State} + end; +handle_event(_Event, State) -> + {ok, State}. diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl new file mode 100644 index 0000000000..c7c6137380 --- /dev/null +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -0,0 +1,1417 @@ +-module(logger_disk_log_h_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("kernel/include/logger.hrl"). +-include_lib("kernel/src/logger_internal.hrl"). +-include_lib("kernel/src/logger_h_common.hrl"). +-include_lib("stdlib/include/ms_transform.hrl"). +-include_lib("kernel/include/file.hrl"). + +-define(check_no_log, [] = test_server:messages_get()). + +-define(check(Expected), + receive {log,Expected} -> + [] = test_server:messages_get() + after 1000 -> + ct:fail({report_not_received, + {line,?LINE}, + {got,test_server:messages_get()}}) + end). + +-define(msg,"Log from "++atom_to_list(?FUNCTION_NAME)++ + ":"++integer_to_list(?LINE)). +-define(bin(Msg), list_to_binary(Msg++"\n")). +-define(log_no(File,N), lists:concat([File,".",N])). +-define(domain,#{domain=>[?MODULE]}). + +-define(SYNC_REP_INT, if is_atom(?FILESYNC_REPEAT_INTERVAL) -> 5500; + true -> ?FILESYNC_REPEAT_INTERVAL + 500 + end). + +suite() -> + [{timetrap,{seconds,30}}]. + +init_per_suite(Config) -> + timer:start(), % to avoid progress report + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_Group, Config) -> + Config. + +end_per_group(_Group, _Config) -> + ok. + +init_per_testcase(TestHooksCase, Config) when + TestHooksCase == write_failure; + TestHooksCase == sync_failure -> + if ?TEST_HOOKS_TAB == undefined -> + {skip,"Define the TEST_HOOKS macro to run this test"}; + true -> + ct:print("********** ~w **********", [TestHooksCase]), + Config + end; +init_per_testcase(TestCase, Config) -> + ct:print("********** ~w **********", [TestCase]), + Config. + +end_per_testcase(Case, Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +groups() -> + []. + +all() -> + [start_stop_handler, + create_log, + open_existing_log, + disk_log_opts, + default_formatter, + logging, + errors, + formatter_fail, + config_fail, + bad_input, + info_and_reset, + reconfig, + disk_log_sync, + disk_log_full, + disk_log_wrap, + disk_log_events, + write_failure, + sync_failure, + op_switch_to_sync, + op_switch_to_drop, + op_switch_to_flush, + limit_burst_disabled, + limit_burst_enabled_one, + limit_burst_enabled_period, + kill_disabled, + qlen_kill_new, + %% qlen_kill_std, + mem_kill_new, + %% mem_kill_std, + restart_after, + handler_requests_under_load + ]. + +start_stop_handler(_Config) -> + ok = logger:add_handler(?MODULE, logger_disk_log_h, #{}), + {error,{already_exist,?MODULE}} = + logger:add_handler(?MODULE, logger_disk_log_h, #{}), + true = is_pid(whereis(?MODULE)), + ok = logger:remove_handler(?MODULE), + timer:sleep(500), + undefined = whereis(?MODULE). +start_stop_handler(cleanup, _Config) -> + logger:remove_handler(?MODULE). + +create_log(Config) -> + PrivDir = ?config(priv_dir,Config), + %% test new handler + Name1 = list_to_atom(lists:concat([?FUNCTION_NAME,"_A"])), + LogFile1 = filename:join(PrivDir, Name1), + ok = start_and_add(Name1, #{filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}, + #{file=>LogFile1}), + logger:info("hello", ?domain), + logger_disk_log_h:disk_log_sync(Name1), + ct:pal("Checking contents of ~p", [?log_no(LogFile1,1)]), + try_read_file(?log_no(LogFile1,1), {ok,<<"hello\n">>}, 5000), + + %% test second handler + Name2 = list_to_atom(lists:concat([?FUNCTION_NAME,"_B"])), + DLName = lists:concat([?FUNCTION_NAME,"_B_log"]), + LogFile2 = filename:join(PrivDir, DLName), + ok = start_and_add(Name2, #{filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}, + #{file=>LogFile2}), + logger:info("dummy", ?domain), + logger_disk_log_h:disk_log_sync(Name2), + ct:pal("Checking contents of ~p", [?log_no(LogFile2,1)]), + try_read_file(?log_no(LogFile2,1), {ok,<<"dummy\n">>}, 5000), + + remove_and_stop(Name1), + remove_and_stop(Name2), + try_read_file(?log_no(LogFile1,1), {ok,<<"hello\ndummy\n">>}, 1), + try_read_file(?log_no(LogFile2,1), {ok,<<"dummy\n">>}, 5000), + ok. + +open_existing_log(Config) -> + PrivDir = ?config(priv_dir,Config), + %% test new handler + HName = ?FUNCTION_NAME, + DLName = lists:concat([?FUNCTION_NAME,"_log"]), + LogFile1 = filename:join(PrivDir, DLName), + ok = start_and_add(HName, #{filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}, + #{file=>LogFile1}), + logger:info("one", ?domain), + logger_disk_log_h:disk_log_sync(HName), + ct:pal("Checking contents of ~p", [?log_no(LogFile1,1)]), + try_read_file(?log_no(LogFile1,1), {ok,<<"one\n">>}, 5000), + logger:info("two", ?domain), + ok = remove_and_stop(HName), + try_read_file(?log_no(LogFile1,1), {ok,<<"one\ntwo\n">>}, 5000), + + logger:info("two and a half", ?domain), + + ok = start_and_add(HName, #{filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}, + #{file=>LogFile1}), + logger:info("three", ?domain), + logger_disk_log_h:disk_log_sync(HName), + try_read_file(?log_no(LogFile1,1), {ok,<<"one\ntwo\nthree\n">>}, 5000), + remove_and_stop(HName), + try_read_file(?log_no(LogFile1,1), {ok,<<"one\ntwo\nthree\n">>}, 5000). + +disk_log_opts(Config) -> + Get = fun(Key, PL) -> proplists:get_value(Key, PL) end, + PrivDir = ?config(priv_dir,Config), + WName = list_to_atom(lists:concat([?FUNCTION_NAME,"_W"])), + WFile = lists:concat([?FUNCTION_NAME,"_W_log"]), + Size = length("12345"), + ConfigW = #{filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter => {?MODULE,no_nl}}, + WFileFull = filename:join(PrivDir, WFile), + DLOptsW = #{file => WFileFull, + type => wrap, + max_no_bytes => Size, + max_no_files => 2}, + ok = start_and_add(WName, ConfigW, DLOptsW), + WInfo1 = disk_log:info(WName), + ct:log("Fullname = ~s", [WFileFull]), + {WFileFull,wrap,{Size,2},1} = {Get(file,WInfo1),Get(type,WInfo1), + Get(size,WInfo1),Get(current_file,WInfo1)}, + logger:info("123", ?domain), + logger_disk_log_h:disk_log_sync(WName), + timer:sleep(500), + 1 = Get(current_file, disk_log:info(WName)), + + logger:info("45", ?domain), + logger_disk_log_h:disk_log_sync(WName), + timer:sleep(500), + 1 = Get(current_file, disk_log:info(WName)), + + logger:info("6", ?domain), + logger_disk_log_h:disk_log_sync(WName), + timer:sleep(500), + 2 = Get(current_file, disk_log:info(WName)), + + logger:info("7890", ?domain), + logger_disk_log_h:disk_log_sync(WName), + timer:sleep(500), + 2 = Get(current_file, disk_log:info(WName)), + + HName1 = list_to_atom(lists:concat([?FUNCTION_NAME,"_H1"])), + HFile1 = lists:concat([?FUNCTION_NAME,"_H1_log"]), + ConfigH = #{filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter => {?MODULE,no_nl}}, + HFile1Full = filename:join(PrivDir, HFile1), + DLOptsH1 = #{file => HFile1Full, + type => halt}, + ok = start_and_add(HName1, ConfigH, DLOptsH1), + HInfo1 = disk_log:info(HName1), + ct:log("Fullname = ~s", [HFile1Full]), + {HFile1Full,halt,infinity} = {Get(file,HInfo1),Get(type,HInfo1), + Get(size,HInfo1)}, + logger:info("12345", ?domain), + logger_disk_log_h:disk_log_sync(HName1), + timer:sleep(500), + 1 = Get(no_written_items, disk_log:info(HName1)), + + HName2 = list_to_atom(lists:concat([?FUNCTION_NAME,"_H2"])), + HFile2 = lists:concat([?FUNCTION_NAME,"_H2_log"]), + HFile2Full = filename:join(PrivDir, HFile2), + DLOptsH2 = DLOptsH1#{file => HFile2Full, + max_no_bytes => 1000}, + ok = start_and_add(HName2, ConfigH, DLOptsH2), + HInfo3 = disk_log:info(HName2), + ct:log("Fullname = ~s", [HFile2Full]), + {HFile2Full,halt,1000} = {Get(file,HInfo3),Get(type,HInfo3), + Get(size,HInfo3)}, + + remove_and_stop(WName), + remove_and_stop(HName1), + remove_and_stop(HName2), + ok. + +default_formatter(Config) -> + PrivDir = ?config(priv_dir,Config), + LogFile = filename:join(PrivDir,atom_to_list(?FUNCTION_NAME)), + HConfig = #{disk_log_opts => #{file=>LogFile}, + filter_default=>log}, + ct:pal("Log: ~p", [LogFile]), + ok = logger:add_handler(?MODULE, logger_disk_log_h, HConfig), + ok = logger:set_handler_config(?MODULE,formatter, + {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}), + LogName = lists:concat([LogFile, ".1"]), + logger:info("dummy"), + wait_until_written(LogName), + {ok,Bin} = file:read_file(LogName), + match = re:run(Bin, "=INFO REPORT====.*\ndummy", [{capture,none}]), + ok. +default_formatter(cleanup, _Config) -> + logger:remove_handler(?MODULE). + +logging(Config) -> + PrivDir = ?config(priv_dir,Config), + %% test new handler + Name = list_to_atom(lists:concat([?FUNCTION_NAME,"_1"])), + LogFile = filename:join(PrivDir, Name), + ok = start_and_add(Name, #{filter_default=>log, + formatter=>{?MODULE,self()}}, + #{file => LogFile}), + MsgFormatter = fun(Term) -> {io_lib:format("Term:~p",[Term]),[]} end, + logger:info([{x,y}], #{report_cb => MsgFormatter}), + logger:info([{x,y}], #{}), + ct:pal("Checking contents of ~p", [?log_no(LogFile,1)]), + try_read_file(?log_no(LogFile,1), {ok,<<"Term:[{x,y}]\n x: y\n">>}, 5000). + +logging(cleanup, _Config) -> + Name = list_to_atom(lists:concat([?FUNCTION_NAME,"_1"])), + remove_and_stop(Name). + +errors(Config) -> + PrivDir = ?config(priv_dir,Config), + Name1 = list_to_atom(lists:concat([?FUNCTION_NAME,"_1"])), + LogFile1 = filename:join(PrivDir,Name1), + HConfig = #{disk_log_opts=>#{file=>LogFile1}, + filter_default=>log, + formatter=>{?MODULE,self()}}, + ok = logger:add_handler(Name1, logger_disk_log_h, HConfig), + {error,{already_exist,Name1}} = + logger:add_handler(Name1, logger_disk_log_h, #{}), + + %%! TODO: + %%! Check how bad log_opts are handled! + + {error,{illegal_config_change,_,_}} = + logger:set_handler_config(Name1, + disk_log_opts, + #{file=>LogFile1, + type=>halt}), + {error,{illegal_config_change,_,_}} = + logger:set_handler_config(Name1,id,new), + + ok = logger:remove_handler(Name1), + {error,{not_found,Name1}} = logger:remove_handler(Name1), + ok. + +errors(cleanup, _Config) -> + Name1 = list_to_atom(lists:concat([?FUNCTION_NAME,"_1"])), + _ = logger:remove_handler(Name1). + +formatter_fail(Config) -> + PrivDir = ?config(priv_dir,Config), + Name = ?FUNCTION_NAME, + LogFile = filename:join(PrivDir,Name), + ct:pal("Log = ~p", [LogFile]), + HConfig = #{disk_log_opts => #{file=>LogFile}, + filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE])}, + %% no formatter! + logger:add_handler(Name, logger_disk_log_h, HConfig), + Pid = whereis(Name), + true = is_pid(Pid), + {ok,#{handlers:=H}} = logger:get_logger_config(), + true = lists:member(Name,H), + + %% Formatter is added automatically + {ok,{_,#{formatter:={logger_formatter,_}}}} = + logger:get_handler_config(Name), + logger:info(M1=?msg,?domain), + Got1 = try_match_file(?log_no(LogFile,1),"=INFO REPORT====.*\n"++M1,5000), + + ok = logger:set_handler_config(Name,formatter,{nonexistingmodule,#{}}), + logger:info(M2=?msg,?domain), + Got2 = try_match_file(?log_no(LogFile,1), + Got1++"=INFO REPORT====.*\nFORMATTER CRASH: .*"++M2, + 5000), + + ok = logger:set_handler_config(Name,formatter,{?MODULE,crash}), + logger:info(M3=?msg,?domain), + Got3 = try_match_file(?log_no(LogFile,1), + Got2++"=INFO REPORT====.*\nFORMATTER CRASH: .*"++M3, + 5000), + + ok = logger:set_handler_config(Name,formatter,{?MODULE,bad_return}), + logger:info(?msg,?domain), + try_match_file(?log_no(LogFile,1), + Got3++"FORMATTER ERROR: bad_return_value", + 5000), + + %% Check that handler is still alive and was never dead + Pid = whereis(Name), + {ok,#{handlers:=H}} = logger:get_logger_config(), + ok. + +formatter_fail(cleanup,_Config) -> + _ = logger:remove_handler(?FUNCTION_NAME), + ok. + +config_fail(_Config) -> + {error,{handler_not_added,{invalid_config,logger_disk_log_h,{bad,bad}}}} = + logger:add_handler(?MODULE,logger_disk_log_h, + #{logger_disk_log_h => #{bad => bad}, + filter_default=>log, + formatter=>{?MODULE,self()}}), + {error,{handler_not_added,{invalid_levels,{42,42,_}}}} = + logger:add_handler(?MODULE,logger_disk_log_h, + #{logger_disk_log_h => #{toggle_sync_qlen=>42, + drop_new_reqs_qlen=>42}}), + + ok = logger:add_handler(?MODULE,logger_disk_log_h, + #{filter_default=>log, + formatter=>{?MODULE,self()}}), + %% can't change the disk log options for a log already in use + {error,{illegal_config_change,_,_}} = + logger:set_handler_config(?MODULE,disk_log_opts, + #{max_no_files=>2}), + %% can't change name of an existing handler + {error,{illegal_config_change,_,_}} = + logger:set_handler_config(?MODULE,id,bad), + %% incorrect values of OP params + {error,{invalid_levels,_}} = + logger:set_handler_config(?MODULE,logger_disk_log_h, + #{toggle_sync_qlen=>100, + flush_reqs_qlen=>99}), + %% invalid name of config parameter + {error,{invalid_config,logger_disk_log_h,{filesync_rep_int,2000}}} = + logger:set_handler_config(?MODULE, logger_disk_log_h, + #{filesync_rep_int => 2000}), + ok. +config_fail(cleanup,_Config) -> + logger:remove_handler(?MODULE). + +bad_input(_Config) -> + {error,{badarg,{disk_log_sync,["BadType"]}}} = + logger_disk_log_h:disk_log_sync("BadType"), + {error,{badarg,{info,["BadType"]}}} = logger_disk_log_h:info("BadType"), + {error,{badarg,{reset,["BadType"]}}} = logger_disk_log_h:reset("BadType"). + +info_and_reset(_Config) -> + ok = logger:add_handler(?MODULE,logger_disk_log_h, + #{filter_default=>log, + formatter=>{?MODULE,self()}}), + #{id := ?MODULE} = logger_disk_log_h:info(?MODULE), + ok = logger_disk_log_h:reset(?MODULE). +info_and_reset(cleanup,_Config) -> + logger:remove_handler(?MODULE). + +reconfig(Config) -> + Dir = ?config(priv_dir,Config), + ok = logger:add_handler(?MODULE, + logger_disk_log_h, + #{filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}), + #{id := ?MODULE, + toggle_sync_qlen := ?TOGGLE_SYNC_QLEN, + drop_new_reqs_qlen := ?DROP_NEW_REQS_QLEN, + flush_reqs_qlen := ?FLUSH_REQS_QLEN, + enable_burst_limit := ?ENABLE_BURST_LIMIT, + burst_limit_size := ?BURST_LIMIT_SIZE, + burst_window_time := ?BURST_WINDOW_TIME, + enable_kill_overloaded := ?ENABLE_KILL_OVERLOADED, + handler_overloaded_qlen := ?HANDLER_OVERLOADED_QLEN, + handler_overloaded_mem := ?HANDLER_OVERLOADED_MEM, + handler_restart_after := ?HANDLER_RESTART_AFTER, + filesync_repeat_interval := ?FILESYNC_REPEAT_INTERVAL, + log_opts := #{type := ?DISK_LOG_TYPE, + max_no_files := ?DISK_LOG_MAX_NO_FILES, + max_no_bytes := ?DISK_LOG_MAX_NO_BYTES, + file := _DiskLogFile}} = + logger_disk_log_h:info(?MODULE), + + ok = logger:set_handler_config(?MODULE, logger_disk_log_h, + #{toggle_sync_qlen => 1, + drop_new_reqs_qlen => 2, + flush_reqs_qlen => 3, + enable_burst_limit => false, + burst_limit_size => 10, + burst_window_time => 10, + enable_kill_overloaded => true, + handler_overloaded_qlen => 100000, + handler_overloaded_mem => 10000000, + handler_restart_after => never, + filesync_repeat_interval => no_repeat}), + #{id := ?MODULE, + toggle_sync_qlen := 1, + drop_new_reqs_qlen := 2, + flush_reqs_qlen := 3, + enable_burst_limit := false, + burst_limit_size := 10, + burst_window_time := 10, + enable_kill_overloaded := true, + handler_overloaded_qlen := 100000, + handler_overloaded_mem := 10000000, + handler_restart_after := never, + filesync_repeat_interval := no_repeat} = + logger_disk_log_h:info(?MODULE), + + ok = logger:remove_handler(?MODULE), + + File = filename:join(Dir, "logfile"), + ok = logger:add_handler(?MODULE, + logger_disk_log_h, + #{filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}, + disk_log_opts=> + #{type => halt, + max_no_files => 1, + max_no_bytes => 1024, + file => File}}), + #{log_opts := #{type := halt, + max_no_files := 1, + max_no_bytes := 1024, + file := File}} = + logger_disk_log_h:info(?MODULE), + ok. + +reconfig(cleanup, _Config) -> + logger:remove_handler(?MODULE). + +disk_log_sync(Config) -> + Dir = ?config(priv_dir,Config), + File = filename:join(Dir, ?FUNCTION_NAME), + Log = lists:concat([File,".1"]), + ok = logger:add_handler(?MODULE, + logger_disk_log_h, + #{disk_log_opts => #{file => File}, + filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,nl}}), + + start_tracer([{?MODULE,format,2}, + {disk_log,blog,2}, + {disk_log,sync,1}], + [{formatter,"first"}, + {disk_log,blog}, + {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}, + {disk_log,sync,1}], + [{formatter,"second"}, + {formatter,"third"}, + {disk_log,blog}, + {disk_log,blog}, + {disk_log,sync}]), + %% two log requests in fast succession will make the handler skip + %% an automatic disk log sync + logger:info("second", ?domain), + logger:info("third", ?domain), + %% do explicit disk_log_sync + logger_disk_log_h:disk_log_sync(?MODULE), + check_tracer(100), + + %% check that if there's no repeated disk_log_sync active, + %% a disk_log_sync is still performed when handler goes idle + logger:set_handler_config(?MODULE, logger_disk_log_h, + #{filesync_repeat_interval => no_repeat}), + no_repeat = maps:get(filesync_repeat_interval, + logger_disk_log_h:info(?MODULE)), + + start_tracer([{?MODULE,format,2}, + {disk_log,blog,2}, + {disk_log,sync,1}], + [{formatter,"fourth"}, + {disk_log,blog}, + {formatter,"fifth"}, + {disk_log,blog}, + {disk_log,sync}]), + + logger:info("fourth", ?domain), + timer:sleep(?IDLE_DETECT_TIME_MSEC*2), + logger:info("fifth", ?domain), + %% wait for automatic disk_log_sync + check_tracer(?IDLE_DETECT_TIME_MSEC*2), + + try_read_file(Log, {ok,<<"first\nsecond\nthird\nfourth\nfifth\n">>}, 1000), + + %% switch repeated disk_log_sync on and verify that the looping works + SyncInt = 1000, + WaitT = 4500, + OneSync = {logger_disk_log_h,handle_cast,repeated_disk_log_sync}, + %% receive 1 initial repeated_disk_log_sync, then 1 per sec + start_tracer([{logger_disk_log_h,handle_cast,2}], + [OneSync || _ <- lists:seq(1, 1 + trunc(WaitT/SyncInt))]), + + logger:set_handler_config(?MODULE, logger_disk_log_h, + #{filesync_repeat_interval => SyncInt}), + SyncInt = maps:get(filesync_repeat_interval, + logger_disk_log_h:info(?MODULE)), + timer:sleep(WaitT), + logger:set_handler_config(?MODULE, logger_disk_log_h, + #{filesync_repeat_interval => no_repeat}), + check_tracer(100), + ok. +disk_log_sync(cleanup,_Config) -> + logger:remove_handler(?MODULE). + +disk_log_wrap(Config) -> + Get = fun(Key, PL) -> proplists:get_value(Key, PL) end, + Dir = ?config(priv_dir,Config), + File = filename:join(Dir, ?FUNCTION_NAME), + ct:pal("Log = ~p", [File]), + MaxFiles = 3, + MaxBytes = 5, + ok = logger:add_handler(?MODULE, + logger_disk_log_h, + #{filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}, + disk_log_opts=> + #{type => wrap, + max_no_files => MaxFiles, + max_no_bytes => MaxBytes, + file => File}}), + Info = disk_log:info(?MODULE), + {File,wrap,{MaxBytes,MaxFiles},1} = + {Get(file,Info),Get(type,Info),Get(size,Info),Get(current_file,Info)}, + Tester = self(), + TraceFun = fun({trace,_,call,{Mod,Func,Details}}, Pid) -> + Pid ! {trace,Mod,Func,Details}, + Pid + end, + {ok,_} = dbg:tracer(process, {TraceFun, Tester}), + {ok,_} = dbg:p(whereis(?MODULE), [c]), + {ok,_} = dbg:tp(logger_disk_log_h, handle_info, 2, []), + + Text = [34 + rand:uniform(126-34) || _ <- lists:seq(1,MaxBytes)], + ct:pal("String = ~p (~w)", [Text, erts_debug:size(Text)]), + %% fill first file + lists:foreach(fun(N) -> + Log = lists:concat([File,".",N]), + logger:info(Text, ?domain), + wait_until_written(Log), + ct:pal("N = ~w", + [N = Get(current_file, + disk_log:info(?MODULE))]) + end, lists:seq(1,MaxFiles)), + + %% wait for trace messages + timer:sleep(1000), + dbg:stop_clear(), + Received = lists:flatmap(fun({trace,_M,handle_info, + [{disk_log,_Node,_Name,What},_]}) -> + [{trace,What}]; + ({log,_}) -> + [] + end, test_server:messages_get()), + ct:pal("Trace =~n~p", [Received]), + Received = [{trace,{wrap,0}} || _ <- lists:seq(1,MaxFiles-1)], + ok. + +disk_log_wrap(cleanup,_Config) -> + logger:remove_handler(?MODULE). + +disk_log_full(Config) -> + Dir = ?config(priv_dir,Config), + File = filename:join(Dir, ?FUNCTION_NAME), + ct:pal("Log = ~p", [File]), + MaxBytes = 50, + ok = logger:add_handler(?MODULE, + logger_disk_log_h, + #{filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}, + disk_log_opts=> + #{type => halt, + max_no_files => 1, + max_no_bytes => MaxBytes, + file => File}}), + + Tester = self(), + TraceFun = fun({trace,_,call,{Mod,Func,Details}}, Pid) -> + Pid ! {trace,Mod,Func,Details}, + Pid + end, + {ok,_} = dbg:tracer(process, {TraceFun, Tester}), + {ok,_} = dbg:p(whereis(?MODULE), [c]), + {ok,_} = dbg:tp(logger_disk_log_h, handle_info, 2, []), + + NoOfChars = 5, + Text = [34 + rand:uniform(126-34) || _ <- lists:seq(1,NoOfChars)], + [logger:info(Text, ?domain) || _ <- lists:seq(1,trunc(MaxBytes/NoOfChars)+1)], + + %% wait for trace messages + timer:sleep(2000), + dbg:stop_clear(), + Received = lists:flatmap(fun({trace,_M,handle_info, + [{disk_log,_Node,_Name,What},_]}) -> + [{trace,What}]; + ({log,_}) -> + [] + end, test_server:messages_get()), + ct:pal("Trace =~n~p", [Received]), + [{trace,full}, + {trace,{error_status,{error,{full,_}}}}] = Received, + ok. +disk_log_full(cleanup, _Config) -> + logger:remove_handler(?MODULE). + +disk_log_events(Config) -> + Node = node(), + Log = ?MODULE, + ok = logger:add_handler(?MODULE, + logger_disk_log_h, + #{filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}), + + %% Events copied from disk_log API + Events = + [{disk_log, Node, Log, {wrap, 0}}, + {disk_log, Node, Log, {truncated, 0}}, + {disk_log, Node, Log, {read_only, 42}}, + {disk_log, Node, Log, {blocked_log, 42}}, + {disk_log, Node, Log, {format_external, 42}}, + {disk_log, Node, Log, full}, + {disk_log, Node, Log, {error_status, ok}}], + + Tester = self(), + TraceFun = fun({trace,_,call,{Mod,Func,Details}}, Pid) -> + Pid ! {trace,Mod,Func,Details}, + Pid + end, + {ok,_} = dbg:tracer(process, {TraceFun, Tester}), + {ok,_} = dbg:p(whereis(?MODULE), [c]), + {ok,_} = dbg:tp(logger_disk_log_h, handle_info, 2, []), + + [whereis(?MODULE) ! E || E <- Events], + %% wait for trace messages + timer:sleep(2000), + dbg:stop_clear(), + Received = lists:map(fun({trace,_M,handle_info, + [Got,_]}) -> Got + end, test_server:messages_get()), + ct:pal("Trace =~n~p", [Received]), + NoOfEvents = length(Events), + NoOfEvents = length(Received), + lists:foreach(fun(Event) -> + true = lists:member(Event, Received) + end, Received), + ok. +disk_log_events(cleanup, _Config) -> + logger:remove_handler(?MODULE). + +write_failure(Config) -> + Dir = ?config(priv_dir, Config), + File = filename:join(Dir, ?FUNCTION_NAME), + Log = lists:concat([File,".1"]), + ct:pal("Log = ~p", [Log]), + + Node = start_h_on_new_node(Config, ?FUNCTION_NAME, 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]), + rpc:call(Node, ?MODULE, set_result, [disk_log_blog,ok]), + HState = rpc:call(Node, logger_disk_log_h, info, [?STANDARD_HANDLER]), + ct:pal("LogOpts = ~p", [LogOpts = maps:get(log_opts, HState)]), + + ok = log_on_remote_node(Node, "Logged1"), + rpc:call(Node, logger_disk_log_h, disk_log_sync, [?STANDARD_HANDLER]), + ?check_no_log, + try_read_file(Log, {ok,<<"Logged1\n">>}, ?SYNC_REP_INT), + + rpc:call(Node, ?MODULE, set_result, [disk_log_blog,{error,no_such_log}]), + ok = log_on_remote_node(Node, "Cause simple error printout"), + + ?check({error,{?STANDARD_HANDLER,log,LogOpts,{error,no_such_log}}}), + + ok = log_on_remote_node(Node, "No second error printout"), + ?check_no_log, + + rpc:call(Node, ?MODULE, set_result, [disk_log_blog, + {error,{full,?STANDARD_HANDLER}}]), + ok = log_on_remote_node(Node, "Cause simple error printout"), + ?check({error,{?STANDARD_HANDLER,log,LogOpts, + {error,{full,?STANDARD_HANDLER}}}}), + + rpc:call(Node, ?MODULE, set_result, [disk_log_blog,ok]), + ok = log_on_remote_node(Node, "Logged2"), + rpc:call(Node, logger_disk_log_h, disk_log_sync, [?STANDARD_HANDLER]), + ?check_no_log, + try_read_file(Log, {ok,<<"Logged1\nLogged2\n">>}, ?SYNC_REP_INT), + ok. +write_failure(cleanup, _Config) -> + Nodes = nodes(), + [test_server:stop_node(Node) || Node <- Nodes]. + + +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])), + rpc:call(Node, ets, insert, [?TEST_HOOKS_TAB,{tester,self()}]), + rpc:call(Node, ?MODULE, set_internal_log, [?MODULE,internal_log]), + rpc:call(Node, ?MODULE, set_result, [disk_log_sync,ok]), + HState = rpc:call(Node, logger_disk_log_h, info, [?STANDARD_HANDLER]), + LogOpts = maps:get(log_opts, HState), + + SyncInt = 500, + ok = rpc:call(Node, logger, set_handler_config, + [?STANDARD_HANDLER, logger_disk_log_h, + #{filesync_repeat_interval => SyncInt}]), + Info = rpc:call(Node, logger_disk_log_h, info, [?STANDARD_HANDLER]), + SyncInt = maps:get(filesync_repeat_interval, Info), + + ok = log_on_remote_node(Node, "Logged1"), + ?check_no_log, + + rpc:call(Node, ?MODULE, set_result, [disk_log_sync,{error,no_such_log}]), + ok = log_on_remote_node(Node, "Cause simple error printout"), + + ?check({error,{?STANDARD_HANDLER,sync,LogOpts,{error,no_such_log}}}), + + ok = log_on_remote_node(Node, "No second error printout"), + ?check_no_log, + + rpc:call(Node, ?MODULE, set_result, + [disk_log_sync,{error,{blocked_log,?STANDARD_HANDLER}}]), + ok = log_on_remote_node(Node, "Cause simple error printout"), + ?check({error,{?STANDARD_HANDLER,sync,LogOpts, + {error,{blocked_log,?STANDARD_HANDLER}}}}), + + rpc:call(Node, ?MODULE, set_result, [disk_log_sync,ok]), + ok = log_on_remote_node(Node, "Logged2"), + ?check_no_log, + ok. +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), + ok = rpc:call(Node,logger,set_handler_config,[?STANDARD_HANDLER,formatter, + {?MODULE,nl}]), + Node. + +log_on_remote_node(Node,Msg) -> + _ = spawn_link(Node, + fun() -> erlang:group_leader(whereis(user),self()), + logger:info(Msg) + end), + 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_defaults() -> + ?set_defaults(). + +%% internal log function that sends the term to the test case process +internal_log(Type, Term) -> + [{tester,Tester}] = ets:lookup(?TEST_HOOKS_TAB, tester), + Tester ! {log,{Type,Term}}, + logger:internal_log(Type, Term), + ok. + + +%%%----------------------------------------------------------------- +%%% Overload protection tests + +op_switch_to_sync(Config) -> + {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + NewHConfig = + HConfig#{logger_disk_log_h => DLHConfig#{toggle_sync_qlen => 3, + drop_new_reqs_qlen => 501, + flush_reqs_qlen => 2000, + enable_burst_limit => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 500, + send_burst({n,NumOfReqs}, seq, {chars,79}, info), + NumOfReqs = count_lines(Log), + ok = file:delete(Log). +op_switch_to_sync(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +op_switch_to_drop(Config) -> + {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + + NewHConfig = + HConfig#{logger_disk_log_h => DLHConfig#{toggle_sync_qlen => 2, + drop_new_reqs_qlen => 3, + flush_reqs_qlen => 600, + enable_burst_limit => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 500, + send_burst({n,NumOfReqs}, seq, {chars,79}, info), + Logged = count_lines(Log), + ct:pal("Number of messages dropped = ~w (~w)", + [NumOfReqs-Logged,NumOfReqs]), + true = (Logged < NumOfReqs), + ok = file:delete(Log). +op_switch_to_drop(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +op_switch_to_flush() -> + [{timetrap,{seconds,60}}]. +op_switch_to_flush(Config) -> + {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + + %% it's important that both async and sync requests have been queued + %% when the flush happens (verify with coverage of flush_log_requests/2) + + NewHConfig = + HConfig#{logger_disk_log_h => DLHConfig#{toggle_sync_qlen => 2, + drop_new_reqs_qlen => 99, + flush_reqs_qlen => 100, + enable_burst_limit => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 1000, + Procs = 500, + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), + Logged = count_lines(Log), + ct:pal("Number of messages flushed/dropped = ~w (~w)", + [(NumOfReqs*Procs)-Logged,NumOfReqs*Procs]), + true = (Logged < (NumOfReqs*Procs)), + ok = file:delete(Log). +op_switch_to_flush(cleanup, _Config) -> + ok = stop_handler(?MODULE). + + +limit_burst_disabled(Config) -> + {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + NewHConfig = + HConfig#{logger_disk_log_h => DLHConfig#{enable_burst_limit => false, + burst_limit_size => 10, + burst_window_time => 2000, + drop_new_reqs_qlen => 200, + flush_reqs_qlen => 300}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 100, + send_burst({n,NumOfReqs}, seq, {chars,79}, info), + Logged = count_lines(Log), + ct:pal("Number of messages logged = ~w", [Logged]), + ok = file:delete(Log), + NumOfReqs = Logged. +limit_burst_disabled(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +limit_burst_enabled_one(Config) -> + {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + ReqLimit = 10, + NewHConfig = + HConfig#{logger_disk_log_h => DLHConfig#{enable_burst_limit => true, + burst_limit_size => ReqLimit, + burst_window_time => 2000, + drop_new_reqs_qlen => 200, + flush_reqs_qlen => 300}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 100, + send_burst({n,NumOfReqs}, seq, {chars,79}, info), + Logged = count_lines(Log), + ct:pal("Number of messages logged = ~w", [Logged]), + ok = file:delete(Log), + ReqLimit = Logged. +limit_burst_enabled_one(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +limit_burst_enabled_period(Config) -> + {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + ReqLimit = 10, + BurstTWin = 1000, + NewHConfig = + HConfig#{logger_disk_log_h => DLHConfig#{enable_burst_limit => true, + burst_limit_size => ReqLimit, + burst_window_time => BurstTWin, + drop_new_reqs_qlen => 20000, + flush_reqs_qlen => 20001}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + + Windows = 3, + Sent = send_burst({t,BurstTWin*Windows}, seq, {chars,79}, info), + Logged = count_lines(Log), + ct:pal("Number of messages sent = ~w~nNumber of messages logged = ~w", + [Sent,Logged]), + ok = file:delete(Log), + true = (Logged > (ReqLimit*Windows)) andalso + (Logged < (ReqLimit*(Windows+2))). +limit_burst_enabled_period(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +kill_disabled(Config) -> + {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + NewHConfig = + HConfig#{logger_disk_log_h=>DLHConfig#{enable_kill_overloaded=>false, + handler_overloaded_qlen=>10, + handler_overloaded_mem=>100}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 100, + send_burst({n,NumOfReqs}, seq, {chars,79}, info), + Logged = count_lines(Log), + ct:pal("Number of messages logged = ~w", [Logged]), + ok = file:delete(Log), + true = is_pid(whereis(?MODULE)), + ok. +kill_disabled(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +qlen_kill_new(Config) -> + {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + Pid0 = whereis(?MODULE), + {_,Mem0} = process_info(Pid0, memory), + RestartAfter = 2000, + NewHConfig = + HConfig#{logger_disk_log_h => + DLHConfig#{enable_kill_overloaded=>true, + handler_overloaded_qlen=>10, + handler_overloaded_mem=>Mem0+50000, + handler_restart_after=>RestartAfter}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + MRef = erlang:monitor(process, Pid0), + NumOfReqs = 100, + Procs = 2, + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), + %% send_burst({n,NumOfReqs}, seq, {chars,79}, info), + receive + {'DOWN', MRef, _, _, Info} -> + case Info of + {shutdown,{overloaded,?MODULE,QLen,Mem}} -> + ct:pal("Terminated with qlen = ~w, mem = ~w", [QLen,Mem]); + killed -> + ct:pal("Slow shutdown, handler process was killed!", []) + end, + timer:sleep(RestartAfter + 1000), + true = is_pid(whereis(?MODULE)), + ok + after + 5000 -> + Info = logger_disk_log_h:info(?MODULE), + ct:pal("Handler state = ~p", [Info]), + ct:fail("Handler not dead! It should not have survived this!") + end. +qlen_kill_new(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +mem_kill_new(Config) -> + {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + Pid0 = whereis(?MODULE), + {_,Mem0} = process_info(Pid0, memory), + RestartAfter = 2000, + NewHConfig = + HConfig#{logger_disk_log_h => + DLHConfig#{enable_kill_overloaded=>true, + handler_overloaded_qlen=>50000, + handler_overloaded_mem=>Mem0+500, + handler_restart_after=>RestartAfter}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + MRef = erlang:monitor(process, Pid0), + NumOfReqs = 100, + Procs = 2, + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), + %% send_burst({n,NumOfReqs}, seq, {chars,79}, info), + receive + {'DOWN', MRef, _, _, Info} -> + case Info of + {shutdown,{overloaded,?MODULE,QLen,Mem}} -> + ct:pal("Terminated with qlen = ~w, mem = ~w", [QLen,Mem]); + killed -> + ct:pal("Slow shutdown, handler process was killed!", []) + end, + timer:sleep(RestartAfter * 2), + true = is_pid(whereis(?MODULE)), + ok + after + 5000 -> + Info = logger_disk_log_h:info(?MODULE), + ct:pal("Handler state = ~p", [Info]), + ct:fail("Handler not dead! It should not have survived this!") + end. +mem_kill_new(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +restart_after(Config) -> + {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + NewHConfig1 = + HConfig#{logger_disk_log_h=>DLHConfig#{enable_kill_overloaded=>true, + handler_overloaded_qlen=>10, + handler_restart_after=>never}}, + ok = logger:set_handler_config(?MODULE, NewHConfig1), + MRef1 = erlang:monitor(process, whereis(?MODULE)), + %% kill handler + send_burst({n,100}, {spawn,2,0}, {chars,79}, info), + receive + {'DOWN', MRef1, _, _, _Info1} -> + timer:sleep(?HANDLER_RESTART_AFTER + 1000), + undefined = whereis(?MODULE), + ok + after + 5000 -> + ct:fail("Handler not dead! It should not have survived this!") + end, + + {Log,_,_} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + RestartAfter = 2000, + NewHConfig2 = + HConfig#{logger_disk_log_h=>DLHConfig#{enable_kill_overloaded=>true, + handler_overloaded_qlen=>10, + handler_restart_after=>RestartAfter}}, + ok = logger:set_handler_config(?MODULE, NewHConfig2), + Pid0 = whereis(?MODULE), + MRef2 = erlang:monitor(process, Pid0), + %% kill handler + send_burst({n,100}, {spawn,2,0}, {chars,79}, info), + receive + {'DOWN', MRef2, _, _, _Info2} -> + timer:sleep(RestartAfter + 1000), + Pid1 = whereis(?MODULE), + true = is_pid(Pid1), + false = (Pid1 == Pid0), + ok + after + 5000 -> + ct:fail("Handler not dead! It should not have survived this!") + end, + ok. +restart_after(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +%% send handler requests (filesync, info, reset, change_config) +%% during high load to verify that sync, dropping and flushing is +%% handled correctly. +handler_requests_under_load() -> + [{timetrap,{seconds,60}}]. +handler_requests_under_load(Config) -> + {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + NewHConfig = + HConfig#{logger_disk_log_h => DLHConfig#{toggle_sync_qlen => 2, + drop_new_reqs_qlen => 1000, + flush_reqs_qlen => 2000, + enable_burst_limit => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + Pid = spawn_link(fun() -> send_requests(?MODULE, 1, [{disk_log_sync,[]}, + {info,[]}, + {reset,[]}, + {change_config,[]}]) + end), + Procs = 100, + Sent = Procs * send_burst({n,5000}, {spawn,Procs,10}, {chars,79}, info), + Pid ! {self(),finish}, + ReqResult = receive {Pid,Result} -> Result end, + Logged = count_lines(Log), + ct:pal("Number of messages sent = ~w~nNumber of messages logged = ~w", + [Sent,Logged]), + FindError = fun(Res) -> + [E || E <- Res, + is_tuple(E) andalso (element(1,E) == error)] + end, + Errors = [{Req,FindError(Res)} || {Req,Res} <- ReqResult], + 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) -> + ok = stop_handler(?MODULE). + +send_requests(HName, TO, Reqs = [{Req,Res}|Rs]) -> + receive + {From,finish} -> + From ! {self(),Reqs} + after + TO -> + Result = + case Req of + change_config -> + logger:set_handler_config(HName, logger_disk_log_h, + #{enable_kill_overloaded => + false}); + Func -> + logger_disk_log_h:Func(HName) + end, + send_requests(HName, TO, Rs ++ [{Req,[Result|Res]}]) + end. + +%%%----------------------------------------------------------------- +%%% +start_handler(Name, FuncName, Config) -> + Dir = ?config(priv_dir,Config), + File = filename:join(Dir, FuncName), + ct:pal("Logging to ~tp", [File]), + ok = logger:add_handler(Name, + logger_disk_log_h, + #{disk_log_opts=>#{file => File, + max_no_files => 1, + max_no_bytes => 100000000}, + filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([Name]), + formatter=>{?MODULE,op}}), + {ok,{_,HConfig = #{logger_disk_log_h := DLHConfig}}} = + logger:get_handler_config(Name), + {lists:concat([File,".1"]),HConfig,DLHConfig}. + +stop_handler(Name) -> + ok = logger:remove_handler(Name), + ct:pal("Handler ~p stopped!", [Name]). + +send_burst(NorT, Type, {chars,Sz}, Class) -> + Text = [34 + rand:uniform(126-34) || _ <- lists:seq(1,Sz)], + case NorT of + {n,N} -> + %% process_flag(priority, high), + send_n_burst(N, Type, Text, Class), + %% process_flag(priority, normal), + N; + {t,T} -> + ct:pal("Sending messages sequentially for ~w ms", [T]), + T0 = erlang:monotonic_time(millisecond), + send_t_burst(T0, T, Text, Class, 0) + end. + +send_n_burst(0, _, _Text, _Class) -> + ok; +send_n_burst(N, seq, Text, Class) -> + ok = logger:Class(Text, ?domain), + send_n_burst(N-1, seq, Text, Class); +send_n_burst(N, {spawn,Ps,TO}, Text, Class) -> + ct:pal("~w processes each sending ~w messages", [Ps,N]), + PerProc = fun() -> + send_n_burst(N, seq, Text, Class) + end, + MRefs = [begin if TO == 0 -> ok; true -> timer:sleep(TO) end, + monitor(process,spawn_link(PerProc)) end || + _ <- lists:seq(1,Ps)], + lists:foreach(fun(MRef) -> + receive + {'DOWN', MRef, _, _, _} -> + ok + end + end, MRefs), + ct:pal("Message burst sent", []), + ok. + +send_t_burst(T0, T, Text, Class, N) -> + T1 = erlang:monotonic_time(millisecond), + if (T1-T0) > T -> + N; + true -> + ok = logger:Class(Text, ?domain), + send_t_burst(T0, T, Text, Class, N+1) + end. + +%%%----------------------------------------------------------------- +%%% Formatter callback +%%% Using this to send the formatted string back to the test case +%%% process - so it can check for logged events. +format(_,bad_return) -> + bad_return; +format(_,crash) -> + erlang:error(formatter_crashed); +format(#{msg:={report,R},meta:=#{report_cb:=Fun}}=Log,Config) -> + format(Log#{msg=>Fun(R)},Config); +format(#{msg:={string,String0}},no_nl) -> + String = unicode:characters_to_list(String0), + String; +format(#{msg:={string,String0}},nl) -> + String = unicode:characters_to_list(String0), + String++"\n"; +format(#{msg:={string,String0}},op) -> + String = unicode:characters_to_list(String0), + String++"\n"; +format(#{msg:={report,#{label:={supervisor,progress}}}},op) -> + ""; +format(#{msg:={report,#{label:={gen_server,terminate}}}},op) -> + ""; +format(#{msg:={report,#{label:={proc_lib,crash}}}},op) -> + ""; +format(#{msg:={F,A}},Pid) when is_list(F), is_list(A) -> + String = lists:flatten(io_lib:format(F,A)), + Pid ! {log,String}, + String++"\n"; +format(#{msg:={string,String0}},Pid) -> + String = unicode:characters_to_list(String0), + Pid ! {log,String}, + String++"\n"; +format(Msg,Tag) -> + Error = {unexpected_format,Msg,Tag}, + erlang:display(Error), + exit(Error). + +remove(Handler, LogName) -> + logger_disk_log_h:remove(Handler, LogName), + HState = #{log_names := Logs} = logger_disk_log_h:info(), + false = maps:is_key(LogName, HState), + false = lists:member(LogName, Logs), + false = logger_config:exist(logger, LogName), + {error,no_such_log} = disk_log:info(LogName), + ok. + +start_and_add(Name, Config, LogOpts) -> + ct:pal("Adding handler ~w with: ~p", + [Name,Config#{disk_log_opts=>LogOpts}]), + ok = logger:add_handler(Name, logger_disk_log_h, + Config#{disk_log_opts=>LogOpts}), + Pid = whereis(Name), + true = is_pid(Pid), + Name = proplists:get_value(name, disk_log:info(Name)), + ok. + +remove_and_stop(Handler) -> + ok = logger:remove_handler(Handler), + timer:sleep(500), + undefined = whereis(Handler), + ok. + +try_read_file(FileName, Expected, Time) -> + try_read_file(FileName, Expected, Time, undefined). + +try_read_file(FileName, Expected, Time, _) when Time > 0 -> + case file:read_file(FileName) of + Expected -> + ok; + Error = {error,_Reason} -> + erlang:error(Error); + SomethingElse -> + ct:pal("try_read_file read unexpected: ~p~n", [SomethingElse]), + timer:sleep(500), + try_read_file(FileName, Expected, Time-500, SomethingElse) + end; + +try_read_file(_, _, _, Incorrect) -> + ct:pal("try_read_file got incorrect pattern: ~p~n", [Incorrect]), + erlang:error({error,not_matching_pattern,Incorrect}). + +try_match_file(FileName, Pattern, Time) -> + try_match_file(FileName, Pattern, Time, <<>>). + +try_match_file(FileName, Pattern, Time, _) when Time > 0 -> + case file:read_file(FileName) of + {ok, Bin} -> + case re:run(Bin,Pattern,[{capture,none}]) of + match -> + unicode:characters_to_list(Bin); + _ -> + timer:sleep(100), + try_match_file(FileName, Pattern, Time-100, Bin) + end; + Error -> + erlang:error(Error) + end; +try_match_file(_,Pattern,_,Incorrect) -> + ct:pal("try_match_file did not match pattern: ~p~nGot: ~p~n", + [Pattern,Incorrect]), + erlang:error({error,not_matching_pattern,Pattern,Incorrect}). + +count_lines(File) -> + wait_until_written(File), + count_lines1(File). + +wait_until_written(File) -> + wait_until_written(File, -1). + +wait_until_written(File, Sz) -> + timer:sleep(2000), + case file:read_file_info(File) of + {ok,#file_info{size = Sz}} -> + timer:sleep(1000), + case file:read_file_info(File) of + {ok,#file_info{size = Sz1}} -> + ok; + {ok,#file_info{size = Sz2}} -> + wait_until_written(File, Sz2) + end; + {ok,#file_info{size = Sz1}} -> + wait_until_written(File, Sz1) + end. + +count_lines1(File) -> + Counter = fun Cnt(Dev,LC) -> + case file:read_line(Dev) of + eof -> LC; + _ -> Cnt(Dev,LC+1) + end + end, + {_,Dev} = file:open(File, [read]), + Lines = Counter(Dev, 0), + file:close(Dev), + Lines. + +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. + +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,{?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,Func,_}}, {Pid,[{Mod,Func}|Expected]}) -> + maybe_tracer_done(Pid,Expected,{Mod,Func}); +tracer({trace,_,call,Call}, {Pid,Expected}) -> + Pid ! {tracer_got_unexpected,Call,Expected}, + {Pid,Expected}. + +maybe_tracer_done(Pid,[],Got) -> + ct:log("Tracer got: ~p~n",[Got]), + Pid ! tracer_done; +maybe_tracer_done(Pid,Expected,Got) -> + ct:log("Tracer got: ~p~n",[Got]), + {Pid,Expected}. + +check_tracer(T) -> + receive + tracer_done -> + dbg:stop_clear(), + ok; + {tracer_got_unexpected,Got,Expected} -> + dbg:stop_clear(), + ct:fail({tracer_got_unexpected,Got,Expected}) + after T -> + ct:fail({timeout,tracer}) + end. diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl new file mode 100644 index 0000000000..c2d3364701 --- /dev/null +++ b/lib/kernel/test/logger_env_var_SUITE.erl @@ -0,0 +1,451 @@ +% +%% %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_env_var_SUITE). + +-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}]). + +suite() -> + [{timetrap,{seconds,30}}]. + +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(), + ok. + +groups() -> + []. + +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 + ]. + +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)), + 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), + 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)), + 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. + +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)), + 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), + 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)), + 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. + +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), + {logger_simple,logger_simple,SimpleC} = lists:keyfind(logger_simple,1,Hs), + notice = maps:get(level,SimpleC), + 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. + +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), + {logger_simple,logger_simple,SimpleC} = lists:keyfind(logger_simple,1,Hs), + notice = maps:get(level,SimpleC), + SimpleFilters = maps:get(filters,SimpleC), + {domain,{_,{log,prefix_of,[beam,erlang,otp,sasl]}}} = + lists:keyfind(domain,1,SimpleFilters), + false = lists:keymember(stop_progress,1,SimpleFilters), + 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), + {logger_simple,logger_simple,SimpleC} = lists:keyfind(logger_simple,1,Hs), + notice = maps:get(level,SimpleC), + 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), + 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), + false = lists:keymember(logger_simple,1,Hs), + true = lists:keymember(sasl_h,1,Hs), + true = is_pid(whereis(sasl_h)), + ok. + + +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 + 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 + 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 + 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 + 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 + ok. + +bad_dest(Config) -> + {error,{bad_config,{kernel,{logger_dest,baddest}}}} = + setup(Config,?FUNCTION_NAME, + logger_dest, + baddest, + undefined, + undefined, + undefined). + +bad_level(Config) -> + error = + setup(Config,?FUNCTION_NAME, + logger_dest, + tty, + badlevel, + undefined, + undefined). + +bad_sasl_compatibility(Config) -> + error = + setup(Config,?FUNCTION_NAME, + logger_dest, + tty, + info, + badcomp, + undefined). + +bad_progress(Config) -> + error = + setup(Config,?FUNCTION_NAME, + logger_dest, + tty, + info, + undefined, + 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. + +check_log(Log,Dest,NumProgress) -> + ok = logger:alert("dummy1"), + ok = logger: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), + 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), + ct:log("Log content:~n~s",[Bin2]), + match(Bin2,<<"INFO REPORT">>,1), + match(Bin1,<<"DEBUG REPORT">>,0), + ok. + +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. diff --git a/lib/kernel/test/logger_filters_SUITE.erl b/lib/kernel/test/logger_filters_SUITE.erl new file mode 100644 index 0000000000..21f14bbc02 --- /dev/null +++ b/lib/kernel/test/logger_filters_SUITE.erl @@ -0,0 +1,214 @@ +%% +%% %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_filters_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("kernel/include/logger.hrl"). + +-define(ndlog, + #{level=>info,msg=>{"Line: ~p",[?LINE]},meta=>#{}}). +-define(dlog(Domain), + #{level=>info,msg=>{"Line: ~p",[?LINE]},meta=>#{domain=>Domain}}). +-define(llog(Level), + #{level=>Level,msg=>{"Line: ~p",[?LINE]},meta=>#{}}). +-define(plog, + #{level=>info, + msg=>{report,#{label=>{?MODULE,progress}}}, + meta=>#{line=>?LINE}}). +-define(rlog(Node), + #{level=>info, + msg=>{"Line: ~p",[?LINE]}, + meta=>#{gl=>rpc:call(Node,erlang,whereis,[user])}}). + +-define(TRY(X), my_try(fun() -> X end)). + +suite() -> + [{timetrap,{seconds,30}}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + 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, + ok. + +groups() -> + []. + +all() -> + [domain, + level, + progress, + remote_gl]. + +domain(_Config) -> + L1 = logger_filters:domain(L1=?dlog([]),{log,prefix_of,[]}), + stop = logger_filters:domain(?dlog([]),{stop,prefix_of,[]}), + L2 = logger_filters:domain(L2=?dlog([]),{log,starts_with,[]}), + stop = logger_filters:domain(?dlog([]),{stop,starts_with,[]}), + L3 = logger_filters:domain(L3=?dlog([]),{log,equals,[]}), + stop = logger_filters:domain(?dlog([]),{stop,equals,[]}), + ignore = logger_filters:domain(?dlog([]),{log,no_domain,[]}), + ignore = logger_filters:domain(?dlog([]),{stop,no_domain,[]}), + + L4 = logger_filters:domain(L4=?dlog([a]),{log,prefix_of,[a,b]}), + stop = logger_filters:domain(?dlog([a]),{stop,prefix_of,[a,b]}), + ignore = logger_filters:domain(?dlog([a]),{log,starts_with,[a,b]}), + ignore = logger_filters:domain(?dlog([a]),{stop,starts_with,[a,b]}), + ignore = logger_filters:domain(?dlog([a]),{log,equals,[a,b]}), + ignore = logger_filters:domain(?dlog([a]),{stop,equals,[a,b]}), + ignore = logger_filters:domain(?dlog([a]),{log,no_domain,[a,b]}), + ignore = logger_filters:domain(?dlog([a]),{stop,no_domain,[a,b]}), + + ignore = logger_filters:domain(?dlog([a,b]),{log,prefix_of,[a]}), + ignore = logger_filters:domain(?dlog([a,b]),{stop,prefix_of,[a]}), + L5 = logger_filters:domain(L5=?dlog([a,b]),{log,starts_with,[a]}), + stop = logger_filters:domain(?dlog([a,b]),{stop,starts_with,[a]}), + ignore = logger_filters:domain(?dlog([a,b]),{log,equals,[a]}), + ignore = logger_filters:domain(?dlog([a,b]),{stop,equals,[a]}), + ignore = logger_filters:domain(?dlog([a,b]),{log,no_domain,[a]}), + ignore = logger_filters:domain(?dlog([a,b]),{stop,no_domain,[a]}), + + ignore = logger_filters:domain(?ndlog,{log,prefix_of,[a]}), + ignore = logger_filters:domain(?ndlog,{stop,prefix_of,[a]}), + ignore = logger_filters:domain(?ndlog,{log,starts_with,[a]}), + ignore = logger_filters:domain(?ndlog,{stop,starts_with,[a]}), + ignore = logger_filters:domain(?ndlog,{log,equals,[a]}), + ignore = logger_filters:domain(?ndlog,{stop,equals,[a]}), + L6 = logger_filters:domain(L6=?ndlog,{log,no_domain,[a]}), + stop = logger_filters:domain(?ndlog,{stop,no_domain,[a]}), + + L7 = logger_filters:domain(L7=?dlog([a,b,c,d]),{log,prefix_of,[a,b,c,d]}), + stop = logger_filters:domain(?dlog([a,b,c,d]),{stop,prefix_of,[a,b,c,d]}), + L8 = logger_filters:domain(L8=?dlog([a,b,c,d]),{log,starts_with,[a,b,c,d]}), + stop = logger_filters:domain(?dlog([a,b,c,d]),{stop,starts_with,[a,b,c,d]}), + L9 = logger_filters:domain(L9=?dlog([a,b,c,d]),{log,equals,[a,b,c,d]}), + stop = logger_filters:domain(?dlog([a,b,c,d]),{stop,equals,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog([a,b,c,d]),{log,no_domain,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog([a,b,c,d]),{stop,no_domain,[a,b,c,d]}), + + %% A domain field in meta which is not a list is allowed by the + %% filter, but it will never match. + ignore = logger_filters:domain(?dlog(dummy),{log,prefix_of,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{stop,prefix_of,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{log,starts_with,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{stop,starts_with,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{log,equals,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{stop,equals,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{log,no_domain,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{stop,no_domain,[a,b,c,d]}), + + {error,badarg} = ?TRY(logger_filters:domain(?ndlog,bad)), + {error,badarg} = ?TRY(logger_filters:domain(?ndlog,{bad,prefix_of,[]})), + {error,badarg} = ?TRY(logger_filters:domain(?ndlog,{log,bad,[]})), + {error,badarg} = ?TRY(logger_filters:domain(?ndlog,{log,prefix_of,bad})), + + ok. + +level(_Config) -> + ignore = logger_filters:level(?llog(info),{log,lt,info}), + ignore = logger_filters:level(?llog(info),{stop,lt,info}), + ignore = logger_filters:level(?llog(info),{log,gt,info}), + ignore = logger_filters:level(?llog(info),{stop,gt,info}), + L1 = logger_filters:level(L1=?llog(info),{log,lteq,info}), + stop = logger_filters:level(?llog(info),{stop,lteq,info}), + L2 = logger_filters:level(L2=?llog(info),{log,gteq,info}), + stop = logger_filters:level(?llog(info),{stop,gteq,info}), + L3 = logger_filters:level(L3=?llog(info),{log,eq,info}), + stop = logger_filters:level(?llog(info),{stop,eq,info}), + ignore = logger_filters:level(?llog(info),{log,neq,info}), + ignore = logger_filters:level(?llog(info),{stop,neq,info}), + + ignore = logger_filters:level(?llog(error),{log,lt,info}), + ignore = logger_filters:level(?llog(error),{stop,lt,info}), + L4 = logger_filters:level(L4=?llog(error),{log,gt,info}), + stop = logger_filters:level(?llog(error),{stop,gt,info}), + ignore = logger_filters:level(?llog(error),{log,lteq,info}), + ignore = logger_filters:level(?llog(error),{stop,lteq,info}), + L5 = logger_filters:level(L5=?llog(error),{log,gteq,info}), + stop = logger_filters:level(?llog(error),{stop,gteq,info}), + ignore = logger_filters:level(?llog(error),{log,eq,info}), + ignore = logger_filters:level(?llog(error),{stop,eq,info}), + L6 = logger_filters:level(L6=?llog(error),{log,neq,info}), + stop = logger_filters:level(?llog(error),{stop,neq,info}), + + L7 = logger_filters:level(L7=?llog(info),{log,lt,error}), + stop = logger_filters:level(?llog(info),{stop,lt,error}), + ignore = logger_filters:level(?llog(info),{log,gt,error}), + ignore = logger_filters:level(?llog(info),{stop,gt,error}), + L8 = logger_filters:level(L8=?llog(info),{log,lteq,error}), + stop = logger_filters:level(?llog(info),{stop,lteq,error}), + ignore = logger_filters:level(?llog(info),{log,gteq,error}), + ignore = logger_filters:level(?llog(info),{stop,gteq,error}), + ignore = logger_filters:level(?llog(info),{log,eq,error}), + ignore = logger_filters:level(?llog(info),{stop,eq,error}), + L9 = logger_filters:level(L9=?llog(info),{log,neq,error}), + stop = logger_filters:level(?llog(info),{stop,neq,error}), + + {error,badarg} = ?TRY(logger_filters:level(?llog(info),bad)), + {error,badarg} = ?TRY(logger_filters:level(?llog(info),{bad,eq,info})), + {error,badarg} = ?TRY(logger_filters:level(?llog(info),{log,bad,info})), + {error,badarg} = ?TRY(logger_filters:level(?llog(info),{log,eq,bad})), + + ok. + +progress(_Config) -> + L1 = logger_filters:progress(L1=?plog,log), + stop = logger_filters:progress(?plog,stop), + ignore = logger_filters:progress(?ndlog,log), + ignore = logger_filters:progress(?ndlog,stop), + + {error,badarg} = ?TRY(logger_filters:progress(?plog,bad)), + + ok. + +remote_gl(_Config) -> + {ok,Node} = test_server:start_node(?FUNCTION_NAME,slave,[]), + L1 = logger_filters:remote_gl(L1=?rlog(Node),log), + stop = logger_filters:remote_gl(?rlog(Node),stop), + ignore = logger_filters:remote_gl(?ndlog,log), + ignore = logger_filters:remote_gl(?ndlog,stop), + + {error,badarg} = ?TRY(logger_filters:remote_gl(?rlog(Node),bad)), + ok. + +remote_gl(cleanup,_Config) -> + [test_server:stop_node(N) || N<-nodes()]. + +%%%----------------------------------------------------------------- +%%% Called by macro ?TRY(X) +my_try(Fun) -> + try Fun() catch C:R -> {C,R} end. diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl new file mode 100644 index 0000000000..44e12d2d2a --- /dev/null +++ b/lib/kernel/test/logger_formatter_SUITE.erl @@ -0,0 +1,501 @@ +%% +%% %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_formatter_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("kernel/include/logger.hrl"). + +-define(TRY(X), my_try(fun() -> X end)). + +suite() -> + [{timetrap,{seconds,30}}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + 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, + ok. + +groups() -> + []. + +all() -> + [default, + legacy_header, + single_line, + template, + format_msg, + report_cb, + max_size, + depth, + format_mfa, + format_time, + level_or_msg_in_meta, + faulty_log, + faulty_config, + faulty_msg]. + +default(_Config) -> + String1 = format(info,{"~p",[term]},#{},#{}), + ct:log(String1), + [_Date,_Time,"info:\nterm\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), + ok. + +legacy_header(_Config) -> + Time = timestamp(), + String1 = format(info,{"~p",[term]},#{time=>Time},#{legacy_header=>true}), + ct:log(String1), + "=INFO REPORT==== "++Rest = String1, + [Timestamp,"\nterm\n"] = string:lexemes(Rest," ="), + [D,M,Y,H,Min,S,Micro] = string:lexemes(Timestamp,"-:."), + integer(D,31), + integer(Y,2018,infinity), + integer(H,23), + integer(Min,59), + integer(S,59), + integer(Micro,999999), + 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}), + 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}), + ct:log(String3), + String3 = String2, + + String4 = format(info,{"~p",[term]},#{time=>Time}, + #{legacy_header=>true, + single_line=>true}), % <---ignored + ct:log(String4), + String4 = String1, + + String5 = format(info,{"~p",[term]},#{}, % <--- no time + #{legacy_header=>true}), + ct:log(String5), + "=INFO REPORT==== "++_ = String5, + ok. + +single_line(_Config) -> + Time = timestamp(), + ExpectedTimestamp = default_time_format(Time), + String1 = format(info,{"~p",[term]},#{time=>Time},#{single_line=>true}), + ct:log(String1), + " info: term\n" = string:prefix(String1,ExpectedTimestamp), + + String2 = format(info,{"~p",[term]},#{time=>Time},#{single_line=>false}), + ct:log(String2), + " info:\nterm\n" = string:prefix(String2,ExpectedTimestamp), + + String2 = format(info,{"~p",[term]},#{time=>Time},#{single_line=>bad}), + ok. + +template(_Config) -> + Time = timestamp(), + + Template1 = [msg], + String1 = format(info,{"~p",[term]},#{time=>Time},#{template=>Template1}), + ct:log(String1), + "term" = String1, + + Template2 = [msg,unknown], + String2 = format(info,{"~p",[term]},#{time=>Time},#{template=>Template2}), + ct:log(String2), + "term" = String2, + + Template3 = ["string"], + String3 = format(info,{"~p",[term]},#{time=>Time},#{template=>Template3}), + ct:log(String3), + "string" = String3, + + Template4 = ["string\nnewline"], + String4 = format(info,{"~p",[term]},#{time=>Time},#{template=>Template4, + single_line=>true}), + ct:log(String4), + "string\nnewline" = String4, + + Template5 = [], + String5 = format(info,{"~p",[term]},#{time=>Time},#{template=>Template5}), + ct:log(String5), + "" = String5, + + Ref6 = erlang:make_ref(), + Meta6 = #{atom=>some_atom, + integer=>632, + list=>[list,"string",4321,#{},{tuple}], + mfa=>{mod,func,0}, + pid=>self(), + ref=>Ref6, + string=>"some string", + time=>Time, + tuple=>{1,atom,"list"}, + nested=>#{subkey=>subvalue}}, + Template6 = lists:join(";",maps:keys(maps:remove(nested,Meta6)) ++ + [{nested,subkey}]), + String6 = format(info,{"~p",[term]},Meta6,#{template=>Template6, + single_line=>true}), + ct:log(String6), + SelfStr = pid_to_list(self()), + RefStr6 = ref_to_list(Ref6), + ListStr = "[list,\"string\",4321,#{},{tuple}]", + ExpectedTime6 = default_time_format(Time), + ["some_atom", + "632", + ListStr, + "mod:func/0", + SelfStr, + RefStr6, + "some string", + ExpectedTime6, + "{1,atom,\"list\"}", + "subvalue"] = string:lexemes(String6,";"), + + Meta7 = #{time=>Time, + nested=>#{key1=>#{subkey1=>value1}, + key2=>value2}}, + Template7 = lists:join(";",[nested, + {nested,key1}, + {nested,key1,subkey1}, + {nested,key2}, + {nested,key2,subkey2}, + {nested,key3}, + {nested,key3,subkey3}]), + String7 = format(info,{"~p",[term]},Meta7,#{template=>Template7, + single_line=>true}), + ct:log(String7), + [MultipleKeysStr, + "#{subkey1 => value1}", + "value1", + "value2", + "", + "", + ""] = string:split(String7,";",all), + %% Order of keys is not fixed + case MultipleKeysStr of + "#{key2 => value2,key1 => #{subkey1 => value1}}" -> ok; + "#{key1 => #{subkey1 => value1},key2 => value2}" -> ok; + _ -> ct:fail({full_nested_map_unexpected,MultipleKeysStr}) + end, + ok. + +format_msg(_Config) -> + Template = [msg], + + String1 = format(info,{"~p",[term]},#{},#{template=>Template}), + ct:log(String1), + "term" = String1, + + String2 = format(info,{"list",[term]},#{},#{template=>Template}), + ct:log(String2), + "FORMAT ERROR: \"list\" - [term]" = String2, + + String3 = format(info,{report,term},#{},#{template=>Template}), + ct:log(String3), + "term" = String3, + + String4 = format(info,{report,term}, + #{report_cb=>fun(_)-> {"formatted",[]} end}, + #{template=>Template}), + ct:log(String4), + "formatted" = String4, + + String5 = format(info,{report,term}, + #{report_cb=>fun(_)-> faulty_return end}, + #{template=>Template}), + ct:log(String5), + "REPORT_CB ERROR: term; Returned: faulty_return" = String5, + + String6 = format(info,{report,term}, + #{report_cb=>fun(_)-> erlang:error(fun_crashed) end}, + #{template=>Template}), + ct:log(String6), + "REPORT_CB CRASH: term; Reason: {error,fun_crashed}" = String6, + + %% strings are not formatted + String7 = format(info,{string,"string"}, + #{report_cb=>fun(_)-> {"formatted",[]} end}, + #{template=>Template}), + ct:log(String7), + "string" = String7, + + String8 = format(info,{string,['not',printable,list]}, + #{report_cb=>fun(_)-> {"formatted",[]} end}, + #{template=>Template}), + ct:log(String8), + "INVALID STRING: ['not',printable,list]" = String8, + + String9 = format(info,{string,"string"},#{},#{template=>Template}), + ct:log(String9), + "string" = String9, + + ok. + +report_cb(_Config) -> + Template = [msg], + MetaFun = fun(_) -> {"meta_rcb",[]} end, + ConfigFun = fun(_) -> {"config_rcb",[]} end, + "term" = format(info,{report,term},#{},#{template=>Template}), + "meta_rcb" = + format(info,{report,term},#{report_cb=>MetaFun},#{template=>Template}), + "config_rcb" = + format(info,{report,term},#{},#{template=>Template, + report_cb=>ConfigFun}), + "config_rcb" = + format(info,{report,term},#{report_cb=>MetaFun},#{template=>Template, + report_cb=>ConfigFun}), + ok. + +max_size(_Config) -> + Template = [msg], + "12345678901234567890" = + format(info,{"12345678901234567890",[]},#{},#{template=>Template}), + application:set_env(kernel,logger_max_size,11), + "12345678901234567890" = % min value is 50, so this is not limited + format(info,{"12345678901234567890",[]},#{},#{template=>Template}), + "12345678901234567890123456789012345678901234567..." = % 50 + format(info, + {"123456789012345678901234567890123456789012345678901234567890", + []}, + #{}, + #{template=>Template}), + application:set_env(kernel,logger_max_size,53), + "12345678901234567890123456789012345678901234567890..." = %53 + format(info, + {"123456789012345678901234567890123456789012345678901234567890", + []}, + #{}, + #{template=>Template}), + "1234567..." = + format(info,{"12345678901234567890",[]},#{},#{template=>Template, + max_size=>10}), + "12345678901234567890" = + format(info,{"12345678901234567890",[]},#{},#{template=>Template, + max_size=>unlimited}), + ok. +max_size(cleanup,_Config) -> + application:unset_env(kernel,logger_max_size), + ok. + +depth(_Config) -> + Template = [msg], + "[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0]" = + format(info, + {"~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,error_logger_format_depth,11), + "[1,2,3,4,5,6,7,8,9,0|...]" = + format(info, + {"~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), + "[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]]}, + #{}, + #{template=>Template}), + "[1,2,3,4,5,6,7,8,9,0,1,2|...]" = + format(info, + {"~p",[[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0]]}, + #{}, + #{template=>Template, + depth=>13}), + "[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0]" = + format(info, + {"~p",[[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0]]}, + #{}, + #{template=>Template, + depth=>unlimited}), + ok. +depth(cleanup,_Config) -> + application:unset_env(kernel,logger_format_depth), + ok. + +format_mfa(_Config) -> + Template = [mfa], + + Meta1 = #{mfa=>{mod,func,0}}, + String1 = format(info,{"~p",[term]},Meta1,#{template=>Template}), + ct:log(String1), + "mod:func/0" = String1, + + Meta2 = #{mfa=>{mod,func,[]}}, + String2 = format(info,{"~p",[term]},Meta2,#{template=>Template}), + ct:log(String2), + "mod:func/0" = String2, + + Meta3 = #{mfa=>"mod:func/0"}, + String3 = format(info,{"~p",[term]},Meta3,#{template=>Template}), + ct:log(String3), + "mod:func/0" = String3, + + Meta4 = #{mfa=>othermfa}, + String4 = format(info,{"~p",[term]},Meta4,#{template=>Template}), + ct:log(String4), + "othermfa" = String4, + + ok. + +format_time(_Config) -> + Time1 = timestamp(), + ExpectedTimestamp1 = default_time_format(Time1), + String1 = format(info,{"~p",[term]},#{time=>Time1},#{}), + ct:log(String1), + " info:\nterm\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), + + 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), + + ok. + +format_time(cleanup,_Config) -> + application:unset_env(kernel,logger_utc), + ok. + +level_or_msg_in_meta(_Config) -> + %% The template contains atoms to pick out values from meta, + %% or level/msg to add these from the log event. What if you have + %% a key named 'level' or 'msg' in meta and want to display + %% its value? + %% For now we simply ignore Meta on this and display the + %% actual level and msg from the log event. + + Meta = #{level=>mylevel, + msg=>"metamsg"}, + Template = [level,";",msg], + String = format(info,{"~p",[term]},Meta,#{template=>Template}), + ct:log(String), + "info;term" = String, % so mylevel and "metamsg" are ignored + + ok. + +faulty_log(_Config) -> + %% Unexpected log (should be type logger:log()) - print error + {error, + function_clause, + {logger_formatter,format,[_,_],_}} = + ?TRY(logger_formatter:format(unexp_log,#{})), + ok. + +faulty_config(_Config) -> + {error, + function_clause, + {logger_formatter,format,[_,_],_}} = + ?TRY(logger_formatter:format(#{level=>info, + msg=>{"~p",[term]}, + meta=>#{time=>timestamp()}}, + unexp_config)), + ok. + +faulty_msg(_Config) -> + {error, + function_clause, + {logger_formatter,_,[_,_],_}} = + ?TRY(logger_formatter:format(#{level=>info, + msg=>term, + meta=>#{time=>timestamp()}}, + #{})), + ok. + +%%%----------------------------------------------------------------- +%%% Internal +format(Level,Msg,Meta,Config) -> + format(#{level=>Level,msg=>Msg,meta=>add_time(Meta)},Config). + +format(Log,Config) -> + lists:flatten(logger_formatter:format(Log,Config)). + +default_time_format(Timestamp) -> + default_time_format(Timestamp,false). + +default_time_format(Timestamp0,Utc) when is_integer(Timestamp0) -> + Timestamp=Timestamp0+erlang:time_offset(microsecond), + %% calendar:system_time_to_rfc3339(Time,[{unit,microsecond}]). + Micro = Timestamp rem 1000000, + Sec = Timestamp div 1000000, + UniversalTime = erlang:posixtime_to_universaltime(Sec), + {Date,Time} = + if Utc -> UniversalTime; + true -> erlang:universaltime_to_localtime(UniversalTime) + end, + default_time_format(Date,Time,Micro). + +default_time_format({Y,M,D},{H,Min,S},Micro) -> + lists:flatten( + io_lib:format("~4w-~2..0w-~2..0w ~2w:~2..0w:~2..0w.~6..0w", + [Y,M,D,H,Min,S,Micro])). + +integer(Str) -> + is_integer(list_to_integer(Str)). +integer(Str,Max) -> + integer(Str,0,Max). +integer(Str,Min,Max) -> + Int = list_to_integer(Str), + Int >= Min andalso Int = + try Fun() catch C:R:S -> {C,R,hd(S)} end. + +timestamp() -> + erlang:monotonic_time(microsecond). + +%% necessary? +add_time(#{time:=_}=Meta) -> + Meta; +add_time(Meta) -> + Meta#{time=>timestamp()}. diff --git a/lib/kernel/test/logger_legacy_SUITE.erl b/lib/kernel/test/logger_legacy_SUITE.erl new file mode 100644 index 0000000000..b59f5f7758 --- /dev/null +++ b/lib/kernel/test/logger_legacy_SUITE.erl @@ -0,0 +1,282 @@ +%% +%% %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_legacy_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("kernel/include/logger.hrl"). +-include_lib("kernel/src/logger_internal.hrl"). + +%%%----------------------------------------------------------------- +%%% This test suite test that log events from within OTP can be +%%% delivered to legacy error_logger event handlers on the same format +%%% as before 'logger' was introduced. +%%% +%%% Before changing the expected format of any of the log events in +%%% this suite, please make sure that the backwards incompatibility it +%%% introduces is ok. +%%% ----------------------------------------------------------------- + +-define(check(Expected), + receive Expected -> + [] = test_server:messages_get() + after 1000 -> + ct:fail({report_not_received, + {line,?LINE}, + {got,test_server:messages_get()}}) + end). +-define(check_no_flush(Expected), + receive Expected -> + ok + after 1000 -> + ct:fail({report_not_received, + {line,?LINE}, + {got,test_server:messages_get()}}) + end). + +suite() -> + [{timetrap,{seconds,30}}]. + +init_per_suite(Config) -> + logger:add_handler(error_logger,error_logger, + #{level=>info,filter_default=>stop}), + Config. + +end_per_suite(_Config) -> + logger:remove_handler(error_logger), + ok. + +init_per_group(std, Config) -> + ok = logger:set_handler_config( + error_logger,filters, + [{domain,{fun logger_filters:domain/2, + {log,prefix_of,[beam,erlang,otp]}}}]), + Config; +init_per_group(sasl, Config) -> + ok = logger:set_handler_config( + error_logger,filters, + [{domain,{fun logger_filters:domain/2, + {log,prefix_of,[beam,erlang,otp,sasl]}}}]), + + %% cth_log_redirect checks if sasl is started before displaying + %% any sasl reports - so just to see the real sasl reports in tc + %% log: + application:start(sasl), + Config; +init_per_group(_Group, Config) -> + Config. + +end_per_group(sasl, _Config) -> + application:stop(sasl), + ok; +end_per_group(_Group, _Config) -> + ok. + +init_per_testcase(_TestCase, Config) -> + error_logger:add_report_handler(?MODULE,{event_handler,self()}), + Config. + +end_per_testcase(Case, Config) -> + %% Using gen_event directly here, instead of + %% error_logger:delete_report_handler. This is to avoid + %% automatically stopping the error_logger process due to removing + %% the last handler. + gen_event:delete_handler(error_logger,?MODULE,[]), + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +groups() -> + [{std,[],[gen_server, + gen_event, + gen_fsm, + gen_statem]}, + {sasl,[],[sasl_reports, + supervisor_handle_info]}]. + +all() -> + [{group,std}, + {group,sasl}]. + +gen_server(_Config) -> + {ok,Pid} = gen_server:start(?MODULE,gen_server,[]), + Msg = fun() -> a=b end, + Pid ! Msg, + ?check({warning_msg,"** Undefined handle_info in ~p"++_,[?MODULE,Msg]}), + ok = gen_server:cast(Pid,Msg), + ?check({error,"** Generic server ~tp terminating"++_, + [Pid,{'$gen_cast',Msg},gen_server,{{badmatch,b},_}]}). + +gen_event(_Config) -> + {ok,Pid} = gen_event:start(), + ok = gen_event:add_handler(Pid,?MODULE,gen_event), + Msg = fun() -> a=b end, + Pid ! Msg, + ?check({warning_msg,"** Undefined handle_info in ~tp"++_,[?MODULE,Msg]}), + gen_event:notify(Pid,Msg), + ?check({error,"** gen_event handler ~p crashed."++_, + [?MODULE,Pid,Msg,gen_event,{{badmatch,b},_}]}). + +gen_fsm(_Config) -> + {ok,Pid} = gen_fsm:start(?MODULE,gen_fsm,[]), + Msg = fun() -> a=b end, + Pid ! Msg, + ?check({warning_msg,"** Undefined handle_info in ~p"++_,[?MODULE,Msg]}), + gen_fsm:send_all_state_event(Pid,Msg), + ?check({error,"** State machine ~tp terminating"++_, + [Pid,Msg,mystate,gen_fsm,{{badmatch,b},_}]}). + +gen_statem(_Config) -> + {ok,Pid} = gen_statem:start(?MODULE,gen_statem,[]), + Msg = fun() -> a=b end, + Pid ! Msg, + ?check({error,"** State machine ~tp terminating"++_, + [Pid,{info,Msg},{mystate,gen_statem},error,{badmatch,b}|_]}). + +sasl_reports(Config) -> + App = {application,?MODULE,[{description, ""}, + {vsn, "1.0"}, + {modules, [?MODULE]}, + {registered, []}, + {applications, []}, + {mod, {?MODULE, []}}]}, + AppStr = io_lib:format("~p.",[App]), + Dir = ?config(priv_dir,Config), + AppFile = filename:join(Dir,?MODULE_STRING++".app"), + ok = file:write_file(AppFile,AppStr), + true = code:add_patha(Dir), + ok = application:start(?MODULE), + SupName = sup_name(), + Pid = whereis(SupName), + [{ch,ChPid,_,_}] = supervisor:which_children(Pid), + Node = node(), + ?check_no_flush({info_report,progress,[{application,?MODULE}, + {started_at,Node}]}), + ?check({info_report,progress,[{supervisor,{local,SupName}}, + {started,[{pid,ChPid}|_]}]}), + ok = gen_server:cast(ChPid, fun() -> + spawn_link(fun() -> receive x->ok end end) + end), + Msg = fun() -> a=b end, + ok = gen_server:cast(ChPid,Msg), + ?check_no_flush({error,"** Generic server ~tp terminating"++_, + [ChPid,{'$gen_cast',Msg},gen_server,{{badmatch,b},_}]}), + ?check_no_flush({error_report,crash_report, + [[{initial_call,_}, + {pid,ChPid}, + {registered_name,[]}, + {error_info,{error,{badmatch,b},_}}, + {ancestors,_}, + {message_queue_len,_}, + {messages,_}, + {links,[Pid,Neighbour]}, + {dictionary,_}, + {trap_exit,_}, + {status,_}, + {heap_size,_}, + {stack_size,_}, + {reductions,_}], + [{neighbour,[{pid,Neighbour}, + {registered_name,_}, + {initial_call,_}, + {current_function,_}, + {ancestors,_}, + {message_queue_len,_}, + {links,[ChPid]}, + {trap_exit,_}, + {status,_}, + {heap_size,_}, + {stack_size,_}, + {reductions,_}, + {current_stacktrace,_}]}]]}), + ?check_no_flush({error_report,supervisor_report, + [{supervisor,{local,SupName}}, + {errorContext,child_terminated}, + {reason,{{badmatch,b},_}}, + {offender,[{pid,ChPid}|_]}]}), + ?check({info_report,progress,[{supervisor,{local,SupName}}, + {started,_}]}), + + ok = application:stop(?MODULE), + ?check({info_report,std_info,[{application,?MODULE}, + {exited,stopped}, + {type,temporary}]}). + +sasl_reports(cleanup,_Config) -> + application:stop(?MODULE). + +supervisor_handle_info(_Config) -> + {ok,Pid} = supervisor:start_link({local,sup_name()},?MODULE,supervisor), + ?check({info_report,progress,[{supervisor,_},{started,_}]}), + Pid ! msg, + ?check({error,"Supervisor received unexpected message: ~tp~n",[msg]}). + +supervisor_handle_info(cleanup,_Config) -> + Pid = whereis(sup_name()), + unlink(Pid), + exit(Pid,shutdown). + +%%%----------------------------------------------------------------- +%%% Callbacks for error_logger event handler, gen_server, gen_statem, +%%% gen_fsm, gen_event, supervisor and application. +start(_,_) -> + supervisor:start_link({local,sup_name()},?MODULE,supervisor). + +init(supervisor) -> + {ok,{#{},[#{id=>ch,start=>{gen_server,start_link,[?MODULE,gen_server,[]]}}]}}; +init(StateMachine) when StateMachine==gen_statem; StateMachine==gen_fsm -> + {ok,mystate,StateMachine}; +init(State) -> + {ok,State}. + +%% error_logger event handler +handle_event({Tag,_Gl,{_Pid,Type,Report}},{_,Pid}=State) -> + Pid ! {Tag,Type,Report}, + {ok,State}; +%% other gen_event +handle_event(Fun,State) when is_function(Fun) -> + Fun(), + {next_state,State}. + +%% gen_fsm +handle_event(Fun,State,Data) when is_function(Fun) -> + Fun(), + {next_state,State,Data}. + +%% gen_statem +handle_event(info,Fun,State,Data) when is_function(Fun) -> + Fun(), + {next_state,State,Data}. + +%% gen_server +handle_cast(Fun,State) when is_function(Fun) -> + Fun(), + {noreply,State}. + +%% gen_statem +callback_mode() -> + handle_event_function. + +%%%----------------------------------------------------------------- +%%% Internal +sup_name() -> + list_to_atom(?MODULE_STRING++"_sup"). diff --git a/lib/kernel/test/logger_simple_SUITE.erl b/lib/kernel/test/logger_simple_SUITE.erl new file mode 100644 index 0000000000..5d8d32492d --- /dev/null +++ b/lib/kernel/test/logger_simple_SUITE.erl @@ -0,0 +1,247 @@ +%% +%% %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_simple_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("kernel/include/logger.hrl"). +-include_lib("kernel/src/logger_internal.hrl"). + +-define(check_no_log,[] = test_server:messages_get()). +-define(check(Expected), + receive {log,Expected} -> + [] = test_server:messages_get() + after 1000 -> + ct:fail({report_not_received, + {line,?LINE}, + {expected,Expected}, + {got,test_server:messages_get()}}) + end). + +-define(str,"Log from "++atom_to_list(?FUNCTION_NAME)++ + ":"++integer_to_list(?LINE)). +-define(map_rep,#{function=>?FUNCTION_NAME, line=>?LINE}). +-define(keyval_rep,[{function,?FUNCTION_NAME}, {line,?LINE}]). + +suite() -> + [{timetrap,{seconds,30}}]. + +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}]], + [{env,Env},{logger,Hs}|Config]. + +end_per_suite(Config) -> + [application:set_env(App,Key,Val) || {App,Key,Val} <- ?config(env,Config)], + 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, + ok. + +groups() -> + []. + +all() -> + [start_stop, + get_buffer, + replace_file, + replace_disk_log + ]. + +start_stop(_Config) -> + undefined = whereis(logger_simple), + register(logger_simple,self()), + {error,_} = logger:add_handler(logger_simple, + logger_simple, + #{filter_default=>log}), + unregister(logger_simple), + ok = logger:add_handler(logger_simple,logger_simple,#{filter_default=>log}), + Pid = whereis(logger_simple), + true = is_pid(Pid), + ok = logger:remove_handler(logger_simple), + false = is_pid(whereis(logger_simple)), + ok. +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), + + %% 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), + + %% 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) || + L <- binary:split(Bin,<<"\n">>,[global,trim])], + ["=EMERGENCY REPORT===="++_, + M1, + "=ALERT REPORT===="++_, + M2, + "=ERROR REPORT===="++_, + _, + _, + "=INFO 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) || + L <- binary:split(Bin,<<"\n">>,[global,trim])], + ["=EMERGENCY REPORT===="++_, + M1, + "=ALERT REPORT===="++_, + M2, + "=ERROR REPORT===="++_, + _, + _, + "=INFO REPORT===="++_, + _, + _|_] = Lines, % the tail might be an info report about opening the disk log + 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 new file mode 100644 index 0000000000..e940e0a026 --- /dev/null +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -0,0 +1,1396 @@ +%% +%% %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_std_h_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("kernel/include/logger.hrl"). +-include_lib("kernel/src/logger_internal.hrl"). +-include_lib("kernel/src/logger_h_common.hrl"). +-include_lib("stdlib/include/ms_transform.hrl"). +-include_lib("kernel/include/file.hrl"). + +-define(check_no_log, [] = test_server:messages_get()). +-define(check(Expected), + receive + {log,Expected} -> + [] = test_server:messages_get() + after 5000 -> + ct:fail({report_not_received, + {line,?LINE}, + {expected,Expected}, + {got,test_server:messages_get()}}) + end). + +-define(msg,"Log from "++atom_to_list(?FUNCTION_NAME)++ + ":"++integer_to_list(?LINE)). +-define(bin(Msg), list_to_binary(Msg++"\n")). +-define(domain,#{domain=>[?MODULE]}). + +-define(FILESYNC_REP_INT, if is_atom(?FILESYNC_REPEAT_INTERVAL) -> 5500; + true -> ?FILESYNC_REPEAT_INTERVAL + 500 + end). + +suite() -> + [{timetrap,{seconds,30}}]. + +init_per_suite(Config) -> + timer:start(), % to avoid progress report + {ok,{?STANDARD_HANDLER,#{formatter:=OrigFormatter}}} = + logger:get_handler_config(?STANDARD_HANDLER), + [{formatter,OrigFormatter}|Config]. + +end_per_suite(Config) -> + {OrigMod,OrigConf} = proplists:get_value(formatter,Config), + logger:set_handler_config(?STANDARD_HANDLER,formatter,{OrigMod,OrigConf}), + ok. + +init_per_group(_Group, Config) -> + Config. + +end_per_group(_Group, _Config) -> + ok. + +init_per_testcase(TestHooksCase, Config) when + TestHooksCase == write_failure; + TestHooksCase == sync_failure -> + if ?TEST_HOOKS_TAB == undefined -> + {skip,"Define the TEST_HOOKS macro to run this test"}; + true -> + ct:print("********** ~w **********", [TestHooksCase]), + Config + end; +init_per_testcase(TestCase, Config) -> + ct:print("********** ~w **********", [TestCase]), + Config. + +end_per_testcase(Case, Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +groups() -> + []. + +all() -> + [add_remove_instance_tty, + add_remove_instance_standard_io, + add_remove_instance_standard_error, + add_remove_instance_file1, + add_remove_instance_file2, + default_formatter, + errors, + formatter_fail, + config_fail, + crash_std_h_to_file, + crash_std_h_to_disk_log, + bad_input, + info_and_reset, + reconfig, + file_opts, + filesync, + write_failure, + sync_failure, + op_switch_to_sync_file, + op_switch_to_sync_tty, + op_switch_to_drop_file, + op_switch_to_drop_tty, + op_switch_to_flush_file, + op_switch_to_flush_tty, + limit_burst_disabled, + limit_burst_enabled_one, + limit_burst_enabled_period, + kill_disabled, + qlen_kill_new, + qlen_kill_std, + mem_kill_new, + mem_kill_std, + restart_after, + handler_requests_under_load + ]. + +add_remove_instance_tty(_Config) -> + {error,{handler_not_added,{invalid_config,logger_std_h,{type,tty}}}} = + logger:add_handler(?MODULE,logger_std_h, + #{logger_std_h => #{type => tty}, + filter_default=>log, + formatter=>{?MODULE,self()}}), + ok. + +add_remove_instance_standard_io(_Config) -> + add_remove_instance_nofile(standard_io). +add_remove_instance_standard_io(cleanup,_Config) -> + logger_std_h_remove(). + +add_remove_instance_standard_error(_Config) -> + add_remove_instance_nofile(standard_error). +add_remove_instance_standard_error(cleanup,_Config) -> + logger_std_h_remove(). + +add_remove_instance_file1(Config) -> + Dir = ?config(priv_dir,Config), + Log = filename:join(Dir,"stdlog1.txt"), + Type = {file,Log}, + add_remove_instance_file(Log, Type). +add_remove_instance_file1(cleanup,_Config) -> + logger_std_h_remove(). + +add_remove_instance_file2(Config) -> + Dir = ?config(priv_dir,Config), + Log = filename:join(Dir,"stdlog2.txt"), + Type = {file,Log,[raw,append]}, + add_remove_instance_file(Log, Type). +add_remove_instance_file2(cleanup,_Config) -> + logger_std_h_remove(). + +add_remove_instance_file(Log, Type) -> + ok = logger:add_handler(?MODULE, + logger_std_h, + #{logger_std_h => #{type => Type}, + filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}), + Pid = whereis(?MODULE), + true = is_pid(Pid), + logger:info(M1=?msg,?domain), + ?check(M1), + B1 = ?bin(M1), + try_read_file(Log, {ok,B1}, ?FILESYNC_REP_INT), + ok = logger:remove_handler(?MODULE), + timer:sleep(500), + undefined = whereis(?MODULE), + logger:info(?msg,?domain), + ?check_no_log, + try_read_file(Log, {ok,B1}, ?FILESYNC_REP_INT), + ok. + +default_formatter(_Config) -> + ok = logger:set_handler_config(?STANDARD_HANDLER,formatter, + {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}), + ct:capture_start(), + logger:info(M1=?msg), + timer:sleep(100), + ct:capture_stop(), + [Msg] = ct:capture_get(), + match = re:run(Msg,"=INFO REPORT====.*\n"++M1,[{capture,none}]), + ok. + +errors(Config) -> + Dir = ?config(priv_dir,Config), + Log = filename:join(Dir,?FUNCTION_NAME), + + ok = logger:add_handler(?MODULE,logger_std_h,#{}), + {error,{already_exist,?MODULE}} = + logger:add_handler(?MODULE,logger_std_h,#{}), + + {error,{not_found,no_such_name}} = logger:remove_handler(no_such_name), + + ok = logger:remove_handler(?MODULE), + {error,{not_found,?MODULE}} = logger:remove_handler(?MODULE), + + {error, + {handler_not_added, + {invalid_config,logger_std_h,{type,faulty_type}}}} = + logger:add_handler(?MODULE,logger_std_h, + #{logger_std_h => #{type => faulty_type}}), + + NoDir = lists:concat(["/",?MODULE,"_dir"]), + {error, + {handler_not_added,{{open_failed,NoDir,eacces},_}}} = + logger:add_handler(myh2,logger_std_h, + #{logger_std_h=>#{type=>{file,NoDir}}}), + + {error, + {handler_not_added,{{open_failed,Log,_},_}}} = + logger:add_handler(myh3,logger_std_h, + #{logger_std_h=>#{type=>{file,Log,[bad_file_opt]}}}), + + ok = logger:info(?msg). + +errors(cleanup,_Config) -> + logger:remove_handler(?MODULE). + +formatter_fail(Config) -> + Dir = ?config(priv_dir,Config), + Log = filename:join(Dir,?FUNCTION_NAME), + + %% no formatter + ok = logger:add_handler(?MODULE, + logger_std_h, + #{logger_std_h => #{type => {file,Log}}, + filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE])}), + Pid = whereis(?MODULE), + true = is_pid(Pid), + {ok,#{handlers:=H}} = logger:get_logger_config(), + true = lists:member(?MODULE,H), + + %% Formatter is added automatically + {ok,{_,#{formatter:={logger_formatter,_}}}} = + logger:get_handler_config(?MODULE), + logger:info(M1=?msg,?domain), + Got1 = try_match_file(Log,"=INFO REPORT====.*\n"++M1,5000), + + ok = logger:set_handler_config(?MODULE,formatter,{nonexistingmodule,#{}}), + logger:info(M2=?msg,?domain), + Got2 = try_match_file(Log, + Got1++"=INFO REPORT====.*\nFORMATTER CRASH: .*"++M2, + 5000), + + ok = logger:set_handler_config(?MODULE,formatter,{?MODULE,crash}), + logger:info(M3=?msg,?domain), + Got3 = try_match_file(Log, + Got2++"=INFO REPORT====.*\nFORMATTER CRASH: .*"++M3, + 5000), + + ok = logger:set_handler_config(?MODULE,formatter,{?MODULE,bad_return}), + logger:info(?msg,?domain), + try_match_file(Log, + Got3++"FORMATTER ERROR: bad_return_value", + 5000), + + %% Check that handler is still alive and was never dead + Pid = whereis(?MODULE), + {ok,#{handlers:=H}} = logger:get_logger_config(), + + ok. + +formatter_fail(cleanup,_Config) -> + logger:remove_handler(?MODULE). + +config_fail(_Config) -> + {error,{handler_not_added,{invalid_config,logger_std_h,{bad,bad}}}} = + logger:add_handler(?MODULE,logger_std_h, + #{logger_std_h => #{bad => bad}, + filter_default=>log, + formatter=>{?MODULE,self()}}), + {error,{handler_not_added,{invalid_config,logger_std_h, + {restart_type,bad}}}} = + logger:add_handler(?MODULE,logger_std_h, + #{logger_std_h => #{restart_type => bad}, + filter_default=>log, + formatter=>{?MODULE,self()}}), + {error,{handler_not_added,{invalid_levels,{42,42,_}}}} = + logger:add_handler(?MODULE,logger_std_h, + #{logger_std_h => #{toggle_sync_qlen=>42, + drop_new_reqs_qlen=>42}}), + + ok = logger:add_handler(?MODULE,logger_std_h, + #{filter_default=>log, + formatter=>{?MODULE,self()}}), + {error,{illegal_config_change,_,_}} = + logger:set_handler_config(?MODULE,logger_std_h, + #{type=>{file,"file"}}), + {error,{illegal_config_change,_,_}} = + logger:set_handler_config(?MODULE,id,bad), + {error,{invalid_levels,_}} = + logger:set_handler_config(?MODULE,logger_std_h, + #{toggle_sync_qlen=>100, + flush_reqs_qlen=>99}), + {error,{invalid_config,logger_std_h,{filesync_rep_int,2000}}} = + logger:set_handler_config(?MODULE, logger_std_h, + #{filesync_rep_int => 2000}), + ok. + +config_fail(cleanup,_Config) -> + logger:remove_handler(?MODULE). + +crash_std_h_to_file(Config) -> + crash_std_h(Config,?FUNCTION_NAME,logger_dest,file). +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). +crash_std_h_to_disk_log(cleanup,_Config) -> + crash_std_h(cleanup). + +crash_std_h(Config,Func,Var,Type) -> + Dir = ?config(priv_dir,Config), + File = lists:concat([?MODULE,"_",Func,".log"]), + Log = filename:join(Dir,File), + 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]), + 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}]), + Pid = rpc:call(Node,erlang,whereis,[?STANDARD_HANDLER]), + ok = rpc:call(Node,logger,set_handler_config,[?STANDARD_HANDLER,formatter, + {?MODULE,self()}]), + ok = log_on_remote_node(Node,"dummy1"), + ?check("dummy1"), + {ok,Bin1} = sync_and_read(Node,Type,Log), + <<"dummy1\n">> = binary:part(Bin1,{byte_size(Bin1),-7}), + + %% Kill the logger_std_h process + exit(Pid, kill), + + %% Wait a bit, then check that it is gone + timer:sleep(2000), + undefined = rpc:call(Node,erlang,whereis,[?STANDARD_HANDLER]), + + %% Check that file is not empty + {ok,Bin2} = sync_and_read(Node,Type,Log), + <<"dummy1\n">> = binary:part(Bin2,{byte_size(Bin2),-7}), + ok. + +%% Can not use rpc:call here, since the code would execute on a +%% process with group_leader on this (the calling) node, and thus +%% logger would send the log event to the logger process here instead +%% of logging it itself. +log_on_remote_node(Node,Msg) -> + _ = spawn_link(Node, + fun() -> erlang:group_leader(whereis(user),self()), + logger:info(Msg) + end), + ok. + + +crash_std_h(cleanup) -> + Nodes = nodes(), + [test_server:stop_node(Node) || Node <- Nodes]. + +sync_and_read(Node,disk_log,Log) -> + rpc:call(Node,logger_disk_log_h,disk_log_sync,[?STANDARD_HANDLER]), + case file:read_file(Log ++ ".1") of + {ok,<<>>} -> + timer:sleep(5000), + file:read_file(Log ++ ".1"); + Ok -> + Ok + end; +sync_and_read(Node,file,Log) -> + rpc:call(Node,logger_std_h,filesync,[?STANDARD_HANDLER]), + case file:read_file(Log) of + {ok,<<>>} -> + timer:sleep(5000), + file:read_file(Log); + Ok -> + Ok + end. + +bad_input(_Config) -> + {error,{badarg,{filesync,["BadType"]}}} = logger_std_h:filesync("BadType"), + {error,{badarg,{info,["BadType"]}}} = logger_std_h:info("BadType"), + {error,{badarg,{reset,["BadType"]}}} = logger_std_h:reset("BadType"). + + +info_and_reset(_Config) -> + #{id := ?STANDARD_HANDLER} = logger_std_h:info(?STANDARD_HANDLER), + ok = logger_std_h:reset(?STANDARD_HANDLER). + +reconfig(Config) -> + Dir = ?config(priv_dir,Config), + ok = logger:add_handler(?MODULE, + logger_std_h, + #{logger_std_h => #{type => standard_io}, + filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}), + #{id := ?MODULE, + type := standard_io, + file_ctrl_pid := FileCtrlPid, + toggle_sync_qlen := ?TOGGLE_SYNC_QLEN, + drop_new_reqs_qlen := ?DROP_NEW_REQS_QLEN, + flush_reqs_qlen := ?FLUSH_REQS_QLEN, + enable_burst_limit := ?ENABLE_BURST_LIMIT, + burst_limit_size := ?BURST_LIMIT_SIZE, + burst_window_time := ?BURST_WINDOW_TIME, + enable_kill_overloaded := ?ENABLE_KILL_OVERLOADED, + handler_overloaded_qlen := ?HANDLER_OVERLOADED_QLEN, + handler_overloaded_mem := ?HANDLER_OVERLOADED_MEM, + handler_restart_after := ?HANDLER_RESTART_AFTER, + filesync_repeat_interval := ?FILESYNC_REPEAT_INTERVAL} = + logger_std_h:info(?MODULE), + + ok = logger:set_handler_config(?MODULE, logger_std_h, + #{toggle_sync_qlen => 1, + drop_new_reqs_qlen => 2, + flush_reqs_qlen => 3, + enable_burst_limit => false, + burst_limit_size => 10, + burst_window_time => 10, + enable_kill_overloaded => true, + handler_overloaded_qlen => 100000, + handler_overloaded_mem => 10000000, + handler_restart_after => never, + filesync_repeat_interval => no_repeat}), + #{id := ?MODULE, + type := standard_io, + file_ctrl_pid := FileCtrlPid, + toggle_sync_qlen := 1, + drop_new_reqs_qlen := 2, + flush_reqs_qlen := 3, + enable_burst_limit := false, + burst_limit_size := 10, + burst_window_time := 10, + enable_kill_overloaded := true, + handler_overloaded_qlen := 100000, + handler_overloaded_mem := 10000000, + handler_restart_after := never, + filesync_repeat_interval := no_repeat} = logger_std_h:info(?MODULE), + ok. + +reconfig(cleanup, _Config) -> + logger:remove_handler(?MODULE). + + +file_opts(Config) -> + Dir = ?config(priv_dir,Config), + Log = filename:join(Dir, lists:concat([?FUNCTION_NAME,".log"])), + BadFileOpts = [raw], + BadType = {file,Log,BadFileOpts}, + {error,{handler_not_added,{{open_failed,Log,enoent},_}}} = + logger:add_handler(?MODULE, logger_std_h, + #{logger_std_h => #{type => BadType}}), + + OkFileOpts = [raw,append], + OkType = {file,Log,OkFileOpts}, + ok = logger:add_handler(?MODULE, + logger_std_h, + #{logger_std_h => #{type => OkType}, + filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}), + + #{type := OkType} = logger_std_h:info(?MODULE), + logger:info(M1=?msg,?domain), + ?check(M1), + B1 = ?bin(M1), + try_read_file(Log, {ok,B1}, ?FILESYNC_REP_INT), + ok. +file_opts(cleanup, _Config) -> + logger:remove_handler(?MODULE). + + +filesync(Config) -> + Dir = ?config(priv_dir,Config), + Log = filename:join(Dir, lists:concat([?FUNCTION_NAME,".log"])), + Type = {file,Log}, + ok = logger:add_handler(?MODULE, + logger_std_h, + #{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, []), + + 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}], + + 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 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)), + logger:info("third", ?domain), + 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, []), + + %% switch repeated filesync on and verify that the looping works + SyncInt = 1000, + WaitT = 4500, + 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))]), + ok. +filesync(cleanup, _Config) -> + logger:remove_handler(?MODULE). + +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), + 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]), + rpc:call(Node, ?MODULE, set_result, [file_write,ok]), + + ok = log_on_remote_node(Node, "Logged1"), + rpc:call(Node, logger_std_h, filesync, [?STANDARD_HANDLER]), + ?check_no_log, + try_read_file(Log, {ok,<<"Logged1\n">>}, ?FILESYNC_REP_INT), + + rpc:call(Node, ?MODULE, set_result, [file_write,{error,terminated}]), + ok = log_on_remote_node(Node, "Cause simple error printout"), + + ?check({error,{?STANDARD_HANDLER,write,Log,{error,terminated}}}), + + ok = log_on_remote_node(Node, "No second error printout"), + ?check_no_log, + + rpc:call(Node, ?MODULE, set_result, [file_write,{error,eacces}]), + ok = log_on_remote_node(Node, "Cause simple error printout"), + ?check({error,{?STANDARD_HANDLER,write,Log,{error,eacces}}}), + + rpc:call(Node, ?MODULE, set_result, [file_write,ok]), + ok = log_on_remote_node(Node, "Logged2"), + rpc:call(Node, logger_std_h, filesync, [?STANDARD_HANDLER]), + ?check_no_log, + try_read_file(Log, {ok,<<"Logged1\nLogged2\n">>}, ?FILESYNC_REP_INT), + ok. +write_failure(cleanup, _Config) -> + Nodes = nodes(), + [test_server:stop_node(Node) || Node <- Nodes]. + +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), + 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]), + rpc:call(Node, ?MODULE, set_result, [file_datasync,ok]), + + SyncInt = 500, + ok = rpc:call(Node, logger, set_handler_config, + [?STANDARD_HANDLER, logger_std_h, + #{filesync_repeat_interval => SyncInt}]), + Info = rpc:call(Node, logger_std_h, info, [?STANDARD_HANDLER]), + SyncInt = maps:get(filesync_repeat_interval, Info), + + ok = log_on_remote_node(Node, "Logged1"), + ?check_no_log, + + rpc:call(Node, ?MODULE, set_result, [file_datasync,{error,terminated}]), + ok = log_on_remote_node(Node, "Cause simple error printout"), + + ?check({error,{?STANDARD_HANDLER,filesync,Log,{error,terminated}}}), + + ok = log_on_remote_node(Node, "No second error printout"), + ?check_no_log, + + rpc:call(Node, ?MODULE, set_result, [file_datasync,{error,eacces}]), + ok = log_on_remote_node(Node, "Cause simple error printout"), + ?check({error,{?STANDARD_HANDLER,filesync,Log,{error,eacces}}}), + + rpc:call(Node, ?MODULE, set_result, [file_datasync,ok]), + ok = log_on_remote_node(Node, "Logged2"), + ?check_no_log, + ok. +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), + ok = rpc:call(Node,logger,set_handler_config,[?STANDARD_HANDLER,formatter, + {?MODULE,nl}]), + Node. + +%% 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_defaults() -> + ?set_defaults(). + +%% internal log function that sends the term to the test case process +internal_log(Type, Term) -> + [{tester,Tester}] = ets:lookup(?TEST_HOOKS_TAB, tester), + Tester ! {log,{Type,Term}}, + logger:internal_log(Type, Term), + ok. + + +%%%----------------------------------------------------------------- +%%% Overload protection tests + +op_switch_to_sync_file(Config) -> + {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + NewHConfig = + HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 3, + drop_new_reqs_qlen => 501, + flush_reqs_qlen => 2000, + enable_burst_limit => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + %% TRecvPid = start_op_trace(), + NumOfReqs = 500, + send_burst({n,NumOfReqs}, seq, {chars,79}, info), + NumOfReqs = count_lines(Log), + %% true = analyse_trace(TRecvPid, + %% fun(Events) -> find_mode(async,Events) end), + %% true = analyse_trace(TRecvPid, + %% fun(Events) -> find_mode(sync,Events) end), + %% true = analyse_trace(TRecvPid, + %% fun(Events) -> find_switch(async,sync,Events) end), + %% false = analyse_trace(TRecvPid, + %% fun(Events) -> find_mode(drop,Events) end), + %% false = analyse_trace(TRecvPid, + %% fun(Events) -> find_mode(flush,Events) end), + ok = file:delete(Log), + %% stop_op_trace(TRecvPid), + ok. +op_switch_to_sync_file(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +op_switch_to_sync_tty(Config) -> + {HConfig,StdHConfig} = start_handler(?MODULE, standard_io, Config), + NewHConfig = + HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 3, + drop_new_reqs_qlen => 501, + flush_reqs_qlen => 2000, + enable_burst_limit => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 500, + send_burst({n,NumOfReqs}, seq, {chars,79}, info), + ok. +op_switch_to_sync_tty(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +op_switch_to_drop_file(Config) -> + {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + + NewHConfig = + HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 2, + drop_new_reqs_qlen => 3, + flush_reqs_qlen => 600, + enable_burst_limit => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + %% TRecvPid = start_op_trace(), + NumOfReqs = 500, + send_burst({n,NumOfReqs}, seq, {chars,79}, info), + Logged = count_lines(Log), + ct:pal("Number of messages dropped = ~w (~w)", + [NumOfReqs-Logged,NumOfReqs]), + true = (Logged < NumOfReqs), + %% true = analyse_trace(TRecvPid, + %% fun(Events) -> find_mode(async,Events) end), + %% true = analyse_trace(TRecvPid, + %% fun(Events) -> find_mode(drop,Events) end), + %% false = analyse_trace(TRecvPid, + %% fun(Events) -> find_mode(flush,Events) end), + %% true = analyse_trace(TRecvPid, + %% fun(Events) -> find_switch(async,drop,Events) + %% orelse find_switch(sync,drop,Events) + %% end), + ok = file:delete(Log), + %% stop_op_trace(TRecvPid), + ok. +op_switch_to_drop_file(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +op_switch_to_drop_tty(Config) -> + {HConfig,StdHConfig} = start_handler(?MODULE, standard_io, Config), + NewHConfig = + HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 2, + drop_new_reqs_qlen => 3, + flush_reqs_qlen => 600, + enable_burst_limit => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 500, + send_burst({n,NumOfReqs}, seq, {chars,79}, info), + ok. +op_switch_to_drop_tty(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +op_switch_to_flush_file() -> + [{timetrap,{seconds,60}}]. +op_switch_to_flush_file(Config) -> + {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + + %% it's important that both async and sync requests have been queued + %% when the flush happens (verify with coverage of flush_log_requests/2) + + NewHConfig = + HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 2, + drop_new_reqs_qlen => 99, + flush_reqs_qlen => 100, + enable_burst_limit => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 10000, + Procs = 100, + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), + Logged = count_lines(Log), + ct:pal("Number of messages flushed/dropped = ~w (~w)", + [(NumOfReqs*Procs)-Logged,NumOfReqs*Procs]), + true = (Logged < (NumOfReqs*Procs)), + + %%! --- Thu Apr 12 13:46:00 2018 --- peppe was here! + %%! TODO: Verify that handler has switched to flush mode + + ok = file:delete(Log), + ok. +op_switch_to_flush_file(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +op_switch_to_flush_tty(Config) -> + {HConfig,StdHConfig} = start_handler(?MODULE, standard_io, Config), + + %% it's important that both async and sync requests have been queued + %% when the flush happens (verify with coverage of flush_log_requests/2) + + NewHConfig = + HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 2, + drop_new_reqs_qlen => 99, + flush_reqs_qlen => 100, + enable_burst_limit => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 10000, + Procs = 10, + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), + ok. +op_switch_to_flush_tty(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +limit_burst_disabled(Config) -> + {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + NewHConfig = + HConfig#{logger_std_h => StdHConfig#{enable_burst_limit => false, + burst_limit_size => 10, + burst_window_time => 2000, + drop_new_reqs_qlen => 200, + flush_reqs_qlen => 300}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 100, + send_burst({n,NumOfReqs}, seq, {chars,79}, info), + Logged = count_lines(Log), + ct:pal("Number of messages logged = ~w", [Logged]), + ok = file:delete(Log), + NumOfReqs = Logged. +limit_burst_disabled(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +limit_burst_enabled_one(Config) -> + {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + ReqLimit = 10, + NewHConfig = + HConfig#{logger_std_h => StdHConfig#{enable_burst_limit => true, + burst_limit_size => ReqLimit, + burst_window_time => 2000, + drop_new_reqs_qlen => 200, + flush_reqs_qlen => 300}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 100, + send_burst({n,NumOfReqs}, seq, {chars,79}, info), + Logged = count_lines(Log), + ct:pal("Number of messages logged = ~w", [Logged]), + ok = file:delete(Log), + ReqLimit = Logged. +limit_burst_enabled_one(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +limit_burst_enabled_period(Config) -> + {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + ReqLimit = 10, + BurstTWin = 1000, + NewHConfig = + HConfig#{logger_std_h => StdHConfig#{enable_burst_limit => true, + burst_limit_size => ReqLimit, + burst_window_time => BurstTWin, + drop_new_reqs_qlen => 20000, + flush_reqs_qlen => 20001}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + + Windows = 3, + Sent = send_burst({t,BurstTWin*Windows}, seq, {chars,79}, info), + Logged = count_lines(Log), + ct:pal("Number of messages sent = ~w~nNumber of messages logged = ~w", + [Sent,Logged]), + ok = file:delete(Log), + true = (Logged > (ReqLimit*Windows)) andalso + (Logged < (ReqLimit*(Windows+2))). +limit_burst_enabled_period(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +kill_disabled(Config) -> + {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + NewHConfig = + HConfig#{logger_std_h=>StdHConfig#{enable_kill_overloaded=>false, + handler_overloaded_qlen=>10, + handler_overloaded_mem=>100}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 100, + send_burst({n,NumOfReqs}, seq, {chars,79}, info), + Logged = count_lines(Log), + ct:pal("Number of messages logged = ~w", [Logged]), + ok = file:delete(Log), + true = is_pid(whereis(?MODULE)), + ok. +kill_disabled(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +qlen_kill_new(Config) -> + {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + Pid0 = whereis(?MODULE), + {_,Mem0} = process_info(Pid0, memory), + RestartAfter = 2000, + NewHConfig = + HConfig#{logger_std_h=>StdHConfig#{enable_kill_overloaded=>true, + handler_overloaded_qlen=>10, + handler_overloaded_mem=>Mem0+50000, + handler_restart_after=>RestartAfter}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + MRef = erlang:monitor(process, Pid0), + NumOfReqs = 100, + Procs = 2, + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), + %% send_burst({n,NumOfReqs}, seq, {chars,79}, info), + receive + {'DOWN', MRef, _, _, Info} -> + case Info of + {shutdown,{overloaded,?MODULE,QLen,Mem}} -> + ct:pal("Terminated with qlen = ~w, mem = ~w", [QLen,Mem]); + killed -> + ct:pal("Slow shutdown, handler process was killed!", []) + end, + timer:sleep(RestartAfter + 1000), + true = is_pid(whereis(?MODULE)), + ok + after + 5000 -> + Info = logger_std_h:info(?MODULE), + ct:pal("Handler state = ~p", [Info]), + ct:fail("Handler not dead! It should not have survived this!") + end. +qlen_kill_new(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +%% choke the standard handler on remote node to verify the termination +%% works as expected +qlen_kill_std(Config) -> + %%! HERE + %% 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), + %% ok = rpc:call(Node, logger, set_handler_config, + %% [?STANDARD_HANDLER, logger_std_h, + %% #{enable_kill_overloaded=>true, + %% handler_overloaded_qlen=>10, + %% handler_overloaded_mem=>100000}]), + {skip,"Not done yet"}. + +mem_kill_new(Config) -> + {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + Pid0 = whereis(?MODULE), + {_,Mem0} = process_info(Pid0, memory), + RestartAfter = 2000, + NewHConfig = + HConfig#{logger_std_h=>StdHConfig#{enable_kill_overloaded=>true, + handler_overloaded_qlen=>50000, + handler_overloaded_mem=>Mem0+500, + handler_restart_after=>RestartAfter}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + MRef = erlang:monitor(process, Pid0), + NumOfReqs = 100, + Procs = 2, + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), + %% send_burst({n,NumOfReqs}, seq, {chars,79}, info), + receive + {'DOWN', MRef, _, _, Info} -> + case Info of + {shutdown,{overloaded,?MODULE,QLen,Mem}} -> + ct:pal("Terminated with qlen = ~w, mem = ~w", [QLen,Mem]); + killed -> + ct:pal("Slow shutdown, handler process was killed!", []) + end, + timer:sleep(RestartAfter * 2), + true = is_pid(whereis(?MODULE)), + ok + after + 5000 -> + Info = logger_std_h:info(?MODULE), + ct:pal("Handler state = ~p", [Info]), + ct:fail("Handler not dead! It should not have survived this!") + end. +mem_kill_new(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +%% choke the standard handler on remote node to verify the termination +%% works as expected +mem_kill_std(Config) -> + {skip,"Not done yet"}. + +restart_after(Config) -> + {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + NewHConfig1 = + HConfig#{logger_std_h=>StdHConfig#{enable_kill_overloaded=>true, + handler_overloaded_qlen=>10, + handler_restart_after=>never}}, + ok = logger:set_handler_config(?MODULE, NewHConfig1), + MRef1 = erlang:monitor(process, whereis(?MODULE)), + %% kill handler + send_burst({n,100}, {spawn,2,0}, {chars,79}, info), + receive + {'DOWN', MRef1, _, _, _Info1} -> + timer:sleep(?HANDLER_RESTART_AFTER + 1000), + undefined = whereis(?MODULE), + ok + after + 5000 -> + ct:fail("Handler not dead! It should not have survived this!") + end, + + {Log,_,_} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + RestartAfter = 2000, + NewHConfig2 = + HConfig#{logger_std_h=>StdHConfig#{enable_kill_overloaded=>true, + handler_overloaded_qlen=>10, + handler_restart_after=>RestartAfter}}, + ok = logger:set_handler_config(?MODULE, NewHConfig2), + Pid0 = whereis(?MODULE), + MRef2 = erlang:monitor(process, Pid0), + %% kill handler + send_burst({n,100}, {spawn,2,0}, {chars,79}, info), + receive + {'DOWN', MRef2, _, _, _Info2} -> + timer:sleep(RestartAfter + 1000), + Pid1 = whereis(?MODULE), + true = is_pid(Pid1), + false = (Pid1 == Pid0), + ok + after + 5000 -> + ct:fail("Handler not dead! It should not have survived this!") + end, + ok. +restart_after(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +%% send handler requests (filesync, info, reset, change_config) +%% during high load to verify that sync, dropping and flushing is +%% handled correctly. +handler_requests_under_load() -> + [{timetrap,{seconds,60}}]. +handler_requests_under_load(Config) -> + {Log,HConfig,StdHConfig} = + start_handler(?MODULE, ?FUNCTION_NAME, Config), + NewHConfig = + HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 2, + drop_new_reqs_qlen => 1000, + flush_reqs_qlen => 2000, + enable_burst_limit => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + Pid = spawn_link(fun() -> send_requests(?MODULE, 1, [{filesync,[]}, + {info,[]}, + {reset,[]}, + {change_config,[]}]) + end), + Sent = send_burst({t,10000}, seq, {chars,79}, info), + Pid ! {self(),finish}, + ReqResult = receive {Pid,Result} -> Result end, + Logged = count_lines(Log), + ct:pal("Number of messages sent = ~w~nNumber of messages logged = ~w", + [Sent,Logged]), + FindError = fun(Res) -> + [E || E <- Res, + is_tuple(E) andalso (element(1,E) == error)] + end, + Errors = [{Req,FindError(Res)} || {Req,Res} <- ReqResult], + 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) -> + ok = stop_handler(?MODULE). + +send_requests(HName, TO, Reqs = [{Req,Res}|Rs]) -> + receive + {From,finish} -> + From ! {self(),Reqs} + after + TO -> + Result = + case Req of + change_config -> + logger:set_handler_config(HName, logger_std_h, + #{enable_kill_overloaded => + false}); + Func -> + logger_std_h:Func(HName) + end, + send_requests(HName, TO, Rs ++ [{Req,[Result|Res]}]) + end. + + +%%%----------------------------------------------------------------- +%%% +start_handler(Name, TTY, Config) when TTY == standard_io; + TTY == standard_error-> + ok = logger:add_handler(Name, + logger_std_h, + #{logger_std_h => #{type => TTY}, + filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([Name]), + formatter=>{?MODULE,op}}), + {ok,{_,HConfig = #{logger_std_h := StdHConfig}}} = + logger:get_handler_config(Name), + {HConfig,StdHConfig}; + +start_handler(Name, FuncName, Config) -> + Dir = ?config(priv_dir,Config), + Log = filename:join(Dir, lists:concat([FuncName,".log"])), + ct:pal("Logging to ~tp", [Log]), + Type = {file,Log}, + ok = logger:add_handler(Name, + logger_std_h, + #{logger_std_h => #{type => Type}, + filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([Name]), + formatter=>{?MODULE,op}}), + {ok,{_,HConfig = #{logger_std_h := StdHConfig}}} = + logger:get_handler_config(Name), + {Log,HConfig,StdHConfig}. + +stop_handler(Name) -> + ok = logger:remove_handler(Name), + ct:pal("Handler ~p stopped!", [Name]). + +count_lines(File) -> + wait_until_written(File, -1), + count_lines1(File). + +wait_until_written(File, Sz) -> + timer:sleep(2000), + case file:read_file_info(File) of + {ok,#file_info{size = Sz}} -> + timer:sleep(1000), + case file:read_file_info(File) of + {ok,#file_info{size = Sz1}} -> + ok; + {ok,#file_info{size = Sz2}} -> + wait_until_written(File, Sz2) + end; + {ok,#file_info{size = Sz1}} -> + wait_until_written(File, Sz1) + end. + +count_lines1(File) -> + Counter = fun Cnt(Dev,LC) -> + case file:read_line(Dev) of + eof -> LC; + _ -> Cnt(Dev,LC+1) + end + end, + {_,Dev} = file:open(File, [read]), + Lines = Counter(Dev, 0), + file:close(Dev), + Lines. + +send_burst(NorT, Type, {chars,Sz}, Class) -> + Text = [34 + rand:uniform(126-34) || _ <- lists:seq(1,Sz)], + case NorT of + {n,N} -> + %% process_flag(priority, high), + send_n_burst(N, Type, Text, Class), + %% process_flag(priority, normal), + N; + {t,T} -> + ct:pal("Sending messages sequentially for ~w ms", [T]), + T0 = erlang:monotonic_time(millisecond), + send_t_burst(T0, T, Text, Class, 0) + end. + +send_n_burst(0, _, _Text, _Class) -> + ok; +send_n_burst(N, seq, Text, Class) -> + ok = logger:Class(Text, ?domain), + send_n_burst(N-1, seq, Text, Class); +send_n_burst(N, {spawn,Ps,TO}, Text, Class) -> + ct:pal("~w processes each sending ~w messages", [Ps,N]), + PerProc = fun() -> + send_n_burst(N, seq, Text, Class) + end, + MRefs = [begin if TO == 0 -> ok; true -> timer:sleep(TO) end, + monitor(process,spawn_link(PerProc)) end || + _ <- lists:seq(1,Ps)], + lists:foreach(fun(MRef) -> + receive + {'DOWN', MRef, _, _, _} -> + ok + end + end, MRefs), + ct:pal("Message burst sent", []), + ok. + +send_t_burst(T0, T, Text, Class, N) -> + T1 = erlang:monotonic_time(millisecond), + if (T1-T0) > T -> + N; + true -> + ok = logger:Class(Text, ?domain), + send_t_burst(T0, T, Text, Class, N+1) + end. + +%%%----------------------------------------------------------------- +%%% Formatter callback +%%% Using this to send the formatted string back to the test case +%%% process - so it can check for logged events. +format(_,bad_return) -> + bad_return; +format(_,crash) -> + erlang:error(formatter_crashed); +format(#{msg:={string,String0}},no_nl) -> + String = unicode:characters_to_list(String0), + String; +format(#{msg:={string,String0}},nl) -> + String = unicode:characters_to_list(String0), + String++"\n"; +format(#{msg:={string,String0}},op) -> + String = unicode:characters_to_list(String0), + String++"\n"; +format(#{msg:={report,#{label:={supervisor,progress}}}},op) -> + ""; +format(#{msg:={report,#{label:={gen_server,terminate}}}},op) -> + ""; +format(#{msg:={report,#{label:={proc_lib,crash}}}},op) -> + ""; +format(#{msg:={F,A}},Pid) when is_list(F), is_list(A) -> + String = lists:flatten(io_lib:format(F,A)), + Pid ! {log,String}, + String++"\n"; +format(#{msg:={string,String0}},Pid) -> + String = unicode:characters_to_list(String0), + Pid ! {log,String}, + String++"\n". + +add_remove_instance_nofile(Type) -> + ok = logger:add_handler(?MODULE,logger_std_h, + #{logger_std_h => #{type => Type}, + filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}), + Pid = whereis(?MODULE), + true = is_pid(Pid), + group_leader(group_leader(),Pid), % to get printouts in test log + logger:info(M1=?msg,?domain), + ?check(M1), + %% check that filesync doesn't do damage even if not relevant + ok = logger_std_h:filesync(?MODULE), + ok = logger:remove_handler(?MODULE), + timer:sleep(500), + undefined = whereis(?MODULE), + logger:info(?msg,?domain), + ?check_no_log, + ok. + +logger_std_h_remove() -> + logger:remove_handler(?MODULE). +logger_std_h_remove(Id) -> + logger:remove_handler(Id). + +try_read_file(FileName, Expected, Time) when Time > 0 -> + case file:read_file(FileName) of + Expected -> + ok; + Error = {error,_Reason} -> + ct:pal("Can't read ~tp: ~tp", [FileName,Error]), + erlang:error(Error); + Got -> + ct:pal("try_read_file got ~tp", [Got]), + timer:sleep(500), + try_read_file(FileName, Expected, Time-500) + end; +try_read_file(FileName, Expected, _) -> + ct:pal("Missing pattern ~tp in ~tp", [Expected,FileName]), + erlang:error({error,missing_expected_pattern}). + +try_match_file(FileName, Pattern, Time) -> + try_match_file(FileName, Pattern, Time, <<>>). + +try_match_file(FileName, Pattern, Time, _) when Time > 0 -> + case file:read_file(FileName) of + {ok, Bin} -> + case re:run(Bin,Pattern,[{capture,none}]) of + match -> + unicode:characters_to_list(Bin); + _ -> + timer:sleep(100), + try_match_file(FileName, Pattern, Time-100, Bin) + end; + Error -> + erlang:error(Error) + end; +try_match_file(_,Pattern,_,Incorrect) -> + ct:pal("try_match_file did not match pattern: ~p~nGot: ~p~n", + [Pattern,Incorrect]), + erlang:error({error,not_matching_pattern,Pattern,Incorrect}). + +%%%----------------------------------------------------------------- +%%% +start_op_trace() -> + TraceFun = fun({trace,_,call,{_Mod,Func,Details}}, Pid) -> + Pid ! {trace_call,Func,Details}, + Pid; + ({trace,_,return_from,{_Mod,Func,_},RetVal}, Pid) -> + Pid ! {trace_return,Func,RetVal}, + Pid + end, + TRecvPid = spawn_link(fun() -> trace_receiver(5000) end), + {ok,_} = dbg:tracer(process, {TraceFun, TRecvPid}), + + {ok,_} = dbg:p(whereis(?MODULE), [c]), + {ok,_} = dbg:p(self(), [c]), + + MS1 = dbg:fun2ms(fun([_]) -> return_trace() end), + {ok,_} = dbg:tp(logger_h_common, check_load, 1, MS1), + + {ok,_} = dbg:tpl(logger_h_common, flush_log_requests, 2, []), + + MS2 = dbg:fun2ms(fun([_,mode]) -> return_trace() end), + {ok,_} = dbg:tpl(ets, lookup, 2, MS2), + + ct:pal("Tracing started!", []), + TRecvPid. + +stop_op_trace(TRecvPid) -> + dbg:stop_clear(), + unlink(TRecvPid), + exit(TRecvPid, kill), + ok. + +find_mode(flush, Events) -> + lists:any(fun({trace_call,flush_log_requests,[_,_]}) -> true; + (_) -> false + end, Events); +find_mode(Mode, Events) -> + lists:keymember([{mode,Mode}], 3, Events). + +find_switch(From, To, Events) -> + try lists:foldl(fun({trace_return,check_load,{To,_,_,_}}, + {trace_call,check_load,[#{mode := From}]}) -> + throw(match); + (Event, _) -> + Event + end, undefined, Events) of + _ -> false + catch + throw:match -> true + end. + +analyse_trace(TRecvPid, TestFun) -> + TRecvPid ! {test,self(),TestFun}, + receive + {result,TRecvPid,Result} -> + Result + after + 60000 -> + fails + end. + +trace_receiver(IdleT) -> + Msgs = receive_until_idle(IdleT, 5, []), + ct:pal("~w trace events generated", [length(Msgs)]), + analyse(Msgs). + +receive_until_idle(IdleT, WaitN, Msgs) -> + receive + Msg = {trace_call,_,_} -> + receive_until_idle(IdleT, 5, [Msg | Msgs]); + Msg = {trace_return,_,_} -> + receive_until_idle(IdleT, 5, [Msg | Msgs]) + after + IdleT -> + if WaitN == 0 -> + Msgs; + true -> + receive_until_idle(IdleT, WaitN-1, Msgs) + end + end. + +analyse(Msgs) -> + receive + {test,From,TestFun} -> + From ! {result,self(),TestFun(Msgs)}, + analyse(Msgs) + end. -- cgit v1.2.3 From 05e1548e5b452d4bfe3bc6837a1a1b51a6367f72 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 24 Apr 2018 14:55:29 +0200 Subject: Remove error_logger process and add logger process --- lib/kernel/doc/src/error_logger.xml | 19 +- lib/kernel/doc/src/kernel_app.xml | 154 ++++++++-- lib/kernel/src/code_server.erl | 14 +- lib/kernel/src/error_logger.erl | 545 ++++++++++++++++++------------------ lib/kernel/src/kernel.erl | 26 +- 5 files changed, 447 insertions(+), 311 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml index 91bf57cb91..cb6165c73e 100644 --- a/lib/kernel/doc/src/error_logger.xml +++ b/lib/kernel/doc/src/error_logger.xml @@ -31,6 +31,16 @@ error_logger Erlang error logger. + + +

    In OTP-21, a new API for logging was added to Erlang/OTP. The + old error_logger module can still be used by legacy + code, but new code should use the new API instead.

    +

    See logger(3) and + the Logging chapter + in the user's guide for more information.

    +
    +

    The Erlang error logger is an event manager (see OTP Design Principles and gen_event(3)), @@ -171,14 +181,17 @@ ok Get the value of the Kernel application variable - error_logger_format_depth. + logger_format_depth.

    Returns max(10, Depth), where Depth is the value of - - error_logger_format_depth + + logger_format_depth in the Kernel application, if Depth is an integer. Otherwise, unlimited is returned.

    +

    For backwards compatibility, the value + of error_logger_format_depth is used + if logger_format_depth is not set.

    diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index 0762cebc94..554d675383 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -51,10 +51,13 @@
    - Error Logger Event Handlers -

    Two standard error logger event handlers are defined in - the Kernel application. These are described in - error_logger(3).

    + Logger Handlers +

    Two standard logger handlers are defined in + the Kernel application. These are described in the + Kernel User's Guide, + and in logger_std_h(3) + and logger_disk_log_h(3) + .

    @@ -113,6 +116,7 @@
    + Configuration

    The following configuration parameters are defined for the Kernel application. For more information about configuration parameters, @@ -176,34 +180,105 @@

    Permissions are described in application:permit/2.

    - error_logger = Value + logger_dest = Value

    Value is one of:

    tty -

    Installs the standard event handler, which prints error - reports to stdio. This is the default option.

    +

    Installs the standard handler, + logger_std_h(3), with type set + to standard_io. This is the default + option.

    {file, FileName} -

    Installs the standard event handler, which prints error - reports to file FileName, where FileName +

    Installs the standard handler, + logger_std_h(3), with type set + to {file, FileName}, where FileName is a string. The file is opened with encoding UTF-8.

    + {disk_log, FileName} +

    Installs the disk_log handler, + logger_disk_log_h(3), with file set + to FileName (a string), and possibly other disk_log + parameters set by the environment variables + logger_disk_log_type, logger_disk_log_maxfiles and + logger_disk_log_maxbytes, + see below. The + file is opened with encoding UTF-8.

    false -

    No standard event handler is installed, but - the initial, primitive event handler is kept, printing +

    No standard handler is installed, but + the initial, primitive handler is kept, printing raw event messages to tty.

    silent -

    Error logging is turned off.

    +

    No standard handler is started, and the initial, + primitive handler is removed.

    - error_logger_format_depth = Depth + logger_level = Level + +

    Value = emergency | alert | critical | error | warning | + notice | info | debug

    +

    This parameter specifies which log levels to log. The + specified level, and all levels that are more severe, will + be logged.

    +

    This configuration parameter is used both for the global + logger level, and for the standard handler started by + the Kernel application (see logger_dest variable above).

    +

    The default value is info

    +
    + + logger_disk_log_type = halt | wrap + + logger_disk_log_maxfiles = integer() + + logger_disk_log_maxbytes = integer() + +

    If logger_dest 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.

    +

    The default values are:

    + +logger_disk_log_type = wrap +logger_disk_log_maxfiles = 10 +logger_disk_log_maxbytes = 1048576 +
    + + logger_sasl_compatible = boolean() + +

    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, + these log events will be sent to a second handler instance + named sasl_h, according to values of the SASL environment + variables sasl_error_logger + and sasl_errlog_type, see + SASL(6) +

    +

    The default value is false

    +

    See chapter Backwards + compatibility with error_logger for more + information about handling of the so called SASL reports.

    +
    + + logger_log_progress = boolean() + +

    If logger_sasl_compatible = false, + then logger_log_progress specifies if progress + reports from supervisor + and application_controller shall be logged or + not.

    +

    If logger_sasl_compatible = false, + then logger_log_progress is ignored.

    +
    + + logger_format_depth = Depth -

    Can be used to limit the size of the - formatted output from the error logger event handlers.

    + formatted output from the logger handlers.

    This configuration parameter was introduced in OTP 18.1 and is experimental. Based on user feedback, it @@ -214,16 +289,16 @@ useless.

    Depth is a positive integer representing the maximum - depth to which terms are printed by the error logger event + depth to which terms are printed by the logger handlers included in OTP. This - configuration parameter is used by the two event handlers - defined by the Kernel application and the two event - handlers in the SASL application. - (If you have implemented your own error handlers, this configuration - parameter has no effect on them.)

    + configuration parameter is used by the default formatter, + logger_formatter(3), + unless the formatter's depth parameter is explicitly set. + (If you have implemented your own formatter, this configuration + parameter has no effect on that.)

    Depth is used as follows: Format strings - passed to the event handlers are rewritten. + received by the formatter are rewritten. The format controls ~p and ~w are replaced with ~P and ~W, respectively, and Depth is used as the depth parameter. For details, see @@ -234,7 +309,20 @@ 30. We recommend to test crashing various processes in your application, examine the logs from the crashes, and then increase or decrease the value.

    -
    +
    + logger_max_size = integer() | unlimited + +

    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.

    +
    + logger_utc = boolean() + +

    If set to true, the default formatter will display + all dates in Universal Coordinated Time.

    +
    global_groups = [GroupTuple] @@ -496,6 +584,26 @@ MaxT = TickTime + TickTime / 4
    +
    + Deprecated Configuration Parameters +

    In OTP-21, a new API for logging was added to Erlang/OTP. The + old error_logger event manager, and event handlers + running on this manager, will still work, but they are not used + by default.

    +

    The following application environment variables can still be + set, but they will only be used if the corresponding new logger + variables are not set.

    + + error_logger + Replaced by logger_dest + error_logger_format_depth + Replaced by logger_format_depth + +

    See Backwards + compatibility with error_logger for more + information.

    +
    +
    See Also

    app(4), diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index f5a890cb95..bbfa2a995d 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -1434,14 +1434,20 @@ all_loaded(Db) -> -spec error_msg(io:format(), [term()]) -> 'ok'. error_msg(Format, Args) -> - Msg = {notify,{error, group_leader(), {self(), Format, Args}}}, - error_logger ! Msg, + logger ! {log,error,Format,Args, + #{pid=>self(), + gl=>group_leader(), + time=>erlang:monotonic_time(microsecond), + error_logger=>#{tag=>error}}}, ok. -spec info_msg(io:format(), [term()]) -> 'ok'. info_msg(Format, Args) -> - Msg = {notify,{info_msg, group_leader(), {self(), Format, Args}}}, - error_logger ! Msg, + logger ! {log,info,Format,Args, + #{pid=>self(), + gl=>group_leader(), + time=>erlang:monotonic_time(microsecond), + error_logger=>#{tag=>info_msg}}}, ok. objfile_extension() -> diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index 585507c545..0706220a94 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -19,22 +19,23 @@ %% -module(error_logger). --export([start/0,start_link/0,format/2,error_msg/1,error_msg/2,error_report/1, +-include("logger_internal.hrl"). + +-export([start/0,start_link/0,stop/0, + format/2,error_msg/1,error_msg/2,error_report/1, error_report/2,info_report/1,info_report/2,warning_report/1, warning_report/2,error_info/1, info_msg/1,info_msg/2,warning_msg/1,warning_msg/2, - logfile/1,tty/1,swap_handler/1, + logfile/1,tty/1, add_report_handler/1,add_report_handler/2, - delete_report_handler/1]). + delete_report_handler/1, + which_report_handlers/0]). --export([init/1, - handle_event/2, handle_call/2, handle_info/2, - terminate/2]). +%% logger callbacks +-export([adding_handler/2, removing_handler/1, log/2]). -export([get_format_depth/0, limit_term/1]). --define(buffer_size, 10). - %%----------------------------------------------------------------- %% Types used in this file %%----------------------------------------------------------------- @@ -43,8 +44,6 @@ | 'info' | 'info_msg' | 'info_report' | 'warning_msg' | 'warning_report'. --type state() :: {non_neg_integer(), non_neg_integer(), [term()]}. - %%% BIF -export([warning_map/0]). @@ -59,26 +58,137 @@ warning_map() -> %%----------------------------------------------------------------- --spec start() -> {'ok', pid()} | {'error', any()}. +%%%----------------------------------------------------------------- +%%% Start the event manager process under logger_sup, which is part of +%%% the kernel application's supervision tree. +-spec start() -> 'ok' | {'error', any()}. start() -> - case gen_event:start({local, error_logger}) of - {ok, Pid} -> - simple_logger(?buffer_size), - {ok, Pid}; - Error -> Error + case whereis(?MODULE) of + undefined -> + ErrorLogger = + #{id => ?MODULE, + start => {?MODULE, start_link, []}, + restart => transient, + shutdown => 2000, + type => worker, + modules => dynamic}, + case supervisor:start_child(logger_sup, ErrorLogger) of + {ok,_} -> + ok; + Error -> + Error + end; + _ -> + ok end. +%%%----------------------------------------------------------------- +%%% Start callback specified in child specification to supervisor, see start/0 -spec start_link() -> {'ok', pid()} | {'error', any()}. start_link() -> - case gen_event:start_link({local, error_logger}) of - {ok, Pid} -> - simple_logger(?buffer_size), - {ok, Pid}; - Error -> Error + gen_event:start_link({local, ?MODULE}, + [{spawn_opt,[{message_queue_data, off_heap}]}]). + +%%%----------------------------------------------------------------- +%%% Stop the event manager +-spec stop() -> ok. +stop() -> + _ = supervisor:terminate_child(logger_sup,?MODULE), + _ = supervisor:delete_child(logger_sup,?MODULE), + ok. + +%%%----------------------------------------------------------------- +%%% Callbacks for logger +-spec adding_handler(logger:handler_id(),logger:config()) -> + {ok,logger:config()} | {error,term()}. +adding_handler(?MODULE,Config) -> + case start() of + ok -> + {ok,Config}; + Error -> + Error end. +-spec removing_handler(logger:handler_id()) -> ok. +removing_handler(?MODULE) -> + stop(), + ok. + +-spec log(logger:log(),logger:config()) -> ok. +log(#{level:=Level,msg:=Msg,meta:=Meta},_Config) -> + do_log(Level,Msg,Meta). + +do_log(Level,{report,Msg},#{?MODULE:=#{tag:=Tag,type:=Type}}=Meta) -> + %% From error_logger:*_report/1,2, or logger call which added + %% error_logger data to obtain backwards compatibility with + %% error_logger:*_report/1,2 + Report = + case Msg of + #{label:=_,report:=R} -> R; + _ -> Msg + end, + notify(Level,Tag,Type,Report,Meta); +do_log(Level,{report,Msg},#{?MODULE:=#{tag:=Tag}}=Meta) -> + {Format,Args} = + case Msg of + #{label:=_,format:=F,args:=A} -> + %% From error_logger:*_msg/1,2. + %% In order to be backwards compatible with handling + %% of faulty parameters to error_logger:*_msg/1,2, + %% don't use report_cb here. + {F,A}; + _ -> + %% From logger call which added error_logger data to + %% obtain backwards compatibility with error_logger:*_msg/1,2 + RCBFun=maps:get(report_cb,Meta,fun logger:format_report/1), + try RCBFun(Msg) of + {F,A} when is_list(F), is_list(A) -> + {F,A}; + Other -> + {"REPORT_CB ERROR: ~tp; Returned: ~tp",[Msg,Other]} + catch C:R -> + {"REPORT_CB CRASH: ~tp; Reason: ~tp",[Msg,{C,R}]} + end + end, + notify(Level,Tag,Format,Args,Meta); +do_log(Level,{Format,Args},#{?MODULE:=#{tag:=Tag}}=Meta) + when is_list(Format), is_list(Args) -> + %% From logger call which added error_logger data to obtain + %% backwards compatibility with error_logger:*_msg/1,2 + notify(Level,Tag,Format,Args,Meta); +do_log(_Level,_Msg,_Meta) -> + %% Ignore the rest - i.e. to get backwards compatibility with + %% error_logger, you must use the error_logger API for logging. + %% Some modules within OTP go around this by adding an + %% error_logger field to its metadata. This is done only to allow + %% complete backwards compatibility for log events originating + %% from within OTP, while still using the new logger interface. + ok. + +-spec notify(logger:level(), msg_tag(), any(), any(), map()) -> 'ok'. +notify(Level,Tag0,FormatOrType0,ArgsOrReport,#{pid:=Pid0,gl:=GL,?MODULE:=My}) -> + Tag = fix_warning_tag(Level,Tag0), + Pid = case maps:get(emulator,My,false) of + true -> emulator; + _ -> Pid0 + end, + FormatOrType = fix_warning_type(Level,FormatOrType0), + gen_event:notify(?MODULE,{Tag,GL,{Pid,FormatOrType,ArgsOrReport}}). + +%% This is to fix the case when the client has explicitly added the +%% error logger tag and type in metadata, and not checked the warning map. +fix_warning_tag(error,warning_msg) -> error; +fix_warning_tag(error,warning_report) -> error_report; +fix_warning_tag(info,warning_msg) -> info_msg; +fix_warning_tag(info,warning_report) -> info_report; +fix_warning_tag(_,Tag) -> Tag. + +fix_warning_type(error,std_warning) -> std_error; +fix_warning_type(info,std_warning) -> std_info; +fix_warning_type(_,Type) -> Type. + %%----------------------------------------------------------------- %% These two simple old functions generate events tagged 'error' %% Used for simple messages; error or information. @@ -95,14 +205,18 @@ error_msg(Format) -> Data :: list(). error_msg(Format, Args) -> - notify({error, group_leader(), {self(), Format, Args}}). + logger:log(error, + #{label=>{?MODULE,error_msg}, + format=>Format, + args=>Args}, + meta(error)). -spec format(Format, Data) -> 'ok' when Format :: string(), Data :: list(). format(Format, Args) -> - notify({error, group_leader(), {self(), Format, Args}}). + error_msg(Format, Args). %%----------------------------------------------------------------- %% This functions should be used for error reports. Events @@ -124,7 +238,10 @@ error_report(Report) -> Report :: report(). error_report(Type, Report) -> - notify({error_report, group_leader(), {self(), Type, Report}}). + logger:log(error, + #{label=>{?MODULE,error_report}, + report=>Report}, + meta(error_report,Type)). %%----------------------------------------------------------------- %% This function should be used for warning reports. @@ -146,7 +263,8 @@ warning_report(Report) -> Report :: report(). warning_report(Type, Report) -> - {Tag, NType} = case error_logger:warning_map() of + Level = error_logger:warning_map(), + {Tag, NType} = case Level of info -> if Type =:= std_warning -> @@ -164,7 +282,10 @@ warning_report(Type, Report) -> {error_report, Type} end end, - notify({Tag, group_leader(), {self(), NType, Report}}). + logger:log(Level, + #{label=>{?MODULE,warning_report}, + report=>Report}, + meta(Tag,NType)). %%----------------------------------------------------------------- %% This function provides similar functions as error_msg for @@ -183,7 +304,8 @@ warning_msg(Format) -> Data :: list(). warning_msg(Format, Args) -> - Tag = case error_logger:warning_map() of + Level = error_logger:warning_map(), + Tag = case Level of warning -> warning_msg; info -> @@ -191,7 +313,11 @@ warning_msg(Format, Args) -> error -> error end, - notify({Tag, group_leader(), {self(), Format, Args}}). + logger:log(Level, + #{label=>{?MODULE,warning_msg}, + format=>Format, + args=>Args}, + meta(Tag)). %%----------------------------------------------------------------- %% This function should be used for information reports. Events @@ -210,7 +336,10 @@ info_report(Report) -> Report :: report(). info_report(Type, Report) -> - notify({info_report, group_leader(), {self(), Type, Report}}). + logger:log(info, + #{label=>{?MODULE,info_report}, + report=>Report}, + meta(info_report,Type)). %%----------------------------------------------------------------- %% This function provides similar functions as error_msg for @@ -228,7 +357,11 @@ info_msg(Format) -> Data :: list(). info_msg(Format, Args) -> - notify({info_msg, group_leader(), {self(), Format, Args}}). + logger:log(info, + #{label=>{?MODULE,info_msg}, + format=>Format, + args=>Args}, + meta(info_msg)). %%----------------------------------------------------------------- %% Used by the init process. Events are tagged 'info'. @@ -236,38 +369,75 @@ info_msg(Format, Args) -> -spec error_info(Error :: any()) -> 'ok'. +%% unused? error_info(Error) -> - notify({info, group_leader(), {self(), Error, []}}). - --spec notify({msg_tag(), pid(), {pid(), any(), any()}}) -> 'ok'. - -notify(Msg) -> - gen_event:notify(error_logger, Msg). - --type swap_handler_type() :: 'false' | 'silent' | 'tty' | {'logfile', string()}. --spec swap_handler(Type :: swap_handler_type()) -> any(). - -swap_handler(tty) -> - R = gen_event:swap_handler(error_logger, {error_logger, swap}, - {error_logger_tty_h, []}), - ok = simple_logger(), - R; -swap_handler({logfile, File}) -> - R = gen_event:swap_handler(error_logger, {error_logger, swap}, - {error_logger_file_h, File}), - ok = simple_logger(), - R; -swap_handler(silent) -> - _ = gen_event:delete_handler(error_logger, error_logger, delete), - ok = simple_logger(); -swap_handler(false) -> - ok. % keep primitive event handler as-is + {Format,Args} = + case string_p(Error) of + true -> {Error,[]}; + false -> {"~p",[Error]} + end, + MyMeta = #{tag=>info,type=>Error}, + logger:log(info, Format, Args, #{?MODULE=>MyMeta,domain=>[Error]}). + +%%----------------------------------------------------------------- +%% Create metadata +meta(Tag) -> + meta(Tag,undefined). +meta(Tag,Type) -> + meta(Tag,Type,#{report_cb=>fun report_to_format/1}). +meta(Tag,undefined,Meta0) -> + Meta0#{?MODULE=>#{tag=>Tag}}; +meta(Tag,Type,Meta0) -> + maybe_add_domain(Tag,Type,Meta0#{?MODULE=>#{tag=>Tag,type=>Type}}). + +%% This is to prevent events of non standard type from being printed +%% with the standard logger. Similar to how error_logger_tty_h +%% discards events of non standard type. +maybe_add_domain(error_report,std_error,Meta) -> Meta; +maybe_add_domain(info_report,std_info,Meta) -> Meta; +maybe_add_domain(warning_report,std_warning,Meta) -> Meta; +maybe_add_domain(_,Type,Meta) -> Meta#{domain=>[Type]}. + +%% ----------------------------------------------------------------- +%% Report formatting - i.e. Term => {Format,Args} +%% This was earlier done in the event handler (error_logger_tty_h, etc) +%% ----------------------------------------------------------------- +report_to_format(#{label:={?MODULE,_}, + report:=Report}) when is_map(Report) -> + %% logger:format_otp_report does maps:to_list, and for backwards + %% compatibility reasons we don't want that. + {"~tp\n",[Report]}; +report_to_format(#{label:={?MODULE,_}, + format:=Format, + args:=Args}) -> + %% This is not efficient, but needed for backwards compatibility + %% in giving faulty arguments to the *_msg functions. + try io_lib:scan_format(Format,Args) of + _ -> {Format,Args} + catch _:_ -> + {"ERROR: ~tp - ~tp",[Format,Args]} + end; +report_to_format(Term) -> + logger:format_otp_report(Term). +string_p(List) when is_list(List) -> + string_p1(lists:flatten(List)); +string_p(_) -> + false. + +string_p1([]) -> + false; +string_p1(FlatList) -> + io_lib:printable_list(FlatList). + +%% ----------------------------------------------------------------- +%% Stuff directly related to the event manager +%% ----------------------------------------------------------------- -spec add_report_handler(Handler) -> any() when Handler :: module(). add_report_handler(Module) when is_atom(Module) -> - gen_event:add_handler(error_logger, Module, []). + add_report_handler(Module, []). -spec add_report_handler(Handler, Args) -> Result when Handler :: module(), @@ -275,24 +445,37 @@ add_report_handler(Module) when is_atom(Module) -> Result :: gen_event:add_handler_ret(). add_report_handler(Module, Args) when is_atom(Module) -> - gen_event:add_handler(error_logger, Module, Args). + _ = logger:add_handler(?MODULE,?MODULE,#{level=>info,filter_default=>log}), + gen_event:add_handler(?MODULE, Module, Args). -spec delete_report_handler(Handler) -> Result when Handler :: module(), Result :: gen_event:del_handler_ret(). delete_report_handler(Module) when is_atom(Module) -> - gen_event:delete_handler(error_logger, Module, []). - -%% Start the lowest level error_logger handler with Buffer. - -simple_logger(Buffer_size) when is_integer(Buffer_size) -> - gen_event:add_handler(error_logger, error_logger, Buffer_size). - -%% Start the lowest level error_logger handler without Buffer. + case whereis(?MODULE) of + Pid when is_pid(Pid) -> + Return = gen_event:delete_handler(?MODULE, Module, []), + case gen_event:which_handlers(?MODULE) of + [] -> + %% Don't want a lot of logs here if it's not needed + _ = logger:remove_handler(?MODULE), + ok; + _ -> + ok + end, + Return; + _ -> + ok + end. -simple_logger() -> - gen_event:add_handler(error_logger, error_logger, []). +which_report_handlers() -> + case whereis(?MODULE) of + Pid when is_pid(Pid) -> + gen_event:which_handlers(?MODULE); + undefined -> + [] + end. %% Log all errors to File for all eternity @@ -308,26 +491,35 @@ simple_logger() -> FilenameReason :: no_log_file. logfile({open, File}) -> - case lists:member(error_logger_file_h, - gen_event:which_handlers(error_logger)) of + case lists:member(error_logger_file_h,which_report_handlers()) of true -> {error, allready_have_logfile}; _ -> - gen_event:add_handler(error_logger, error_logger_file_h, File) + add_report_handler(error_logger_file_h, File) end; logfile(close) -> - case gen_event:delete_handler(error_logger, error_logger_file_h, normal) of - {error,Reason} -> - {error,Reason}; - _ -> - ok + case whereis(?MODULE) of + Pid when is_pid(Pid) -> + case gen_event:delete_handler(?MODULE, error_logger_file_h, normal) of + {error,Reason} -> + {error,Reason}; + _ -> + ok + end; + _ -> + {error,module_not_found} end; logfile(filename) -> - case gen_event:call(error_logger, error_logger_file_h, filename) of - {error,_} -> - {error, no_log_file}; - Val -> - Val + case whereis(?MODULE) of + Pid when is_pid(Pid) -> + case gen_event:call(?MODULE, error_logger_file_h, filename) of + {error,_} -> + {error, no_log_file}; + Val -> + Val + end; + _ -> + {error, no_log_file} end. %% Possibly turn off all tty printouts, maybe we only want the errors @@ -337,193 +529,17 @@ logfile(filename) -> Flag :: boolean(). tty(true) -> - Hs = gen_event:which_handlers(error_logger), - case lists:member(error_logger_tty_h, Hs) of + case lists:member(error_logger_tty_h, which_report_handlers()) of false -> - gen_event:add_handler(error_logger, error_logger_tty_h, []); - true -> + add_report_handler(error_logger_tty_h, []); + true -> ignore end, ok; tty(false) -> - gen_event:delete_handler(error_logger, error_logger_tty_h, []), - ok. - + delete_report_handler(error_logger_tty_h). -%%% --------------------------------------------------- -%%% This is the default error_logger handler. -%%% --------------------------------------------------- - --spec init(term()) -> {'ok', state() | []}. - -init(Max) when is_integer(Max) -> - {ok, {Max, 0, []}}; -%% This one is called if someone took over from us, and now wants to -%% go back. -init({go_back, _PostState}) -> - {ok, {?buffer_size, 0, []}}; -init(_) -> - %% The error logger process may receive a huge amount of - %% messages. Make sure that they are stored off heap to - %% avoid exessive GCs. - process_flag(message_queue_data, off_heap), - {ok, []}. - --spec handle_event(term(), state()) -> {'ok', state()}. - -handle_event({Type, GL, Msg}, State) when node(GL) =/= node() -> - gen_event:notify({error_logger, node(GL)},{Type, GL, Msg}), - %% handle_event2({Type, GL, Msg}, State); %% Shall we do something - {ok, State}; %% at this node too ??? -handle_event({info_report, _, {_, Type, _}}, State) when Type =/= std_info -> - {ok, State}; %% Ignore other info reports here -handle_event(Event, State) -> - handle_event2(Event, State). - --spec handle_info(term(), state()) -> {'ok', state()}. - -handle_info({emulator, GL, Chars}, State) when node(GL) =/= node() -> - {error_logger, node(GL)} ! {emulator, GL, add_node(Chars,self())}, - {ok, State}; -handle_info({emulator, GL, Chars}, State) -> - handle_event2({emulator, GL, Chars}, State); -handle_info(_, State) -> - {ok, State}. - --spec handle_call(term(), state()) -> {'ok', {'error', 'bad_query'}, state()}. - -handle_call(_Query, State) -> {ok, {error, bad_query}, State}. - --spec terminate(term(), state()) -> {'error_logger', [term()]}. - -terminate(swap, {_, 0, Buff}) -> - {error_logger, Buff}; -terminate(swap, {_, Lost, Buff}) -> - Myevent = {info, group_leader(), {self(), {lost_messages, Lost}, []}}, - {error_logger, [tag_event(Myevent)|Buff]}; -terminate(_, _) -> - {error_logger, []}. - -handle_event2(Event, {1, Lost, Buff}) -> - display(tag_event(Event)), - {ok, {1, Lost+1, Buff}}; -handle_event2(Event, {N, Lost, Buff}) -> - Tagged = tag_event(Event), - display(Tagged), - {ok, {N-1, Lost, [Tagged|Buff]}}; -handle_event2(_, State) -> - {ok, State}. - -tag_event(Event) -> - {erlang:localtime(), Event}. - -display({Tag,{error,_,{_,Format,Args}}}) -> - display2(Tag,Format,Args); -display({Tag,{error_report,_,{_,Type,Report}}}) -> - display2(Tag,Type,Report); -display({Tag,{info_report,_,{_,Type,Report}}}) -> - display2(Tag,Type,Report); -display({Tag,{info,_,{_,Error,_}}}) -> - display2(Tag,Error,[]); -display({Tag,{info_msg,_,{_,Format,Args}}}) -> - display2(Tag,Format,Args); -display({Tag,{warning_report,_,{_,Type,Report}}}) -> - display2(Tag,Type,Report); -display({Tag,{warning_msg,_,{_,Format,Args}}}) -> - display2(Tag,Format,Args); -display({Tag,{emulator,_,Chars}}) -> - display2(Tag,Chars,[]). - -add_node(X, Pid) when is_atom(X) -> - add_node(atom_to_list(X), Pid); -add_node(X, Pid) -> - lists:concat([X,"** at node ",node(Pid)," **~n"]). - -%% Can't do io_lib:format - -display2({{_Y,_Mo,_D},{_H,_Mi,_S}} = Date, F, A) -> - display_date(Date), - display3(string_p(F), F, A). - -display_date({{Y,Mo,D},{H,Mi,S}}) -> - erlang:display_string( - integer_to_list(Y) ++ "-" ++ - two_digits(Mo) ++ "-" ++ - two_digits(D) ++ " " ++ - two_digits(H) ++ ":" ++ - two_digits(Mi) ++ ":" ++ - two_digits(S) ++ " "). - -two_digits(N) when 0 =< N, N =< 9 -> - [$0, $0 + N]; -two_digits(N) -> - integer_to_list(N). - -display3(true, F, A) -> - %% Format string with arguments - erlang:display_string(F ++ "\n"), - [begin - erlang:display_string("\t"), - erlang:display(Arg) - end || Arg <- A], - ok; -display3(false, Atom, A) when is_atom(Atom) -> - %% The widest atom seems to be 'supervisor_report' at 17. - ColumnWidth = 20, - AtomString = atom_to_list(Atom), - AtomLength = length(AtomString), - Padding = lists:duplicate(ColumnWidth - AtomLength, $\s), - erlang:display_string(AtomString ++ Padding), - display4(A); -display3(_, F, A) -> - erlang:display({F, A}). - -display4([A, []]) -> - %% Not sure why crash reports look like this. - display4(A); -display4(A = [_|_]) -> - case lists:all(fun({Key,_Value}) -> is_atom(Key); (_) -> false end, A) of - true -> - erlang:display_string("\n"), - lists:foreach( - fun({Key, Value}) -> - erlang:display_string( - " " ++ - atom_to_list(Key) ++ - ": "), - erlang:display(Value) - end, A); - false -> - erlang:display(A) - end; -display4(A) -> - erlang:display(A). - -string_p([]) -> - false; -string_p(Term) -> - string_p1(Term). - -string_p1([H|T]) when is_integer(H), H >= $\040, H =< $\176 -> - string_p1(T); -string_p1([H|T]) when is_integer(H), H >= 16#A0, H < 16#D800; - is_integer(H), H > 16#DFFF, H < 16#FFFE; - is_integer(H), H > 16#FFFF, H =< 16#10FFFF -> - string_p1(T); -string_p1([$\n|T]) -> string_p1(T); -string_p1([$\r|T]) -> string_p1(T); -string_p1([$\t|T]) -> string_p1(T); -string_p1([$\v|T]) -> string_p1(T); -string_p1([$\b|T]) -> string_p1(T); -string_p1([$\f|T]) -> string_p1(T); -string_p1([$\e|T]) -> string_p1(T); -string_p1([H|T]) when is_list(H) -> - case string_p1(H) of - true -> string_p1(T); - _ -> false - end; -string_p1([]) -> true; -string_p1(_) -> false. +%%%----------------------------------------------------------------- -spec limit_term(term()) -> term(). @@ -536,9 +552,4 @@ limit_term(Term) -> -spec get_format_depth() -> 'unlimited' | pos_integer(). get_format_depth() -> - case application:get_env(kernel, error_logger_format_depth) of - {ok, Depth} when is_integer(Depth) -> - max(10, Depth); - undefined -> - unlimited - end. + logger:get_format_depth(). diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl index 0382764b39..20aa47f602 100644 --- a/lib/kernel/src/kernel.erl +++ b/lib/kernel/src/kernel.erl @@ -40,8 +40,7 @@ start(_, []) -> ok = gen_event:add_handler(erl_signal_server, erl_signal_handler, []) end, %% add error handler - Type = get_error_logger_type(), - case error_logger:swap_handler(Type) of + case logger:setup_standard_handler() of ok -> {ok, Pid, []}; Error -> %% Not necessary since the node will crash anyway: @@ -62,16 +61,6 @@ config_change(Changed, New, Removed) -> do_global_groups_change(Changed, New, Removed), ok. -get_error_logger_type() -> - case application:get_env(kernel, error_logger) of - {ok, tty} -> tty; - {ok, {file, File}} when is_list(File) -> {logfile, File}; - {ok, false} -> false; - {ok, silent} -> silent; - undefined -> tty; % default value - {ok, Bad} -> exit({bad_config, {kernel, {error_logger, Bad}}}) - end. - %%%----------------------------------------------------------------- %%% The process structure in kernel is as shown in the figure. %%% @@ -153,9 +142,18 @@ init([]) -> type => supervisor, modules => [?MODULE]}, + + LoggerSup = #{id => logger_sup, + start => {logger_sup, start_link, []}, + restart => permanent, + shutdown => infinity, + type => supervisor, + modules => [logger_sup]}, + case init:get_argument(mode) of {ok, [["minimal"]]} -> - {ok, {SupFlags, [Code, File, StdError, User, Config, RefC, SafeSup]}}; + {ok, {SupFlags, + [Code, File, StdError, User, Config, RefC, SafeSup, LoggerSup]}}; _ -> Rpc = #{id => rex, start => {rpc, start_link, []}, @@ -206,7 +204,7 @@ init([]) -> {ok, {SupFlags, [Code, Rpc, Global, InetDb | DistAC] ++ [NetSup, GlGroup, File, SigSrv, - StdError, User, Config, RefC, SafeSup] ++ Timer}} + StdError, User, Config, RefC, SafeSup, LoggerSup] ++ Timer}} end; init(safe) -> SupFlags = #{strategy => one_for_one, -- cgit v1.2.3 From c4504cbe3a474b0bb5dab00ae66d680d3d0e9ff1 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 24 Apr 2018 15:15:57 +0200 Subject: Start using logger internally in kernel and stdlib --- lib/kernel/src/application_controller.erl | 35 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 14 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/application_controller.erl b/lib/kernel/src/application_controller.erl index 3b642f5873..b9cb722575 100644 --- a/lib/kernel/src/application_controller.erl +++ b/lib/kernel/src/application_controller.erl @@ -44,6 +44,7 @@ keyfind/3, keydelete/3, keyreplace/4]). -include("application_master.hrl"). +-include("logger.hrl"). -define(AC, ?MODULE). % Name of process @@ -1546,9 +1547,8 @@ do_change_apps(Applications, Config, OldAppls) -> %% Report errors, but do not terminate %% (backwards compatible behaviour) lists:foreach(fun({error, {SysFName, Line, Str}}) -> - Str2 = lists:flatten(io_lib:format("~tp: ~w: ~ts~n", - [SysFName, Line, Str])), - error_logger:format(Str2, []) + ?LOG_ERROR("~tp: ~w: ~ts~n",[SysFName, Line, Str], + #{error_logger=>#{tag=>error}}) end, Errors), @@ -1631,8 +1631,9 @@ make_term(Str) -> end. handle_make_term_error(Mod, Reason, Str) -> - error_logger:format("application_controller: ~ts: ~ts~n", - [Mod:format_error(Reason), Str]), + ?LOG_ERROR("application_controller: ~ts: ~ts~n", + [Mod:format_error(Reason), Str], + #{error_logger=>#{tag=>error}}), throw({error, {bad_environment_value, Str}}). get_env_i(Name, #state{conf_data = ConfData}) when is_list(ConfData) -> @@ -1913,19 +1914,25 @@ config_error() -> "configuration file must contain ONE list ended by "}}. %%----------------------------------------------------------------- -%% Info messages sent to error_logger +%% Info messages sent to logger %%----------------------------------------------------------------- info_started(Name, Node) -> - Rep = [{application, Name}, - {started_at, Node}], - error_logger:info_report(progress, Rep). + ?LOG_INFO(#{label=>{application_controller,progress}, + report=>[{application, Name}, + {started_at, Node}]}, + #{domain=>[beam,erlang,otp,sasl], + report_cb=>fun logger:format_otp_report/1, + logger_formatter=>#{title=>"PROGRESS REPORT"}, + error_logger=>#{tag=>info_report,type=>progress}}). info_exited(Name, Reason, Type) -> - Rep = [{application, Name}, - {exited, Reason}, - {type, Type}], - error_logger:info_report(Rep). - + ?LOG_INFO(#{label=>{application_controller,exit}, + report=>[{application, Name}, + {exited, Reason}, + {type, Type}]}, + #{domain=>[beam,erlang,otp], + report_cb=>fun logger:format_otp_report/1, + error_logger=>#{tag=>info_report,type=>std_info}}). %%----------------------------------------------------------------- %% Reply to all processes waiting this application to be started. -- cgit v1.2.3 From 291cf6d8530f4e0ad2f22f199b5ae6975135bbb3 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 24 Apr 2018 15:18:31 +0200 Subject: Test cuddle for logger --- lib/kernel/test/application_SUITE.erl | 3 +- lib/kernel/test/error_logger_SUITE.erl | 46 ++++++++++++++++++++-- lib/kernel/test/init_SUITE.erl | 6 +-- lib/kernel/test/wrap_log_reader_SUITE.erl | 17 ++++---- .../wrap_log_reader_SUITE_data/wrap_log_test.erl | 24 +++++------ 5 files changed, 68 insertions(+), 28 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl index 866043cfb4..c00fb44c46 100644 --- a/lib/kernel/test/application_SUITE.erl +++ b/lib/kernel/test/application_SUITE.erl @@ -1568,7 +1568,8 @@ loop5606(Pid) -> %% Tests get_env/* functions. get_env(Conf) when is_list(Conf) -> - {ok, _} = application:get_env(kernel, error_logger), + ok = application:set_env(kernel, new_var, new_val), + {ok, new_val} = application:get_env(kernel, new_var), undefined = application:get_env(undefined_app, a), undefined = application:get_env(kernel, error_logger_xyz), default = application:get_env(kernel, error_logger_xyz, default), diff --git a/lib/kernel/test/error_logger_SUITE.erl b/lib/kernel/test/error_logger_SUITE.erl index 2d26a7246c..6c4526d0cf 100644 --- a/lib/kernel/test/error_logger_SUITE.erl +++ b/lib/kernel/test/error_logger_SUITE.erl @@ -32,7 +32,8 @@ init_per_group/2,end_per_group/2, off_heap/1, error_report/1, info_report/1, error/1, info/1, - emulator/1, tty/1, logfile/1, add/1, delete/1]). + emulator/1, via_logger_process/1, other_node/1, + tty/1, logfile/1, add/1, delete/1]). -export([generate_error/2]). @@ -46,16 +47,19 @@ suite() -> {timetrap,{minutes,1}}]. all() -> - [off_heap, error_report, info_report, error, info, emulator, tty, - logfile, add, delete]. + [off_heap, error_report, info_report, error, info, emulator, + via_logger_process, other_node, tty, logfile, add, delete]. groups() -> []. init_per_suite(Config) -> + logger:add_handler(error_logger,error_logger, + #{level=>info,filter_default=>log}), Config. end_per_suite(_Config) -> + logger:remove_handler(error_logger), ok. init_per_group(_GroupName, Config) -> @@ -225,6 +229,40 @@ emulator(Config) when is_list(Config) -> generate_error(Error, Stack) -> erlang:raise(error, Error, Stack). +%%----------------------------------------------------------------- + +via_logger_process(Config) -> + case os:type() of + {win32,_} -> + {skip,"Skip on windows - cant change file mode"}; + _ -> + error_logger:add_report_handler(?MODULE, self()), + Dir = filename:join(?config(priv_dir,Config),"dummydir"), + Msg = "File operation error: eacces. Target: " ++ + Dir ++ ". Function: list_dir. ", + ok = file:make_dir(Dir), + ok = file:change_mode(Dir,8#0222), + error = erl_prim_loader:list_dir(Dir), + ok = file:change_mode(Dir,8#0664), + _ = file:del_dir(Dir), + reported(error_report, std_error, Msg), + my_yes = error_logger:delete_report_handler(?MODULE), + ok + end. + +%%----------------------------------------------------------------- + +other_node(_Config) -> + error_logger:add_report_handler(?MODULE, self()), + {ok,Node} = test_server:start_node(?FUNCTION_NAME,slave,[]), + ok = rpc:call(Node,logger,add_handler,[error_logger,error_logger, + #{level=>info,filter_default=>log}]), + rpc:call(Node,error_logger,error_report,[hi_from_remote]), + reported(error_report,std_error,hi_from_remote), + test_server:stop_node(Node), + ok. + + %%----------------------------------------------------------------- %% We don't enables or disables tty error logging here. We do not %% want to interact with the test run. @@ -279,7 +317,7 @@ reported(Tag, Type, Report) -> test_server:messages_get(), ok after 1000 -> - ct:fail(no_report_received) + ct:fail({no_report_received,test_server:messages_get()}) end. %%----------------------------------------------------------------- diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl index c8415b34e5..6a006cdc01 100644 --- a/lib/kernel/test/init_SUITE.erl +++ b/lib/kernel/test/init_SUITE.erl @@ -299,7 +299,7 @@ many_restarts() -> many_restarts(Config) when is_list(Config) -> {ok, Node} = loose_node:start(init_test, "", ?DEFAULT_TIMEOUT_SEC), - loop_restart(50,Node,rpc:call(Node,erlang,whereis,[error_logger])), + loop_restart(50,Node,rpc:call(Node,erlang,whereis,[logger])), loose_node:stop(Node), ok. @@ -316,13 +316,13 @@ loop_restart(N,Node,EHPid) -> ct:fail(not_stopping) end, ok = wait_for(30, Node, EHPid), - loop_restart(N-1,Node,rpc:call(Node,erlang,whereis,[error_logger])). + loop_restart(N-1,Node,rpc:call(Node,erlang,whereis,[logger])). wait_for(0,Node,_) -> loose_node:stop(Node), error; wait_for(N,Node,EHPid) -> - case rpc:call(Node, erlang, whereis, [error_logger]) of + case rpc:call(Node, erlang, whereis, [logger]) of Pid when is_pid(Pid), Pid =/= EHPid -> %% erlang:display(ok), ok; diff --git a/lib/kernel/test/wrap_log_reader_SUITE.erl b/lib/kernel/test/wrap_log_reader_SUITE.erl index 40a016aed0..b1ee29a11f 100644 --- a/lib/kernel/test/wrap_log_reader_SUITE.erl +++ b/lib/kernel/test/wrap_log_reader_SUITE.erl @@ -428,13 +428,14 @@ stop() -> ok = wrap_log_test:stop(), dl_wait(). -%% Give disk logs opened by 'logger' and 'wlt' time to close after +%% Give disk logs opened by 'wlr_logger' and 'wlt' time to close after %% receiving EXIT signals. dl_wait() -> case disk_log:accessible_logs() of {[], []} -> ok; - _ -> + _X -> + erlang:display(_X), timer:sleep(100), dl_wait() end. @@ -507,27 +508,27 @@ add_ext(Name, Ext) -> %% disk_log. open(Log, File, Where) -> - logger ! {open, self(), Log, File}, + wlr_logger ! {open, self(), Log, File}, rec1(ok, Where). open_ext(Log, File, Where) -> - logger ! {open_ext, self(), Log, File}, + wlr_logger ! {open_ext, self(), Log, File}, rec1(ok, Where). close(Log) -> - logger ! {close, self(), Log}, + wlr_logger ! {close, self(), Log}, rec(ok, ?LINE). sync(Log) -> - logger ! {sync, self(), Log}, + wlr_logger ! {sync, self(), Log}, rec(ok, ?LINE). log_terms(File, Terms) -> - logger ! {log_terms, self(), File, Terms}, + wlr_logger ! {log_terms, self(), File, Terms}, rec(ok, ?LINE). blog_terms(File, Terms) -> - logger ! {blog_terms, self(), File, Terms}, + wlr_logger ! {blog_terms, self(), File, Terms}, rec(ok, ?LINE). rec1(M, Where) -> diff --git a/lib/kernel/test/wrap_log_reader_SUITE_data/wrap_log_test.erl b/lib/kernel/test/wrap_log_reader_SUITE_data/wrap_log_test.erl index 38449b6bb3..2b24ccc66f 100644 --- a/lib/kernel/test/wrap_log_reader_SUITE_data/wrap_log_test.erl +++ b/lib/kernel/test/wrap_log_reader_SUITE_data/wrap_log_test.erl @@ -36,9 +36,9 @@ -endif. init() -> - spawn(fun() -> start(logger) end), + spawn(fun() -> start(wlr_logger) end), spawn(fun() -> start2(wlt) end), - wait_registered(logger), + wait_registered(wlr_logger), wait_registered(wlt), ok. @@ -52,9 +52,9 @@ wait_registered(Name) -> end. stop() -> - catch logger ! exit, + catch wlr_logger ! exit, catch wlt ! exit, - wait_unregistered(logger), + wait_unregistered(wlr_logger), wait_unregistered(wlt), ok. @@ -82,47 +82,47 @@ loop() -> {open, Pid, Name, File} -> R = disk_log:open([{name, Name}, {type, wrap}, {file, File}, {size, {?fsize, ?fno}}]), - ?format("logger: open ~p -> ~p~n", [Name, R]), + ?format("wlr_logger: open ~p -> ~p~n", [Name, R]), Pid ! R, loop(); {open_ext, Pid, Name, File} -> R = disk_log:open([{name, Name}, {type, wrap}, {file, File}, {format, external}, {size, {?fsize, ?fno}}]), - ?format("logger: open ~p -> ~p~n", [Name, R]), + ?format("wlr_logger: open ~p -> ~p~n", [Name, R]), Pid ! R, loop(); {close, Pid, Name} -> R = disk_log:close(Name), - ?format("logger: close ~p -> ~p~n", [Name, R]), + ?format("wlr_logger: close ~p -> ~p~n", [Name, R]), Pid ! R, loop(); {sync, Pid, Name} -> R = disk_log:sync(Name), - ?format("logger: sync ~p -> ~p~n", [Name, R]), + ?format("wlr_logger: sync ~p -> ~p~n", [Name, R]), Pid ! R, loop(); {log_terms, Pid, Name, Terms} -> R = disk_log:log_terms(Name, Terms), - ?format("logger: log_terms ~p -> ~p~n", [Name, R]), + ?format("wlr_logger: log_terms ~p -> ~p~n", [Name, R]), Pid ! R, loop(); {blog_terms, Pid, Name, Terms} -> R = disk_log:blog_terms(Name, Terms), - ?format("logger: blog_terms ~p -> ~p~n", [Name, R]), + ?format("wlr_logger: blog_terms ~p -> ~p~n", [Name, R]), Pid ! R, loop(); exit -> - ?format("Stopping logger~n", []), + ?format("Stopping wlr_logger~n", []), exit(normal); _Else -> - ?format("logger: ignored: ~p~n", [_Else]), + ?format("wlr_logger: ignored: ~p~n", [_Else]), loop() end. -- cgit v1.2.3 From c54be0299219c32e5b66df952cf6d260ef2dadfa Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 25 Apr 2018 15:48:09 +0200 Subject: Set call timeout for logger_server to infinity This is to avoid failing starts when running make with '-j', i.e. maximum parallell make (or on extremely slow machines). --- lib/kernel/src/logger_h_common.hrl | 2 +- lib/kernel/src/logger_internal.hrl | 4 +++- lib/kernel/src/logger_simple.erl | 2 -- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_h_common.hrl b/lib/kernel/src/logger_h_common.hrl index f110bbaf97..89378dbb10 100644 --- a/lib/kernel/src/logger_h_common.hrl +++ b/lib/kernel/src/logger_h_common.hrl @@ -195,7 +195,7 @@ -define(file_datasync(DEVICE), file:datasync(DEVICE)). -define(disk_log_blog(LOG, DATA), disk_log:blog(LOG, DATA)). -define(disk_log_sync(LOG), disk_log:sync(LOG)). - -define(DEFAULT_CALL_TIMEOUT, ?DEFAULT_LOGGER_CALL_TIMEOUT-2000). + -define(DEFAULT_CALL_TIMEOUT, 10000). -endif. %%%----------------------------------------------------------------- diff --git a/lib/kernel/src/logger_internal.hrl b/lib/kernel/src/logger_internal.hrl index 8fcaba8de6..82df499c2b 100644 --- a/lib/kernel/src/logger_internal.hrl +++ b/lib/kernel/src/logger_internal.hrl @@ -23,7 +23,6 @@ -define(HANDLER_KEY,'$handler_config$'). -define(LOGGER_META_KEY,'$logger_metadata$'). -define(STANDARD_HANDLER, logger_std_h). --define(DEFAULT_LOGGER_CALL_TIMEOUT, 10000). -define(DEFAULT_HANDLER_FILTERS, ?DEFAULT_HANDLER_FILTERS([beam,erlang,otp])). -define(DEFAULT_HANDLER_FILTERS(Domain), @@ -40,9 +39,12 @@ -define(DEFAULT_FORMAT_TEMPLATE, [time," ",level,":\n",msg,"\n"]). +-define(DEFAULT_LOGGER_CALL_TIMEOUT, infinity). + -define(LOG_INTERNAL(Level,Report), case logger:allow(Level,?MODULE) of true -> + %% Spawn this to avoid deadlocks _ = spawn(logger,macro_log,[?LOCATION,Level,Report, logger:add_default_metadata(#{})]), ok; diff --git a/lib/kernel/src/logger_simple.erl b/lib/kernel/src/logger_simple.erl index 61387ac7c9..23ff6ccd2e 100644 --- a/lib/kernel/src/logger_simple.erl +++ b/lib/kernel/src/logger_simple.erl @@ -161,8 +161,6 @@ do_log(#{msg:={report,Report}, display_date(T), display_report(Type,Report); do_log(#{msg:=Msg,meta:=#{time:=T}}) -> - %% Maybe buffer event - if next handler shall be able to - %% re-display it display_date(T), display(Msg). -- cgit v1.2.3 From 992b57ad0a52f7a3ba945cbfaea53ce3ae3c3f5d Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 25 Apr 2018 18:49:09 +0200 Subject: Add chars_limit option to logger_formatter --- lib/kernel/doc/src/logger_formatter.xml | 14 +++- lib/kernel/src/logger_formatter.erl | 102 ++++++++++++++++++++--------- lib/kernel/test/logger_formatter_SUITE.erl | 63 +++++++++++++++++- 3 files changed, 142 insertions(+), 37 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger_formatter.xml b/lib/kernel/doc/src/logger_formatter.xml index 213a592e47..6a17e3641f 100644 --- a/lib/kernel/doc/src/logger_formatter.xml +++ b/lib/kernel/doc/src/logger_formatter.xml @@ -77,13 +77,21 @@ 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 - io:format/2 + io:format/2,3 in STDLIB.

    +

    chars_limit is a positive integer representing the + value of the option with the same name to be used when calling + io:format/3. 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 max_size.

    +

    max_size is a positive integer representing the maximum size a string returned from this formatter can - have. If the formatted string is longer, it will be - truncated.

    + have. If the formatted string is longer, after possibly + being limited by depth and/or chars_limit, it + will be truncated.

    utc is a boolean. If set to true, all dates are displayed in Universal Coordinated Time. Default diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl index 91283ab299..386e7832e2 100644 --- a/lib/kernel/src/logger_formatter.erl +++ b/lib/kernel/src/logger_formatter.erl @@ -34,6 +34,7 @@ Config :: #{single_line=>boolean(), legacy_header=>boolean(), report_cb=>fun((logger:report()) -> {io:format(),[term()]}), + chars_limit=>pos_integer()| unlimited, max_size=>pos_integer() | unlimited, depth=>pos_integer() | unlimited, template=>template(), @@ -43,19 +44,43 @@ format(#{level:=Level,msg:=Msg0,meta:=Meta},Config0) when is_map(Config0) -> Config = add_default_config(Config0), Meta1 = maybe_add_legacy_header(Level,Meta,Config), - MsgStr0 = format_msg(Msg0,Meta1,Config), + Template = maps:get(template,Config), + {BT,AT0} = lists:splitwith(fun(msg) -> false; (_) -> true end, Template), + {DoMsg,AT} = + case AT0 of + [msg|Rest] -> {true,Rest}; + _ ->{false,AT0} + end, + B = do_format(Level,"",Meta1,BT,Config), + A = do_format(Level,"",Meta1,AT,Config), MsgStr = - case maps:get(single_line,Config) of - true -> - %% Trim leading and trailing whitespaces, and replace - %% newlines with ", " - re:replace(string:trim(MsgStr0),",?\r?\n\s*",", ", - [{return,list},global]); - _false2 -> - MsgStr0 + if DoMsg -> + Config1 = + case maps:get(chars_limit,Config) of + unlimited -> + Config; + Size0 -> + Size = + case Size0 - string:length([B,A]) of + S when S>=0 -> S; + _ -> 0 + end, + Config#{chars_limit=>Size} + end, + MsgStr0 = format_msg(Msg0,Meta1,Config1), + case maps:get(single_line,Config) of + true -> + %% Trim leading and trailing whitespaces, and replace + %% newlines with ", " + re:replace(string:trim(MsgStr0),",?\r?\n\s*",", ", + [{return,list},global]); + _false -> + MsgStr0 + end; + true -> + "" end, - String = do_format(Level,MsgStr,Meta1,maps:get(template,Config),Config), - truncate(String,maps:get(max_size,Config)). + truncate(B ++ MsgStr ++ A,maps:get(max_size,Config)). do_format(Level,Msg,Data,[level|Format],Config) -> [to_string(level,Level,Config)|do_format(Level,Msg,Data,Format,Config)]; @@ -124,24 +149,25 @@ format_msg({report,Report},Meta,Config) -> format_msg({report,Report}, Meta#{report_cb=>fun logger:format_report/1}, Config); -format_msg(Msg,_Meta,Config) -> - limit_depth(Msg, maps:get(depth,Config)). - -limit_depth(Msg,false) -> - Depth = logger:get_format_depth(), - limit_depth(Msg,Depth); -limit_depth({Format,Args},unlimited) -> - try io_lib:format(Format,Args) +format_msg(Msg,_Meta,#{depth:=Depth,chars_limit:=CharsLimit}) -> + limit_size(Msg, Depth, CharsLimit). + +limit_size(Msg,Depth,unlimited) -> + limit_size(Msg,Depth,[]); +limit_size(Msg,Depth,CharsLimit) when is_integer(CharsLimit) -> + limit_size(Msg,Depth,[{chars_limit,CharsLimit}]); +limit_size({Format,Args},unlimited,Opts) when is_list(Opts) -> + try io_lib:format(Format,Args,Opts) catch _:_ -> - io_lib:format("FORMAT ERROR: ~tp - ~tp",[Format,Args]) + io_lib:format("FORMAT ERROR: ~tp - ~tp",[Format,Args],Opts) end; -limit_depth({Format0,Args},Depth) -> +limit_size({Format0,Args},Depth,Opts) when is_integer(Depth) -> try Format1 = io_lib:scan_format(Format0, Args), Format = limit_format(Format1, Depth), - io_lib:build_text(Format) + io_lib:build_text(Format,Opts) catch _:_ -> - limit_depth({"FORMAT ERROR: ~tp - ~tp",[Format0,Args]},Depth) + limit_size({"FORMAT ERROR: ~tp - ~tp",[Format0,Args]},Depth,Opts) end. limit_format([#{control_char:=C0}=M0|T], Depth) when C0 =:= $p; @@ -157,13 +183,15 @@ limit_format([], _) -> truncate(String,unlimited) -> String; -truncate(String,false) -> - Size = logger:get_max_size(), - truncate(String,Size); truncate(String,Size) -> Length = string:length(String), if Length>Size -> - string:slice(String,0,Size-3)++"..."; + case lists:reverse(lists:flatten(String)) of + [$\n|_] -> + string:slice(String,0,Size-4)++"...\n"; + _ -> + string:slice(String,0,Size-3)++"..." + end; true -> String end. @@ -232,13 +260,15 @@ month(12) -> "Dec". utcstr(#{utc:=true}) -> "UTC "; utcstr(_) -> "". -add_default_config(#{utc:=_}=Config) -> +add_default_config(#{utc:=_}=Config0) -> Default = #{legacy_header=>false, single_line=>false, - max_size=>false, - depth=>false}, - add_default_template(maps:merge(Default,Config)); + chars_limit=>unlimited}, + MaxSize = get_max_size(maps:get(max_size,Config0,false)), + Depth = get_depth(maps:get(depth,Config0,false)), + add_default_template(maps:merge(Default,Config0#{max_size=>MaxSize, + depth=>Depth})); add_default_config(Config) -> add_default_config(Config#{utc=>logger:get_utc_config()}). @@ -253,3 +283,13 @@ default_template(#{single_line:=true}) -> ?DEFAULT_FORMAT_TEMPLATE_SINGLE; default_template(_) -> ?DEFAULT_FORMAT_TEMPLATE. + +get_max_size(false) -> + logger:get_max_size(); +get_max_size(S) -> + max(10,S). + +get_depth(false) -> + logger:get_format_depth(); +get_depth(S) -> + max(5,S). diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl index 44e12d2d2a..ac1abba629 100644 --- a/lib/kernel/test/logger_formatter_SUITE.erl +++ b/lib/kernel/test/logger_formatter_SUITE.erl @@ -62,6 +62,7 @@ all() -> report_cb, max_size, depth, + chars_limit, format_mfa, format_time, level_or_msg_in_meta, @@ -307,12 +308,19 @@ max_size(_Config) -> []}, #{}, #{template=>Template}), - "1234567..." = + "123456789012..." = format(info,{"12345678901234567890",[]},#{},#{template=>Template, - max_size=>10}), + max_size=>15}), "12345678901234567890" = format(info,{"12345678901234567890",[]},#{},#{template=>Template, 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}), + "12345678901...\n" = + format(info,{"12345678901234567890",[]},#{},#{template=>[msg,"\n"], + max_size=>15}), ok. max_size(cleanup,_Config) -> application:unset_env(kernel,logger_max_size), @@ -354,6 +362,55 @@ depth(cleanup,_Config) -> application:unset_env(kernel,logger_format_depth), ok. +chars_limit(_Config) -> + FA = {"LoL: ~p~nL: ~p~nMap: ~p~n", + [lists:duplicate(10,lists:seq(1,100)), + lists:seq(1,100), + maps:from_list(lists:zip(lists:seq(1,100), + lists:duplicate(100,value)))]}, + Meta = #{time=>"2018-04-26 9:15:40.449879"}, + Template = [time," - ", msg, "\n"], + FC = #{template=>Template, + depth=>unlimited, + max_size=>unlimited, + chars_limit=>unlimited, + single_line=>true}, + CL1 = 80, + String1 = format(info,FA,Meta,FC#{chars_limit=>CL1}), + L1 = string:length(String1), + ct:log("String1: ~p~nLength1: ~p~n",[lists:flatten(String1),L1]), + true = L1 > CL1, + true = L1 < CL1 + 10, + + String2 = format(info,FA,Meta,FC#{chars_limit=>CL1,depth=>10}), + L2 = string:length(String2), + ct:log("String2: ~p~nLength2: ~p~n",[lists:flatten(String2),L2]), + String2 = String1, + + CL3 = 200, + String3 = format(info,FA,Meta,FC#{chars_limit=>CL3}), + L3 = string:length(String3), + ct:log("String3: ~p~nLength3: ~p~n",[lists:flatten(String3),L3]), + true = L3 > CL3, + true = L3 < CL3 + 10, + + String4 = format(info,FA,Meta,FC#{chars_limit=>CL3,depth=>10}), + L4 = string:length(String4), + ct:log("String4: ~p~nLength4: ~p~n",[lists:flatten(String4),L4]), + true = L4 > CL3, + true = L4 < CL3 + 10, + + %% Test that max_size truncates the string which is limited by + %% depth and chars_limit + MS5 = 150, + String5 = format(info,FA,Meta,FC#{chars_limit=>CL3,depth=>10,max_size=>MS5}), + L5 = string:length(String5), + ct:log("String5: ~p~nLength5: ~p~n",[String5,L5]), + L5 = MS5, + true = lists:prefix(lists:sublist(String5,L5-4),String4), + + ok. + format_mfa(_Config) -> Template = [mfa], @@ -443,7 +500,7 @@ faulty_config(_Config) -> faulty_msg(_Config) -> {error, function_clause, - {logger_formatter,_,[_,_],_}} = + {logger_formatter,_,_,_}} = ?TRY(logger_formatter:format(#{level=>info, msg=>term, meta=>#{time=>timestamp()}}, -- cgit v1.2.3 From 87fa7eab402cd93796a66ab648eab75909e17254 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 26 Apr 2018 15:13:45 +0200 Subject: Catch badarg in logger:get_format_depth/0 This env var lookup may fail during termination, when application_controller process has terminated before logger. --- lib/kernel/src/logger.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 7606912da4..943ef8c2d1 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -621,9 +621,13 @@ get_type_config(Type) -> -spec limit_term(term()) -> term(). limit_term(Term) -> - case get_format_depth() of + try get_format_depth() of unlimited -> Term; D -> io_lib:limit_term(Term, D) + catch error:badarg -> + %% This could happen during system termination, after + %% application_controller process is dead. + unlimited end. -spec get_format_depth() -> 'unlimited' | pos_integer(). -- cgit v1.2.3 From 5586959ed008c09141689f1e8865476150e48519 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Thu, 26 Apr 2018 09:37:24 +0200 Subject: Allow check for node name --- lib/kernel/src/dist_util.erl | 76 +++++++++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 25 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index 402d474c6d..ecc022b28d 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -645,7 +645,7 @@ recv_name(#hs_data{socket = Socket, f_recv = Recv} = HSData) -> end. is_node_name(OtherNodeName) -> - case string:split(OtherNodeName, "@") of + case string:split(OtherNodeName, "@", all) of [Name,Host] -> (not string:is_empty(Name)) andalso (not string:is_empty(Host)); @@ -654,24 +654,32 @@ is_node_name(OtherNodeName) -> end. split_node(Node) -> - case string:split(listify_node(Node), "@") of - [Name,Host] = Split -> - case - (not string:is_empty(Name)) - andalso (not string:is_empty(Host)) - of + Split = string:split(listify(Node), "@", all), + case Split of + [Name,Host] -> + case string:is_empty(Name) of true -> - {Name,Host}; + Split; false -> - Split + case string:is_empty(Host) of + true -> + {name,Name}; + false -> + {node,Name,Host} + end end; - Split -> - Split + [Host] -> + case string:is_empty(Host) of + true -> + Split; + false -> + {host,Host} + end end. -%% -%% check if connecting node is allowed to connect -%% with allow-node-scheme +%% Check if connecting node is allowed to connect +%% with allow-node-scheme. An empty allowed list +%% allows all nodes. %% is_allowed(#hs_data{allowed = []}, Flags, Node, Version) -> {Flags,list_to_atom(Node),Version}; @@ -686,43 +694,61 @@ is_allowed(#hs_data{allowed = Allowed} = HSData, Flags, Node, Version) -> ?shutdown2(Node, {is_allowed, not_allowed}) end. -%% Allow Node on Allowed node list, and also if host part -%% of Node matches Allowed list item. The Allowed list -%% contains node names or host names. +%% The allowed list can contain node names, host names +%% or names before '@', in atom or list form: +%% [node@host.example.org, "host.example.org", "node@"]. +%% An empty allowed list allows no nodes. +%% +%% Allow a node that matches any entry in the allowed list. +%% Also allow allowed entries as node to match, not from +%% this module; here the node has to be a valid name. %% is_allowed(_Node, []) -> false; -is_allowed(Node, [Node|_Allowed]) when is_atom(Node) -> +is_allowed(Node, [Node|_Allowed]) -> + %% Just an optimization true; is_allowed(Node, [AllowedNode|Allowed]) -> case split_node(AllowedNode) of - {AllowedName,AllowedHost} -> + {node,AllowedName,AllowedHost} -> %% Allowed node name case split_node(Node) of - {AllowedName,AllowedHost} -> + {node,AllowedName,AllowedHost} -> true; _ -> is_allowed(Node, Allowed) end; - [AllowedHost] -> + {host,AllowedHost} -> %% Allowed host name case split_node(Node) of - {_,AllowedHost} -> + {node,_,AllowedHost} -> %% Matching Host part true; - [AllowedHost] -> + {host,AllowedHost} -> %% Host matches Host true; _ -> is_allowed(Node, Allowed) end; + {name,AllowedName} -> + %% Allowed name before '@' + case split_node(Node) of + {node,AllowedName,_} -> + %% Matching Name part + true; + {name,AllowedName} -> + %% Name matches Name + true; + _ -> + is_allowed(Node, Allowed) + end; _ -> is_allowed(Node, Allowed) end. -listify_node(Atom) when is_atom(Atom) -> +listify(Atom) when is_atom(Atom) -> atom_to_list(Atom); -listify_node(Node) when is_list(Node) -> +listify(Node) when is_list(Node) -> Node. publish_type(Flags) -> -- cgit v1.2.3 From ee44197422710dabe43f2f4b80d1034aae9c916f Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Wed, 25 Apr 2018 21:18:08 +0200 Subject: Add documentation of the built-in logger handlers --- lib/kernel/doc/src/Makefile | 1 + lib/kernel/doc/src/logger_chapter.xml | 178 ++++++++++++++++++++++++++++++- lib/kernel/doc/src/logger_disk_log_h.xml | 146 +++++++++++++++++++++++++ lib/kernel/doc/src/logger_std_h.xml | 110 ++++++++++++++----- lib/kernel/doc/src/ref_man.xml | 1 + lib/kernel/doc/src/specs.xml | 1 + lib/kernel/src/logger_h_common.erl | 4 - 7 files changed, 406 insertions(+), 35 deletions(-) create mode 100644 lib/kernel/doc/src/logger_disk_log_h.xml (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile index 10bdf46119..82869d7b15 100644 --- a/lib/kernel/doc/src/Makefile +++ b/lib/kernel/doc/src/Makefile @@ -58,6 +58,7 @@ XML_REF3_FILES = application.xml \ init_stub.xml \ logger.xml \ logger_std_h.xml \ + logger_disk_log_h.xml \ logger_filters.xml \ logger_formatter.xml \ net_adm.xml \ diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 2a325453da..0bc3b37476 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -296,6 +296,7 @@

    + Handler configuration level @@ -577,8 +578,8 @@ log(#{msg:={F,A}},#{myhandler_fd:=Fd}) ->

    For examples of overload protection, please refer to the implementation of logger_std_h - and logger_disk_log_h - .

    + and logger_disk_log_h + .

    Below is a simpler example of a handler which logs through one single process, and uses the default formatter to gain a common @@ -632,6 +633,179 @@ my_report_cb(R) ->

    +
    + + Protecting the handler from overload +

    In order for the built-in handlers to survive, and stay responsive, + during periods of high load (i.e. when huge numbers of incoming + log requests must be handled), a mechanism for overload protection + has been implemented in the + logger_std_h + and logger_disk_log_h + handler. The mechanism, used by both handlers, works + as follows:

    + +
    + Message queue length +

    The handler process keeps track of the length of its message + queue and reacts in different ways depending on the current status. + The purpose is to keep the handler in, or (as quickly as possible), + get the handler into, a state where it can keep up with the pace + of incoming log requests. The memory usage of the handler must never + keep growing larger and larger, since that would eventually cause the + handler to crash. Three thresholds with associated actions have been + defined:

    + + + toggle_sync_qlen + +

    The default value of this level is 10 messages, + and as long as the length of the message queue is lower, all log + requests are handled asynchronously. This simply means that the + process sending the log request (by calling a log function in the + logger API) does not wait for a response from the handler but + continues executing immediately after the request (i.e. it will not + be affected by the time it takes the handler to print to the log + device). If the message queue grows larger than this value, however, + the handler starts handling the log requests synchronously instead, + meaning the process sending the request will have to wait for a + response. When the handler manages to reduce the message queue to a + level below the toggle_sync_qlen threshold, asynchronous + operation is resumed. The switch from asynchronous to synchronous + mode will force the logging tempo of few busy senders to slow down, + but can not protect the handler sufficiently in situations of many + concurrent senders.

    +
    + drop_new_reqs_qlen + +

    When the message queue has grown larger than this threshold, which + defaults to 200 messages, the handler switches to a mode in + which it drops any new requests being made. Dropping a message in + this state means that the log function never actually sends a message + to the handler. The log call simply returns without an action. When + the length of the message queue has been reduced to a level below this + threshold, synchronous or asynchronous request handling mode is + resumed.

    +
    + flush_reqs_qlen + +

    Above this threshold, which defaults to 1000 messages, a + flush operation takes place, in which all messages buffered in the + process mailbox get deleted without any logging actually taking + place. (Processes waiting for a response from a synchronous log request + will receive a reply indicating that the request has been dropped).

    +
    +
    + +

    For the overload protection algorithm to work properly, it is a + requirement that:

    + +

    toggle_sync_qlen < drop_new_reqs_qlen < flush_reqs_qlen

    + +

    During high load scenarios, the length of the handler message queue + rarely grows in a linear and predictable way. Instead, whenever the + handler process gets scheduled in, it can have an almost arbitrary number + of messages waiting in the mailbox. It's for this reason that the overload + protection mechanism is focused on acting quickly and quite drastically + (such as immediately dropping or flushing messages) as soon as a large + queue length is detected.

    + +

    The thresholds listed above may be modified by the user if, e.g, a handler + shouldn't drop or flush messages unless the message queue length grows + extremely large. (The handler must be allowed to use large amounts of memory + under such circumstances however). Another example of when the user might want + to change the settings is if, for performance reasons, the logging processes must + never get blocked by synchronous log requests, while dropping or flushing requests + is perfectly acceptable (since it doesn't affect the performance of the + loggers).

    + +

    A configuration example:

    + +logger:add_handler(my_standard_h, logger_std_h, + #{logger_std_h => + #{type => {file,"./system_info.log"}, + toggle_sync_qlen => 100, + drop_new_reqs_qlen => 1000, + flush_reqs_qlen => 2000}}). + +
    + +
    + Controlling bursts of log requests +

    A potential problem with large bursts of log requests, is that log files + may get full or wrapped too quickly (in the latter case overwriting + previously logged data that could be of great importance). For this reason, + both built-in handlers offer the possibility to set a maximum level of how + many requests to process with a certain time frame. With this burst control + feature enabled, the handler will take care of bursts of log requests + without choking log files, or the console, with massive amounts of + printouts. These are the configuration parameters:

    + + + enable_burst_limit + +

    This is set to true by default. The value false + disables the burst control feature.

    +
    + burst_limit_size + +

    This is how many requests should be processed within the + burst_window_time time frame. After this maximum has been + reached, successive requests will be dropped until the end of the + time frame. The default value is 500 messages.

    +
    + burst_window_time + +

    The default window is 1000 milliseconds long.

    +
    +
    + +

    A configuration example:

    + +logger:add_handler(my_disk_log_h, logger_disk_log_h, + #{disk_log_opts => + #{file => "./my_disk_log"}, + logger_disk_log_h => + #{burst_limit_size => 10, + burst_window_time => 500}}). + +
    + +
    + Terminating a large handler +

    A handler process may grow large even if it can manage peaks of high load + without crashing. The overload protection mechanism includes user configurable + levels for a maximum allowed message queue length and maximum allowed memory + usage. This feature is disabled by default, but can be switched on by means + of the following configuration parameters:

    + + + enable_kill_overloaded + +

    This is set to false by default. The value true + enables the feature.

    +
    + handler_overloaded_qlen + +

    This is the maximum allowed queue length. If the mailbox grows larger + than this, the handler process gets terminated.

    +
    + handler_overloaded_mem + +

    This is the maximum allowed memory usage of the handler process. If + the handler grows any larger, the process gets terminated.

    +
    + handler_restart_after + +

    If the handler gets terminated because of its queue length or + memory usage, it can get automatically restarted again after a + configurable delay time. The time is specified in milliseconds + and 5000 is the default value. The value never can + also be set, which prevents a restart.

    +
    +
    +
    +
    See Also diff --git a/lib/kernel/doc/src/logger_disk_log_h.xml b/lib/kernel/doc/src/logger_disk_log_h.xml new file mode 100644 index 0000000000..90cc4fec30 --- /dev/null +++ b/lib/kernel/doc/src/logger_disk_log_h.xml @@ -0,0 +1,146 @@ + + + + +
    + + 2017 + Ericsson AB. 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. + + + + logger_disk_log_h + + + + + + + A + logger_disk_log_h.xml +
    + logger_disk_log_h + A disk_log based handler for the Logger + application. + + +

    This is a handler for the Logger application that offers circular + (wrapped) logs by using the disk_log application. Multiple instances + of this handler can be added to logger, and each instance will print to + its own disk_log file, created with the name and settings specified in + the handler configuration.

    +

    The default standard handler, + logger_std_h, can be + replaced by a disk_log handler at startup of the kernel application. + See an example of this below.

    +

    The handler has an overload protection mechanism that will keep the handler + process and the kernel application alive during a high load of log + requests. How this feature works, and how to modify the configuration, + is described in the + User's Guide + .

    +

    To add a new instance of the disk_log handler, use + logger:add_handler/3 + . The handler configuration argument is a map which may contain + general configuration parameters, as documented in the + User's Guide + , as well as handler specific parameters.

    +

    The settings for the disk_log log file should be specified with the + key disk_log_opts. These settings are a subset of the disk_log + datatype + dlog_option().

    +

    Parameters in the disk_log_opts map:

    + + file + This is the full name of the disk_log log file. + type + This is the disk_log type, wrap or halt. The + default value is wrap. + max_no_files + This is the maximum number of files that disk_log will use + for its circular logging. The default value is 10. (The setting + has no effect on a halt log). + max_no_bytes + This is the maximum number of bytes that will be written to + a log file before disk_log proceeds with the next file in order (or + generates an error in case of a full halt log). The default value for + a wrap log is 1048576 bytes, and infinity for a halt + log. + +

    Specific configuration for the handler (represented as a sub map) + is specified with the key logger_disk_log_h. It may contain the + following parameter:

    + + filesync_repeat_interval + +

    This value (in milliseconds) specifies how often the handler will + do a disk_log sync operation in order to make sure that buffered data + gets written to disk. The handler will repeatedly attempt this + operation, but only perform it if something has actually been logged + since the last sync. The default value is 5000 milliseconds. + If no_repeat is set as value, the repeated sync operation is + disabled. The user can also call the + disk_log_sync/1 + function to perform a disk_log sync.

    +
    +

    There are a number of other configuration parameters available, that are + to be used for customizing the overload protection behaviour. The same + parameters are used both in the standard handler and the disk_log handler, + and are documented in the + User's Guide + .

    +

    Note that when changing the configuration of the handler in runtime, by + calling + logger:set_handler_config/2 + or logger:set_handler_config/3, the disk_log_opts + settings may not be modified.

    +

    Example of adding a disk_log handler:

    + +logger:add_handler(my_disk_log_h, logger_disk_log_h, + #{level => error, + filter_default => log, + disk_log_opts => + #{file => "./my_disk_log", + type => wrap, + max_no_files => 4, + max_no_bytes => 10000}, + logger_disk_log_h => + #{filesync_repeat_interval => 1000}}). + +

    In order to use the disk_log handler instead of the default standard + handler when starting en Erlang node, use the kernel configuration parameter + logger_dest with + value {disk_log,FileName}. Example:

    + +erl -kernel logger_dest '{disk_log,"./system_disk_log"}' + +
    + + + + + + Writes buffered data to disk. + +

    Write buffered data to disk.

    +
    +
    + +
    + +
    + + diff --git a/lib/kernel/doc/src/logger_std_h.xml b/lib/kernel/doc/src/logger_std_h.xml index 2a368b16eb..fe9b9ca5a9 100644 --- a/lib/kernel/doc/src/logger_std_h.xml +++ b/lib/kernel/doc/src/logger_std_h.xml @@ -39,42 +39,94 @@

    This is the default handler for the Logger application. Multiple instances of this handler can be added to logger, and each instance will print logs to standard_io, - standard_error or to a file.

    - -

    To add a new instance, - use logger:add_handler/3 + standard_error or to file. The default instance that starts + with kernel is named logger_std_h - which is the name to be used + for reconfiguration.

    +

    The handler has an overload protection mechanism that will keep the handler + process and the kernel application alive during a high load of log + requests. How this feature works, and how to modify the configuration, + is described in the + User's Guide .

    - -

    The handler configuration may contain the following keys, - associated with values as described:

    +

    To add a new instance of the standard handler, use + logger:add_handler/3 + . The handler configuration argument is a map which may contain + general configuration parameters, as documented in the + User's Guide + , as well as handler specific parameters. The specific parameters + are stored in a sub map with the key logger_std_h. The following + keys and values may be specified:

    - filters - -

    A list of {Id,{Fun,Args}}, each representing a filter - that may selct or modify log events to forward to this - handler.

    - filter_default - -

    The atom log or stop, specifying what to - do with a log event if all filters - return ignore.

    - formatter + type -

    {Module,Extra}, - where Module:format(Log,Extra) will be called by - the handler to produce the string that will be printed to - the handler's destination.

    - level +

    This will have the value standard_io, standard_error, + {file,LogFileName}, or {file,LogFileName,LogFileOpts}, + where standard_io is the default value for type. It's recommended + to not specify LogFileOpts if not absolutely necessary. The + default options used by the handler to open a file for logging are: + raw, append and delayed_write. The standard + handler does not have support for circular logging. Use the + logger_disk_log_h + handler for this.

    + filesync_repeat_interval -

    The level of log events that logger shall forward to - this handler. Log events of the specified, or more severe - levels, are forwarded.

    +

    This value (in milliseconds) specifies how often the handler will + do a file sync operation in order to make sure that buffered data gets + written to disk. The handler will repeatedly attempt this + operation, but only perform it if something has actually been logged + since the last sync. The default value is 5000 milliseconds. + If no_repeat is set as value, the repeated file sync operation + is disabled, and it will be the operating system settings that determine + how quickly or slowly data gets written to disk. The user can also call + the filesync/1 + function to perform a file sync.

    - +

    There are a number of other configuration parameters available, that are + to be used for customizing the overload protection behaviour. The same + parameters are used both in the standard handler and the disk_log handler, + and are documented in the + User's Guide + .

    +

    Note that when changing the configuration of the handler in runtime, by + calling + logger:set_handler_config/2 + , or + logger:set_handler_config/3 + , + the type parameter may not be modified.

    +

    Example of adding a standard handler:

    + +logger:add_handler(my_standard_h, logger_std_h, + #{level => info, + filter_default => log, + logger_std_h => + #{type => {file,"./system_info.log"}, + filesync_repeat_interval => 1000}}). + +

    In order to configure the default handler (that starts initially with + the kernel application) to log to file instead of standard_io, + use the kernel configuration parameter + logger_dest with + value {file,FileName}. Example:

    + +erl -kernel logger_dest '{file,"./erl.log"}' + +

    An example of how to replace the standard handler with a disk_log handler + at startup can be found in the manual of + logger_disk_log_h.

    - + + + + + Writes buffered data to disk. + +

    Write buffered data to disk.

    +
    +
    + +
    diff --git a/lib/kernel/doc/src/ref_man.xml b/lib/kernel/doc/src/ref_man.xml index 7f4d5b384f..c06914d23d 100644 --- a/lib/kernel/doc/src/ref_man.xml +++ b/lib/kernel/doc/src/ref_man.xml @@ -56,6 +56,7 @@ + diff --git a/lib/kernel/doc/src/specs.xml b/lib/kernel/doc/src/specs.xml index a67e26ef16..bcc422930e 100644 --- a/lib/kernel/doc/src/specs.xml +++ b/lib/kernel/doc/src/specs.xml @@ -24,6 +24,7 @@ + diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl index 7a5082fb84..7caad366ae 100644 --- a/lib/kernel/src/logger_h_common.erl +++ b/lib/kernel/src/logger_h_common.erl @@ -133,10 +133,6 @@ call_cast_or_drop(Name, Bin) -> dropped -> ok catch _:{timeout,_} -> - %%! --- Tue Mar 6 23:10:48 2018 --- peppe was here! - erlang:display('CLIENT_TIMEOUT'), - %% exit('CLIENT_TIMEOUT'), - ?observe(Name,{dropped,1}) end; drop -> ?observe(Name,{dropped,1}) -- cgit v1.2.3 From c0e4e821312d792aef092803f0d8b70409a38263 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 23 Oct 2017 14:22:35 +0200 Subject: Update appups in kernel, stdlib and sasl for OTP-21.0 --- lib/kernel/src/kernel.appup.src | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src index 4ee497bbbd..305a1c788c 100644 --- a/lib/kernel/src/kernel.appup.src +++ b/lib/kernel/src/kernel.appup.src @@ -18,9 +18,11 @@ %% %CopyrightEnd% {"%VSN%", %% Up from - max one major revision back - [{<<"5\\.[0-3](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*, OTP-20.0 - {<<"5\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-20.1+ + [{<<"5\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-20.0 + {<<"5\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-20.1+ + {<<"6\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-21 %% Down to - max one major revision back - [{<<"5\\.[0-3](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*, OTP-20.0 - {<<"5\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-20.1+ + [{<<"5\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-20.0 + {<<"5\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-20.1+ + {<<"6\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-21 }. -- cgit v1.2.3 From 345f7f527a4c26ef49cef0d81e2c8b71bf01ebc3 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Mon, 30 Apr 2018 10:03:17 +0200 Subject: Update version numbers --- lib/kernel/vsn.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 60a1b0bff8..aa8e4dc119 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 5.4.3 +KERNEL_VSN = 6.0 -- cgit v1.2.3 From 202bb737e3deabfebee683266f4b7c42781eb521 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Mon, 30 Apr 2018 10:06:42 +0200 Subject: Update release notes --- lib/kernel/doc/src/notes.xml | 192 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 09844f1502..b02545877e 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,198 @@

    This document describes the changes made to the Kernel application.

    +
    Kernel 6.0 + +
    Fixed Bugs and Malfunctions + + +

    Clarify the documentation of rpc:multicall/5. +

    +

    + Own Id: OTP-10551

    +
    + +

    + The DNS resolver when getting econnrefused from a server + retained an invalid socket so look up towards the next + server(s) also failed.

    +

    + Own Id: OTP-13133 Aux Id: PR-1557

    +
    + +

    + No resolver backend returns V4Mapped IPv6 addresses any + more. This was inconsistent before, some did, some did + not. To facilitate working with such addresses a new + function inet:ipv4_mapped_ipv6_address/1 has been + added.

    +

    + *** POTENTIAL INCOMPATIBILITY ***

    +

    + Own Id: OTP-13761 Aux Id: ERL-503

    +
    + +

    + The type specifications for file:posix/0 and + inet:posix/0 have been updated according to which + errors file and socket operations should be able to + return.

    +

    + Own Id: OTP-14019 Aux Id: ERL-550

    +
    + +

    + Fix name resolving in IPv6 only environments when doing + the initial distributed connection.

    +

    + Own Id: OTP-14501

    +
    + +

    File operations used to accept filenames + containing null characters (integer value zero). This + caused the name to be truncated and in some cases + arguments to primitive operations to be mixed up. + Filenames containing null characters inside the filename + are now rejected and will cause primitive file + operations to fail.

    Also environment variable + operations used to accept names and + values of + environment variables containing null characters (integer + value zero). This caused operations to silently produce + erroneous results. Environment variable names and values + containing null characters inside the name or value are + now rejected and will cause environment variable + operations to fail.

    Primitive environment + variable operations also used to accept the $= + character in environment variable names causing various + problems. $= characters in environment variable + names are now also rejected.

    Also + os:cmd/1 now + reject null characters inside its command. +

    erlang:open_port/2 + will also reject null characters inside the port name + from now on.

    +

    + *** POTENTIAL INCOMPATIBILITY ***

    +

    + Own Id: OTP-14543 Aux Id: ERL-370

    +
    + +

    os:putenv and os:getenv no longer access + the process environment directly and instead work on a + thread-safe emulation. The only observable difference is + that it's not kept in sync with libc + getenv(3) / putenv(3), so those who relied + on that behavior in drivers or NIFs will need to add + manual synchronization.

    On Windows this means that + you can no longer resolve DLL dependencies by modifying + the PATH just before loading the driver/NIF. To + make this less of a problem, the emulator now adds the + target DLL's folder to the DLL search path.

    +

    + *** POTENTIAL INCOMPATIBILITY ***

    +

    + Own Id: OTP-14666

    +
    + +

    + Fixed connection tick toward primitive hidden nodes + (erl_interface) that could cause faulty tick timeout in + rare cases when payload data is sent to hidden node but + not received.

    +

    + Own Id: OTP-14681

    +
    + +

    + Make group react immediately on an EXIT-signal from shell + in e.g ssh.

    +

    + Own Id: OTP-14991 Aux Id: PR1705

    +
    + +

    + Calls to gen_tcp:send/2 on closed sockets now returns + {error, closed} instead of + {error,enotconn}.

    +

    + Own Id: OTP-15001

    +
    +
    +
    + + +
    Improvements and New Features + + +

    + A new logging API is added to OTP. This is implemented in + the Kernel application, module logger.

    +

    + Legacy calls to error_logger will be automatically + redirected to the new API.

    +

    + See the reference manual for module logger, and the + User's Guide for the Kernel application for more + information.

    +

    + Own Id: OTP-13295

    +
    + +

    + The function inet:i/0 has been documented.

    +

    + Own Id: OTP-13713 Aux Id: PR-1645

    +
    + +

    + New functionality for implementation of alternative + carriers for the Erlang distribution has been introduced. + This mainly consists of support for usage of distribution + controller processes (previously only ports could be used + as distribution controllers). For more information see + ERTS + User's Guide ➜ How to implement an Alternative Carrier + for the Erlang Distribution ➜ Distribution + Module.

    +

    + Own Id: OTP-14459

    +
    + +

    seq_trace labels may now be any erlang + term.

    +

    + Own Id: OTP-14899

    +
    + +

    + The SSL distribution protocol -proto inet_tls has + stopped setting the SSL option + server_name_indication. New verify funs for client + and server in inet_tls_dist has been added, not + documented yet, that checks node name if present in peer + certificate. Usage is still also yet to be documented.

    +

    + Own Id: OTP-14969 Aux Id: OTP-14465, ERL-598

    +
    + +

    + Changed timeout of gen_server calls to auth + server from default 5 seconds to infinity.

    +

    + Own Id: OTP-15009 Aux Id: ERL-601

    +
    +
    +
    + +
    +
    Kernel 5.4.3
    Fixed Bugs and Malfunctions -- cgit v1.2.3 From ff819e79274c4638c9c9cf77ba4f45f24a0a2ab0 Mon Sep 17 00:00:00 2001 From: Henrik Date: Wed, 2 May 2018 09:18:58 +0200 Subject: Revert "Update version numbers" This reverts commit 345f7f527a4c26ef49cef0d81e2c8b71bf01ebc3. --- lib/kernel/vsn.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index aa8e4dc119..60a1b0bff8 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 6.0 +KERNEL_VSN = 5.4.3 -- cgit v1.2.3 From 561e871d680a081d531ff205b8331eacca1d7a3c Mon Sep 17 00:00:00 2001 From: Henrik Date: Wed, 2 May 2018 09:19:05 +0200 Subject: Revert "Update release notes" This reverts commit 202bb737e3deabfebee683266f4b7c42781eb521. --- lib/kernel/doc/src/notes.xml | 192 ------------------------------------------- 1 file changed, 192 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index b02545877e..09844f1502 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,198 +31,6 @@

    This document describes the changes made to the Kernel application.

    -
    Kernel 6.0 - -
    Fixed Bugs and Malfunctions - - -

    Clarify the documentation of rpc:multicall/5. -

    -

    - Own Id: OTP-10551

    -
    - -

    - The DNS resolver when getting econnrefused from a server - retained an invalid socket so look up towards the next - server(s) also failed.

    -

    - Own Id: OTP-13133 Aux Id: PR-1557

    -
    - -

    - No resolver backend returns V4Mapped IPv6 addresses any - more. This was inconsistent before, some did, some did - not. To facilitate working with such addresses a new - function inet:ipv4_mapped_ipv6_address/1 has been - added.

    -

    - *** POTENTIAL INCOMPATIBILITY ***

    -

    - Own Id: OTP-13761 Aux Id: ERL-503

    -
    - -

    - The type specifications for file:posix/0 and - inet:posix/0 have been updated according to which - errors file and socket operations should be able to - return.

    -

    - Own Id: OTP-14019 Aux Id: ERL-550

    -
    - -

    - Fix name resolving in IPv6 only environments when doing - the initial distributed connection.

    -

    - Own Id: OTP-14501

    -
    - -

    File operations used to accept filenames - containing null characters (integer value zero). This - caused the name to be truncated and in some cases - arguments to primitive operations to be mixed up. - Filenames containing null characters inside the filename - are now rejected and will cause primitive file - operations to fail.

    Also environment variable - operations used to accept names and - values of - environment variables containing null characters (integer - value zero). This caused operations to silently produce - erroneous results. Environment variable names and values - containing null characters inside the name or value are - now rejected and will cause environment variable - operations to fail.

    Primitive environment - variable operations also used to accept the $= - character in environment variable names causing various - problems. $= characters in environment variable - names are now also rejected.

    Also - os:cmd/1 now - reject null characters inside its command. -

    erlang:open_port/2 - will also reject null characters inside the port name - from now on.

    -

    - *** POTENTIAL INCOMPATIBILITY ***

    -

    - Own Id: OTP-14543 Aux Id: ERL-370

    -
    - -

    os:putenv and os:getenv no longer access - the process environment directly and instead work on a - thread-safe emulation. The only observable difference is - that it's not kept in sync with libc - getenv(3) / putenv(3), so those who relied - on that behavior in drivers or NIFs will need to add - manual synchronization.

    On Windows this means that - you can no longer resolve DLL dependencies by modifying - the PATH just before loading the driver/NIF. To - make this less of a problem, the emulator now adds the - target DLL's folder to the DLL search path.

    -

    - *** POTENTIAL INCOMPATIBILITY ***

    -

    - Own Id: OTP-14666

    -
    - -

    - Fixed connection tick toward primitive hidden nodes - (erl_interface) that could cause faulty tick timeout in - rare cases when payload data is sent to hidden node but - not received.

    -

    - Own Id: OTP-14681

    -
    - -

    - Make group react immediately on an EXIT-signal from shell - in e.g ssh.

    -

    - Own Id: OTP-14991 Aux Id: PR1705

    -
    - -

    - Calls to gen_tcp:send/2 on closed sockets now returns - {error, closed} instead of - {error,enotconn}.

    -

    - Own Id: OTP-15001

    -
    -
    -
    - - -
    Improvements and New Features - - -

    - A new logging API is added to OTP. This is implemented in - the Kernel application, module logger.

    -

    - Legacy calls to error_logger will be automatically - redirected to the new API.

    -

    - See the reference manual for module logger, and the - User's Guide for the Kernel application for more - information.

    -

    - Own Id: OTP-13295

    -
    - -

    - The function inet:i/0 has been documented.

    -

    - Own Id: OTP-13713 Aux Id: PR-1645

    -
    - -

    - New functionality for implementation of alternative - carriers for the Erlang distribution has been introduced. - This mainly consists of support for usage of distribution - controller processes (previously only ports could be used - as distribution controllers). For more information see - ERTS - User's Guide ➜ How to implement an Alternative Carrier - for the Erlang Distribution ➜ Distribution - Module.

    -

    - Own Id: OTP-14459

    -
    - -

    seq_trace labels may now be any erlang - term.

    -

    - Own Id: OTP-14899

    -
    - -

    - The SSL distribution protocol -proto inet_tls has - stopped setting the SSL option - server_name_indication. New verify funs for client - and server in inet_tls_dist has been added, not - documented yet, that checks node name if present in peer - certificate. Usage is still also yet to be documented.

    -

    - Own Id: OTP-14969 Aux Id: OTP-14465, ERL-598

    -
    - -

    - Changed timeout of gen_server calls to auth - server from default 5 seconds to infinity.

    -

    - Own Id: OTP-15009 Aux Id: ERL-601

    -
    -
    -
    - -
    -
    Kernel 5.4.3
    Fixed Bugs and Malfunctions -- cgit v1.2.3 From 425f51c3022b9b96f2f870ac931c5946cb08bb54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Wed, 2 May 2018 17:28:33 +0200 Subject: Add file_int.hrl to release target --- lib/kernel/src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile index 0bc9f121a0..024aa3a2f0 100644 --- a/lib/kernel/src/Makefile +++ b/lib/kernel/src/Makefile @@ -135,7 +135,7 @@ HRL_FILES= ../include/file.hrl ../include/inet.hrl ../include/inet_sctp.hrl \ ../include/net_address.hrl INTERNAL_HRL_FILES= application_master.hrl disk_log.hrl \ - erl_epmd.hrl hipe_ext_format.hrl \ + erl_epmd.hrl file_int.hrl hipe_ext_format.hrl \ inet_dns.hrl inet_res.hrl \ inet_boot.hrl inet_config.hrl inet_int.hrl \ inet_dns_record_adts.hrl -- cgit v1.2.3 From 65cd0e43158e38a3c42b00e7f591723226d235fb Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 2 May 2018 17:14:26 +0200 Subject: kernel: Use formatter in simple logger example --- lib/kernel/doc/src/logger_chapter.xml | 37 +++++++---------------------------- 1 file changed, 7 insertions(+), 30 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 0bc3b37476..88dcfbe8d9 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -539,12 +539,8 @@ changing_config(logger:handler_id(),logger:config(),logger:config()) -> {ok,logg -module(myhandler). -export([log/2]). -log(#{msg:={report,R}},_) -> - io:format("~p~n",[R]); -log(#{msg:={string,S}},_) -> - io:put_chars(S); -log(#{msg:={F,A}},_) -> - io:format(F,A). +log(Log,#{formatter:={FModule,FConfig}) -> + io:put_chars(FModule:format(Log,FConfig)).

    A simple handler which prints to file could be implemented like @@ -562,18 +558,13 @@ removing_handler(Id,#{myhandler_fd:=Fd}) -> _ = file:close(Fd), ok. -log(#{msg:={report,R}},#{myhandler_fd:=Fd}) -> - io:format(Fd,"~p~n",[R]); -log(#{msg:={string,S}},#{myhandler_fd:=Fd}) -> - io:put_chars(Fd,S); -log(#{msg:={F,A}},#{myhandler_fd:=Fd}) -> - io:format(Fd,F,A). +log(Log,#{myhandler_fd:=Fd,formatter:={FModule,FConfig}}) -> + io:put_chars(Fd,FModule:format(Log,FConfig)). -

    Note that none of the above handlers have any overload +

    The above handlers do not have any overload protection, and all log events are printed directly from the - client process. Neither do the handlers use the formatter or - in any way add time or other metadata to the printed events.

    + client process.

    For examples of overload protection, please refer to the implementation @@ -582,14 +573,7 @@ log(#{msg:={F,A}},#{myhandler_fd:=Fd}) -> .

    Below is a simpler example of a handler which logs through one - single process, and uses the default formatter to gain a common - look of the log events.

    -

    It also uses the metadata field report_cb, if it exists, - to print reports in the way the event issuer suggests. The - formatter will normally do this, but if the handler either has - an own default (as in this example) or if the - given report_cb should not be used at all, then the - handler must take care of this itself.

    + single process.

    -module(myhandler). -export([adding_handler/2, removing_handler/1, log/2]). @@ -620,16 +604,9 @@ terminate(Reason,#{fd:=Fd}) -> _ = file:close(Fd), ok. -do_log(Fd,#{msg:={report,R}} = Log, Config) -> - Fun = maps:get(report_cb,Config,fun my_report_cb/1, - {F,A} = Fun(R), - do_log(Fd,Log#{msg=>{F,A},Config); do_log(Fd,Log,#{formatter:={FModule,FConfig}}) -> String = FModule:format(Log,FConfig), io:put_chars(Fd,String). - -my_report_cb(R) -> - {"~p",[R]}.
    -- cgit v1.2.3 From 2ea3d7b972141ac73a1dd7f0ac1a3ccbde342f6d Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 30 Apr 2018 13:58:34 +0200 Subject: Set single_line=true by default in logger_formatter --- lib/kernel/src/logger_formatter.erl | 2 +- lib/kernel/src/logger_internal.hrl | 1 + lib/kernel/test/logger_formatter_SUITE.erl | 46 ++++++++++++++++-------------- 3 files changed, 26 insertions(+), 23 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl index 386e7832e2..5358652692 100644 --- a/lib/kernel/src/logger_formatter.erl +++ b/lib/kernel/src/logger_formatter.erl @@ -263,7 +263,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/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. -- cgit v1.2.3 From 0d3b793ac551f9c69f9e1c0c5a76186386f3cd04 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 2 May 2018 11:50:49 +0200 Subject: Update logger documentation --- lib/kernel/doc/src/kernel_app.xml | 30 ++--- lib/kernel/doc/src/logger.xml | 6 +- lib/kernel/doc/src/logger_chapter.xml | 5 +- lib/kernel/doc/src/logger_formatter.xml | 217 +++++++++++++++++++------------- 4 files changed, 146 insertions(+), 112 deletions(-) (limited to 'lib/kernel') 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 @@

    This configuration parameter is used both for the global logger level, and for the standard handler started by the Kernel application (see logger_dest variable above).

    -

    The default value is info

    +

    The default value is info.

    logger_disk_log_type = halt | wrap @@ -251,14 +251,14 @@ logger_disk_log_maxbytes = 1048576

    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 sasl_error_logger + named sasl_h, according to values of the SASL + environment variables sasl_error_logger and sasl_errlog_type, see SASL(6)

    -

    The default value is false

    +

    The default value is false.

    See chapter Backwards compatibility with error_logger for more information about handling of the so called SASL reports.

    @@ -271,7 +271,7 @@ logger_disk_log_maxbytes = 1048576 reports from supervisor and application_controller shall be logged or not.

    -

    If logger_sasl_compatible = false, +

    If logger_sasl_compatible = true, then logger_log_progress is ignored.

    @@ -280,14 +280,6 @@ logger_disk_log_maxbytes = 1048576

    Can be used to limit the size of the formatted output from the logger handlers.

    -

    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.

    -

    Depth 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 logger_max_size = integer() | unlimited -

    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.

    +

    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.

    logger_utc = boolean() diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index 66e6e5c689..ded9c1fb37 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -131,8 +131,8 @@ #{mfa=>{?MODULE,?FUNCTION_NAME,?FUNCTION_ARITY}, - file=>?FILE, - line=>?LINE} + file=>?FILE, + line=>?LINE}

    The call is wrapped in a case statement and will be evaluated @@ -297,7 +297,7 @@ print Pretty print all the current logger configuration to standard out. Example: - logger:i(). + logger:i(print). Current logger configuration: Level: info FilterDefault: log diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 88dcfbe8d9..78d6f46c95 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -488,8 +488,9 @@ error_logger:add_report_handler/1,2. level => debug} 2> logger:add_handler(debug_handler,logger_std_h,Config). ok -

    By default, the handler receives all events, so we need to add a filter - to stop all non-debug events:

    +

    By default, the handler receives all events + (filter_defalt=log), so we need to add a filter to stop + all non-debug events:

     3> Fun = fun(#{level:=debug}=Log,_) -> Log; (_,_) -> stop end.
     #Fun<erl_eval.12.98642416>
    diff --git a/lib/kernel/doc/src/logger_formatter.xml b/lib/kernel/doc/src/logger_formatter.xml
    index 6a17e3641f..a0940100ee 100644
    --- a/lib/kernel/doc/src/logger_formatter.xml
    +++ b/lib/kernel/doc/src/logger_formatter.xml
    @@ -37,116 +37,157 @@
     
       
         

    Default formatter for the Logger application.

    -
    - - - - - - - - - - - - - Formats the given message. - -

    Formats the given message.

    -

    The template is a list of atoms, tuples and strings. Atoms - can be level or msg, 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 {key1,key2} will be replaced by the value of - the key2 field in this nested map (the value vill be - converted to a string):

    - - -#{key1=>#{key2=>my_value, - ...}, - ...} - - -

    Strings are printed literally.

    - -

    depth 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 - io:format/2,3 - in STDLIB.

    - -

    chars_limit is a positive integer representing the - value of the option with the same name to be used when calling - io:format/3. 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 max_size.

    - -

    max_size 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 depth and/or chars_limit, it - will be truncated.

    - -

    utc is a boolean. If set to true, all dates are - displayed in Universal Coordinated Time. Default - is false.

    -

    report_cb must be a function with arity 1, - returning {Format,Args}. This function will replace - any report_cb found in metadata.

    - -

    If single_line=true, all newlines in the message are - replaced with ", ", and whitespaces following directly - after newlines are removed. Note that newlines added by the - formatter template are not replaced.

    + -

    If legacy_header=true a header field is added to +

    + Configuration +

    The following configuration parameters can be set + for logger_formatter:

    + + single_line = boolean() + +

    If set to true, all newlines in the message are + replaced with ", ", and whitespaces following + directly after newlines are removed. Note that newlines + added by the formatter template are not replaced.

    +

    Default is true.

    +
    + legacy_header = boolen() + +

    If set to true a header field is added to logger_formatter's part of Metadata. The value of this field is a string similar to the header created by the old error_logger event handlers. It can be included in the log event by adding the - tuple {logger_formatter,header} to the template.

    - -

    The default template when legacy_header=true is

    - - [{logger_formatter,header},"\n",msg,"\n"] + tuple {logger_formatter,header} to the + template. See Default + Templates for more information

    +

    Default is false.

    +
    + report_cb = fun((logger:report()) -> {io:format(),[term()]}) + +

    A function with arity 1, + returning {Format,Args}. This function will replace + any report_cb found in metadata.

    +
    + depth = pos_integer() | unlimited + +

    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 + io:format/2,3 + in STDLIB.

    +

    Default is unlimited.

    +
    + chars_limit = pos_integer() | unlimited + +

    A positive integer representing the value of the option + with the same name to be used when calling + io_lib:format/3. + This value limits the total number of characters printed + for each log event. Note that this is a soft limit. For a + hard truncation limit, see option max_size.

    +

    Default is unlimited.

    +
    + max_size = pos_integer() | unlimited + +

    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 depth and/or chars_limit, it will be + truncated.

    +

    Default is unlimited.

    +
    + template = template() + +

    The template is a list of atoms, tuples and strings. The + atoms level or msg, 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 in a nested map. For example the + tuple {key1,key2} will be replaced by the value of + the key2 field in the nested map below. The + atom key1 on its own would be replaced by the + complete value of the key1 field. The values are + converted to strings.

    -

    which will cause log entries like this:

    + +#{key1=>#{key2=>my_value, + ...} + ...} - =ERROR REPORT==== 29-Dec-2017::13:30:51.245123 === +

    Strings are printed literally.

    +

    The default template differs depending on the values + of legacy_header + and single_line. See Default + Templates for more information

    +
    + utc = boolean() + +

    If set to true, all dates are displayed in Universal + Coordinated Time. Default is false.

    +
    +
    +
    + +
    + + Default templates +

    The default template when legacy_header=true is

    + + [{logger_formatter,header},"\n",msg,"\n"] + +

    which will cause log entries like this:

    + + =ERROR REPORT==== 29-Dec-2017::13:30:51.245123 === process: <0.74.0> exit_reason: "Something went wrong" -

    Note that all eight levels might occur here, not - only ERROR, WARNING or INFO. And also - that micro seconds are added at the end of the - timestamp.

    +

    Note that all eight levels might occur here, not + only ERROR, WARNING or INFO. And also that + micro seconds are added at the end of the timestamp.

    -

    The default template when single_line=true is

    +

    The default template when single_line=true is

    - [time," ",level,": ",msg,"\n"] + [time," ",level,": ",msg,"\n"] -

    which will cause log entries like this:

    +

    which will cause log entries like this:

    - 2017-12-29 13:31:49.640317 error: process: <0.74.0>, exit_reason: "Something went wrong" + 2017-12-29 13:31:49.640317 error: process: <0.74.0>, exit_reason: "Something went wrong" -

    The default template when both legacy_header and - single_line are set to false is:

    +

    The default template when both legacy_header and + single_line are set to false is:

    - [time," ",level,":\n",msg,"\n"] + [time," ",level,":\n",msg,"\n"] -

    which will cause log entries like this:

    +

    which will cause log entries like this:

    - 2017-12-29 13:32:25.191925 error: + 2017-12-29 13:32:25.191925 error: process: <0.74.0> exit_reason: "Something went wrong" +
    + + + + + + + + + + + + Formats the given message. + +

    This the callback function to be called from handlers. It + formats the given messages.

    -- cgit v1.2.3 From 5a93c690267d0a3ca12304c34b0e4538549fabb1 Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Thu, 3 May 2018 17:23:16 +0200 Subject: Make it possible to disable sync and drop mode Also add tests and update failing test cases --- lib/kernel/doc/src/logger_chapter.xml | 12 +++- lib/kernel/src/logger_h_common.erl | 16 +++-- lib/kernel/src/logger_h_common.hrl | 2 +- lib/kernel/test/logger_disk_log_h_SUITE.erl | 65 +++++++++++++------- lib/kernel/test/logger_std_h_SUITE.erl | 94 +++++++++++++++++------------ 5 files changed, 123 insertions(+), 66 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 0bc3b37476..5fb81034dc 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -700,7 +700,17 @@ my_report_cb(R) ->

    For the overload protection algorithm to work properly, it is a requirement that:

    -

    toggle_sync_qlen < drop_new_reqs_qlen < flush_reqs_qlen

    +

    toggle_sync_qlen =< drop_new_reqs_qlen =< flush_reqs_qlen

    + +

    and that:

    + +

    drop_new_reqs_qlen > 1

    + +

    If toggle_sync_qlen is set to 0, the handler will handle all + requests synchronously. Setting the value of toggle_sync_qlen to the same + as drop_new_reqs_qlen, disables the synchronous mode. Likewise, setting + the value of drop_new_reqs_qlen to the same as flush_reqs_qlen, + disables the drop mode.

    During high load scenarios, the length of the handler message queue rarely grows in a linear and predictable way. Instead, whenever the diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl index 7caad366ae..901c4c0dad 100644 --- a/lib/kernel/src/logger_h_common.erl +++ b/lib/kernel/src/logger_h_common.erl @@ -135,7 +135,8 @@ call_cast_or_drop(Name, Bin) -> _:{timeout,_} -> ?observe(Name,{dropped,1}) end; - drop -> ?observe(Name,{dropped,1}) + drop -> + ?observe(Name,{dropped,1}) catch %% if the ETS table doesn't exist (maybe because of a %% handler restart), we can only drop the request @@ -152,12 +153,15 @@ check_load(State = #{id:=Name, mode := Mode, flush_reqs_qlen := FlushQLen}) -> {_,Mem} = process_info(self(), memory), ?observe(Name,{max_mem,Mem}), - %% make sure the handler process doesn't get scheduled - %% out between the message_queue_len check below and the - %% action that follows (flush or write). {_,QLen} = process_info(self(), message_queue_len), ?observe(Name,{max_qlen,QLen}), - + %% When the handler process gets scheduled in, it's impossible + %% to predict the QLen. We could jump "up" arbitrarily from say + %% async to sync, async to drop, sync to flush, etc. However, when + %% the handler process manages the log requests (without flushing), + %% one after the other, we will move "down" from drop to sync and + %% from sync to async. This way we don't risk getting stuck in + %% drop or sync mode with an empty mailbox. {Mode1,_NewDrops,_NewFlushes} = if QLen >= FlushQLen -> @@ -292,7 +296,7 @@ overload_levels_ok(HandlerConfig) -> TSQL = maps:get(toggle_sync_qlen, HandlerConfig, ?TOGGLE_SYNC_QLEN), DNRQL = maps:get(drop_new_reqs_qlen, HandlerConfig, ?DROP_NEW_REQS_QLEN), FRQL = maps:get(flush_reqs_qlen, HandlerConfig, ?FLUSH_REQS_QLEN), - (TSQL < DNRQL) andalso (DNRQL < FRQL). + (DNRQL > 1) andalso (TSQL =< DNRQL) andalso (DNRQL =< FRQL). error_notify(Term) -> ?internal_log(error, Term). diff --git a/lib/kernel/src/logger_h_common.hrl b/lib/kernel/src/logger_h_common.hrl index 89378dbb10..ed365ce6eb 100644 --- a/lib/kernel/src/logger_h_common.hrl +++ b/lib/kernel/src/logger_h_common.hrl @@ -124,7 +124,7 @@ %%% slow down execution and therefore should not be include in code %%% to be officially released. -%% -define(TEST_HOOKS, true). +-define(TEST_HOOKS, true). -ifdef(TEST_HOOKS). -define(TEST_HOOKS_TAB, logger_h_test_hooks). diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index c7c6137380..6a4ec72d5e 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -66,7 +66,19 @@ end_per_testcase(Case, Config) -> ok. groups() -> - []. + [ + {retry_op_switch_to_sync, + [{repeat_until_all_ok,10}], + [op_switch_to_sync]}, + + {retry_op_switch_to_drop, + [{repeat_until_all_ok,10}], + [op_switch_to_drop]}, + + {retry_op_switch_to_flush, + [{repeat_until_all_ok,10}], + [op_switch_to_flush]} + ]. all() -> [start_stop_handler, @@ -87,9 +99,9 @@ all() -> disk_log_events, write_failure, sync_failure, - op_switch_to_sync, - op_switch_to_drop, - op_switch_to_flush, + {group,retry_op_switch_to_sync}, + {group,retry_op_switch_to_drop}, + {group,retry_op_switch_to_flush}, limit_burst_disabled, limit_burst_enabled_one, limit_burst_enabled_period, @@ -369,10 +381,18 @@ config_fail(_Config) -> #{logger_disk_log_h => #{bad => bad}, filter_default=>log, formatter=>{?MODULE,self()}}), - {error,{handler_not_added,{invalid_levels,{42,42,_}}}} = + + {error,{handler_not_added,{invalid_levels,{_,1,_}}}} = logger:add_handler(?MODULE,logger_disk_log_h, - #{logger_disk_log_h => #{toggle_sync_qlen=>42, + #{logger_disk_log_h => #{drop_new_reqs_qlen=>1}}), + {error,{handler_not_added,{invalid_levels,{43,42,_}}}} = + logger:add_handler(?MODULE,logger_disk_log_h, + #{logger_disk_log_h => #{toggle_sync_qlen=>43, drop_new_reqs_qlen=>42}}), + {error,{handler_not_added,{invalid_levels,{_,43,42}}}} = + logger:add_handler(?MODULE,logger_disk_log_h, + #{logger_disk_log_h => #{drop_new_reqs_qlen=>43, + flush_reqs_qlen=>42}}), ok = logger:add_handler(?MODULE,logger_disk_log_h, #{filter_default=>log, @@ -852,13 +872,13 @@ internal_log(Type, Term) -> op_switch_to_sync(Config) -> {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + NumOfReqs = 500, NewHConfig = - HConfig#{logger_disk_log_h => DLHConfig#{toggle_sync_qlen => 3, - drop_new_reqs_qlen => 501, - flush_reqs_qlen => 2000, + HConfig#{logger_disk_log_h => DLHConfig#{toggle_sync_qlen => 2, + drop_new_reqs_qlen => NumOfReqs+1, + flush_reqs_qlen => 2*NumOfReqs, enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), - NumOfReqs = 500, send_burst({n,NumOfReqs}, seq, {chars,79}, info), NumOfReqs = count_lines(Log), ok = file:delete(Log). @@ -867,19 +887,20 @@ op_switch_to_sync(cleanup, _Config) -> op_switch_to_drop(Config) -> {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), - + NumOfReqs = 300, + Procs = 2, NewHConfig = - HConfig#{logger_disk_log_h => DLHConfig#{toggle_sync_qlen => 2, - drop_new_reqs_qlen => 3, - flush_reqs_qlen => 600, + HConfig#{logger_disk_log_h => DLHConfig#{toggle_sync_qlen => 1, + drop_new_reqs_qlen => 2, + flush_reqs_qlen => Procs*NumOfReqs+1, enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), - NumOfReqs = 500, - send_burst({n,NumOfReqs}, seq, {chars,79}, info), + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), Logged = count_lines(Log), ct:pal("Number of messages dropped = ~w (~w)", - [NumOfReqs-Logged,NumOfReqs]), - true = (Logged < NumOfReqs), + [Procs*NumOfReqs-Logged,Procs*NumOfReqs]), + true = (Logged < (Procs*NumOfReqs)), + true = (Logged > 0), ok = file:delete(Log). op_switch_to_drop(cleanup, _Config) -> ok = stop_handler(?MODULE). @@ -894,17 +915,19 @@ op_switch_to_flush(Config) -> NewHConfig = HConfig#{logger_disk_log_h => DLHConfig#{toggle_sync_qlen => 2, - drop_new_reqs_qlen => 99, - flush_reqs_qlen => 100, + %% disable drop mode + drop_new_reqs_qlen => 500, + flush_reqs_qlen => 500, enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 1000, - Procs = 500, + Procs = 200, send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), Logged = count_lines(Log), ct:pal("Number of messages flushed/dropped = ~w (~w)", [(NumOfReqs*Procs)-Logged,NumOfReqs*Procs]), true = (Logged < (NumOfReqs*Procs)), + true = (Logged > 0), ok = file:delete(Log). op_switch_to_flush(cleanup, _Config) -> ok = stop_handler(?MODULE). diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index e940e0a026..ed5504431e 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -89,7 +89,19 @@ end_per_testcase(Case, Config) -> ok. groups() -> - []. + [ + {retry_op_switch_to_sync_file, + [{repeat_until_all_ok,10}], + [op_switch_to_sync_file]}, + + {retry_op_switch_to_drop_file, + [{repeat_until_all_ok,10}], + [op_switch_to_drop_file]}, + + {retry_op_switch_to_flush_file, + [{repeat_until_all_ok,10}], + [op_switch_to_flush_file]} + ]. all() -> [add_remove_instance_tty, @@ -110,11 +122,11 @@ all() -> filesync, write_failure, sync_failure, - op_switch_to_sync_file, + {group,retry_op_switch_to_sync_file}, op_switch_to_sync_tty, - op_switch_to_drop_file, + {group,retry_op_switch_to_drop_file}, op_switch_to_drop_tty, - op_switch_to_flush_file, + {group,retry_op_switch_to_flush_file}, op_switch_to_flush_tty, limit_burst_disabled, limit_burst_enabled_one, @@ -289,10 +301,17 @@ config_fail(_Config) -> #{logger_std_h => #{restart_type => bad}, filter_default=>log, formatter=>{?MODULE,self()}}), - {error,{handler_not_added,{invalid_levels,{42,42,_}}}} = + {error,{handler_not_added,{invalid_levels,{_,1,_}}}} = + logger:add_handler(?MODULE,logger_std_h, + #{logger_std_h => #{drop_new_reqs_qlen=>1}}), + {error,{handler_not_added,{invalid_levels,{43,42,_}}}} = logger:add_handler(?MODULE,logger_std_h, - #{logger_std_h => #{toggle_sync_qlen=>42, + #{logger_std_h => #{toggle_sync_qlen=>43, drop_new_reqs_qlen=>42}}), + {error,{handler_not_added,{invalid_levels,{_,43,42}}}} = + logger:add_handler(?MODULE,logger_std_h, + #{logger_std_h => #{drop_new_reqs_qlen=>43, + flush_reqs_qlen=>42}}), ok = logger:add_handler(?MODULE,logger_std_h, #{filter_default=>log, @@ -698,14 +717,14 @@ internal_log(Type, Term) -> op_switch_to_sync_file(Config) -> {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + NumOfReqs = 500, NewHConfig = - HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 3, - drop_new_reqs_qlen => 501, - flush_reqs_qlen => 2000, + HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 2, + drop_new_reqs_qlen => NumOfReqs+1, + flush_reqs_qlen => 2*NumOfReqs, enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), %% TRecvPid = start_op_trace(), - NumOfReqs = 500, send_burst({n,NumOfReqs}, seq, {chars,79}, info), NumOfReqs = count_lines(Log), %% true = analyse_trace(TRecvPid, @@ -726,13 +745,13 @@ op_switch_to_sync_file(cleanup, _Config) -> op_switch_to_sync_tty(Config) -> {HConfig,StdHConfig} = start_handler(?MODULE, standard_io, Config), + NumOfReqs = 500, NewHConfig = HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 3, - drop_new_reqs_qlen => 501, - flush_reqs_qlen => 2000, + drop_new_reqs_qlen => NumOfReqs+1, + flush_reqs_qlen => 2*NumOfReqs, enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), - NumOfReqs = 500, send_burst({n,NumOfReqs}, seq, {chars,79}, info), ok. op_switch_to_sync_tty(cleanup, _Config) -> @@ -740,20 +759,21 @@ op_switch_to_sync_tty(cleanup, _Config) -> op_switch_to_drop_file(Config) -> {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), - + NumOfReqs = 300, + Procs = 2, NewHConfig = - HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 2, - drop_new_reqs_qlen => 3, - flush_reqs_qlen => 600, + HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 1, + drop_new_reqs_qlen => 2, + flush_reqs_qlen => Procs*NumOfReqs+1, enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), %% TRecvPid = start_op_trace(), - NumOfReqs = 500, - send_burst({n,NumOfReqs}, seq, {chars,79}, info), + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), Logged = count_lines(Log), ct:pal("Number of messages dropped = ~w (~w)", - [NumOfReqs-Logged,NumOfReqs]), - true = (Logged < NumOfReqs), + [Procs*NumOfReqs-Logged,Procs*NumOfReqs]), + true = (Logged < (Procs*NumOfReqs)), + true = (Logged > 0), %% true = analyse_trace(TRecvPid, %% fun(Events) -> find_mode(async,Events) end), %% true = analyse_trace(TRecvPid, @@ -772,14 +792,15 @@ op_switch_to_drop_file(cleanup, _Config) -> op_switch_to_drop_tty(Config) -> {HConfig,StdHConfig} = start_handler(?MODULE, standard_io, Config), + NumOfReqs = 300, + Procs = 2, NewHConfig = - HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 2, - drop_new_reqs_qlen => 3, - flush_reqs_qlen => 600, + HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 1, + drop_new_reqs_qlen => 2, + flush_reqs_qlen => Procs*NumOfReqs+1, enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), - NumOfReqs = 500, - send_burst({n,NumOfReqs}, seq, {chars,79}, info), + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), ok. op_switch_to_drop_tty(cleanup, _Config) -> ok = stop_handler(?MODULE). @@ -794,21 +815,19 @@ op_switch_to_flush_file(Config) -> NewHConfig = HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 2, - drop_new_reqs_qlen => 99, - flush_reqs_qlen => 100, + %% disable drop mode + drop_new_reqs_qlen => 500, + flush_reqs_qlen => 500, enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), - NumOfReqs = 10000, - Procs = 100, + NumOfReqs = 1000, + Procs = 200, send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), Logged = count_lines(Log), ct:pal("Number of messages flushed/dropped = ~w (~w)", [(NumOfReqs*Procs)-Logged,NumOfReqs*Procs]), true = (Logged < (NumOfReqs*Procs)), - - %%! --- Thu Apr 12 13:46:00 2018 --- peppe was here! - %%! TODO: Verify that handler has switched to flush mode - + true = (Logged > 0), ok = file:delete(Log), ok. op_switch_to_flush_file(cleanup, _Config) -> @@ -822,12 +841,13 @@ op_switch_to_flush_tty(Config) -> NewHConfig = HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 2, - drop_new_reqs_qlen => 99, + %% disable drop mode + drop_new_reqs_qlen => 100, flush_reqs_qlen => 100, enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), - NumOfReqs = 10000, - Procs = 10, + NumOfReqs = 1000, + Procs = 100, send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), ok. op_switch_to_flush_tty(cleanup, _Config) -> -- cgit v1.2.3 From e7a3bf9c385ac6df2209c6f7c92543a3dd7f6497 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Tue, 17 Apr 2018 13:28:24 +0200 Subject: Move lib:nonl/1 into yecc.erl --- lib/kernel/test/os_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl index 591fbb2125..a9e1ae6aa2 100644 --- a/lib/kernel/test/os_SUITE.erl +++ b/lib/kernel/test/os_SUITE.erl @@ -388,7 +388,7 @@ comp(Expected, Got) -> ct:fail(failed) end. -%% Like lib:nonl/1, but strips \r as well as \n. +%% strips \n and \r\n from end of string strip_nl([$\r, $\n]) -> []; strip_nl([$\n]) -> []; -- cgit v1.2.3 From 3d44429177a7dbdac5662433cfc55f5b5113a959 Mon Sep 17 00:00:00 2001 From: Richard Carlsson Date: Tue, 17 Apr 2018 14:11:34 +0200 Subject: Add ct:get_progname/0 This replaces all uses of lib:progname/0 in tests. --- lib/kernel/test/erl_distribution_SUITE.erl | 2 +- lib/kernel/test/heart_SUITE.erl | 8 ++++---- lib/kernel/test/kernel_config_SUITE.erl | 2 +- lib/kernel/test/os_SUITE.erl | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl index 0470f09f29..9c6712ad74 100644 --- a/lib/kernel/test/erl_distribution_SUITE.erl +++ b/lib/kernel/test/erl_distribution_SUITE.erl @@ -244,7 +244,7 @@ illegal(Name) -> test_node(Name) -> test_node(Name, false). test_node(Name, Illigal) -> - ProgName = atom_to_list(lib:progname()), + ProgName = ct:get_progname(), Command = ProgName ++ " -noinput " ++ long_or_short() ++ Name ++ " -eval \"net_adm:ping('" ++ atom_to_list(node()) ++ "')\"" ++ case Illigal of diff --git a/lib/kernel/test/heart_SUITE.erl b/lib/kernel/test/heart_SUITE.erl index 22db24de5f..e95635b800 100644 --- a/lib/kernel/test/heart_SUITE.erl +++ b/lib/kernel/test/heart_SUITE.erl @@ -168,7 +168,7 @@ reboot(Config) when is_list(Config) -> {ok, Node} = start_check(slave, ?UNIQ_NODE_NAME), ok = rpc:call(Node, heart, set_cmd, - [atom_to_list(lib:progname()) ++ + [ct:get_progname() ++ " -noshell -heart " ++ name(Node) ++ "&"]), rpc:call(Node, init, reboot, []), receive @@ -203,7 +203,7 @@ node_start_immediately_after_crash_test(Config) when is_list(Config) -> [{"ERL_CRASH_DUMP_SECONDS", "0"}]), ok = rpc:call(Node, heart, set_cmd, - [atom_to_list(lib:progname()) ++ + [ct:get_progname() ++ " -noshell -heart " ++ name(Node) ++ "&"]), Mod = exhaust_atoms, @@ -254,7 +254,7 @@ node_start_soon_after_crash_test(Config) when is_list(Config) -> [{"ERL_CRASH_DUMP_SECONDS", "10"}]), ok = rpc:call(Node, heart, set_cmd, - [atom_to_list(lib:progname()) ++ + [ct:get_progname() ++ " -noshell -heart " ++ name(Node) ++ "&"]), Mod = exhaust_atoms, @@ -309,7 +309,7 @@ set_cmd(Config) when is_list(Config) -> clear_cmd(Config) when is_list(Config) -> {ok, Node} = start_check(slave, ?UNIQ_NODE_NAME), ok = rpc:call(Node, heart, set_cmd, - [atom_to_list(lib:progname()) ++ + [ct:get_progname() ++ " -noshell -heart " ++ name(Node) ++ "&"]), rpc:call(Node, init, reboot, []), receive diff --git a/lib/kernel/test/kernel_config_SUITE.erl b/lib/kernel/test/kernel_config_SUITE.erl index 9a4578917d..a21020ff97 100644 --- a/lib/kernel/test/kernel_config_SUITE.erl +++ b/lib/kernel/test/kernel_config_SUITE.erl @@ -76,7 +76,7 @@ sync(Conf) when is_list(Conf) -> %% Reset wall_clock {T1,_} = erlang:statistics(wall_clock), io:format("~p~n", [{t1, T1}]), - Command = lists:concat([lib:progname(), + Command = lists:append([ct:get_progname(), " -detached -sname cp1 ", "-config ", Config, " -env ERL_CRASH_DUMP erl_crash_dump.cp1"]), diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl index a9e1ae6aa2..abbc301360 100644 --- a/lib/kernel/test/os_SUITE.erl +++ b/lib/kernel/test/os_SUITE.erl @@ -227,8 +227,8 @@ find_executable(Config) when is_list(Config) -> DataDir = proplists:get_value(data_dir, Config), %% Smoke test. - case lib:progname() of - erl -> + case ct:get_progname() of + "erl" -> ErlPath = os:find_executable("erl"), true = is_list(ErlPath), true = filelib:is_regular(ErlPath); -- cgit v1.2.3 From 1a6adf47cf8895a508cd76660ed69f1f3dfb0bcd Mon Sep 17 00:00:00 2001 From: Radek Szymczyszyn Date: Thu, 26 Apr 2018 19:10:40 +0200 Subject: Fix syntactic issues in EDoc comments across some libs See https://github.com/erszcz/docsh/issues/23 for an explanation about how the files were found. --- lib/kernel/src/hipe_unified_loader.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') 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()] -- cgit v1.2.3 From 681bc6ad427fce5f087ccc48fb0172b46c02b52a Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Fri, 4 May 2018 15:00:19 +0200 Subject: Various logger handler improvements and updated test cases --- lib/kernel/doc/src/logger_chapter.xml | 4 +- lib/kernel/src/logger_disk_log_h.erl | 64 ++++---- lib/kernel/src/logger_std_h.erl | 22 +-- lib/kernel/test/logger_disk_log_h_SUITE.erl | 193 +++++++++++++++-------- lib/kernel/test/logger_std_h_SUITE.erl | 233 +++++++++++++++++----------- 5 files changed, 311 insertions(+), 205 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 5fb81034dc..0374a0c93a 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -697,8 +697,8 @@ my_report_cb(R) -> -

    For the overload protection algorithm to work properly, it is a - requirement that:

    +

    For the overload protection algorithm to work properly, it is + required that:

    toggle_sync_qlen =< drop_new_reqs_qlen =< flush_reqs_qlen

    diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index 3b71f936d8..eaa5cd6f99 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -278,10 +278,11 @@ init([Name, Config = #{disk_log_opts := LogOpts}, last_log_ts => T0, burst_win_ts => T0, burst_msg_count => 0, + last_op => sync, prev_log_result => ok, prev_sync_result => ok, prev_disk_log_info => undefined}), - gen_server:cast(self(), {repeated_disk_log_sync,T0}), + gen_server:cast(self(), repeated_disk_log_sync), enter_loop(Config, State1); Error -> logger_h_common:error_notify({open_disk_log,Name,Error}), @@ -316,8 +317,7 @@ handle_call(disk_log_sync, _From, State = #{id := Name}) -> {reply, Result, State1}; handle_call({change_config,_OldConfig,NewConfig}, _From, - State = #{filesync_repeat_interval := FSyncInt0, - last_log_ts := LastLogTS}) -> + State = #{filesync_repeat_interval := FSyncInt0}) -> HConfig = maps:get(?MODULE, NewConfig, #{}), State1 = #{toggle_sync_qlen := TSQL, drop_new_reqs_qlen := DNRQL, @@ -338,9 +338,8 @@ handle_call({change_config,_OldConfig,NewConfig}, _From, _ = logger_h_common:cancel_timer(maps:get(rep_sync_tref, State, undefined)), - _ = gen_server:cast(self(), {repeated_disk_log_sync, - LastLogTS}) - end, + _ = gen_server:cast(self(), repeated_disk_log_sync) + end, {reply, ok, State1}; false -> {reply, {error,{invalid_levels,{TSQL,DNRQL,FRQL}}}, State} @@ -370,24 +369,23 @@ handle_cast({log, Bin}, State) -> %% clause gets called repeatedly by the handler. In order to %% guarantee that a filesync *always* happens after the last log %% request, the repeat operation must be active! -handle_cast({repeated_disk_log_sync,LastLogTS0}, +handle_cast(repeated_disk_log_sync, State = #{id := Name, filesync_repeat_interval := FSyncInt, - last_log_ts := LastLogTS1}) -> + last_op := LastOp}) -> State1 = if is_integer(FSyncInt) -> %% only do filesync if something has been %% written since last time we checked - NewState = if LastLogTS1 == LastLogTS0 -> + NewState = if LastOp == sync -> State; true -> disk_log_sync(Name, State) end, {ok,TRef} = timer:apply_after(FSyncInt, gen_server,cast, - [self(), - {repeated_disk_log_sync,LastLogTS1}]), - NewState#{rep_sync_tref => TRef}; + [self(),repeated_disk_log_sync]), + NewState#{rep_sync_tref => TRef, last_op => sync}; true -> State end, @@ -649,10 +647,9 @@ close_disk_log(Name, _) -> ok. disk_log_write(Name, Bin, State) -> - Result = case ?disk_log_blog(Name, Bin) of ok -> - ok; + State#{prev_log_result => ok, last_op => write}; LogError -> _ = case maps:get(prev_log_result, State) of LogError -> @@ -664,29 +661,26 @@ disk_log_write(Name, Bin, State) -> LogOpts, LogError}) end, - LogError - end, - State#{prev_log_result => Result}. + State#{prev_log_result => LogError} + end. disk_log_sync(Name, State) -> - Result = - case ?disk_log_sync(Name) of - ok -> - ok; - SyncError -> - _ = case maps:get(prev_sync_result, State) of - SyncError -> - %% don't report same error twice - ok; - _ -> - LogOpts = maps:get(log_opts, State), - logger_h_common:error_notify({Name,sync, - LogOpts, - SyncError}) - end, - SyncError - end, - State#{prev_sync_result => Result}. + case ?disk_log_sync(Name) of + ok -> + State#{prev_sync_result => ok, last_op => sync}; + SyncError -> + _ = case maps:get(prev_sync_result, State) of + SyncError -> + %% don't report same error twice + ok; + _ -> + LogOpts = maps:get(log_opts, State), + logger_h_common:error_notify({Name,sync, + LogOpts, + SyncError}) + end, + State#{prev_sync_result => SyncError} + end. error_notify_new(Info,Info, _Term) -> ok; diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index cbc9db372c..813fbad0ed 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -257,10 +257,11 @@ init([Name, Config, file_ctrl_sync => FileCtrlSyncInt, last_qlen => 0, last_log_ts => T0, + last_op => sync, burst_win_ts => T0, burst_msg_count => 0}), proc_lib:init_ack({ok,self()}), - gen_server:cast(self(), {repeated_filesync,T0}), + gen_server:cast(self(), repeated_filesync), enter_loop(Config, State1); Error -> logger_h_common:error_notify({init_handler,Name,Error}), @@ -310,12 +311,11 @@ handle_call(filesync, _From, State = #{type := Type, if is_atom(Type) -> {reply, ok, State}; true -> - {reply, file_ctrl_filesync_sync(FileCtrlPid), State} + {reply, file_ctrl_filesync_sync(FileCtrlPid), State#{last_op=>sync}} end; handle_call({change_config,_OldConfig,NewConfig}, _From, - State = #{filesync_repeat_interval := FSyncInt0, - last_log_ts := LastLogTS}) -> + State = #{filesync_repeat_interval := FSyncInt0}) -> HConfig = maps:get(?MODULE, NewConfig, #{}), State1 = maps:merge(State, HConfig), case logger_h_common:overload_levels_ok(State1) of @@ -334,8 +334,7 @@ handle_call({change_config,_OldConfig,NewConfig}, _From, _ = logger_h_common:cancel_timer(maps:get(rep_sync_tref, State, undefined)), - gen_server:cast(self(), {repeated_filesync, - LastLogTS}) + gen_server:cast(self(), repeated_filesync) end, {reply, ok, State1}; false -> @@ -365,24 +364,24 @@ handle_cast({log, Bin}, State) -> %% clause gets called repeatedly by the handler. In order to %% guarantee that a filesync *always* happens after the last log %% request, the repeat operation must be active! -handle_cast({repeated_filesync,LastLogTS0}, +handle_cast(repeated_filesync, State = #{type := Type, file_ctrl_pid := FileCtrlPid, filesync_repeat_interval := FSyncInt, - last_log_ts := LastLogTS1}) -> + last_op := LastOp}) -> State1 = if not is_atom(Type), is_integer(FSyncInt) -> %% only do filesync if something has been %% written since last time we checked - if LastLogTS1 == LastLogTS0 -> + if LastOp == sync -> ok; true -> file_ctrl_filesync_async(FileCtrlPid) end, {ok,TRef} = timer:apply_after(FSyncInt, gen_server,cast, - [self(),{repeated_filesync,LastLogTS1}]), - State#{rep_sync_tref => TRef}; + [self(),repeated_filesync]), + State#{rep_sync_tref => TRef, last_op => sync}; true -> State end, @@ -600,6 +599,7 @@ write(Name, Mode, T1, Bin, _CallOrCast, State1#{mode => Mode1, last_qlen := LastQLen1, last_log_ts => T1, + last_op => write, burst_win_ts => BurstWinT, burst_msg_count => BurstMsgCount1, file_ctrl_sync => diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 6a4ec72d5e..bb88c53f26 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -66,19 +66,7 @@ end_per_testcase(Case, Config) -> ok. groups() -> - [ - {retry_op_switch_to_sync, - [{repeat_until_all_ok,10}], - [op_switch_to_sync]}, - - {retry_op_switch_to_drop, - [{repeat_until_all_ok,10}], - [op_switch_to_drop]}, - - {retry_op_switch_to_flush, - [{repeat_until_all_ok,10}], - [op_switch_to_flush]} - ]. + []. all() -> [start_stop_handler, @@ -99,9 +87,9 @@ all() -> disk_log_events, write_failure, sync_failure, - {group,retry_op_switch_to_sync}, - {group,retry_op_switch_to_drop}, - {group,retry_op_switch_to_flush}, + op_switch_to_sync, + op_switch_to_drop, + op_switch_to_flush, limit_burst_disabled, limit_burst_enabled_one, limit_burst_enabled_period, @@ -880,57 +868,107 @@ op_switch_to_sync(Config) -> enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), send_burst({n,NumOfReqs}, seq, {chars,79}, info), - NumOfReqs = count_lines(Log), - ok = file:delete(Log). + Lines = count_lines(Log), + ok = file:delete(Log), + NumOfReqs = Lines, + ok. op_switch_to_sync(cleanup, _Config) -> ok = stop_handler(?MODULE). +op_switch_to_drop() -> + [{timetrap,{seconds,180}}]. op_switch_to_drop(Config) -> - {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), - NumOfReqs = 300, - Procs = 2, - NewHConfig = - HConfig#{logger_disk_log_h => DLHConfig#{toggle_sync_qlen => 1, - drop_new_reqs_qlen => 2, - flush_reqs_qlen => Procs*NumOfReqs+1, - enable_burst_limit => false}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), - send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), - Logged = count_lines(Log), - ct:pal("Number of messages dropped = ~w (~w)", - [Procs*NumOfReqs-Logged,Procs*NumOfReqs]), - true = (Logged < (Procs*NumOfReqs)), - true = (Logged > 0), - ok = file:delete(Log). + Test = + fun() -> + {Log,HConfig,DLHConfig} = + start_handler(?MODULE, ?FUNCTION_NAME, Config), + NumOfReqs = 300, + Procs = 2, + Bursts = 10, + NewHConfig = + HConfig#{logger_disk_log_h => + DLHConfig#{toggle_sync_qlen => 1, + drop_new_reqs_qlen => 2, + flush_reqs_qlen => Procs*NumOfReqs*Bursts, + enable_burst_limit => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + %% It sometimes happens that the handler either gets + %% the requests in a slow enough pace so that dropping + %% never occurs. Therefore, lets generate a number of + %% bursts to increase the chance of message buildup. + [send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info) || + _ <- lists:seq(1, Bursts)], + Logged = count_lines(Log), + ok= stop_handler(?MODULE), + _ = file:delete(Log), + ct:pal("Number of messages dropped = ~w (~w)", + [Procs*NumOfReqs*Bursts-Logged,Procs*NumOfReqs*Bursts]), + true = (Logged < (Procs*NumOfReqs*Bursts)), + true = (Logged > 0), + ok + end, + %% As it's tricky to get the timing right in only one go, we perform the + %% test repeatedly, hoping that will generate a successful result. + case repeat_until_ok(Test, 10) of + {ok,{Failures,_Result}} -> + ct:log("Failed ~w times before success!", [Failures]); + {fails,Reason} -> + ct:fail(Reason) + end. op_switch_to_drop(cleanup, _Config) -> - ok = stop_handler(?MODULE). + _ = stop_handler(?MODULE). op_switch_to_flush() -> - [{timetrap,{seconds,60}}]. + [{timetrap,{seconds,180}}]. op_switch_to_flush(Config) -> - {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + Test = + fun() -> + {Log,HConfig,DLHConfig} = + start_handler(?MODULE, ?FUNCTION_NAME, Config), + + %% NOTE: it's important that both async and sync + %% requests have been queued when the flush happens + %% (verify with coverage of flush_log_requests/2) - %% it's important that both async and sync requests have been queued - %% when the flush happens (verify with coverage of flush_log_requests/2) - - NewHConfig = - HConfig#{logger_disk_log_h => DLHConfig#{toggle_sync_qlen => 2, - %% disable drop mode - drop_new_reqs_qlen => 500, - flush_reqs_qlen => 500, - enable_burst_limit => false}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), - NumOfReqs = 1000, - Procs = 200, - send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), - Logged = count_lines(Log), - ct:pal("Number of messages flushed/dropped = ~w (~w)", - [(NumOfReqs*Procs)-Logged,NumOfReqs*Procs]), - true = (Logged < (NumOfReqs*Procs)), - true = (Logged > 0), - ok = file:delete(Log). + NewHConfig = + HConfig#{logger_disk_log_h => + DLHConfig#{toggle_sync_qlen => 2, + %% disable drop mode + drop_new_reqs_qlen => 300, + flush_reqs_qlen => 300, + enable_burst_limit => false}}, + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 1500, + Procs = 10, + Bursts = 10, + %% It sometimes happens that the handler either gets + %% the requests in a slow enough pace so that flushing + %% never occurs, or it gets all messages at once, + %% causing all messages to get flushed (no dropping of + %% sync messages gets tested). Therefore, lets + %% generate a number of bursts to increase the chance + %% of message buildup in some random fashion. + [send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info) || + _ <- lists:seq(1,Bursts)], + Logged = count_lines(Log), + ok= stop_handler(?MODULE), + _ = file:delete(Log), + ct:pal("Number of messages flushed/dropped = ~w (~w)", + [NumOfReqs*Procs*Bursts-Logged,NumOfReqs*Procs*Bursts]), + true = (Logged < (NumOfReqs*Procs*Bursts)), + true = (Logged > 0), + ok + end, + %% As it's tricky to get the timing right in only one go, we perform the + %% test repeatedly, hoping that will generate a successful result. + case repeat_until_ok(Test, 10) of + {ok,{Failures,_Result}} -> + ct:log("Failed ~w times before success!", [Failures]); + {fails,Reason} -> + ct:fail(Reason) + end. op_switch_to_flush(cleanup, _Config) -> - ok = stop_handler(?MODULE). + _ = stop_handler(?MODULE). limit_burst_disabled(Config) -> @@ -1014,7 +1052,7 @@ qlen_kill_new(Config) -> {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), Pid0 = whereis(?MODULE), {_,Mem0} = process_info(Pid0, memory), - RestartAfter = 2000, + RestartAfter = ?HANDLER_RESTART_AFTER, NewHConfig = HConfig#{logger_disk_log_h => DLHConfig#{enable_kill_overloaded=>true, @@ -1035,7 +1073,7 @@ qlen_kill_new(Config) -> killed -> ct:pal("Slow shutdown, handler process was killed!", []) end, - timer:sleep(RestartAfter + 1000), + timer:sleep(RestartAfter + 2000), true = is_pid(whereis(?MODULE)), ok after @@ -1051,7 +1089,7 @@ mem_kill_new(Config) -> {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), Pid0 = whereis(?MODULE), {_,Mem0} = process_info(Pid0, memory), - RestartAfter = 2000, + RestartAfter = ?HANDLER_RESTART_AFTER, NewHConfig = HConfig#{logger_disk_log_h => DLHConfig#{enable_kill_overloaded=>true, @@ -1072,7 +1110,7 @@ mem_kill_new(Config) -> killed -> ct:pal("Slow shutdown, handler process was killed!", []) end, - timer:sleep(RestartAfter * 2), + timer:sleep(RestartAfter + 2000), true = is_pid(whereis(?MODULE)), ok after @@ -1105,7 +1143,7 @@ restart_after(Config) -> end, {Log,_,_} = start_handler(?MODULE, ?FUNCTION_NAME, Config), - RestartAfter = 2000, + RestartAfter = ?HANDLER_RESTART_AFTER, NewHConfig2 = HConfig#{logger_disk_log_h=>DLHConfig#{enable_kill_overloaded=>true, handler_overloaded_qlen=>10, @@ -1117,7 +1155,7 @@ restart_after(Config) -> send_burst({n,100}, {spawn,2,0}, {chars,79}, info), receive {'DOWN', MRef2, _, _, _Info2} -> - timer:sleep(RestartAfter + 1000), + timer:sleep(RestartAfter + 2000), Pid1 = whereis(?MODULE), true = is_pid(Pid1), false = (Pid1 == Pid0), @@ -1388,6 +1426,29 @@ count_lines1(File) -> file:close(Dev), Lines. +repeat_until_ok(Fun, N) -> + repeat_until_ok(Fun, 0, N, undefined). + +repeat_until_ok(_Fun, Stop, Stop, Reason) -> + {fails,Reason}; + +repeat_until_ok(Fun, C, Stop, FirstReason) -> + if C > 0 -> timer:sleep(5000); + true -> ok + end, + try Fun() of + Result -> + {ok,{C,Result}} + catch + _:Reason:Stack -> + ct:pal("Test fails: ~p (~p)~n", [Reason,hd(Stack)]), + if FirstReason == undefined -> + repeat_until_ok(Fun, C+1, Stop, {Reason,Stack}); + true -> + repeat_until_ok(Fun, C+1, Stop, FirstReason) + end + end. + start_tracer(Trace,Expected) -> Pid = self(), dbg:tracer(process,{fun tracer/2,{Pid,Expected}}), @@ -1410,9 +1471,11 @@ tpl([{M,F,A}|Trace]) -> tpl([]) -> ok. -tracer({trace,_,call,{?MODULE,format,[#{msg:={string,Msg}}|_]}}, {Pid,[{formatter,Msg}|Expected]}) -> +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]}) -> +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,Func,_}}, {Pid,[{Mod,Func}|Expected]}) -> maybe_tracer_done(Pid,Expected,{Mod,Func}); diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index ed5504431e..3ebbbe74ef 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -89,19 +89,7 @@ end_per_testcase(Case, Config) -> ok. groups() -> - [ - {retry_op_switch_to_sync_file, - [{repeat_until_all_ok,10}], - [op_switch_to_sync_file]}, - - {retry_op_switch_to_drop_file, - [{repeat_until_all_ok,10}], - [op_switch_to_drop_file]}, - - {retry_op_switch_to_flush_file, - [{repeat_until_all_ok,10}], - [op_switch_to_flush_file]} - ]. + []. all() -> [add_remove_instance_tty, @@ -122,11 +110,11 @@ all() -> filesync, write_failure, sync_failure, - {group,retry_op_switch_to_sync_file}, + op_switch_to_sync_file, op_switch_to_sync_tty, - {group,retry_op_switch_to_drop_file}, + op_switch_to_drop_file, op_switch_to_drop_tty, - {group,retry_op_switch_to_flush_file}, + op_switch_to_flush_file, op_switch_to_flush_tty, limit_burst_disabled, limit_burst_enabled_one, @@ -588,7 +576,7 @@ filesync(Config) -> #{filesync_repeat_interval => no_repeat}), dbg:stop_clear(), - Received2 = lists:map(fun({trace,_M,handle_cast,[{Op,_},_]}) -> {trace,Op}; + 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]), @@ -726,7 +714,8 @@ op_switch_to_sync_file(Config) -> ok = logger:set_handler_config(?MODULE, NewHConfig), %% TRecvPid = start_op_trace(), send_burst({n,NumOfReqs}, seq, {chars,79}, info), - NumOfReqs = count_lines(Log), + Lines = count_lines(Log), + ok = file:delete(Log), %% true = analyse_trace(TRecvPid, %% fun(Events) -> find_mode(async,Events) end), %% true = analyse_trace(TRecvPid, @@ -737,8 +726,8 @@ op_switch_to_sync_file(Config) -> %% fun(Events) -> find_mode(drop,Events) end), %% false = analyse_trace(TRecvPid, %% fun(Events) -> find_mode(flush,Events) end), - ok = file:delete(Log), %% stop_op_trace(TRecvPid), + NumOfReqs = Lines, ok. op_switch_to_sync_file(cleanup, _Config) -> ok = stop_handler(?MODULE). @@ -757,38 +746,49 @@ op_switch_to_sync_tty(Config) -> op_switch_to_sync_tty(cleanup, _Config) -> ok = stop_handler(?MODULE). +op_switch_to_drop_file() -> + [{timetrap,{seconds,180}}]. op_switch_to_drop_file(Config) -> - {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), - NumOfReqs = 300, - Procs = 2, - NewHConfig = - HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 1, + Test = + fun() -> + {Log,HConfig,StdHConfig} = + start_handler(?MODULE, ?FUNCTION_NAME, Config), + NumOfReqs = 300, + Procs = 2, + Bursts = 10, + NewHConfig = + HConfig#{logger_std_h => + StdHConfig#{toggle_sync_qlen => 1, drop_new_reqs_qlen => 2, - flush_reqs_qlen => Procs*NumOfReqs+1, + flush_reqs_qlen => + Procs*NumOfReqs*Bursts, enable_burst_limit => false}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), - %% TRecvPid = start_op_trace(), - send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), - Logged = count_lines(Log), - ct:pal("Number of messages dropped = ~w (~w)", - [Procs*NumOfReqs-Logged,Procs*NumOfReqs]), - true = (Logged < (Procs*NumOfReqs)), - true = (Logged > 0), - %% true = analyse_trace(TRecvPid, - %% fun(Events) -> find_mode(async,Events) end), - %% true = analyse_trace(TRecvPid, - %% fun(Events) -> find_mode(drop,Events) end), - %% false = analyse_trace(TRecvPid, - %% fun(Events) -> find_mode(flush,Events) end), - %% true = analyse_trace(TRecvPid, - %% fun(Events) -> find_switch(async,drop,Events) - %% orelse find_switch(sync,drop,Events) - %% end), - ok = file:delete(Log), - %% stop_op_trace(TRecvPid), - ok. + ok = logger:set_handler_config(?MODULE, NewHConfig), + %% It sometimes happens that the handler gets the + %% requests in a slow enough pace so that dropping + %% never occurs. Therefore, lets generate a number of + %% bursts to increase the chance of message buildup. + [send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info) || + _ <- lists:seq(1, Bursts)], + Logged = count_lines(Log), + ok = stop_handler(?MODULE), + _ = file:delete(Log), + ct:pal("Number of messages dropped = ~w (~w)", + [Procs*NumOfReqs*Bursts-Logged,Procs*NumOfReqs*Bursts]), + true = (Logged < (Procs*NumOfReqs*Bursts)), + true = (Logged > 0), + ok + end, + %% As it's tricky to get the timing right in only one go, we perform the + %% test repeatedly, hoping that will generate a successful result. + case repeat_until_ok(Test, 10) of + {ok,{Failures,_Result}} -> + ct:log("Failed ~w times before success!", [Failures]); + {fails,Reason} -> + ct:fail(Reason) + end. op_switch_to_drop_file(cleanup, _Config) -> - ok = stop_handler(?MODULE). + _ = stop_handler(?MODULE). op_switch_to_drop_tty(Config) -> {HConfig,StdHConfig} = start_handler(?MODULE, standard_io, Config), @@ -797,7 +797,8 @@ op_switch_to_drop_tty(Config) -> NewHConfig = HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 1, drop_new_reqs_qlen => 2, - flush_reqs_qlen => Procs*NumOfReqs+1, + flush_reqs_qlen => + Procs*NumOfReqs+1, enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), @@ -806,32 +807,56 @@ op_switch_to_drop_tty(cleanup, _Config) -> ok = stop_handler(?MODULE). op_switch_to_flush_file() -> - [{timetrap,{seconds,60}}]. + [{timetrap,{seconds,180}}]. op_switch_to_flush_file(Config) -> - {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), - - %% it's important that both async and sync requests have been queued - %% when the flush happens (verify with coverage of flush_log_requests/2) - - NewHConfig = - HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 2, + Test = + fun() -> + {Log,HConfig,StdHConfig} = + start_handler(?MODULE, ?FUNCTION_NAME, Config), + + %% NOTE: it's important that both async and sync + %% requests have been queued when the flush happens + %% (verify with coverage of flush_log_requests/2) + + NewHConfig = + HConfig#{logger_std_h => + StdHConfig#{toggle_sync_qlen => 2, %% disable drop mode - drop_new_reqs_qlen => 500, - flush_reqs_qlen => 500, + drop_new_reqs_qlen => 300, + flush_reqs_qlen => 300, enable_burst_limit => false}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), - NumOfReqs = 1000, - Procs = 200, - send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), - Logged = count_lines(Log), - ct:pal("Number of messages flushed/dropped = ~w (~w)", - [(NumOfReqs*Procs)-Logged,NumOfReqs*Procs]), - true = (Logged < (NumOfReqs*Procs)), - true = (Logged > 0), - ok = file:delete(Log), - ok. + ok = logger:set_handler_config(?MODULE, NewHConfig), + NumOfReqs = 1500, + Procs = 10, + Bursts = 10, + %% It sometimes happens that the handler either gets + %% the requests in a slow enough pace so that flushing + %% never occurs, or it gets all messages at once, + %% causing all messages to get flushed (no dropping of + %% sync messages gets tested). Therefore, lets + %% generate a number of bursts to increase the chance + %% of message buildup in some random fashion. + [send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info) || + _ <- lists:seq(1,Bursts)], + Logged = count_lines(Log), + ok = stop_handler(?MODULE), + _ = file:delete(Log), + ct:pal("Number of messages flushed/dropped = ~w (~w)", + [NumOfReqs*Procs*Bursts-Logged,NumOfReqs*Procs*Bursts]), + true = (Logged < (NumOfReqs*Procs*Bursts)), + true = (Logged > 0), + ok + end, + %% As it's tricky to get the timing right in only one go, we perform the + %% test repeatedly, hoping that will generate a successful result. + case repeat_until_ok(Test, 10) of + {ok,{Failures,_Result}} -> + ct:log("Failed ~w times before success!", [Failures]); + {fails,Reason} -> + ct:fail(Reason) + end. op_switch_to_flush_file(cleanup, _Config) -> - ok = stop_handler(?MODULE). + _ = stop_handler(?MODULE). op_switch_to_flush_tty(Config) -> {HConfig,StdHConfig} = start_handler(?MODULE, standard_io, Config), @@ -931,10 +956,10 @@ kill_disabled(cleanup, _Config) -> ok = stop_handler(?MODULE). qlen_kill_new(Config) -> - {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + {_Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), Pid0 = whereis(?MODULE), {_,Mem0} = process_info(Pid0, memory), - RestartAfter = 2000, + RestartAfter = ?HANDLER_RESTART_AFTER, NewHConfig = HConfig#{logger_std_h=>StdHConfig#{enable_kill_overloaded=>true, handler_overloaded_qlen=>10, @@ -954,7 +979,7 @@ qlen_kill_new(Config) -> killed -> ct:pal("Slow shutdown, handler process was killed!", []) end, - timer:sleep(RestartAfter + 1000), + timer:sleep(RestartAfter + 2000), true = is_pid(whereis(?MODULE)), ok after @@ -968,7 +993,7 @@ qlen_kill_new(cleanup, _Config) -> %% choke the standard handler on remote node to verify the termination %% works as expected -qlen_kill_std(Config) -> +qlen_kill_std(_Config) -> %%! HERE %% Dir = ?config(priv_dir, Config), %% File = lists:concat([?MODULE,"_",?FUNCTION_NAME,".log"]), @@ -982,10 +1007,10 @@ qlen_kill_std(Config) -> {skip,"Not done yet"}. mem_kill_new(Config) -> - {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + {_Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), Pid0 = whereis(?MODULE), {_,Mem0} = process_info(Pid0, memory), - RestartAfter = 2000, + RestartAfter = ?HANDLER_RESTART_AFTER, NewHConfig = HConfig#{logger_std_h=>StdHConfig#{enable_kill_overloaded=>true, handler_overloaded_qlen=>50000, @@ -1005,7 +1030,7 @@ mem_kill_new(Config) -> killed -> ct:pal("Slow shutdown, handler process was killed!", []) end, - timer:sleep(RestartAfter * 2), + timer:sleep(RestartAfter + 2000), true = is_pid(whereis(?MODULE)), ok after @@ -1019,7 +1044,7 @@ mem_kill_new(cleanup, _Config) -> %% choke the standard handler on remote node to verify the termination %% works as expected -mem_kill_std(Config) -> +mem_kill_std(_Config) -> {skip,"Not done yet"}. restart_after(Config) -> @@ -1043,7 +1068,7 @@ restart_after(Config) -> end, {Log,_,_} = start_handler(?MODULE, ?FUNCTION_NAME, Config), - RestartAfter = 2000, + RestartAfter = ?HANDLER_RESTART_AFTER, NewHConfig2 = HConfig#{logger_std_h=>StdHConfig#{enable_kill_overloaded=>true, handler_overloaded_qlen=>10, @@ -1055,7 +1080,7 @@ restart_after(Config) -> send_burst({n,100}, {spawn,2,0}, {chars,79}, info), receive {'DOWN', MRef2, _, _, _Info2} -> - timer:sleep(RestartAfter + 1000), + timer:sleep(RestartAfter + 2000), Pid1 = whereis(?MODULE), true = is_pid(Pid1), false = (Pid1 == Pid0), @@ -1101,7 +1126,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]) -> @@ -1326,6 +1351,30 @@ try_match_file(_,Pattern,_,Incorrect) -> [Pattern,Incorrect]), erlang:error({error,not_matching_pattern,Pattern,Incorrect}). +repeat_until_ok(Fun, N) -> + repeat_until_ok(Fun, 0, N, undefined). + +repeat_until_ok(_Fun, Stop, Stop, Reason) -> + {fails,Reason}; + +repeat_until_ok(Fun, C, Stop, FirstReason) -> + if C > 0 -> timer:sleep(5000); + true -> ok + end, + try Fun() of + Result -> + {ok,{C,Result}} + catch + _:Reason:Stack -> + ct:pal("Test fails: ~p (~p)~n", [Reason,hd(Stack)]), + if FirstReason == undefined -> + repeat_until_ok(Fun, C+1, Stop, {Reason,Stack}); + true -> + repeat_until_ok(Fun, C+1, Stop, FirstReason) + end + end. + + %%%----------------------------------------------------------------- %%% start_op_trace() -> @@ -1366,17 +1415,17 @@ find_mode(flush, Events) -> find_mode(Mode, Events) -> lists:keymember([{mode,Mode}], 3, Events). -find_switch(From, To, Events) -> - try lists:foldl(fun({trace_return,check_load,{To,_,_,_}}, - {trace_call,check_load,[#{mode := From}]}) -> - throw(match); - (Event, _) -> - Event - end, undefined, Events) of - _ -> false - catch - throw:match -> true - end. +%% find_switch(_From, To, Events) -> +%% try lists:foldl(fun({trace_return,check_load,{To,_,_,_}}, +%% {trace_call,check_load,[#{mode := From}]}) -> +%% throw(match); +%% (Event, _) -> +%% Event +%% end, undefined, Events) of +%% _ -> false +%% catch +%% throw:match -> true +%% end. analyse_trace(TRecvPid, TestFun) -> TRecvPid ! {test,self(),TestFun}, -- cgit v1.2.3 From a178e9545725455d064efd3f039c5052918544d5 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 4 May 2018 15:01:54 +0200 Subject: Don't save included_applications as application environment variable During application load, the included_applications key from the .app file would earlier be duplicated as an application environment variable. Due to this, its value could be retrieved in any of the following ways: application:get_key(App,included_applications). application:get_env(App,included_applications). It would also be included in the resulting list from the following calls: application:get_all_key(App). application:get_all_env(App). This commit removes the duplication, and included_applications will no longer be returned by application:get_env/2 or application:get_all_env/1. The reason for this change is mainly to avoid confusion, but also to avoid the potensial inconsistency which would occur if the environment variable was changed during runtime by calls to application:put_env(App,included_applications,NewInclApps). --- lib/kernel/src/application_controller.erl | 12 +++--------- lib/kernel/test/application_SUITE.erl | 12 ++++-------- 2 files changed, 7 insertions(+), 17 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/application_controller.erl b/lib/kernel/src/application_controller.erl index b9cb722575..ff5df667b5 100644 --- a/lib/kernel/src/application_controller.erl +++ b/lib/kernel/src/application_controller.erl @@ -1272,9 +1272,7 @@ load(S, {ApplData, ApplEnv, IncApps, Descr, Id, Vsn, Apps}) -> NewEnv = merge_app_env(ApplEnv, ConfEnv), CmdLineEnv = get_cmd_env(Name), NewEnv2 = merge_app_env(NewEnv, CmdLineEnv), - NewEnv3 = keyreplaceadd(included_applications, 1, NewEnv2, - {included_applications, IncApps}), - add_env(Name, NewEnv3), + add_env(Name, NewEnv2), Appl = #appl{name = Name, descr = Descr, id = Id, vsn = Vsn, appl_data = ApplData, inc_apps = IncApps, apps = Apps}, ets:insert(ac_tab, {{loaded, Name}, Appl}), @@ -1292,7 +1290,7 @@ load(S, {ApplData, ApplEnv, IncApps, Descr, Id, Vsn, Apps}) -> {ok, NewS}. unload(AppName, S) -> - {ok, IncApps} = get_env(AppName, included_applications), + {ok, IncApps} = get_key(AppName, included_applications), del_env(AppName), ets:delete(ac_tab, {loaded, AppName}), foldl(fun(App, S1) -> @@ -1583,13 +1581,9 @@ do_change_appl({ok, {ApplData, Env, IncApps, Descr, Id, Vsn, Apps}}, CmdLineEnv = get_cmd_env(AppName), NewEnv2 = merge_app_env(NewEnv1, CmdLineEnv), - %% included_apps is made into an env parameter as well - NewEnv3 = keyreplaceadd(included_applications, 1, NewEnv2, - {included_applications, IncApps}), - %% Update ets table with new application env del_env(AppName), - add_env(AppName, NewEnv3), + add_env(AppName, NewEnv2), OldAppl#appl{appl_data=ApplData, descr=Descr, diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl index c00fb44c46..988f26280f 100644 --- a/lib/kernel/test/application_SUITE.erl +++ b/lib/kernel/test/application_SUITE.erl @@ -1603,8 +1603,7 @@ get_key(Conf) when is_list(Conf) -> {ok, [{init, [kalle]}, {takeover, []}, {go, [sune]}]} = rpc:call(Cp1, application, get_key, [appinc, start_phases]), {ok, Env} = rpc:call(Cp1, application, get_key, [appinc ,env]), - [{included_applications,[appinc1,appinc2]}, - {own2,val2},{own_env1,value1}] = lists:sort(Env), + [{own2,val2},{own_env1,value1}] = lists:sort(Env), {ok, []} = rpc:call(Cp1, application, get_key, [appinc, modules]), {ok, {application_starter, [ch_sup, {appinc, 41, 43}] }} = rpc:call(Cp1, application, get_key, [appinc, mod]), @@ -1625,8 +1624,7 @@ get_key(Conf) when is_list(Conf) -> {mod, {application_starter, [ch_sup, {appinc, 41, 43}] }}, {start_phases, [{init, [kalle]}, {takeover, []}, {go, [sune]}]}]} = rpc:call(Cp1, application, get_all_key, [appinc]), - [{included_applications,[appinc1,appinc2]}, - {own2,val2},{own_env1,value1}] = lists:sort(Env), + [{own2,val2},{own_env1,value1}] = lists:sort(Env), {ok, "Test of new app file, including appnew"} = gen_server:call({global, {ch,41}}, {get_pid_key, description}), @@ -1643,8 +1641,7 @@ get_key(Conf) when is_list(Conf) -> {ok, [{init, [kalle]}, {takeover, []}, {go, [sune]}]} = gen_server:call({global, {ch,41}}, {get_pid_key, start_phases}), {ok, Env} = gen_server:call({global, {ch,41}}, {get_pid_key, env}), - [{included_applications,[appinc1,appinc2]}, - {own2,val2},{own_env1,value1}] = lists:sort(Env), + [{own2,val2},{own_env1,value1}] = lists:sort(Env), {ok, []} = gen_server:call({global, {ch,41}}, {get_pid_key, modules}), {ok, {application_starter, [ch_sup, {appinc, 41, 43}] }} = @@ -1671,8 +1668,7 @@ get_key(Conf) when is_list(Conf) -> {mod, {application_starter, [ch_sup, {appinc, 41, 43}] }}, {start_phases, [{init, [kalle]}, {takeover, []}, {go, [sune]}]}]} = gen_server:call({global, {ch,41}}, get_pid_all_key), - [{included_applications,[appinc1,appinc2]}, - {own2,val2},{own_env1,value1}] = lists:sort(Env), + [{own2,val2},{own_env1,value1}] = lists:sort(Env), stop_node_nice(Cp1), ok. -- cgit v1.2.3 From 6aff9ab0a70e1ee3776d601c93f8697cde87b89a Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 2 May 2018 15:55:02 +0200 Subject: Improve test of logger_disk_log_h and logger_std_h --- lib/kernel/test/logger_disk_log_h_SUITE.erl | 55 +++++----- lib/kernel/test/logger_std_h_SUITE.erl | 163 +++++++++++++++++++--------- 2 files changed, 136 insertions(+), 82 deletions(-) (limited to 'lib/kernel') 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_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. -- cgit v1.2.3 From de2965827bfb903dcfcd0a97603548b2f8e3a807 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 2 May 2018 18:55:10 +0200 Subject: Change return type from logger_formatter:format/2 This used to be string() is now changed to unicode:chardata(). --- lib/kernel/doc/src/logger_chapter.xml | 22 ++++------------------ lib/kernel/src/logger_formatter.erl | 5 ++--- 2 files changed, 6 insertions(+), 21 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 78d6f46c95..36f5f6dea3 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -157,7 +157,7 @@

    A formatter is defined as a module exporting the following function:

    - format(Log,Extra) -> string() + format(Log,Extra) -> unicode:chardata()

    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 ignore.

    Default is log.

    - depth = pos_integer() | unlimited - -

    Specifies if the depth of terms in the log events shall - be limited by using control characters ~P - and ~W instead of ~p and ~w, - respectively. See - io:format.

    -
    - max_size = pos_integer() | unlimited - -

    Specifies if the size of a log event shall be limited by - truncating the formatted string.

    -
    formatter = {Module::module(),Extra::term()}

    See Formatter for more @@ -347,10 +334,9 @@

    Note that level and filters are obeyed by Logger itself before forwarding the log events to each - handler, while depth, max_size - and formatter are left to the handler - implementation. All Logger's built-in handlers do apply these - configuration parameters before printing.

    + handler, while formatter is left to the handler + implementation. All Logger's built-in handlers will call the + given formatter before printing.

    diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl index 5358652692..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), -- cgit v1.2.3 From 336e7eed4e44fe9267234e4702db6fd613064dcd Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 2 May 2018 18:57:23 +0200 Subject: Improve documentation of logger:set_*_config functions --- lib/kernel/doc/src/logger.xml | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index ded9c1fb37..2beefd1240 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -404,21 +404,55 @@ Current logger configuration: + Set configuration data for the logger. + +

    Set configuration data for the logger. This overwrites the + current logger configuration.

    +

    To modify the existing configuration, + use set_logger_config/2 + , or read the current configuration + with get_logger_config/0 + , then merge in your added or updated + associations before writing it back.

    +
    +
    + + Add or update configuration data for the logger. -

    Add or update configuration data for the logger.

    +

    Add or update configuration data for the logger. If the + given Key already exists, its associated + value will be set to Value. If it + doesn't exist, it will be added.

    + Set configuration data for the specified handler. + +

    Set configuration data for the specified handler. This + overwrites the current handler configuration.

    +

    To modify the existing configuration, + use set_handler_config/3 + , or read the current configuration + with get_handler_config/1 + , then merge in your added or updated + associations before writing it back.

    +
    +
    + + Add or update configuration data for the specified handler.

    Add or update configuration data for the specified - handler.

    + handler. If the + given Key already exists, its associated + value will be set to Value. If it + doesn't exist, it will be added.

    -- cgit v1.2.3 From 3cef0524e4332ebf547180ec0d0feafe0a559b5c Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 2 May 2018 19:20:27 +0200 Subject: Add logger:update_process_metadata/1 --- lib/kernel/doc/src/logger.xml | 22 ++++++++++++++++++---- lib/kernel/src/logger.erl | 17 +++++++++++++++-- lib/kernel/test/logger_SUITE.erl | 3 +++ 3 files changed, 36 insertions(+), 6 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index 2beefd1240..2f7feb5eef 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -471,10 +471,9 @@ Current logger configuration: Set metadata to use when logging from current process. -

    Set metadata which logger automatically inserts it - in all log events produced on the current - process. Subsequent calls will overwrite previous data set - by this function.

    +

    Set metadata which logger automatically inserts in + all log events produced on the current process. Subsequent + calls will overwrite previous data set by this function.

    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 @@ -485,6 +484,21 @@ Current logger configuration: + + + Update metadata to use when logging from current process. + +

    Update metadata to use when logging from current process

    +

    This function behaves as if it was implemented as follows:

    + +logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)) + +

    If no process metadata exists, the function behaves as + set_process_metadata/1 + .

    +
    + + Retrieve data set with set_process_metadata/1. diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 943ef8c2d1..5562764204 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]). @@ -390,6 +390,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() -> diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index 0edce3e34c..94fd87bdb0 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(), -- cgit v1.2.3 From d8e804043c6b4974943884cb15b86b40a5c870f1 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 3 May 2018 15:46:57 +0200 Subject: Don't crash logger_server due to unexpected message --- lib/kernel/src/logger_server.erl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index 6ef3b8582a..052036b8d0 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -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. -- cgit v1.2.3 From 3c9e4adff06a3f74513cb6658405e43e674133c1 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 4 May 2018 16:40:55 +0200 Subject: Change logger callback removing_handler/1 to removing_handler/2 --- lib/kernel/doc/src/logger_chapter.xml | 8 ++++---- lib/kernel/src/error_logger.erl | 6 +++--- lib/kernel/src/logger_disk_log_h.erl | 4 ++-- lib/kernel/src/logger_server.erl | 2 +- lib/kernel/src/logger_simple.erl | 4 ++-- lib/kernel/src/logger_std_h.erl | 4 ++-- lib/kernel/test/logger_SUITE.erl | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 36f5f6dea3..519df2ba48 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -503,7 +503,7 @@ ok

    It may also implement the following callbacks:

    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()}

    When logger:add_handler(Id,Module,Config) is called, logger @@ -513,7 +513,7 @@ changing_config(logger:handler_id(),logger:config(),logger:config()) -> {ok,logg events as calls to Module:log/2.

    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.

    When logger:set_handler_config is called, logger calls Module:changing_config(Id,OldConfig,NewConfig). If this function @@ -534,7 +534,7 @@ log(Log,#{formatter:={FModule,FConfig}) -> this:

    -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) -> @@ -563,7 +563,7 @@ log(Log,#{myhandler_fd:=Fd,formatter:={FModule,FConfig}}) -> single process.

    -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/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/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_server.erl b/lib/kernel/src/logger_server.erl index 052036b8d0..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; 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 94fd87bdb0..f311a9c7ed 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -723,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}) -> -- cgit v1.2.3 From bf0e9a16660a560db7c3e13d8021a4b7e87cfb27 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 8 May 2018 14:36:09 +0200 Subject: Update some specs in logger --- lib/kernel/src/logger.erl | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 5562764204..98a9937111 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -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(), @@ -712,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()]} | -- cgit v1.2.3 From 6f49591e7d5bf11d8bfc6014a0711ae59e3f3c33 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 8 May 2018 14:36:37 +0200 Subject: Update reference manuals for logger --- lib/kernel/doc/src/logger.xml | 371 +++++++++++++++++++++++++++----- lib/kernel/doc/src/logger_filters.xml | 14 +- lib/kernel/doc/src/logger_formatter.xml | 213 +++++++++++------- 3 files changed, 455 insertions(+), 143 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index 2f7feb5eef..d901454e62 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -67,37 +67,86 @@ -

    Metadata associated with the message to be logged.

    +

    Metadata for the log event.

    +

    Logger adds the following metadata to each log event:

    + + pid => self() + gl => group_leader() + time => erlang:monotonic_time(microsecond) + +

    When a log macro is used, Logger also inserts location + information:

    + + mfa => {?MODULE,?FUNCTION_NAME,?FUNCTION_ARITY} + file => ?FILE + line => ?LINE + +

    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 + set_process_metadata/1 + or + update_process_metadata/1.

    +

    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.

    -

    +

    Configuration data for the logger part of Logger, or for a handler.

    +

    The following default values apply:

    + + level => info + filter_default => log + filters => [] + formatter => {logger_formatter,DefaultFormatterConfig} + +

    See the + logger_formatter(3) manual page for + information about the default configuration for this + formatter.

    -

    +

    A unique identifier for a handler instance.

    -

    +

    A unique identifier for a filter.

    -

    +

    A filter which can be installed for logger or for a handler.

    +
    +
    + + + +

    The second argument to the filter fun.

    -

    +

    The return value from the filter fun.

    +
    +
    + + + +

    A timestamp produced + with + erlang:monotonic_time(microsecond).

    @@ -126,14 +175,10 @@

    All macros expand to a call to logger, where Level is - taken from the macro name, and the following metadata is added, - or merged with the given Metadata:

    - - -#{mfa=>{?MODULE,?FUNCTION_NAME,?FUNCTION_ARITY}, - file=>?FILE, - line=>?LINE} - + taken from the macro name, and location data is added. See the + description of + the metadata() + type for more information about the location data.

    The call is wrapped in a case statement and will be evaluated only if Level is equal to or below the configured log @@ -267,7 +312,7 @@ - Get information about all logger configurations + Get all logger configurations

    Same as logger:i(term)

    @@ -277,26 +322,29 @@ - Get information about all logger configurations + Get all logger configurations -

    The logger:i/1 function can be used to get all - current logger configuration. The way that the information - is returned depends on the Action

    +

    Display or return all current logger configuration.

    - string - Return the pretty printed current logger configuration - as iodata. - term - Return the current logger configuration as a term. The - format of this term may change inbetween releases. For a - stable format use + Action = string + +

    Return the pretty printed current logger configuration + as iodata.

    +
    + Action = term + +

    Return the current logger configuration as a term. The + format of this term may change inbetween releases. For a + stable format use logger:get_handler_config/1 - and + and logger:get_logger_config/0. - The same as calling logger:i(). - print - Pretty print all the current logger configuration to - standard out. Example: + The same as calling logger:i().

    +
    + Action = print + +

    Pretty print all the current logger configuration to + standard out. Example:

    logger:i(print). Current logger configuration: Level: info @@ -339,6 +387,39 @@ Current logger configuration: Add a filter to the logger.

    Add a filter to the logger.

    +

    The filter fun is called with the log event as the first + parameter, and the specified filter_args() as the + second parameter.

    +

    The return value of the fun specifies if a log event is to + be discarded or forwarded to the handlers:

    + + log() + +

    The filter passed. 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.

    +
    + stop + +

    The filter did not pass, and the log event is + immediately discarded.

    +
    + ignore + +

    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 filter_default + configuration parameter for the logger specifies if the + log event shall be discarded or forwarded to the handler + part.

    +
    +
    +

    See section + Filter in the User's Guide for more information + about filters.

    +

    Some built-in filters exist. These are defined + in logger_filters.

    @@ -347,6 +428,39 @@ Current logger configuration: Add a filter to the specified handler.

    Add a filter to the specified handler.

    +

    The filter fun is called with the log event as the first + parameter, and the specified filter_args() as the + second parameter.

    +

    The return value of the fun specifies if a log event is to + be discarded or forwarded to the handler callback:

    + + log() + +

    The filter passed. 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.

    +
    + stop + +

    The filter did not pass, and the log event is + immediately discarded.

    +
    + ignore + +

    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 filter_default configuration parameter for + the handler specifies if the log event shall be + discarded or forwarded to the handler callback.

    +
    +
    +

    See + section Filter + in the User's Guide for more information about filters.

    +

    Some built-in filters exist. These are defined in + logger_filters.

    @@ -354,7 +468,8 @@ Current logger configuration: Remove a filter from the logger. -

    Remove the filter with the specified identity from the logger.

    +

    Remove the filter identified + by FilterId from the logger.

    @@ -362,7 +477,9 @@ Current logger configuration: Remove a filter from the specified handler. -

    Remove the filter with the specified identity from the given handler.

    +

    Remove the filter identified + by FilterId from the handler identified + by HandlerId.

    @@ -371,6 +488,9 @@ Current logger configuration: Add a handler with the given configuration.

    Add a handler with the given configuration.

    +

    HandlerId is a unique identifier which + must be used in all subsequent calls reffering to this + handler.

    @@ -378,7 +498,7 @@ Current logger configuration: Remove the handler with the specified identity. -

    Remove the handler with the specified identity.

    +

    Remove the handler identified by HandlerId.

    @@ -386,10 +506,37 @@ Current logger configuration: Set the log level for the specified module. -

    Set the log level for the specified module.

    -

    To change the logging level globally, use - logger:set_logger_config(level, Level). -

    +

    Set the log level for the + specified Module.

    +

    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.

    +

    For example: Assume that the global log level for the + logger is info, and there is one handler, h1, + with level info and one handler, h2, with + level debug.

    +

    With this configuration, no debug messages will be logged, + since they are all stopped by the global log level.

    +

    If the level for mymodule is set now set + to debug, then debug events from this module will be + logged by the handler h2, but not by + handler h1.

    +

    Debug events from other modules are still not logged.

    +

    To change the global log level for the logger, use + + logger:set_logger_config(level,Level).

    +

    To change the log level for a handler, use + + logger:set_handler_config(HandlerId,level,Level).

    + +

    The originating module for a log event is only detected + if mfa=>{Module,Function,Arity} 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.

    +
    @@ -414,6 +561,8 @@ Current logger configuration: with get_logger_config/0 , then merge in your added or updated associations before writing it back.

    +

    If a key is removed compared to the current configuration, + the default value is used.

    @@ -423,7 +572,7 @@ Current logger configuration:

    Add or update configuration data for the logger. If the given Key already exists, its associated - value will be set to Value. If it + value will be changed to Value. If it doesn't exist, it will be added.

    @@ -440,6 +589,11 @@ Current logger configuration: with get_handler_config/1 , then merge in your added or updated associations before writing it back.

    +

    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.

    @@ -449,10 +603,10 @@ Current logger configuration: handler.

    Add or update configuration data for the specified - handler. If the - given Key already exists, its associated - value will be set to Value. If it - doesn't exist, it will be added.

    + handler. If the given Key already + exists, its associated value will be changed + to Value. If it doesn't exist, it will + be added.

    @@ -471,30 +625,36 @@ Current logger configuration: Set metadata to use when logging from current process. -

    Set metadata which logger automatically inserts in - all log events produced on the current process. Subsequent - calls will overwrite previous data set by this function.

    -

    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.

    +

    Set metadata which Logger shall automatically insert in + all log events produced on the current process.

    +

    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.

    +

    Subsequent calls to this function overwrites previous data + set. To update existing data instead of overwriting it, + see + update_process_metadata/1.

    - Update metadata to use when logging from current process. + Set or update metadata to use when logging from + current process. -

    Update metadata to use when logging from current process

    -

    This function behaves as if it was implemented as follows:

    +

    Set or update metadata to use when logging from current + process

    +

    If process metadata exists for the current process, this + function behaves as if it was implemented as follows:

    logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta))

    If no process metadata exists, the function behaves as - set_process_metadata/1 + + set_process_metadata/1 .

    @@ -505,7 +665,9 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta))

    Retrieve data set with - set_process_metadata/1.

    + set_process_metadata/1 or + + update_process_metadata/1.

    @@ -515,12 +677,103 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta))

    Delete data set with - set_process_metadata/1.

    + set_process_metadata/1 or + + update_process_metadata/1.

    +
    + + + + + Convert a log message on report form to {Format,Args}. + +

    Convert a log message on report form to {Format,Args}.

    +

    This is the default report callback used + by + logger_formatter when no custom report + callback is found.

    +

    The function produces lines of Key: Value from + key-value lists. Strings are printed with ~ts and + other terms with ~tp.

    +

    If the Report is a map, it is + converted to a key-value list before formatting as such.

    +
    + Callback Functions +

    The following functions are to be exported from a handler + callback module.

    +
    + + + + Module:adding_handler(HandlerId,Config1) -> {ok,Config2} | {error,Reason} + An instance of this handler is about to be added. + + HandlerId = + handler_id() + Config1 = Config2 = + config() + Reason = term() + + +

    This callback function is optional.

    +

    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.

    +

    If everything succeeds, the callback function can add + possible default values or internal state values to the + configuration, and return the adjusted map + in {ok,Config2}.

    +

    If the configuration is faulty, or if the initiation fails, + the callback function must return {error,Reason}.

    +
    +
    + + + Module:removing_handler(HandlerId,Config) -> ok + The given handler is about to be removed. + + HandlerId = + handler_id() + Config = + config() + + +

    This callback function is optional.

    +

    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.

    +
    +
    + + + Module:changing_config(HandlerId,Config1,Config2) -> {ok,Config3} | {error,Reason} + The configuration for this handler is about to change. + + HandlerId = + handler_id() + Config1 = Config2 = Config3 = + config() + Reason = term() + + +

    This callback function is optional.

    +

    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.

    +

    Config1 is the existing configuration + and Config2 is the new configuration.

    +

    If everything succeeds, the callback function must return a + possibly adjusted configuration in {ok,Config3}.

    +

    If the configuration is faulty, the callback function must + return {error,Reason}.

    +
    +
    +
    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 @@ logger_filters.xml logger_filters - Filters to use with logger. + Filters to use with Logger. -

    Filters to use with logger. All functions exported from this - module can be used as logger or handler +

    All functions exported from this module can be used as logger + or handler filters. See logger:add_logger_filter/2 and - logger:add_handler_filter/3 - for more information about how filters are added.

    + logger:add_handler_filter/3 for more information + about how filters are added.

    +

    Filters are removed with + logger:remove_logger_filter/1 + and + logger:remove_handler_filter/2.

    diff --git a/lib/kernel/doc/src/logger_formatter.xml b/lib/kernel/doc/src/logger_formatter.xml index a0940100ee..7df4c88f40 100644 --- a/lib/kernel/doc/src/logger_formatter.xml +++ b/lib/kernel/doc/src/logger_formatter.xml @@ -33,43 +33,38 @@ logger_formatter.xml logger_formatter - Default formatter for the Logger application. + Default formatter for Logger. -

    Default formatter for the Logger application.

    - +

    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 + (unicode:chardata()) which can be written to the output + device of the handler.

    +

    logger_formatter is the default formatter used by + Logger.

    Configuration -

    The following configuration parameters can be set - for logger_formatter:

    +

    The configuration term for logger_formatter is a map, + and the following keys can be set as configuration + parameters:

    - single_line = boolean() - -

    If set to true, all newlines in the message are - replaced with ", ", and whitespaces following - directly after newlines are removed. Note that newlines - added by the formatter template are not replaced.

    -

    Default is true.

    -
    - legacy_header = boolen() - -

    If set to true a header field is added to - logger_formatter's part of Metadata. The value of - this field is a string similar to the header created by the - old error_logger event handlers. It can be included - in the log event by adding the - tuple {logger_formatter,header} to the - template. See Default - Templates for more information

    -

    Default is false.

    -
    - report_cb = fun((logger:report()) -> {io:format(),[term()]}) + chars_limit = pos_integer() | unlimited -

    A function with arity 1, - returning {Format,Args}. This function will replace - any report_cb found in metadata.

    +

    A positive integer representing the value of the option + with the same name to be used when calling + io_lib:format/3. + 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 max_size.

    +

    Default is unlimited.

    + +

    chars_limit has no effect on log messages on + string form. These are expected to be short, but can still + be truncated by the max_size parameter.

    +
    depth = pos_integer() | unlimited @@ -82,47 +77,79 @@ io:format/2,3 in STDLIB.

    Default is unlimited.

    -
    - chars_limit = pos_integer() | unlimited - -

    A positive integer representing the value of the option - with the same name to be used when calling - io_lib:format/3. - This value limits the total number of characters printed - for each log event. Note that this is a soft limit. For a - hard truncation limit, see option max_size.

    -

    Default is unlimited.

    + +

    depth has no effect on log messages on string + form. These are expected to be short, but can still be + truncated by the max_size parameter.

    +
    max_size = pos_integer() | unlimited -

    A positive integer representing the absolute maximum size - a string returned from this formatter can have. If the +

    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 depth and/or chars_limit, it will be - truncated.

    + by chars_limit or depth, it is truncated.

    Default is unlimited.

    + single_line = boolean() + +

    If set to true, all newlines in the message are + replaced with ", ", and whitespaces following + directly after newlines are removed. Note that newlines + added by the template parameter are not replaced.

    +

    Default is true.

    +
    + legacy_header = boolen() + +

    If set to true a header field is added to + logger_formatter's part of Metadata. The value of + this field is a string similar to the header created by the + old error_logger event handlers. It can be included + in the log event by adding the + tuple {logger_formatter,header} to the template. See + section Default + Templates for more information.

    +

    Default is false.

    +
    + report_cb = fun((logger:report()) -> {io:format(),[term()]}) + +

    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, logger_formatter will + use + logger:format_report/1 as default + callback.

    +

    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.

    +

    The value must be a function with arity 1, + returning {Format,Args}, and it will be called with a + report as only argument.

    +
    template = template()

    The template is a list of atoms, tuples and strings. The atoms level or msg, 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 in a nested map. For example the - tuple {key1,key2} will be replaced by the value of + 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 {key1,key2} is replaced by the value of the key2 field in the nested map below. The - atom key1 on its own would be replaced by the - complete value of the key1 field. The values are - converted to strings.

    + atom key1 on its own is replaced by the complete + value of the key1 field. The values are converted to + strings.

    #{key1=>#{key2=>my_value, ...} ...} -

    Strings are printed literally.

    +

    Strings in the template are printed literally.

    The default template differs depending on the values of legacy_header and single_line. See Default @@ -131,7 +158,8 @@ utc = boolean()

    If set to true, all dates are displayed in Universal - Coordinated Time. Default is false.

    + Coordinated Time.

    +

    Default is false.

    @@ -139,38 +167,51 @@
    Default templates -

    The default template when legacy_header=true is

    - - [{logger_formatter,header},"\n",msg,"\n"] - -

    which will cause log entries like this:

    - - =ERROR REPORT==== 29-Dec-2017::13:30:51.245123 === - process: <0.74.0> - exit_reason: "Something went wrong" - -

    Note that all eight levels might occur here, not - only ERROR, WARNING or INFO. And also that - micro seconds are added at the end of the timestamp.

    - -

    The default template when single_line=true is

    - [time," ",level,": ",msg,"\n"] +

    The default value for the template configuration + parameter depends on the value of single_line + and legacy_header as follows.

    -

    which will cause log entries like this:

    +

    The log event used in the examples is:

    + +?LOG_ERROR("name: ~p~nexit_reason: ~p",[my_reg_name,"It crashed"]) - 2017-12-29 13:31:49.640317 error: process: <0.74.0>, exit_reason: "Something went wrong" + + legacy_header=true + +

    Default template: [{logger_formatter,header},"\n",msg,"\n"]

    + +

    Example log entry:

    + +=ERROR REPORT==== 29-Dec-2017::13:30:51.245123 === +name: my_reg_name +exit_reason: "It crashed" + +

    Notice that all eight levels might occur in the heading, + not only ERROR, WARNING or INFO as the + old error_logger produced. And microseconds are + added at the end of the timestamp.

    +
    -

    The default template when both legacy_header and - single_line are set to false is:

    + single_line=true + +

    Default template: [time," ",level,": ",msg,"\n"]

    - [time," ",level,":\n",msg,"\n"] +

    Example log entry:

    + 2017-12-29 13:31:49.640317 error: name: my_reg_name, exit_reason: "It crashed" +
    -

    which will cause log entries like this:

    + legacy_header=false, single_line=false + +

    Default template: [time," ",level,":\n",msg,"\n"]

    - 2017-12-29 13:32:25.191925 error: - process: <0.74.0> - exit_reason: "Something went wrong" +

    Example log entry:

    + +2017-12-29 13:32:25.191925 error: +name: my_reg_name +exit_reason: "It crashed" +
    +
    @@ -186,8 +227,22 @@ Formats the given message. -

    This the callback function to be called from handlers. It - formats the given messages.

    +

    This the formatter callback function to be called from + handlers. The log event is processed as follows:

    + + If the message is on report form, it is converted to + {Format,Args} by calling the report + callback. + The size is limited according to the values of + configuration parameters chars_limit + and depth. Notice that this does not apply to + messages on string form. + The full log entry is composed according to + the template. + If the final string is too long, it is truncated + according to the value of configuration + parameter max_size. +
    -- cgit v1.2.3 From cafff554ed7c8b820a7758b10aa32fc699f341eb Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 3 May 2018 14:50:17 +0200 Subject: kernel: Remove doc for unused browser_cmd variable --- lib/kernel/doc/src/kernel_app.xml | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index f96d946a5d..7894600c21 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -122,21 +122,6 @@ application. For more information about configuration parameters, see file app(4).

    - browser_cmd = string() | {M,F,A} - -

    When pressing the Help button in a tool such as Debugger, - the help text (an HTML file File) is by default - displayed in a Netscape browser, which is required to be - operational. This parameter can be used to change the command for - how to display the help text if another browser than Netscape - is preferred, or if another platform than Unix or Windows is - used.

    -

    If set to a string Command, the command - "Command File" is evaluated using - os:cmd/1.

    -

    If set to a module-function-args tuple, {M,F,A}, - the call apply(M,F,[File|A]) is evaluated.

    -
    distributed = [Distrib]

    Specifies which applications that are distributed and on which -- cgit v1.2.3 From e785f86176f6b8d82377cb05a79c8b932e2b468d Mon Sep 17 00:00:00 2001 From: Chris Spiegel Date: Sun, 13 May 2018 13:40:48 -0700 Subject: Add typespecs for netns and bind_to_device options Some networking functions accept the options netns (to switch network namespaces) and bind_to_device (to bind to a device with SO_BINDTODEVICE), but these functions are not annotated to accept these options, which causes dialyzer to raise issues. This patch applies these type specs to the options for gen_tcp:connect/3,4, gen_tcp:listen/2, gen_udp:open/1,2, and gen_sctp:open/0,1,2, as these are the documented functions which accept the netns and bind_to_device options. --- lib/kernel/src/gen_sctp.erl | 2 ++ lib/kernel/src/gen_tcp.erl | 4 ++++ lib/kernel/src/gen_udp.erl | 2 ++ 3 files changed, 8 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl index a6aa0edd15..3526df3600 100644 --- a/lib/kernel/src/gen_sctp.erl +++ b/lib/kernel/src/gen_sctp.erl @@ -118,6 +118,8 @@ open() -> | inet:address_family() | {port,Port} | {type,SockType} + | {netns, file:filename_all()} + | {bind_to_device, binary()} | option(), IP :: inet:ip_address() | any | loopback, Port :: inet:port_number(), diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl index ac61dbc792..253c63528f 100644 --- a/lib/kernel/src/gen_tcp.erl +++ b/lib/kernel/src/gen_tcp.erl @@ -102,6 +102,8 @@ inet:address_family() | {port, inet:port_number()} | {tcp_module, module()} | + {netns, file:filename_all()} | + {bind_to_device, binary()} | option(). -type listen_option() :: {ip, inet:socket_address()} | @@ -111,6 +113,8 @@ {port, inet:port_number()} | {backlog, B :: non_neg_integer()} | {tcp_module, module()} | + {netns, file:filename_all()} | + {bind_to_device, binary()} | option(). -type socket() :: port(). diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl index 3121544719..9ab58011ec 100644 --- a/lib/kernel/src/gen_udp.erl +++ b/lib/kernel/src/gen_udp.erl @@ -97,6 +97,8 @@ open(Port) -> | {ifaddr, inet:socket_address()} | inet:address_family() | {port, inet:port_number()} + | {netns, file:filename_all()} + | {bind_to_device, binary()} | option(), Socket :: socket(), Reason :: inet:posix(). -- cgit v1.2.3 From 6dcc96f4c8a14e05398a1ee8c4c5ccc7f2fbe778 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 3 May 2018 20:20:52 +0200 Subject: kernel: Refactor erl_signal_handler startup --- lib/kernel/src/erl_signal_handler.erl | 11 ++++++++++- lib/kernel/src/kernel.erl | 8 +------- 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/erl_signal_handler.erl b/lib/kernel/src/erl_signal_handler.erl index 22f235d4e4..b76c2a217a 100644 --- a/lib/kernel/src/erl_signal_handler.erl +++ b/lib/kernel/src/erl_signal_handler.erl @@ -19,12 +19,21 @@ -module(erl_signal_handler). -behaviour(gen_event). --export([init/1, format_status/2, +-export([start/0, init/1, format_status/2, handle_event/2, handle_call/2, handle_info/2, terminate/2, code_change/3]). -record(state,{}). +start() -> + %% add signal handler + case whereis(erl_signal_server) of + %% in case of minimal mode + undefined -> ok; + _ -> + gen_event:add_handler(erl_signal_server, erl_signal_handler, []) + end. + init(_Args) -> {ok, #state{}}. diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl index 20aa47f602..ae982c1741 100644 --- a/lib/kernel/src/kernel.erl +++ b/lib/kernel/src/kernel.erl @@ -32,13 +32,7 @@ start(_, []) -> case supervisor:start_link({local, kernel_sup}, kernel, []) of {ok, Pid} -> - %% add signal handler - case whereis(erl_signal_server) of - %% in case of minimal mode - undefined -> ok; - _ -> - ok = gen_event:add_handler(erl_signal_server, erl_signal_handler, []) - end, + ok = erl_signal_handler:start(), %% add error handler case logger:setup_standard_handler() of ok -> {ok, Pid, []}; -- cgit v1.2.3 From af60ccf4b2b176ff10a16b5285efbeaf4d16d3af Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 3 May 2018 20:21:59 +0200 Subject: kernel: Fix comment in kernel_config The error_logger is no longer added here. --- lib/kernel/src/kernel_config.erl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/kernel_config.erl b/lib/kernel/src/kernel_config.erl index 535083ef27..c5ff1887c2 100644 --- a/lib/kernel/src/kernel_config.erl +++ b/lib/kernel/src/kernel_config.erl @@ -30,11 +30,8 @@ %%%----------------------------------------------------------------- %%% This module implements a process that configures the kernel %%% application. -%%% Its purpose is that in the init phase add an error_logger -%%% and when it dies (when the kernel application dies) deleting the -%%% previously installed error_logger. -%%% Also, this process waits for other nodes at startup, if -%%% specified. +%%% Its purpose is that in the init phase waits for other nodes at startup, +%%% if specified. %%%----------------------------------------------------------------- start_link() -> gen_server:start_link(kernel_config, [], []). -- cgit v1.2.3 From 662f3c7ba50ff8ec13d86171bcfc61fd3da9deed Mon Sep 17 00:00:00 2001 From: Timmo Verlaan Date: Mon, 29 Jan 2018 21:27:22 +0100 Subject: epmd: allow alternative to dns resolving for nodename This makes it possible to create a custom integration with a key-value store for example. The key would then point to the actual address. You would have to write your own epmd module to make use of that feature. --- lib/kernel/doc/src/Makefile | 1 + lib/kernel/doc/src/erl_epmd.xml | 104 +++++++++++++++++++++++++++ lib/kernel/doc/src/ref_man.xml | 1 + lib/kernel/doc/src/specs.xml | 1 + lib/kernel/src/erl_epmd.erl | 64 ++++++++++++++++- lib/kernel/src/inet_tcp_dist.erl | 148 +++++++++++++++++++++++---------------- 6 files changed, 254 insertions(+), 65 deletions(-) create mode 100644 lib/kernel/doc/src/erl_epmd.xml (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile index 82869d7b15..29dc73a523 100644 --- a/lib/kernel/doc/src/Makefile +++ b/lib/kernel/doc/src/Makefile @@ -42,6 +42,7 @@ XML_REF3_FILES = application.xml \ disk_log.xml \ erl_boot_server.xml \ erl_ddll.xml \ + erl_epmd.xml \ erl_prim_loader_stub.xml \ erlang_stub.xml \ error_handler.xml \ diff --git a/lib/kernel/doc/src/erl_epmd.xml b/lib/kernel/doc/src/erl_epmd.xml new file mode 100644 index 0000000000..8b076cd2d7 --- /dev/null +++ b/lib/kernel/doc/src/erl_epmd.xml @@ -0,0 +1,104 @@ + + + + +

    + + 20182018 + Ericsson AB. 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. + + + + erl_epmd + Timmo Verlaan + 1 + 2018-02-19 + A +
    + erl_epmd + + Erlang interface towards epmd + + +

    This module communicates with the EPMD daemon, see epmd. To implement your own epmd module please + see ERTS User's Guide: How to Implement an + Alternative Service Discovery for Erlang Distribution

    +
    + + + + + Callback for erl_distribution supervisor. + +

    This function is invoked as this module is added as a child of the + erl_distribution supervisor.

    +
    +
    + + + + + Registers the node with epmd. + +

    Registers the node with epmd and tells epmd what port will be + used for the current node. It returns a creation number. This number is + incremented on each register to help with identifying if a node is + reconnecting to epmd.

    +
    +
    + + + + + Returns the port number for a given node. + +

    Requests the distribution port for the given node of an EPMD + instance. Together with the port it returns a distribution protocol + version which has been 5 since Erlang/OTP R6.

    +
    +
    + + + + Returns address and port. + +

    Called by the distribution module. Resolves the Host to an IP + address.

    +

    Another epmd module may return port and distribution protocol version + as well.

    +
    +
    + + + + Names of Erlang nodes at a host. + +

    Called by net_adm:names/0. + Host defaults to the localhost. Returns the names and associated + port numbers of the Erlang nodes that epmd registered at the + specified host. Returns {error, address} if epmd is not + operational.

    +

    Example:

    +
    +(arne@dunn)1> erl_epmd:names(localhost).
    +{ok,[{"arne",40262}]}
    +
    +
    +
    + + + diff --git a/lib/kernel/doc/src/ref_man.xml b/lib/kernel/doc/src/ref_man.xml index c06914d23d..a633ae4832 100644 --- a/lib/kernel/doc/src/ref_man.xml +++ b/lib/kernel/doc/src/ref_man.xml @@ -38,6 +38,7 @@ + diff --git a/lib/kernel/doc/src/specs.xml b/lib/kernel/doc/src/specs.xml index bcc422930e..b8c25ca53b 100644 --- a/lib/kernel/doc/src/specs.xml +++ b/lib/kernel/doc/src/specs.xml @@ -6,6 +6,7 @@ + diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl index f96bc88913..9a0939972d 100644 --- a/lib/kernel/src/erl_epmd.erl +++ b/lib/kernel/src/erl_epmd.erl @@ -29,10 +29,20 @@ -define(port_please_failure2(Term), noop). -endif. +-ifndef(erlang_daemon_port). +-define(erlang_daemon_port, 4369). +-endif. +-ifndef(epmd_dist_high). +-define(epmd_dist_high, 4370). +-endif. +-ifndef(epmd_dist_low). +-define(epmd_dist_low, 4370). +-endif. + %% External exports -export([start/0, start_link/0, stop/0, port_please/2, port_please/3, names/0, names/1, - register_node/2, register_node/3, open/0, open/1, open/2]). + register_node/2, register_node/3, address_please/3, open/0, open/1, open/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, @@ -53,7 +63,7 @@ start() -> gen_server:start({local, erl_epmd}, ?MODULE, [], []). - +-spec start_link() -> {ok, pid()} | ignore | {error,term()}. start_link() -> gen_server:start_link({local, erl_epmd}, ?MODULE, [], []). @@ -66,9 +76,22 @@ stop() -> %% return {port, P, Version} | noport %% +-spec port_please(Name, Host) -> {ok, Port, Version} | noport when + Name :: string(), + Host :: inet:ip_address(), + Port :: non_neg_integer(), + Version :: non_neg_integer(). + port_please(Node, Host) -> port_please(Node, Host, infinity). +-spec port_please(Name, Host, Timeout) -> {ok, Port, Version} | noport when + Name :: string(), + Host :: inet:ip_address(), + Timeout :: non_neg_integer() | infinity, + Port :: non_neg_integer(), + Version :: non_neg_integer(). + port_please(Node,HostName, Timeout) when is_atom(HostName) -> port_please1(Node,atom_to_list(HostName), Timeout); port_please(Node,HostName, Timeout) when is_list(HostName) -> @@ -92,10 +115,21 @@ port_please1(Node,HostName, Timeout) -> Else end. +-spec names() -> {ok, [{Name, Port}]} | {error, Reason} when + Name :: string(), + Port :: non_neg_integer(), + Reason :: address | file:posix(). + names() -> {ok, H} = inet:gethostname(), names(H). +-spec names(Host) -> {ok, [{Name, Port}]} | {error, Reason} when + Host :: atom() | string() | inet:ip_address(), + Name :: string(), + Port :: non_neg_integer(), + Reason :: address | file:posix(). + names(HostName) when is_atom(HostName); is_list(HostName) -> case inet:gethostbyname(HostName) of {ok,{hostent, _Name, _ , _Af, _Size, [EpmdAddr | _]}} -> @@ -106,9 +140,22 @@ names(HostName) when is_atom(HostName); is_list(HostName) -> names(EpmdAddr) -> get_names(EpmdAddr). +-spec register_node(Name, Port) -> Result when + Name :: string(), + Port :: non_neg_integer(), + Creation :: non_neg_integer(), + Result :: {ok, Creation} | {error, already_registered} | term(). register_node(Name, PortNo) -> - register_node(Name, PortNo, inet). + register_node(Name, PortNo, inet). + +-spec register_node(Name, Port, Driver) -> Result when + Name :: string(), + Port :: non_neg_integer(), + Driver :: inet_tcp | inet6_tcp | inet | inet6, + Creation :: non_neg_integer(), + Result :: {ok, Creation} | {error, already_registered} | term(). + register_node(Name, PortNo, inet_tcp) -> register_node(Name, PortNo, inet); register_node(Name, PortNo, inet6_tcp) -> @@ -116,6 +163,17 @@ register_node(Name, PortNo, inet6_tcp) -> register_node(Name, PortNo, Family) -> gen_server:call(erl_epmd, {register, Name, PortNo, Family}, infinity). +-spec address_please(Name, Host, AddressFamily) -> Success | {error, term()} when + Name :: string(), + Host :: string() | inet:ip_address(), + AddressFamily :: inet | inet6, + Port :: non_neg_integer(), + Version :: non_neg_integer(), + Success :: {ok, inet:ip_address()} | {ok, inet:ip_address(), Port, Version}. + +address_please(_Name, Host, AddressFamily) -> + inet:getaddr(Host, AddressFamily). + %%%---------------------------------------------------------------------- %%% Callback functions from gen_server %%%---------------------------------------------------------------------- diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl index e3fdb1bb22..b4b50899f7 100644 --- a/lib/kernel/src/inet_tcp_dist.erl +++ b/lib/kernel/src/inet_tcp_dist.erl @@ -283,73 +283,22 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) -> ?trace("~p~n",[{inet_tcp_dist,self(),setup,Node}]), [Name, Address] = splitnode(Driver, Node, LongOrShortNames), AddressFamily = Driver:family(), - case inet:getaddr(Address, AddressFamily) of + ErlEpmd = net_kernel:epmd_module(), + {ARMod, ARFun} = get_address_resolver(ErlEpmd), + Timer = dist_util:start_timer(SetupTime), + case ARMod:ARFun(Name, Address, AddressFamily) of + {ok, Ip, TcpPort, Version} -> + ?trace("address_please(~p) -> version ~p~n", + [Node,Version]), + do_setup_connect(Driver, Kernel, Node, Address, AddressFamily, + Ip, TcpPort, Version, Type, MyNode, Timer); {ok, Ip} -> - Timer = dist_util:start_timer(SetupTime), - ErlEpmd = net_kernel:epmd_module(), case ErlEpmd:port_please(Name, Ip) of {port, TcpPort, Version} -> ?trace("port_please(~p) -> version ~p~n", [Node,Version]), - dist_util:reset_timer(Timer), - case - Driver:connect( - Ip, TcpPort, - connect_options([{active, false}, {packet, 2}])) - of - {ok, Socket} -> - HSData = #hs_data{ - kernel_pid = Kernel, - other_node = Node, - this_node = MyNode, - socket = Socket, - timer = Timer, - this_flags = 0, - other_version = Version, - f_send = fun Driver:send/2, - f_recv = fun Driver:recv/3, - f_setopts_pre_nodeup = - fun(S) -> - inet:setopts - (S, - [{active, false}, - {packet, 4}, - nodelay()]) - end, - f_setopts_post_nodeup = - fun(S) -> - inet:setopts - (S, - [{active, true}, - {deliver, port}, - {packet, 4}, - nodelay()]) - end, - - f_getll = fun inet:getll/1, - f_address = - fun(_,_) -> - #net_address{ - address = {Ip,TcpPort}, - host = Address, - protocol = tcp, - family = AddressFamily} - end, - mf_tick = fun(S) -> ?MODULE:tick(Driver, S) end, - mf_getstat = fun ?MODULE:getstat/1, - request_type = Type, - mf_setopts = fun ?MODULE:setopts/2, - mf_getopts = fun ?MODULE:getopts/2 - }, - dist_util:handshake_we_started(HSData); - _ -> - %% Other Node may have closed since - %% port_please ! - ?trace("other node (~p) " - "closed since port_please.~n", - [Node]), - ?shutdown(Node) - end; + do_setup_connect(Driver, Kernel, Node, Address, AddressFamily, + Ip, TcpPort, Version, Type, MyNode, Timer); _ -> ?trace("port_please (~p) " "failed.~n", [Node]), @@ -361,6 +310,71 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) -> ?shutdown(Node) end. +%% +%% Actual setup of connection +%% +do_setup_connect(Driver, Kernel, Node, Address, AddressFamily, + Ip, TcpPort, Version, Type, MyNode, Timer) -> + dist_util:reset_timer(Timer), + case + Driver:connect( + Ip, TcpPort, + connect_options([{active, false}, {packet, 2}])) + of + {ok, Socket} -> + HSData = #hs_data{ + kernel_pid = Kernel, + other_node = Node, + this_node = MyNode, + socket = Socket, + timer = Timer, + this_flags = 0, + other_version = Version, + f_send = fun Driver:send/2, + f_recv = fun Driver:recv/3, + f_setopts_pre_nodeup = + fun(S) -> + inet:setopts + (S, + [{active, false}, + {packet, 4}, + nodelay()]) + end, + f_setopts_post_nodeup = + fun(S) -> + inet:setopts + (S, + [{active, true}, + {deliver, port}, + {packet, 4}, + nodelay()]) + end, + + f_getll = fun inet:getll/1, + f_address = + fun(_,_) -> + #net_address{ + address = {Ip,TcpPort}, + host = Address, + protocol = tcp, + family = AddressFamily} + end, + mf_tick = fun(S) -> ?MODULE:tick(Driver, S) end, + mf_getstat = fun ?MODULE:getstat/1, + request_type = Type, + mf_setopts = fun ?MODULE:setopts/2, + mf_getopts = fun ?MODULE:getopts/2 + }, + dist_util:handshake_we_started(HSData); + _ -> + %% Other Node may have closed since + %% discovery ! + ?trace("other node (~p) " + "closed since discovery (port_please).~n", + [Node]), + ?shutdown(Node) + end. + connect_options(Opts) -> case application:get_env(kernel, inet_dist_connect_options) of {ok,ConnectOpts} -> @@ -429,6 +443,16 @@ get_tcp_address(Driver, Socket) -> family = Driver:family() }. +%% ------------------------------------------------------------ +%% Determine if EPMD module supports address resolving. Default +%% is to use inet:getaddr/2. +%% ------------------------------------------------------------ +get_address_resolver(EpmdModule) -> + case erlang:function_exported(EpmdModule, address_please, 3) of + true -> {EpmdModule, address_please}; + _ -> {inet, getaddr} + end. + %% ------------------------------------------------------------ %% Do only accept new connection attempts from nodes at our %% own LAN, if the check_ip environment parameter is true. -- cgit v1.2.3 From 395dbad6bcc38c3384840674f40657273b05b14a Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 8 May 2018 16:13:50 +0200 Subject: logger: Rework configuration of logger Most logger configuration that was possible through kernel application variables have been moved into a common 'logger' application environment in kernel. Now all the configuration possible through the logger API can be done as sys config. The handler started by kernel has been renamed to 'default' instead of logger_std_h. There is a new logger:setup_handlers/1 function that given an application name can be used to setup handlers in other applications. --- lib/kernel/doc/src/config.xml | 4 +- lib/kernel/doc/src/kernel_app.xml | 141 ++--- lib/kernel/doc/src/logger.xml | 103 +++- lib/kernel/doc/src/logger_chapter.xml | 101 +++- lib/kernel/doc/src/logger_disk_log_h.xml | 8 +- lib/kernel/doc/src/logger_formatter.xml | 2 +- lib/kernel/doc/src/logger_std_h.xml | 11 +- lib/kernel/doc/src/ref_man.xml | 4 +- lib/kernel/src/file.erl | 2 +- lib/kernel/src/kernel.app.src | 5 +- lib/kernel/src/kernel.erl | 14 +- lib/kernel/src/logger.erl | 271 ++++++---- lib/kernel/src/logger_config.erl | 2 +- lib/kernel/src/logger_internal.hrl | 2 +- lib/kernel/src/logger_server.erl | 24 +- lib/kernel/src/logger_simple.erl | 69 +-- lib/kernel/test/Makefile | 1 + lib/kernel/test/logger_SUITE.erl | 12 +- lib/kernel/test/logger_disk_log_h_SUITE.erl | 28 +- lib/kernel/test/logger_env_var_SUITE.erl | 790 +++++++++++++++++----------- lib/kernel/test/logger_formatter_SUITE.erl | 4 +- lib/kernel/test/logger_simple_SUITE.erl | 203 +++---- lib/kernel/test/logger_std_h_SUITE.erl | 59 +-- lib/kernel/test/logger_test_lib.erl | 82 +++ 24 files changed, 1155 insertions(+), 787 deletions(-) create mode 100644 lib/kernel/test/logger_test_lib.erl (limited to 'lib/kernel') 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 Name.config.

    Configuration parameter values in the configuration file override the values in the application resource files (see - app(4). + app(4)). The values in the configuration file can be overridden by command-line flags (see - erts:erl(1).

    + erts:erl(1)).

    The value of a configuration parameter is retrieved by calling application:get_env/1,2.

    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 @@

    Permissions are described in application:permit/2.

    - logger_dest = Value + logger = [Config] -

    Value is one of:

    - - tty -

    Installs the standard handler, - logger_std_h(3), with type set - to standard_io. This is the default - option.

    - {file, FileName} -

    Installs the standard handler, - logger_std_h(3), with type set - to {file, FileName}, where FileName - is a string. The file is opened with encoding UTF-8.

    - {disk_log, FileName} -

    Installs the disk_log handler, - logger_disk_log_h(3), with file set - to FileName (a string), and possibly other disk_log - parameters set by the environment variables - logger_disk_log_type, logger_disk_log_maxfiles and - logger_disk_log_maxbytes, - see below. The - file is opened with encoding UTF-8.

    - false - -

    No standard handler is installed, but - the initial, primitive handler is kept, printing - raw event messages to tty.

    -
    - silent - -

    No standard handler is started, and the initial, - primitive handler is removed.

    -
    -
    +

    Specifies how logger should be + configured.

    +

    For more details and examples, see the + Configuration section in the + Logger User's Guide. +

    - logger_level = Level + logger_level = Level -

    Value = emergency | alert | critical | error | warning | +

    Level = emergency | alert | critical | error | warning | notice | info | debug

    This parameter specifies which log levels to log. The specified level, and all levels that are more severe, will be logged.

    -

    This configuration parameter is used both for the global - logger level, and for the standard handler started by - the Kernel application (see logger_dest variable above).

    +

    It is possible to change this variable at run-time + using + logger:set_logger_config(#{ level => error }). + .

    The default value is info.

    - - logger_disk_log_type = halt | wrap - - logger_disk_log_maxfiles = integer() - - logger_disk_log_maxbytes = integer() - -

    If logger_dest 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.

    -

    The default values are:

    - -logger_disk_log_type = wrap -logger_disk_log_maxfiles = 10 -logger_disk_log_maxbytes = 1048576 -
    logger_sasl_compatible = boolean() -

    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, +

    If this parameter is set to true, then the default 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 sasl_h, according to values of the SASL environment variables sasl_error_logger @@ -247,6 +203,8 @@ logger_disk_log_maxbytes = 1048576

    See chapter Backwards compatibility with error_logger for more information about handling of the so called SASL reports.

    +

    This configuration option only effects the default + and sasl handler. Any other handlers are uneffected.

    logger_log_progress = boolean() @@ -254,51 +212,13 @@ logger_disk_log_maxbytes = 1048576

    If logger_sasl_compatible = false, then logger_log_progress specifies if progress reports from supervisor - and application_controller shall be logged or - not.

    + and application_controller shall be logged by the + default logger.

    If logger_sasl_compatible = true, then logger_log_progress is ignored.

    - - - logger_format_depth = Depth - -

    Can be used to limit the size of the - formatted output from the logger handlers.

    - -

    Depth 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, - logger_formatter(3), - unless the formatter's depth parameter is explicitly set. - (If you have implemented your own formatter, this configuration - parameter has no effect on that.)

    - -

    Depth is used as follows: Format strings - received by the formatter are rewritten. - The format controls ~p and ~w are replaced with - ~P and ~W, respectively, and Depth is - used as the depth parameter. For details, see - io:format/2 - in STDLIB.

    - -

    A reasonable starting value for Depth is - 30. We recommend to test crashing various processes in your - application, examine the logs from the crashes, and then - increase or decrease the value.

    -
    - logger_max_size = integer() | unlimited - -

    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.

    -
    - logger_utc = boolean() - -

    If set to true, the default formatter will display - all dates in Universal Coordinated Time.

    +

    The default value is false

    +

    This configuration option only effects the default + and sasl handler. Any other handlers are uneffected.

    global_groups = [GroupTuple] @@ -572,9 +492,20 @@ MaxT = TickTime + TickTime / 4 variables are not set.

    error_logger - Replaced by logger_dest + Replaced by setting the type of the default + logger_std_h + to the same value. Example: + +erl -kernel logger '[{handler,default,logger_std_h,#{logger_std_h=>#{type=>{file,"/tmp/erlang.log"}}}}]' + + error_logger_format_depth - Replaced by logger_format_depth + Replaced by setting the depth + parameter of the default handlers formatter. Example: + +erl -kernel logger '[{handler,default,logger_std_h,#{formatter=>{logger_formatter,#{legacy_header=>true,template=>[{logger_formatter,header},"\n",msg,"\n"],depth=>10}}}]' + +

    See Backwards compatibility with error_logger 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 @@ logger.xml logger - API module for the logger application. + API module for the logger. - +

    + 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 + macros should be used. For instance, + to log a new error log event:

    + +?LOG_ERROR("error happened because: ~p",[Reason]). %% With macro +logger:error("error happened because: ~p",[Reason]). %% Without macro + +

    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 sys.config.

    + +[{kernel, + [{logger, + [{handler,default,logger_std_h, + #{logger_std_h=>#{type=>{file,"path/to/file.log"}}}}]}]}]. + +

    + For more information about: +

    + + how to use the API, + see the User's Guide. + how to configure logger, + see the Configuration + section in the User's Guide. + the convinience macros in logger.hrl, + see the macro section. + what the builtin formatter can do, + see logger_formatter. + what the builtin handlers can do, + see logger_std_h and + logger_disk_log_h. + what builtin filters are available, + see logger_filters. + @@ -348,16 +387,16 @@ 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 @@ -549,6 +588,56 @@ Current logger configuration: + + + Setup logger handlers from the applications configuration parameters. + +

    Reads the application configuration parameter logger and + calls logger:add_handlers/1 with it contents.

    +
    +
    + + + + Setup logger handlers. + + +

    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 logger:add_handlers/1 + just after the processes that the handler needs are started + and pass the applications logger config as an argument. Eg.

    + +-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. +

    This will read the logger configuration parameter from + the handler application and start the configured handlers. The contents + of the configuration use the same rules as the + logger handler configuration. +

    +

    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 sys.config file that disables the kernel handler and adds + a custom handler could looks like this:

    + +[{kernel, + [{logger, + %% Disable the default kernel handler + [{handler,default,undefined}]}]}, + {my_app, + [{logger, + %% Enable this handler as the default + [{handler,default,my_handler,#{}}]}]}]. + +
    +
    + Set configuration data for the logger. @@ -650,7 +739,7 @@ Current logger configuration:

    If process metadata exists for the current process, this function behaves as if it was implemented as follows:

    -logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)) +logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)).

    If no process metadata exists, the function behaves as 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 @@

    Configuration +

    Logger can be configured either when the system starts through + configuration parameters, + or at run-time by using the logger + API. The recommended approach is to do the initial configuration in + the sys.config file and then use the API when some configuration + has to be changed at run-time, such as the logging level.

    +
    - Application environment variables -

    See Kernel(6) for - information about the application environment variables that can - be used for configuring logger.

    + Application configuration parameters +

    Logger is best configured by using the configuration parameters + of kernel. There are three possible configuration parameters: + logger, + logger_level, + logger_sasl_compatible and + logger_log_progress. + logger_level, logger_sasl_compatible and logger_log_progress are described in the + Kernel Configuration, + while logger is described below.

    +
    + + logger +

    The logger 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:

    + + DisableHandler = {handler,default,undefined} + Disable the default handler. This will allow another application + to add its own default handler. See + logger:add_handlers/1 for more details. + AddHandler = {handler,HandlerId,Module,HandlerConfig} + Add a handler as if + logger:add_handler(HandlerId,Module,HandlerConfig) had been + called. + Filters = {filters, FilterDefault, [Filter]}
    + FilterDefault = log | stop
    + Filter = {FilterId, {FilterFun, FilterConfig}}
    + Add the specified + logger filters. Only one entry is allowed of this option. + ModuleLevel + {module_level, Level, [Module]}, + this option configures the + module log level to be used. It is possible to have multiple + module_level entries. +
    +

    Examples:

    + + +

    Output logs into a the file "logs/erlang.log"

    + +[{kernel, + [{logger, + [{handler, default, logger_std_h, + #{ logger_std_h => #{ type => {file,"log/erlang.log"}}}}]}]}]. + +
    + +

    Output logs in single line format

    + +[{kernel, + [{logger, + [{handler, default, logger_std_h, + #{ formatter => { logger_formatter,#{ single_line => true}}}}]}]}]. + +
    + +

    Add the pid to each log event

    + +[{kernel, + [{logger, + [{handler, default, logger_std_h, + #{ formatter => { logger_formatter, + #{ template => [time," ",pid," ",msg,"\n"]}} + }}]}]}]. + +
    + +

    Use a different file for debug logging

    + +[{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"}}}} + ]}]}]. + +
    +
    +
    @@ -330,6 +416,13 @@ logger_formatter, and Extra is it's configuration map.

    + HandlerConfig, term() = term() + + Any keys not listed above are considered to be handler specific + configuration. The configuration of the Kernel handlers can be found in + logger_std_h and + logger_disk_log_h. +

    Note that level and filters 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}}).

    In order to use the disk_log handler instead of the default standard - handler when starting en Erlang node, use the kernel configuration parameter - logger_dest with - value {disk_log,FileName}. Example:

    + handler when starting en Erlang node, change the Kernel default logger to + use disk_log. Example:

    -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"}}}]' 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 max_size parameter.

    - depth = pos_integer() | unlimited + depth = pos_integer() | unlimited

    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 standard_io, standard_error or to file. The default instance that starts - with kernel is named logger_std_h - which is the name to be used + with kernel is named default - which is the name to be used for reconfiguration.

    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 logger_std_h. The following keys and values may be specified:

    - type + type

    This will have the value standard_io, standard_error, {file,LogFileName}, or {file,LogFileName,LogFileOpts}, @@ -105,11 +105,10 @@ logger:add_handler(my_standard_h, logger_std_h,

    In order to configure the default handler (that starts initially with the kernel application) to log to file instead of standard_io, - use the kernel configuration parameter - logger_dest with - value {file,FileName}. Example:

    + change the Kernel default logger to use a file. Example:

    -erl -kernel logger_dest '{file,"./erl.log"}' +erl -kernel logger '[{handler,default,logger_std_h, + #{ logger_std_h => #{ type => {file,"./log.log"}}}}]'

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

    Returns max(10, Depth), where Depth is the - value of - - logger_format_depth + value of error_logger_format_depth in the Kernel application, if Depth is an integer. Otherwise, unlimited is returned.

    -

    For backwards compatibility, the value - of error_logger_format_depth is used - if logger_format_depth is not set.

    + +

    The error_logger_format_depth variable + is + deprecated since + the Logger API was + introduced in OTP-21. The variable, and this function, are + kept for backwards compatibility since they still might be + used by legacy report handlers.

    +
    diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index ebc7962f29..7cd05dab14 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -181,11 +181,10 @@

    This parameter specifies which log levels to log. The specified level, and all levels that are more severe, will be logged.

    -

    It is possible to change this variable at run-time - using - logger:set_logger_config(#{ level => error }). - .

    The default value is info.

    +

    To change the global log level at run-time, use + + logger:set_logger_config(level, error).

    logger_sasl_compatible = boolean() diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 484358f392..4232429589 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -136,7 +136,7 @@

    Filters can be set on the logger or on a handler. Logger filters are applied first, and if passed, the handler filters - for each handler are applied. The handler plugin is only + for each handler are applied. The handler callback is only called if all handler filters for the handler in question also pass.

    @@ -159,7 +159,7 @@ format(Log,Extra) -> unicode:chardata() -

    The formatter plugin is called by each handler, and the +

    The formatter callback is called by each handler, and the returned string can be printed to the handler's destination (stdout, file, ...).

    @@ -214,8 +214,8 @@ logger_filters:level/2

    This filter provides a way of filtering log events based - on the log level. See - logger_filters:domain/2

    + on the log level. See + logger_filters:level/2

    logger_filters:progress/2 @@ -518,7 +518,7 @@ error_logger:add_report_handler/1,2. handler named sasl_h.

    All SASL reports have a metadata field domain=>[beam,erlang,otp,sasl], which can be - used, for example, by filters to to stop or allow the + used, for example, by filters to stop or allow the events.

    -- cgit v1.2.3 From bfe8a57bb9356415e6438f0deb410b03a7c602d1 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 9 May 2018 17:22:43 +0200 Subject: Remove logger env vars for format_depth, max_size and utc These are replaced by new config handling and must not be used any more. --- lib/kernel/src/error_logger.erl | 8 +++- lib/kernel/src/logger.erl | 60 ------------------------------ lib/kernel/src/logger_formatter.erl | 29 ++++++++++++--- lib/kernel/test/logger_formatter_SUITE.erl | 50 +++++++++++-------------- 4 files changed, 51 insertions(+), 96 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index 47d0ca5ea3..196c70d05f 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -540,7 +540,6 @@ tty(false) -> delete_report_handler(error_logger_tty_h). %%%----------------------------------------------------------------- - -spec limit_term(term()) -> term(). limit_term(Term) -> @@ -552,4 +551,9 @@ limit_term(Term) -> -spec get_format_depth() -> 'unlimited' | pos_integer(). get_format_depth() -> - logger:get_format_depth(). + case application:get_env(kernel, error_logger_format_depth) of + {ok, Depth} when is_integer(Depth) -> + max(10, Depth); + undefined -> + unlimited + end. diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 9a6719923e..7ce4a80bbd 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -51,7 +51,6 @@ -export([set_process_metadata/1, update_process_metadata/1, unset_process_metadata/0, get_process_metadata/0]). -export([i/0, i/1]). --export([limit_term/1, get_format_depth/0, get_max_size/0, get_utc_config/0]). %% Basic report formatting -export([format_report/1, format_otp_report/1]). @@ -705,65 +704,6 @@ get_default_handler_filters() -> get_logger_env() -> application:get_env(kernel, logger, []). -%%%----------------------------------------------------------------- --spec limit_term(term()) -> term(). - -limit_term(Term) -> - try get_format_depth() of - unlimited -> Term; - D -> io_lib:limit_term(Term, D) - catch error:badarg -> - %% This could happen during system termination, after - %% application_controller process is dead. - unlimited - end. - --spec get_format_depth() -> 'unlimited' | pos_integer(). - -get_format_depth() -> - Depth = - case application:get_env(kernel, logger_format_depth) of - {ok, D} when is_integer(D) -> - D; - undefined -> - case application:get_env(kernel, error_logger_format_depth) of - {ok, D} when is_integer(D) -> - D; - undefined -> - unlimited - end - end, - max(10, Depth). - --spec get_max_size() -> 'unlimited' | pos_integer(). - -get_max_size() -> - case application:get_env(kernel, logger_max_size) of - {ok, Size} when is_integer(Size) -> - max(50, Size); - undefined -> - unlimited - end. - --spec get_utc_config() -> boolean(). - -get_utc_config() -> - %% Kernel's logger_utc configuration overrides SASL utc_log, which - %% in turn overrides stdlib config - in order to have uniform - %% timestamps in log messages - case application:get_env(kernel, logger_utc) of - {ok, Val} -> Val; - undefined -> - case application:get_env(sasl, utc_log) of - {ok, Val} -> Val; - undefined -> - case application:get_env(stdlib, utc_log) of - {ok, Val} -> Val; - undefined -> false - end - end - end. - %%%----------------------------------------------------------------- %%% Internal do_log(warning,Msg,Meta) -> diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl index 8e954f8d98..624f523465 100644 --- a/lib/kernel/src/logger_formatter.erl +++ b/lib/kernel/src/logger_formatter.erl @@ -259,17 +259,17 @@ month(12) -> "Dec". utcstr(#{utc:=true}) -> "UTC "; utcstr(_) -> "". -add_default_config(#{utc:=_}=Config0) -> +add_default_config(Config0) -> Default = #{legacy_header=>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)), + Utc = get_utc(maps:get(utc,Config0,false)), add_default_template(maps:merge(Default,Config0#{max_size=>MaxSize, - depth=>Depth})); -add_default_config(Config) -> - add_default_config(Config#{utc=>logger:get_utc_config()}). + depth=>Depth, + utc=>Utc})). add_default_template(#{template:=_}=Config) -> Config; @@ -284,11 +284,28 @@ default_template(_) -> ?DEFAULT_FORMAT_TEMPLATE. get_max_size(false) -> - logger:get_max_size(); + unlimited; get_max_size(S) -> max(10,S). get_depth(false) -> - logger:get_format_depth(); + error_logger:get_format_depth(); get_depth(S) -> max(5,S). + +get_utc(false) -> + get_utc_config(); +get_utc(U) -> + U. + +get_utc_config() -> + %% SASL utc_log overrides stdlib config - in order to have uniform + %% timestamps in log messages + case application:get_env(sasl, utc_log) of + {ok, Val} -> Val; + undefined -> + case application:get_env(stdlib, utc_log) of + {ok, Val} -> Val; + undefined -> false + end + end. diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl index c80e5694cf..bd5bc2d2e4 100644 --- a/lib/kernel/test/logger_formatter_SUITE.erl +++ b/lib/kernel/test/logger_formatter_SUITE.erl @@ -297,22 +297,22 @@ max_size(_Config) -> single_line=>false}, "12345678901234567890" = 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",[]},#{},Cfg), - "12345678901234567890123456789012345678901234567..." = % 50 - format(info, - {"123456789012345678901234567890123456789012345678901234567890", - []}, - #{}, - Cfg), - application:set_env(kernel,logger_max_size,53), - "12345678901234567890123456789012345678901234567890..." = %53 - format(info, - {"123456789012345678901234567890123456789012345678901234567890", - []}, - #{}, - Cfg), + %% application:set_env(kernel,logger_max_size,11), + %% "12345678901234567890" = % min value is 50, so this is not limited + %% format(info,{"12345678901234567890",[]},#{},Cfg), + %% "12345678901234567890123456789012345678901234567..." = % 50 + %% format(info, + %% {"123456789012345678901234567890123456789012345678901234567890", + %% []}, + %% #{}, + %% Cfg), + %% application:set_env(kernel,logger_max_size,53), + %% "12345678901234567890123456789012345678901234567890..." = %53 + %% format(info, + %% {"123456789012345678901234567890123456789012345678901234567890", + %% []}, + %% #{}, + %% Cfg), "123456789012..." = format(info,{"12345678901234567890",[]},#{},Cfg#{max_size=>15}), "12345678901234567890" = @@ -341,12 +341,6 @@ 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,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]]}, - #{}, - #{template=>Template}), "[1,2,3,4,5,6,7,8,9,0,1,2|...]" = format(info, {"~p",[[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0]]}, @@ -451,12 +445,12 @@ format_time(_Config) -> ct:log(String2), " 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: term\n" = string:prefix(String3,ExpectedTimestamp3), + %% 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: term\n" = string:prefix(String3,ExpectedTimestamp3), ok. -- cgit v1.2.3 From 033bcc2296d7d79c53d56798ab0ccf232f451574 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 14 May 2018 17:01:15 +0200 Subject: Fix error_logger:tty/1 to turn on/off tty logging This function earlier only added/removed the old error_logger_tty_h report handler. Since it is mostly used for turning off logging to tty in general, it now also checks the default logger handlers. It works as follows: tty(true): * If default handler is logger_std_h of type standard_io, make sure it has no 'error_logger_tty_false filter'. * Else, add a new instance of logger_std_h with type standard_io with id 'error_logger_tty_true'. tty(false): * Remove error_logger_tty_h report handler if it exists. * Remove error_logger_tty_false logger handler if it exists. * If default handler is logger_std_h of type standard_io, add a filter named 'error_logger_tty_false', which stops all events. --- lib/kernel/src/error_logger.erl | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index 196c70d05f..6c3b308308 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -529,15 +529,36 @@ logfile(filename) -> Flag :: boolean(). tty(true) -> - case lists:member(error_logger_tty_h, which_report_handlers()) of - false -> - add_report_handler(error_logger_tty_h, []); - true -> - ignore - end, + _ = case lists:member(error_logger_tty_h, which_report_handlers()) of + false -> + case logger:get_handler_config(default) of + {ok,{logger_std_h,#{logger_std_h:=#{type:=standard_io}}}} -> + logger:remove_handler_filter(default, + error_logger_tty_false); + _ -> + logger:add_handler(error_logger_tty_true,logger_std_h, + #{filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS( + [beam,erlang,otp]), + formatter=>{?DEFAULT_FORMATTER, + ?DEFAULT_FORMAT_CONFIG}, + logger_std_h=>#{type=>standard_io}}) + end; + true -> + ok + end, ok; tty(false) -> - delete_report_handler(error_logger_tty_h). + delete_report_handler(error_logger_tty_h), + _ = logger:remove_handler(error_logger_tty_true), + _ = case logger:get_handler_config(default) of + {ok,{logger_std_h,#{logger_std_h:=#{type:=standard_io}}}} -> + logger:add_handler_filter(default,error_logger_tty_false, + {fun(_,_) -> stop end, ok}); + _ -> + ok + end, + ok. %%%----------------------------------------------------------------- -spec limit_term(term()) -> term(). -- cgit v1.2.3 From c2c5e22e0c0f67d0c445113b7908dd07e4e8d4c4 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 14 May 2018 18:03:15 +0200 Subject: Improve santiy check of formatter config --- lib/kernel/src/logger_formatter.erl | 45 +++++++++++++++++++++++++++++++ lib/kernel/src/logger_server.erl | 46 +++++++++++--------------------- lib/kernel/test/logger_SUITE.erl | 53 ++++++++++++++++++++++++++++++++----- 3 files changed, 107 insertions(+), 37 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl index 624f523465..0d55244034 100644 --- a/lib/kernel/src/logger_formatter.erl +++ b/lib/kernel/src/logger_formatter.erl @@ -20,6 +20,7 @@ -module(logger_formatter). -export([format/2]). +-export([check_config/1]). -include("logger_internal.hrl"). @@ -309,3 +310,47 @@ get_utc_config() -> undefined -> false end end. + +check_config(Config) when is_map(Config) -> + do_check_config(maps:to_list(Config)); +check_config(Config) -> + {error,{invalid_formatter_config,?MODULE,Config}}. + +do_check_config([{Type,L}|Config]) when Type == chars_limit; + Type == depth; + Type == max_size -> + case check_limit(L) of + ok -> do_check_config(Config); + error -> {error,{invalid_formatter_config,?MODULE,{Type,L}}} + end; +do_check_config([{single_line,SL}|Config]) when is_boolean(SL) -> + do_check_config(Config); +do_check_config([{legacy_header,LH}|Config]) when is_boolean(LH) -> + do_check_config(Config); +do_check_config([{report_cb,RCB}|Config]) when is_function(RCB,1) -> + do_check_config(Config); +do_check_config([{template,T}|Config]) when is_list(T) -> + case lists:all(fun(X) when is_atom(X) -> true; + (X) when is_tuple(X), is_atom(element(1,X)) -> true; + (X) when is_list(X) -> io_lib:printable_unicode_list(X); + (_) -> false + end, + T) of + true -> + do_check_config(Config); + false -> + {error,{invalid_formatter_template,?MODULE,T}} + end; +do_check_config([{utc,Utc}|Config]) when is_boolean(Utc) -> + do_check_config(Config); +do_check_config([C|_]) -> + {error,{invalid_formatter_config,?MODULE,C}}; +do_check_config([]) -> + ok. + +check_limit(L) when is_integer(L), L>0 -> + ok; +check_limit(unlimited) -> + ok; +check_limit(_) -> + error. diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index 2a3723177f..f1b4a21c6a 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -416,40 +416,24 @@ check_filter_default(FD) when FD==stop; FD==log -> check_filter_default(FD) -> throw({invalid_filter_default,FD}). -check_formatter({logger_formatter,Config}) when is_map(Config) -> - check_logger_formatter_config(maps:to_list(Config)); -check_formatter({logger_formatter,Config}) -> - throw({invalid_formatter_config,Config}); -check_formatter({Mod,_}) -> - %% no knowledge of other formatters - check_mod(Mod); +check_formatter({Mod,Config}) -> + check_mod(Mod), + try Mod:check_config(Config) of + ok -> ok; + {error,Error} -> throw(Error) + catch + C:R:S -> + case {C,R,S} of + {error,undef,[{Mod,check_config,[Config],_}|_]} -> + ok; + _ -> + throw({callback_crashed, + {C,R,logger:filter_stacktrace(?MODULE,S)}}) + end + end; check_formatter(Formatter) -> throw({invalid_formatter,Formatter}). - -check_logger_formatter_config([{template,T}|Config]) when is_list(T) -> - case lists:all(fun(X) when is_atom(X) -> true; - (X) when is_tuple(X), is_atom(element(1,X)) -> true; - (X) when is_list(X) -> io_lib:printable_unicode_list(X); - (_) -> false - end, - T) of - true -> - check_logger_formatter_config(Config); - false -> - throw({invalid_formatter_template,T}) - end; -check_logger_formatter_config([{legacy_header,LH}|Config]) when is_boolean(LH) -> - check_logger_formatter_config(Config); -check_logger_formatter_config([{single_line,SL}|Config]) when is_boolean(SL) -> - check_logger_formatter_config(Config); -check_logger_formatter_config([{utc,Utc}|Config]) when is_boolean(Utc) -> - check_logger_formatter_config(Config); -check_logger_formatter_config([C|_]) -> - throw({invalid_formatter_config,C}); -check_logger_formatter_config([]) -> - ok. - call_h(Module, Function, Args, DefRet) -> %% Not calling code:ensure_loaded + erlang:function_exported here, %% since in some rare terminal cases, the code_server might not diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index 8c5d4da70d..9fea92ee0d 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -544,28 +544,64 @@ config_sanity_check(_Config) -> logger:set_handler_config(h1,formatter,bad), {error,{invalid_module,{bad}}} = logger:set_handler_config(h1,formatter,{{bad},cfg}), - {error,{invalid_formatter_config,bad}} = + {error,{invalid_formatter_config,logger_formatter,bad}} = logger:set_handler_config(h1,formatter,{logger_formatter,bad}), - {error,{invalid_formatter_config,{bad,bad}}} = + {error,{invalid_formatter_config,logger_formatter,{bad,bad}}} = logger:set_handler_config(h1,formatter,{logger_formatter,#{bad=>bad}}), - {error,{invalid_formatter_config,{template,bad}}} = + {error,{invalid_formatter_config,logger_formatter,{template,bad}}} = logger:set_handler_config(h1,formatter,{logger_formatter, #{template=>bad}}), - {error,{invalid_formatter_template,[1]}} = + {error,{invalid_formatter_template,logger_formatter,[1]}} = logger:set_handler_config(h1,formatter,{logger_formatter, #{template=>[1]}}), ok = logger:set_handler_config(h1,formatter,{logger_formatter, #{template=>[]}}), - {error,{invalid_formatter_config,{single_line,bad}}} = + {error,{invalid_formatter_config,logger_formatter,{single_line,bad}}} = logger:set_handler_config(h1,formatter,{logger_formatter, #{single_line=>bad}}), ok = logger:set_handler_config(h1,formatter,{logger_formatter, #{single_line=>true}}), - {error,{invalid_formatter_config,{legacy_header,bad}}} = + {error,{invalid_formatter_config,logger_formatter,{legacy_header,bad}}} = logger:set_handler_config(h1,formatter,{logger_formatter, #{legacy_header=>bad}}), ok = logger:set_handler_config(h1,formatter,{logger_formatter, #{legacy_header=>true}}), + {error,{invalid_formatter_config,logger_formatter,{report_cb,bad}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{report_cb=>bad}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{report_cb=>fun(R) -> + {"~p",[R]} + end}}), + {error,{invalid_formatter_config,logger_formatter,{utc,bad}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{utc=>bad}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{utc=>true}}), + {error,{invalid_formatter_config,logger_formatter,{chars_limit,bad}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{chars_limit=>bad}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{chars_limit=>unlimited}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{chars_limit=>4}}), + {error,{invalid_formatter_config,logger_formatter,{depth,bad}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{depth=>bad}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{depth=>unlimited}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{depth=>4}}), + {error,{invalid_formatter_config,logger_formatter,{max_size,bad}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{max_size=>bad}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{max_size=>unlimited}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{max_size=>4}}), + ok = logger:set_handler_config(h1,formatter,{module,config}), + {error,{callback_crashed,{error,{badmatch,3},[{?MODULE,check_config,1,_}]}}} = + logger:set_handler_config(h1,formatter,{?MODULE,crash}), ok = logger:set_handler_config(h1,custom,custom), ok. @@ -877,3 +913,8 @@ test_macros(emergency=Level) -> %%% Called by macro ?TRY(X) my_try(Fun) -> try Fun() catch C:R -> {C,R} end. + +check_config(crash) -> + erlang:error({badmatch,3}); +check_config(_) -> + ok. -- cgit v1.2.3 From d29fca17a0c053fca2e5961fb146e9aeefb55aa9 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 14 May 2018 19:06:22 +0200 Subject: Hide handlers field in logger config map from the API This field contains the index of all installed handlers. It is internal and can not be altered by the user, and should therefore not be visible. --- lib/kernel/src/logger.erl | 17 +++++------ lib/kernel/src/logger_server.erl | 44 +++++++++-------------------- lib/kernel/test/logger_SUITE.erl | 34 ++++++++++------------ lib/kernel/test/logger_disk_log_h_SUITE.erl | 6 ++-- lib/kernel/test/logger_std_h_SUITE.erl | 6 ++-- 5 files changed, 46 insertions(+), 61 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 7ce4a80bbd..2c7466fdf6 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -364,7 +364,8 @@ set_handler_config(HandlerId,Config) -> -spec get_logger_config() -> {ok,Config} when Config :: config(). get_logger_config() -> - logger_config:get(?LOGGER_TABLE,logger). + {ok,Config} = logger_config:get(?LOGGER_TABLE,logger), + {ok,maps:remove(handlers,Config)}. -spec get_handler_config(HandlerId) -> {ok,{Module,Config}} | {error,term()} when HandlerId :: handler_id(), @@ -445,8 +446,9 @@ i() -> i(_Action = print) -> io:put_chars(i(string)); i(_Action = string) -> - #{logger := #{level := Level, handlers := Handlers, - filters := Filters, filter_default := FilterDefault}, + #{logger := #{level := Level, + filters := Filters, + filter_default := FilterDefault}, handlers := HandlerConfigs, module_levels := Modules} = i(term), [io_lib:format("Current logger configuration:~n", []), @@ -455,16 +457,15 @@ i(_Action = string) -> io_lib:format(" Filters: ~n", []), print_filters(4, Filters), io_lib:format(" Handlers: ~n", []), - print_handlers([C || {Id, _, _} = C <- HandlerConfigs, - lists:member(Id, Handlers)]), + print_handlers(HandlerConfigs), io_lib:format(" Level set per module: ~n", []), print_module_levels(Modules) ]; i(_Action = term) -> {Logger, Handlers, Modules} = logger_config:get(tid()), - #{logger=>Logger, - handlers=>Handlers, - module_levels=>Modules}. + #{logger=>maps:remove(handlers,Logger), + handlers=>lists:keysort(1,Handlers), + module_levels=>lists:keysort(1,Modules)}. print_filters(Indent, {Id, {Fun, Config}}) -> io_lib:format("~sId: ~p~n" diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index f1b4a21c6a..bd0c325f2b 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -48,23 +48,18 @@ start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). add_handler(Id,Module,Config0) -> - case sanity_check(logger,handlers,[Id]) of - ok -> - try check_mod(Module) of + try {check_id(Id),check_mod(Module)} of + {ok,ok} -> + case sanity_check(Id,Config0) of ok -> - case sanity_check(Id,Config0) of - ok -> - Default = default_config(Id), - Config = maps:merge(Default,Config0), - call({add_handler,Id,Module,Config}); - Error -> - Error - end - catch throw:Error -> - {error,Error} - end; - Error -> - Error + Default = default_config(Id), + Config = maps:merge(Default,Config0), + call({add_handler,Id,Module,Config}); + Error -> + Error + end + catch throw:Error -> + {error,Error} end. remove_handler(HandlerId) -> @@ -197,7 +192,8 @@ handle_call({update_config,Id,NewConfig}, From, #state{tid=Tid}=State) -> {reply,Error,State} end; handle_call({set_config,logger,Config}, _From, #state{tid=Tid}=State) -> - Reply = do_set_config(Tid,logger,Config), + {ok,#{handlers:=Handlers}} = logger_config:get(Tid,logger), + Reply = do_set_config(Tid,logger,Config#{handlers=>Handlers}), {reply,Reply,State}; handle_call({set_config,HandlerId,Config}, From, #state{tid=Tid}=State) -> case logger_config:get(Tid,HandlerId) of @@ -322,8 +318,7 @@ do_set_config(Tid,Id,Config) -> default_config(logger) -> #{level=>info, filters=>[], - filter_default=>log, - handlers=>[]}; + filter_default=>log}; default_config(_) -> #{level=>info, filters=>[], @@ -354,9 +349,6 @@ get_type(Id) -> check_config(Owner,[{level,Level}|Config]) -> check_level(Level), check_config(Owner,Config); -check_config(logger,[{handlers,Handlers}|Config]) -> - check_handlers(Handlers), - check_config(logger,Config); check_config(Owner,[{filters,Filters}|Config]) -> check_filters(Filters), check_config(Owner,Config); @@ -394,14 +386,6 @@ check_level(Level) -> throw({invalid_level,Level}) end. -check_handlers([Id|Handlers]) -> - check_id(Id), - check_handlers(Handlers); -check_handlers([]) -> - ok; -check_handlers(Handlers) -> - throw({invalid_handlers,Handlers}). - check_filters([{Id,{Fun,_Args}}|Filters]) when is_atom(Id), is_function(Fun,2) -> check_filters(Filters); check_filters([Filter|_]) -> diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index 9fea92ee0d..1b75d8278e 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -105,12 +105,12 @@ start_stop(_Config) -> add_remove_handler(_Config) -> register(callback_receiver,self()), - {ok,#{handlers:=Hs0}} = logger:get_logger_config(), + #{handlers:=Hs0} = logger:i(), {error,{not_found,h1}} = logger:get_handler_config(h1), ok = logger:add_handler(h1,?MODULE,#{}), [add] = test_server:messages_get(), - {ok,#{handlers:=Hs}} = logger:get_logger_config(), - [h1|Hs0] = Hs, + #{handlers:=Hs} = logger:i(), + {value,_,Hs0} = lists:keytake(h1,1,Hs), {ok,{?MODULE,#{level:=info,filters:=[],filter_default:=log}}} = % defaults logger:get_handler_config(h1), ok = logger:set_handler_config(h1,filter_default,stop), @@ -124,7 +124,7 @@ add_remove_handler(_Config) -> ok = check_logged(info,"hello",[],?MY_LOC(1)), ok = logger:remove_handler(h1), [remove] = test_server:messages_get(), - {ok,#{handlers:=Hs0}} = logger:get_logger_config(), + #{handlers:=Hs0} = logger:i(), {error,{not_found,h1}} = logger:get_handler_config(h1), {error,{not_found,h1}} = logger:remove_handler(h1), logger:info("hello",[]), @@ -237,13 +237,18 @@ change_config(_Config) -> %% Overwrite logger config - check that defaults are added {ok,LConfig} = logger:get_logger_config(), ok = logger:set_logger_config(#{filter_default=>stop}), - {ok,#{level:=info,filters:=[],handlers:=[],filter_default:=stop}=LC1} = + {ok,#{level:=info,filters:=[],filter_default:=stop}=LC1} = logger:get_logger_config(), - 4 = maps:size(LC1), + 3 = maps:size(LC1), + %% Check that internal 'handlers' field has not been changed + #{handlers:=HCs} = logger:i(), + HIds1 = [Id || {Id,_,_} <- HCs], + {ok,#{handlers:=HIds2}} = logger_config:get(?LOGGER_TABLE,logger), + HIds1 = lists:sort(HIds2), %% Change one key only - ok = logger:set_logger_config(handlers,[h1]), - {ok,#{level:=info,filters:=[],handlers:=[h1],filter_default:=stop}} = + ok = logger:set_logger_config(level,warning), + {ok,#{level:=warning,filters:=[],filter_default:=stop}} = logger:get_logger_config(), %% Cleanup @@ -441,9 +446,7 @@ handler_failed(_Config) -> {error,{invalid_handler,_}} = logger:add_handler(h1,nomodule,#{filter_default=>log}), logger:info(?map_rep), check_no_log(), - #{logger:=#{handlers:=Ids1}, - handlers:=H1} = logger:i(), - false = lists:member(h1,Ids1), + #{handlers:=H1} = logger:i(), false = lists:keymember(h1,1,H1), {error,{not_found,h1}} = logger:remove_handler(h1), @@ -453,10 +456,7 @@ handler_failed(_Config) -> logger:info(?map_rep), [remove] = test_server:messages_get(), - - #{logger:=#{handlers:=Ids2}, - handlers:=H2} = logger:i(), - false = lists:member(h2,Ids2), + #{handlers:=H2} = logger:i(), false = lists:keymember(h2,1,H2), {error,{not_found,h2}} = logger:remove_handler(h2), @@ -511,10 +511,6 @@ config_sanity_check(_Config) -> {error,{invalid_filter_default,bad}} = logger:set_logger_config(filter_default,bad), {error,{invalid_level,bad}} = logger:set_logger_config(level,bad), - {error,{invalid_handlers,bad}} = logger:set_logger_config(handlers,bad), - {error,{invalid_id,{bad,bad}}} = - logger:set_logger_config(handlers,[{bad,bad}]), - {error,{invalid_id,"bad"}} = logger:set_logger_config(handlers,["bad"]), {error,{invalid_filters,bad}} = logger:set_logger_config(filters,bad), {error,{invalid_filter,bad}} = logger:set_logger_config(filters,[bad]), {error,{invalid_filter,{_,_}}} = diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index cdd1e68947..3aa1c3557b 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -328,7 +328,8 @@ formatter_fail(Config) -> logger:add_handler(Name, logger_disk_log_h, HConfig), Pid = whereis(Name), true = is_pid(Pid), - {ok,#{handlers:=H}} = logger:get_logger_config(), + #{handlers:=HC1} = logger:i(), + H = [Id || {Id,_,_} <- HC1], true = lists:member(Name,H), %% Formatter is added automatically @@ -357,7 +358,8 @@ formatter_fail(Config) -> %% Check that handler is still alive and was never dead Pid = whereis(Name), - {ok,#{handlers:=H}} = logger:get_logger_config(), + #{handlers:=HC2} = logger:i(), + H = [Id || {Id,_,_} <- HC2], ok. formatter_fail(cleanup,_Config) -> diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index 83bc96620f..fc59d393e0 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -242,7 +242,8 @@ formatter_fail(Config) -> filters=>?DEFAULT_HANDLER_FILTERS([?MODULE])}), Pid = whereis(?MODULE), true = is_pid(Pid), - {ok,#{handlers:=H}} = logger:get_logger_config(), + #{handlers:=HC1} = logger:i(), + H = [Id || {Id,_,_} <- HC1], true = lists:member(?MODULE,H), %% Formatter is added automatically @@ -271,7 +272,8 @@ formatter_fail(Config) -> %% Check that handler is still alive and was never dead Pid = whereis(?MODULE), - {ok,#{handlers:=H}} = logger:get_logger_config(), + #{handlers:=HC2} = logger:i(), + H = [Id || {Id,_,_} <- HC2], ok. -- cgit v1.2.3 From 5d49f8a25005945b7260facc1a985c294746616b Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 14 May 2018 20:27:55 +0200 Subject: Add update_logger_config/1 and update_handler_config/2 to logger --- lib/kernel/doc/src/logger.xml | 54 ++++++++++++++++++++++++++++++++-------- lib/kernel/src/logger.erl | 12 +++++++++ lib/kernel/src/logger_server.erl | 15 +++++++---- lib/kernel/test/logger_SUITE.erl | 27 ++++++++++++++------ 4 files changed, 85 insertions(+), 23 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index 239b9553b1..183102f240 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -645,11 +645,12 @@ start(_, []) ->

    Set configuration data for the logger. This overwrites the current logger configuration.

    To modify the existing configuration, - use set_logger_config/2 - , or read the current configuration + use + update_logger_config/1, or, if a more + complex merge is needed, read the current configuration with get_logger_config/0 - , then merge in your added or updated - associations before writing it back.

    + , then do the merge before writing the new + configuration back with this function.

    If a key is removed compared to the current configuration, the default value is used.

    @@ -662,7 +663,23 @@ start(_, []) ->

    Add or update configuration data for the logger. If the given Key already exists, its associated value will be changed to Value. If it - doesn't exist, it will be added.

    + does not exist, it will be added.

    + + + + + + Update configuration data for the logger. + +

    Update configuration data for the logger. This function + behaves as if it was implemented as follows:

    + +{ok,Old} = logger:get_logger_config(), +logger:set_logger_config(maps:merge(Old,Config)). + +

    To overwrite the existing configuration without any merge, + use set_logger_config/1 + .

    @@ -673,11 +690,12 @@ start(_, []) ->

    Set configuration data for the specified handler. This overwrites the current handler configuration.

    To modify the existing configuration, - use set_handler_config/3 - , or read the current configuration + use + update_handler_config/2, or, if a more + complex merge is needed, read the current configuration with get_handler_config/1 - , then merge in your added or updated - associations before writing it back.

    + , then do the merge before writing the new + configuration back with this function.

    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 @@ -694,11 +712,27 @@ start(_, []) ->

    Add or update configuration data for the specified handler. If the given Key already exists, its associated value will be changed - to Value. If it doesn't exist, it will + to Value. If it does not exist, it will be added.

    + + + Update configuration data for the specified handler. + +

    Update configuration data for the specified handler. This function + behaves as if it was implemented as follows:

    + +{ok,{_,Old}} = logger:get_handler_config(HandlerId), +logger:set_handler_config(HandlerId,maps:merge(Old,Config)). + +

    To overwrite the existing configuration without any merge, + use set_handler_config/2 + .

    +
    +
    + Compare the severity of two log levels. diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 2c7466fdf6..a839f97e62 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -40,6 +40,7 @@ set_module_level/2, reset_module_level/1, set_logger_config/1, set_logger_config/2, set_handler_config/2, set_handler_config/3, + update_logger_config/1, update_handler_config/2, get_logger_config/0, get_handler_config/1, add_handlers/1]). @@ -361,6 +362,17 @@ set_handler_config(HandlerId,Key,Value) -> set_handler_config(HandlerId,Config) -> logger_server:set_config(HandlerId,Config). +-spec update_logger_config(Config) -> ok | {error,term()} when + Config :: config(). +update_logger_config(Config) -> + logger_server:update_config(logger,Config). + +-spec update_handler_config(HandlerId,Config) -> ok | {error,term()} when + HandlerId :: handler_id(), + Config :: config(). +update_handler_config(HandlerId,Config) -> + logger_server:update_config(HandlerId,Config). + -spec get_logger_config() -> {ok,Config} when Config :: config(). get_logger_config() -> diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index bd0c325f2b..275b9c476f 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -27,7 +27,7 @@ add_filter/2, remove_filter/2, set_module_level/2, reset_module_level/1, cache_module_level/1, - set_config/2, set_config/3]). + set_config/2, set_config/3, update_config/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, @@ -92,10 +92,7 @@ cache_module_level(Module) -> set_config(Owner,Key,Value) -> - case sanity_check(Owner,Key,Value) of - ok -> call({update_config,Owner,#{Key=>Value}}); - Error -> Error - end. + update_config(Owner,#{Key=>Value}). set_config(Owner,Config0) -> case sanity_check(Owner,Config0) of @@ -106,6 +103,14 @@ set_config(Owner,Config0) -> Error end. +update_config(Owner, Config) -> + case sanity_check(Owner,Config) of + ok -> + call({update_config,Owner,Config}); + Error -> + Error + end. + %%%=================================================================== %%% gen_server callbacks %%%=================================================================== diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index 1b75d8278e..64968cbca2 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -227,15 +227,31 @@ change_config(_Config) -> h1,#{conf_call=>fun() -> logger:set_module_level(?MODULE,debug) end}), {ok,{?MODULE,C2}} = logger:get_handler_config(h1), - %% Change one key only + %% Change handler config: Single key {error,fail} = logger:set_handler_config(h1,conf_call,fun() -> {error,fail} end), ok = logger:set_handler_config(h1,custom,custom), [changing_config] = test_server:messages_get(), {ok,{?MODULE,#{custom:=custom}=C3}} = logger:get_handler_config(h1), C2 = maps:remove(custom,C3), + %% Change handler config: Map + ok = logger:update_handler_config(h1,#{custom=>new_custom}), + [changing_config] = test_server:messages_get(), + {ok,{_,C4}} = logger:get_handler_config(h1), + C4 = C3#{custom:=new_custom}, + + %% Change logger config: Single key + {ok,LConfig0} = logger:get_logger_config(), + ok = logger:set_logger_config(level,warning), + {ok,LConfig1} = logger:get_logger_config(), + LConfig1 = LConfig0#{level:=warning}, + + %% Change logger config: Map + ok = logger:update_logger_config(#{level=>error}), + {ok,LConfig2} = logger:get_logger_config(), + LConfig2 = LConfig1#{level:=error}, + %% Overwrite logger config - check that defaults are added - {ok,LConfig} = logger:get_logger_config(), ok = logger:set_logger_config(#{filter_default=>stop}), {ok,#{level:=info,filters:=[],filter_default:=stop}=LC1} = logger:get_logger_config(), @@ -246,13 +262,8 @@ change_config(_Config) -> {ok,#{handlers:=HIds2}} = logger_config:get(?LOGGER_TABLE,logger), HIds1 = lists:sort(HIds2), - %% Change one key only - ok = logger:set_logger_config(level,warning), - {ok,#{level:=warning,filters:=[],filter_default:=stop}} = - logger:get_logger_config(), - %% Cleanup - ok = logger:set_logger_config(LConfig), + ok = logger:set_logger_config(LConfig0), [] = test_server:messages_get(), ok. -- cgit v1.2.3 From 0afab64f105c09976c7c07a64f54bf962ff9a18f Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 16 May 2018 14:12:23 +0200 Subject: Format logger timestamps according to RFC3339 --- lib/kernel/doc/src/logger_formatter.xml | 53 +++++++++--- lib/kernel/src/logger_formatter.erl | 125 +++++++++++++++++++--------- lib/kernel/test/error_logger_warn_SUITE.erl | 14 +++- lib/kernel/test/logger_SUITE.erl | 41 +++++++-- lib/kernel/test/logger_formatter_SUITE.erl | 102 ++++++++++++++--------- 5 files changed, 237 insertions(+), 98 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger_formatter.xml b/lib/kernel/doc/src/logger_formatter.xml index 5f13e54365..370d61d338 100644 --- a/lib/kernel/doc/src/logger_formatter.xml +++ b/lib/kernel/doc/src/logger_formatter.xml @@ -155,11 +155,40 @@ and single_line. See Default Templates for more information

    - utc = boolean() + time_designator = byte() -

    If set to true, all dates are displayed in Universal - Coordinated Time.

    -

    Default is false.

    +

    Timestamps are formatted according to RFC3339, and the time + designator is the character used as date and time + separator.

    +

    Default is $T.

    +

    The value of this parameter is used as + the time_designator option + to + calendar:system_time_to_rcf3339/2.

    +
    + time_offset = integer() | [byte()] + +

    The time offset, either a string or an integer, to be + used when formatting the timestamp.

    +

    An empty string is interpreted as local time. The + values "Z", "z" or 0 are interpreted as + Universal Coordinated Time (UTC).

    +

    Strings, other than "Z", "z", or "", + must be on the form ±[hh]:[mm], for + example "-02:00" or "+00:00".

    +

    Integers must be in microseconds, meaning that the + offset 7200000000 is equivalent + to "+02:00".

    +

    The default value is an empty string, meaning that + timestamps are displayed in local time. However, for + backwards compatibility, if the SASL environment + variable + utc_log=true, the default is + changed to "Z", meaning that timestamps are displayed + in UTC.

    +

    The value of this parameter is used as the offset + option to + calendar:system_time_to_rcf3339/2.

    @@ -174,7 +203,7 @@

    The log event used in the examples is:

    -?LOG_ERROR("name: ~p~nexit_reason: ~p",[my_reg_name,"It crashed"]) +?LOG_ERROR("name: ~p~nexit_reason: ~p",[my_name,"It crashed"]) legacy_header=true @@ -182,9 +211,9 @@

    Default template: [{logger_formatter,header},"\n",msg,"\n"]

    Example log entry:

    - -=ERROR REPORT==== 29-Dec-2017::13:30:51.245123 === -name: my_reg_name + +2018-05-16T11:55:50.448382+02:00 error: +name: my_name exit_reason: "It crashed"

    Notice that all eight levels might occur in the heading, @@ -198,7 +227,7 @@ exit_reason: "It crashed"

    Default template: [time," ",level,": ",msg,"\n"]

    Example log entry:

    - 2017-12-29 13:31:49.640317 error: name: my_reg_name, exit_reason: "It crashed" + 2018-05-16T11:55:50.448382+02:00 error: name: my_name, exit_reason: "It crashed" legacy_header=false, single_line=false @@ -206,9 +235,9 @@ exit_reason: "It crashed"

    Default template: [time," ",level,":\n",msg,"\n"]

    Example log entry:

    - -2017-12-29 13:32:25.191925 error: -name: my_reg_name + +2018-05-16T11:55:50.448382+02:00 error: +name: my_name exit_reason: "It crashed"
    diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl index 0d55244034..602c666cc7 100644 --- a/lib/kernel/src/logger_formatter.erl +++ b/lib/kernel/src/logger_formatter.erl @@ -39,7 +39,8 @@ max_size=>pos_integer() | unlimited, depth=>pos_integer() | unlimited, template=>template(), - utc=>boolean()}. + time_designator=>byte(), + time_offset=>integer()|[byte()]}. format(#{level:=Level,msg:=Msg0,meta:=Meta},Config0) when is_map(Config0) -> Config = add_default_config(Config0), @@ -196,16 +197,12 @@ truncate(String,Size) -> String end. -format_time(Timestamp,Config) when is_integer(Timestamp) -> - {Date,Time,Micro} = timestamp_to_datetimemicro(Timestamp,Config), - format_time(Date,Time,Micro); -format_time(Other,_Config) -> - %% E.g. a string - to_string(Other). - -format_time({Y,M,D},{H,Min,S},Micro) -> - io_lib:format("~4w-~2..0w-~2..0w ~2w:~2..0w:~2..0w.~6..0w", - [Y,M,D,H,Min,S,Micro]). +format_time(Timestamp,#{time_offset:=Offset,time_designator:=Des}) + when is_integer(Timestamp) -> + SysTime = Timestamp + erlang:time_offset(microsecond), + calendar:system_time_to_rfc3339(SysTime,[{unit,microsecond}, + {offset,Offset}, + {time_designator,Des}]). %% Assuming this is monotonic time in microseconds timestamp_to_datetimemicro(Timestamp,Config) when is_integer(Timestamp) -> @@ -213,12 +210,12 @@ timestamp_to_datetimemicro(Timestamp,Config) when is_integer(Timestamp) -> Micro = SysTime rem 1000000, Sec = SysTime div 1000000, UniversalTime = erlang:posixtime_to_universaltime(Sec), - {Date,Time} = - case Config of - #{utc:=true} -> UniversalTime; - _ -> erlang:universaltime_to_localtime(UniversalTime) + {{Date,Time},UtcStr} = + case offset_to_utc(maps:get(time_offset,Config)) of + true -> {UniversalTime,"UTC "}; + _ -> {erlang:universaltime_to_localtime(UniversalTime),""} end, - {Date,Time,Micro}. + {Date,Time,Micro,UtcStr}. format_mfa({M,F,A}) when is_atom(M), is_atom(F), is_integer(A) -> atom_to_list(M)++":"++atom_to_list(F)++"/"++integer_to_list(A); @@ -231,9 +228,11 @@ maybe_add_legacy_header(Level, #{time:=Timestamp}=Meta, #{legacy_header:=true}=Config) -> #{title:=Title}=MyMeta = add_legacy_title(Level,maps:get(?MODULE,Meta,#{})), - {{Y,Mo,D},{H,Mi,S},Micro} = timestamp_to_datetimemicro(Timestamp,Config), - Header = io_lib:format("=~ts==== ~w-~s-~4w::~2..0w:~2..0w:~2..0w.~6..0w ~s===", - [Title,D,month(Mo),Y,H,Mi,S,Micro,utcstr(Config)]), + {{Y,Mo,D},{H,Mi,S},Micro,UtcStr} = + timestamp_to_datetimemicro(Timestamp,Config), + Header = + io_lib:format("=~ts==== ~w-~s-~4w::~2..0w:~2..0w:~2..0w.~6..0w ~s===", + [Title,D,month(Mo),Y,H,Mi,S,Micro,UtcStr]), Meta#{?MODULE=>MyMeta#{header=>Header}}; maybe_add_legacy_header(_,Meta,_) -> Meta. @@ -257,20 +256,20 @@ month(10) -> "Oct"; month(11) -> "Nov"; month(12) -> "Dec". -utcstr(#{utc:=true}) -> "UTC "; -utcstr(_) -> "". - +%% Ensure that all valid configuration parameters exist in the final +%% configuration map add_default_config(Config0) -> Default = #{legacy_header=>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)), - Utc = get_utc(maps:get(utc,Config0,false)), + chars_limit=>unlimited, + time_designator=>$T}, + MaxSize = get_max_size(maps:get(max_size,Config0,undefined)), + Depth = get_depth(maps:get(depth,Config0,undefined)), + Offset = get_offset(maps:get(time_offset,Config0,undefined)), add_default_template(maps:merge(Default,Config0#{max_size=>MaxSize, depth=>Depth, - utc=>Utc})). + time_offset=>Offset})). add_default_template(#{template:=_}=Config) -> Config; @@ -284,33 +283,50 @@ default_template(#{single_line:=true}) -> default_template(_) -> ?DEFAULT_FORMAT_TEMPLATE. -get_max_size(false) -> +get_max_size(undefined) -> unlimited; get_max_size(S) -> max(10,S). -get_depth(false) -> +get_depth(undefined) -> error_logger:get_format_depth(); get_depth(S) -> max(5,S). -get_utc(false) -> - get_utc_config(); -get_utc(U) -> - U. +get_offset(undefined) -> + utc_to_offset(get_utc_config()); +get_offset(Offset) -> + Offset. + +utc_to_offset(true) -> + "Z"; +utc_to_offset(false) -> + "". get_utc_config() -> %% SASL utc_log overrides stdlib config - in order to have uniform %% timestamps in log messages case application:get_env(sasl, utc_log) of - {ok, Val} -> Val; - undefined -> + {ok, Val} when is_boolean(Val) -> Val; + _ -> case application:get_env(stdlib, utc_log) of - {ok, Val} -> Val; - undefined -> false + {ok, Val} when is_boolean(Val) -> Val; + _ -> false end end. +offset_to_utc(Z) when Z=:=0; Z=:="z"; Z=:="Z" -> + true; +offset_to_utc([$+|Tz]) -> + case io_lib:fread("~d:~d", Tz) of + {ok, [0, 0], []} -> + true; + _ -> + false + end; +offset_to_utc(_) -> + false. + check_config(Config) when is_map(Config) -> do_check_config(maps:to_list(Config)); check_config(Config) -> @@ -341,8 +357,20 @@ do_check_config([{template,T}|Config]) when is_list(T) -> false -> {error,{invalid_formatter_template,?MODULE,T}} end; -do_check_config([{utc,Utc}|Config]) when is_boolean(Utc) -> - do_check_config(Config); +do_check_config([{time_offset,Offset}|Config]) -> + case check_offset(Offset) of + ok -> + do_check_config(Config); + error -> + {error,{invalid_formatter_config,?MODULE,{time_offset,Offset}}} + end; +do_check_config([{time_designator,Char}|Config]) when Char>=0, Char=<255 -> + case io_lib:printable_latin1_list([Char]) of + true -> + do_check_config(Config); + false -> + {error,{invalid_formatter_config,?MODULE,{time_designator,Char}}} + end; do_check_config([C|_]) -> {error,{invalid_formatter_config,?MODULE,C}}; do_check_config([]) -> @@ -354,3 +382,22 @@ check_limit(unlimited) -> ok; check_limit(_) -> error. + +check_offset(I) when is_integer(I) -> + ok; +check_offset(Tz) when Tz=:=""; Tz=:="Z"; Tz=:="z" -> + ok; +check_offset([Sign|Tz]) when Sign=:=$+; Sign=:=$- -> + check_timezone(Tz); +check_offset(_) -> + error. + +check_timezone(Tz) -> + try io_lib:fread("~d:~d", Tz) of + {ok, [_, _], []} -> + ok; + _ -> + error + catch _:_ -> + error + end. diff --git a/lib/kernel/test/error_logger_warn_SUITE.erl b/lib/kernel/test/error_logger_warn_SUITE.erl index a8087e11f9..ef55a2d339 100644 --- a/lib/kernel/test/error_logger_warn_SUITE.erl +++ b/lib/kernel/test/error_logger_warn_SUITE.erl @@ -480,9 +480,12 @@ rb_utc() -> UtcLog=case application:get_env(sasl,utc_log) of {ok,true} -> true; - _AllOthers -> + {ok,false} -> application:set_env(sasl,utc_log,true), - false + false; + undefined -> + application:set_env(sasl,utc_log,true), + undefined end, application:start(sasl), rb:start([{report_dir, rd()}]), @@ -494,7 +497,12 @@ rb_utc() -> Sum=one_rb_findstr([],"UTC"), rb:stop(), application:stop(sasl), - application:set_env(sasl,utc_log,UtcLog), + case UtcLog of + undefined -> + application:unset_env(sasl,utc_log); + _ -> + application:set_env(sasl,utc_log,UtcLog) + end, stop_node(Node), ok. diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index 64968cbca2..e602fa2576 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -580,11 +580,6 @@ config_sanity_check(_Config) -> #{report_cb=>fun(R) -> {"~p",[R]} end}}), - {error,{invalid_formatter_config,logger_formatter,{utc,bad}}} = - logger:set_handler_config(h1,formatter,{logger_formatter, - #{utc=>bad}}), - ok = logger:set_handler_config(h1,formatter,{logger_formatter, - #{utc=>true}}), {error,{invalid_formatter_config,logger_formatter,{chars_limit,bad}}} = logger:set_handler_config(h1,formatter,{logger_formatter, #{chars_limit=>bad}}), @@ -610,6 +605,42 @@ config_sanity_check(_Config) -> {error,{callback_crashed,{error,{badmatch,3},[{?MODULE,check_config,1,_}]}}} = logger:set_handler_config(h1,formatter,{?MODULE,crash}), ok = logger:set_handler_config(h1,custom,custom), + + %% Old utc parameter is no longer allowed (replaced by time_offset) + {error,{invalid_formatter_config,logger_formatter,{utc,true}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{utc=>true}}), + {error,{invalid_formatter_config,logger_formatter,{time_offset,bad}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_offset=>bad}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_offset=>0}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_offset=>""}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_offset=>"Z"}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_offset=>"z"}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_offset=>"-0:0"}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_offset=>"+10:13"}}), + + {error,{invalid_formatter_config,logger_formatter,{time_offset,"+0"}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_offset=>"+0"}}), + + {error,{invalid_formatter_config,logger_formatter,{time_designator,bad}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_designator=>bad}}), + {error,{invalid_formatter_config,logger_formatter,{time_designator,"s"}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_designator=>"s"}}), + {error,{invalid_formatter_config,logger_formatter,{time_designator,0}}} = + logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_designator=>0}}), + ok = logger:set_handler_config(h1,formatter,{logger_formatter, + #{time_designator=>$\s}}), ok. config_sanity_check(cleanup,_Config) -> diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl index bd5bc2d2e4..9baadfd65a 100644 --- a/lib/kernel/test/logger_formatter_SUITE.erl +++ b/lib/kernel/test/logger_formatter_SUITE.erl @@ -73,7 +73,7 @@ all() -> default(_Config) -> String1 = format(info,{"~p",[term]},#{},#{}), ct:log(String1), - [_Date,_Time,"info:","term\n"] = string:lexemes(String1," "), + [_DateTime,"info:","term\n"] = string:lexemes(String1," "), Time = timestamp(), ExpectedTimestamp = default_time_format(Time), @@ -364,7 +364,7 @@ chars_limit(_Config) -> lists:seq(1,100), maps:from_list(lists:zip(lists:seq(1,100), lists:duplicate(100,value)))]}, - Meta = #{time=>"2018-04-26 9:15:40.449879"}, + Meta = #{time=>timestamp()}, Template = [time," - ", msg, "\n"], FC = #{template=>Template, depth=>unlimited, @@ -376,7 +376,7 @@ chars_limit(_Config) -> L1 = string:length(String1), ct:log("String1: ~p~nLength1: ~p~n",[lists:flatten(String1),L1]), true = L1 > CL1, - true = L1 < CL1 + 10, + true = L1 < CL1 + 15, String2 = format(info,FA,Meta,FC#{chars_limit=>CL1,depth=>10}), L2 = string:length(String2), @@ -388,13 +388,13 @@ chars_limit(_Config) -> L3 = string:length(String3), ct:log("String3: ~p~nLength3: ~p~n",[lists:flatten(String3),L3]), true = L3 > CL3, - true = L3 < CL3 + 10, + true = L3 < CL3 + 15, String4 = format(info,FA,Meta,FC#{chars_limit=>CL3,depth=>10}), L4 = string:length(String4), ct:log("String4: ~p~nLength4: ~p~n",[lists:flatten(String4),L4]), true = L4 > CL3, - true = L4 < CL3 + 10, + true = L4 < CL3 + 15, %% Test that max_size truncates the string which is limited by %% depth and chars_limit @@ -433,29 +433,58 @@ format_mfa(_Config) -> ok. format_time(_Config) -> - Time1 = timestamp(), - ExpectedTimestamp1 = default_time_format(Time1), - String1 = format(info,{"~p",[term]},#{time=>Time1},#{}), - ct:log(String1), - " 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: 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: term\n" = string:prefix(String3,ExpectedTimestamp3), + Time = timestamp(), + Meta = #{time=>Time}, + FC = #{template=>[time]}, + Msg = {string,""}, + ExpectedLocal = default_time_format(Time,false), + ExpectedUtc = default_time_format(Time,true), + + %% default - local time + ExpectedLocal = format(info,Msg,Meta,FC), + + %% time_offset config parameter to formatter + ExpectedLocal = format(info,Msg,Meta,FC#{time_offset=>""}), + ExpectedUtc = format(info,Msg,Meta,FC#{time_offset=>"Z"}), + + %% stdlib utc_log works when time_offset parameter is not set + application:set_env(stdlib,utc_log,true), + ExpectedUtc = format(info,Msg,Meta,FC), + + %% sasl utc_log overwrites stdlib utc_log + application:set_env(sasl,utc_log,false), + ExpectedLocal = format(info,Msg,Meta,FC), + + %% sasl utc_log overwrites stdlib utc_log + application:set_env(sasl,utc_log,true), + application:set_env(stdlib,utc_log,false), + ExpectedUtc = format(info,Msg,Meta,FC), + + %% time_offset config parameter to formatter + %% overwrites sasl and stdlib utc_log + application:set_env(sasl,utc_log,false), + ExpectedUtc = format(info,Msg,Meta,FC#{time_offset=>"Z"}), + + %% time_offset config parameter to formatter + %% overwrites sasl and stdlib utc_log + application:set_env(sasl,utc_log,true), + application:set_env(stdlib,utc_log,true), + ExpectedLocal = format(info,Msg,Meta,FC#{time_offset=>""}), + + %% time_designator config parameter to formatter + ExpectedLocalS = default_time_format(Time,false,$\s), + ExpectedUtcS = default_time_format(Time,true,$\s), + + ExpectedLocalS = format(info,Msg,Meta,FC#{time_offset=>"", + time_designator=>$\s}), + ExpectedUtcS = format(info,Msg,Meta,FC#{time_offset=>"Z", + time_designator=>$\s}), ok. format_time(cleanup,_Config) -> - application:unset_env(kernel,logger_utc), + application:unset_env(sasl,utc_log), + application:unset_env(stdlib,utc_log), ok. level_or_msg_in_meta(_Config) -> @@ -514,22 +543,17 @@ format(Log,Config) -> default_time_format(Timestamp) -> default_time_format(Timestamp,false). -default_time_format(Timestamp0,Utc) when is_integer(Timestamp0) -> +default_time_format(Timestamp,Utc) -> + default_time_format(Timestamp,Utc,$T). + +default_time_format(Timestamp0,Utc,Sep) -> Timestamp=Timestamp0+erlang:time_offset(microsecond), - %% calendar:system_time_to_rfc3339(Time,[{unit,microsecond}]). - Micro = Timestamp rem 1000000, - Sec = Timestamp div 1000000, - UniversalTime = erlang:posixtime_to_universaltime(Sec), - {Date,Time} = - if Utc -> UniversalTime; - true -> erlang:universaltime_to_localtime(UniversalTime) - end, - default_time_format(Date,Time,Micro). - -default_time_format({Y,M,D},{H,Min,S},Micro) -> - lists:flatten( - io_lib:format("~4w-~2..0w-~2..0w ~2w:~2..0w:~2..0w.~6..0w", - [Y,M,D,H,Min,S,Micro])). + Offset = if Utc -> "Z"; + true -> "" + end, + calendar:system_time_to_rfc3339(Timestamp,[{unit,microsecond}, + {time_designator,Sep}, + {offset,Offset}]). integer(Str) -> is_integer(list_to_integer(Str)). -- cgit v1.2.3 From 4e9f0492fd46d1ba6d919883cbdd5caaff8a949b Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 16 May 2018 14:58:03 +0200 Subject: Add Action=differs to logger_filters:domain/2 --- lib/kernel/doc/src/logger_filters.xml | 4 ++++ lib/kernel/src/logger_filters.erl | 6 +++++- lib/kernel/test/logger_filters_SUITE.erl | 25 +++++++++++++++++++------ 3 files changed, 28 insertions(+), 7 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger_filters.xml b/lib/kernel/doc/src/logger_filters.xml index c34ec7d14c..1bbae8be21 100644 --- a/lib/kernel/doc/src/logger_filters.xml +++ b/lib/kernel/doc/src/logger_filters.xml @@ -78,6 +78,10 @@ Compare = equals

    The filter matches if Domain is equal to MatchDomain.

    + Compare = differs +

    The filter matches if Domain differs + from MatchDomain, or if there is no domain field + in metadata.

    Compare = no_domain

    The filter matches if there is no domain field in metadata. In this case MatchDomain shall diff --git a/lib/kernel/src/logger_filters.erl b/lib/kernel/src/logger_filters.erl index 85928f0fd6..592ff28cc2 100644 --- a/lib/kernel/src/logger_filters.erl +++ b/lib/kernel/src/logger_filters.erl @@ -38,6 +38,7 @@ domain(#{meta:=Meta}=Log,{Action,Compare,MatchDomain}) (Compare==prefix_of orelse Compare==starts_with orelse Compare==equals orelse + Compare==differs orelse Compare==no_domain) andalso is_list(MatchDomain) -> filter_domain(Compare,Meta,MatchDomain,on_match(Action,Log)); @@ -87,9 +88,12 @@ filter_domain(starts_with,#{domain:=Domain},MatchDomain,OnMatch) -> is_prefix(MatchDomain,Domain,OnMatch); filter_domain(equals,#{domain:=Domain},Domain,OnMatch) -> OnMatch; +filter_domain(differs,#{domain:=Domain},MatchDomain,OnMatch) + when Domain=/=MatchDomain -> + OnMatch; filter_domain(Action,Meta,_,OnMatch) -> case maps:is_key(domain,Meta) of - false when Action==no_domain -> OnMatch; + false when Action==no_domain; Action==differs -> OnMatch; _ -> ignore end. diff --git a/lib/kernel/test/logger_filters_SUITE.erl b/lib/kernel/test/logger_filters_SUITE.erl index 21f14bbc02..c4b31370ff 100644 --- a/lib/kernel/test/logger_filters_SUITE.erl +++ b/lib/kernel/test/logger_filters_SUITE.erl @@ -81,6 +81,8 @@ domain(_Config) -> stop = logger_filters:domain(?dlog([]),{stop,starts_with,[]}), L3 = logger_filters:domain(L3=?dlog([]),{log,equals,[]}), stop = logger_filters:domain(?dlog([]),{stop,equals,[]}), + ignore = logger_filters:domain(?dlog([]),{log,differs,[]}), + ignore = logger_filters:domain(?dlog([]),{stop,differs,[]}), ignore = logger_filters:domain(?dlog([]),{log,no_domain,[]}), ignore = logger_filters:domain(?dlog([]),{stop,no_domain,[]}), @@ -90,15 +92,19 @@ domain(_Config) -> ignore = logger_filters:domain(?dlog([a]),{stop,starts_with,[a,b]}), ignore = logger_filters:domain(?dlog([a]),{log,equals,[a,b]}), ignore = logger_filters:domain(?dlog([a]),{stop,equals,[a,b]}), + L5 = logger_filters:domain(L5=?dlog([a]),{log,differs,[a,b]}), + stop = logger_filters:domain(?dlog([a]),{stop,differs,[a,b]}), ignore = logger_filters:domain(?dlog([a]),{log,no_domain,[a,b]}), ignore = logger_filters:domain(?dlog([a]),{stop,no_domain,[a,b]}), ignore = logger_filters:domain(?dlog([a,b]),{log,prefix_of,[a]}), ignore = logger_filters:domain(?dlog([a,b]),{stop,prefix_of,[a]}), - L5 = logger_filters:domain(L5=?dlog([a,b]),{log,starts_with,[a]}), + L6 = logger_filters:domain(L6=?dlog([a,b]),{log,starts_with,[a]}), stop = logger_filters:domain(?dlog([a,b]),{stop,starts_with,[a]}), ignore = logger_filters:domain(?dlog([a,b]),{log,equals,[a]}), ignore = logger_filters:domain(?dlog([a,b]),{stop,equals,[a]}), + L7 = logger_filters:domain(L7=?dlog([a,b]),{log,differs,[a]}), + stop = logger_filters:domain(?dlog([a,b]),{stop,differs,[a]}), ignore = logger_filters:domain(?dlog([a,b]),{log,no_domain,[a]}), ignore = logger_filters:domain(?dlog([a,b]),{stop,no_domain,[a]}), @@ -108,26 +114,33 @@ domain(_Config) -> ignore = logger_filters:domain(?ndlog,{stop,starts_with,[a]}), ignore = logger_filters:domain(?ndlog,{log,equals,[a]}), ignore = logger_filters:domain(?ndlog,{stop,equals,[a]}), - L6 = logger_filters:domain(L6=?ndlog,{log,no_domain,[a]}), + L8 = logger_filters:domain(L8=?ndlog,{log,differs,[a]}), + stop = logger_filters:domain(?ndlog,{stop,differs,[a]}), + L9 = logger_filters:domain(L9=?ndlog,{log,no_domain,[a]}), stop = logger_filters:domain(?ndlog,{stop,no_domain,[a]}), - L7 = logger_filters:domain(L7=?dlog([a,b,c,d]),{log,prefix_of,[a,b,c,d]}), + L10 = logger_filters:domain(L10=?dlog([a,b,c,d]),{log,prefix_of,[a,b,c,d]}), stop = logger_filters:domain(?dlog([a,b,c,d]),{stop,prefix_of,[a,b,c,d]}), - L8 = logger_filters:domain(L8=?dlog([a,b,c,d]),{log,starts_with,[a,b,c,d]}), + L11 = logger_filters:domain(L11=?dlog([a,b,c,d]),{log,starts_with,[a,b,c,d]}), stop = logger_filters:domain(?dlog([a,b,c,d]),{stop,starts_with,[a,b,c,d]}), - L9 = logger_filters:domain(L9=?dlog([a,b,c,d]),{log,equals,[a,b,c,d]}), + L12 = logger_filters:domain(L12=?dlog([a,b,c,d]),{log,equals,[a,b,c,d]}), stop = logger_filters:domain(?dlog([a,b,c,d]),{stop,equals,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog([a,b,c,d]),{log,differs,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog([a,b,c,d]),{stop,differs,[a,b,c,d]}), ignore = logger_filters:domain(?dlog([a,b,c,d]),{log,no_domain,[a,b,c,d]}), ignore = logger_filters:domain(?dlog([a,b,c,d]),{stop,no_domain,[a,b,c,d]}), %% A domain field in meta which is not a list is allowed by the - %% filter, but it will never match. + %% filter, but since MatchDomain is always a list of atoms, only + %% Action=differs can ever match. ignore = logger_filters:domain(?dlog(dummy),{log,prefix_of,[a,b,c,d]}), ignore = logger_filters:domain(?dlog(dummy),{stop,prefix_of,[a,b,c,d]}), ignore = logger_filters:domain(?dlog(dummy),{log,starts_with,[a,b,c,d]}), ignore = logger_filters:domain(?dlog(dummy),{stop,starts_with,[a,b,c,d]}), ignore = logger_filters:domain(?dlog(dummy),{log,equals,[a,b,c,d]}), ignore = logger_filters:domain(?dlog(dummy),{stop,equals,[a,b,c,d]}), + L13 = logger_filters:domain(L13=?dlog(dummy),{log,differs,[a,b,c,d]}), + stop = logger_filters:domain(?dlog(dummy),{stop,differs,[a,b,c,d]}), ignore = logger_filters:domain(?dlog(dummy),{log,no_domain,[a,b,c,d]}), ignore = logger_filters:domain(?dlog(dummy),{stop,no_domain,[a,b,c,d]}), -- cgit v1.2.3 From 8aa64c90ddd20ec0ca8cc5fe92a6124324c51da5 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 16 May 2018 16:10:30 +0200 Subject: Use system_time instead of monotonic_time as timestamp in logger --- lib/kernel/doc/src/logger.xml | 6 +++--- lib/kernel/src/code_server.erl | 4 ++-- lib/kernel/src/logger.erl | 2 +- lib/kernel/src/logger_formatter.erl | 11 +++++------ lib/kernel/src/logger_simple.erl | 7 +++---- lib/kernel/test/logger_SUITE.erl | 4 ++-- lib/kernel/test/logger_formatter_SUITE.erl | 5 ++--- 7 files changed, 18 insertions(+), 21 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index 183102f240..cac9c39f2a 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -111,7 +111,7 @@ logger:error("error happened because: ~p",[Reason]). %% Without macro pid => self() gl => group_leader() - time => erlang:monotonic_time(microsecond) + time => erlang:system_time(microsecond)

    When a log macro is used, Logger also inserts location information:

    @@ -184,8 +184,8 @@ logger:error("error happened because: ~p",[Reason]). %% Without macro

    A timestamp produced - with - erlang:monotonic_time(microsecond).

    + with + erlang:system_time(microsecond).

    diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index bbfa2a995d..1a7677295b 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -1437,7 +1437,7 @@ error_msg(Format, Args) -> logger ! {log,error,Format,Args, #{pid=>self(), gl=>group_leader(), - time=>erlang:monotonic_time(microsecond), + time=>erlang:system_time(microsecond), error_logger=>#{tag=>error}}}, ok. @@ -1446,7 +1446,7 @@ info_msg(Format, Args) -> logger ! {log,info,Format,Args, #{pid=>self(), gl=>group_leader(), - time=>erlang:monotonic_time(microsecond), + time=>erlang:system_time(microsecond), error_logger=>#{tag=>info_msg}}}, ok. diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index a839f97e62..281521ae6e 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -833,7 +833,7 @@ proc_meta() -> default(pid) -> self(); default(gl) -> group_leader(); -default(time) -> erlang:monotonic_time(microsecond). +default(time) -> erlang:system_time(microsecond). %% Remove everything upto and including this module from the stacktrace filter_stacktrace(Module,[{Module,_,_,_}|_]) -> diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl index 602c666cc7..a19e7bbc73 100644 --- a/lib/kernel/src/logger_formatter.erl +++ b/lib/kernel/src/logger_formatter.erl @@ -197,16 +197,15 @@ truncate(String,Size) -> String end. -format_time(Timestamp,#{time_offset:=Offset,time_designator:=Des}) - when is_integer(Timestamp) -> - SysTime = Timestamp + erlang:time_offset(microsecond), +%% SysTime is the system time in microseconds +format_time(SysTime,#{time_offset:=Offset,time_designator:=Des}) + when is_integer(SysTime) -> calendar:system_time_to_rfc3339(SysTime,[{unit,microsecond}, {offset,Offset}, {time_designator,Des}]). -%% Assuming this is monotonic time in microseconds -timestamp_to_datetimemicro(Timestamp,Config) when is_integer(Timestamp) -> - SysTime = Timestamp + erlang:time_offset(microsecond), +%% SysTime is the system time in microseconds +timestamp_to_datetimemicro(SysTime,Config) when is_integer(SysTime) -> Micro = SysTime rem 1000000, Sec = SysTime div 1000000, UniversalTime = erlang:posixtime_to_universaltime(Sec), diff --git a/lib/kernel/src/logger_simple.erl b/lib/kernel/src/logger_simple.erl index 5272455a2d..c8cdf25887 100644 --- a/lib/kernel/src/logger_simple.erl +++ b/lib/kernel/src/logger_simple.erl @@ -70,7 +70,7 @@ log(#{msg:=_,meta:=#{time:=_}}=Log,_Config) -> do_log( #{level=>error, msg=>{report,{error,simple_handler_process_dead}}, - meta=>#{time=>erlang:monotonic_time(microsecond)}}), + meta=>#{time=>erlang:system_time(microsecond)}}), do_log(Log); _ -> ?MODULE ! {log,Log} @@ -126,7 +126,7 @@ drop_msg(0) -> drop_msg(N) -> [#{level=>info, msg=>{"Simple handler buffer full, dropped ~w messages",[N]}, - meta=>#{time=>erlang:monotonic_time(microsecond)}}]. + meta=>#{time=>erlang:system_time(microsecond)}}]. %%%----------------------------------------------------------------- %%% Internal @@ -141,8 +141,7 @@ do_log(#{msg:=Msg,meta:=#{time:=T}}) -> display_date(T), display(Msg). -display_date(Timestamp0) when is_integer(Timestamp0) -> - Timestamp = Timestamp0 + erlang:time_offset(microsecond), +display_date(Timestamp) when is_integer(Timestamp) -> Micro = Timestamp rem 1000000, Sec = Timestamp div 1000000, {{Y,Mo,D},{H,Mi,S}} = erlang:universaltime_to_localtime( diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index e602fa2576..1d3410e356 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -769,14 +769,14 @@ process_metadata(_Config) -> undefined = logger:get_process_metadata(), {error,badarg} = ?TRY(logger:set_process_metadata(bad)), ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log}), - Time = erlang:monotonic_time(microsecond), + Time = erlang:system_time(microsecond), ProcMeta = #{time=>Time,line=>0,custom=>proc}, ok = logger:set_process_metadata(ProcMeta), S1 = ?str, ?LOG_INFO(S1,#{custom=>macro}), check_logged(info,S1,#{time=>Time,line=>0,custom=>macro}), - Time2 = erlang:monotonic_time(microsecond), + Time2 = erlang:system_time(microsecond), S2 = ?str, ?LOG_INFO(S2,#{time=>Time2,line=>1,custom=>macro}), check_logged(info,S2,#{time=>Time2,line=>1,custom=>macro}), diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl index 9baadfd65a..c171165944 100644 --- a/lib/kernel/test/logger_formatter_SUITE.erl +++ b/lib/kernel/test/logger_formatter_SUITE.erl @@ -546,8 +546,7 @@ default_time_format(Timestamp) -> default_time_format(Timestamp,Utc) -> default_time_format(Timestamp,Utc,$T). -default_time_format(Timestamp0,Utc,Sep) -> - Timestamp=Timestamp0+erlang:time_offset(microsecond), +default_time_format(Timestamp,Utc,Sep) -> Offset = if Utc -> "Z"; true -> "" end, @@ -569,7 +568,7 @@ my_try(Fun) -> try Fun() catch C:R:S -> {C,R,hd(S)} end. timestamp() -> - erlang:monotonic_time(microsecond). + erlang:system_time(microsecond). %% necessary? add_time(#{time:=_}=Meta) -> -- cgit v1.2.3 From a9a6b803a60793d42a74e0f1693a7594dffb6bc3 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 17 May 2018 18:19:57 +0200 Subject: Improve documentation of logger and error_logger --- lib/kernel/doc/src/error_logger.xml | 48 +++--- lib/kernel/doc/src/introduction_chapter.xml | 1 - lib/kernel/doc/src/kernel_app.xml | 11 +- lib/kernel/doc/src/logger.xml | 184 ++++++++++++++--------- lib/kernel/doc/src/logger_chapter.xml | 222 ++++++++++++++++------------ lib/kernel/doc/src/logger_disk_log_h.xml | 22 ++- lib/kernel/doc/src/logger_formatter.xml | 40 +++-- lib/kernel/doc/src/logger_std_h.xml | 19 ++- 8 files changed, 320 insertions(+), 227 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml index c9fe9484e4..e91fc734a4 100644 --- a/lib/kernel/doc/src/error_logger.xml +++ b/lib/kernel/doc/src/error_logger.xml @@ -36,41 +36,31 @@

    In OTP-21, a new API for logging was added to Erlang/OTP. The old error_logger module can still be used by legacy code, but new code should use the new API instead.

    +

    error_logger is no longer started by default, but is + automatically started when an event handler is added + with error_logger:add_report_handler/1,2. The error_logger + module is then also added as a handler to the new logger.

    See logger(3) and the Logging chapter - in the user's guide for more information.

    + in the User's Guide for more information.

    The Erlang error logger is an event manager (see OTP Design Principles and gen_event(3)), - registered as error_logger. Errors, warnings, and info events - are sent to the error logger from the Erlang runtime system and - the different Erlang/OTP applications. The events are, by default, - logged to the terminal. Notice that an event from a process P is - logged at the node of the group leader of P. This means - that log output is directed to the node from which a process was - created, which not necessarily is the same node as where it is - executing.

    -

    Initially, error_logger has only a primitive event - handler, which buffers and prints the raw event messages. During - system startup, the Kernel application replaces this with a - standard event handler, by default one that writes - nicely formatted output to the terminal. Kernel can also be - configured so that events are logged to a file instead, or not logged at all, - see kernel(6).

    -

    Also the SASL application, if started, adds its own event - handler, which by default writes supervisor, crash, and progress - reports to the terminal. See - sasl(6).

    -

    It is recommended that user-defined applications report - errors through the error logger to get uniform reports. - User-defined event handlers can be added to handle application-specific - events, see - add_report_handler/1,2. - Also, a useful event handler is provided in STDLIB for multi-file - logging of events, see - log_mf_h(3).

    + registered as error_logger.

    +

    Error logger is no longer started by default, but is + automatically started when an event handler is added + with + add_report_handler/1,2. The error_logger + module is then also added as a handler to the new logger, + causing log events to be forwarded from logger to error logger, + and consequently to all installed error logger event + handlers.

    +

    User-defined event handlers can be added to handle application-specific + events.

    +

    Existing event handlers provided by STDLIB and SASL are still + available, but are no longer used by OTP.

    Warning events were introduced in Erlang/OTP R9C and are enabled by default as from Erlang/OTP 18.0. To retain backwards compatibility with existing user-defined event handlers, the warning events can be @@ -191,7 +181,7 @@ ok

    The error_logger_format_depth variable is deprecated since - the Logger API was + the logging API was introduced in OTP-21. The variable, and this function, are kept for backwards compatibility since they still might be used by legacy report handlers.

    diff --git a/lib/kernel/doc/src/introduction_chapter.xml b/lib/kernel/doc/src/introduction_chapter.xml index 6e6990ddda..2eadc70abf 100644 --- a/lib/kernel/doc/src/introduction_chapter.xml +++ b/lib/kernel/doc/src/introduction_chapter.xml @@ -46,7 +46,6 @@ Start, stop, supervision, configuration, and distribution of applications Code loading Logging - Error logging Global name service Supervision of Erlang/OTP Communication with sockets diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index 7cd05dab14..5bc423afb6 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -42,7 +42,6 @@ Start, stop, supervision, configuration, and distribution of applications Code loading Logging - Error logging Global name service Supervision of Erlang/OTP Communication with sockets @@ -194,9 +193,9 @@ 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 sasl_error_logger + configuration parameter sasl_error_logger and sasl_errlog_type, see - SASL(6) + sasl(6)

    The default value is false.

    See chapter Backwards @@ -271,7 +270,7 @@ inet_parse_error_log = silent

    If set, no - error_logger messages are generated when erroneous + logger messages are generated when erroneous lines are found and skipped in the various Inet configuration files.

    @@ -486,7 +485,7 @@ MaxT = TickTime + TickTime / 4
    old error_logger event manager, and event handlers running on this manager, will still work, but they are not used by default.

    -

    The following application environment variables can still be +

    The following application configuration parameters can still be set, but they will only be used if the corresponding new logger variables are not set.

    @@ -519,12 +518,12 @@ erl -kernel logger '[{handler,default,logger_std_h,#{formatter=>{logger_formatte disk_log(3), erl_boot_server(3), erl_ddll(3), - error_logger(3), file(3), global(3), global_group(3), heart(3), inet(3), + logger(3), net_kernel(3), os(3), pg2(3), diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index cac9c39f2a..f7e740e90d 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -33,23 +33,51 @@ logger.xml logger - API module for the logger. + API module for logging in Erlang/OTP. + + + + +

    - 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 - macros should be used. For instance, - to log a new error log event:

    + This module is the main API for logging in Erlang/OTP. It + contains functions that allow applications to use a single log + API and the system to manage those log events independently. Use + the API functions or the log + macros to log events. For instance, + to log a new error event:

    ?LOG_ERROR("error happened because: ~p",[Reason]). %% With macro logger:error("error happened because: ~p",[Reason]). %% Without macro -

    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 +

    This log event is then sent to the configured log handlers which + by default means that it is 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 + must configure the default handler to do so. The simplest way is to include the following in your sys.config.

    [{kernel, @@ -63,7 +91,7 @@ logger:error("error happened because: ~p",[Reason]). %% Without macro how to use the API, see the User's Guide. - how to configure logger, + how to configure Logger, see the Configuration section in the User's Guide. the convinience macros in logger.hrl, @@ -129,8 +157,8 @@ logger:error("error happened because: ~p",[Reason]). %% Without macro update_process_metadata/1.

    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.

    + from the log call overwrite process metadata, which in turn + overwrite values set by Logger.

    @@ -165,7 +193,8 @@ logger:error("error happened because: ~p",[Reason]). %% Without macro -

    A filter which can be installed for logger or for a handler.

    +

    A filter which can be installed for the logger part of + Logger, or for a handler.

    @@ -213,9 +242,9 @@ logger:error("error happened because: ~p",[Reason]). %% Without macro ?LOG_DEBUG(FunOrFormat,Args[,Metadata])
    -

    All macros expand to a call to logger, where Level is - taken from the macro name, and location data is added. See the - description of +

    All macros expand to a call to Logger, where Level is + taken from the macro name, and location data is added to the + metadata. See the description of the metadata() type for more information about the location data.

    @@ -335,23 +364,26 @@ logger:error("error happened because: ~p",[Reason]). %% Without macro - Lookup the current configuration for logger. + Look up the current configuration for the logger part + of Logger. -

    Lookup the current configuration for logger.

    +

    Look up the current configuration for the logger part of + Logger.

    - Lookup the current configuration for the given handler. + Look up the current configuration for the given + handler. -

    Lookup the current configuration for the given handler.

    +

    Look up the current configuration for the given handler.

    - Get all logger configurations + Get all Logger configurations

    Same as logger:i(term)

    @@ -361,28 +393,28 @@ logger:error("error happened because: ~p",[Reason]). %% Without macro - Get all logger configurations + Get all Logger configurations -

    Display or return all current logger configuration.

    +

    Display or return all current Logger configurations.

    Action = string -

    Return the pretty printed current logger configuration +

    Return the pretty printed current Logger configuration as iodata.

    Action = term -

    Return the current logger configuration as a term. The - format of this term may change inbetween releases. For a +

    Return the current Logger configuration as a term. The + format of this term may change between releases. For a stable format use logger:get_handler_config/1 and - logger:get_logger_config/0. - The same as calling logger:i().

    + logger:get_logger_config/0.

    +

    The same as calling logger:i().

    Action = print -

    Pretty print all the current logger configuration to +

    Pretty print all the current Logger configuration to standard out. Example:

    logger:i(print). Current logger configuration: @@ -423,9 +455,9 @@ Current logger configuration: - Add a filter to the logger. + Add a filter to the logger part of Logger. -

    Add a filter to the logger.

    +

    Add a filter to the logger part of Logger.

    The filter fun is called with the log event as the first parameter, and the specified filter_args() as the second parameter.

    @@ -436,8 +468,8 @@ Current logger configuration:

    The filter passed. 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.

    + log event is forwarded to the handler part of Logger, + where handler filters are applied.

    stop @@ -449,9 +481,9 @@ Current logger configuration:

    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 filter_default - configuration parameter for the logger specifies if the - log event shall be discarded or forwarded to the handler - part.

    + configuration parameter for the logger part specifies if + the log event shall be discarded or forwarded to the + handler part.

    See section @@ -505,10 +537,10 @@ Current logger configuration: - Remove a filter from the logger. + Remove a filter from the logger part of Logger.

    Remove the filter identified - by FilterId from the logger.

    + by FilterId from the logger part of Logger.

    @@ -548,21 +580,21 @@ Current logger configuration:

    Set the log level for the specified Module.

    The log level for a module overrides the global log level - of the logger for log event originating from the module in + of Logger for log events originating from the module in question. Notice, however, that it does not override the level configuration for any handler.

    -

    For example: Assume that the global log level for the - logger is info, and there is one handler, h1, - with level info and one handler, h2, with +

    For example: Assume that the global log level for Logger + is info, and there is one handler, h1, with + level info and one handler, h2, with level debug.

    With this configuration, no debug messages will be logged, since they are all stopped by the global log level.

    -

    If the level for mymodule is set now set +

    If the level for mymodule is now set to debug, then debug events from this module will be logged by the handler h2, but not by handler h1.

    Debug events from other modules are still not logged.

    -

    To change the global log level for the logger, use +

    To change the global log level for Logger, use logger:set_logger_config(level,Level).

    To change the log level for a handler, use @@ -572,9 +604,10 @@ Current logger configuration:

    The originating module for a log event is only detected if mfa=>{Module,Function,Arity} exists in the metadata. When log macros are used, this association is - automatically added to all log events. If the logger API + automatically added to all log events. If an API function is called directly, without using a macro, the logging - client must explicitly add this information.

    + client must explicitly add this information if module + levels shall have any effect.

    @@ -590,10 +623,11 @@ Current logger configuration: - Setup logger handlers from the applications configuration parameters. + Set up log handlers from the application's + configuration parameters.

    Reads the application configuration parameter logger and - calls logger:add_handlers/1 with it contents.

    + calls logger:add_handlers/1 with its contents.

    @@ -602,11 +636,12 @@ Current logger configuration: Setup logger handlers. -

    This function should be used by custom logger handlers to make +

    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 logger:add_handlers/1 - just after the processes that the handler needs are started - and pass the applications logger config as an argument. Eg.

    + Normal usage is to add a call to logger:add_handlers/1 + just after the processes that the handler needs are started, + and pass the application's logger configuration as the argument. + For example:

    -behaviour(application). start(_, []) -> @@ -616,19 +651,20 @@ start(_, []) -> {ok, Pid, []}; Error -> Error end. -

    This will read the logger configuration parameter from - the handler application and start the configured handlers. The contents - of the configuration use the same rules as the +

    This reads the logger configuration parameter from + the my_all application and starts the configured + handlers. The contents of the configuration use the same + rules as the logger handler configuration.

    -

    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 sys.config file that disables the kernel handler and adds - a custom handler could looks like this:

    +

    If the handler is meant to replace the default handler, the Kernel's + default handler have to be disabled before the new handler is added. + A sys.config file that disables the Kernel handler and adds + a custom handler could look like this:

    [{kernel, [{logger, - %% Disable the default kernel handler + %% Disable the default Kernel handler [{handler,default,undefined}]}]}, {my_app, [{logger, @@ -640,10 +676,10 @@ start(_, []) -> - Set configuration data for the logger. + Set configuration data for the logger part of Logger. -

    Set configuration data for the logger. This overwrites the - current logger configuration.

    +

    Set configuration data for the logger part of Logger. This + overwrites the current logger configuration.

    To modify the existing configuration, use update_logger_config/1, or, if a more @@ -658,21 +694,25 @@ start(_, []) -> - Add or update configuration data for the logger. + Add or update configuration data for the logger part + of Logger. -

    Add or update configuration data for the logger. If the - given Key already exists, its associated - value will be changed to Value. If it - does not exist, it will be added.

    +

    Add or update configuration data for the logger part of + Logger. If the given Key already exists, + its associated value will be changed + to Value. If it does not exist, it will + be added.

    - Update configuration data for the logger. + Update configuration data for the logger part of + Logger. -

    Update configuration data for the logger. This function - behaves as if it was implemented as follows:

    +

    Update configuration data for the logger part of + Logger. This function behaves as if it was implemented as + follows:

    {ok,Old} = logger:get_logger_config(), logger:set_logger_config(maps:merge(Old,Config)). @@ -697,7 +737,7 @@ logger:set_logger_config(maps:merge(Old,Config)). , then do the merge before writing the new configuration back with this function.

    If a key is removed compared to the current configuration, - and the key is know by Logger, the default value is used. If + and the key is known 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.

    diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 4232429589..21b460e72a 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -36,14 +36,14 @@ this API can be used as is, or it can be customized to suite specific needs.

    It consists of two parts - the logger part and the - handler part. The logger will forward log events to one - or more handler(s).

    + handler part. The logger part forwards log events to + one or more handler(s).

    Conceptual overview -

    Filters can be added to the logger and to each +

    Filters can be added to the logger part and to each handler. The filters decide if an event is to be forwarded or not, and they can also modify all parts of the log event.

    @@ -121,10 +121,10 @@ log(Log, Config) -> ok -

    A handler is called by the logger backend after filtering on - logger level and on handler level for the handler which is - about to be called. The function call is done on the client - process, and it is up to the handler implementation if other +

    The handler callback is called after filtering on logger + level and on handler level for the handler in + question. The function call is done on the client process, + and it is up to the handler implementation if other processes are to be involved or not.

    Multiple instances of the same handler can be @@ -134,7 +134,7 @@ Filter -

    Filters can be set on the logger or on a handler. Logger +

    Filters can be set on the logger part, or on a handler. Logger filters are applied first, and if passed, the handler filters for each handler are applied. The handler callback is only called if all handler filters for the handler in question also @@ -145,7 +145,7 @@ {fun((Log,Extra) -> Log | stop | ignore), Extra}

    The configuration parameter filter_default - specifies the behavior if all filters return ignore. + specifies the behaviour if all filters return ignore. filter_default is by default set to log.

    The Extra parameter may contain any data that the @@ -250,52 +250,58 @@

    Logger can be configured either when the system starts through configuration parameters, - or at run-time by using the logger + or at run-time by using the logger(3) API. The recommended approach is to do the initial configuration in the sys.config file and then use the API when some configuration - has to be changed at run-time, such as the logging level.

    + has to be changed at run-time, such as the log level.

    - Application configuration parameters + Kernel Configuration Parameters

    Logger is best configured by using the configuration parameters - of kernel. There are three possible configuration parameters: + of Kernel. There are four possible configuration parameters: logger, logger_level, logger_sasl_compatible and logger_log_progress. - logger_level, logger_sasl_compatible and logger_log_progress are described in the + logger_level, logger_sasl_compatible and logger_log_progress are described in the Kernel Configuration, while logger is described below.

    -
    + - logger -

    The logger application configuration parameter is used to configure - three different logger aspects; handlers, logger filters and module levels. +

    logger

    +

    The application configuration parameter logger 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:

    DisableHandler = {handler,default,undefined} - Disable the default handler. This will allow another application + +

    Disable the default handler. This allows another application to add its own default handler. See - logger:add_handlers/1 for more details. + logger:add_handlers/1 for more details.

    +

    Only one entry of this option is allowed.

    AddHandler = {handler,HandlerId,Module,HandlerConfig} - Add a handler as if - logger:add_handler(HandlerId,Module,HandlerConfig) had been - called. - Filters = {filters, FilterDefault, [Filter]}
    + +

    Add a handler as if + logger:add_handler(HandlerId,Module,HandlerConfig) is + called.

    +

    It is allowed to have multiple entries of this option.

    + Filters = {filters, default, [Filter]}
    FilterDefault = log | stop
    Filter = {FilterId, {FilterFun, FilterConfig}}
    - Add the specified - logger filters. Only one entry is allowed of this option. - ModuleLevel - {module_level, Level, [Module]}, - this option configures the - module log level to be used. It is possible to have multiple - module_level entries. + +

    Add the specified + logger filters.

    +

    Only one entry of this option is allowed.

    + ModuleLevel = {module_level, Level, [Module]} + +

    This option configures + module log level.

    +

    It is allowed to have multiple entries of this option.

    Examples:

    -

    Output logs into a the file "logs/erlang.log"

    +

    Output logs into the file "logs/erlang.log"

    [{kernel, [{logger, @@ -338,7 +344,6 @@
    -
    @@ -441,36 +446,30 @@ error_logger in the following ways:

    - Legacy event handlers - -

    To use event handlers written for error_logger, just - add your event handler with

    - -error_logger:add_report_handler/1,2. - -

    This will automatically start the error_logger - event manager, and add error_logger as a - handler to logger, with configuration

    - -#{level=>info, - filter_default=>log, - filters=>[]}. - -

    Note that this handler will ignore events that do not - originate from the old error_logger API, or from - within OTP. This means that if your code uses the logger API - for logging, then your log events will be discarded by this - handler.

    -

    Also note that error_logger is not overload - protected.

    -
    - Logger API + API for Logging

    The old error_logger API still exists, but should only be used by legacy code. It will be removed in a later release.

    +

    Calls + to + error_logger:error_report/1,2, + + error_logger:error_msg/1,2, and + corresponding functions for warning and info messages, are + all forwarded to Logger as calls + to + logger:log(Level,Report,Metadata).

    +

    Level = error | warning | info and is taken + from the function name. Report contains the actual + log message, and Metadata contains additional + information which can be used for creating backwards + compatible events for legacy error_logger event + handlers, see + section Legacy + Event Handlers.

    - Output format + Output Format

    To get log events on the same format as produced by error_logger_tty_h and error_logger_file_h, @@ -478,13 +477,13 @@ error_logger:add_report_handler/1,2. configuration parameter legacy_header=>true. This is also the default.

    - Default format of log events from OTP + Default Format of Log Events from OTP

    By default, all log events originating from within OTP, except the former so called "SASL reports", look the same as before.

    - SASL reports + SASL Reports

    By SASL reports we mean supervisor reports, crash reports and progress reports.

    @@ -494,32 +493,63 @@ error_logger:add_report_handler/1,2. named sasl_report_tty_h and sasl_report_file_h.

    The destination of these log events were configured by - environment variables for the SASL application.

    + SASL + configuration parameters.

    Due to the specific event handlers, the output format slightly differed from other log events.

    As of OTP-21, the concept of SASL reports is removed, - meaning that the default behavior is as follows:

    + meaning that the default behaviour is as follows:

    Supervisor reports, crash reports and progress reports are no longer connected to the SASL application. Supervisor reports and crash reports are logged by default. Progress reports are not logged by default, but can be - enabled with the kernel environment - variable logger_log_progress. + enabled with the Kernel configuration + parameter + logger_log_progress.
    The output format is the same for all log events. -

    If the old behavior is preferred, the kernel environment - variable logger_sasl_compatible can be set - to true. The old SASL environment variables can then - be used as before, and the SASL reports will only be printed - if the SASL application is running - through a second log - handler named sasl_h.

    +

    If the old behaviour is preferred, the Kernel configuation + parameter + logger_sasl_compatible can be set + to true. The + old SASL + configuration parameters can then be used as + before, and the SASL reports will only be printed if the + SASL application is running, through a second log handler + named sasl_h.

    All SASL reports have a metadata field domain=>[beam,erlang,otp,sasl], which can be used, for example, by filters to stop or allow the events.

    +

    See the SASL User's + Guide for more information about the old SASL + error logging functionality.

    + + Legacy Event Handlers + +

    To use event handlers written for error_logger, just + add your event handler with

    + +error_logger:add_report_handler/1,2. + +

    This will automatically start the error_logger + event manager, and add error_logger as a + handler to logger, with configuration

    + +#{level=>info, + filter_default=>log, + filters=>[]}. + +

    Notice that this handler will ignore events that do not + originate from the old error_logger API, or from + within OTP. This means that if your code uses the Logger API + for logging, then your log events will be discarded by this + handler.

    +

    Also notice that error_logger is not overload + protected.

    @@ -535,14 +565,14 @@ error_logger:add_report_handler/1,2. converting the report to a format string and arguments. The handler might also do a custom conversion if the default format is not desired.

    -

    logger does, to a certain extent, check its input data +

    Logger does, to a certain extent, check its input data before forwarding a log event to the handlers, but it does not evaluate conversion funs or check the validity of format strings and arguments. This means that any filter or handler must be careful when formatting the data of a log event, making sure that it does not crash due to bad input data or faulty callbacks.

    -

    If a filter or handler still crashes, logger will remove the +

    If a filter or handler still crashes, Logger will remove the filter or handler in question from the configuration, and then print a short error message on the console. A debug event containing the crash reason and other details is also issued, @@ -552,13 +582,13 @@ error_logger:add_report_handler/1,2.

    Example: add a handler to log debug events to file -

    When starting an erlang node, the default behavior is that all +

    When starting an erlang node, the default behaviour is that all log events with level info and above are logged to the console. In order to also log debug events, you can either change the global log level to debug or add a separate handler to take care of this. In this example we will add a new handler which prints the debug events to a separate file.

    -

    First, we add an instance of logger_std_h with +

    First, we add an instance of logger_std_h with type {file,File}, and we set the handler's level to debug:

    @@ -575,9 +605,9 @@ ok
    #Fun<erl_eval.12.98642416> 4> logger:add_handler_filter(debug_handler,allow_debug,{Fun,[]}). ok -

    And finally, we need to make sure that the logger itself allows +

    And finally, we need to make sure that Logger itself allows debug events. This can either be done by setting the global - logger level:

    + log level:

     5> logger:set_logger_config(level,debug).
     ok
    @@ -599,21 +629,22 @@ adding_handler(logger:handler_id(),logger:config()) -> {ok,logger:config()} | {e removing_handler(logger:handler_id(),logger:config()) -> ok changing_config(logger:handler_id(),logger:config(),logger:config()) -> {ok,logger:config()} | {error,term()}
    -

    When logger:add_handler(Id,Module,Config) is called, logger - will first call Module:adding_handler(Id,Config), and if it - returns {ok,NewConfig} the NewConfig is written to the +

    When logger:add_handler(Id,Module,Config) is called, Logger + will first call Module:adding_handler(Id,Config), and if it + returns {ok,NewConfig}, NewConfig is written to the configuration database. After this, the handler may receive log - events as calls to Module:log/2.

    + events as calls to Module:log/2.

    A handler can be removed by calling - logger:remove_handler(Id). logger will call - Module:removing_handler(Id,Config), and then remove the handler's - configuration from the configuration database.

    -

    When logger:set_handler_config is called, logger calls - Module:changing_config(Id,OldConfig,NewConfig). If this function - returns ok, the NewConfig is written to the configuration - database.

    - -

    A simple handler which prints to the console could be + logger:remove_handler(Id). Logger will call + Module:removing_handler(Id,Config), and then remove the + handler's configuration from the configuration database.

    +

    When logger:set_handler_config/2,3 + or logger:update_handler_config/2 are called, Logger + calls Module:changing_config(Id,OldConfig,NewConfig). If + this function returns {ok,NewConfig}, NewConfig is + written to the configuration database.

    + +

    A simple handler that prints to the console could be implemented as follows:

    -module(myhandler). @@ -720,7 +751,7 @@ do_log(Fd,Log,#{formatter:={FModule,FConfig}}) -> and as long as the length of the message queue is lower, all log requests are handled asynchronously. This simply means that the process sending the log request (by calling a log function in the - logger API) does not wait for a response from the handler but + Logger API) does not wait for a response from the handler but continues executing immediately after the request (i.e. it will not be affected by the time it takes the handler to print to the log device). If the message queue grows larger than this value, however, @@ -876,7 +907,14 @@ logger:add_handler(my_disk_log_h, logger_disk_log_h,
    See Also -

    error_logger(3), - SASL(6)

    +

    + disk_log(3), + error_logger(3), + logger(3), + logger_disk_log_h(3), + logger_filters(3), + logger_formatter(3), + logger_std_h(3), + sasl(6)

    diff --git a/lib/kernel/doc/src/logger_disk_log_h.xml b/lib/kernel/doc/src/logger_disk_log_h.xml index 440ae28e5d..20b49b8ca0 100644 --- a/lib/kernel/doc/src/logger_disk_log_h.xml +++ b/lib/kernel/doc/src/logger_disk_log_h.xml @@ -33,21 +33,21 @@ logger_disk_log_h.xml logger_disk_log_h - A disk_log based handler for the Logger - application. + A disk_log based handler for the Logger. -

    This is a handler for the Logger application that offers circular - (wrapped) logs by using the disk_log application. Multiple instances - of this handler can be added to logger, and each instance will print to +

    This is a handler for Logger that offers circular + (wrapped) logs by using disk_log. + Multiple instances + of this handler can be added to Logger, and each instance prints to its own disk_log file, created with the name and settings specified in the handler configuration.

    The default standard handler, logger_std_h, can be - replaced by a disk_log handler at startup of the kernel application. + replaced by a disk_log handler at startup of the Kernel application. See an example of this below.

    The handler has an overload protection mechanism that will keep the handler - process and the kernel application alive during a high load of log + process and the Kernel application alive during a high load of log requests. How this feature works, and how to modify the configuration, is described in the User's Guide @@ -121,7 +121,7 @@ logger:add_handler(my_disk_log_h, logger_disk_log_h, #{filesync_repeat_interval => 1000}}).

    In order to use the disk_log handler instead of the default standard - handler when starting en Erlang node, change the Kernel default logger to + handler when starting an Erlang node, change the Kernel default logger to use disk_log. Example:

    erl -kernel logger '[{handler,default,logger_disk_log_h, @@ -141,6 +141,12 @@ erl -kernel logger '[{handler,default,logger_disk_log_h, +
    + See Also +

    logger(3)

    +

    logger_std_h(3)

    +

    disk_log(3)

    +
    diff --git a/lib/kernel/doc/src/logger_formatter.xml b/lib/kernel/doc/src/logger_formatter.xml index 370d61d338..ee43634cf9 100644 --- a/lib/kernel/doc/src/logger_formatter.xml +++ b/lib/kernel/doc/src/logger_formatter.xml @@ -39,7 +39,8 @@

    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 - (unicode:chardata()) which can be written to the output + (unicode:chardata() + ) which can be written to the output device of the handler.

    logger_formatter is the default formatter used by Logger.

    @@ -47,7 +48,8 @@
    Configuration -

    The configuration term for logger_formatter is a map, +

    The configuration term for logger_formatter is a + map, and the following keys can be set as configuration parameters:

    @@ -55,7 +57,7 @@

    A positive integer representing the value of the option with the same name to be used when calling - io_lib:format/3. + io_lib:format/3. 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 max_size.

    @@ -99,7 +101,7 @@ added by the template parameter are not replaced.

    Default is true.

    - legacy_header = boolen() + legacy_header = boolean()

    If set to true a header field is added to logger_formatter's part of Metadata. The value of @@ -181,8 +183,8 @@ to "+02:00".

    The default value is an empty string, meaning that timestamps are displayed in local time. However, for - backwards compatibility, if the SASL environment - variable + backwards compatibility, if the SASL configuration + parameter utc_log=true, the default is changed to "Z", meaning that timestamps are displayed in UTC.

    @@ -206,28 +208,42 @@ ?LOG_ERROR("name: ~p~nexit_reason: ~p",[my_name,"It crashed"])
    - legacy_header=true + legacy_header=true, single_line=false

    Default template: [{logger_formatter,header},"\n",msg,"\n"]

    Example log entry:

    -2018-05-16T11:55:50.448382+02:00 error: +=ERROR REPORT==== 17-May-2018::18:30:19.453447 === name: my_name exit_reason: "It crashed" -

    Notice that all eight levels might occur in the heading, +

    Notice that all eight levels can occur in the heading, not only ERROR, WARNING or INFO as the old error_logger produced. And microseconds are added at the end of the timestamp.

    - single_line=true + legacy_header=true, single_line=true + +

    Default template: [{logger_formatter,header},"\n",msg,"\n"]

    + +

    Notice that the template is here the same as + for single_line=false, but the resulting log entry + differs in that there is only one line after the + heading:

    + +=ERROR REPORT==== 17-May-2018::18:31:06.952665 === +name: my_name, exit_reason: "It crashed" +
    + + legacy_header=false, single_line=true

    Default template: [time," ",level,": ",msg,"\n"]

    Example log entry:

    - 2018-05-16T11:55:50.448382+02:00 error: name: my_name, exit_reason: "It crashed" + +2018-05-17T18:31:31.152864+02:00 error: name: my_name, exit_reason: "It crashed"
    legacy_header=false, single_line=false @@ -236,7 +252,7 @@ exit_reason: "It crashed"

    Example log entry:

    -2018-05-16T11:55:50.448382+02:00 error: +2018-05-17T18:32:20.105422+02:00 error: name: my_name exit_reason: "It crashed" diff --git a/lib/kernel/doc/src/logger_std_h.xml b/lib/kernel/doc/src/logger_std_h.xml index bf23d874c8..a4f2848037 100644 --- a/lib/kernel/doc/src/logger_std_h.xml +++ b/lib/kernel/doc/src/logger_std_h.xml @@ -33,17 +33,17 @@ logger_std_h.xml logger_std_h - Default handler for the Logger application. + Default handler for Logger. -

    This is the default handler for the Logger - application. Multiple instances of this handler can be added to - logger, and each instance will print logs to standard_io, +

    This is the default handler for Logger. + Multiple instances of this handler can be added to + Logger, and each instance will print logs to standard_io, standard_error or to file. The default instance that starts - with kernel is named default - which is the name to be used + with Kernel is named default - which is the name to be used for reconfiguration.

    The handler has an overload protection mechanism that will keep the handler - process and the kernel application alive during a high load of log + process and the Kernel application alive during a high load of log requests. How this feature works, and how to modify the configuration, is described in the User's Guide @@ -104,7 +104,7 @@ logger:add_handler(my_standard_h, logger_std_h, filesync_repeat_interval => 1000}}).

    In order to configure the default handler (that starts initially with - the kernel application) to log to file instead of standard_io, + the Kernel application) to log to file instead of standard_io, change the Kernel default logger to use a file. Example:

    erl -kernel logger '[{handler,default,logger_std_h, @@ -127,6 +127,11 @@ erl -kernel logger '[{handler,default,logger_std_h, +
    + See Also +

    logger(3)

    +

    logger_disk_log_h(3)

    +
    -- cgit v1.2.3 From be959158a9ebe4b4ed8c336b969ac0c0e9399312 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 18 May 2018 11:54:32 +0200 Subject: Set legacy_header=true for kernel's default handler only --- lib/kernel/src/logger.erl | 10 +++++++++- lib/kernel/src/logger_h_common.erl | 2 +- lib/kernel/src/logger_internal.hrl | 3 +-- lib/kernel/src/logger_server.erl | 2 +- lib/kernel/test/logger_disk_log_h_SUITE.erl | 15 +++++++++++---- lib/kernel/test/logger_env_var_SUITE.erl | 4 ++-- lib/kernel/test/logger_simple_SUITE.erl | 6 ++++-- lib/kernel/test/logger_std_h_SUITE.erl | 17 ++++++++++++----- 8 files changed, 41 insertions(+), 18 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 281521ae6e..51743822af 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -677,7 +677,8 @@ 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}}, + DefaultFormatter = #{formatter=>{?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}}, + DefaultConfig = DefaultFormatter#{logger_std_h=>#{type=>Type}}, NewLoggerEnv = case lists:keyfind(default, 2, Env) of {handler, default, Module, Config} -> @@ -687,6 +688,13 @@ init_default_config(Type) when Type==standard_io; %% if not configured by user AND the default %% handler is still the logger_std_h. {handler, default, Module, maps:merge(DefaultConfig,Config)}; + ({handler, default, logger_disk_log_h, _}) -> + %% Add default formatter. The point of this + %% is to get the expected formatter config + %% for the default handler, since this + %% differs from the default values that + %% logger_formatter itself adds. + {handler, default, logger_disk_log_h, maps:merge(DefaultFormatter,Config)}; (Other) -> Other end, Env); diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl index 901c4c0dad..70531c0e34 100644 --- a/lib/kernel/src/logger_h_common.erl +++ b/lib/kernel/src/logger_h_common.erl @@ -72,7 +72,7 @@ try_format(Log,Formatter,FormatterConfig) -> {log,Log}, {reason, {C,R,logger:filter_stacktrace(?MODULE,S)}}]), - case {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG} of + case {?DEFAULT_FORMATTER,#{}} of {Formatter,FormatterConfig} -> "DEFAULT FORMATTER CRASHED"; {DefaultFormatter,DefaultConfig} -> diff --git a/lib/kernel/src/logger_internal.hrl b/lib/kernel/src/logger_internal.hrl index f9377259f3..97ef4abfa4 100644 --- a/lib/kernel/src/logger_internal.hrl +++ b/lib/kernel/src/logger_internal.hrl @@ -31,8 +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}). + single_line=>false}). -define(DEFAULT_FORMAT_TEMPLATE_HEADER, [{logger_formatter,header},"\n",msg,"\n"]). -define(DEFAULT_FORMAT_TEMPLATE_SINGLE, diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index 275b9c476f..67befef156 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -328,7 +328,7 @@ default_config(_) -> #{level=>info, filters=>[], filter_default=>log, - formatter=>{?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}}. + formatter=>{?DEFAULT_FORMATTER,#{}}}. sanity_check(Owner,Key,Value) -> sanity_check_1(Owner,[{Key,Value}]). diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 3aa1c3557b..7a1736c814 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -336,24 +336,24 @@ formatter_fail(Config) -> {ok,{_,#{formatter:={logger_formatter,_}}}} = logger:get_handler_config(Name), logger:info(M1=?msg,?domain), - Got1 = try_match_file(?log_no(LogFile,1),"=INFO REPORT====.*\n"++M1,5000), + Got1 = try_match_file(?log_no(LogFile,1),"[0-9\\+\\-T:\\.]* info: "++M1,5000), ok = logger:set_handler_config(Name,formatter,{nonexistingmodule,#{}}), logger:info(M2=?msg,?domain), Got2 = try_match_file(?log_no(LogFile,1), - Got1++"=INFO REPORT====.*\nFORMATTER CRASH: .*"++M2, + escape(Got1)++"[0-9\\+\\-T:\\.]* info: FORMATTER CRASH: .*"++M2, 5000), ok = logger:set_handler_config(Name,formatter,{?MODULE,crash}), logger:info(M3=?msg,?domain), Got3 = try_match_file(?log_no(LogFile,1), - Got2++"=INFO REPORT====.*\nFORMATTER CRASH: .*"++M3, + escape(Got2)++"[0-9\\+\\-T:\\.]* info: FORMATTER CRASH: .*"++M3, 5000), ok = logger:set_handler_config(Name,formatter,{?MODULE,bad_return}), logger:info(?msg,?domain), try_match_file(?log_no(LogFile,1), - Got3++"FORMATTER ERROR: bad_return_value", + escape(Got3)++"FORMATTER ERROR: bad_return_value", 5000), %% Check that handler is still alive and was never dead @@ -1491,3 +1491,10 @@ check_tracer(T) -> dbg:stop_clear(), ct:fail({timeout,tracer}) end. + +escape([$+|Rest]) -> + [$\\,$+|escape(Rest)]; +escape([H|T]) -> + [H|escape(T)]; +escape([]) -> + []. diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl index 764f443634..32612d616e 100644 --- a/lib/kernel/test/logger_env_var_SUITE.erl +++ b/lib/kernel/test/logger_env_var_SUITE.erl @@ -542,8 +542,8 @@ logger_many_handlers(Config, Env, LogErr, LogInfo, NumProgress) -> 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), + match(Bin,<<"info:">>,NumProgress+1,info,info), + match(Bin,<<"alert:">>,0,alert,info), ok. diff --git a/lib/kernel/test/logger_simple_SUITE.erl b/lib/kernel/test/logger_simple_SUITE.erl index 0d505b14f5..d7f9fa21cf 100644 --- a/lib/kernel/test/logger_simple_SUITE.erl +++ b/lib/kernel/test/logger_simple_SUITE.erl @@ -138,7 +138,8 @@ replace_file(Config) -> ok = rpc:call(Node, logger, add_handlers, [[{handler, default, logger_std_h, - #{ logger_std_h => #{ type => {file, File} }}}]]), + #{ logger_std_h => #{ type => {file, File} }, + formatter => {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}}}]]), {ok,Bin} = sync_and_read(Node, file, File), Lines = [unicode:characters_to_list(L) || @@ -181,7 +182,8 @@ replace_disk_log(Config) -> ok = rpc:call(Node, logger, add_handlers, [[{handler, default, logger_disk_log_h, - #{ disk_log_opts => #{ file => File }}}]]), + #{ disk_log_opts => #{ file => File }, + formatter => {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}}}]]), {ok,Bin} = sync_and_read(Node, disk_log, File), Lines = [unicode:characters_to_list(L) || L <- binary:split(Bin,<<"\n">>,[global,trim])], diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index fc59d393e0..5764abd063 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -250,24 +250,24 @@ formatter_fail(Config) -> {ok,{_,#{formatter:={logger_formatter,_}}}} = logger:get_handler_config(?MODULE), logger:info(M1=?msg,?domain), - Got1 = try_match_file(Log,"=INFO REPORT====.*\n"++M1,5000), + Got1 = try_match_file(Log,"[0-9\\+\\-T:\\.]* info: "++M1,5000), ok = logger:set_handler_config(?MODULE,formatter,{nonexistingmodule,#{}}), logger:info(M2=?msg,?domain), Got2 = try_match_file(Log, - Got1++"=INFO REPORT====.*\nFORMATTER CRASH: .*"++M2, + escape(Got1)++"[0-9\\+\\-T:\\.]* info: FORMATTER CRASH: .*"++M2, 5000), ok = logger:set_handler_config(?MODULE,formatter,{?MODULE,crash}), logger:info(M3=?msg,?domain), Got3 = try_match_file(Log, - Got2++"=INFO REPORT====.*\nFORMATTER CRASH: .*"++M3, + escape(Got2)++"[0-9\\+\\-T:\\.]* info: FORMATTER CRASH: .*"++M3, 5000), ok = logger:set_handler_config(?MODULE,formatter,{?MODULE,bad_return}), logger:info(?msg,?domain), try_match_file(Log, - Got3++"FORMATTER ERROR: bad_return_value", + escape(Got3)++"FORMATTER ERROR: bad_return_value", 5000), %% Check that handler is still alive and was never dead @@ -1020,7 +1020,7 @@ mem_kill_new(Config) -> killed -> ct:pal("Slow shutdown, handler process was killed!", []) end, - timer:sleep(RestartAfter + 2000), + timer:sleep(RestartAfter * 3), true = is_pid(whereis(?MODULE)), ok after @@ -1518,3 +1518,10 @@ check_tracer(T,TimeoutFun) -> dbg:stop_clear(), TimeoutFun() end. + +escape([$+|Rest]) -> + [$\\,$+|escape(Rest)]; +escape([H|T]) -> + [H|escape(T)]; +escape([]) -> + []. -- cgit v1.2.3 From cfbe1afe6ba1f1083c8aa41c7aeb422f253f5d23 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 18 May 2018 13:27:56 +0200 Subject: Add logger:update_formatter_config/2,3 --- lib/kernel/doc/src/logger.xml | 25 ++++++++++++ lib/kernel/doc/src/logger_chapter.xml | 6 +-- lib/kernel/src/logger.erl | 18 ++++++++- lib/kernel/src/logger_server.erl | 26 ++++++++++++- lib/kernel/test/logger_formatter_SUITE.erl | 61 +++++++++++++++++++++++++++++- 5 files changed, 130 insertions(+), 6 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index f7e740e90d..be733685cc 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -773,6 +773,31 @@ logger:set_handler_config(HandlerId,maps:merge(Old,Config)). + + + Update the formatter configuration for the specified handler. + +

    Update the formatter configuration for the specified handler.

    +

    The new configuration is merged with the existing formatter + configuration.

    +

    To overwrite the existing configuration without any merge, + use + set_handler_config(HandlerId,formatter, + {FormatterModule,FormatterConfig}).

    +
    +
    + + + + Update the formatter configuration for the specified handler. + +

    Update the formatter configuration for the specified handler.

    +

    This is equivalent + to
    + update_formatter_config(HandlerId,#{Key=>Value})

    +
    +
    + Compare the severity of two log levels. diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 21b460e72a..522d0ce29d 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -157,7 +157,7 @@

    A formatter is defined as a module exporting the following function:

    - format(Log,Extra) -> unicode:chardata() + format(Log,FConfig) -> unicode:chardata()

    The formatter callback is called by each handler, and the returned string can be printed to the handler's destination @@ -413,12 +413,12 @@ return ignore.

    Default is log.

    - formatter = {Module::module(),Extra::term()} + formatter = {FModule::module(),FConfig::map()}

    See Formatter for more information.

    The default module is - logger_formatter, and Extra is + logger_formatter, and FConfig is it's configuration map.

    HandlerConfig, term() = term() diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 51743822af..d60be180bc 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -41,6 +41,7 @@ set_logger_config/1, set_logger_config/2, set_handler_config/2, set_handler_config/3, update_logger_config/1, update_handler_config/2, + update_formatter_config/2, update_formatter_config/3, get_logger_config/0, get_handler_config/1, add_handlers/1]). @@ -92,7 +93,7 @@ -type config() :: #{level => level(), filter_default => log | stop, filters => [{filter_id(),filter()}], - formatter => {module(),term()}, + formatter => {module(),map()}, term() => term()}. -type timestamp() :: integer(). @@ -386,6 +387,21 @@ get_logger_config() -> get_handler_config(HandlerId) -> logger_config:get(?LOGGER_TABLE,HandlerId). +-spec update_formatter_config(HandlerId,FormatterConfig) -> + ok | {error,term()} when + HandlerId :: config(), + FormatterConfig :: map(). +update_formatter_config(HandlerId,FormatterConfig) -> + logger_server:update_formatter_config(HandlerId,FormatterConfig). + +-spec update_formatter_config(HandlerId,Key,Value) -> + ok | {error,term()} when + HandlerId :: config(), + Key :: atom(), + Value :: term(). +update_formatter_config(HandlerId,Key,Value) -> + logger_server:update_formatter_config(HandlerId,#{Key=>Value}). + -spec set_module_level(Module,Level) -> ok | {error,term()} when Module :: module(), Level :: level(). diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index 67befef156..18a784c8c2 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -27,7 +27,8 @@ add_filter/2, remove_filter/2, set_module_level/2, reset_module_level/1, cache_module_level/1, - set_config/2, set_config/3, update_config/2]). + set_config/2, set_config/3, update_config/2, + update_formatter_config/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, @@ -111,6 +112,13 @@ update_config(Owner, Config) -> Error end. +update_formatter_config(HandlerId, FormatterConfig) + when is_map(FormatterConfig) -> + call({update_formatter_config,HandlerId,FormatterConfig}); +update_formatter_config(_HandlerId, FormatterConfig) -> + {error,{invalid_formatter_config,FormatterConfig}}. + + %%%=================================================================== %%% gen_server callbacks %%%=================================================================== @@ -216,6 +224,22 @@ handle_call({set_config,HandlerId,Config}, From, #state{tid=Tid}=State) -> _ -> {reply,{error,{not_found,HandlerId}},State} end; +handle_call({update_formatter_config,HandlerId,NewFConfig},_From, + #state{tid=Tid}=State) -> + Reply = + case logger_config:get(Tid,HandlerId) of + {ok,{_Mod,#{formatter:={FMod,OldFConfig}}=Config}} -> + try + FConfig = maps:merge(OldFConfig,NewFConfig), + check_formatter({FMod,FConfig}), + do_set_config(Tid,HandlerId, + Config#{formatter=>{FMod,FConfig}}) + catch throw:Reason -> {error,Reason} + end; + _ -> + {error,{not_found,HandlerId}} + end, + {reply,Reply,State}; handle_call({set_module_level,Module,Level}, _From, #state{tid=Tid}=State) -> Reply = logger_config:set_module_level(Tid,Module,Level), {reply,Reply,State}; diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl index c171165944..934fe4deef 100644 --- a/lib/kernel/test/logger_formatter_SUITE.erl +++ b/lib/kernel/test/logger_formatter_SUITE.erl @@ -68,7 +68,8 @@ all() -> level_or_msg_in_meta, faulty_log, faulty_config, - faulty_msg]. + faulty_msg, + update_config]. default(_Config) -> String1 = format(info,{"~p",[term]},#{},#{}), @@ -532,6 +533,54 @@ faulty_msg(_Config) -> #{})), ok. +%% Test that formatter config can be changed, and that the default +%% template is updated accordingly +update_config(_Config) -> + logger:add_handler_filter(default,silence,{fun(_,_) -> stop end,ok}), + ok = logger:add_handler(?MODULE,?MODULE,#{}), + D = lists:seq(1,1000), + logger:info("~p~n",[D]), + {Lines1,C1} = check_log(), + [ct:log(L) || L <- Lines1], + ct:log("~p",[C1]), + [Line1] = Lines1, + [_Time,"info: "++D1] = string:split(Line1," "), + true = length(D1)>3000, + true = #{}==C1, + + ok = logger:update_formatter_config(?MODULE,single_line,false), + logger:info("~p~n",[D]), + {Lines2,C2} = check_log(), + [ct:log(L) || L <- Lines2], + ct:log("~p",[C2]), + true = length(Lines2)>50, + true = #{single_line=>false}==C2, + + ok = logger:update_formatter_config(?MODULE,#{legacy_header=>true}), + logger:info("~p~n",[D]), + {Lines3,C3} = check_log(), + [ct:log(L) || L <- Lines3], + ct:log("~p",[C3]), + ["=INFO REPORT==== "++_|D3] = Lines3, + true = length(D3)>50, + true = #{legacy_header=>true,single_line=>false}==C3, + + ok = logger:update_formatter_config(?MODULE,single_line,true), + logger:info("~p~n",[D]), + {Lines4,C4} = check_log(), + [ct:log(L) || L <- Lines4], + ct:log("~p",[C4]), + ["=INFO REPORT==== "++_,D4] = Lines4, + true = length(D4)>3000, + true = #{legacy_header=>true,single_line=>true}==C4, + + ok. + +update_config(cleanup,_Config) -> + _ = logger:remove_handler(?MODULE), + _ = logger:remove_handler_filter(default,silence), + ok. + %%%----------------------------------------------------------------- %%% Internal format(Level,Msg,Meta,Config) -> @@ -575,3 +624,13 @@ add_time(#{time:=_}=Meta) -> Meta; add_time(Meta) -> Meta#{time=>timestamp()}. + +%%%----------------------------------------------------------------- +%%% handler callback +log(Log,#{formatter:={M,C}}) -> + put(log,{M:format(Log,C),C}), + ok. + +check_log() -> + {S,C} = erase(log), + {string:lexemes(S,"\n"),C}. -- cgit v1.2.3 From 4a055a655e1f82639b261689e56cbf2f1dc2d5c3 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 18 May 2018 14:49:42 +0200 Subject: Allow chars_limit to limit strings Earlier, only reports and {Format,Args} was limited by chars_limit, and max_size was needed to limit the size of a string. --- lib/kernel/src/logger_formatter.erl | 6 +----- lib/kernel/test/logger_formatter_SUITE.erl | 12 ++++++++++-- 2 files changed, 11 insertions(+), 7 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl index a19e7bbc73..5882c70ddf 100644 --- a/lib/kernel/src/logger_formatter.erl +++ b/lib/kernel/src/logger_formatter.erl @@ -85,8 +85,6 @@ format(#{level:=Level,msg:=Msg0,meta:=Meta},Config0) do_format(Level,Msg,Data,[level|Format],Config) -> [to_string(level,Level,Config)|do_format(Level,Msg,Data,Format,Config)]; -do_format(Level,Msg,Data,[msg|Format],Config) -> - [Msg|do_format(Level,Msg,Data,Format,Config)]; do_format(Level,Msg,Data,[Key|Format],Config) when is_atom(Key); is_tuple(Key) -> Value = value(Key,Data), [to_string(Key,Value,Config)|do_format(Level,Msg,Data,Format,Config)]; @@ -130,9 +128,7 @@ to_string(X) -> io_lib:format("~tp",[X]). format_msg({string,Chardata},Meta,Config) -> - try unicode:characters_to_list(Chardata) - catch _:_ -> format_msg({"INVALID STRING: ~tp",[Chardata]},Meta,Config) - end; + format_msg({"~ts",[Chardata]},Meta,Config); format_msg({report,_}=Msg,Meta,#{report_cb:=Fun}=Config) when is_function(Fun,1) -> format_msg(Msg,Meta#{report_cb=>Fun},maps:remove(report_cb,Config)); format_msg({report,Report},#{report_cb:=Fun}=Meta,Config) when is_function(Fun,1) -> diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl index 934fe4deef..eaa6c0aca5 100644 --- a/lib/kernel/test/logger_formatter_SUITE.erl +++ b/lib/kernel/test/logger_formatter_SUITE.erl @@ -269,8 +269,8 @@ format_msg(_Config) -> String8 = format(info,{string,['not',printable,list]}, #{report_cb=>fun(_)-> {"formatted",[]} end}, #{template=>Template}), - ct:log(String8), - "INVALID STRING: ['not',printable,list]" = String8, + ct:log("~ts",[String8]), % avoiding ct_log crash + "FORMAT ERROR: \"~ts\" - [['not',printable,list]]" = String8, String9 = format(info,{string,"string"},#{},#{template=>Template}), ct:log(String9), @@ -406,6 +406,14 @@ chars_limit(_Config) -> L5 = MS5, true = lists:prefix(lists:sublist(String5,L5-4),String4), + %% Test that chars_limit limits string also + Str = "123456789012345678901234567890123456789012345678901234567890123456789", + CL6 = 80, + String6 = format(info,{string,Str},Meta,FC#{chars_limit=>CL6}), + L6 = string:length(String6), + ct:log("String6: ~p~nLength6: ~p~n",[String6,L6]), + L6 = CL6, + ok. format_mfa(_Config) -> -- cgit v1.2.3 From 6342a95bdaa8681bf24f2dc8fba261d21b75c9e4 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 18 May 2018 14:26:24 +0200 Subject: Rename reset_module_level to unset_module_level --- lib/kernel/doc/src/logger.xml | 2 +- lib/kernel/src/logger.erl | 8 ++++---- lib/kernel/src/logger_config.erl | 4 ++-- lib/kernel/src/logger_server.erl | 12 ++++++------ lib/kernel/test/logger_SUITE.erl | 14 +++++++------- 5 files changed, 20 insertions(+), 20 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index be733685cc..13d3e182d4 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -613,7 +613,7 @@ Current logger configuration:
    - + Remove a module specific log setting.

    Remove a module specific log setting. After this, the diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index d60be180bc..2a0e2e5f50 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -37,7 +37,7 @@ -export([add_handler/3, remove_handler/1, add_logger_filter/2, add_handler_filter/3, remove_logger_filter/1, remove_handler_filter/2, - set_module_level/2, reset_module_level/1, + set_module_level/2, unset_module_level/1, set_logger_config/1, set_logger_config/2, set_handler_config/2, set_handler_config/3, update_logger_config/1, update_handler_config/2, @@ -408,10 +408,10 @@ update_formatter_config(HandlerId,Key,Value) -> set_module_level(Module,Level) -> logger_server:set_module_level(Module,Level). --spec reset_module_level(Module) -> ok | {error,term()} when +-spec unset_module_level(Module) -> ok | {error,term()} when Module :: module(). -reset_module_level(Module) -> - logger_server:reset_module_level(Module). +unset_module_level(Module) -> + logger_server:unset_module_level(Module). %%%----------------------------------------------------------------- %%% Misc diff --git a/lib/kernel/src/logger_config.erl b/lib/kernel/src/logger_config.erl index 40dc1b1e1b..1d35c2e068 100644 --- a/lib/kernel/src/logger_config.erl +++ b/lib/kernel/src/logger_config.erl @@ -24,7 +24,7 @@ allow/2,allow/3, get/2, get/3, get/1, create/3, create/4, set/3, - set_module_level/3,reset_module_level/2, + set_module_level/3,unset_module_level/2, cache_module_level/2, level_to_int/1]). @@ -109,7 +109,7 @@ set_module_level(Tid,Module,Level) -> ets:insert(Tid,{Module,level_to_int(Level)}), ok. -reset_module_level(Tid,Module) -> +unset_module_level(Tid,Module) -> ets:delete(Tid,Module), % should possibley overwrite instead of delete? ok. diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index 18a784c8c2..16f61abdd6 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -25,7 +25,7 @@ -export([start_link/0, add_handler/3, remove_handler/1, add_filter/2, remove_filter/2, - set_module_level/2, reset_module_level/1, + set_module_level/2, unset_module_level/1, cache_module_level/1, set_config/2, set_config/3, update_config/2, update_formatter_config/2]). @@ -83,9 +83,9 @@ set_module_level(Module,Level) when is_atom(Module) -> set_module_level(Module,_) -> {error,{not_a_module,Module}}. -reset_module_level(Module) when is_atom(Module) -> - call({reset_module_level,Module}); -reset_module_level(Module) -> +unset_module_level(Module) when is_atom(Module) -> + call({unset_module_level,Module}); +unset_module_level(Module) -> {error,{not_a_module,Module}}. cache_module_level(Module) -> @@ -243,8 +243,8 @@ handle_call({update_formatter_config,HandlerId,NewFConfig},_From, handle_call({set_module_level,Module,Level}, _From, #state{tid=Tid}=State) -> Reply = logger_config:set_module_level(Tid,Module,Level), {reply,Reply,State}; -handle_call({reset_module_level,Module}, _From, #state{tid=Tid}=State) -> - Reply = logger_config:reset_module_level(Tid,Module), +handle_call({unset_module_level,Module}, _From, #state{tid=Tid}=State) -> + Reply = logger_config:unset_module_level(Tid,Module), {reply,Reply,State}. handle_cast({async_req_reply,_Ref,_Reply} = Reply,State) -> diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index 1d3410e356..66eee8219a 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -318,7 +318,7 @@ macros(_Config) -> macros(cleanup,_Config) -> logger:remove_handler(h1), - logger:reset_module_level(?MODULE), + logger:unset_module_level(?MODULE), ok. set_level(_Config) -> @@ -350,29 +350,29 @@ set_level_module(_Config) -> logger:info(M2=?map_rep,?MY_LOC(0)), ok = check_logged(info,M2,?MY_LOC(1)), - {error,{not_a_module,{bad}}} = logger:reset_module_level({bad}), - ok = logger:reset_module_level(?MODULE), + {error,{not_a_module,{bad}}} = logger:unset_module_level({bad}), + ok = logger:unset_module_level(?MODULE), ok. set_level_module(cleanup,_Config) -> logger:remove_handler(h1), - logger:reset_module_level(?MODULE), + logger:unset_module_level(?MODULE), ok. cache_level_module(_Config) -> - ok = logger:reset_module_level(?MODULE), + ok = logger:unset_module_level(?MODULE), [] = ets:lookup(logger,?MODULE), %dirty - add API in logger_config? ?LOG_INFO(?map_rep), %% Caching is done asynchronously, so wait a bit for the update timer:sleep(100), [_] = ets:lookup(logger,?MODULE), %dirty - add API in logger_config? - ok = logger:reset_module_level(?MODULE), + ok = logger:unset_module_level(?MODULE), [] = ets:lookup(logger,?MODULE), %dirty - add API in logger_config? ok. cache_level_module(cleanup,_Config) -> - logger:reset_module_level(?MODULE), + logger:unset_module_level(?MODULE), ok. format_report(_Config) -> -- cgit v1.2.3 From e484181ecc5c3e2928d10632138837eba3c3229e Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Sun, 20 May 2018 09:21:10 +0200 Subject: Update Logger documentation --- lib/kernel/doc/src/logger.xml | 140 +++++---- lib/kernel/doc/src/logger_arch.png | Bin 31459 -> 32377 bytes lib/kernel/doc/src/logger_chapter.xml | 535 ++++++++++++++++++++-------------- lib/kernel/src/logger.erl | 10 +- 4 files changed, 419 insertions(+), 266 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index 13d3e182d4..e2f3dd6e83 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -36,33 +36,6 @@ API module for logging in Erlang/OTP. - - - - -

    This module is the main API for logging in Erlang/OTP. It contains functions that allow applications to use a single log @@ -217,6 +190,15 @@ logger:error("error happened because: ~p",[Reason]). %% Without macro erlang:system_time(microsecond).

    + + + +

    Configuration data for the + formatter. See + logger_formatter(3) + for an example of a formatter implementation.

    +
    +
    @@ -486,8 +468,8 @@ Current logger configuration: handler part.

    -

    See section - Filter in the User's Guide for more information +

    See section + Filters in the User's Guide for more information about filters.

    Some built-in filters exist. These are defined in logger_filters.

    @@ -528,7 +510,7 @@ Current logger configuration:

    See - section Filter + section Filters in the User's Guide for more information about filters.

    Some built-in filters exist. These are defined in logger_filters.

    @@ -891,14 +873,15 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)).
    - Callback Functions + + Handler Callback Functions

    The following functions are to be exported from a handler callback module.

    - Module:adding_handler(HandlerId,Config1) -> {ok,Config2} | {error,Reason} + HModule:adding_handler(HandlerId,Config1) -> {ok,Config2} | {error,Reason} An instance of this handler is about to be added. HandlerId = @@ -911,7 +894,7 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)).

    This callback function is optional.

    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.

    + initiate all resources needed by the handler.

    If everything succeeds, the callback function can add possible default values or internal state values to the configuration, and return the adjusted map @@ -922,24 +905,7 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)). - Module:removing_handler(HandlerId,Config) -> ok - The given handler is about to be removed. - - HandlerId = - handler_id() - Config = - config() - - -

    This callback function is optional.

    -

    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.

    - -
    - - - Module:changing_config(HandlerId,Config1,Config2) -> {ok,Config3} | {error,Reason} + HModule:changing_config(HandlerId,Config1,Config2) -> {ok,Config3} | {error,Reason} The configuration for this handler is about to change. HandlerId = @@ -961,6 +927,78 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)). return {error,Reason}.

    + + + HModule:log(Log,Config) -> void() + Log the given log event. + + Log = + log() + Config = + config() + + +

    This callback function is mandatory.

    +

    The function is called when all global filters and all + handler filters for the handler in question have passed for + the given log event.

    +

    The handler must log the event.

    +

    The return value from this function is ignored by + Logger.

    +
    +
    + + + HModule:removing_handler(HandlerId,Config) -> ok + The given handler is about to be removed. + + HandlerId = + handler_id() + Config = + config() + + +

    This callback function is optional.

    +

    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.

    +
    +
    + +
    + +
    + + Formatter Callback Functions +

    The following functions are to be exported from a formatter + callback module.

    +
    + + + + FModule:format(Log,FConfig) -> FormattedLogEntry + Format the given log event. + + Log = + log() + FConfig = + formatter_config() + FormattedLogEntry = + unicode:chardata() + + +

    This callback function is mandatory.

    +

    The function can be called by a log handler to convert a + log event term to a printable string. The returned value + can, for example, be printed as a log entry to the console + or a file using + io:put_chars/1,2.

    +

    See + logger_formatter(3) + for an example implementation. logger_formatter is the + default formatter used by Logger.

    +
    +
    diff --git a/lib/kernel/doc/src/logger_arch.png b/lib/kernel/doc/src/logger_arch.png index 727609a6ef..901122193a 100644 Binary files a/lib/kernel/doc/src/logger_arch.png and b/lib/kernel/doc/src/logger_arch.png differ diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 522d0ce29d..fd86e9e366 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -30,30 +30,44 @@ logger_chapter.xml +

    As of OTP-21, Erlang/OTP provides a standard API for logging + through Logger, which is part of the Kernel + application. Logger consists of the API for issuing log events, + and a customizable backend where log handlers, filters and + formatters can be plugged in.

    +

    By default, the Kernel application installs one log handler at + system start. This handler is named default. It receives + and processes standard log events produced by the Erlang runtime + system, standard behaviours and different Erlang/OTP + applications. The log events are by default written to the + terminal.

    +

    You can also configure the system so that the default handler + prints log events to a single file, or to a set of wrap logs + via disk_log.

    +

    By confiugration, you can aslo modify or disable the default + handler, replace it by a custom handler, and install additional + handlers.

    +
    Overview -

    Erlang/OTP provides a standard API for logging. The backend of - this API can be used as is, or it can be customized to suite - specific needs.

    -

    It consists of two parts - the logger part and the - handler part. The logger part forwards log events to - one or more handler(s).

    +

    A log event consists of a log level, the + message to be logged, and metadata.

    +

    The Logger backend forwards log events from the API, first + through a set of global filters, then through a set + of handler filters for each log handler.

    +

    Each filter set consists of a log level check, + followed by zero or more filter functions.

    +

    The following figure show a conseptual overview of Logger. The + figure shows two log handlers, but any number of handlers can be + installed.

    - Conceptual overview + Conceptual Overview -

    Filters can be added to the logger part and to each - handler. The filters decide if an event is to be forwarded or - not, and they can also modify all parts of the log event.

    - -

    A formatter can be set for each handler. The formatter - does the final formatting of the log event, including the log - message itself, and possibly a timestamp, header and other - metadata.

    - -

    In accordance with the Syslog protocol, RFC-5424, eight - severity levels can be specified:

    + +

    In accordance with the Syslog protocol, RFC-5424, eight log + levels can be specified:

    @@ -101,148 +115,216 @@ 7 debug-level messages - Severity levels + Log Levels
    -

    A log event is allowed by Logger if the integer value of - its Level is less than or equal to the currently - configured log level. The log level can be configured globally, - or to allow more verbose logging from a specific part of the - system, per module.

    - -
    - Customizable parts - - - Handler - -

    A handler is defined as a module exporting the following - function:

    - - log(Log, Config) -> ok - -

    The handler callback is called after filtering on logger - level and on handler level for the handler in - question. The function call is done on the client process, - and it is up to the handler implementation if other - processes are to be involved or not.

    - -

    Multiple instances of the same handler can be - added. Configuration is per instance.

    - -
    - - Filter - -

    Filters can be set on the logger part, or on a handler. Logger - filters are applied first, and if passed, the handler filters - for each handler are applied. The handler callback is only - called if all handler filters for the handler in question also - pass.

    - -

    A filter is specified as:

    - - {fun((Log,Extra) -> Log | stop | ignore), Extra} - -

    The configuration parameter filter_default - specifies the behaviour if all filters return ignore. - filter_default is by default set to log.

    - -

    The Extra parameter may contain any data that the - filter needs.

    -
    - - Formatter - -

    A formatter is defined as a module exporting the following - function:

    - - format(Log,FConfig) -> unicode:chardata() - -

    The formatter callback is called by each handler, and the - returned string can be printed to the handler's destination - (stdout, file, ...).

    -
    - -
    -
    - -
    - Built-in handlers - - - logger_std_h - -

    This is the default handler used by OTP. Multiple instances - can be started, and each instance will write log events to a - given destination, console or file. Filters can be used for - selecting which event to send to which handler instance.

    -
    - - logger_disk_log_h - -

    This handler behaves much like logger_std_h, except it uses - disk_log as its - destination.

    -
    - - error_logger - -

    This handler is to be used for backwards compatibility - only. It is not started by default, but will be automatically - started the first time an event handler is added - with - error_logger:add_report_handler/1,2.

    - -

    No built-in event handlers exist.

    -
    -
    -
    - -
    - Built-in filters - - - logger_filters:domain/2 - -

    This filter provides a way of filtering log events based on a - domain field Metadata. See - - logger_filters:domain/2

    -
    +

    A log event passes the level check if the integer value of its + log level is less than or equal to the currently configured log + level, that is, if the event is equally or more severe than the + configured level.

    +

    The global log level can be overridden by a log level + configured per module. This is to, for instance, allow more + verbose logging from a specific part of the system.

    +

    Filter functions can be used for more sophisticated filtering + than the log level check provides. A filter function can stop or + pass a log event, based on any of the event's contents. It can + also modify all parts of the log event. See see + section Filters for more + details.

    +

    If a log event passes through all global filters and all + handler filters for a specific handler, Logger forwards the event + to the handler callback. The handler formats and prints the + event to its destination. See + section Handlers for + more details.

    +

    Everything upto and including the call to the handler callbacks + is executed on the client process, that is, the process where + the log event was issued. It is up to the handler implementation + if other processes are to be involved or not.

    +

    The handlers are called in sequence, and the order is not + defined.

    +
    +
    + + Filters +

    Filters can be global, or attached to a specific + handler. Logger calls the global filters first, and if they all + pass, it calls the handler filters for each handler. Logger + calls the handler callback only if all filters attached to the + handler in question also pass.

    +

    A filter is defined as:

    +
    {FilterFun, Extra}
    +

    where FilterFun is a function of arity 2, + and Extra is any term. When applying the filter, Logger + calls the function with the log event as the first argument, + and the value of Extra as the second + argument. See + logger:filter() for type definitions.

    +

    The filter function can return stop, ignore or + the (possibly modified) log event.

    +

    If stop is returned, the log event is immediately + discarded. If the filter is global, no handler filters or + callbacks are called. If it is a handler filter, the + corresponding handler callback is not called, but the log event + is forwarded to the filters attached to the next handler, if + any.

    +

    If the log event is returned, the next filter function is + called with the returned value as the first argument. That is, + if a filter function modifies the log event, the next filter + function receivea the modified event. The value returned from + the last filter funcion is the value that the handler callback + receives.

    +

    If the filter function returns ignore, it means that it + did not recognize the log event, and thus leaves to other + filters to decide the event's destiny.

    +

    The configuration + option filter_default + specifies the behaviour if all filter functions + return ignore. filter_default is by default set + to log, meaning that if all filters ignore a log event, + Logger forwards the event to the handler + callback. If filter_default is set to stop, + Logger discards such events.

    + +

    Filters are applied in the reverse order of installation, + meaning that the last added filter is applied first.

    + +

    Global filters are added + with + logger:add_logger_filter/2 + and removed + with + logger:remove_logger_filter/1. They can also + be added at system start via Kernel configuration + parameter logger.

    +

    Handler filters are added with + with + logger:add_handler_filter/3 + and removed + with + logger:remove_handler_filter/2. They can also + be specified directly in the configuration when adding a handler + with + logger:add_handler/3 + or via Kernel configuration + parameter logger.

    + +

    To see which filters are currently installed in the system, + use logger:i/0, + or + logger:get_logger_config/0 + and + logger:get_handler_config/1. Filters are + applied in the order they are listed.

    + +

    For convenience, the following built-in filters exist:

    + + + +

    + logger_filters:domain/2 provides a way of + filtering log events based on a + domain field Metadata.

    +
    + +

    + logger_filters:level/2 provides a way of + filtering log events based on the log level.

    +
    + +

    + logger_filters:progress/2 stops or allows + progress reports from supervisor + and application_controller.

    +
    + +

    + logger_filters:remote_gl/2 stops or allows + log events originating from a process that has its group + leader on a remote node.

    +
    +
    +
    - logger_filters:level/2 - -

    This filter provides a way of filtering log events based - on the log level. See - logger_filters:level/2

    -
    +
    + + Handlers +

    A handler is defined as a module exporting at least the + following function:

    + +
    log(Log, Config)
    + +

    This function is called when a log event has passed through all + global filters, and all handler filters attached to the handler + in question. The function call is executed on the client + process, and it is up to the handler implementation if other + processes are to be involved or not.

    + +

    Logger allows adding multiple instances of a handler + callback. That is, the callback module might be implemented in + such a way that, by using different handler identities, the same + callback module can be used for multiple handler + instances. Handler configuration is per instance.

    + +

    In addition to the mandatory callback function log/2, a + handler module can export the optional callback + functions adding_handler/2, changing_config/3 + and removing_handler/1. See + section Handler + Callback Functions in the logger(3) manual for more + information about these function.

    + +

    The following built-in handlers exist:

    - logger_filters:progress/2 - -

    This filter matches all progress reports - from supervisor and application_controller. - See - logger_filters:progress/2

    -
    + + logger_std_h + +

    This is the default handler used by OTP. Multiple instances + can be started, and each instance will write log events to a + given destination, console or file. Filters can be used for + selecting which event to send to which handler instance.

    +
    - logger_filters:remote_gl/2 - -

    This filter matches all events originating from a process - that has its group leader on a remote node. - See - logger_filters:remote_gl/2

    -
    -
    -
    + logger_disk_log_h + +

    This handler behaves much like logger_std_h, except it uses + disk_log as its + destination.

    +
    -
    - Default formatter + error_logger + +

    This handler is to be used for backwards compatibility + only. It is not started by default, but will be + automatically started the first time an error_logger + event handler is added + with + error_logger:add_report_handler/1,2.

    + +

    The old error_logger event handlers in STDLIB and + SASL still exist, but they are not added by Erlang/OTP.

    +
    + +
    -

    The default formatter is logger_formatter. - See - logger_formatter:format/2.

    -
    +
    + + Formatters +

    A formatter can be used by the handler implementation to do the + final formatting of a log event, before printing to the + handler's destination. The handler callback gets the formatter + information in the handler configuration, which is passed as the + second argument to + HModule:log/2.

    +

    The formatter information consits of a formatter + module, FModule and its + configuration, FConfig. FModule must export the + following function, which can be called by the handler:

    +
    format(Log,FConfig)
    +	-> FormattedLogEntry
    +

    See the + logger_formatter(3) manual for the full + description of the default formatter used by Logger.

    @@ -347,101 +429,130 @@
    - Logger configuration + Global Logger Configuration - level + level = + logger:level() -

    Specifies the severity level to log.

    +

    Specifies the global log level to log.

    +

    See table Log + Levels in the Overview section for a listing + and description of possible log levels.

    +

    The initial value of this option is set by the Kernel + configuration + parameter + logger_level. It can be changed during + runtime + with + logger:set_logger_config(level,NewLevel).

    - filters + filters = [{ + logger:filter_id(), + + logger:filter()}] -

    Logger filters are added or removed with +

    Global filters are added and removed with logger:add_logger_filter/2 and logger:remove_logger_filter/1, respectively.

    -

    See Filter for more - information.

    -

    By default, no filters exist.

    +

    See section Filters + for more information.

    +

    Default is [], that is, no filters exist.

    - filter_default = log | stop + filter_default = log | stop

    Specifies what to do with an event if all filters return ignore.

    +

    See section Filters + for more information about how this option is used.

    Default is log.

    - handlers - -

    Handlers are added or removed with - - logger:add_handler/3 and - - logger:remove_handler/1, - respectively.

    -

    See Handler for more - information.

    -
    - Handler configuration + Handler Configuration - level + level = + logger:level() -

    Specifies the severity level to log.

    +

    Specifies the log level which the handler logs.

    +

    See table Log + Levels in the Overview section for a listing + and description of possible log levels.

    +

    The log level can be specified when adding the handler, + or changed during runtime with, for + instance, + logger:set_handler_config/3.

    +

    Default is info.

    - filters + filters = [{ + logger:filter_id(), + + logger:filter()}]

    Handler filters can be specified when adding the handler, - or added or removed later with + or added or removed during runtime with logger:add_handler_filter/3 and logger:remove_handler_filter/2, respectively.

    -

    See Filter for more +

    See Filters for more information.

    -

    By default, no filters exist.

    +

    Default is [], that is, no filters exist.

    - filter_default = log | stop + filter_default = log | stop

    Specifies what to do with an event if all filters return ignore.

    +

    See section Filters + for more information about how this option is used.

    Default is log.

    - formatter = {FModule::module(),FConfig::map()} + formatter = {module(), + logger:formatter_config()} -

    See Formatter for more +

    The formatter which the handler can use for converting + the log event term to a printable string.

    +

    See Formatters for more information.

    -

    The default module is - logger_formatter, and FConfig is - it's configuration map.

    +

    Default + is {logger_formatter,DefaultFormatterConfig}, see + the + logger_formatter(3) + manual for information about this formatter and its + default configuration.

    - HandlerConfig, term() = term() + HandlerConfig, atom() = term() - Any keys not listed above are considered to be handler specific - configuration. The configuration of the Kernel handlers can be found in - logger_std_h and - logger_disk_log_h. +

    Any keys not listed above are considered to be handler + specific configuration. The configuration of the Kernel + handlers can be found in + the logger_std_h(3) + and + logger_disk_log_h(3) + manual pages.

    -

    Note that level and filters are obeyed by +

    Notice that level and filters are obeyed by Logger itself before forwarding the log events to each - handler, while formatter is left to the handler - implementation. All Logger's built-in handlers will call the - given formatter before printing.

    + handler, while formatter and all handle specific + options are left to the handler implementation.

    +

    All Logger's built-in handlers will call the given formatter + before printing.

    - Backwards compatibility with error_logger + Backwards Compatibility with error_logger

    Logger provides backwards compatibility with the old error_logger in the following ways:

    @@ -556,15 +667,17 @@ error_logger:add_report_handler/1,2.
    - Error handling + Error Handling

    Log data is expected to be either a format string and - arguments, a string (unicode:chardata), or a report (map or + arguments, a string + ( + unicode:chardata()), or a report (map or key-value list) which can be converted to a format string and - arguments by the handler. A default report callback should be - included in the log event's metadata, which can be used for - converting the report to a format string and arguments. The - handler might also do a custom conversion if the default format - is not desired.

    + arguments by the handler. If a report is given, a default report + callback can be included in the log event's metadata. The + handler can use this callback for converting the report to a + format string and arguments. The handler might also do a custom + conversion if the default format is not desired.

    Logger does, to a certain extent, check its input data before forwarding a log event to the handlers, but it does not evaluate conversion funs or check the validity of format strings @@ -630,17 +743,17 @@ removing_handler(logger:handler_id(),logger:config()) -> ok changing_config(logger:handler_id(),logger:config(),logger:config()) -> {ok,logger:config()} | {error,term()}

    When logger:add_handler(Id,Module,Config) is called, Logger - will first call Module:adding_handler(Id,Config), and if it + will first call HModule:adding_handler(Id,Config), and if it returns {ok,NewConfig}, NewConfig is written to the configuration database. After this, the handler may receive log - events as calls to Module:log/2.

    + events as calls to HModule:log/2.

    A handler can be removed by calling logger:remove_handler(Id). Logger will call - Module:removing_handler(Id,Config), and then remove the + HModule:removing_handler(Id,Config), and then remove the handler's configuration from the configuration database.

    When logger:set_handler_config/2,3 or logger:update_handler_config/2 are called, Logger - calls Module:changing_config(Id,OldConfig,NewConfig). If + calls HModule:changing_config(Id,OldConfig,NewConfig). If this function returns {ok,NewConfig}, NewConfig is written to the configuration database.

    @@ -723,7 +836,7 @@ do_log(Fd,Log,#{formatter:={FModule,FConfig}}) ->
    - Protecting the handler from overload + Protecting the Handler from Overload

    In order for the built-in handlers to survive, and stay responsive, during periods of high load (i.e. when huge numbers of incoming log requests must be handled), a mechanism for overload protection @@ -734,7 +847,7 @@ do_log(Fd,Log,#{formatter:={FModule,FConfig}}) -> as follows:

    - Message queue length + Message Queue Length

    The handler process keeps track of the length of its message queue and reacts in different ways depending on the current status. The purpose is to keep the handler in, or (as quickly as possible), @@ -829,7 +942,7 @@ logger:add_handler(my_standard_h, logger_std_h,

    - Controlling bursts of log requests + Controlling Bursts of Log Requests

    A potential problem with large bursts of log requests, is that log files may get full or wrapped too quickly (in the latter case overwriting previously logged data that could be of great importance). For this reason, @@ -870,7 +983,7 @@ logger:add_handler(my_disk_log_h, logger_disk_log_h,

    - Terminating a large handler + Terminating a Large Handler

    A handler process may grow large even if it can manage peaks of high load without crashing. The overload protection mechanism includes user configurable levels for a maximum allowed message queue length and maximum allowed memory diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 2a0e2e5f50..9c721d7fee 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -93,14 +93,16 @@ -type config() :: #{level => level(), filter_default => log | stop, filters => [{filter_id(),filter()}], - formatter => {module(),map()}, - term() => term()}. + formatter => {module(),formatter_config()}, + atom() => term()}. -type timestamp() :: integer(). +-type formatter_config() :: #{atom() => term()}. -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, config_handler/0]). + filter_id/0,filter/0,filter_arg/0,filter_return/0,config_handler/0, + formatter_config/0]). %%%----------------------------------------------------------------- %%% API @@ -390,7 +392,7 @@ get_handler_config(HandlerId) -> -spec update_formatter_config(HandlerId,FormatterConfig) -> ok | {error,term()} when HandlerId :: config(), - FormatterConfig :: map(). + FormatterConfig :: formatter_config(). update_formatter_config(HandlerId,FormatterConfig) -> logger_server:update_formatter_config(HandlerId,FormatterConfig). -- cgit v1.2.3 From d35c100a10239edc669b465a2176000ffcc1300f Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 21 May 2018 16:47:46 +0200 Subject: Change Compare parameter to logger_filters:domain/2 This configuration option has been removed. logger_formatter will read the utc_log configuration parameter and format the timestamp accordingly. --- lib/kernel/doc/src/logger.xml | 4 +- lib/kernel/doc/src/logger_chapter.xml | 2 +- lib/kernel/doc/src/logger_filters.xml | 145 ++++++++++++++++++++----------- lib/kernel/src/logger_filters.erl | 24 ++--- lib/kernel/src/logger_internal.hrl | 4 +- lib/kernel/test/logger_env_var_SUITE.erl | 28 +++--- lib/kernel/test/logger_filters_SUITE.erl | 134 ++++++++++++++-------------- lib/kernel/test/logger_legacy_SUITE.erl | 4 +- 8 files changed, 192 insertions(+), 153 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index e2f3dd6e83..6e50473fa8 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -421,10 +421,10 @@ Current logger configuration: Config: stop Id: domain Fun: fun logger_filters:domain/2 - Config: {log,prefix_of,[beam,erlang,otp,sasl]} + Config: {log,super,[beam,erlang,otp,sasl]} Id: no_domain Fun: fun logger_filters:domain/2 - Config: {log,no_domain,[]} + Config: {log,undefined,[]} Handler Config: logger_std_h: #{type => standard_io} Level set per module: diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index fd86e9e366..34c65be47a 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -594,7 +594,7 @@ except the former so called "SASL reports", look the same as before.

    - SASL Reports + SASL Reports

    By SASL reports we mean supervisor reports, crash reports and progress reports.

    diff --git a/lib/kernel/doc/src/logger_filters.xml b/lib/kernel/doc/src/logger_filters.xml index 1bbae8be21..f92181ea3f 100644 --- a/lib/kernel/doc/src/logger_filters.xml +++ b/lib/kernel/doc/src/logger_filters.xml @@ -52,61 +52,100 @@ - Filter log events based on the domain field in metadata. + Filter log events based on the domain field in + metadata. -

    This filter provides a way of filtering log events based on a - domain field Metadata.

    - -

    The Extra parameter is specified when - adding the filter - via - logger:add_logger_filter/2 - or - logger:add_handler_filter/3.

    - -

    The filter compares the value of the domain field - in the log event's metadata (Domain) - to MatchDomain as follows:

    - - - Compare = starts_with -

    The filter matches if MatchDomain is a prefix - of Domain.

    - Compare = prefix_of -

    The filter matches if Domain is a prefix - of MatchDomain.

    - Compare = equals -

    The filter matches if Domain is equal - to MatchDomain.

    - Compare = differs -

    The filter matches if Domain differs - from MatchDomain, or if there is no domain field - in metadata.

    - Compare = no_domain -

    The filter matches if there is no domain field in - metadata. In this case MatchDomain shall - be [].

    -
    - -

    If the filter matches and Action = - log, the log event is allowed. If the filter matches - and Action = stop, the log event is - stopped.

    - -

    If the filter does not match, it returns ignore, - meaning that other filters, or the value of the - configuration parameter filter_default, will decide - if the event is allowed or not.

    - -

    Log events that do not contain any domain field, will - only match when Compare = no_domain.

    - -

    Example: stop all events with - domain [beam,erlang,otp,sasl|_]

    - - +

    This filter provides a way of filtering log events based on a + domain field in Metadata. This field is + optional, and the purpose of using it is to group log events + from, for example, a specific functional area. This allows + filtering or other specialized treatment in a Logger + handler.

    + +

    A domain field must be a list of atoms, creating smaller + and more specialized domains as the list grows longer. The + biggest domain is [], which comprices all + possible domains.

    + +

    For example, consider the following domains:

    +
    +D1 = [beam,erlang,otp]
    +D2 = [beam,erlang,otp,sasl]
    + +

    D1 is the biggest of the two, and is said to be a + super-domain of D2. D2 is a + sub-domain D1. Both D1 and D2 are + sub-domains of []

    + +

    The above domains are used for logs originating from + Erlang/OTP. D1 specifies that the log event comes from + Erlang/OTP in general, and D2 indicates that the log event + is a so + called SASL + report.

    + +

    The Extra parameter to + the domain/2 function is specified when adding the + filter via + logger:add_logger_filter/2 + or + logger:add_handler_filter/3.

    + +

    The filter compares the value of the domain field + in the log event's metadata (Domain) + to MatchDomain as follows:

    + + + Compare = sub + +

    The filter matches if Domain is equal to or + a sub-domain of MatchDomain, that is, + if MatchDomain is a prefix of Domain.

    +
    + Compare = super + +

    The filter matches if Domain is equal to or a + super-domain of MatchDomain, that is, + if Domain is a prefix of MatchDomain.

    +
    + Compare = equal + +

    The filter matches if Domain is equal + to MatchDomain.

    +
    + Compare = not_equal + +

    The filter matches if Domain is not equal + to MatchDomain, or if there is no domain field in + metadata.

    +
    + Compare = undefined +

    The filter matches if there is no domain field in + metadata. In this case MatchDomain + must be set to [].

    +
    +
    + +

    If the filter matches and Action = log, + the log event is allowed. If the filter matches + and Action = stop, the log event is + stopped.

    + +

    If the filter does not match, it returns ignore, + meaning that other filters, or the value of the + configuration parameter filter_default, decide if the + event is allowed or not.

    + +

    Log events that do not contain any domain field, match only + when Compare = undefined + or Compare = not_equal.

    + +

    Example: stop all events with + domain [beam,erlang,otp,sasl|_]

    + + logger:set_handler_config(h1,filter_default,log). % this is the default -Filter = {fun logger_filters:domain/2,{stop,starts_with,[beam,erlang,otp,sasl]}}. +Filter = {fun logger_filters:domain/2,{stop,sub,[beam,erlang,otp,sasl]}}. logger:add_handler_filter(h1,no_sasl,Filter). ok
    diff --git a/lib/kernel/src/logger_filters.erl b/lib/kernel/src/logger_filters.erl index 592ff28cc2..322aa0265d 100644 --- a/lib/kernel/src/logger_filters.erl +++ b/lib/kernel/src/logger_filters.erl @@ -31,15 +31,15 @@ Log :: logger:log(), Extra :: {Action,Compare,MatchDomain}, Action :: log | stop, - Compare :: prefix_of | starts_with | equals | no_domain, + Compare :: super | sub | equal | not_equal | undefined, MatchDomain :: list(atom()). domain(#{meta:=Meta}=Log,{Action,Compare,MatchDomain}) when ?IS_ACTION(Action) andalso - (Compare==prefix_of orelse - Compare==starts_with orelse - Compare==equals orelse - Compare==differs orelse - Compare==no_domain) andalso + (Compare==super orelse + Compare==sub orelse + Compare==equal orelse + Compare==not_equal orelse + Compare==undefined) andalso is_list(MatchDomain) -> filter_domain(Compare,Meta,MatchDomain,on_match(Action,Log)); domain(Log,Extra) -> @@ -82,18 +82,18 @@ remote_gl(Log,Action) -> %%%----------------------------------------------------------------- %%% Internal -filter_domain(prefix_of,#{domain:=Domain},MatchDomain,OnMatch) -> +filter_domain(super,#{domain:=Domain},MatchDomain,OnMatch) -> is_prefix(Domain,MatchDomain,OnMatch); -filter_domain(starts_with,#{domain:=Domain},MatchDomain,OnMatch) -> +filter_domain(sub,#{domain:=Domain},MatchDomain,OnMatch) -> is_prefix(MatchDomain,Domain,OnMatch); -filter_domain(equals,#{domain:=Domain},Domain,OnMatch) -> +filter_domain(equal,#{domain:=Domain},Domain,OnMatch) -> OnMatch; -filter_domain(differs,#{domain:=Domain},MatchDomain,OnMatch) +filter_domain(not_equal,#{domain:=Domain},MatchDomain,OnMatch) when Domain=/=MatchDomain -> OnMatch; -filter_domain(Action,Meta,_,OnMatch) -> +filter_domain(Compare,Meta,_,OnMatch) -> case maps:is_key(domain,Meta) of - false when Action==no_domain; Action==differs -> OnMatch; + false when Compare==undefined; Compare==not_equal -> OnMatch; _ -> ignore end. diff --git a/lib/kernel/src/logger_internal.hrl b/lib/kernel/src/logger_internal.hrl index 97ef4abfa4..fedd6db370 100644 --- a/lib/kernel/src/logger_internal.hrl +++ b/lib/kernel/src/logger_internal.hrl @@ -27,8 +27,8 @@ ?DEFAULT_HANDLER_FILTERS([beam,erlang,otp])). -define(DEFAULT_HANDLER_FILTERS(Domain), [{remote_gl,{fun logger_filters:remote_gl/2,stop}}, - {domain,{fun logger_filters:domain/2,{log,prefix_of,Domain}}}, - {no_domain,{fun logger_filters:domain/2,{log,no_domain,[]}}}]). + {domain,{fun logger_filters:domain/2,{log,super,Domain}}}, + {no_domain,{fun logger_filters:domain/2,{log,undefined,[]}}}]). -define(DEFAULT_FORMATTER,logger_formatter). -define(DEFAULT_FORMAT_CONFIG,#{legacy_header=>true, single_line=>false}). diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl index 32612d616e..081d4f0a80 100644 --- a/lib/kernel/test/logger_env_var_SUITE.erl +++ b/lib/kernel/test/logger_env_var_SUITE.erl @@ -82,7 +82,7 @@ default(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]}}} = + {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), true = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(logger_simple,1,Hs), @@ -95,7 +95,7 @@ default_sasl_compatible(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]}}} = + {domain,{_,{log,super,[beam,erlang,otp]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(logger_simple,1,Hs), @@ -107,7 +107,7 @@ error_logger_tty(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]}}} = + {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), true = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(logger_simple,1,Hs), @@ -121,7 +121,7 @@ error_logger_tty_sasl_compatible(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]}}} = + {domain,{_,{log,super,[beam,erlang,otp]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(logger_simple,1,Hs), @@ -138,7 +138,7 @@ error_logger_false(Config) -> info = maps:get(level,SimpleC), notice = maps:get(level,L), SimpleFilters = maps:get(filters,SimpleC), - {domain,{_,{log,prefix_of,[beam,erlang,otp,sasl]}}} = + {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), true = lists:keymember(stop_progress,1,SimpleFilters), false = lists:keymember(sasl_h,1,Hs), @@ -155,7 +155,7 @@ error_logger_false_progress(Config) -> info = maps:get(level,SimpleC), notice = maps:get(level,L), SimpleFilters = maps:get(filters,SimpleC), - {domain,{_,{log,prefix_of,[beam,erlang,otp,sasl]}}} = + {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), false = lists:keymember(stop_progress,1,SimpleFilters), false = lists:keymember(sasl_h,1,Hs), @@ -172,7 +172,7 @@ error_logger_false_sasl_compatible(Config) -> info = maps:get(level,SimpleC), notice = maps:get(level,L), SimpleFilters = maps:get(filters,SimpleC), - {domain,{_,{log,prefix_of,[beam,erlang,otp]}}} = + {domain,{_,{log,super,[beam,erlang,otp]}}} = lists:keyfind(domain,1,SimpleFilters), false = lists:keymember(stop_progress,1,SimpleFilters), true = lists:keymember(sasl_h,1,Hs), @@ -220,7 +220,7 @@ logger_file(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]}}} = + {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), true = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(logger_simple,1,Hs), @@ -243,7 +243,7 @@ logger_file_sasl_compatible(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]}}} = + {domain,{_,{log,super,[beam,erlang,otp]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(logger_simple,1,Hs), @@ -266,7 +266,7 @@ logger_file_log_progress(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]}}} = + {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(logger_simple,1,Hs), @@ -353,7 +353,7 @@ logger_filters(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]}}} = + {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(logger_simple,1,Hs), @@ -406,7 +406,7 @@ logger_module_level(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]}}} = + {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(logger_simple,1,Hs), @@ -428,7 +428,7 @@ logger_disk_log(Config) -> {?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]}}} = + {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), true = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(logger_simple,1,Hs), @@ -465,7 +465,7 @@ logger_undefined(Config) -> info = maps:get(level,SimpleC), info = maps:get(level,L), SimpleFilters = maps:get(filters,SimpleC), - {domain,{_,{log,prefix_of,[beam,erlang,otp,sasl]}}} = + {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), true = lists:keymember(stop_progress,1,SimpleFilters), false = lists:keymember(sasl_h,1,Hs), diff --git a/lib/kernel/test/logger_filters_SUITE.erl b/lib/kernel/test/logger_filters_SUITE.erl index c4b31370ff..11cce8fd20 100644 --- a/lib/kernel/test/logger_filters_SUITE.erl +++ b/lib/kernel/test/logger_filters_SUITE.erl @@ -75,79 +75,79 @@ all() -> remote_gl]. domain(_Config) -> - L1 = logger_filters:domain(L1=?dlog([]),{log,prefix_of,[]}), - stop = logger_filters:domain(?dlog([]),{stop,prefix_of,[]}), - L2 = logger_filters:domain(L2=?dlog([]),{log,starts_with,[]}), - stop = logger_filters:domain(?dlog([]),{stop,starts_with,[]}), - L3 = logger_filters:domain(L3=?dlog([]),{log,equals,[]}), - stop = logger_filters:domain(?dlog([]),{stop,equals,[]}), - ignore = logger_filters:domain(?dlog([]),{log,differs,[]}), - ignore = logger_filters:domain(?dlog([]),{stop,differs,[]}), - ignore = logger_filters:domain(?dlog([]),{log,no_domain,[]}), - ignore = logger_filters:domain(?dlog([]),{stop,no_domain,[]}), - - L4 = logger_filters:domain(L4=?dlog([a]),{log,prefix_of,[a,b]}), - stop = logger_filters:domain(?dlog([a]),{stop,prefix_of,[a,b]}), - ignore = logger_filters:domain(?dlog([a]),{log,starts_with,[a,b]}), - ignore = logger_filters:domain(?dlog([a]),{stop,starts_with,[a,b]}), - ignore = logger_filters:domain(?dlog([a]),{log,equals,[a,b]}), - ignore = logger_filters:domain(?dlog([a]),{stop,equals,[a,b]}), - L5 = logger_filters:domain(L5=?dlog([a]),{log,differs,[a,b]}), - stop = logger_filters:domain(?dlog([a]),{stop,differs,[a,b]}), - ignore = logger_filters:domain(?dlog([a]),{log,no_domain,[a,b]}), - ignore = logger_filters:domain(?dlog([a]),{stop,no_domain,[a,b]}), - - ignore = logger_filters:domain(?dlog([a,b]),{log,prefix_of,[a]}), - ignore = logger_filters:domain(?dlog([a,b]),{stop,prefix_of,[a]}), - L6 = logger_filters:domain(L6=?dlog([a,b]),{log,starts_with,[a]}), - stop = logger_filters:domain(?dlog([a,b]),{stop,starts_with,[a]}), - ignore = logger_filters:domain(?dlog([a,b]),{log,equals,[a]}), - ignore = logger_filters:domain(?dlog([a,b]),{stop,equals,[a]}), - L7 = logger_filters:domain(L7=?dlog([a,b]),{log,differs,[a]}), - stop = logger_filters:domain(?dlog([a,b]),{stop,differs,[a]}), - ignore = logger_filters:domain(?dlog([a,b]),{log,no_domain,[a]}), - ignore = logger_filters:domain(?dlog([a,b]),{stop,no_domain,[a]}), - - ignore = logger_filters:domain(?ndlog,{log,prefix_of,[a]}), - ignore = logger_filters:domain(?ndlog,{stop,prefix_of,[a]}), - ignore = logger_filters:domain(?ndlog,{log,starts_with,[a]}), - ignore = logger_filters:domain(?ndlog,{stop,starts_with,[a]}), - ignore = logger_filters:domain(?ndlog,{log,equals,[a]}), - ignore = logger_filters:domain(?ndlog,{stop,equals,[a]}), - L8 = logger_filters:domain(L8=?ndlog,{log,differs,[a]}), - stop = logger_filters:domain(?ndlog,{stop,differs,[a]}), - L9 = logger_filters:domain(L9=?ndlog,{log,no_domain,[a]}), - stop = logger_filters:domain(?ndlog,{stop,no_domain,[a]}), - - L10 = logger_filters:domain(L10=?dlog([a,b,c,d]),{log,prefix_of,[a,b,c,d]}), - stop = logger_filters:domain(?dlog([a,b,c,d]),{stop,prefix_of,[a,b,c,d]}), - L11 = logger_filters:domain(L11=?dlog([a,b,c,d]),{log,starts_with,[a,b,c,d]}), - stop = logger_filters:domain(?dlog([a,b,c,d]),{stop,starts_with,[a,b,c,d]}), - L12 = logger_filters:domain(L12=?dlog([a,b,c,d]),{log,equals,[a,b,c,d]}), - stop = logger_filters:domain(?dlog([a,b,c,d]),{stop,equals,[a,b,c,d]}), - ignore = logger_filters:domain(?dlog([a,b,c,d]),{log,differs,[a,b,c,d]}), - ignore = logger_filters:domain(?dlog([a,b,c,d]),{stop,differs,[a,b,c,d]}), - ignore = logger_filters:domain(?dlog([a,b,c,d]),{log,no_domain,[a,b,c,d]}), - ignore = logger_filters:domain(?dlog([a,b,c,d]),{stop,no_domain,[a,b,c,d]}), + L1 = logger_filters:domain(L1=?dlog([]),{log,super,[]}), + stop = logger_filters:domain(?dlog([]),{stop,super,[]}), + L2 = logger_filters:domain(L2=?dlog([]),{log,sub,[]}), + stop = logger_filters:domain(?dlog([]),{stop,sub,[]}), + L3 = logger_filters:domain(L3=?dlog([]),{log,equal,[]}), + stop = logger_filters:domain(?dlog([]),{stop,equal,[]}), + ignore = logger_filters:domain(?dlog([]),{log,not_equal,[]}), + ignore = logger_filters:domain(?dlog([]),{stop,not_equal,[]}), + ignore = logger_filters:domain(?dlog([]),{log,undefined,[]}), + ignore = logger_filters:domain(?dlog([]),{stop,undefined,[]}), + + L4 = logger_filters:domain(L4=?dlog([a]),{log,super,[a,b]}), + stop = logger_filters:domain(?dlog([a]),{stop,super,[a,b]}), + ignore = logger_filters:domain(?dlog([a]),{log,sub,[a,b]}), + ignore = logger_filters:domain(?dlog([a]),{stop,sub,[a,b]}), + ignore = logger_filters:domain(?dlog([a]),{log,equal,[a,b]}), + ignore = logger_filters:domain(?dlog([a]),{stop,equal,[a,b]}), + L5 = logger_filters:domain(L5=?dlog([a]),{log,not_equal,[a,b]}), + stop = logger_filters:domain(?dlog([a]),{stop,not_equal,[a,b]}), + ignore = logger_filters:domain(?dlog([a]),{log,undefined,[a,b]}), + ignore = logger_filters:domain(?dlog([a]),{stop,undefined,[a,b]}), + + ignore = logger_filters:domain(?dlog([a,b]),{log,super,[a]}), + ignore = logger_filters:domain(?dlog([a,b]),{stop,super,[a]}), + L6 = logger_filters:domain(L6=?dlog([a,b]),{log,sub,[a]}), + stop = logger_filters:domain(?dlog([a,b]),{stop,sub,[a]}), + ignore = logger_filters:domain(?dlog([a,b]),{log,equal,[a]}), + ignore = logger_filters:domain(?dlog([a,b]),{stop,equal,[a]}), + L7 = logger_filters:domain(L7=?dlog([a,b]),{log,not_equal,[a]}), + stop = logger_filters:domain(?dlog([a,b]),{stop,not_equal,[a]}), + ignore = logger_filters:domain(?dlog([a,b]),{log,undefined,[a]}), + ignore = logger_filters:domain(?dlog([a,b]),{stop,undefined,[a]}), + + ignore = logger_filters:domain(?ndlog,{log,super,[a]}), + ignore = logger_filters:domain(?ndlog,{stop,super,[a]}), + ignore = logger_filters:domain(?ndlog,{log,sub,[a]}), + ignore = logger_filters:domain(?ndlog,{stop,sub,[a]}), + ignore = logger_filters:domain(?ndlog,{log,equal,[a]}), + ignore = logger_filters:domain(?ndlog,{stop,equal,[a]}), + L8 = logger_filters:domain(L8=?ndlog,{log,not_equal,[a]}), + stop = logger_filters:domain(?ndlog,{stop,not_equal,[a]}), + L9 = logger_filters:domain(L9=?ndlog,{log,undefined,[a]}), + stop = logger_filters:domain(?ndlog,{stop,undefined,[a]}), + + L10 = logger_filters:domain(L10=?dlog([a,b,c,d]),{log,super,[a,b,c,d]}), + stop = logger_filters:domain(?dlog([a,b,c,d]),{stop,super,[a,b,c,d]}), + L11 = logger_filters:domain(L11=?dlog([a,b,c,d]),{log,sub,[a,b,c,d]}), + stop = logger_filters:domain(?dlog([a,b,c,d]),{stop,sub,[a,b,c,d]}), + L12 = logger_filters:domain(L12=?dlog([a,b,c,d]),{log,equal,[a,b,c,d]}), + stop = logger_filters:domain(?dlog([a,b,c,d]),{stop,equal,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog([a,b,c,d]),{log,not_equal,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog([a,b,c,d]),{stop,not_equal,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog([a,b,c,d]),{log,undefined,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog([a,b,c,d]),{stop,undefined,[a,b,c,d]}), %% A domain field in meta which is not a list is allowed by the %% filter, but since MatchDomain is always a list of atoms, only - %% Action=differs can ever match. - ignore = logger_filters:domain(?dlog(dummy),{log,prefix_of,[a,b,c,d]}), - ignore = logger_filters:domain(?dlog(dummy),{stop,prefix_of,[a,b,c,d]}), - ignore = logger_filters:domain(?dlog(dummy),{log,starts_with,[a,b,c,d]}), - ignore = logger_filters:domain(?dlog(dummy),{stop,starts_with,[a,b,c,d]}), - ignore = logger_filters:domain(?dlog(dummy),{log,equals,[a,b,c,d]}), - ignore = logger_filters:domain(?dlog(dummy),{stop,equals,[a,b,c,d]}), - L13 = logger_filters:domain(L13=?dlog(dummy),{log,differs,[a,b,c,d]}), - stop = logger_filters:domain(?dlog(dummy),{stop,differs,[a,b,c,d]}), - ignore = logger_filters:domain(?dlog(dummy),{log,no_domain,[a,b,c,d]}), - ignore = logger_filters:domain(?dlog(dummy),{stop,no_domain,[a,b,c,d]}), + %% Action=not_equal can ever match. + ignore = logger_filters:domain(?dlog(dummy),{log,super,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{stop,super,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{log,sub,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{stop,sub,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{log,equal,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{stop,equal,[a,b,c,d]}), + L13 = logger_filters:domain(L13=?dlog(dummy),{log,not_equal,[a,b,c,d]}), + stop = logger_filters:domain(?dlog(dummy),{stop,not_equal,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{log,undefined,[a,b,c,d]}), + ignore = logger_filters:domain(?dlog(dummy),{stop,undefined,[a,b,c,d]}), {error,badarg} = ?TRY(logger_filters:domain(?ndlog,bad)), - {error,badarg} = ?TRY(logger_filters:domain(?ndlog,{bad,prefix_of,[]})), + {error,badarg} = ?TRY(logger_filters:domain(?ndlog,{bad,super,[]})), {error,badarg} = ?TRY(logger_filters:domain(?ndlog,{log,bad,[]})), - {error,badarg} = ?TRY(logger_filters:domain(?ndlog,{log,prefix_of,bad})), + {error,badarg} = ?TRY(logger_filters:domain(?ndlog,{log,super,bad})), ok. diff --git a/lib/kernel/test/logger_legacy_SUITE.erl b/lib/kernel/test/logger_legacy_SUITE.erl index b59f5f7758..cfba35e43f 100644 --- a/lib/kernel/test/logger_legacy_SUITE.erl +++ b/lib/kernel/test/logger_legacy_SUITE.erl @@ -68,13 +68,13 @@ init_per_group(std, Config) -> ok = logger:set_handler_config( error_logger,filters, [{domain,{fun logger_filters:domain/2, - {log,prefix_of,[beam,erlang,otp]}}}]), + {log,super,[beam,erlang,otp]}}}]), Config; init_per_group(sasl, Config) -> ok = logger:set_handler_config( error_logger,filters, [{domain,{fun logger_filters:domain/2, - {log,prefix_of,[beam,erlang,otp,sasl]}}}]), + {log,super,[beam,erlang,otp,sasl]}}}]), %% cth_log_redirect checks if sasl is started before displaying %% any sasl reports - so just to see the real sasl reports in tc -- cgit v1.2.3 From ca1bf7a9d915a973203aa046bb9f921c11681ee0 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 21 May 2018 16:52:23 +0200 Subject: Change handler id for sasl handler from sasl_h to sasl --- lib/kernel/doc/src/kernel_app.xml | 2 +- lib/kernel/doc/src/logger_chapter.xml | 2 +- lib/kernel/test/logger_env_var_SUITE.erl | 40 ++++++++++++++++---------------- 3 files changed, 22 insertions(+), 22 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index 5bc423afb6..a9292e7660 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -192,7 +192,7 @@ 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 sasl_h, according to values of the SASL + named sasl, according to values of the SASL configuration parameter sasl_error_logger and sasl_errlog_type, see sasl(6) diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 34c65be47a..1195808160 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -630,7 +630,7 @@ configuration parameters can then be used as before, and the SASL reports will only be printed if the SASL application is running, through a second log handler - named sasl_h.

    + named sasl.

    All SASL reports have a metadata field domain=>[beam,erlang,otp,sasl], which can be used, for example, by filters to stop or allow the diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl index 081d4f0a80..6a8a868cc2 100644 --- a/lib/kernel/test/logger_env_var_SUITE.erl +++ b/lib/kernel/test/logger_env_var_SUITE.erl @@ -86,7 +86,7 @@ default(Config) -> 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), + false = lists:keymember(sasl,1,Hs), ok. default_sasl_compatible(Config) -> @@ -99,7 +99,7 @@ default_sasl_compatible(Config) -> 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 = lists:keymember(sasl,1,Hs), ok. error_logger_tty(Config) -> @@ -111,7 +111,7 @@ error_logger_tty(Config) -> 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), + false = lists:keymember(sasl,1,Hs), ok. error_logger_tty_sasl_compatible(Config) -> @@ -125,7 +125,7 @@ error_logger_tty_sasl_compatible(Config) -> 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 = lists:keymember(sasl,1,Hs), ok. error_logger_false(Config) -> @@ -141,7 +141,7 @@ error_logger_false(Config) -> {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), true = lists:keymember(stop_progress,1,SimpleFilters), - false = lists:keymember(sasl_h,1,Hs), + false = lists:keymember(sasl,1,Hs), ok. error_logger_false_progress(Config) -> @@ -158,7 +158,7 @@ error_logger_false_progress(Config) -> {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), false = lists:keymember(stop_progress,1,SimpleFilters), - false = lists:keymember(sasl_h,1,Hs), + false = lists:keymember(sasl,1,Hs), ok. error_logger_false_sasl_compatible(Config) -> @@ -175,7 +175,7 @@ error_logger_false_sasl_compatible(Config) -> {domain,{_,{log,super,[beam,erlang,otp]}}} = lists:keyfind(domain,1,SimpleFilters), false = lists:keymember(stop_progress,1,SimpleFilters), - true = lists:keymember(sasl_h,1,Hs), + true = lists:keymember(sasl,1,Hs), ok. error_logger_silent(Config) -> @@ -183,7 +183,7 @@ error_logger_silent(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), + false = lists:keymember(sasl,1,Hs), ok. error_logger_silent_sasl_compatible(Config) -> @@ -192,7 +192,7 @@ error_logger_silent_sasl_compatible(Config) -> {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), + true = lists:keymember(sasl,1,Hs), ok. @@ -224,7 +224,7 @@ logger_file(Config) -> 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), + false = lists:keymember(sasl,1,Hs), ok. @@ -247,7 +247,7 @@ logger_file_sasl_compatible(Config) -> 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 = lists:keymember(sasl,1,Hs), ok. @@ -270,7 +270,7 @@ logger_file_log_progress(Config) -> 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), + false = lists:keymember(sasl,1,Hs), ok. @@ -290,7 +290,7 @@ logger_file_no_filter(Config) -> info = maps:get(level,StdC), [] = maps:get(filters,StdC), false = lists:keymember(logger_simple,1,Hs), - false = lists:keymember(sasl_h,1,Hs), + false = lists:keymember(sasl,1,Hs), ok. @@ -311,7 +311,7 @@ logger_file_no_filter_level(Config) -> error = maps:get(level,StdC), [] = maps:get(filters,StdC), false = lists:keymember(logger_simple,1,Hs), - false = lists:keymember(sasl_h,1,Hs), + false = lists:keymember(sasl,1,Hs), ok. @@ -332,7 +332,7 @@ logger_file_formatter(Config) -> info = maps:get(level,StdC), [] = maps:get(filters,StdC), false = lists:keymember(logger_simple,1,Hs), - false = lists:keymember(sasl_h,1,Hs), + false = lists:keymember(sasl,1,Hs), ok. @@ -357,7 +357,7 @@ logger_filters(Config) -> 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), + false = lists:keymember(sasl,1,Hs), LoggerFilters = maps:get(filters,Logger), true = lists:keymember(stop_progress,1,LoggerFilters), @@ -410,7 +410,7 @@ logger_module_level(Config) -> 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), + false = lists:keymember(sasl,1,Hs), [{supervisor,error}] = ModuleLevels, ok. @@ -432,7 +432,7 @@ logger_disk_log(Config) -> 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), + false = lists:keymember(sasl,1,Hs), ok. @@ -453,7 +453,7 @@ logger_disk_log_formatter(Config) -> info = maps:get(level,StdC), [] = maps:get(filters,StdC), false = lists:keymember(logger_simple,1,Hs), - false = lists:keymember(sasl_h,1,Hs), + false = lists:keymember(sasl,1,Hs), ok. @@ -468,7 +468,7 @@ logger_undefined(Config) -> {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), true = lists:keymember(stop_progress,1,SimpleFilters), - false = lists:keymember(sasl_h,1,Hs), + false = lists:keymember(sasl,1,Hs), ok. -- cgit v1.2.3 From f38163aa64547e09f99e362edefeda713e06ddb7 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 21 May 2018 17:45:21 +0200 Subject: Remove HandlerId from handler callback functions and add it to Config --- lib/kernel/doc/src/logger.xml | 23 +++++++++++++---------- lib/kernel/doc/src/logger_chapter.xml | 26 +++++++++++++------------- lib/kernel/src/error_logger.erl | 10 +++++----- lib/kernel/src/logger.erl | 3 ++- lib/kernel/src/logger_disk_log_h.erl | 25 +++++++++++-------------- lib/kernel/src/logger_server.erl | 13 +++++++------ lib/kernel/src/logger_simple.erl | 6 +++--- lib/kernel/src/logger_std_h.erl | 25 +++++++++++-------------- lib/kernel/test/logger_SUITE.erl | 12 ++++++------ 9 files changed, 71 insertions(+), 72 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index 6e50473fa8..d6576db6f3 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -881,11 +881,9 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)). - HModule:adding_handler(HandlerId,Config1) -> {ok,Config2} | {error,Reason} + HModule:adding_handler(Config1) -> {ok,Config2} | {error,Reason} An instance of this handler is about to be added. - HandlerId = - handler_id() Config1 = Config2 = config() Reason = term() @@ -895,6 +893,8 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)).

    The function is called when an new handler is about to be added, and the purpose is to verify the configuration and initiate all resources needed by the handler.

    +

    The handler identity is associated with the id key + in Config1.

    If everything succeeds, the callback function can add possible default values or internal state values to the configuration, and return the adjusted map @@ -905,11 +905,9 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)). - HModule:changing_config(HandlerId,Config1,Config2) -> {ok,Config3} | {error,Reason} + HModule:changing_config(Config1,Config2) -> {ok,Config3} | {error,Reason} The configuration for this handler is about to change. - HandlerId = - handler_id() Config1 = Config2 = Config3 = config() Reason = term() @@ -921,6 +919,8 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)). the new configuration.

    Config1 is the existing configuration and Config2 is the new configuration.

    +

    The handler identity is associated with the id key + in Config1.

    If everything succeeds, the callback function must return a possibly adjusted configuration in {ok,Config3}.

    If the configuration is faulty, the callback function must @@ -942,6 +942,8 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)).

    The function is called when all global filters and all handler filters for the handler in question have passed for the given log event.

    +

    The handler identity is associated with the id key + in Config.

    The handler must log the event.

    The return value from this function is ignored by Logger.

    @@ -949,11 +951,9 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)).
    - HModule:removing_handler(HandlerId,Config) -> ok + HModule:removing_handler(Config) -> ok The given handler is about to be removed. - HandlerId = - handler_id() Config = config() @@ -961,7 +961,10 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)).

    This callback function is optional.

    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.

    + the handler.

    +

    The handler identity is associated with the id key + in Config.

    +

    The return value is ignored by Logger.

    diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 1195808160..78c595d521 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -268,7 +268,7 @@

    In addition to the mandatory callback function log/2, a handler module can export the optional callback - functions adding_handler/2, changing_config/3 + functions adding_handler/1, changing_config/2 and removing_handler/1. See section Handler Callback Functions in the logger(3) manual for more @@ -738,22 +738,22 @@ ok log(logger:log(),logger:config()) ->ok

    It may also implement the following callbacks:

    -adding_handler(logger:handler_id(),logger:config()) -> {ok,logger:config()} | {error,term()} -removing_handler(logger:handler_id(),logger:config()) -> ok -changing_config(logger:handler_id(),logger:config(),logger:config()) -> {ok,logger:config()} | {error,term()} +adding_handler(logger:config()) -> {ok,logger:config()} | {error,term()} +removing_handler(logger:config()) -> ok +changing_config(logger:config(),logger:config()) -> {ok,logger:config()} | {error,term()}

    When logger:add_handler(Id,Module,Config) is called, Logger - will first call HModule:adding_handler(Id,Config), and if it + will first call HModule:adding_handler(Config), and if it returns {ok,NewConfig}, NewConfig is written to the configuration database. After this, the handler may receive log events as calls to HModule:log/2.

    A handler can be removed by calling logger:remove_handler(Id). Logger will call - HModule:removing_handler(Id,Config), and then remove the + HModule:removing_handler(Config), and then remove the handler's configuration from the configuration database.

    When logger:set_handler_config/2,3 or logger:update_handler_config/2 are called, Logger - calls HModule:changing_config(Id,OldConfig,NewConfig). If + calls HModule:changing_config(OldConfig,NewConfig). If this function returns {ok,NewConfig}, NewConfig is written to the configuration database.

    @@ -771,14 +771,14 @@ log(Log,#{formatter:={FModule,FConfig}) -> this:

    -module(myhandler). --export([adding_handler/2, removing_handler/2, log/2]). +-export([adding_handler/1, removing_handler/1, log/2]). -export([init/1, handle_call/3, handle_cast/2, terminate/2]). -adding_handler(Id,Config) -> +adding_handler(Config) -> {ok,Fd} = file:open(File,[append,{encoding,utf8}]), {ok,Config#{myhandler_fd=>Fd}}. -removing_handler(Id,#{myhandler_fd:=Fd}) -> +removing_handler(#{myhandler_fd:=Fd}) -> _ = file:close(Fd), ok. @@ -800,14 +800,14 @@ log(Log,#{myhandler_fd:=Fd,formatter:={FModule,FConfig}}) -> single process.

    -module(myhandler). --export([adding_handler/2, removing_handler/2, log/2]). +-export([adding_handler/1, removing_handler/1, log/2]). -export([init/1, handle_call/3, handle_cast/2, terminate/2]). -adding_handler(Id,Config) -> +adding_handler(Config) -> {ok,Pid} = gen_server:start(?MODULE,Config), {ok,Config#{myhandler_pid=>Pid}}. -removing_handler(Id,#{myhandler_pid:=Pid}) -> +removing_handler(#{myhandler_pid:=Pid}) -> gen_server:stop(Pid). log(Log,#{myhandler_pid:=Pid} = Config) -> diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index 6c3b308308..96f6536e61 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/2, log/2]). +-export([adding_handler/1, removing_handler/1, log/2]). -export([get_format_depth/0, limit_term/1]). @@ -101,9 +101,9 @@ stop() -> %%%----------------------------------------------------------------- %%% Callbacks for logger --spec adding_handler(logger:handler_id(),logger:config()) -> +-spec adding_handler(logger:config()) -> {ok,logger:config()} | {error,term()}. -adding_handler(?MODULE,Config) -> +adding_handler(#{id:=?MODULE}=Config) -> case start() of ok -> {ok,Config}; @@ -111,8 +111,8 @@ adding_handler(?MODULE,Config) -> Error end. --spec removing_handler(logger:handler_id(),logger:config()) -> ok. -removing_handler(?MODULE,_Config) -> +-spec removing_handler(logger:config()) -> ok. +removing_handler(#{id:=?MODULE}) -> stop(), ok. diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 9c721d7fee..f22fd5e8de 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -90,7 +90,8 @@ -type filter() :: {fun((log(),filter_arg()) -> filter_return()),filter_arg()}. -type filter_arg() :: term(). -type filter_return() :: stop | ignore | log(). --type config() :: #{level => level(), +-type config() :: #{id => handler_id(), + level => level(), filter_default => log | stop, filters => [{filter_id(),filter()}], formatter => {module(),formatter_config()}, diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index 57c54ce27e..37a147e3dc 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -34,8 +34,8 @@ %% logger callbacks -export([log/2, - adding_handler/2, removing_handler/2, - changing_config/3, swap_buffer/2]). + adding_handler/1, removing_handler/1, + changing_config/2, swap_buffer/2]). %%%=================================================================== %%% API @@ -108,8 +108,8 @@ reset(Name) -> %%%----------------------------------------------------------------- %%% Handler being added -adding_handler(Name, Config) -> - case check_config(adding, Name, Config) of +adding_handler(#{id:=Name}=Config) -> + case check_config(adding, Config) of {ok, Config1} -> %% create initial handler state by merging defaults with config HConfig = maps:get(?MODULE, Config1, #{}), @@ -136,10 +136,9 @@ adding_handler(Name, Config) -> %%%----------------------------------------------------------------- %%% Updating handler config -changing_config(Name, - OldConfig=#{id:=Id, disk_log_opts:=DLOpts}, - NewConfig=#{id:=Id, disk_log_opts:=DLOpts}) -> - case check_config(changing, Name, NewConfig) of +changing_config(OldConfig=#{id:=Name, disk_log_opts:=DLOpts}, + NewConfig=#{id:=Name, disk_log_opts:=DLOpts}) -> + case check_config(changing, NewConfig) of Result = {ok,NewConfig1} -> try gen_server:call(Name, {change_config,OldConfig,NewConfig1}, ?DEFAULT_CALL_TIMEOUT) of @@ -151,12 +150,10 @@ changing_config(Name, Error -> Error end; -changing_config(_Name, OldConfig, NewConfig) -> +changing_config(OldConfig, NewConfig) -> {error,{illegal_config_change,OldConfig,NewConfig}}. -check_config(adding, Name, Config0) -> - %% Merge in defaults on top level - Config = maps:merge(#{id => Name}, Config0), +check_config(adding, #{id:=Name}=Config) -> %% Merge in defaults on handler level LogOpts0 = maps:get(disk_log_opts, Config, #{}), LogOpts = merge_default_logopts(Name, LogOpts0), @@ -173,7 +170,7 @@ check_config(adding, Name, Config0) -> Error -> Error end; -check_config(changing, _Name, Config) -> +check_config(changing, Config) -> MyConfig = maps:get(?MODULE, Config, #{}), case check_my_config(maps:to_list(MyConfig)) of ok -> {ok,Config}; @@ -223,7 +220,7 @@ check_my_config([]) -> %%%----------------------------------------------------------------- %%% Handler being removed -removing_handler(Name, _Config) -> +removing_handler(#{id:=Name}) -> stop(Name). %%%----------------------------------------------------------------- diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index 16f61abdd6..9182b0b486 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -134,7 +134,7 @@ init([]) -> filters=>?DEFAULT_HANDLER_FILTERS}), %% If this fails, then the node should crash {ok,SimpleConfig} = - logger_simple:adding_handler(logger_simple,SimpleConfig0), + logger_simple:adding_handler(SimpleConfig0), logger_config:create(Tid,logger_simple,logger_simple,SimpleConfig), {ok, #state{tid=Tid, async_req_queue = queue:new()}}. @@ -146,7 +146,7 @@ handle_call({add_handler,Id,Module,HConfig}, From, #state{tid=Tid}=State) -> call_h_async( fun() -> %% inform the handler - call_h(Module,adding_handler,[Id,HConfig],{ok,HConfig}) + call_h(Module,adding_handler,[HConfig],{ok,HConfig}) end, fun({ok,HConfig1}) -> %% We know that the call_h would have loaded the module @@ -177,7 +177,7 @@ handle_call({remove_handler,HandlerId}, From, #state{tid=Tid}=State) -> call_h_async( fun() -> %% inform the handler - call_h(Module,removing_handler,[HandlerId,HConfig],ok) + call_h(Module,removing_handler,[HConfig],ok) end, fun(_Res) -> do_set_config(Tid,logger,Config#{handlers=>Handlers}), @@ -213,7 +213,7 @@ handle_call({set_config,HandlerId,Config}, From, #state{tid=Tid}=State) -> {ok,{Module,OldConfig}} -> call_h_async( fun() -> - call_h(Module,changing_config,[HandlerId,OldConfig,Config], + call_h(Module,changing_config,[OldConfig,Config], {ok,Config}) end, fun({ok,Config1}) -> @@ -348,8 +348,9 @@ default_config(logger) -> #{level=>info, filters=>[], filter_default=>log}; -default_config(_) -> - #{level=>info, +default_config(Id) -> + #{id=>Id, + level=>info, filters=>[], filter_default=>log, formatter=>{?DEFAULT_FORMATTER,#{}}}. diff --git a/lib/kernel/src/logger_simple.erl b/lib/kernel/src/logger_simple.erl index c8cdf25887..cfb3fcb620 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/2, log/2]). +-export([adding_handler/1, removing_handler/1, log/2]). %% This module implements a simple handler for logger. It is the %% default used during system start. @@ -27,7 +27,7 @@ %%%----------------------------------------------------------------- %%% Logger callback -adding_handler(?MODULE,Config) -> +adding_handler(#{id:=?MODULE}=Config) -> Me = self(), case whereis(?MODULE) of undefined -> @@ -44,7 +44,7 @@ adding_handler(?MODULE,Config) -> {error,{handler_process_name_already_exists,?MODULE}} end. -removing_handler(?MODULE,_Config) -> +removing_handler(#{id:=?MODULE}) -> 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 e5e0febc88..fc8ca1535b 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -35,8 +35,8 @@ terminate/2, code_change/3]). %% logger callbacks --export([log/2, adding_handler/2, removing_handler/2, - changing_config/3, swap_buffer/2]). +-export([log/2, adding_handler/1, removing_handler/1, + changing_config/2, swap_buffer/2]). %%%=================================================================== %%% API @@ -109,8 +109,8 @@ reset(Name) -> %%%----------------------------------------------------------------- %%% Handler being added -adding_handler(Name, Config) -> - case check_config(adding, Name, Config) of +adding_handler(#{id:=Name}=Config) -> + case check_config(adding, Config) of {ok, Config1} -> %% create initial handler state by merging defaults with config HConfig = maps:get(?MODULE, Config1, #{}), @@ -137,9 +137,8 @@ adding_handler(Name, Config) -> %%%----------------------------------------------------------------- %%% Updating handler config -changing_config(Name, - OldConfig=#{id:=Id, ?MODULE:=#{type:=Type}}, - NewConfig=#{id:=Id}) -> +changing_config(OldConfig=#{id:=Name, ?MODULE:=#{type:=Type}}, + NewConfig=#{id:=Name}) -> MyConfig = maps:get(?MODULE, NewConfig, #{}), case maps:get(type, MyConfig, Type) of Type -> @@ -149,11 +148,11 @@ changing_config(Name, _ -> {error,{illegal_config_change,OldConfig,NewConfig}} end; -changing_config(_Name, OldConfig, NewConfig) -> +changing_config(OldConfig, NewConfig) -> {error,{illegal_config_change,OldConfig,NewConfig}}. changing_config1(Name, OldConfig, NewConfig) -> - case check_config(changing, Name, NewConfig) of + case check_config(changing, NewConfig) of Result = {ok,NewConfig1} -> try gen_server:call(Name, {change_config,OldConfig,NewConfig1}, ?DEFAULT_CALL_TIMEOUT) of @@ -166,9 +165,7 @@ changing_config1(Name, OldConfig, NewConfig) -> Error end. -check_config(adding, Name, Config0) -> - %% Merge in defaults on top level - Config = maps:merge(#{id => Name}, Config0), +check_config(adding, Config) -> %% Merge in defaults on handler level MyConfig0 = maps:get(?MODULE, Config, #{}), MyConfig = maps:merge(#{type => standard_io}, @@ -179,7 +176,7 @@ check_config(adding, Name, Config0) -> Error -> Error end; -check_config(changing, _Name, Config) -> +check_config(changing, Config) -> MyConfig = maps:get(?MODULE, Config, #{}), case check_my_config(maps:to_list(MyConfig)) of ok -> {ok,Config}; @@ -207,7 +204,7 @@ check_my_config([]) -> %%%----------------------------------------------------------------- %%% Handler being removed -removing_handler(Name,_Config) -> +removing_handler(#{id:=Name}) -> stop(Name). %%%----------------------------------------------------------------- diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index 66eee8219a..f7ec59a7b7 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -839,20 +839,20 @@ check_maps(Expected,Got,What) -> end. %% Handler -adding_handler(_Id,#{add_call:=Fun}) -> +adding_handler(#{add_call:=Fun}) -> Fun(); -adding_handler(_Id,Config) -> +adding_handler(Config) -> maybe_send(add), {ok,Config}. -removing_handler(_Id,#{rem_call:=Fun}) -> +removing_handler(#{rem_call:=Fun}) -> Fun(); -removing_handler(_Id,_Config) -> +removing_handler(_Config) -> maybe_send(remove), ok. -changing_config(_Id,_Old,#{conf_call:=Fun}) -> +changing_config(_Old,#{conf_call:=Fun}) -> Fun(); -changing_config(_Id,_Old,Config) -> +changing_config(_Old,Config) -> maybe_send(changing_config), {ok,Config}. -- cgit v1.2.3 From 51c6464a011eacd78340b60e2641eaecc36933f6 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 21 May 2018 17:59:10 +0200 Subject: Change env var logger_log_progress to logger_progress_reports --- lib/kernel/doc/src/kernel_app.xml | 10 +++++----- lib/kernel/doc/src/logger_chapter.xml | 8 ++++---- lib/kernel/src/kernel.app.src | 2 +- lib/kernel/src/logger.erl | 6 +++--- lib/kernel/test/logger_env_var_SUITE.erl | 16 ++++++++-------- 5 files changed, 21 insertions(+), 21 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index a9292e7660..0f7e95472b 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -204,17 +204,17 @@

    This configuration option only effects the default and sasl handler. Any other handlers are uneffected.

    - - logger_log_progress = boolean() + + logger_progress_reports = stop | log

    If logger_sasl_compatible = false, - then logger_log_progress specifies if progress + then logger_progress_reports specifies if progress reports from supervisor and application_controller shall be logged by the default logger.

    If logger_sasl_compatible = true, - then logger_log_progress is ignored.

    -

    The default value is false

    + then logger_progress_reports is ignored.

    +

    The default value is stop

    This configuration option only effects the default and sasl handler. Any other handlers are uneffected.

    diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 78c595d521..dff9ee2892 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -344,8 +344,8 @@ logger, logger_level, logger_sasl_compatible and - logger_log_progress. - logger_level, logger_sasl_compatible and logger_log_progress are described in the + logger_progress_reports. + logger_level, logger_sasl_compatible and logger_progress_reports are described in the Kernel Configuration, while logger is described below.

    @@ -617,8 +617,8 @@ default. Progress reports are not logged by default, but can be enabled with the Kernel configuration - parameter - logger_log_progress. + parameter + logger_progress_reports. The output format is the same for all log events. diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 23ac5b3444..46fb3e09d5 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -142,7 +142,7 @@ {applications, []}, {env, [{logger_level, info}, {logger_sasl_compatible, false}, - {logger_log_progress, false} + {logger_progress_reports, stop} ]}, {mod, {kernel, []}}, {runtime_dependencies, ["erts-10.0", "stdlib-3.5", "sasl-3.0"]} diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index f22fd5e8de..b32fca1fda 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -731,10 +731,10 @@ get_default_handler_filters() -> ?DEFAULT_HANDLER_FILTERS([beam,erlang,otp]); false -> Extra = - case application:get_env(kernel, logger_log_progress, false) of - true -> + case application:get_env(kernel, logger_progress_reports, stop) of + log -> []; - false -> + stop -> [{stop_progress, {fun logger_filters:progress/2,stop}}] end, diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl index 6a8a868cc2..a808a31f17 100644 --- a/lib/kernel/test/logger_env_var_SUITE.erl +++ b/lib/kernel/test/logger_env_var_SUITE.erl @@ -149,7 +149,7 @@ error_logger_false_progress(Config) -> setup(Config, [{error_logger,false}, {logger_level,notice}, - {logger_log_progress,true}]), + {logger_progress_reports,log}]), false = lists:keymember(?STANDARD_HANDLER,1,Hs), {logger_simple,logger_simple,SimpleC} = lists:keyfind(logger_simple,1,Hs), info = maps:get(level,SimpleC), @@ -255,7 +255,7 @@ logger_file_log_progress(Config) -> Log = file(Config,?FUNCTION_NAME), {ok,#{handlers:=Hs},Node} = setup(Config, - [{logger_log_progress,true}, + [{logger_progress_reports,log}, {logger, [{handler,?STANDARD_HANDLER,logger_std_h, #{logger_std_h=>#{type=>{file,Log}}}}]}]), @@ -340,7 +340,7 @@ logger_filters(Config) -> Log = file(Config,?FUNCTION_NAME), {ok,#{handlers:=Hs,logger:=Logger},Node} = setup(Config, - [{logger_log_progress,true}, + [{logger_progress_reports,log}, {logger, [{handler,?STANDARD_HANDLER,logger_std_h, #{logger_std_h=>#{type=>{file,Log}}}}, @@ -367,7 +367,7 @@ logger_filters_stop(Config) -> Log = file(Config,?FUNCTION_NAME), {ok,#{handlers:=Hs,logger:=Logger},Node} = setup(Config, - [{logger_log_progress,true}, + [{logger_progress_reports,log}, {logger, [{handler,?STANDARD_HANDLER,logger_std_h, #{filters=>[], @@ -393,7 +393,7 @@ logger_module_level(Config) -> Log = file(Config,?FUNCTION_NAME), {ok,#{handlers:=Hs,module_levels:=ModuleLevels},Node} = setup(Config, - [{logger_log_progress,true}, + [{logger_progress_reports,log}, {logger, [{handler,?STANDARD_HANDLER,logger_std_h, #{logger_std_h=>#{type=>{file,Log}}}}, @@ -552,7 +552,7 @@ sasl_compatible_false(Config) -> {ok,_Hs,Node} = setup(Config, [{error_logger,{file,Log}}, {logger_sasl_compatible,false}, - {logger_log_progress,true}]), + {logger_progress_reports,log}]), check_default_log(Node,Log, file,% dest 6),% progress in std logger @@ -563,7 +563,7 @@ sasl_compatible_false_no_progress(Config) -> {ok,_Hs,Node} = setup(Config, [{error_logger,{file,Log}}, {logger_sasl_compatible,false}, - {logger_log_progress,false}]), + {logger_progress_reports,stop}]), check_default_log(Node,Log, file,% dest 0),% progress in std logger @@ -589,7 +589,7 @@ bad_sasl_compatibility(Config) -> error = setup(Config,[{logger_sasl_compatible,badcomp}]). bad_progress(Config) -> - error = setup(Config,[{logger_log_progress,badprogress}]). + error = setup(Config,[{logger_progress_reports,badprogress}]). %%%----------------------------------------------------------------- %%% Internal -- cgit v1.2.3 From 530d3cf99c562e3678eeb08e135ae90c4df4e052 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 17 May 2018 19:45:50 +0200 Subject: Rename module logger_simple to logger_simple_h Also, change HandlerId from logger_simple to simple. --- lib/kernel/src/Makefile | 4 +- lib/kernel/src/kernel.app.src | 2 +- lib/kernel/src/logger.erl | 10 +- lib/kernel/src/logger_server.erl | 9 +- lib/kernel/src/logger_simple.erl | 212 ------------------------------ lib/kernel/src/logger_simple_h.erl | 212 ++++++++++++++++++++++++++++++ lib/kernel/test/Makefile | 2 +- lib/kernel/test/logger.cover | 2 +- lib/kernel/test/logger.spec | 2 +- lib/kernel/test/logger_env_var_SUITE.erl | 44 +++---- lib/kernel/test/logger_simple_SUITE.erl | 210 ----------------------------- lib/kernel/test/logger_simple_h_SUITE.erl | 210 +++++++++++++++++++++++++++++ 12 files changed, 459 insertions(+), 460 deletions(-) delete mode 100644 lib/kernel/src/logger_simple.erl create mode 100644 lib/kernel/src/logger_simple_h.erl delete mode 100644 lib/kernel/test/logger_simple_SUITE.erl create mode 100644 lib/kernel/test/logger_simple_h_SUITE.erl (limited to 'lib/kernel') diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile index eeb8c6ab2f..f265fdd272 100644 --- a/lib/kernel/src/Makefile +++ b/lib/kernel/src/Makefile @@ -118,7 +118,7 @@ MODULES = \ logger_filters \ logger_formatter \ logger_server \ - logger_simple \ + logger_simple_h \ logger_sup \ net \ net_adm \ @@ -279,7 +279,7 @@ $(EBIN)/logger_disk_log_h.beam: logger_h_common.hrl logger_internal.hrl ../inclu $(EBIN)/logger_filters.beam: logger_internal.hrl ../include/logger.hrl $(EBIN)/logger_formatter.beam: logger_internal.hrl ../include/logger.hrl $(EBIN)/logger_server.beam: logger_internal.hrl ../include/logger.hrl -$(EBIN)/logger_simple.beam: logger_internal.hrl ../include/logger.hrl +$(EBIN)/logger_simple_h.beam: logger_internal.hrl ../include/logger.hrl $(EBIN)/logger_std_h.beam: logger_h_common.hrl logger_internal.hrl ../include/logger.hrl ../include/file.hrl $(EBIN)/logger_h_common.beam: logger_h_common.hrl logger_internal.hrl ../include/logger.hrl $(EBIN)/net_kernel.beam: ../include/net_address.hrl diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 46fb3e09d5..d873178f55 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -68,7 +68,7 @@ logger_formatter, logger_h_common, logger_server, - logger_simple, + logger_simple_h, logger_std_h, logger_sup, net, diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index b32fca1fda..f35036aa04 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -561,10 +561,10 @@ internal_init_logger() -> end || Module <- Modules] || {module_level, Level, Modules} <- get_logger_env()], - case logger:set_handler_config(logger_simple,filters, + case logger:set_handler_config(simple,filters, get_default_handler_filters()) of ok -> ok; - {error,{not_found,logger_simple}} -> ok + {error,{not_found,simple}} -> ok end, init_kernel_handlers() @@ -580,7 +580,7 @@ init_kernel_handlers() -> try case get_logger_type() of {ok,silent} -> - ok = logger:remove_handler(logger_simple); + ok = logger:remove_handler(simple); {ok,false} -> ok; {ok,Type} -> @@ -621,9 +621,9 @@ add_handlers(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 + _ = [case logger:remove_handler(simple) of ok -> ok; - {error,{not_found,logger_simple}} -> ok + {error,{not_found,simple}} -> ok end || DefaultAdded], ok catch throw:Reason -> diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index 9182b0b486..47010c9fa5 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -127,15 +127,14 @@ init([]) -> process_flag(trap_exit, true), Tid = logger_config:new(?LOGGER_TABLE), LoggerConfig = maps:merge(default_config(logger), - #{handlers=>[logger_simple]}), + #{handlers=>[simple]}), logger_config:create(Tid,logger,LoggerConfig), - SimpleConfig0 = maps:merge(default_config(logger_simple), + SimpleConfig0 = maps:merge(default_config(simple), #{filter_default=>stop, filters=>?DEFAULT_HANDLER_FILTERS}), %% If this fails, then the node should crash - {ok,SimpleConfig} = - logger_simple:adding_handler(SimpleConfig0), - logger_config:create(Tid,logger_simple,logger_simple,SimpleConfig), + {ok,SimpleConfig} = logger_simple_h:adding_handler(SimpleConfig0), + logger_config:create(Tid,simple,logger_simple_h,SimpleConfig), {ok, #state{tid=Tid, async_req_queue = queue:new()}}. handle_call({add_handler,Id,Module,HConfig}, From, #state{tid=Tid}=State) -> diff --git a/lib/kernel/src/logger_simple.erl b/lib/kernel/src/logger_simple.erl deleted file mode 100644 index cfb3fcb620..0000000000 --- a/lib/kernel/src/logger_simple.erl +++ /dev/null @@ -1,212 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2017. 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_simple). - --export([adding_handler/1, removing_handler/1, log/2]). - -%% This module implements a simple handler for logger. It is the -%% default used during system start. - -%%%----------------------------------------------------------------- -%%% Logger callback - -adding_handler(#{id:=?MODULE}=Config) -> - Me = self(), - case whereis(?MODULE) of - undefined -> - {Pid,Ref} = spawn_opt(fun() -> init(Me) end, - [link,monitor,{message_queue_data,off_heap}]), - receive - {'DOWN',Ref,process,Pid,Reason} -> - {error,Reason}; - {Pid,started} -> - erlang:demonitor(Ref), - {ok,Config} - end; - _ -> - {error,{handler_process_name_already_exists,?MODULE}} - end. - -removing_handler(#{id:=?MODULE}) -> - case whereis(?MODULE) of - undefined -> - ok; - Pid -> - Ref = erlang:monitor(process,Pid), - unlink(Pid), - Pid ! stop, - receive {'DOWN',Ref,process,Pid,_} -> - ok - end - end. - -log(#{meta:=#{error_logger:=#{tag:=info_report,type:=Type}}},_Config) - when Type=/=std_info -> - %% Skip info reports that are not 'std_info' (ref simple logger in - %% error_logger) - ok; -log(#{msg:=_,meta:=#{time:=_}}=Log,_Config) -> - _ = case whereis(?MODULE) of - undefined -> - %% Is the node on the way down? Real emergency? - %% Log directly from client just to get it out - do_log( - #{level=>error, - msg=>{report,{error,simple_handler_process_dead}}, - meta=>#{time=>erlang:system_time(microsecond)}}), - do_log(Log); - _ -> - ?MODULE ! {log,Log} - end, - ok; -log(_,_) -> - %% Unexpected log. - %% We don't want to crash the simple logger, so ignore this. - ok. - -%%%----------------------------------------------------------------- -%%% Process -init(Starter) -> - register(?MODULE,self()), - Starter ! {self(),started}, - loop(#{buffer_size=>10,dropped=>0,buffer=>[]}). - -loop(Buffer) -> - receive - stop -> - %% 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)); - _ -> - %% Unexpected message - flush it! - loop(Buffer) - end. - -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) -> - [#{level=>info, - msg=>{"Simple handler buffer full, dropped ~w messages",[N]}, - meta=>#{time=>erlang:system_time(microsecond)}}]. - -%%%----------------------------------------------------------------- -%%% Internal - -%% Can't do io_lib:format - -do_log(#{msg:={report,Report}, - meta:=#{time:=T,error_logger:=#{type:=Type}}}) -> - display_date(T), - display_report(Type,Report); -do_log(#{msg:=Msg,meta:=#{time:=T}}) -> - display_date(T), - display(Msg). - -display_date(Timestamp) when is_integer(Timestamp) -> - Micro = Timestamp rem 1000000, - Sec = Timestamp div 1000000, - {{Y,Mo,D},{H,Mi,S}} = erlang:universaltime_to_localtime( - erlang:posixtime_to_universaltime(Sec)), - erlang:display_string( - integer_to_list(Y) ++ "-" ++ - pad(Mo,2) ++ "-" ++ - pad(D,2) ++ " " ++ - pad(H,2) ++ ":" ++ - pad(Mi,2) ++ ":" ++ - pad(S,2) ++ "." ++ - pad(Micro,6) ++ " "). - -pad(Int,Size) when is_integer(Int) -> - pad(integer_to_list(Int),Size); -pad(Str,Size) when length(Str)==Size -> - Str; -pad(Str,Size) -> - pad([$0|Str],Size). - -display({string,Chardata}) -> - try unicode:characters_to_list(Chardata) of - String -> erlang:display_string(String), erlang:display_string("\n") - catch _:_ -> erlang:display(Chardata) - end; -display({report,Report}) when is_map(Report) -> - display_report(maps:to_list(Report)); -display({report,Report}) -> - display_report(Report); -display({F, A}) when is_list(F), is_list(A) -> - erlang:display_string(F ++ "\n"), - [begin - erlang:display_string("\t"), - erlang:display(Arg) - end || Arg <- A], - ok. - -display_report(Atom, A) when is_atom(Atom) -> - %% The widest atom seems to be 'supervisor_report' at 17. - ColumnWidth = 20, - AtomString = atom_to_list(Atom), - AtomLength = length(AtomString), - Padding = lists:duplicate(ColumnWidth - AtomLength, $\s), - erlang:display_string(AtomString ++ Padding), - display_report(A); -display_report(F, A) -> - erlang:display({F, A}). - -display_report([A, []]) -> - %% Special case for crash reports when process has no links - display_report(A); -display_report(A = [_|_]) -> - case lists:all(fun({Key,_Value}) -> is_atom(Key); (_) -> false end, A) of - true -> - erlang:display_string("\n"), - lists:foreach( - fun({Key, Value}) -> - erlang:display_string( - " " ++ - atom_to_list(Key) ++ - ": "), - erlang:display(Value) - end, A); - false -> - erlang:display(A) - end; -display_report(A) -> - erlang:display(A). diff --git a/lib/kernel/src/logger_simple_h.erl b/lib/kernel/src/logger_simple_h.erl new file mode 100644 index 0000000000..19fb3b54ba --- /dev/null +++ b/lib/kernel/src/logger_simple_h.erl @@ -0,0 +1,212 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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_simple_h). + +-export([adding_handler/1, removing_handler/1, log/2]). + +%% This module implements a simple handler for logger. It is the +%% default used during system start. + +%%%----------------------------------------------------------------- +%%% Logger callback + +adding_handler(#{id:=simple}=Config) -> + Me = self(), + case whereis(?MODULE) of + undefined -> + {Pid,Ref} = spawn_opt(fun() -> init(Me) end, + [link,monitor,{message_queue_data,off_heap}]), + receive + {'DOWN',Ref,process,Pid,Reason} -> + {error,Reason}; + {Pid,started} -> + erlang:demonitor(Ref), + {ok,Config} + end; + _ -> + {error,{handler_process_name_already_exists,?MODULE}} + end. + +removing_handler(#{id:=simple}) -> + case whereis(?MODULE) of + undefined -> + ok; + Pid -> + Ref = erlang:monitor(process,Pid), + unlink(Pid), + Pid ! stop, + receive {'DOWN',Ref,process,Pid,_} -> + ok + end + end. + +log(#{meta:=#{error_logger:=#{tag:=info_report,type:=Type}}},_Config) + when Type=/=std_info -> + %% Skip info reports that are not 'std_info' (ref simple logger in + %% error_logger) + ok; +log(#{msg:=_,meta:=#{time:=_}}=Log,_Config) -> + _ = case whereis(?MODULE) of + undefined -> + %% Is the node on the way down? Real emergency? + %% Log directly from client just to get it out + do_log( + #{level=>error, + msg=>{report,{error,simple_handler_process_dead}}, + meta=>#{time=>erlang:system_time(microsecond)}}), + do_log(Log); + _ -> + ?MODULE ! {log,Log} + end, + ok; +log(_,_) -> + %% Unexpected log. + %% We don't want to crash the simple logger, so ignore this. + ok. + +%%%----------------------------------------------------------------- +%%% Process +init(Starter) -> + register(?MODULE,self()), + Starter ! {self(),started}, + loop(#{buffer_size=>10,dropped=>0,buffer=>[]}). + +loop(Buffer) -> + receive + stop -> + %% 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)); + _ -> + %% Unexpected message - flush it! + loop(Buffer) + end. + +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) -> + [#{level=>info, + msg=>{"Simple handler buffer full, dropped ~w messages",[N]}, + meta=>#{time=>erlang:system_time(microsecond)}}]. + +%%%----------------------------------------------------------------- +%%% Internal + +%% Can't do io_lib:format + +do_log(#{msg:={report,Report}, + meta:=#{time:=T,error_logger:=#{type:=Type}}}) -> + display_date(T), + display_report(Type,Report); +do_log(#{msg:=Msg,meta:=#{time:=T}}) -> + display_date(T), + display(Msg). + +display_date(Timestamp) when is_integer(Timestamp) -> + Micro = Timestamp rem 1000000, + Sec = Timestamp div 1000000, + {{Y,Mo,D},{H,Mi,S}} = erlang:universaltime_to_localtime( + erlang:posixtime_to_universaltime(Sec)), + erlang:display_string( + integer_to_list(Y) ++ "-" ++ + pad(Mo,2) ++ "-" ++ + pad(D,2) ++ " " ++ + pad(H,2) ++ ":" ++ + pad(Mi,2) ++ ":" ++ + pad(S,2) ++ "." ++ + pad(Micro,6) ++ " "). + +pad(Int,Size) when is_integer(Int) -> + pad(integer_to_list(Int),Size); +pad(Str,Size) when length(Str)==Size -> + Str; +pad(Str,Size) -> + pad([$0|Str],Size). + +display({string,Chardata}) -> + try unicode:characters_to_list(Chardata) of + String -> erlang:display_string(String), erlang:display_string("\n") + catch _:_ -> erlang:display(Chardata) + end; +display({report,Report}) when is_map(Report) -> + display_report(maps:to_list(Report)); +display({report,Report}) -> + display_report(Report); +display({F, A}) when is_list(F), is_list(A) -> + erlang:display_string(F ++ "\n"), + [begin + erlang:display_string("\t"), + erlang:display(Arg) + end || Arg <- A], + ok. + +display_report(Atom, A) when is_atom(Atom) -> + %% The widest atom seems to be 'supervisor_report' at 17. + ColumnWidth = 20, + AtomString = atom_to_list(Atom), + AtomLength = length(AtomString), + Padding = lists:duplicate(ColumnWidth - AtomLength, $\s), + erlang:display_string(AtomString ++ Padding), + display_report(A); +display_report(F, A) -> + erlang:display({F, A}). + +display_report([A, []]) -> + %% Special case for crash reports when process has no links + display_report(A); +display_report(A = [_|_]) -> + case lists:all(fun({Key,_Value}) -> is_atom(Key); (_) -> false end, A) of + true -> + erlang:display_string("\n"), + lists:foreach( + fun({Key, Value}) -> + erlang:display_string( + " " ++ + atom_to_list(Key) ++ + ": "), + erlang:display(Value) + end, A); + false -> + erlang:display(A) + end; +display_report(A) -> + erlang:display(A). diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile index 2f637ca9de..2ad1e3107c 100644 --- a/lib/kernel/test/Makefile +++ b/lib/kernel/test/Makefile @@ -77,7 +77,7 @@ MODULES= \ logger_filters_SUITE \ logger_formatter_SUITE \ logger_legacy_SUITE \ - logger_simple_SUITE \ + logger_simple_h_SUITE \ logger_std_h_SUITE \ logger_test_lib \ os_SUITE \ diff --git a/lib/kernel/test/logger.cover b/lib/kernel/test/logger.cover index b30bcfe920..960bc0abff 100644 --- a/lib/kernel/test/logger.cover +++ b/lib/kernel/test/logger.cover @@ -8,7 +8,7 @@ logger_filters, logger_formatter, logger_server, - logger_simple, + logger_simple_h, logger_std_h, logger_sup]}. diff --git a/lib/kernel/test/logger.spec b/lib/kernel/test/logger.spec index cd76a754a4..1ab90b3e93 100644 --- a/lib/kernel/test/logger.spec +++ b/lib/kernel/test/logger.spec @@ -7,5 +7,5 @@ logger_filters_SUITE, logger_formatter_SUITE, logger_legacy_SUITE, - logger_simple_SUITE, + logger_simple_h_SUITE, logger_std_h_SUITE]}. diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl index a808a31f17..601d331fb0 100644 --- a/lib/kernel/test/logger_env_var_SUITE.erl +++ b/lib/kernel/test/logger_env_var_SUITE.erl @@ -85,7 +85,7 @@ default(Config) -> {domain,{_,{log,super,[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(simple,1,Hs), false = lists:keymember(sasl,1,Hs), ok. @@ -98,7 +98,7 @@ default_sasl_compatible(Config) -> {domain,{_,{log,super,[beam,erlang,otp]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), - false = lists:keymember(logger_simple,1,Hs), + false = lists:keymember(simple,1,Hs), true = lists:keymember(sasl,1,Hs), ok. @@ -110,7 +110,7 @@ error_logger_tty(Config) -> {domain,{_,{log,super,[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(simple,1,Hs), false = lists:keymember(sasl,1,Hs), ok. @@ -124,7 +124,7 @@ error_logger_tty_sasl_compatible(Config) -> {domain,{_,{log,super,[beam,erlang,otp]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), - false = lists:keymember(logger_simple,1,Hs), + false = lists:keymember(simple,1,Hs), true = lists:keymember(sasl,1,Hs), ok. @@ -134,7 +134,7 @@ error_logger_false(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), + {simple,logger_simple_h,SimpleC} = lists:keyfind(simple,1,Hs), info = maps:get(level,SimpleC), notice = maps:get(level,L), SimpleFilters = maps:get(filters,SimpleC), @@ -151,7 +151,7 @@ error_logger_false_progress(Config) -> {logger_level,notice}, {logger_progress_reports,log}]), false = lists:keymember(?STANDARD_HANDLER,1,Hs), - {logger_simple,logger_simple,SimpleC} = lists:keyfind(logger_simple,1,Hs), + {simple,logger_simple_h,SimpleC} = lists:keyfind(simple,1,Hs), info = maps:get(level,SimpleC), notice = maps:get(level,L), SimpleFilters = maps:get(filters,SimpleC), @@ -168,7 +168,7 @@ error_logger_false_sasl_compatible(Config) -> {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), + {simple,logger_simple_h,SimpleC} = lists:keyfind(simple,1,Hs), info = maps:get(level,SimpleC), notice = maps:get(level,L), SimpleFilters = maps:get(filters,SimpleC), @@ -182,7 +182,7 @@ 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(simple,1,Hs), false = lists:keymember(sasl,1,Hs), ok. @@ -191,7 +191,7 @@ error_logger_silent_sasl_compatible(Config) -> [{error_logger,silent}, {logger_sasl_compatible,true}]), false = lists:keymember(?STANDARD_HANDLER,1,Hs), - false = lists:keymember(logger_simple,1,Hs), + false = lists:keymember(simple,1,Hs), true = lists:keymember(sasl,1,Hs), ok. @@ -223,7 +223,7 @@ logger_file(Config) -> {domain,{_,{log,super,[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(simple,1,Hs), false = lists:keymember(sasl,1,Hs), ok. @@ -246,7 +246,7 @@ logger_file_sasl_compatible(Config) -> {domain,{_,{log,super,[beam,erlang,otp]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), - false = lists:keymember(logger_simple,1,Hs), + false = lists:keymember(simple,1,Hs), true = lists:keymember(sasl,1,Hs), ok. @@ -269,7 +269,7 @@ logger_file_log_progress(Config) -> {domain,{_,{log,super,[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(simple,1,Hs), false = lists:keymember(sasl,1,Hs), ok. @@ -289,7 +289,7 @@ logger_file_no_filter(Config) -> {?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(simple,1,Hs), false = lists:keymember(sasl,1,Hs), ok. @@ -310,7 +310,7 @@ logger_file_no_filter_level(Config) -> {?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(simple,1,Hs), false = lists:keymember(sasl,1,Hs), ok. @@ -331,7 +331,7 @@ logger_file_formatter(Config) -> {?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(simple,1,Hs), false = lists:keymember(sasl,1,Hs), ok. @@ -356,7 +356,7 @@ logger_filters(Config) -> {domain,{_,{log,super,[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(simple,1,Hs), false = lists:keymember(sasl,1,Hs), LoggerFilters = maps:get(filters,Logger), true = lists:keymember(stop_progress,1,LoggerFilters), @@ -382,8 +382,8 @@ logger_filters_stop(Config) -> {?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), + false = lists:keymember(simple,1,Hs), + false = lists:keymember(sasl,1,Hs), LoggerFilters = maps:get(filters,Logger), true = lists:keymember(log_error,1,LoggerFilters), @@ -409,7 +409,7 @@ logger_module_level(Config) -> {domain,{_,{log,super,[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(simple,1,Hs), false = lists:keymember(sasl,1,Hs), [{supervisor,error}] = ModuleLevels, ok. @@ -431,7 +431,7 @@ logger_disk_log(Config) -> {domain,{_,{log,super,[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(simple,1,Hs), false = lists:keymember(sasl,1,Hs), ok. @@ -452,7 +452,7 @@ logger_disk_log_formatter(Config) -> {?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(simple,1,Hs), false = lists:keymember(sasl,1,Hs), ok. @@ -461,7 +461,7 @@ 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), + {simple,logger_simple_h,SimpleC} = lists:keyfind(simple,1,Hs), info = maps:get(level,SimpleC), info = maps:get(level,L), SimpleFilters = maps:get(filters,SimpleC), diff --git a/lib/kernel/test/logger_simple_SUITE.erl b/lib/kernel/test/logger_simple_SUITE.erl deleted file mode 100644 index d7f9fa21cf..0000000000 --- a/lib/kernel/test/logger_simple_SUITE.erl +++ /dev/null @@ -1,210 +0,0 @@ -%% -%% %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_simple_SUITE). - --compile(export_all). - --include_lib("common_test/include/ct.hrl"). --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} -> - [] = test_server:messages_get() - after 1000 -> - ct:fail({report_not_received, - {line,?LINE}, - {expected,Expected}, - {got,test_server:messages_get()}}) - end). - --define(str,"Log from "++atom_to_list(?FUNCTION_NAME)++ - ":"++integer_to_list(?LINE)). --define(map_rep,#{function=>?FUNCTION_NAME, line=>?LINE}). --define(keyval_rep,[{function,?FUNCTION_NAME}, {line,?LINE}]). - -suite() -> - [{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_level}]], - [{env,Env},{logger,Hs}|Config]. - -end_per_suite(Config) -> - [application:set_env(App,Key,Val) || {App,Key,Val} <- ?config(env,Config)], - 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, - ok. - -groups() -> - []. - -all() -> - [start_stop, - replace_default, - replace_file, - replace_disk_log - ]. - -start_stop(_Config) -> - undefined = whereis(logger_simple), - register(logger_simple,self()), - {error,_} = logger:add_handler(logger_simple, - logger_simple, - #{filter_default=>log}), - unregister(logger_simple), - ok = logger:add_handler(logger_simple,logger_simple,#{filter_default=>log}), - Pid = whereis(logger_simple), - true = is_pid(Pid), - ok = logger:remove_handler(logger_simple), - false = is_pid(whereis(logger_simple)), - ok. -start_stop(cleanup,_Config) -> - 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) -> - - {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]), - - ok. - -replace_file(Config) -> - - {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} }, - formatter => {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}}}]]), - - {ok,Bin} = sync_and_read(Node, file, File), - Lines = [unicode:characters_to_list(L) || - L <- binary:split(Bin,<<"\n">>,[global,trim])], - ["=EMERGENCY REPORT===="++_, - M1, - "=ALERT REPORT===="++_, - M2, - "=ERROR REPORT===="++_, - _, - _, - "=INFO REPORT===="++_, - _, - _, - "=INFO REPORT===="++_, - _, - _, - _, - "=CRITICAL REPORT===="++_, - _, - _, - "=NOTICE REPORT===="++_, - _ - ] = Lines, - ok. - -replace_disk_log(Config) -> - - {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 }, - formatter => {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}}}]]), - {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, - "=ALERT REPORT===="++_, - M2, - "=ERROR REPORT===="++_, - _, - _, - "=INFO REPORT===="++_, - _, - _, - "=INFO REPORT===="++_, - _, - _, - _, - "=CRITICAL REPORT===="++_, - _, - _, - "=NOTICE REPORT===="++_, - _ - ] = Lines, - ok. diff --git a/lib/kernel/test/logger_simple_h_SUITE.erl b/lib/kernel/test/logger_simple_h_SUITE.erl new file mode 100644 index 0000000000..271a2126de --- /dev/null +++ b/lib/kernel/test/logger_simple_h_SUITE.erl @@ -0,0 +1,210 @@ +%% +%% %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_simple_h_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-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} -> + [] = test_server:messages_get() + after 1000 -> + ct:fail({report_not_received, + {line,?LINE}, + {expected,Expected}, + {got,test_server:messages_get()}}) + end). + +-define(str,"Log from "++atom_to_list(?FUNCTION_NAME)++ + ":"++integer_to_list(?LINE)). +-define(map_rep,#{function=>?FUNCTION_NAME, line=>?LINE}). +-define(keyval_rep,[{function,?FUNCTION_NAME}, {line,?LINE}]). + +suite() -> + [{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_level}]], + [{env,Env},{logger,Hs}|Config]. + +end_per_suite(Config) -> + [application:set_env(App,Key,Val) || {App,Key,Val} <- ?config(env,Config)], + 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, + ok. + +groups() -> + []. + +all() -> + [start_stop, + replace_default, + replace_file, + replace_disk_log + ]. + +start_stop(_Config) -> + undefined = whereis(logger_simple_h), + register(logger_simple_h,self()), + {error,_} = logger:add_handler(simple, + logger_simple_h, + #{filter_default=>log}), + unregister(logger_simple_h), + ok = logger:add_handler(simple,logger_simple_h,#{filter_default=>log}), + Pid = whereis(logger_simple_h), + true = is_pid(Pid), + ok = logger:remove_handler(simple), + false = is_pid(whereis(logger_simple_h)), + ok. +start_stop(cleanup,_Config) -> + logger:remove_handler(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) -> + + {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]), + + ok. + +replace_file(Config) -> + + {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} }, + formatter => {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}}}]]), + + {ok,Bin} = sync_and_read(Node, file, File), + Lines = [unicode:characters_to_list(L) || + L <- binary:split(Bin,<<"\n">>,[global,trim])], + ["=EMERGENCY REPORT===="++_, + M1, + "=ALERT REPORT===="++_, + M2, + "=ERROR REPORT===="++_, + _, + _, + "=INFO REPORT===="++_, + _, + _, + "=INFO REPORT===="++_, + _, + _, + _, + "=CRITICAL REPORT===="++_, + _, + _, + "=NOTICE REPORT===="++_, + _ + ] = Lines, + ok. + +replace_disk_log(Config) -> + + {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 }, + formatter => {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}}}]]), + {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, + "=ALERT REPORT===="++_, + M2, + "=ERROR REPORT===="++_, + _, + _, + "=INFO REPORT===="++_, + _, + _, + "=INFO REPORT===="++_, + _, + _, + _, + "=CRITICAL REPORT===="++_, + _, + _, + "=NOTICE REPORT===="++_, + _ + ] = Lines, + ok. -- cgit v1.2.3 From 72f1ce276401e0b8de9c0c527cbc324da0186939 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 23 May 2018 12:34:30 +0200 Subject: Change type name logger:log() to logger:log_event() --- lib/kernel/doc/src/logger.xml | 18 +++++------ lib/kernel/doc/src/logger_chapter.xml | 26 ++++++++-------- lib/kernel/src/error_logger.erl | 2 +- lib/kernel/src/logger.erl | 21 +++++++------ lib/kernel/src/logger_backend.erl | 4 +-- lib/kernel/src/logger_disk_log_h.erl | 8 ++--- lib/kernel/src/logger_filters.erl | 50 +++++++++++++++--------------- lib/kernel/src/logger_formatter.erl | 4 +-- lib/kernel/src/logger_h_common.erl | 8 ++--- lib/kernel/src/logger_std_h.erl | 8 ++--- lib/kernel/test/logger_formatter_SUITE.erl | 2 +- 11 files changed, 76 insertions(+), 75 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index d6576db6f3..bb356c5b86 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -87,7 +87,7 @@ logger:error("error happened because: ~p",[Reason]). %% Without macro - +

    @@ -446,7 +446,7 @@ Current logger configuration:

    The return value of the fun specifies if a log event is to be discarded or forwarded to the handlers:

    - log() + log_event()

    The filter passed. The next logger filter, if any, is applied. If no more logger filters exist, the @@ -487,7 +487,7 @@ Current logger configuration:

    The return value of the fun specifies if a log event is to be discarded or forwarded to the handler callback:

    - log() + log_event()

    The filter passed. The next handler filter, if any, is applied. If no more filters exist for this @@ -929,11 +929,11 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)). - HModule:log(Log,Config) -> void() + HModule:log(LogEvent,Config) -> void() Log the given log event. - Log = - log() + LogEvent = + log_event() Config = config() @@ -979,11 +979,11 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)). - FModule:format(Log,FConfig) -> FormattedLogEntry + FModule:format(LogEvent,FConfig) -> FormattedLogEntry Format the given log event. - Log = - log() + LogEvent = + log_event() FConfig = formatter_config() FormattedLogEntry = diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index dff9ee2892..3b4cafb010 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -252,7 +252,7 @@

    A handler is defined as a module exporting at least the following function:

    -
    log(Log, Config)
    +
    log(LogEvent, Config)

    This function is called when a log event has passed through all global filters, and all handler filters attached to the handler @@ -320,7 +320,7 @@ module, FModule and its configuration, FConfig. FModule must export the following function, which can be called by the handler:

    -
    format(Log,FConfig)
    +    
    format(LogEvent,FConfig)
     	-> FormattedLogEntry

    See the logger_formatter(3) manual for the full @@ -735,7 +735,7 @@ ok

    Example: implement a handler

    The only requirement that a handler MUST fulfill is to export the following function:

    - log(logger:log(),logger:config()) ->ok + log(logger:log_event(),logger:config()) -> ok

    It may also implement the following callbacks:

    adding_handler(logger:config()) -> {ok,logger:config()} | {error,term()} @@ -763,8 +763,8 @@ changing_config(logger:config(),logger:config()) -> {ok,logger:config()} | {erro -module(myhandler). -export([log/2]). -log(Log,#{formatter:={FModule,FConfig}) -> - io:put_chars(FModule:format(Log,FConfig)). +log(LogEvent,#{formatter:={FModule,FConfig}) -> + io:put_chars(FModule:format(LogEvent,FConfig)).

    A simple handler which prints to file could be implemented like @@ -782,8 +782,8 @@ removing_handler(#{myhandler_fd:=Fd}) -> _ = file:close(Fd), ok. -log(Log,#{myhandler_fd:=Fd,formatter:={FModule,FConfig}}) -> - io:put_chars(Fd,FModule:format(Log,FConfig)). +log(LogEvent,#{myhandler_fd:=Fd,formatter:={FModule,FConfig}}) -> + io:put_chars(Fd,FModule:format(LogEvent,FConfig)).

    The above handlers do not have any overload @@ -810,8 +810,8 @@ adding_handler(Config) -> removing_handler(#{myhandler_pid:=Pid}) -> gen_server:stop(Pid). -log(Log,#{myhandler_pid:=Pid} = Config) -> - gen_server:cast(Pid,{log,Log,Config}). +log(LogEvent,#{myhandler_pid:=Pid} = Config) -> + gen_server:cast(Pid,{log,LogEvent,Config}). init(#{myhandler_file:=File}) -> {ok,Fd} = file:open(File,[append,{encoding,utf8}]), @@ -820,16 +820,16 @@ init(#{myhandler_file:=File}) -> handle_call(_,_,State) -> {reply,{error,bad_request},State}. -handle_cast({log,Log,Config},#{fd:=Fd} = State) -> - do_log(Fd,Log,Config), +handle_cast({log,LogEvent,Config},#{fd:=Fd} = State) -> + do_log(Fd,LogEvent,Config), {noreply,State}. terminate(Reason,#{fd:=Fd}) -> _ = file:close(Fd), ok. -do_log(Fd,Log,#{formatter:={FModule,FConfig}}) -> - String = FModule:format(Log,FConfig), +do_log(Fd,LogEvent,#{formatter:={FModule,FConfig}}) -> + String = FModule:format(LogEvent,FConfig), io:put_chars(Fd,String).

    diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index 96f6536e61..b3957d0c7e 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -116,7 +116,7 @@ removing_handler(#{id:=?MODULE}) -> stop(), ok. --spec log(logger:log(),logger:config()) -> ok. +-spec log(logger:log_event(),logger:config()) -> ok. log(#{level:=Level,msg:=Msg,meta:=Meta},_Config) -> do_log(Level,Msg,Meta). diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index f35036aa04..12f8dd77cf 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -64,11 +64,11 @@ %%%----------------------------------------------------------------- %%% Types --type log() :: #{level:=level(), - msg:={io:format(),[term()]} | - {report,report()} | - {string,unicode:chardata()}, - meta:=metadata()}. +-type log_event() :: #{level:=level(), + msg:={io:format(),[term()]} | + {report,report()} | + {string,unicode:chardata()}, + meta:=metadata()}. -type level() :: emergency | alert | critical | error | warning | notice | info | debug. -type report() :: map() | [{atom(),term()}]. @@ -87,9 +87,10 @@ line := non_neg_integer()}. -type handler_id() :: atom(). -type filter_id() :: atom(). --type filter() :: {fun((log(),filter_arg()) -> filter_return()),filter_arg()}. +-type filter() :: {fun((log_event(),filter_arg()) -> + filter_return()),filter_arg()}. -type filter_arg() :: term(). --type filter_return() :: stop | ignore | log(). +-type filter_return() :: stop | ignore | log_event(). -type config() :: #{id => handler_id(), level => level(), filter_default => log | stop, @@ -101,9 +102,9 @@ -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,config_handler/0, - formatter_config/0]). +-export_type([log_event/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, + config_handler/0,formatter_config/0]). %%%----------------------------------------------------------------- %%% API diff --git a/lib/kernel/src/logger_backend.erl b/lib/kernel/src/logger_backend.erl index d9f5aa6faf..b3cf7d67dd 100644 --- a/lib/kernel/src/logger_backend.erl +++ b/lib/kernel/src/logger_backend.erl @@ -58,7 +58,7 @@ call_handlers(#{level:=Level}=Log,[Id|Handlers],Tid) -> debug, [{logger,removed_failing_handler}, {handler,{Id,Module}}, - {log,Log1}, + {log_event,Log1}, {config,Config1}, {reason,{C,R,filter_stacktrace(S)}}]); {error,{not_found,_}} -> @@ -122,7 +122,7 @@ handle_filter_failed({Id,_}=Filter,Owner,Log,Reason) -> [{logger,removed_failing_filter}, {filter,Filter}, {owner,Owner}, - {log,Log}, + {log_event,Log}, {reason,Reason}]); _ -> ok diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index 37a147e3dc..773aa75bc6 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -235,15 +235,15 @@ swap_buffer(Name,Buffer) -> %%%----------------------------------------------------------------- %%% Log a string or report --spec log(Log, Config) -> ok | dropped when - Log :: logger:log(), +-spec log(LogEvent, Config) -> ok | dropped when + LogEvent :: logger:log_event(), Config :: logger:config(). -log(Log,Config=#{id:=Name}) -> +log(LogEvent,Config=#{id:=Name}) -> %% if the handler has crashed, we must drop this request %% and hope the handler restarts so we can try again true = is_pid(whereis(Name)), - Bin = logger_h_common:log_to_binary(Log,Config), + Bin = logger_h_common:log_to_binary(LogEvent,Config), logger_h_common:call_cast_or_drop(Name, Bin). diff --git a/lib/kernel/src/logger_filters.erl b/lib/kernel/src/logger_filters.erl index 322aa0265d..7359b3b4b7 100644 --- a/lib/kernel/src/logger_filters.erl +++ b/lib/kernel/src/logger_filters.erl @@ -27,13 +27,13 @@ -include("logger_internal.hrl"). -define(IS_ACTION(A), (A==log orelse A==stop)). --spec domain(Log,Extra) -> logger:filter_return() when - Log :: logger:log(), +-spec domain(LogEvent,Extra) -> logger:filter_return() when + LogEvent :: logger:log_event(), Extra :: {Action,Compare,MatchDomain}, Action :: log | stop, Compare :: super | sub | equal | not_equal | undefined, MatchDomain :: list(atom()). -domain(#{meta:=Meta}=Log,{Action,Compare,MatchDomain}) +domain(#{meta:=Meta}=LogEvent,{Action,Compare,MatchDomain}) when ?IS_ACTION(Action) andalso (Compare==super orelse Compare==sub orelse @@ -41,17 +41,17 @@ domain(#{meta:=Meta}=Log,{Action,Compare,MatchDomain}) Compare==not_equal orelse Compare==undefined) andalso is_list(MatchDomain) -> - filter_domain(Compare,Meta,MatchDomain,on_match(Action,Log)); -domain(Log,Extra) -> - erlang:error(badarg,[Log,Extra]). + filter_domain(Compare,Meta,MatchDomain,on_match(Action,LogEvent)); +domain(LogEvent,Extra) -> + erlang:error(badarg,[LogEvent,Extra]). --spec level(Log,Extra) -> logger:filter_return() when - Log :: logger:log(), +-spec level(LogEvent,Extra) -> logger:filter_return() when + LogEvent :: logger:log_event(), Extra :: {Action,Operator,MatchLevel}, Action :: log | stop, Operator :: neq | eq | lt | gt | lteq | gteq, MatchLevel :: logger:level(). -level(#{level:=L1}=Log,{Action,Op,L2}) +level(#{level:=L1}=LogEvent,{Action,Op,L2}) when ?IS_ACTION(Action) andalso (Op==neq orelse Op==eq orelse @@ -60,25 +60,25 @@ level(#{level:=L1}=Log,{Action,Op,L2}) Op==lteq orelse Op==gteq) andalso ?IS_LEVEL(L2) -> - filter_level(Op,L1,L2,on_match(Action,Log)); -level(Log,Extra) -> - erlang:error(badarg,[Log,Extra]). + filter_level(Op,L1,L2,on_match(Action,LogEvent)); +level(LogEvent,Extra) -> + erlang:error(badarg,[LogEvent,Extra]). --spec progress(Log,Extra) -> logger:filter_return() when - Log :: logger:log(), +-spec progress(LogEvent,Extra) -> logger:filter_return() when + LogEvent :: logger:log_event(), Extra :: log | stop. -progress(Log,Action) when ?IS_ACTION(Action) -> - filter_progress(Log,on_match(Action,Log)); -progress(Log,Action) -> - erlang:error(badarg,[Log,Action]). +progress(LogEvent,Action) when ?IS_ACTION(Action) -> + filter_progress(LogEvent,on_match(Action,LogEvent)); +progress(LogEvent,Action) -> + erlang:error(badarg,[LogEvent,Action]). --spec remote_gl(Log,Extra) -> logger:filter_return() when - Log :: logger:log(), +-spec remote_gl(LogEvent,Extra) -> logger:filter_return() when + LogEvent :: logger:log_event(), Extra :: log | stop. -remote_gl(Log,Action) when ?IS_ACTION(Action) -> - filter_remote_gl(Log,on_match(Action,Log)); -remote_gl(Log,Action) -> - erlang:error(badarg,[Log,Action]). +remote_gl(LogEvent,Action) when ?IS_ACTION(Action) -> + filter_remote_gl(LogEvent,on_match(Action,LogEvent)); +remote_gl(LogEvent,Action) -> + erlang:error(badarg,[LogEvent,Action]). %%%----------------------------------------------------------------- %%% Internal @@ -123,5 +123,5 @@ filter_remote_gl(#{meta:=#{gl:=GL}},OnMatch) when node(GL)=/=node() -> filter_remote_gl(_,_) -> ignore. -on_match(log,Log) -> Log; +on_match(log,LogEvent) -> LogEvent; on_match(stop,_) -> stop. diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl index 5882c70ddf..95cb6e35fc 100644 --- a/lib/kernel/src/logger_formatter.erl +++ b/lib/kernel/src/logger_formatter.erl @@ -30,8 +30,8 @@ %%%----------------------------------------------------------------- %%% API --spec format(Log,Config) -> unicode:chardata() when - Log :: logger:log(), +-spec format(LogEvent,Config) -> unicode:chardata() when + LogEvent :: logger:log_event(), Config :: #{single_line=>boolean(), legacy_header=>boolean(), report_cb=>fun((logger:report()) -> {io:format(),[term()]}), diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl index 70531c0e34..336398cd4a 100644 --- a/lib/kernel/src/logger_h_common.erl +++ b/lib/kernel/src/logger_h_common.erl @@ -39,8 +39,8 @@ %%%----------------------------------------------------------------- %%% Covert log data on any form to binary --spec log_to_binary(Log,Config) -> LogString when - Log :: logger:log(), +-spec log_to_binary(LogEvent,Config) -> LogString when + LogEvent :: logger:log_event(), Config :: logger:config(), LogString :: binary(). log_to_binary(#{msg:={report,_},meta:=#{report_cb:=_}}=Log,Config) -> @@ -58,7 +58,7 @@ do_log_to_binary(Log,Config) -> catch _:_ -> ?LOG_INTERNAL(debug,[{formatter_error,Formatter}, {config,FormatterConfig}, - {log,Log}, + {log_event,Log}, {bad_return_value,String}]), <<"FORMATTER ERROR: bad_return_value">> end. @@ -69,7 +69,7 @@ try_format(Log,Formatter,FormatterConfig) -> C:R:S -> ?LOG_INTERNAL(debug,[{formatter_crashed,Formatter}, {config,FormatterConfig}, - {log,Log}, + {log_event,Log}, {reason, {C,R,logger:filter_stacktrace(?MODULE,S)}}]), case {?DEFAULT_FORMATTER,#{}} of diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index fc8ca1535b..63c3ab2dac 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -219,15 +219,15 @@ swap_buffer(Name,Buffer) -> %%%----------------------------------------------------------------- %%% Log a string or report --spec log(Log, Config) -> ok | dropped when - Log :: logger:log(), +-spec log(LogEvent, Config) -> ok | dropped when + LogEvent :: logger:log_event(), Config :: logger:config(). -log(Log,Config=#{id:=Name}) -> +log(LogEvent,Config=#{id:=Name}) -> %% if the handler has crashed, we must drop this request %% and hope the handler restarts so we can try again true = is_pid(whereis(Name)), - Bin = logger_h_common:log_to_binary(Log,Config), + Bin = logger_h_common:log_to_binary(LogEvent,Config), logger_h_common:call_cast_or_drop(Name, Bin). %%%=================================================================== diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl index eaa6c0aca5..7a93f2ca79 100644 --- a/lib/kernel/test/logger_formatter_SUITE.erl +++ b/lib/kernel/test/logger_formatter_SUITE.erl @@ -514,7 +514,7 @@ level_or_msg_in_meta(_Config) -> ok. faulty_log(_Config) -> - %% Unexpected log (should be type logger:log()) - print error + %% Unexpected log (should be type logger:log_event()) - print error {error, function_clause, {logger_formatter,format,[_,_],_}} = -- cgit v1.2.3 From 53539ee751da408029c9dfae80384fdec6c7a552 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 22 May 2018 13:22:55 +0200 Subject: Update documentation of logger and error_logger --- lib/kernel/doc/src/error_logger.xml | 233 ++++++++++------ lib/kernel/doc/src/kernel_app.xml | 8 +- lib/kernel/doc/src/logger.xml | 44 ++- lib/kernel/doc/src/logger_chapter.xml | 477 ++++++++++++++++++++------------ lib/kernel/doc/src/logger_formatter.xml | 370 +++++++++++++------------ lib/kernel/src/logger.erl | 4 +- lib/kernel/src/logger_formatter.erl | 21 +- 7 files changed, 697 insertions(+), 460 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml index e91fc734a4..f418aa5bbe 100644 --- a/lib/kernel/doc/src/error_logger.xml +++ b/lib/kernel/doc/src/error_logger.xml @@ -33,9 +33,10 @@ -

    In OTP-21, a new API for logging was added to Erlang/OTP. The +

    In Erlang/OTP 21.0, a new API for logging was added. The old error_logger module can still be used by legacy - code, but new code should use the new API instead.

    + code, but log events are redirected to the new Logger API. New + code should use the Logger API directly.

    error_logger is no longer started by default, but is automatically started when an event handler is added with error_logger:add_report_handler/1,2. The error_logger @@ -89,6 +90,9 @@ The function returns ok if successful.

    The event handler must be able to handle the events in this module, see section Events.

    +

    The first time this function is called, + error_logger is added as a Logger handler, and + the error_logger process is started.

    @@ -98,37 +102,40 @@

    Deletes an event handler from the error logger by calling gen_event:delete_handler(error_logger, Handler, []), see gen_event(3).

    +

    If no more event handlers exist after the deletion, + error_logger is removed as a Logger handler, and + the error_logger process is stopped.

    - Send a standard error event to the error logger. + Log a standard error event. -

    Sends a standard error event to the error logger. - The Format and Data arguments - are the same as the arguments of +

    Log a standard error event. The Format + and Data arguments are the same as the + arguments of io:format/2 - in STDLIB. - The event is handled by the standard event handler.

    + in STDLIB.

    +

    Error logger forwards the event to Logger, including + metadata that allows backwards compatibility with legacy + error logger event handlers.

    +

    The event is handled by the default Logger handler.

    +

    These functions are kept for backwards compatibility and + must not be used by new code. Use the + ?LOG_ERROR macro or + logger:error/1,2,3 + instead.

    Example:

    -1> error_logger:error_msg("An error occurred in ~p~n", [a_module]).
    -
    -=ERROR REPORT==== 11-Aug-2005::14:03:19 ===
    +1> error_logger:error_msg("An error occurred in ~p", [a_module]).
    +=ERROR REPORT==== 22-May-2018::11:18:43.376917 ===
     An error occurred in a_module
     ok
    - -

    If called with bad arguments, this function can crash - the standard event handler, meaning no further events are - logged. When in doubt, use - error_report/1 - instead.

    -

    If the Unicode translation modifier (t) is used in - the format string, all error handlers must ensure that the + the format string, all event handlers must ensure that the formatted output is correctly encoded for the I/O device.

    @@ -136,36 +143,51 @@ ok
    - Send a standard error report event to the error logger. + Log a standard error event. -

    Sends a standard error report event to the error logger. - The event is handled by the standard event handler.

    +

    Log a standard error event. Error logger forwards the event + to Logger, including metadata that allows backwards + compatibility with legacy error logger event handlers.

    +

    The event is handled by the default Logger handler.

    +

    This functions is kept for backwards compatibility and + must not be used by new code. Use the + ?LOG_ERROR macro or + logger:error/1,2,3 + instead.

    Example:

     2> error_logger:error_report([{tag1,data1},a_term,{tag2,data}]).
    -
    -=ERROR REPORT==== 11-Aug-2005::13:45:41 ===
    +=ERROR REPORT==== 22-May-2018::11:24:23.699306 ===
         tag1: data1
         a_term
         tag2: data
     ok
     3> error_logger:error_report("Serious error in my module").
    -
    -=ERROR REPORT==== 11-Aug-2005::13:45:49 ===
    +=ERROR REPORT==== 22-May-2018::11:24:45.972445 ===
     Serious error in my module
     ok
    - Send a user-defined error report event to the error logger. + Log a user-defined error event. -

    Sends a user-defined error report event to the error logger. - An event handler to handle the event is supposed to have been - added. The event is ignored by the standard event handler.

    +

    Log a user-defined error event. Error logger forwards the + event to Logger, including metadata that allows backwards + compatibility with legacy error logger event handlers.

    +

    Error logger also adds a domain field with + value [Type] to this event's metadata, + causing the filters of the default Logger handler to discard + the event. A different Logger handler, or an error logger + event handler, must be added to handle this event.

    It is recommended that Report follows the same structure as for error_report/1.

    +

    This functions is kept for backwards compatibility and + must not be used by new code. Use the + ?LOG_ERROR macro or + logger:error/1,2,3 + instead.

    @@ -181,38 +203,41 @@ ok

    The error_logger_format_depth variable is deprecated since - the logging API was - introduced in OTP-21. The variable, and this function, are - kept for backwards compatibility since they still might be - used by legacy report handlers.

    + the Logger API was + introduced in Erlang/OTP 21.0. The variable, and this + function, are kept for backwards compatibility since they + still might be used by legacy report handlers.

    - Send a standard information event to the error logger. + Log a standard information event. -

    Sends a standard information event to the error logger. - The Format and Data arguments - are the same as the arguments of +

    Log a standard information event. The Format + and Data arguments are the same as the + arguments of io:format/2 - in STDLIB. The event is handled by the standard event handler.

    + in STDLIB.

    +

    Error logger forwards the event to Logger, including + metadata that allows backwards compatibility with legacy + error logger event handlers.

    +

    The event is handled by the default Logger handler.

    +

    These functions are kept for backwards compatibility and + must not be used by new code. Use the + ?LOG_INFO macro or + logger:info/1,2,3 + instead.

    Example:

    -1> error_logger:info_msg("Something happened in ~p~n", [a_module]).
    -
    -=INFO REPORT==== 11-Aug-2005::14:06:15 ===
    +1> error_logger:info_msg("Something happened in ~p", [a_module]).
    +=INFO REPORT==== 22-May-2018::12:03:32.612462 ===
     Something happened in a_module
     ok
    - -

    If called with bad arguments, this function can crash - the standard event handler, meaning no further events are - logged. When in doubt, use info_report/1 instead.

    -

    If the Unicode translation modifier (t) is used in - the format string, all error handlers must ensure that the + the format string, all event handlers must ensure that the formatted output is correctly encoded for the I/O device.

    @@ -220,37 +245,52 @@ ok
    - Send a standard information report event to the error logger. + Log a standard information event. -

    Sends a standard information report event to the error - logger. The event is handled by the standard event handler.

    +

    Log a standard information event. Error logger forwards the + event to Logger, including metadata that allows backwards + compatibility with legacy error logger event handlers.

    +

    The event is handled by the default Logger handler.

    +

    This functions is kept for backwards compatibility and + must not be used by new code. Use the + ?LOG_INFO macro or + logger:info/1,2,3 + instead.

    Example:

     2> error_logger:info_report([{tag1,data1},a_term,{tag2,data}]).
    -
    -=INFO REPORT==== 11-Aug-2005::13:55:09 ===
    +=INFO REPORT==== 22-May-2018::12:06:35.994440 ===
         tag1: data1
         a_term
         tag2: data
     ok
     3> error_logger:info_report("Something strange happened").
    -
    -=INFO REPORT==== 11-Aug-2005::13:55:36 ===
    +=INFO REPORT==== 22-May-2018::12:06:49.066872 ===
     Something strange happened
     ok
    - Send a user-defined information report event to the error logger. + Log a user-defined information event. -

    Sends a user-defined information report event to the error - logger. An event handler to handle the event is supposed to - have been added. The event is ignored by the standard event - handler.

    +

    Log a user-defined information event. Error logger forwards + the event to Logger, including metadata that allows + backwards compatibility with legacy error logger event + handlers.

    +

    Error logger also adds a domain field with + value [Type] to this event's metadata, + causing the filters of the default Logger handler to discard + the event. A different Logger handler, or an error logger + event handler, must be added to handle this event.

    It is recommended that Report follows the same structure as for info_report/1.

    +

    This functions is kept for backwards compatibility and + must not be used by new code. Use the + ?LOG_INFO macro or + logger:info/1,2,3 + instead.

    @@ -349,24 +389,27 @@ ok - Send a standard warning event to the error logger. + Log a standard warning event. -

    Sends a standard warning event to the error logger. - The Format and Data arguments - are the same as the arguments of +

    Log a standard warning event. The Format + and Data arguments are the same as the + arguments of io:format/2 - in STDLIB. - The event is handled by the standard event handler. It is tagged - as an error, warning, or info, see + in STDLIB.

    +

    Error logger forwards the event to Logger, including + metadata that allows backwards compatibility with legacy + error logger event handlers.

    +

    The event is handled by the default Logger handler. The log + level can be changed to error or info, see warning_map/0.

    - -

    If called with bad arguments, this function can crash - the standard event handler, meaning no further events are - logged. When in doubt, use warning_report/1 instead.

    -
    +

    These functions are kept for backwards compatibility and + must not be used by new code. Use the + ?LOG_WARNING macro or + logger:warning/1,2,3 + instead.

    If the Unicode translation modifier (t) is used in - the format string, all error handlers must ensure that the + the format string, all event handlers must ensure that the formatted output is correctly encoded for the I/O device.

    @@ -374,24 +417,43 @@ ok
    - Send a standard warning report event to the error logger. + Log a standard warning event. -

    Sends a standard warning report event to the error logger. - The event is handled by the standard event handler. It is - tagged as an error, warning, or info, see +

    Log a standard warning event. Error logger forwards the event + to Logger, including metadata that allows backwards + compatibility with legacy error logger event handlers.

    +

    The event is handled by the default Logger handler. The log + level can be changed to error or info, see warning_map/0.

    +

    This functions is kept for backwards compatibility and + must not be used by new code. Use the + ?LOG_WARNING macro or + logger:warning/1,2,3 + instead.

    - Send a user-defined warning report event to the error logger. + Log a user-defined warning event. -

    Sends a user-defined warning report event to the error - logger. An event handler to handle the event is supposed to - have been added. The event is ignored by the standard event - handler. It is tagged as an error, warning, or info, - depending on the value of +

    Log a user-defined warning event. Error logger forwards the + event to Logger, including metadata that allows backwards + compatibility with legacy error logger event handlers.

    +

    Error logger also adds a domain field with + value [Type] to this event's metadata, + causing the filters of the default Logger handler to discard + the event. A different Logger handler, or an error logger + event handler, must be added to handle this event.

    +

    The log level can be changed to error or info, see warning_map/0.

    +

    It is recommended that Report follows the same + structure as for + warning_report/1.

    +

    This functions is kept for backwards compatibility and + must not be used by new code. Use the + ?LOG_WARNING macro or + logger:warning/1,2,3 + instead.

    @@ -455,8 +517,9 @@ ok
    See Also

    gen_event(3), - log_mf_h(3) - kernel(6) + logger(3), + log_mf_h(3), + kernel(6), sasl(6)

    diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index 0f7e95472b..e2a6d30249 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -481,13 +481,13 @@ MaxT = TickTime + TickTime / 4
    Deprecated Configuration Parameters -

    In OTP-21, a new API for logging was added to Erlang/OTP. The +

    In Erlang/OTP 21.0, a new API for logging was added. The old error_logger event manager, and event handlers - running on this manager, will still work, but they are not used + running on this manager, still work, but they are no longer used by default.

    The following application configuration parameters can still be - set, but they will only be used if the corresponding new logger - variables are not set.

    + set, but they are only used if the corresponding configuration + parameters for Logger are not set.

    error_logger Replaced by setting the type of the default diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index bb356c5b86..c0b3d81367 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -132,6 +132,29 @@ logger:error("error happened because: ~p",[Reason]). %% Without macro log event to the handlers. If the same keys occur, values from the log call overwrite process metadata, which in turn overwrite values set by Logger.

    +

    The following custom metadata keys have special meaning:

    + + domain + +

    The value associated with this key is used by filters + for grouping log events originating from, for example, + specific functional + areas. See + logger_filters:domain/2 + for a description of how this field can be used.

    +
    + report_cb + +

    If the log message is specified as + a report(), + the report_cb key can be associated with a fun + (report callback) that converts the report to a format + string and arguments. See + section Log + Message in the User's Guide for more + information about report callbacks.

    +
    +
    @@ -145,7 +168,7 @@ logger:error("error happened because: ~p",[Reason]). %% Without macro filters => [] formatter => {logger_formatter,DefaultFormatterConfig} -

    See the +

    See the logger_formatter(3) manual page for information about the default configuration for this formatter.

    @@ -978,6 +1001,25 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)).
    + + FModule:check_config(FConfig) -> ok | {error,term()} + Validate the given formatter configuration. + + FConfig = + formatter_config() + + +

    This callback function is optional.

    +

    The function is called by a Logger when formatter + configuration is set or modified. The formatter must + validate the given configuration and return ok if it + is correct, and {error,term()} if it is faulty.

    +

    See + logger_formatter(3) + for an example implementation. logger_formatter is the + default formatter used by Logger.

    +
    +
    FModule:format(LogEvent,FConfig) -> FormattedLogEntry Format the given log event. diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 3b4cafb010..a3eec7bd4b 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -30,7 +30,7 @@ logger_chapter.xml -

    As of OTP-21, Erlang/OTP provides a standard API for logging +

    Erlang/OTP 21.0 provides a new standard API for logging through Logger, which is part of the Kernel application. Logger consists of the API for issuing log events, and a customizable backend where log handlers, filters and @@ -65,63 +65,14 @@ Conceptual Overview - -

    In accordance with the Syslog protocol, RFC-5424, eight log - levels can be specified:

    - - - - Level - Integer - Description - - - emergency - 0 - system is unusable - - - alert - 1 - action must be taken immediately - - - critical - 2 - critical contidions - - - error - 3 - error conditions - - - warning - 4 - warning conditions - - - notice - 5 - normal but significant conditions - - - info - 6 - informational messages - - - debug - 7 - debug-level messages - - Log Levels -
    - -

    A log event passes the level check if the integer value of its - log level is less than or equal to the currently configured log - level, that is, if the event is equally or more severe than the - configured level.

    +

    Log levels are expressed as atoms. Internally in Logger, the + atoms are mapped to integer values, and a log event passes the + log level check if the integer value of its log level is less + than or equal to the currently configured log level. That is, + the check pases if the event is equally or more severe than the + configured level. See section Log + Level for a listing and description of all log + levels.

    The global log level can be overridden by a log level configured per module. This is to, for instance, allow more verbose logging from a specific part of the system.

    @@ -137,13 +88,150 @@ event to its destination. See section Handlers for more details.

    -

    Everything upto and including the call to the handler callbacks - is executed on the client process, that is, the process where - the log event was issued. It is up to the handler implementation - if other processes are to be involved or not.

    +

    Everything up to and including the call to the handler + callbacks is executed on the client process, that is, the + process where the log event was issued. It is up to the handler + implementation if other processes are involved or not.

    The handlers are called in sequence, and the order is not defined.

    +
    + Logger API +

    The API for logging consists of a set + of macros, and a set + of functions on the form logger:Level/1,2,3, which are + all shortcuts + for + logger:log(Level,Arg1[,Arg2[,Arg3]]).

    +

    The difference between using the macros and the exported + functions is that macros add location (originator) information + to the metadata, and performs lazy evaluation by wrapping the + logger call in a case statement, so it is only evaluated if the + log level of the event passes the global log level check.

    +
    + + Log Level +

    The log level indicates the severity of a event. In + accordance with the Syslog protocol, RFC-5424, eight log + levels can be specified. The following table lists all + possible log levels by name (atom), integer value, and + description:

    + + + + Level + Integer + Description + + + emergency + 0 + system is unusable + + + alert + 1 + action must be taken immediately + + + critical + 2 + critical contidions + + + error + 3 + error conditions + + + warning + 4 + warning conditions + + + notice + 5 + normal but significant conditions + + + info + 6 + informational messages + + + debug + 7 + debug-level messages + + Log Levels +
    +

    Notice that the integer value is only used internally in + Logger. In the API, you must always use the atom. To compare + the severity of two log levels, + use + logger:compare_levels/2.

    +
    +
    + + Log Message +

    The log message contains the information to be logged. The + message can consist of a format string and arguments (given as + two separate parameters in the Logger API), a string or a + report. The latter, which is either a map or a key-value list, + can be accompanied by a report callback specified in the log + event's metadata. The + report callback is a convenience function that + the formatter can use + to convert the report to a format string and arguments. The + formatter can also use its own conversion function, if no + callback is provided, or if a customized formatting is + desired.

    +

    Example, format string and arguments:

    + logger:error("The file does not exist: ~ts",[Filename]) +

    Example, string:

    + logger:notice("Something strange happened!") +

    Example, report, and metadata with report callback:

    + +logger:debug(#{got => connection_request, id => Id, state => State}, + #{report_cb => fun(R) -> {"~p",[R]} end}) +

    The log message can also be provided through a fun for lazy + evaluation. The fun is only evaluated if the global log level + check passes, and is therefore recommended if it is expensive + to generate the message. The lazy fun must return a string, a + report, or a tuple with format string and arguments.

    +
    +
    + Metadata +

    Metadata contains additional data associated with a log + message. Logger inserts some metadata fields by default, and + the client can add custom metadata in two different ways:

    + + Set process metadata + +

    Process metadata is set and updated + with + logger:set_process_metadata/1 + and + logger:update_process metadata/1, + respectively. This metadata applies to the process on + which these calls are made, and Logger adds the metadata + to all log events issued on that process.

    +
    + Add metadata to a specifc log event + +

    Metadata associated with one specifc log event is given + as the last parameter to the log macro or Logger API + function when the event is issued. For example:

    + ?LOG_ERROR("Connection closed",#{context => server}) +
    +
    +

    See the description of + the + logger:metadata() type for information + about which default keys Logger inserts, and how the different + metadata maps are merged.

    +
    +
    Filters @@ -166,13 +254,13 @@ discarded. If the filter is global, no handler filters or callbacks are called. If it is a handler filter, the corresponding handler callback is not called, but the log event - is forwarded to the filters attached to the next handler, if + is forwarded to filters attached to the next handler, if any.

    If the log event is returned, the next filter function is called with the returned value as the first argument. That is, if a filter function modifies the log event, the next filter - function receivea the modified event. The value returned from - the last filter funcion is the value that the handler callback + function receives the modified event. The value returned from + the last filter function is the value that the handler callback receives.

    If the filter function returns ignore, it means that it did not recognize the log event, and thus leaves to other @@ -180,24 +268,21 @@

    The configuration option filter_default specifies the behaviour if all filter functions - return ignore. filter_default is by default set - to log, meaning that if all filters ignore a log event, - Logger forwards the event to the handler - callback. If filter_default is set to stop, - Logger discards such events.

    - -

    Filters are applied in the reverse order of installation, - meaning that the last added filter is applied first.

    - + return ignore, or if no filters + exist. filter_default is by default set to log, + meaning that if all existing filters ignore a log event, Logger + forwards the event to the handler + callback. If filter_default is set to stop, Logger + discards such events.

    Global filters are added with logger:add_logger_filter/2 and removed with logger:remove_logger_filter/1. They can also - be added at system start via Kernel configuration + be added at system start via the Kernel configuration parameter logger.

    -

    Handler filters are added with +

    Handler filters are added with logger:add_handler_filter/3 and removed @@ -206,7 +291,7 @@ be specified directly in the configuration when adding a handler with logger:add_handler/3 - or via Kernel configuration + or via the Kernel configuration parameter logger.

    To see which filters are currently installed in the system, @@ -215,35 +300,37 @@ logger:get_logger_config/0 and logger:get_handler_config/1. Filters are - applied in the order they are listed.

    + listed in the order they are applied, that is, the first + filter in the list is applied first, and so on.

    For convenience, the following built-in filters exist:

    - - -

    - logger_filters:domain/2 provides a way of - filtering log events based on a - domain field Metadata.

    -
    - -

    - logger_filters:level/2 provides a way of - filtering log events based on the log level.

    -
    - -

    - logger_filters:progress/2 stops or allows - progress reports from supervisor - and application_controller.

    -
    - -

    - logger_filters:remote_gl/2 stops or allows - log events originating from a process that has its group - leader on a remote node.

    -
    -
    + + + logger_filters:domain/2 + +

    Provides a way of filtering log events based on a + domain field in Metadata.

    +
    + + logger_filters:level/2 + +

    Provides a way of filtering log events based on the log + level.

    +
    + + logger_filters:progress/2 + +

    Stops or allows progress reports from supervisor + and application_controller.

    +
    + + logger_filters:remote_gl/2 + +

    Stops or allows log events originating from a process + that has its group leader on a remote node.

    +
    +
    @@ -252,19 +339,19 @@

    A handler is defined as a module exporting at least the following function:

    -
    log(LogEvent, Config)
    +
    log(LogEvent, Config) -> void()

    This function is called when a log event has passed through all global filters, and all handler filters attached to the handler in question. The function call is executed on the client process, and it is up to the handler implementation if other - processes are to be involved or not.

    + processes are involved or not.

    Logger allows adding multiple instances of a handler - callback. That is, the callback module might be implemented in - such a way that, by using different handler identities, the same - callback module can be used for multiple handler - instances. Handler configuration is per instance.

    + callback. That is, if a callback module implementation allows + it, you can add multiple handler instances using the same + callback module. The different instances are identified by + unique handler identities.

    In addition to the mandatory callback function log/2, a handler module can export the optional callback @@ -281,20 +368,19 @@

    This is the default handler used by OTP. Multiple instances can be started, and each instance will write log events to a - given destination, console or file. Filters can be used for - selecting which event to send to which handler instance.

    + given destination, terminal or file.

    logger_disk_log_h -

    This handler behaves much like logger_std_h, except it uses +

    This handler behaves much like logger_std_h, except it uses disk_log as its destination.

    error_logger -

    This handler is to be used for backwards compatibility +

    This handler is provided for backwards compatibility only. It is not started by default, but will be automatically started the first time an error_logger event handler is added @@ -302,7 +388,8 @@ error_logger:add_report_handler/1,2.

    The old error_logger event handlers in STDLIB and - SASL still exist, but they are not added by Erlang/OTP.

    + SASL still exist, but they are not added by Erlang/OTP 21.0 + or later.

    @@ -312,9 +399,10 @@ Formatters

    A formatter can be used by the handler implementation to do the final formatting of a log event, before printing to the - handler's destination. The handler callback gets the formatter - information in the handler configuration, which is passed as the - second argument to + handler's destination. The handler callback receives the + formatter information as part of the handler configuration, + which is passed as the second argument + to HModule:log/2.

    The formatter information consits of a formatter module, FModule and its @@ -322,9 +410,23 @@ following function, which can be called by the handler:

    format(LogEvent,FConfig)
     	-> FormattedLogEntry
    -

    See the - logger_formatter(3) manual for the full - description of the default formatter used by Logger.

    +

    The formatter information for a handler is set as a part of its + configuration when the handler is added. It can also be changed + during runtime + with + logger:set_handler_config(HandlerId,formatter,{FModule,FConfig}) + , which overwrites the current formatter information, + or with + logger:update_formatter_config/2,3, which + only modifies the formatter configuration.

    +

    If the formatter module exports the optional callback + function + check_config(FConfig), Logger calls this + function when the formatter information is set or modified, to + verify the validity of the formatter configuration.

    +

    If no formatter information is specified for a handler, Logger + uses + logger_formatter(3) as default.

    @@ -335,7 +437,7 @@ or at run-time by using the logger(3) API. The recommended approach is to do the initial configuration in the sys.config file and then use the API when some configuration - has to be changed at run-time, such as the log level.

    + has to be changed at runtime, such as the log level.

    Kernel Configuration Parameters @@ -436,9 +538,9 @@ logger:level()

    Specifies the global log level to log.

    -

    See table Log - Levels in the Overview section for a listing - and description of possible log levels.

    +

    See section Log + Level for a listing and description of + possible log levels.

    The initial value of this option is set by the Kernel configuration parameter @@ -465,7 +567,7 @@ filter_default = log | stop

    Specifies what to do with an event if all filters - return ignore.

    + return ignore, or if no filters exist.

    See section Filters for more information about how this option is used.

    Default is log.

    @@ -481,9 +583,9 @@ logger:level()

    Specifies the log level which the handler logs.

    -

    See table Log - Levels in the Overview section for a listing - and description of possible log levels.

    +

    See section Log + Level for a listing and description of + possible log levels.

    The log level can be specified when adding the handler, or changed during runtime with, for instance, @@ -509,7 +611,7 @@ filter_default = log | stop

    Specifies what to do with an event if all filters - return ignore.

    + return ignore, or if no filters exist.

    See section Filters for more information about how this option is used.

    Default is log.

    @@ -553,14 +655,14 @@
    Backwards Compatibility with error_logger -

    Logger provides backwards compatibility with the old +

    Logger provides backwards compatibility with error_logger in the following ways:

    API for Logging -

    The old error_logger API still exists, but should - only be used by legacy code. It will be removed in a later +

    The error_logger API still exists, but should only + be used by legacy code. It will be removed in a later release.

    Calls to @@ -585,7 +687,7 @@

    To get log events on the same format as produced by error_logger_tty_h and error_logger_file_h, use the default formatter, logger_formatter, with - configuration parameter legacy_header=>true. This is + configuration parameter legacy_header => true. This is also the default.

    Default Format of Log Events from OTP @@ -608,10 +710,11 @@ configuration parameters.

    Due to the specific event handlers, the output format slightly differed from other log events.

    -

    As of OTP-21, the concept of SASL reports is removed, - meaning that the default behaviour is as follows:

    +

    As of Erlang/OTP 21.0, the concept of SASL reports is + removed, meaning that the default behaviour is as + follows:

    - Supervisor reports, crash reports and progress reports + Supervisor reports, crash reports, and progress reports are no longer connected to the SASL application. Supervisor reports and crash reports are logged by default. @@ -626,15 +729,15 @@ parameter logger_sasl_compatible can be set to true. The - old SASL + SASL configuration parameters can then be used as before, and the SASL reports will only be printed if the SASL application is running, through a second log handler named sasl.

    All SASL reports have a metadata - field domain=>[beam,erlang,otp,sasl], which can be + field domain => [beam,erlang,otp,sasl], which can be used, for example, by filters to stop or allow the - events.

    + log events.

    See the SASL User's Guide for more information about the old SASL error logging functionality.

    @@ -650,14 +753,14 @@ error_logger:add_report_handler/1,2. event manager, and add error_logger as a handler to logger, with configuration

    -#{level=>info, - filter_default=>log, - filters=>[]}. +#{level => info, + filter_default => log, + filters => []}.

    Notice that this handler will ignore events that do not - originate from the old error_logger API, or from - within OTP. This means that if your code uses the Logger API - for logging, then your log events will be discarded by this + originate from the error_logger API, or from within + OTP. This means that if your code uses the Logger API for + logging, then your log events will be discarded by this handler.

    Also notice that error_logger is not overload protected.

    @@ -676,8 +779,9 @@ error_logger:add_report_handler/1,2. arguments by the handler. If a report is given, a default report callback can be included in the log event's metadata. The handler can use this callback for converting the report to a - format string and arguments. The handler might also do a custom - conversion if the default format is not desired.

    + format string and arguments. If the format obtained by the + provided callback is not desired, or if there is no provided + callback, the handler must do a custom conversion.

    Logger does, to a certain extent, check its input data before forwarding a log event to the handlers, but it does not evaluate conversion funs or check the validity of format strings @@ -686,18 +790,17 @@ error_logger:add_report_handler/1,2. that it does not crash due to bad input data or faulty callbacks.

    If a filter or handler still crashes, Logger will remove the - filter or handler in question from the configuration, and then - print a short error message on the console. A debug event - containing the crash reason and other details is also issued, - and can be seen if a handler is installed which logs on debug - level.

    + filter or handler in question from the configuration, and print + a short error message to the terminal. A debug event containing + the crash reason and other details is also issued, and can be + seen if a handler logging debug events is installed.

    Example: add a handler to log debug events to file -

    When starting an erlang node, the default behaviour is that all +

    When starting an Erlang node, the default behaviour is that all log events with level info and above are logged to the - console. In order to also log debug events, you can either + terminal. In order to also log debug events, you can either change the global log level to debug or add a separate handler to take care of this. In this example we will add a new handler which prints the debug events to a separate file.

    @@ -705,28 +808,32 @@ error_logger:add_report_handler/1,2. type {file,File}, and we set the handler's level to debug:

    -1> Config = #{level=>debug,logger_std_h=>#{type=>{file,"./debug.log"}}}.
    +1> Config = #{level => debug, logger_std_h => #{type => {file,"./debug.log"}}}.
     #{logger_std_h => #{type => {file,"./debug.log"}},
       level => debug}
     2> logger:add_handler(debug_handler,logger_std_h,Config).
     ok

    By default, the handler receives all events - (filter_defalt=log), so we need to add a filter to stop - all non-debug events:

    + (filter_default=log, see + section Filters for more + details), so we need to add a filter to stop all non-debug + events. The built-in + filter + logger_filters:level/2 + is used for this:

    -3> Fun = fun(#{level:=debug}=Log,_) -> Log; (_,_) -> stop end.
    -#Fun<erl_eval.12.98642416>
    -4> logger:add_handler_filter(debug_handler,allow_debug,{Fun,[]}).
    +3> logger:add_handler_filter(debug_handler,stop_non_debug,
    +                             {fun logger_filters:level/2,{stop,neq,debug}}).
     ok

    And finally, we need to make sure that Logger itself allows debug events. This can either be done by setting the global log level:

    -5> logger:set_logger_config(level,debug).
    +4> logger:set_logger_config(level,debug).
     ok

    Or by allowing debug events from one or a few modules only:

    -6> logger:set_module_level(mymodule,debug).
    +5> logger:set_module_level(mymodule,debug).
     ok
    @@ -736,29 +843,31 @@ ok

    The only requirement that a handler MUST fulfill is to export the following function:

    log(logger:log_event(),logger:config()) -> ok -

    It may also implement the following callbacks:

    +

    It can optionally also implement the following callbacks:

    adding_handler(logger:config()) -> {ok,logger:config()} | {error,term()} removing_handler(logger:config()) -> ok changing_config(logger:config(),logger:config()) -> {ok,logger:config()} | {error,term()} -

    When logger:add_handler(Id,Module,Config) is called, Logger - will first call HModule:adding_handler(Config), and if it - returns {ok,NewConfig}, NewConfig is written to the - configuration database. After this, the handler may receive log - events as calls to HModule:log/2.

    +

    When logger:add_handler(Id,Module,Config) is called, + Logger first calls HModule:adding_handler(Config). If + this function returns {ok,NewConfig}, Logger + writes NewConfig to the configuration database, and + the logger:add_handler/3 call returns. After this, the + handler is installed and must be ready to receive log events as + calls to HModule:log/2.

    A handler can be removed by calling - logger:remove_handler(Id). Logger will call - HModule:removing_handler(Config), and then remove the + logger:remove_handler(Id). Logger calls + HModule:removing_handler(Config), and removes the handler's configuration from the configuration database.

    When logger:set_handler_config/2,3 - or logger:update_handler_config/2 are called, Logger + or logger:update_handler_config/2 is called, Logger calls HModule:changing_config(OldConfig,NewConfig). If - this function returns {ok,NewConfig}, NewConfig is - written to the configuration database.

    + this function returns {ok,NewConfig}, Logger + writes NewConfig to the configuration database.

    -

    A simple handler that prints to the console could be - implemented as follows:

    +

    A simple handler that prints to the terminal can be implemented + as follows:

    -module(myhandler). -export([log/2]). @@ -776,7 +885,7 @@ log(LogEvent,#{formatter:={FModule,FConfig}) -> adding_handler(Config) -> {ok,Fd} = file:open(File,[append,{encoding,utf8}]), - {ok,Config#{myhandler_fd=>Fd}}. + {ok,Config#{myhandler_fd => Fd}}. removing_handler(#{myhandler_fd:=Fd}) -> _ = file:close(Fd), @@ -790,10 +899,12 @@ log(LogEvent,#{myhandler_fd:=Fd,formatter:={FModule,FConfig}}) -> protection, and all log events are printed directly from the client process.

    -

    For examples of overload protection, please refer to the - implementation - of logger_std_h - and logger_disk_log_h +

    For information and examples of overload protection, please + refer to + section Protecting the + Handler from Overload, and the implementation + of logger_std_h(3) + and logger_disk_log_h(3) .

    Below is a simpler example of a handler which logs through one @@ -805,7 +916,7 @@ log(LogEvent,#{myhandler_fd:=Fd,formatter:={FModule,FConfig}}) -> adding_handler(Config) -> {ok,Pid} = gen_server:start(?MODULE,Config), - {ok,Config#{myhandler_pid=>Pid}}. + {ok,Config#{myhandler_pid => Pid}}. removing_handler(#{myhandler_pid:=Pid}) -> gen_server:stop(Pid). @@ -815,7 +926,7 @@ log(LogEvent,#{myhandler_pid:=Pid} = Config) -> init(#{myhandler_file:=File}) -> {ok,Fd} = file:open(File,[append,{encoding,utf8}]), - {ok,#{file=>File,fd=>Fd}}. + {ok,#{file => File, fd => Fd}}. handle_call(_,_,State) -> {reply,{error,bad_request},State}. @@ -949,7 +1060,7 @@ logger:add_handler(my_standard_h, logger_std_h, both built-in handlers offer the possibility to set a maximum level of how many requests to process with a certain time frame. With this burst control feature enabled, the handler will take care of bursts of log requests - without choking log files, or the console, with massive amounts of + without choking log files, or the terminal, with massive amounts of printouts. These are the configuration parameters:

    diff --git a/lib/kernel/doc/src/logger_formatter.xml b/lib/kernel/doc/src/logger_formatter.xml index ee43634cf9..02f89b26be 100644 --- a/lib/kernel/doc/src/logger_formatter.xml +++ b/lib/kernel/doc/src/logger_formatter.xml @@ -46,97 +46,150 @@ Logger.

    -
    - Configuration -

    The configuration term for logger_formatter is a - map, - and the following keys can be set as configuration - parameters:

    - - chars_limit = pos_integer() | unlimited - -

    A positive integer representing the value of the option - with the same name to be used when calling - io_lib:format/3. - 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 max_size.

    -

    Default is unlimited.

    - -

    chars_limit has no effect on log messages on - string form. These are expected to be short, but can still - be truncated by the max_size parameter.

    -
    -
    - depth = pos_integer() | unlimited - -

    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 - io:format/2,3 - in STDLIB.

    -

    Default is unlimited.

    - -

    depth has no effect on log messages on string - form. These are expected to be short, but can still be - truncated by the max_size parameter.

    -
    -
    - max_size = pos_integer() | unlimited - -

    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 chars_limit or depth, it is truncated.

    -

    Default is unlimited.

    -
    - single_line = boolean() - -

    If set to true, all newlines in the message are - replaced with ", ", and whitespaces following - directly after newlines are removed. Note that newlines - added by the template parameter are not replaced.

    -

    Default is true.

    -
    - legacy_header = boolean() - -

    If set to true a header field is added to - logger_formatter's part of Metadata. The value of - this field is a string similar to the header created by the - old error_logger event handlers. It can be included - in the log event by adding the - tuple {logger_formatter,header} to the template. See - section Default - Templates for more information.

    -

    Default is false.

    -
    - report_cb = fun((logger:report()) -> {io:format(),[term()]}) - -

    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, logger_formatter will - use - logger:format_report/1 as default - callback.

    -

    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.

    -

    The value must be a function with arity 1, - returning {Format,Args}, and it will be called with a - report as only argument.

    -
    - template = template() - + + + + + +

    The configuration term for logger_formatter is a + map, and the + following keys can be set as configuration parameters:

    + + chars_limit = pos_integer() | unlimited + +

    A positive integer representing the value of the option + with the same name to be used when calling + + io_lib:format/3. + 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 max_size.

    +

    Defaults to unlimited.

    + +

    chars_limit has no effect on log messages on + string form. These are expected to be short, but can + still be truncated by the max_size + parameter.

    +
    +
    + depth = pos_integer() | unlimited + +

    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 + io:format/2,3 + in STDLIB.

    +

    Defaults to unlimited.

    + +

    depth has no effect on log messages on string + form. These are expected to be short, but can still be + truncated by the max_size parameter.

    +
    +
    + legacy_header = boolean() + +

    If set to true a header field is added to + logger_formatter's part of Metadata. The value of + this field is a string similar to the header created by + the old error_logger event handlers. It can be + included in the log event by adding the + tuple {logger_formatter,header} to the + template. See the description of + the template() + type for more information.

    +

    Defaults to false.

    +
    + max_size = pos_integer() | unlimited + +

    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 chars_limit or depth, it is truncated.

    +

    Defaults to unlimited.

    +
    + report_cb = fun((logger:report()) -> {io:format(),[term()]}) + +

    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, logger_formatter will + use + logger:format_report/1 as default + callback.

    +

    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.

    +

    The value must be a function with arity 1, + returning {Format,Args}, and it will be called + with a report as only argument.

    +
    + single_line = boolean() + +

    If set to true, all newlines in the message are + replaced with ", ", and whitespaces following + directly after newlines are removed. Note that newlines + added by the template parameter are not replaced.

    +

    Defaults to true.

    +
    + template = template() + +

    The template describes how the formatted string is + composed by combining different data values from the log + event. See the description of + the template() + type for more information about this.

    +
    + time_designator = byte() + +

    Timestamps are formatted according to RFC3339, and the + time designator is the character used as date and time + separator.

    +

    Defaults to $T.

    +

    The value of this parameter is used as + the time_designator option + to + calendar:system_time_to_rcf3339/2.

    +
    + time_offset = integer() | [byte()] + +

    The time offset, either a string or an integer, to be + used when formatting the timestamp.

    +

    An empty string is interpreted as local time. The + values "Z", "z" or 0 are + interpreted as Universal Coordinated Time (UTC).

    +

    Strings, other than "Z", "z", + or "", must be on the form ±[hh]:[mm], for + example "-02:00" or "+00:00".

    +

    Integers must be in microseconds, meaning that the + offset 7200000000 is equivalent + to "+02:00".

    +

    Defaults to an empty string, meaning that timestamps + are displayed in local time. However, for backwards + compatibility, if the SASL configuration + parameter + utc_log=true, the default is + changed to "Z", meaning that timestamps are displayed + in UTC.

    +

    The value of this parameter is used as + the offset option + to + calendar:system_time_to_rcf3339/2.

    +
    +
    +
    +
    + + +

    The template is a list of atoms, tuples and strings. The atoms level or msg, are treated as placeholders for the severity level and the log message, - repectively. Other atoms or tuples are interpreted as + respectively. 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 @@ -154,120 +207,84 @@

    Strings in the template are printed literally.

    The default template differs depending on the values of legacy_header - and single_line. See Default - Templates for more information

    -
    - time_designator = byte() - -

    Timestamps are formatted according to RFC3339, and the time - designator is the character used as date and time - separator.

    -

    Default is $T.

    -

    The value of this parameter is used as - the time_designator option - to - calendar:system_time_to_rcf3339/2.

    -
    - time_offset = integer() | [byte()] - -

    The time offset, either a string or an integer, to be - used when formatting the timestamp.

    -

    An empty string is interpreted as local time. The - values "Z", "z" or 0 are interpreted as - Universal Coordinated Time (UTC).

    -

    Strings, other than "Z", "z", or "", - must be on the form ±[hh]:[mm], for - example "-02:00" or "+00:00".

    -

    Integers must be in microseconds, meaning that the - offset 7200000000 is equivalent - to "+02:00".

    -

    The default value is an empty string, meaning that - timestamps are displayed in local time. However, for - backwards compatibility, if the SASL configuration - parameter - utc_log=true, the default is - changed to "Z", meaning that timestamps are displayed - in UTC.

    -

    The value of this parameter is used as the offset - option to - calendar:system_time_to_rcf3339/2.

    -
    -
    -
    - -
    - - Default templates + and single_line:

    -

    The default value for the template configuration - parameter depends on the value of single_line - and legacy_header as follows.

    +

    The default value for the template configuration + parameter depends on the value of the single_line + and legacy_header configuration parameters as + follows.

    -

    The log event used in the examples is:

    - +

    The log event used in the examples is:

    + ?LOG_ERROR("name: ~p~nexit_reason: ~p",[my_name,"It crashed"]) - - legacy_header=true, single_line=false - -

    Default template: [{logger_formatter,header},"\n",msg,"\n"]

    + + legacy_header=true, single_line=false + +

    Default + template: [{logger_formatter,header},"\n",msg,"\n"]

    -

    Example log entry:

    - +

    Example log entry:

    + =ERROR REPORT==== 17-May-2018::18:30:19.453447 === name: my_name exit_reason: "It crashed" -

    Notice that all eight levels can occur in the heading, - not only ERROR, WARNING or INFO as the - old error_logger produced. And microseconds are - added at the end of the timestamp.

    -
    +

    Notice that all eight levels can occur in the heading, + not only ERROR, WARNING or INFO as the + old error_logger produced. And microseconds are + added at the end of the timestamp.

    +
    - legacy_header=true, single_line=true - -

    Default template: [{logger_formatter,header},"\n",msg,"\n"]

    + legacy_header=true, single_line=true + +

    Default + template: [{logger_formatter,header},"\n",msg,"\n"]

    -

    Notice that the template is here the same as - for single_line=false, but the resulting log entry - differs in that there is only one line after the - heading:

    - +

    Notice that the template is here the same as + for single_line=false, but the resulting log entry + differs in that there is only one line after the + heading:

    + =ERROR REPORT==== 17-May-2018::18:31:06.952665 === name: my_name, exit_reason: "It crashed" -
    +
    - legacy_header=false, single_line=true - -

    Default template: [time," ",level,": ",msg,"\n"]

    + legacy_header=false, single_line=true + +

    Default template: [time," ",level,": ",msg,"\n"]

    -

    Example log entry:

    - +

    Example log entry:

    + 2018-05-17T18:31:31.152864+02:00 error: name: my_name, exit_reason: "It crashed" -
    +
    - legacy_header=false, single_line=false - -

    Default template: [time," ",level,":\n",msg,"\n"]

    + legacy_header=false, single_line=false + +

    Default template: [time," ",level,":\n",msg,"\n"]

    -

    Example log entry:

    - +

    Example log entry:

    + 2018-05-17T18:32:20.105422+02:00 error: name: my_name exit_reason: "It crashed" -
    -
    -
    - - - - - +
    + + + + Validates the given formatter configuration. + +

    This callback function is called by Logger when the + formatter configuration for a handler is set or modified. It + returns ok if the configuration is valid, + and {error,term()} if it is faulty.

    +
    +
    Formats the given message. @@ -290,7 +307,6 @@ exit_reason: "It crashed"
    - diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 12f8dd77cf..3beb3102fa 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -81,7 +81,9 @@ mfa => {module(),atom(),non_neg_integer()}, file => file:filename(), line => non_neg_integer(), - term() => term()}. + domain => [atom()], + report_cb => fun((report()) -> {io:format(),[term()]}), + atom() => term()}. -type location() :: #{mfa := {module(),atom(),non_neg_integer()}, file := file:filename(), line := non_neg_integer()}. diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl index 95cb6e35fc..4d727b3da0 100644 --- a/lib/kernel/src/logger_formatter.erl +++ b/lib/kernel/src/logger_formatter.erl @@ -26,21 +26,22 @@ %%%----------------------------------------------------------------- %%% Types +-type config() :: #{chars_limit=>pos_integer()| unlimited, + depth=>pos_integer() | unlimited, + legacy_header=>boolean(), + max_size=>pos_integer() | unlimited, + report_cb=>fun((logger:report()) -> {io:format(),[term()]}), + single_line=>boolean(), + template=>template(), + time_designator=>byte(), + time_offset=>integer()|[byte()]}. -type template() :: [atom()|tuple()|string()]. %%%----------------------------------------------------------------- %%% API -spec format(LogEvent,Config) -> unicode:chardata() when LogEvent :: logger:log_event(), - Config :: #{single_line=>boolean(), - legacy_header=>boolean(), - report_cb=>fun((logger:report()) -> {io:format(),[term()]}), - chars_limit=>pos_integer()| unlimited, - max_size=>pos_integer() | unlimited, - depth=>pos_integer() | unlimited, - template=>template(), - time_designator=>byte(), - time_offset=>integer()|[byte()]}. + Config :: config(). format(#{level:=Level,msg:=Msg0,meta:=Meta},Config0) when is_map(Config0) -> Config = add_default_config(Config0), @@ -322,6 +323,8 @@ offset_to_utc([$+|Tz]) -> offset_to_utc(_) -> false. +-spec check_config(Config) -> ok | {error,term()} when + Config :: config(). check_config(Config) when is_map(Config) -> do_check_config(maps:to_list(Config)); check_config(Config) -> -- cgit v1.2.3 From dfc935298bccbe9d183b3260a51c17946688a392 Mon Sep 17 00:00:00 2001 From: Kenneth Lundin Date: Wed, 23 May 2018 15:28:58 +0200 Subject: Update logger.xml corrected spelling reffering -> referring --- lib/kernel/doc/src/logger.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index 183102f240..2ee1059df8 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -528,7 +528,7 @@ Current logger configuration:

    Add a handler with the given configuration.

    HandlerId is a unique identifier which - must be used in all subsequent calls reffering to this + must be used in all subsequent calls referring to this handler.

    -- cgit v1.2.3 From fd8e49b5bddceaae803670121b603b5eee8c5c08 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 25 May 2018 12:14:27 +0200 Subject: Prepare release --- lib/kernel/doc/src/notes.xml | 256 +++++++++++++++++++++++++++++++++++++++++++ lib/kernel/vsn.mk | 2 +- 2 files changed, 257 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 09844f1502..3eda30f981 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,262 @@

    This document describes the changes made to the Kernel application.

    +
    Kernel 6.0 + +
    Fixed Bugs and Malfunctions + + +

    Clarify the documentation of rpc:multicall/5. +

    +

    + Own Id: OTP-10551

    +
    + +

    + The DNS resolver when getting econnrefused from a server + retained an invalid socket so look up towards the next + server(s) also failed.

    +

    + Own Id: OTP-13133 Aux Id: PR-1557

    +
    + +

    + No resolver backend returns V4Mapped IPv6 addresses any + more. This was inconsistent before, some did, some did + not. To facilitate working with such addresses a new + function inet:ipv4_mapped_ipv6_address/1 has been + added.

    +

    + *** POTENTIAL INCOMPATIBILITY ***

    +

    + Own Id: OTP-13761 Aux Id: ERL-503

    +
    + +

    + The type specifications for file:posix/0 and + inet:posix/0 have been updated according to which + errors file and socket operations should be able to + return.

    +

    + Own Id: OTP-14019 Aux Id: ERL-550

    +
    + +

    + Fix name resolving in IPv6 only environments when doing + the initial distributed connection.

    +

    + Own Id: OTP-14501

    +
    + +

    File operations used to accept filenames + containing null characters (integer value zero). This + caused the name to be truncated and in some cases + arguments to primitive operations to be mixed up. + Filenames containing null characters inside the filename + are now rejected and will cause primitive file + operations to fail.

    Also environment variable + operations used to accept names and + values of + environment variables containing null characters (integer + value zero). This caused operations to silently produce + erroneous results. Environment variable names and values + containing null characters inside the name or value are + now rejected and will cause environment variable + operations to fail.

    Primitive environment + variable operations also used to accept the $= + character in environment variable names causing various + problems. $= characters in environment variable + names are now also rejected.

    Also + os:cmd/1 now + reject null characters inside its command. +

    erlang:open_port/2 + will also reject null characters inside the port name + from now on.

    +

    + *** POTENTIAL INCOMPATIBILITY ***

    +

    + Own Id: OTP-14543 Aux Id: ERL-370

    +
    + +

    os:putenv and os:getenv no longer access + the process environment directly and instead work on a + thread-safe emulation. The only observable difference is + that it's not kept in sync with libc + getenv(3) / putenv(3), so those who relied + on that behavior in drivers or NIFs will need to add + manual synchronization.

    On Windows this means that + you can no longer resolve DLL dependencies by modifying + the PATH just before loading the driver/NIF. To + make this less of a problem, the emulator now adds the + target DLL's folder to the DLL search path.

    +

    + *** POTENTIAL INCOMPATIBILITY ***

    +

    + Own Id: OTP-14666

    +
    + +

    + Fixed connection tick toward primitive hidden nodes + (erl_interface) that could cause faulty tick timeout in + rare cases when payload data is sent to hidden node but + not received.

    +

    + Own Id: OTP-14681

    +
    + +

    + Make group react immediately on an EXIT-signal from shell + in e.g ssh.

    +

    + Own Id: OTP-14991 Aux Id: PR1705

    +
    + +

    + Calls to gen_tcp:send/2 on closed sockets now + returns {error, closed} instead of + {error,enotconn}.

    +

    + Own Id: OTP-15001

    +
    + +

    + The included_applications key are no longer + duplicated as application environment variable. Earlier, + the included applications could be read both with + application:get[_all]_env(...) and + application:get[_all]_key(...) functions. Now, it + can only be read with + application:get[_all]_key(...).

    +

    + *** POTENTIAL INCOMPATIBILITY ***

    +

    + Own Id: OTP-15071

    +
    +
    +
    + + +
    Improvements and New Features + + +

    A new logging API is added to Erlang/OTP, see the + logger(3) manual + page, and section Logging in the + Kernel User's Guide.

    +

    Calls to error_logger are automatically + redirected to the new API, and legacy error logger event + handlers can still be used. It is, however, recommended + to use the Logger API directly when writing new code.

    +

    Notice the following potential incompatibilities:

    +

    Kernel configuration parameters + error_logger still works, but is overruled if the + default handler's output destination is configured with + Kernel configuration parameter logger.

    In + general, parameters for configuring error logger are + overwritten by new parameters for configuring + Logger.

    The concept of SASL error + logging is deprecated, meaning that by default the SASL + application does not affect which log events are + logged.

    By default, supervisor reports and crash + reports are logged by the default Logger handler started + by Kernel, and end up at the same destination (terminal + or file) as other standard log event from Erlang/OTP.

    +

    Progress reports are not logged by default, but can be + enabled with the Kernel configuration parameter + logger_progress_reports.

    To obtain + backwards compatibility with the SASL error logging + functionality from earlier releases, set Kernel + configuration parameter logger_sasl_compatible to + true. This prevents the default Logger handler + from logging any supervisor-, crash-, or progress + reports. Instead, SASL adds a separate Logger handler + during application start, which takes care of these log + events. The SASL configuration parameters + sasl_error_logger and sasl_errlog_type + specify the destination (terminal or file) and severity + level to log for these events.

    +

    + *** POTENTIAL INCOMPATIBILITY ***

    +

    + Own Id: OTP-13295

    +
    + +

    + The function inet:i/0 has been documented.

    +

    + Own Id: OTP-13713 Aux Id: PR-1645

    +
    + +

    + Typespecs for netns and bind_to_device + options have been added to gen_tcp, gen_udp + and gen_sctp functions.

    +

    + Own Id: OTP-14359 Aux Id: PR-1816

    +
    + +

    + New functionality for implementation of alternative + carriers for the Erlang distribution has been introduced. + This mainly consists of support for usage of distribution + controller processes (previously only ports could be used + as distribution controllers). For more information see + ERTS + User's Guide ➜ How to implement an Alternative Carrier + for the Erlang Distribution ➜ Distribution + Module.

    +

    + Own Id: OTP-14459

    +
    + +

    seq_trace labels may now be any erlang + term.

    +

    + Own Id: OTP-14899

    +
    + +

    + The SSL distribution protocol -proto inet_tls has + stopped setting the SSL option + server_name_indication. New verify funs for client + and server in inet_tls_dist has been added, not + documented yet, that checks node name if present in peer + certificate. Usage is still also yet to be documented.

    +

    + Own Id: OTP-14969 Aux Id: OTP-14465, ERL-598

    +
    + +

    + Changed timeout of gen_server calls to auth + server from default 5 seconds to infinity.

    +

    + Own Id: OTP-15009 Aux Id: ERL-601

    +
    + +

    The callback module passed as -epmd_module to + erl has been expanded to be able to do name and port + resolving.

    Documentation has also been added in + the erl_epmd + reference manual and ERTS User's Guide How to Implement an Alternative + Service Discovery for Erlang Distribution.

    +

    + Own Id: OTP-15086 Aux Id: PR-1694

    +
    +
    +
    + +
    +
    Kernel 5.4.3
    Fixed Bugs and Malfunctions diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 60a1b0bff8..aa8e4dc119 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 5.4.3 +KERNEL_VSN = 6.0 -- cgit v1.2.3 From 3ac7567e7565df4bcba589052e524aaa74d21c1c Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Mon, 28 May 2018 13:11:51 +0200 Subject: kernel: Improve contracts --- lib/kernel/src/code.erl | 4 ++-- lib/kernel/src/file_server.erl | 6 +++--- lib/kernel/src/kernel_refc.erl | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index f143a49d2f..7faef93609 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. @@ -630,7 +630,7 @@ do_par_recv(N, Good, Bad) -> call(Req) -> code_server:call(Req). --spec start_link() -> {'ok', pid()} | {'error', 'crash'}. +-spec start_link() -> {'ok', pid()}. start_link() -> do_start(). diff --git a/lib/kernel/src/file_server.erl b/lib/kernel/src/file_server.erl index ecc1ffbdd6..29eaa23375 100644 --- a/lib/kernel/src/file_server.erl +++ b/lib/kernel/src/file_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2017. All Rights Reserved. +%% Copyright Ericsson AB 2000-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. @@ -73,7 +73,7 @@ stop() -> %% {stop, Reason} %%---------------------------------------------------------------------- --spec init([]) -> {'ok', state()} | {'stop', term()}. +-spec init([]) -> {'ok', state()}. init([]) -> process_flag(trap_exit, true), @@ -225,7 +225,7 @@ handle_cast(Msg, State) -> %%---------------------------------------------------------------------- -spec handle_info(term(), state()) -> - {'noreply', state()} | {'stop', 'normal', state()}. + {'noreply', state()}. handle_info({'EXIT', Pid, _Reason}, State) when is_pid(Pid) -> ets:delete(?FILE_IO_SERVER_TABLE, Pid), diff --git a/lib/kernel/src/kernel_refc.erl b/lib/kernel/src/kernel_refc.erl index 05076dc885..8e04ff99d8 100644 --- a/lib/kernel/src/kernel_refc.erl +++ b/lib/kernel/src/kernel_refc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2017. All Rights Reserved. +%% Copyright Ericsson AB 2017-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. @@ -44,7 +44,7 @@ scheduler_wall_time(Bool) -> %% Callback functions from gen_server %%----------------------------------------------------------------- --spec init([]) -> {'ok', map()} | {'stop', term()}. +-spec init([]) -> {'ok', map()}. init([]) -> resource(scheduler_wall_time, false), -- cgit v1.2.3 From b9faca269de1de51ae33577cc204befbea24243c Mon Sep 17 00:00:00 2001 From: Henrik Date: Tue, 29 May 2018 10:47:25 +0200 Subject: Revert "Prepare release" This reverts commit fd8e49b5bddceaae803670121b603b5eee8c5c08. --- lib/kernel/doc/src/notes.xml | 256 ------------------------------------------- lib/kernel/vsn.mk | 2 +- 2 files changed, 1 insertion(+), 257 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 3eda30f981..09844f1502 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,262 +31,6 @@

    This document describes the changes made to the Kernel application.

    -
    Kernel 6.0 - -
    Fixed Bugs and Malfunctions - - -

    Clarify the documentation of rpc:multicall/5. -

    -

    - Own Id: OTP-10551

    -
    - -

    - The DNS resolver when getting econnrefused from a server - retained an invalid socket so look up towards the next - server(s) also failed.

    -

    - Own Id: OTP-13133 Aux Id: PR-1557

    -
    - -

    - No resolver backend returns V4Mapped IPv6 addresses any - more. This was inconsistent before, some did, some did - not. To facilitate working with such addresses a new - function inet:ipv4_mapped_ipv6_address/1 has been - added.

    -

    - *** POTENTIAL INCOMPATIBILITY ***

    -

    - Own Id: OTP-13761 Aux Id: ERL-503

    -
    - -

    - The type specifications for file:posix/0 and - inet:posix/0 have been updated according to which - errors file and socket operations should be able to - return.

    -

    - Own Id: OTP-14019 Aux Id: ERL-550

    -
    - -

    - Fix name resolving in IPv6 only environments when doing - the initial distributed connection.

    -

    - Own Id: OTP-14501

    -
    - -

    File operations used to accept filenames - containing null characters (integer value zero). This - caused the name to be truncated and in some cases - arguments to primitive operations to be mixed up. - Filenames containing null characters inside the filename - are now rejected and will cause primitive file - operations to fail.

    Also environment variable - operations used to accept names and - values of - environment variables containing null characters (integer - value zero). This caused operations to silently produce - erroneous results. Environment variable names and values - containing null characters inside the name or value are - now rejected and will cause environment variable - operations to fail.

    Primitive environment - variable operations also used to accept the $= - character in environment variable names causing various - problems. $= characters in environment variable - names are now also rejected.

    Also - os:cmd/1 now - reject null characters inside its command. -

    erlang:open_port/2 - will also reject null characters inside the port name - from now on.

    -

    - *** POTENTIAL INCOMPATIBILITY ***

    -

    - Own Id: OTP-14543 Aux Id: ERL-370

    -
    - -

    os:putenv and os:getenv no longer access - the process environment directly and instead work on a - thread-safe emulation. The only observable difference is - that it's not kept in sync with libc - getenv(3) / putenv(3), so those who relied - on that behavior in drivers or NIFs will need to add - manual synchronization.

    On Windows this means that - you can no longer resolve DLL dependencies by modifying - the PATH just before loading the driver/NIF. To - make this less of a problem, the emulator now adds the - target DLL's folder to the DLL search path.

    -

    - *** POTENTIAL INCOMPATIBILITY ***

    -

    - Own Id: OTP-14666

    -
    - -

    - Fixed connection tick toward primitive hidden nodes - (erl_interface) that could cause faulty tick timeout in - rare cases when payload data is sent to hidden node but - not received.

    -

    - Own Id: OTP-14681

    -
    - -

    - Make group react immediately on an EXIT-signal from shell - in e.g ssh.

    -

    - Own Id: OTP-14991 Aux Id: PR1705

    -
    - -

    - Calls to gen_tcp:send/2 on closed sockets now - returns {error, closed} instead of - {error,enotconn}.

    -

    - Own Id: OTP-15001

    -
    - -

    - The included_applications key are no longer - duplicated as application environment variable. Earlier, - the included applications could be read both with - application:get[_all]_env(...) and - application:get[_all]_key(...) functions. Now, it - can only be read with - application:get[_all]_key(...).

    -

    - *** POTENTIAL INCOMPATIBILITY ***

    -

    - Own Id: OTP-15071

    -
    -
    -
    - - -
    Improvements and New Features - - -

    A new logging API is added to Erlang/OTP, see the - logger(3) manual - page, and section Logging in the - Kernel User's Guide.

    -

    Calls to error_logger are automatically - redirected to the new API, and legacy error logger event - handlers can still be used. It is, however, recommended - to use the Logger API directly when writing new code.

    -

    Notice the following potential incompatibilities:

    -

    Kernel configuration parameters - error_logger still works, but is overruled if the - default handler's output destination is configured with - Kernel configuration parameter logger.

    In - general, parameters for configuring error logger are - overwritten by new parameters for configuring - Logger.

    The concept of SASL error - logging is deprecated, meaning that by default the SASL - application does not affect which log events are - logged.

    By default, supervisor reports and crash - reports are logged by the default Logger handler started - by Kernel, and end up at the same destination (terminal - or file) as other standard log event from Erlang/OTP.

    -

    Progress reports are not logged by default, but can be - enabled with the Kernel configuration parameter - logger_progress_reports.

    To obtain - backwards compatibility with the SASL error logging - functionality from earlier releases, set Kernel - configuration parameter logger_sasl_compatible to - true. This prevents the default Logger handler - from logging any supervisor-, crash-, or progress - reports. Instead, SASL adds a separate Logger handler - during application start, which takes care of these log - events. The SASL configuration parameters - sasl_error_logger and sasl_errlog_type - specify the destination (terminal or file) and severity - level to log for these events.

    -

    - *** POTENTIAL INCOMPATIBILITY ***

    -

    - Own Id: OTP-13295

    -
    - -

    - The function inet:i/0 has been documented.

    -

    - Own Id: OTP-13713 Aux Id: PR-1645

    -
    - -

    - Typespecs for netns and bind_to_device - options have been added to gen_tcp, gen_udp - and gen_sctp functions.

    -

    - Own Id: OTP-14359 Aux Id: PR-1816

    -
    - -

    - New functionality for implementation of alternative - carriers for the Erlang distribution has been introduced. - This mainly consists of support for usage of distribution - controller processes (previously only ports could be used - as distribution controllers). For more information see - ERTS - User's Guide ➜ How to implement an Alternative Carrier - for the Erlang Distribution ➜ Distribution - Module.

    -

    - Own Id: OTP-14459

    -
    - -

    seq_trace labels may now be any erlang - term.

    -

    - Own Id: OTP-14899

    -
    - -

    - The SSL distribution protocol -proto inet_tls has - stopped setting the SSL option - server_name_indication. New verify funs for client - and server in inet_tls_dist has been added, not - documented yet, that checks node name if present in peer - certificate. Usage is still also yet to be documented.

    -

    - Own Id: OTP-14969 Aux Id: OTP-14465, ERL-598

    -
    - -

    - Changed timeout of gen_server calls to auth - server from default 5 seconds to infinity.

    -

    - Own Id: OTP-15009 Aux Id: ERL-601

    -
    - -

    The callback module passed as -epmd_module to - erl has been expanded to be able to do name and port - resolving.

    Documentation has also been added in - the erl_epmd - reference manual and ERTS User's Guide How to Implement an Alternative - Service Discovery for Erlang Distribution.

    -

    - Own Id: OTP-15086 Aux Id: PR-1694

    -
    -
    -
    - -
    -
    Kernel 5.4.3
    Fixed Bugs and Malfunctions diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index aa8e4dc119..60a1b0bff8 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 6.0 +KERNEL_VSN = 5.4.3 -- cgit v1.2.3 From 2df1c47c4bfc86bd65e013e4be377a4484b0a77d Mon Sep 17 00:00:00 2001 From: Artur Cygan Date: Sat, 2 Jun 2018 19:13:57 +0200 Subject: Fix typo in inet doc --- lib/kernel/doc/src/inet.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index 9552332948..e6a7962c5a 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -1149,7 +1149,7 @@ setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp gen_tcp:recv/2 gets {error, closed}. In active mode, the controlling process receives a - {tcp_close, Socket} message, indicating that the + {tcp_closed, Socket} message, indicating that the peer has closed the connection.

    Setting this option to true allows you to distinguish between a connection that was closed normally, -- cgit v1.2.3 From cfd29269fa1bbcf16ef38273e6f0c91d727f8c06 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 4 Jun 2018 17:40:31 +0200 Subject: kernel: Add LOGGER_SERVER_TAG to logger_server This makes recursive calls to not dead-lock logger_server. --- lib/kernel/src/logger_server.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index 275b9c476f..c67b3820a8 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -117,6 +117,7 @@ update_config(Owner, Config) -> init([]) -> process_flag(trap_exit, true), + put(?LOGGER_SERVER_TAG,true), Tid = logger_config:new(?LOGGER_TABLE), LoggerConfig = maps:merge(default_config(logger), #{handlers=>[logger_simple]}), -- cgit v1.2.3 From 7a5ae72a5f46059d8c862a4e5964b4799d1fda4b Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Fri, 8 Jun 2018 14:09:49 +0200 Subject: kernel: Use ~0p --- lib/kernel/src/application_controller.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/application_controller.erl b/lib/kernel/src/application_controller.erl index ff5df667b5..6906ad0d6e 100644 --- a/lib/kernel/src/application_controller.erl +++ b/lib/kernel/src/application_controller.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. @@ -2013,5 +2013,5 @@ to_string(Term) -> true -> Term; false -> - lists:flatten(io_lib:format("~134217728p", [Term])) + lists:flatten(io_lib:format("~0p", [Term])) end. -- cgit v1.2.3 From a1c498df4b4ee20172def79c469e6fd8dd41e841 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 8 Jun 2018 14:17:44 +0200 Subject: [logger] Change names of sync functions in built-in handlers logger_std_h:filesync/1 -----> logger_std_h:sync/1 logger_disk_log_h:disk_log_sync/1 -----> logger_disk_log_h:sync/1 --- lib/kernel/src/logger_disk_log_h.erl | 10 ++++---- lib/kernel/src/logger_std_h.erl | 12 ++++----- lib/kernel/test/logger_disk_log_h_SUITE.erl | 40 ++++++++++++++--------------- lib/kernel/test/logger_env_var_SUITE.erl | 2 +- lib/kernel/test/logger_std_h_SUITE.erl | 36 +++++++++++++------------- lib/kernel/test/logger_test_lib.erl | 4 +-- 6 files changed, 52 insertions(+), 52 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index 773aa75bc6..00643b6a84 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -26,7 +26,7 @@ -include("logger_h_common.hrl"). %%% API --export([start_link/3, info/1, disk_log_sync/1, reset/1]). +-export([start_link/3, info/1, sync/1, reset/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, @@ -57,18 +57,18 @@ start_link(Name, Config, HandlerState) -> %%%----------------------------------------------------------------- %%% --spec disk_log_sync(Name) -> ok | {error,Reason} when +-spec sync(Name) -> ok | {error,Reason} when Name :: atom(), Reason :: handler_busy | {badarg,term()}. -disk_log_sync(Name) when is_atom(Name) -> +sync(Name) when is_atom(Name) -> try gen_server:call(Name, disk_log_sync, ?DEFAULT_CALL_TIMEOUT) catch _:{timeout,_} -> {error,handler_busy} end; -disk_log_sync(Name) -> - {error,{badarg,{disk_log_sync,[Name]}}}. +sync(Name) -> + {error,{badarg,{sync,[Name]}}}. %%%----------------------------------------------------------------- %%% diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index 63c3ab2dac..323f4f02e7 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -28,7 +28,7 @@ -include_lib("kernel/include/file.hrl"). %% API --export([start_link/3, info/1, filesync/1, reset/1]). +-export([start_link/3, info/1, sync/1, reset/1]). %% gen_server and proc_lib callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, @@ -58,18 +58,18 @@ start_link(Name, Config, HandlerState) -> %%%----------------------------------------------------------------- %%% --spec filesync(Name) -> ok | {error,Reason} when +-spec sync(Name) -> ok | {error,Reason} when Name :: atom(), Reason :: handler_busy | {badarg,term()}. -filesync(Name) when is_atom(Name) -> +sync(Name) when is_atom(Name) -> try gen_server:call(Name, filesync, ?DEFAULT_CALL_TIMEOUT) catch _:{timeout,_} -> {error,handler_busy} end; -filesync(Name) -> - {error,{badarg,{filesync,[Name]}}}. +sync(Name) -> + {error,{badarg,{sync,[Name]}}}. %%%----------------------------------------------------------------- %%% @@ -790,7 +790,7 @@ sync_dev(Fd, DevName, PrevSyncResult, HandlerName) -> %% don't report same error twice PrevSyncResult; Error -> - logger_h_common:error_notify({HandlerName,filesync,DevName,Error}), + logger_h_common:error_notify({HandlerName,sync,DevName,Error}), Error end. diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 7a1736c814..2b5bbe6aed 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -82,7 +82,7 @@ all() -> bad_input, info_and_reset, reconfig, - disk_log_sync, + sync, disk_log_full, disk_log_wrap, disk_log_events, @@ -124,7 +124,7 @@ create_log(Config) -> formatter=>{?MODULE,self()}}, #{file=>LogFile1}), logger:info("hello", ?domain), - logger_disk_log_h:disk_log_sync(Name1), + logger_disk_log_h:sync(Name1), ct:pal("Checking contents of ~p", [?log_no(LogFile1,1)]), try_read_file(?log_no(LogFile1,1), {ok,<<"hello\n">>}, 5000), @@ -137,7 +137,7 @@ create_log(Config) -> formatter=>{?MODULE,self()}}, #{file=>LogFile2}), logger:info("dummy", ?domain), - logger_disk_log_h:disk_log_sync(Name2), + logger_disk_log_h:sync(Name2), ct:pal("Checking contents of ~p", [?log_no(LogFile2,1)]), try_read_file(?log_no(LogFile2,1), {ok,<<"dummy\n">>}, 5000), @@ -158,7 +158,7 @@ open_existing_log(Config) -> formatter=>{?MODULE,self()}}, #{file=>LogFile1}), logger:info("one", ?domain), - logger_disk_log_h:disk_log_sync(HName), + logger_disk_log_h:sync(HName), ct:pal("Checking contents of ~p", [?log_no(LogFile1,1)]), try_read_file(?log_no(LogFile1,1), {ok,<<"one\n">>}, 5000), logger:info("two", ?domain), @@ -172,7 +172,7 @@ open_existing_log(Config) -> formatter=>{?MODULE,self()}}, #{file=>LogFile1}), logger:info("three", ?domain), - logger_disk_log_h:disk_log_sync(HName), + logger_disk_log_h:sync(HName), try_read_file(?log_no(LogFile1,1), {ok,<<"one\ntwo\nthree\n">>}, 5000), remove_and_stop(HName), try_read_file(?log_no(LogFile1,1), {ok,<<"one\ntwo\nthree\n">>}, 5000). @@ -197,22 +197,22 @@ disk_log_opts(Config) -> {WFileFull,wrap,{Size,2},1} = {Get(file,WInfo1),Get(type,WInfo1), Get(size,WInfo1),Get(current_file,WInfo1)}, logger:info("123", ?domain), - logger_disk_log_h:disk_log_sync(WName), + logger_disk_log_h:sync(WName), timer:sleep(500), 1 = Get(current_file, disk_log:info(WName)), logger:info("45", ?domain), - logger_disk_log_h:disk_log_sync(WName), + logger_disk_log_h:sync(WName), timer:sleep(500), 1 = Get(current_file, disk_log:info(WName)), logger:info("6", ?domain), - logger_disk_log_h:disk_log_sync(WName), + logger_disk_log_h:sync(WName), timer:sleep(500), 2 = Get(current_file, disk_log:info(WName)), logger:info("7890", ?domain), - logger_disk_log_h:disk_log_sync(WName), + logger_disk_log_h:sync(WName), timer:sleep(500), 2 = Get(current_file, disk_log:info(WName)), @@ -230,7 +230,7 @@ disk_log_opts(Config) -> {HFile1Full,halt,infinity} = {Get(file,HInfo1),Get(type,HInfo1), Get(size,HInfo1)}, logger:info("12345", ?domain), - logger_disk_log_h:disk_log_sync(HName1), + logger_disk_log_h:sync(HName1), timer:sleep(500), 1 = Get(no_written_items, disk_log:info(HName1)), @@ -409,8 +409,8 @@ config_fail(cleanup,_Config) -> logger:remove_handler(?MODULE). bad_input(_Config) -> - {error,{badarg,{disk_log_sync,["BadType"]}}} = - logger_disk_log_h:disk_log_sync("BadType"), + {error,{badarg,{sync,["BadType"]}}} = + logger_disk_log_h:sync("BadType"), {error,{badarg,{info,["BadType"]}}} = logger_disk_log_h:info("BadType"), {error,{badarg,{reset,["BadType"]}}} = logger_disk_log_h:reset("BadType"). @@ -497,7 +497,7 @@ reconfig(Config) -> reconfig(cleanup, _Config) -> logger:remove_handler(?MODULE). -disk_log_sync(Config) -> +sync(Config) -> Dir = ?config(priv_dir,Config), File = filename:join(Dir, ?FUNCTION_NAME), Log = lists:concat([File,".1"]), @@ -526,8 +526,8 @@ disk_log_sync(Config) -> %% an automatic disk log sync logger:info("second", ?domain), logger:info("third", ?domain), - %% do explicit disk_log_sync - logger_disk_log_h:disk_log_sync(?MODULE), + %% do explicit sync + logger_disk_log_h:sync(?MODULE), check_tracer(100), %% check that if there's no repeated disk_log_sync active, @@ -568,7 +568,7 @@ disk_log_sync(Config) -> #{filesync_repeat_interval => no_repeat}), check_tracer(100), ok. -disk_log_sync(cleanup,_Config) -> +sync(cleanup,_Config) -> dbg:stop_clear(), logger:remove_handler(?MODULE). @@ -737,7 +737,7 @@ write_failure(Config) -> ct:pal("LogOpts = ~p", [LogOpts = maps:get(log_opts, HState)]), ok = log_on_remote_node(Node, "Logged1"), - rpc:call(Node, logger_disk_log_h, disk_log_sync, [?STANDARD_HANDLER]), + rpc:call(Node, logger_disk_log_h, sync, [?STANDARD_HANDLER]), ?check_no_log, try_read_file(Log, {ok,<<"Logged1\n">>}, ?SYNC_REP_INT), @@ -757,7 +757,7 @@ write_failure(Config) -> rpc:call(Node, ?MODULE, set_result, [disk_log_blog,ok]), ok = log_on_remote_node(Node, "Logged2"), - rpc:call(Node, logger_disk_log_h, disk_log_sync, [?STANDARD_HANDLER]), + rpc:call(Node, logger_disk_log_h, sync, [?STANDARD_HANDLER]), ?check_no_log, try_read_file(Log, {ok,<<"Logged1\nLogged2\n">>}, ?SYNC_REP_INT), ok. @@ -1158,7 +1158,7 @@ restart_after(Config) -> restart_after(cleanup, _Config) -> ok = stop_handler(?MODULE). -%% send handler requests (filesync, info, reset, change_config) +%% send handler requests (sync, info, reset, change_config) %% during high load to verify that sync, dropping and flushing is %% handled correctly. handler_requests_under_load() -> @@ -1171,7 +1171,7 @@ handler_requests_under_load(Config) -> flush_reqs_qlen => 2000, enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), - Pid = spawn_link(fun() -> send_requests(?MODULE, 1, [{disk_log_sync,[]}, + Pid = spawn_link(fun() -> send_requests(?MODULE, 1, [{sync,[]}, {info,[]}, {reset,[]}, {change_config,[]}]) diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl index 601d331fb0..be168a264f 100644 --- a/lib/kernel/test/logger_env_var_SUITE.erl +++ b/lib/kernel/test/logger_env_var_SUITE.erl @@ -539,7 +539,7 @@ logger_many_handlers(Config, Env, LogErr, LogInfo, NumProgress) -> file,% dest 0,% progress in std logger error), % level - ok = rpc:call(Node,logger_std_h,filesync,[info]), + ok = rpc:call(Node,logger_std_h,sync,[info]), {ok, Bin} = file:read_file(LogInfo), ct:log("Log content:~n~s",[Bin]), match(Bin,<<"info:">>,NumProgress+1,info,info), diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index 5764abd063..c6391e55fc 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -108,7 +108,7 @@ all() -> info_and_reset, reconfig, file_opts, - filesync, + sync, write_failure, sync_failure, op_switch_to_sync_file, @@ -391,7 +391,7 @@ crash_std_h(cleanup) -> [test_server:stop_node(Node) || Node <- Nodes]. sync_and_read(Node,disk_log,Log) -> - rpc:call(Node,logger_disk_log_h,disk_log_sync,[?STANDARD_HANDLER]), + rpc:call(Node,logger_disk_log_h,sync,[?STANDARD_HANDLER]), case file:read_file(Log ++ ".1") of {ok,<<>>} -> timer:sleep(5000), @@ -400,7 +400,7 @@ sync_and_read(Node,disk_log,Log) -> Ok end; sync_and_read(Node,file,Log) -> - rpc:call(Node,logger_std_h,filesync,[?STANDARD_HANDLER]), + rpc:call(Node,logger_std_h,sync,[?STANDARD_HANDLER]), case file:read_file(Log) of {ok,<<>>} -> timer:sleep(5000), @@ -410,7 +410,7 @@ sync_and_read(Node,file,Log) -> end. bad_input(_Config) -> - {error,{badarg,{filesync,["BadType"]}}} = logger_std_h:filesync("BadType"), + {error,{badarg,{sync,["BadType"]}}} = logger_std_h:sync("BadType"), {error,{badarg,{info,["BadType"]}}} = logger_std_h:info("BadType"), {error,{badarg,{reset,["BadType"]}}} = logger_std_h:reset("BadType"). @@ -503,7 +503,7 @@ file_opts(cleanup, _Config) -> logger:remove_handler(?MODULE). -filesync(Config) -> +sync(Config) -> Dir = ?config(priv_dir,Config), Log = filename:join(Dir, lists:concat([?FUNCTION_NAME,".log"])), Type = {file,Log}, @@ -536,10 +536,10 @@ filesync(Config) -> {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), + %% do explicit sync + logger_std_h:sync(?MODULE), + %% a second sync should be ignored + logger_std_h:sync(?MODULE), check_tracer(100), %% check that if there's no repeated filesync active, @@ -583,7 +583,7 @@ filesync(Config) -> #{filesync_repeat_interval => no_repeat}), check_tracer(100), ok. -filesync(cleanup, _Config) -> +sync(cleanup, _Config) -> dbg:stop_clear(), logger:remove_handler(?MODULE). @@ -598,7 +598,7 @@ write_failure(Config) -> rpc:call(Node, ?MODULE, set_result, [file_write,ok]), ok = log_on_remote_node(Node, "Logged1"), - rpc:call(Node, logger_std_h, filesync, [?STANDARD_HANDLER]), + rpc:call(Node, logger_std_h, sync, [?STANDARD_HANDLER]), ?check_no_log, try_read_file(Log, {ok,<<"Logged1\n">>}, ?FILESYNC_REP_INT), @@ -616,7 +616,7 @@ write_failure(Config) -> rpc:call(Node, ?MODULE, set_result, [file_write,ok]), ok = log_on_remote_node(Node, "Logged2"), - rpc:call(Node, logger_std_h, filesync, [?STANDARD_HANDLER]), + rpc:call(Node, logger_std_h, sync, [?STANDARD_HANDLER]), ?check_no_log, try_read_file(Log, {ok,<<"Logged1\nLogged2\n">>}, ?FILESYNC_REP_INT), ok. @@ -647,14 +647,14 @@ sync_failure(Config) -> rpc:call(Node, ?MODULE, set_result, [file_datasync,{error,terminated}]), ok = log_on_remote_node(Node, "Cause simple error printout"), - ?check({error,{?STANDARD_HANDLER,filesync,Log,{error,terminated}}}), + ?check({error,{?STANDARD_HANDLER,sync,Log,{error,terminated}}}), ok = log_on_remote_node(Node, "No second error printout"), ?check_no_log, rpc:call(Node, ?MODULE, set_result, [file_datasync,{error,eacces}]), ok = log_on_remote_node(Node, "Cause simple error printout"), - ?check({error,{?STANDARD_HANDLER,filesync,Log,{error,eacces}}}), + ?check({error,{?STANDARD_HANDLER,sync,Log,{error,eacces}}}), rpc:call(Node, ?MODULE, set_result, [file_datasync,ok]), ok = log_on_remote_node(Node, "Logged2"), @@ -1083,7 +1083,7 @@ restart_after(Config) -> restart_after(cleanup, _Config) -> ok = stop_handler(?MODULE). -%% send handler requests (filesync, info, reset, change_config) +%% send handler requests (sync, info, reset, change_config) %% during high load to verify that sync, dropping and flushing is %% handled correctly. handler_requests_under_load() -> @@ -1097,7 +1097,7 @@ handler_requests_under_load(Config) -> flush_reqs_qlen => 2000, enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), - Pid = spawn_link(fun() -> send_requests(?MODULE, 1, [{filesync,[]}, + Pid = spawn_link(fun() -> send_requests(?MODULE, 1, [{sync,[]}, {info,[]}, {reset,[]}, {change_config,[]}]) @@ -1291,8 +1291,8 @@ add_remove_instance_nofile(Type) -> group_leader(group_leader(),Pid), % to get printouts in test log logger:info(M1=?msg,?domain), ?check(M1), - %% check that filesync doesn't do damage even if not relevant - ok = logger_std_h:filesync(?MODULE), + %% check that sync doesn't do damage even if not relevant + ok = logger_std_h:sync(?MODULE), ok = logger:remove_handler(?MODULE), timer:sleep(500), undefined = whereis(?MODULE), diff --git a/lib/kernel/test/logger_test_lib.erl b/lib/kernel/test/logger_test_lib.erl index 4ac05e6480..5ad6336925 100644 --- a/lib/kernel/test/logger_test_lib.erl +++ b/lib/kernel/test/logger_test_lib.erl @@ -52,10 +52,10 @@ log(Node, M, F, A) -> 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]), + rpc:call(Node,logger_disk_log_h,sync,[?STANDARD_HANDLER]), file:read_file(Log ++ ".1"); sync_and_read(Node, file,Log) -> - ok = rpc:call(Node,logger_std_h,filesync,[?STANDARD_HANDLER]), + ok = rpc:call(Node,logger_std_h,sync,[?STANDARD_HANDLER]), file:read_file(Log). -- cgit v1.2.3 From c8c1c67d331b5d1ec8cd3cdad5d8231063d7c5d8 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 8 Jun 2018 14:20:00 +0200 Subject: [logger] Change handler config to accept all levels by default --- lib/kernel/src/logger_server.erl | 2 +- lib/kernel/test/logger_SUITE.erl | 8 +++---- lib/kernel/test/logger_env_var_SUITE.erl | 40 ++++++++++++++++---------------- 3 files changed, 25 insertions(+), 25 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index 47010c9fa5..e066837b63 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -349,7 +349,7 @@ default_config(logger) -> filter_default=>log}; default_config(Id) -> #{id=>Id, - level=>info, + level=>debug, filters=>[], filter_default=>log, formatter=>{?DEFAULT_FORMATTER,#{}}}. diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index f7ec59a7b7..a193ae4011 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -111,7 +111,7 @@ add_remove_handler(_Config) -> [add] = test_server:messages_get(), #{handlers:=Hs} = logger:i(), {value,_,Hs0} = lists:keytake(h1,1,Hs), - {ok,{?MODULE,#{level:=info,filters:=[],filter_default:=log}}} = % defaults + {ok,{?MODULE,#{level:=debug,filters:=[],filter_default:=log}}} = % defaults logger:get_handler_config(h1), ok = logger:set_handler_config(h1,filter_default,stop), [changing_config] = test_server:messages_get(), @@ -209,13 +209,13 @@ add_remove_filter(cleanup,_Config) -> change_config(_Config) -> %% Overwrite handler config - check that defaults are added - ok = logger:add_handler(h1,?MODULE,#{level=>debug,custom=>custom}), - {ok,{?MODULE,#{level:=debug,filter_default:=log,custom:=custom}}} = + ok = logger:add_handler(h1,?MODULE,#{level=>info,custom=>custom}), + {ok,{?MODULE,#{level:=info,filter_default:=log,custom:=custom}}} = logger:get_handler_config(h1), register(callback_receiver,self()), ok = logger:set_handler_config(h1,#{filter_default=>stop}), [changing_config] = test_server:messages_get(), - {ok,{?MODULE,#{level:=info,filter_default:=stop}=C2}} = + {ok,{?MODULE,#{level:=debug,filter_default:=stop}=C2}} = logger:get_handler_config(h1), false = maps:is_key(custom,C2), {error,fail} = logger:set_handler_config(h1,#{conf_call=>fun() -> {error,fail} end}), diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl index be168a264f..ed9e5b795a 100644 --- a/lib/kernel/test/logger_env_var_SUITE.erl +++ b/lib/kernel/test/logger_env_var_SUITE.erl @@ -80,7 +80,7 @@ all() -> default(Config) -> {ok,#{handlers:=Hs},_Node} = setup(Config,[]), {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - info = maps:get(level,StdC), + debug = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), @@ -93,7 +93,7 @@ default_sasl_compatible(Config) -> {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), + debug = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[beam,erlang,otp]}}} = lists:keyfind(domain,1,StdFilters), @@ -105,7 +105,7 @@ default_sasl_compatible(Config) -> 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), + debug = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), @@ -119,7 +119,7 @@ error_logger_tty_sasl_compatible(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), + debug = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[beam,erlang,otp]}}} = lists:keyfind(domain,1,StdFilters), @@ -135,7 +135,7 @@ error_logger_false(Config) -> {logger_level,notice}]), false = lists:keymember(?STANDARD_HANDLER,1,Hs), {simple,logger_simple_h,SimpleC} = lists:keyfind(simple,1,Hs), - info = maps:get(level,SimpleC), + debug = maps:get(level,SimpleC), notice = maps:get(level,L), SimpleFilters = maps:get(filters,SimpleC), {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = @@ -152,7 +152,7 @@ error_logger_false_progress(Config) -> {logger_progress_reports,log}]), false = lists:keymember(?STANDARD_HANDLER,1,Hs), {simple,logger_simple_h,SimpleC} = lists:keyfind(simple,1,Hs), - info = maps:get(level,SimpleC), + debug = maps:get(level,SimpleC), notice = maps:get(level,L), SimpleFilters = maps:get(filters,SimpleC), {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = @@ -169,7 +169,7 @@ error_logger_false_sasl_compatible(Config) -> {logger_sasl_compatible,true}]), false = lists:keymember(?STANDARD_HANDLER,1,Hs), {simple,logger_simple_h,SimpleC} = lists:keyfind(simple,1,Hs), - info = maps:get(level,SimpleC), + debug = maps:get(level,SimpleC), notice = maps:get(level,L), SimpleFilters = maps:get(filters,SimpleC), {domain,{_,{log,super,[beam,erlang,otp]}}} = @@ -218,7 +218,7 @@ logger_file(Config) -> 0),% progress in std logger {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - info = maps:get(level,StdC), + debug = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), @@ -241,7 +241,7 @@ logger_file_sasl_compatible(Config) -> 0),% progress in std logger {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - info = maps:get(level,StdC), + debug = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[beam,erlang,otp]}}} = lists:keyfind(domain,1,StdFilters), @@ -264,7 +264,7 @@ logger_file_log_progress(Config) -> 6),% progress in std logger {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - info = maps:get(level,StdC), + debug = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), @@ -287,7 +287,7 @@ logger_file_no_filter(Config) -> 6),% progress in std logger {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - info = maps:get(level,StdC), + debug = maps:get(level,StdC), [] = maps:get(filters,StdC), false = lists:keymember(simple,1,Hs), false = lists:keymember(sasl,1,Hs), @@ -329,7 +329,7 @@ logger_file_formatter(Config) -> 6),% progress in std logger {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - info = maps:get(level,StdC), + debug = maps:get(level,StdC), [] = maps:get(filters,StdC), false = lists:keymember(simple,1,Hs), false = lists:keymember(sasl,1,Hs), @@ -351,7 +351,7 @@ logger_filters(Config) -> 0),% progress in std logger {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - info = maps:get(level,StdC), + debug = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), @@ -376,11 +376,11 @@ logger_filters_stop(Config) -> ]}]), check_default_log(Node,Log, file,% dest - 0, - notice),% progress in std logger + 0,% progress in std logger + notice), {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - info = maps:get(level,StdC), + debug = maps:get(level,StdC), [] = maps:get(filters,StdC), false = lists:keymember(simple,1,Hs), false = lists:keymember(sasl,1,Hs), @@ -404,7 +404,7 @@ logger_module_level(Config) -> 3),% progress in std logger {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - info = maps:get(level,StdC), + debug = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), @@ -426,7 +426,7 @@ logger_disk_log(Config) -> 0),% progress in std logger {?STANDARD_HANDLER,logger_disk_log_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - info = maps:get(level,StdC), + debug = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), @@ -450,7 +450,7 @@ logger_disk_log_formatter(Config) -> 6),% progress in std logger {?STANDARD_HANDLER,logger_disk_log_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - info = maps:get(level,StdC), + debug = maps:get(level,StdC), [] = maps:get(filters,StdC), false = lists:keymember(simple,1,Hs), false = lists:keymember(sasl,1,Hs), @@ -462,7 +462,7 @@ logger_undefined(Config) -> setup(Config,[{logger,[{handler,?STANDARD_HANDLER,undefined}]}]), false = lists:keymember(?STANDARD_HANDLER,1,Hs), {simple,logger_simple_h,SimpleC} = lists:keyfind(simple,1,Hs), - info = maps:get(level,SimpleC), + debug = maps:get(level,SimpleC), info = maps:get(level,L), SimpleFilters = maps:get(filters,SimpleC), {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = -- cgit v1.2.3 From ba4b23c2d1cd07d754518201a5686f09cb1332d2 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 8 Jun 2018 14:21:47 +0200 Subject: [logger] Change base OTP domain from [beam,erlang,otp] to [otp] --- lib/kernel/src/application_controller.erl | 4 +-- lib/kernel/src/error_logger.erl | 2 +- lib/kernel/src/logger.erl | 4 +-- lib/kernel/src/logger_internal.hrl | 3 +-- lib/kernel/test/logger_env_var_SUITE.erl | 42 +++++++++++-------------------- lib/kernel/test/logger_legacy_SUITE.erl | 6 ++--- 6 files changed, 22 insertions(+), 39 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/application_controller.erl b/lib/kernel/src/application_controller.erl index ff5df667b5..d5a632ef6f 100644 --- a/lib/kernel/src/application_controller.erl +++ b/lib/kernel/src/application_controller.erl @@ -1914,7 +1914,7 @@ info_started(Name, Node) -> ?LOG_INFO(#{label=>{application_controller,progress}, report=>[{application, Name}, {started_at, Node}]}, - #{domain=>[beam,erlang,otp,sasl], + #{domain=>[otp,sasl], report_cb=>fun logger:format_otp_report/1, logger_formatter=>#{title=>"PROGRESS REPORT"}, error_logger=>#{tag=>info_report,type=>progress}}). @@ -1924,7 +1924,7 @@ info_exited(Name, Reason, Type) -> report=>[{application, Name}, {exited, Reason}, {type, Type}]}, - #{domain=>[beam,erlang,otp], + #{domain=>[otp], report_cb=>fun logger:format_otp_report/1, error_logger=>#{tag=>info_report,type=>std_info}}). diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index b3957d0c7e..d54c9d59b4 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -539,7 +539,7 @@ tty(true) -> logger:add_handler(error_logger_tty_true,logger_std_h, #{filter_default=>stop, filters=>?DEFAULT_HANDLER_FILTERS( - [beam,erlang,otp]), + [otp]), formatter=>{?DEFAULT_FORMATTER, ?DEFAULT_FORMAT_CONFIG}, logger_std_h=>#{type=>standard_io}}) diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 3beb3102fa..907e2035f1 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -731,7 +731,7 @@ init_default_config(Type) -> get_default_handler_filters() -> case application:get_env(kernel, logger_sasl_compatible, false) of true -> - ?DEFAULT_HANDLER_FILTERS([beam,erlang,otp]); + ?DEFAULT_HANDLER_FILTERS([otp]); false -> Extra = case application:get_env(kernel, logger_progress_reports, stop) of @@ -741,7 +741,7 @@ get_default_handler_filters() -> [{stop_progress, {fun logger_filters:progress/2,stop}}] end, - Extra ++ ?DEFAULT_HANDLER_FILTERS([beam,erlang,otp,sasl]) + Extra ++ ?DEFAULT_HANDLER_FILTERS([otp,sasl]) end. get_logger_env() -> diff --git a/lib/kernel/src/logger_internal.hrl b/lib/kernel/src/logger_internal.hrl index fedd6db370..1325e38f74 100644 --- a/lib/kernel/src/logger_internal.hrl +++ b/lib/kernel/src/logger_internal.hrl @@ -23,8 +23,7 @@ -define(HANDLER_KEY,'$handler_config$'). -define(LOGGER_META_KEY,'$logger_metadata$'). -define(STANDARD_HANDLER, default). --define(DEFAULT_HANDLER_FILTERS, - ?DEFAULT_HANDLER_FILTERS([beam,erlang,otp])). +-define(DEFAULT_HANDLER_FILTERS,?DEFAULT_HANDLER_FILTERS([otp])). -define(DEFAULT_HANDLER_FILTERS(Domain), [{remote_gl,{fun logger_filters:remote_gl/2,stop}}, {domain,{fun logger_filters:domain/2,{log,super,Domain}}}, diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl index ed9e5b795a..03827c9825 100644 --- a/lib/kernel/test/logger_env_var_SUITE.erl +++ b/lib/kernel/test/logger_env_var_SUITE.erl @@ -82,8 +82,7 @@ default(Config) -> {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), debug = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), - {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = - lists:keyfind(domain,1,StdFilters), + {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), true = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(simple,1,Hs), false = lists:keymember(sasl,1,Hs), @@ -95,8 +94,7 @@ default_sasl_compatible(Config) -> {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), debug = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), - {domain,{_,{log,super,[beam,erlang,otp]}}} = - lists:keyfind(domain,1,StdFilters), + {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(simple,1,Hs), true = lists:keymember(sasl,1,Hs), @@ -107,8 +105,7 @@ error_logger_tty(Config) -> {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), debug = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), - {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = - lists:keyfind(domain,1,StdFilters), + {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), true = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(simple,1,Hs), false = lists:keymember(sasl,1,Hs), @@ -121,8 +118,7 @@ error_logger_tty_sasl_compatible(Config) -> {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), debug = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), - {domain,{_,{log,super,[beam,erlang,otp]}}} = - lists:keyfind(domain,1,StdFilters), + {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(simple,1,Hs), true = lists:keymember(sasl,1,Hs), @@ -138,8 +134,7 @@ error_logger_false(Config) -> debug = maps:get(level,SimpleC), notice = maps:get(level,L), SimpleFilters = maps:get(filters,SimpleC), - {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = - lists:keyfind(domain,1,SimpleFilters), + {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), true = lists:keymember(stop_progress,1,SimpleFilters), false = lists:keymember(sasl,1,Hs), ok. @@ -155,8 +150,7 @@ error_logger_false_progress(Config) -> debug = maps:get(level,SimpleC), notice = maps:get(level,L), SimpleFilters = maps:get(filters,SimpleC), - {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = - lists:keyfind(domain,1,SimpleFilters), + {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), false = lists:keymember(stop_progress,1,SimpleFilters), false = lists:keymember(sasl,1,Hs), ok. @@ -172,8 +166,7 @@ error_logger_false_sasl_compatible(Config) -> debug = maps:get(level,SimpleC), notice = maps:get(level,L), SimpleFilters = maps:get(filters,SimpleC), - {domain,{_,{log,super,[beam,erlang,otp]}}} = - lists:keyfind(domain,1,SimpleFilters), + {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,SimpleFilters), false = lists:keymember(stop_progress,1,SimpleFilters), true = lists:keymember(sasl,1,Hs), ok. @@ -220,8 +213,7 @@ logger_file(Config) -> {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), debug = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), - {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = - lists:keyfind(domain,1,StdFilters), + {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), true = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(simple,1,Hs), false = lists:keymember(sasl,1,Hs), @@ -243,8 +235,7 @@ logger_file_sasl_compatible(Config) -> {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), debug = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), - {domain,{_,{log,super,[beam,erlang,otp]}}} = - lists:keyfind(domain,1,StdFilters), + {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(simple,1,Hs), true = lists:keymember(sasl,1,Hs), @@ -266,8 +257,7 @@ logger_file_log_progress(Config) -> {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), debug = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), - {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = - lists:keyfind(domain,1,StdFilters), + {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(simple,1,Hs), false = lists:keymember(sasl,1,Hs), @@ -353,8 +343,7 @@ logger_filters(Config) -> {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), debug = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), - {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = - lists:keyfind(domain,1,StdFilters), + {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(simple,1,Hs), false = lists:keymember(sasl,1,Hs), @@ -406,8 +395,7 @@ logger_module_level(Config) -> {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), debug = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), - {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = - lists:keyfind(domain,1,StdFilters), + {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(simple,1,Hs), false = lists:keymember(sasl,1,Hs), @@ -428,8 +416,7 @@ logger_disk_log(Config) -> {?STANDARD_HANDLER,logger_disk_log_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), debug = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), - {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = - lists:keyfind(domain,1,StdFilters), + {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), true = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(simple,1,Hs), false = lists:keymember(sasl,1,Hs), @@ -465,8 +452,7 @@ logger_undefined(Config) -> debug = maps:get(level,SimpleC), info = maps:get(level,L), SimpleFilters = maps:get(filters,SimpleC), - {domain,{_,{log,super,[beam,erlang,otp,sasl]}}} = - lists:keyfind(domain,1,SimpleFilters), + {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), true = lists:keymember(stop_progress,1,SimpleFilters), false = lists:keymember(sasl,1,Hs), ok. diff --git a/lib/kernel/test/logger_legacy_SUITE.erl b/lib/kernel/test/logger_legacy_SUITE.erl index cfba35e43f..180db9f51a 100644 --- a/lib/kernel/test/logger_legacy_SUITE.erl +++ b/lib/kernel/test/logger_legacy_SUITE.erl @@ -67,14 +67,12 @@ end_per_suite(_Config) -> init_per_group(std, Config) -> ok = logger:set_handler_config( error_logger,filters, - [{domain,{fun logger_filters:domain/2, - {log,super,[beam,erlang,otp]}}}]), + [{domain,{fun logger_filters:domain/2,{log,super,[otp]}}}]), Config; init_per_group(sasl, Config) -> ok = logger:set_handler_config( error_logger,filters, - [{domain,{fun logger_filters:domain/2, - {log,super,[beam,erlang,otp,sasl]}}}]), + [{domain,{fun logger_filters:domain/2,{log,super,[otp,sasl]}}}]), %% cth_log_redirect checks if sasl is started before displaying %% any sasl reports - so just to see the real sasl reports in tc -- cgit v1.2.3 From 577d903af9eb5b028611a957b8f07b06fbbd741e Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 31 May 2018 15:22:45 +0200 Subject: Allow 'unlimited' as value to Kernel parameter error_logger_format_depth --- lib/kernel/src/error_logger.erl | 2 ++ lib/kernel/test/error_logger_SUITE.erl | 20 ++++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index d54c9d59b4..9238029cef 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -575,6 +575,8 @@ get_format_depth() -> case application:get_env(kernel, error_logger_format_depth) of {ok, Depth} when is_integer(Depth) -> max(10, Depth); + {ok, unlimited} -> + unlimited; undefined -> unlimited end. diff --git a/lib/kernel/test/error_logger_SUITE.erl b/lib/kernel/test/error_logger_SUITE.erl index 6c4526d0cf..895478f170 100644 --- a/lib/kernel/test/error_logger_SUITE.erl +++ b/lib/kernel/test/error_logger_SUITE.erl @@ -33,7 +33,7 @@ off_heap/1, error_report/1, info_report/1, error/1, info/1, emulator/1, via_logger_process/1, other_node/1, - tty/1, logfile/1, add/1, delete/1]). + tty/1, logfile/1, add/1, delete/1, format_depth/1]). -export([generate_error/2]). @@ -48,7 +48,8 @@ suite() -> all() -> [off_heap, error_report, info_report, error, info, emulator, - via_logger_process, other_node, tty, logfile, add, delete]. + via_logger_process, other_node, tty, logfile, add, delete, + format_depth]. groups() -> []. @@ -308,6 +309,21 @@ delete(Config) when is_list(Config) -> {error,_} = error_logger:delete_report_handler(non_existing), ok. +%%----------------------------------------------------------------- + +format_depth(_Config) -> + ok = application:set_env(kernel,error_logger_format_depth,30), + 30 = error_logger:get_format_depth(), + ok = application:set_env(kernel,error_logger_format_depth,3), + 10 = error_logger:get_format_depth(), + ok = application:set_env(kernel,error_logger_format_depth,11), + 11 = error_logger:get_format_depth(), + ok = application:set_env(kernel,error_logger_format_depth,unlimited), + unlimited = error_logger:get_format_depth(), + ok = application:unset_env(kernel,error_logger_format_depth), + unlimited = error_logger:get_format_depth(), + ok. + %%----------------------------------------------------------------- %% Check that the report has been received. %%----------------------------------------------------------------- -- cgit v1.2.3 From 0a235e6b1f008ade99b18438e671b93ff82cc268 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 8 Jun 2018 14:23:13 +0200 Subject: [logger] Update interface for setting/unsetting module level * Level can now be set/unset for multiple modules in one call. * Added functions get_module_level/0 and get_module_level/1. --- lib/kernel/src/logger.erl | 44 ++++++++++++++++++++++++++++++-------- lib/kernel/src/logger_config.erl | 26 ++++++++++++++++------- lib/kernel/src/logger_server.erl | 46 +++++++++++++++++++++++++--------------- lib/kernel/test/logger_SUITE.erl | 37 +++++++++++++++++++++++++------- 4 files changed, 111 insertions(+), 42 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 907e2035f1..395ec448ff 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -37,7 +37,9 @@ -export([add_handler/3, remove_handler/1, add_logger_filter/2, add_handler_filter/3, remove_logger_filter/1, remove_handler_filter/2, - set_module_level/2, unset_module_level/1, + set_module_level/2, + unset_module_level/1, unset_module_level/0, + get_module_level/0, get_module_level/1, set_logger_config/1, set_logger_config/2, set_handler_config/2, set_handler_config/3, update_logger_config/1, update_handler_config/2, @@ -408,16 +410,40 @@ update_formatter_config(HandlerId,FormatterConfig) -> update_formatter_config(HandlerId,Key,Value) -> logger_server:update_formatter_config(HandlerId,#{Key=>Value}). --spec set_module_level(Module,Level) -> ok | {error,term()} when +-spec set_module_level(Modules,Level) -> ok | {error,term()} when + Modules :: [module()] | module(), + Level :: level(). +set_module_level(Module,Level) when is_atom(Module) -> + set_module_level([Module],Level); +set_module_level(Modules,Level) -> + logger_server:set_module_level(Modules,Level). + +-spec unset_module_level(Modules) -> ok when + Modules :: [module()] | module(). +unset_module_level(Module) when is_atom(Module) -> + unset_module_level([Module]); +unset_module_level(Modules) -> + logger_server:unset_module_level(Modules). + +-spec unset_module_level() -> ok. +unset_module_level() -> + logger_server:unset_module_level(). + +-spec get_module_level(Modules) -> [{Module,Level}] when + Modules :: [Module] | Module, Module :: module(), Level :: level(). -set_module_level(Module,Level) -> - logger_server:set_module_level(Module,Level). +get_module_level(Module) when is_atom(Module) -> + get_module_level([Module]); +get_module_level(Modules) when is_list(Modules) -> + [{M,L} || {M,L} <- get_module_level(), + lists:member(M,Modules)]. --spec unset_module_level(Module) -> ok | {error,term()} when - Module :: module(). -unset_module_level(Module) -> - logger_server:unset_module_level(Module). +-spec get_module_level() -> [{Module,Level}] when + Module :: module(), + Level :: level(). +get_module_level() -> + logger_config:get_module_level(?LOGGER_TABLE). %%%----------------------------------------------------------------- %%% Misc @@ -496,7 +522,7 @@ i(_Action = string) -> print_module_levels(Modules) ]; i(_Action = term) -> - {Logger, Handlers, Modules} = logger_config:get(tid()), + {Logger, Handlers, Modules} = logger_config:get(?LOGGER_TABLE), #{logger=>maps:remove(handlers,Logger), handlers=>lists:keysort(1,Handlers), module_levels=>lists:keysort(1,Modules)}. diff --git a/lib/kernel/src/logger_config.erl b/lib/kernel/src/logger_config.erl index 1d35c2e068..8d05648109 100644 --- a/lib/kernel/src/logger_config.erl +++ b/lib/kernel/src/logger_config.erl @@ -25,7 +25,7 @@ get/2, get/3, get/1, create/3, create/4, set/3, set_module_level/3,unset_module_level/2, - cache_module_level/2, + get_module_level/1,cache_module_level/2, level_to_int/1]). -include("logger_internal.hrl"). @@ -105,14 +105,25 @@ set(Tid,What,Config) -> ets:update_element(Tid,table_key(What),[{2,LevelInt},{3,Config}]), ok. -set_module_level(Tid,Module,Level) -> - ets:insert(Tid,{Module,level_to_int(Level)}), +set_module_level(Tid,Modules,Level) -> + LevelInt = level_to_int(Level), + [ets:insert(Tid,{Module,LevelInt}) || Module <- Modules], ok. -unset_module_level(Tid,Module) -> - ets:delete(Tid,Module), % should possibley overwrite instead of delete? +%% should possibly overwrite instead of delete? +unset_module_level(Tid,all) -> + MS = [{{'$1','$2'},[{is_atom,'$1'},{is_integer,'$2'}],[true]}], + _ = ets:select_delete(Tid,MS), + ok; +unset_module_level(Tid,Modules) -> + [ets:delete(Tid,Module) || Module <- Modules], ok. +get_module_level(Tid) -> + MS = [{{'$1','$2'},[{is_atom,'$1'},{is_integer,'$2'}],[{{'$1','$2'}}]}], + Modules = ets:select(Tid,MS), + lists:sort([{M,int_to_level(L)} || {M,L} <- Modules]). + cache_module_level(Tid,Module) -> GlobalLevelInt = ets:lookup_element(Tid,?LOGGER_KEY,2), ets:insert_new(Tid,{Module,{GlobalLevelInt,cached}}), @@ -122,9 +133,8 @@ get(Tid) -> {ok,Logger} = get(Tid,logger), HMS = [{{table_key('$1'),'_','$2','$3'},[],[{{'$1','$3','$2'}}]}], Handlers = ets:select(Tid,HMS), - MMS = [{{'$1','$2'},[{is_atom,'$1'},{is_integer,'$2'}],[{{'$1','$2'}}]}], - Modules = ets:select(Tid,MMS), - {Logger,Handlers,[{M,int_to_level(L)} || {M,L} <- Modules]}. + Modules = get_module_level(Tid), + {Logger,Handlers,Modules}. level_to_int(emergency) -> ?EMERGENCY; level_to_int(alert) -> ?ALERT; diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index e066837b63..164e193fa0 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -25,8 +25,8 @@ -export([start_link/0, add_handler/3, remove_handler/1, add_filter/2, remove_filter/2, - set_module_level/2, unset_module_level/1, - cache_module_level/1, + set_module_level/2, unset_module_level/0, + unset_module_level/1, cache_module_level/1, set_config/2, set_config/3, update_config/2, update_formatter_config/2]). @@ -75,23 +75,35 @@ add_filter(Owner,Filter) -> remove_filter(Owner,FilterId) -> call({remove_filter,Owner,FilterId}). -set_module_level(Module,Level) when is_atom(Module) -> - case sanity_check(logger,level,Level) of - ok -> call({set_module_level,Module,Level}); - Error -> Error +set_module_level(Modules,Level) when is_list(Modules) -> + case lists:all(fun(M) -> is_atom(M) end,Modules) of + true -> + case sanity_check(logger,level,Level) of + ok -> call({set_module_level,Modules,Level}); + Error -> Error + end; + false -> + {error,{not_a_list_of_modles,Modules}} end; -set_module_level(Module,_) -> - {error,{not_a_module,Module}}. +set_module_level(Modules,_) -> + {error,{not_a_list_of_modules,Modules}}. -unset_module_level(Module) when is_atom(Module) -> - call({unset_module_level,Module}); -unset_module_level(Module) -> - {error,{not_a_module,Module}}. +unset_module_level() -> + call({unset_module_level,all}). + +unset_module_level(Modules) when is_list(Modules) -> + case lists:all(fun(M) -> is_atom(M) end,Modules) of + true -> + call({unset_module_level,Modules}); + false -> + {error,{not_a_list_of_modles,Modules}} + end; +unset_module_level(Modules) -> + {error,{not_a_list_of_modules,Modules}}. cache_module_level(Module) -> gen_server:cast(?SERVER,{cache_module_level,Module}). - set_config(Owner,Key,Value) -> update_config(Owner,#{Key=>Value}). @@ -239,11 +251,11 @@ handle_call({update_formatter_config,HandlerId,NewFConfig},_From, {error,{not_found,HandlerId}} end, {reply,Reply,State}; -handle_call({set_module_level,Module,Level}, _From, #state{tid=Tid}=State) -> - Reply = logger_config:set_module_level(Tid,Module,Level), +handle_call({set_module_level,Modules,Level}, _From, #state{tid=Tid}=State) -> + Reply = logger_config:set_module_level(Tid,Modules,Level), {reply,Reply,State}; -handle_call({unset_module_level,Module}, _From, #state{tid=Tid}=State) -> - Reply = logger_config:unset_module_level(Tid,Module), +handle_call({unset_module_level,Modules}, _From, #state{tid=Tid}=State) -> + Reply = logger_config:unset_module_level(Tid,Modules), {reply,Reply,State}. handle_cast({async_req_reply,_Ref,_Reply} = Reply,State) -> diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index a193ae4011..a8769c450c 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -85,8 +85,8 @@ all() -> log_all_levels_api, macros, set_level, - set_level_module, - cache_level_module, + set_module_level, + cache_module_level, format_report, filter_failed, handler_failed, @@ -337,30 +337,51 @@ set_level(cleanup,_Config) -> logger:set_logger_config(level,info), ok. -set_level_module(_Config) -> +set_module_level(_Config) -> + [] = logger:get_module_level([?MODULE,other]), + [] = logger:get_module_level(?MODULE), + [] = logger:get_module_level(), + ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log}), {error,{invalid_level,bad}} = logger:set_module_level(?MODULE,bad), - {error,{not_a_module,{bad}}} = logger:set_module_level({bad},warning), + {error,{not_a_list_of_modules,{bad}}} = + logger:set_module_level({bad},warning), ok = logger:set_module_level(?MODULE,warning), + [{?MODULE,warning}] = logger:get_module_level([?MODULE,other]), + [{?MODULE,warning}] = logger:get_module_level(?MODULE), + [{?MODULE,warning}] = logger:get_module_level(), logger:info(?map_rep,?MY_LOC(0)), ok = check_no_log(), logger:warning(M1=?map_rep,?MY_LOC(0)), ok = check_logged(warning,M1,?MY_LOC(1)), ok = logger:set_module_level(?MODULE,info), + [{?MODULE,info}] = logger:get_module_level([?MODULE,other]), + [{?MODULE,info}] = logger:get_module_level(?MODULE), + [{?MODULE,info}] = logger:get_module_level(), logger:info(M2=?map_rep,?MY_LOC(0)), ok = check_logged(info,M2,?MY_LOC(1)), - {error,{not_a_module,{bad}}} = logger:unset_module_level({bad}), + {error,{not_a_list_of_modules,{bad}}} = logger:unset_module_level({bad}), ok = logger:unset_module_level(?MODULE), + [] = logger:get_module_level([?MODULE,other]), + [] = logger:get_module_level(?MODULE), + [] = logger:get_module_level(), + + ok = logger:set_module_level([m1,m2,m3],info), + [{m1,info},{m2,info},{m3,info}] = logger:get_module_level(), + ok = logger:unset_module_level(m2), + [{m1,info},{m3,info}] = logger:get_module_level(), + ok = logger:unset_module_level(), + [] = logger:get_module_level(), ok. -set_level_module(cleanup,_Config) -> +set_module_level(cleanup,_Config) -> logger:remove_handler(h1), logger:unset_module_level(?MODULE), ok. -cache_level_module(_Config) -> +cache_module_level(_Config) -> ok = logger:unset_module_level(?MODULE), [] = ets:lookup(logger,?MODULE), %dirty - add API in logger_config? ?LOG_INFO(?map_rep), @@ -371,7 +392,7 @@ cache_level_module(_Config) -> [] = ets:lookup(logger,?MODULE), %dirty - add API in logger_config? ok. -cache_level_module(cleanup,_Config) -> +cache_module_level(cleanup,_Config) -> logger:unset_module_level(?MODULE), ok. -- cgit v1.2.3 From e5bbcfd47088162a1c349eeb5aa3c37d8263d274 Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Mon, 4 Jun 2018 14:55:08 +0200 Subject: [logger] Change registered name of built-in handlers and use TID for mode table --- lib/kernel/src/logger_disk_log_h.erl | 121 +++++++++++++++------------ lib/kernel/src/logger_h_common.erl | 32 ++++---- lib/kernel/src/logger_h_common.hrl | 25 +++--- lib/kernel/src/logger_std_h.erl | 122 ++++++++++++++++------------ lib/kernel/test/logger_disk_log_h_SUITE.erl | 54 ++++++------ lib/kernel/test/logger_std_h_SUITE.erl | 48 ++++++----- 6 files changed, 232 insertions(+), 170 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index 00643b6a84..ba90fefcd3 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -63,7 +63,8 @@ start_link(Name, Config, HandlerState) -> sync(Name) when is_atom(Name) -> try - gen_server:call(Name, disk_log_sync, ?DEFAULT_CALL_TIMEOUT) + gen_server:call(?name_to_reg_name(?MODULE,Name), + disk_log_sync, ?DEFAULT_CALL_TIMEOUT) catch _:{timeout,_} -> {error,handler_busy} end; @@ -79,7 +80,8 @@ sync(Name) -> info(Name) when is_atom(Name) -> try - gen_server:call(Name, info, ?DEFAULT_CALL_TIMEOUT) + gen_server:call(?name_to_reg_name(?MODULE,Name), + info, ?DEFAULT_CALL_TIMEOUT) catch _:{timeout,_} -> {error,handler_busy} end; @@ -94,7 +96,8 @@ info(Name) -> reset(Name) when is_atom(Name) -> try - gen_server:call(Name, reset, ?DEFAULT_CALL_TIMEOUT) + gen_server:call(?name_to_reg_name(?MODULE,Name), + reset, ?DEFAULT_CALL_TIMEOUT) catch _:{timeout,_} -> {error,handler_busy} end; @@ -117,10 +120,10 @@ adding_handler(#{id:=Name}=Config) -> case logger_h_common:overload_levels_ok(HState) of true -> case start(Name, Config1, HState) of - ok -> + {ok,Config2} -> %% Make sure wait_for_buffer is not stored, so we %% won't hang and wait for buffer on a restart - {ok, maps:remove(wait_for_buffer,Config1)}; + {ok, maps:remove(wait_for_buffer,Config2)}; Error -> Error end; @@ -136,13 +139,18 @@ adding_handler(#{id:=Name}=Config) -> %%%----------------------------------------------------------------- %%% Updating handler config -changing_config(OldConfig=#{id:=Name, disk_log_opts:=DLOpts}, +changing_config(OldConfig=#{id:=Name, disk_log_opts:=DLOpts, ?MODULE:=HConfig}, NewConfig=#{id:=Name, disk_log_opts:=DLOpts}) -> case check_config(changing, NewConfig) of - Result = {ok,NewConfig1} -> - try gen_server:call(Name, {change_config,OldConfig,NewConfig1}, + {ok,NewConfig1 = #{?MODULE:=NewHConfig}} -> + #{handler_pid:=HPid, + mode_tab:=ModeTab} = HConfig, + NewHConfig1 = NewHConfig#{handler_pid=>HPid, + mode_tab=>ModeTab}, + NewConfig2 = NewConfig1#{?MODULE=>NewHConfig1}, + try gen_server:call(HPid, {change_config,OldConfig,NewConfig2}, ?DEFAULT_CALL_TIMEOUT) of - ok -> Result; + ok -> {ok,NewConfig2}; HError -> HError catch _:{timeout,_} -> {error,handler_busy} @@ -225,12 +233,12 @@ removing_handler(#{id:=Name}) -> %%%----------------------------------------------------------------- %%% Get buffer when swapping from simple handler -swap_buffer(Name,Buffer) -> - case whereis(Name) of +swap_buffer(Name, Buffer) -> + case whereis(?name_to_reg_name(?MODULE,Name)) of undefined -> ok; - _ -> - Name ! {buffer,Buffer} + Pid -> + Pid ! {buffer,Buffer} end. %%%----------------------------------------------------------------- @@ -239,21 +247,22 @@ swap_buffer(Name,Buffer) -> LogEvent :: logger:log_event(), Config :: logger:config(). -log(LogEvent,Config=#{id:=Name}) -> +log(LogEvent, Config = #{id := Name, + ?MODULE := #{handler_pid := HPid, + mode_tab := ModeTab}}) -> %% if the handler has crashed, we must drop this request %% and hope the handler restarts so we can try again - true = is_pid(whereis(Name)), - Bin = logger_h_common:log_to_binary(LogEvent,Config), - logger_h_common:call_cast_or_drop(Name, Bin). - + true = is_process_alive(HPid), + Bin = logger_h_common:log_to_binary(LogEvent, Config), + logger_h_common:call_cast_or_drop(Name, HPid, ModeTab, Bin). %%%=================================================================== %%% gen_server callbacks %%%=================================================================== -init([Name, Config = #{disk_log_opts := LogOpts}, +init([Name, Config = #{?MODULE := HConfig, disk_log_opts := LogOpts}, State = #{dl_sync_int := DLSyncInt}]) -> - register(Name, self()), + register(?name_to_reg_name(?MODULE,Name), self()), process_flag(trap_exit, true), process_flag(message_queue_data, off_heap), @@ -262,25 +271,36 @@ init([Name, Config = #{disk_log_opts := LogOpts}, case open_disk_log(Name, LogOpts) of ok -> - catch ets:new(Name, [public, named_table]), - ?set_mode(Name, async), - proc_lib:init_ack({ok,self()}), - T0 = ?timestamp(), - State1 = - ?merge_with_stats(State#{id => Name, - mode => async, - dl_sync => DLSyncInt, - log_opts => LogOpts, - last_qlen => 0, - last_log_ts => T0, - burst_win_ts => T0, - burst_msg_count => 0, - last_op => sync, - prev_log_result => ok, - prev_sync_result => ok, - prev_disk_log_info => undefined}), - gen_server:cast(self(), repeated_disk_log_sync), - enter_loop(Config, State1); + try ets:new(Name, [public]) of + ModeTab -> + ?set_mode(ModeTab, async), + T0 = ?timestamp(), + State1 = + ?merge_with_stats(State#{ + id => Name, + mode_tab => ModeTab, + mode => async, + dl_sync => DLSyncInt, + log_opts => LogOpts, + last_qlen => 0, + last_log_ts => T0, + burst_win_ts => T0, + burst_msg_count => 0, + last_op => sync, + prev_log_result => ok, + prev_sync_result => ok, + prev_disk_log_info => undefined}), + Config1 = + Config#{?MODULE => HConfig#{handler_pid => self(), + mode_tab => ModeTab}}, + proc_lib:init_ack({ok,self(),Config1}), + gen_server:cast(self(), repeated_disk_log_sync), + enter_loop(Config1, State1) + catch + _:Error -> + logger_h_common:error_notify({open_disk_log,Name,Error}), + proc_lib:init_ack(Error) + end; Error -> logger_h_common:error_notify({open_disk_log,Name,Error}), proc_lib:init_ack(Error) @@ -477,8 +497,8 @@ start(Name, Config, HandlerState) -> type => worker, modules => [?MODULE]}, case supervisor:start_child(logger_sup, LoggerDLH) of - {ok,_} -> - ok; + {ok,_Pid,Config1} -> + {ok,Config1}; Error -> Error end. @@ -486,16 +506,16 @@ start(Name, Config, HandlerState) -> %%%----------------------------------------------------------------- %%% Stop and remove the handler. stop(Name) -> - case whereis(Name) of + case whereis(?name_to_reg_name(?MODULE,Name)) of undefined -> ok; - _ -> + Pid -> %% We don't want to do supervisor:terminate_child here %% since we need to distinguish this explicit stop from a %% system termination in order to avoid circular attempts %% at removing the handler (implying deadlocks and %% timeouts). - _ = gen_server:call(Name,stop), + _ = gen_server:call(Pid, stop), _ = supervisor:delete_child(logger_sup, Name), ok end. @@ -530,7 +550,7 @@ do_log(Bin, CallOrCast, State = #{id:=Name, mode := _Mode0}) -> %% this function is called by do_log/3 after an overload check %% has been performed, where QLen > FlushQLen -flush(Name, _QLen0, T1, State=#{last_log_ts := _T0}) -> +flush(_Name, _QLen0, T1, State=#{last_log_ts := _T0, mode_tab := ModeTab}) -> %% flush messages in the mailbox (a limited number in %% order to not cause long delays) _NewFlushed = logger_h_common:flush_log_requests(?FLUSH_MAX_N), @@ -539,20 +559,21 @@ flush(Name, _QLen0, T1, State=#{last_log_ts := _T0}) -> %% handler will be scheduled out often and the mailbox could %% grow very large, so we'd better check the queue again here {_,_QLen1} = process_info(self(), message_queue_len), - ?observe(Name,{max_qlen,_QLen1}), + ?observe(_Name,{max_qlen,_QLen1}), %% Add 1 for the current log request - ?observe(Name,{flushed,_NewFlushed+1}), + ?observe(_Name,{flushed,_NewFlushed+1}), State1 = ?update_max_time(?diff_time(T1,_T0),State), {dropped,?update_other(flushed,FLUSHED,_NewFlushed, - State1#{mode => ?set_mode(Name,async), + State1#{mode => ?set_mode(ModeTab,async), last_qlen => 0, last_log_ts => T1})}. %% this function is called to write to disk_log write(Name, Mode, T1, Bin, _CallOrCast, - State = #{dl_sync := DLSync, + State = #{mode_tab := ModeTab, + dl_sync := DLSync, dl_sync_int := DLSyncInt, last_qlen := LastQLen, last_log_ts := T0}) -> @@ -589,7 +610,7 @@ write(Name, Mode, T1, Bin, _CallOrCast, {Mode1,BurstMsgCount1,State2} = if (LastQLen1 < ?FILESYNC_OK_QLEN) andalso (Time > ?IDLE_DETECT_TIME_USEC) -> - {?change_mode(Name,Mode,async), 0, disk_log_sync(Name,State1)}; + {?change_mode(ModeTab,Mode,async), 0, disk_log_sync(Name,State1)}; true -> {Mode, BurstMsgCount,State1} end, diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl index 336398cd4a..73a5b27ea1 100644 --- a/lib/kernel/src/logger_h_common.erl +++ b/lib/kernel/src/logger_h_common.erl @@ -24,7 +24,7 @@ -export([log_to_binary/2, check_common_config/1, - call_cast_or_drop/2, + call_cast_or_drop/4, check_load/1, limit_burst/1, kill_if_choked/4, @@ -84,6 +84,10 @@ try_format(Log,Formatter,FormatterConfig) -> %%%----------------------------------------------------------------- %%% Check that the configuration term is valid +check_common_config({mode_tab,_Tid}) -> + valid; +check_common_config({handler_pid,Pid}) when is_pid(Pid) -> + valid; check_common_config({toggle_sync_qlen,N}) when is_integer(N) -> valid; check_common_config({drop_new_reqs_qlen,N}) when is_integer(N) -> @@ -116,16 +120,16 @@ check_common_config(_) -> %%%----------------------------------------------------------------- %%% Overload Protection -call_cast_or_drop(Name, Bin) -> +call_cast_or_drop(_Name, HandlerPid, ModeTab, Bin) -> %% If the handler process is getting overloaded, the log request %% will be synchronous instead of asynchronous (slows down the %% logging tempo of a process doing lots of logging. If the %% handler is choked, drop mode is set and no request will be sent. - try ?get_mode(Name) of + try ?get_mode(ModeTab) of async -> - gen_server:cast(Name, {log,Bin}); + gen_server:cast(HandlerPid, {log,Bin}); sync -> - try gen_server:call(Name, {log,Bin}, ?DEFAULT_CALL_TIMEOUT) of + try gen_server:call(HandlerPid, {log,Bin}, ?DEFAULT_CALL_TIMEOUT) of %% if return value from call == dropped, the %% message has been flushed by handler and should %% therefore not be counted as dropped in stats @@ -133,28 +137,28 @@ call_cast_or_drop(Name, Bin) -> dropped -> ok catch _:{timeout,_} -> - ?observe(Name,{dropped,1}) + ?observe(_Name,{dropped,1}) end; drop -> - ?observe(Name,{dropped,1}) + ?observe(_Name,{dropped,1}) catch %% if the ETS table doesn't exist (maybe because of a %% handler restart), we can only drop the request - _:_ -> ?observe(Name,{dropped,1}) + _:_ -> ?observe(_Name,{dropped,1}) end, ok. handler_exit(_Name, Reason) -> exit(Reason). -check_load(State = #{id:=Name, mode := Mode, +check_load(State = #{id:=_Name, mode_tab := ModeTab, mode := Mode, toggle_sync_qlen := ToggleSyncQLen, drop_new_reqs_qlen := DropNewQLen, flush_reqs_qlen := FlushQLen}) -> {_,Mem} = process_info(self(), memory), - ?observe(Name,{max_mem,Mem}), + ?observe(_Name,{max_mem,Mem}), {_,QLen} = process_info(self(), message_queue_len), - ?observe(Name,{max_qlen,QLen}), + ?observe(_Name,{max_qlen,QLen}), %% When the handler process gets scheduled in, it's impossible %% to predict the QLen. We could jump "up" arbitrarily from say %% async to sync, async to drop, sync to flush, etc. However, when @@ -171,11 +175,11 @@ check_load(State = #{id:=Name, mode := Mode, %% be dropped on the client side (never sent get to %% the handler). IncDrops = if Mode == drop -> 0; true -> 1 end, - {?change_mode(Name, Mode, drop), IncDrops,0}; + {?change_mode(ModeTab, Mode, drop), IncDrops,0}; QLen >= ToggleSyncQLen -> - {?change_mode(Name, Mode, sync), 0,0}; + {?change_mode(ModeTab, Mode, sync), 0,0}; true -> - {?change_mode(Name, Mode, async), 0,0} + {?change_mode(ModeTab, Mode, async), 0,0} end, State1 = ?update_other(drops,DROPS,_NewDrops,State), {Mode1, QLen, Mem, diff --git a/lib/kernel/src/logger_h_common.hrl b/lib/kernel/src/logger_h_common.hrl index ed365ce6eb..e4d3431468 100644 --- a/lib/kernel/src/logger_h_common.hrl +++ b/lib/kernel/src/logger_h_common.hrl @@ -76,30 +76,31 @@ -define(DISK_LOG_MAX_NO_FILES, 10). -define(DISK_LOG_MAX_NO_BYTES, 1048576). +%%%----------------------------------------------------------------- +%%% Utility macros + +-define(name_to_reg_name(MODULE,Name), + list_to_atom(lists:concat([MODULE,"_",Name]))). + %%%----------------------------------------------------------------- %%% Overload protection macros -define(timestamp(), erlang:monotonic_time(microsecond)). --define(get_mode(HandlerName), - case ets:lookup(HandlerName, mode) of - [{mode,sync}] -> - case whereis(HandlerName)==self() of - true -> async; - _ -> sync - end; +-define(get_mode(Tid), + case ets:lookup(Tid, mode) of [{mode,M}] -> M; _ -> async end). --define(set_mode(HandlerName, M), - begin ets:insert(HandlerName, {mode,M}), M end). +-define(set_mode(Tid, M), + begin ets:insert(Tid, {mode,M}), M end). --define(change_mode(HandlerName, M0, M1), +-define(change_mode(Tid, M0, M1), if M0 == M1 -> M0; true -> - ets:insert(HandlerName, {mode,M1}), + ets:insert(Tid, {mode,M1}), M1 end). @@ -124,7 +125,7 @@ %%% slow down execution and therefore should not be include in code %%% to be officially released. --define(TEST_HOOKS, true). +%%-define(TEST_HOOKS, true). -ifdef(TEST_HOOKS). -define(TEST_HOOKS_TAB, logger_h_test_hooks). diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index 323f4f02e7..5ec9eced12 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -64,7 +64,8 @@ start_link(Name, Config, HandlerState) -> sync(Name) when is_atom(Name) -> try - gen_server:call(Name, filesync, ?DEFAULT_CALL_TIMEOUT) + gen_server:call(?name_to_reg_name(?MODULE,Name), + filesync, ?DEFAULT_CALL_TIMEOUT) catch _:{timeout,_} -> {error,handler_busy} end; @@ -80,7 +81,8 @@ sync(Name) -> info(Name) when is_atom(Name) -> try - gen_server:call(Name, info, ?DEFAULT_CALL_TIMEOUT) + gen_server:call(?name_to_reg_name(?MODULE,Name), + info, ?DEFAULT_CALL_TIMEOUT) catch _:{timeout,_} -> {error,handler_busy} end; @@ -95,7 +97,8 @@ info(Name) -> reset(Name) when is_atom(Name) -> try - gen_server:call(Name, reset, ?DEFAULT_CALL_TIMEOUT) + gen_server:call(?name_to_reg_name(?MODULE,Name), + reset, ?DEFAULT_CALL_TIMEOUT) catch _:{timeout,_} -> {error,handler_busy} end; @@ -118,10 +121,10 @@ adding_handler(#{id:=Name}=Config) -> case logger_h_common:overload_levels_ok(HState) of true -> case start(Name, Config1, HState) of - ok -> + {ok,Config2} -> %% Make sure wait_for_buffer is not stored, so we %% won't hang and wait for buffer on a restart - {ok, maps:remove(wait_for_buffer,Config1)}; + {ok, maps:remove(wait_for_buffer,Config2)}; Error -> Error end; @@ -137,13 +140,16 @@ adding_handler(#{id:=Name}=Config) -> %%%----------------------------------------------------------------- %%% Updating handler config -changing_config(OldConfig=#{id:=Name, ?MODULE:=#{type:=Type}}, +changing_config(OldConfig=#{id:=Name, ?MODULE:=HConfig}, NewConfig=#{id:=Name}) -> + #{type:=Type, handler_pid:=HPid, mode_tab:=ModeTab} = HConfig, MyConfig = maps:get(?MODULE, NewConfig, #{}), case maps:get(type, MyConfig, Type) of Type -> - MyConfig1 = MyConfig#{type=>Type}, - changing_config1(Name, OldConfig, + MyConfig1 = MyConfig#{type=>Type, + handler_pid=>HPid, + mode_tab=>ModeTab}, + changing_config1(HPid, OldConfig, NewConfig#{?MODULE=>MyConfig1}); _ -> {error,{illegal_config_change,OldConfig,NewConfig}} @@ -151,10 +157,10 @@ changing_config(OldConfig=#{id:=Name, ?MODULE:=#{type:=Type}}, changing_config(OldConfig, NewConfig) -> {error,{illegal_config_change,OldConfig,NewConfig}}. -changing_config1(Name, OldConfig, NewConfig) -> +changing_config1(HPid, OldConfig, NewConfig) -> case check_config(changing, NewConfig) of Result = {ok,NewConfig1} -> - try gen_server:call(Name, {change_config,OldConfig,NewConfig1}, + try gen_server:call(HPid, {change_config,OldConfig,NewConfig1}, ?DEFAULT_CALL_TIMEOUT) of ok -> Result; HError -> HError @@ -209,12 +215,12 @@ removing_handler(#{id:=Name}) -> %%%----------------------------------------------------------------- %%% Get buffer when swapping from simple handler -swap_buffer(Name,Buffer) -> - case whereis(Name) of +swap_buffer(Name, Buffer) -> + case whereis(?name_to_reg_name(?MODULE,Name)) of undefined -> ok; - _ -> - Name ! {buffer,Buffer} + Pid -> + Pid ! {buffer,Buffer} end. %%%----------------------------------------------------------------- @@ -223,20 +229,22 @@ swap_buffer(Name,Buffer) -> LogEvent :: logger:log_event(), Config :: logger:config(). -log(LogEvent,Config=#{id:=Name}) -> +log(LogEvent, Config = #{id := Name, + ?MODULE := #{handler_pid := HPid, + mode_tab := ModeTab}}) -> %% if the handler has crashed, we must drop this request %% and hope the handler restarts so we can try again - true = is_pid(whereis(Name)), - Bin = logger_h_common:log_to_binary(LogEvent,Config), - logger_h_common:call_cast_or_drop(Name, Bin). + true = is_process_alive(HPid), + Bin = logger_h_common:log_to_binary(LogEvent, Config), + logger_h_common:call_cast_or_drop(Name, HPid, ModeTab, Bin). %%%=================================================================== %%% gen_server callbacks %%%=================================================================== -init([Name, Config, - State0 = #{type := Type, file_ctrl_sync_int := FileCtrlSyncInt}]) -> - register(Name, self()), +init([Name, Config = #{?MODULE := HConfig}, + State0 = #{type := Type, file_ctrl_sync_int := FileCtrlSyncInt}]) -> + register(?name_to_reg_name(?MODULE,Name), self()), process_flag(trap_exit, true), process_flag(message_queue_data, off_heap), @@ -245,21 +253,32 @@ init([Name, Config, case do_init(Name, Type) of {ok,InitState} -> - catch ets:new(Name, [public, named_table]), - ?set_mode(Name, async), - State = maps:merge(State0, InitState), - T0 = ?timestamp(), - State1 = - ?merge_with_stats(State#{mode => async, - file_ctrl_sync => FileCtrlSyncInt, - last_qlen => 0, - last_log_ts => T0, - last_op => sync, - burst_win_ts => T0, - burst_msg_count => 0}), - proc_lib:init_ack({ok,self()}), - gen_server:cast(self(), repeated_filesync), - enter_loop(Config, State1); + try ets:new(Name, [public]) of + ModeTab -> + ?set_mode(ModeTab, async), + State = maps:merge(State0, InitState), + T0 = ?timestamp(), + State1 = + ?merge_with_stats(State#{ + mode_tab => ModeTab, + mode => async, + file_ctrl_sync => FileCtrlSyncInt, + last_qlen => 0, + last_log_ts => T0, + last_op => sync, + burst_win_ts => T0, + burst_msg_count => 0}), + Config1 = + Config#{?MODULE => HConfig#{handler_pid => self(), + mode_tab => ModeTab}}, + proc_lib:init_ack({ok,self(),Config1}), + gen_server:cast(self(), repeated_filesync), + enter_loop(Config1, State1) + catch + _:Error -> + logger_h_common:error_notify({init_handler,Name,Error}), + proc_lib:init_ack(Error) + end; Error -> logger_h_common:error_notify({init_handler,Name,Error}), proc_lib:init_ack(Error) @@ -466,8 +485,8 @@ start(Name, Config, HandlerState) -> type => worker, modules => [?MODULE]}, case supervisor:start_child(logger_sup, LoggerStdH) of - {ok,_Pid} -> - ok; + {ok,_Pid,Config1} -> + {ok,Config1}; Error -> Error end. @@ -475,16 +494,16 @@ start(Name, Config, HandlerState) -> %%%----------------------------------------------------------------- %%% Stop and remove the handler. stop(Name) -> - case whereis(Name) of + case whereis(?name_to_reg_name(?MODULE,Name)) of undefined -> ok; - _ -> + Pid -> %% We don't want to do supervisor:terminate_child here %% since we need to distinguish this explicit stop from a %% system termination in order to avoid circular attempts %% at removing the handler (implying deadlocks and %% timeouts). - _ = gen_server:call(Name,stop), + _ = gen_server:call(Pid, stop), _ = supervisor:delete_child(logger_sup, Name), ok end. @@ -519,7 +538,7 @@ do_log(Bin, CallOrCast, State = #{id:=Name}) -> %% this clause is called by do_log/3 after an overload check %% has been performed, where QLen > FlushQLen -flush(Name, _QLen0, T1, State=#{last_log_ts := _T0}) -> +flush(_Name, _QLen0, T1, State=#{last_log_ts := _T0, mode_tab := ModeTab}) -> %% flush messages in the mailbox (a limited number in %% order to not cause long delays) _NewFlushed = logger_h_common:flush_log_requests(?FLUSH_MAX_N), @@ -528,20 +547,21 @@ flush(Name, _QLen0, T1, State=#{last_log_ts := _T0}) -> %% handler will be scheduled out often and the mailbox could %% grow very large, so we'd better check the queue again here {_,_QLen1} = process_info(self(), message_queue_len), - ?observe(Name,{max_qlen,_QLen1}), + ?observe(_Name,{max_qlen,_QLen1}), %% Add 1 for the current log request - ?observe(Name,{flushed,_NewFlushed+1}), + ?observe(_Name,{flushed,_NewFlushed+1}), State1 = ?update_max_time(?diff_time(T1,_T0),State), {dropped,?update_other(flushed,FLUSHED,_NewFlushed, - State1#{mode => ?set_mode(Name,async), + State1#{mode => ?set_mode(ModeTab,async), last_qlen => 0, last_log_ts => T1})}. %% this clause is called to write to file -write(Name, Mode, T1, Bin, _CallOrCast, - State = #{file_ctrl_pid := FileCtrlPid, +write(_Name, Mode, T1, Bin, _CallOrCast, + State = #{mode_tab := ModeTab, + file_ctrl_pid := FileCtrlPid, file_ctrl_sync := FileCtrlSync, last_qlen := LastQLen, last_log_ts := T0, @@ -555,15 +575,15 @@ write(Name, Mode, T1, Bin, _CallOrCast, %% file writes so it can keep up with incoming messages {Result,LastQLen1} = if DoWrite, FileCtrlSync == 0 -> - ?observe(Name,{_CallOrCast,1}), + ?observe(_Name,{_CallOrCast,1}), file_write_sync(FileCtrlPid, Bin, false), {ok,element(2, process_info(self(), message_queue_len))}; DoWrite -> - ?observe(Name,{_CallOrCast,1}), + ?observe(_Name,{_CallOrCast,1}), file_write_async(FileCtrlPid, Bin), {ok,LastQLen}; not DoWrite -> - ?observe(Name,{flushed,1}), + ?observe(_Name,{flushed,1}), {dropped,LastQLen} end, @@ -585,7 +605,7 @@ write(Name, Mode, T1, Bin, _CallOrCast, _File -> file_ctrl_filesync_async(FileCtrlPid) end, - {?change_mode(Name, Mode, async),0}; + {?change_mode(ModeTab, Mode, async),0}; true -> {Mode,BurstMsgCount} end, diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 2b5bbe6aed..e1b1f16414 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -107,10 +107,10 @@ start_stop_handler(_Config) -> ok = logger:add_handler(?MODULE, logger_disk_log_h, #{}), {error,{already_exist,?MODULE}} = logger:add_handler(?MODULE, logger_disk_log_h, #{}), - true = is_pid(whereis(?MODULE)), + true = is_pid(whereis(h_proc_name())), ok = logger:remove_handler(?MODULE), timer:sleep(500), - undefined = whereis(?MODULE). + undefined = whereis(h_proc_name()). start_stop_handler(cleanup, _Config) -> logger:remove_handler(?MODULE). @@ -326,7 +326,7 @@ formatter_fail(Config) -> filters=>?DEFAULT_HANDLER_FILTERS([?MODULE])}, %% no formatter! logger:add_handler(Name, logger_disk_log_h, HConfig), - Pid = whereis(Name), + Pid = whereis(h_proc_name(Name)), true = is_pid(Pid), #{handlers:=HC1} = logger:i(), H = [Id || {Id,_,_} <- HC1], @@ -357,7 +357,7 @@ formatter_fail(Config) -> 5000), %% Check that handler is still alive and was never dead - Pid = whereis(Name), + Pid = whereis(h_proc_name(Name)), #{handlers:=HC2} = logger:i(), H = [Id || {Id,_,_} <- HC2], ok. @@ -598,7 +598,7 @@ disk_log_wrap(Config) -> Pid end, {ok,_} = dbg:tracer(process, {TraceFun, Tester}), - {ok,_} = dbg:p(whereis(?MODULE), [c]), + {ok,_} = dbg:p(whereis(h_proc_name()), [c]), {ok,_} = dbg:tp(logger_disk_log_h, handle_info, 2, []), Text = [34 + rand:uniform(126-34) || _ <- lists:seq(1,MaxBytes)], @@ -652,7 +652,7 @@ disk_log_full(Config) -> Pid end, {ok,_} = dbg:tracer(process, {TraceFun, Tester}), - {ok,_} = dbg:p(whereis(?MODULE), [c]), + {ok,_} = dbg:p(whereis(h_proc_name()), [c]), {ok,_} = dbg:tp(logger_disk_log_h, handle_info, 2, []), NoOfChars = 5, @@ -701,10 +701,10 @@ disk_log_events(Config) -> Pid end, {ok,_} = dbg:tracer(process, {TraceFun, Tester}), - {ok,_} = dbg:p(whereis(?MODULE), [c]), + {ok,_} = dbg:p(whereis(h_proc_name()), [c]), {ok,_} = dbg:tp(logger_disk_log_h, handle_info, 2, []), - [whereis(?MODULE) ! E || E <- Events], + [whereis(h_proc_name()) ! E || E <- Events], %% wait for trace messages timer:sleep(2000), dbg:stop_clear(), @@ -889,7 +889,7 @@ op_switch_to_drop(Config) -> [send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info) || _ <- lists:seq(1, Bursts)], Logged = count_lines(Log), - ok= stop_handler(?MODULE), + ok = stop_handler(?MODULE), _ = file:delete(Log), ct:pal("Number of messages dropped = ~w (~w)", [Procs*NumOfReqs*Bursts-Logged,Procs*NumOfReqs*Bursts]), @@ -1033,14 +1033,14 @@ kill_disabled(Config) -> Logged = count_lines(Log), ct:pal("Number of messages logged = ~w", [Logged]), ok = file:delete(Log), - true = is_pid(whereis(?MODULE)), + true = is_pid(whereis(h_proc_name())), ok. kill_disabled(cleanup, _Config) -> ok = stop_handler(?MODULE). qlen_kill_new(Config) -> {_Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), - Pid0 = whereis(?MODULE), + Pid0 = whereis(h_proc_name()), {_,Mem0} = process_info(Pid0, memory), RestartAfter = ?HANDLER_RESTART_AFTER, NewHConfig = @@ -1064,7 +1064,7 @@ qlen_kill_new(Config) -> ct:pal("Slow shutdown, handler process was killed!", []) end, timer:sleep(RestartAfter + 2000), - true = is_pid(whereis(?MODULE)), + true = is_pid(whereis(h_proc_name())), ok after 5000 -> @@ -1077,7 +1077,7 @@ qlen_kill_new(cleanup, _Config) -> mem_kill_new(Config) -> {_Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), - Pid0 = whereis(?MODULE), + Pid0 = whereis(h_proc_name()), {_,Mem0} = process_info(Pid0, memory), RestartAfter = ?HANDLER_RESTART_AFTER, NewHConfig = @@ -1101,7 +1101,7 @@ mem_kill_new(Config) -> ct:pal("Slow shutdown, handler process was killed!", []) end, timer:sleep(RestartAfter + 2000), - true = is_pid(whereis(?MODULE)), + true = is_pid(whereis(h_proc_name())), ok after 5000 -> @@ -1119,13 +1119,13 @@ restart_after(Config) -> handler_overloaded_qlen=>10, handler_restart_after=>never}}, ok = logger:set_handler_config(?MODULE, NewHConfig1), - MRef1 = erlang:monitor(process, whereis(?MODULE)), + MRef1 = erlang:monitor(process, whereis(h_proc_name())), %% kill handler send_burst({n,100}, {spawn,2,0}, {chars,79}, info), receive {'DOWN', MRef1, _, _, _Info1} -> timer:sleep(?HANDLER_RESTART_AFTER + 1000), - undefined = whereis(?MODULE), + undefined = whereis(h_proc_name()), ok after 5000 -> @@ -1139,14 +1139,14 @@ restart_after(Config) -> handler_overloaded_qlen=>10, handler_restart_after=>RestartAfter}}, ok = logger:set_handler_config(?MODULE, NewHConfig2), - Pid0 = whereis(?MODULE), + Pid0 = whereis(h_proc_name()), MRef2 = erlang:monitor(process, Pid0), %% kill handler send_burst({n,100}, {spawn,2,0}, {chars,79}, info), receive {'DOWN', MRef2, _, _, _Info2} -> timer:sleep(RestartAfter + 2000), - Pid1 = whereis(?MODULE), + Pid1 = whereis(h_proc_name()), true = is_pid(Pid1), false = (Pid1 == Pid0), ok @@ -1225,14 +1225,15 @@ start_handler(Name, FuncName, Config) -> max_no_bytes => 100000000}, filter_default=>log, filters=>?DEFAULT_HANDLER_FILTERS([Name]), - formatter=>{?MODULE,op}}), + formatter=>{?MODULE,op}, + level => info}), {ok,{_,HConfig = #{logger_disk_log_h := DLHConfig}}} = logger:get_handler_config(Name), {lists:concat([File,".1"]),HConfig,DLHConfig}. stop_handler(Name) -> - ok = logger:remove_handler(Name), - ct:pal("Handler ~p stopped!", [Name]). + ct:pal("Stopping handler ~p!", [Name]), + logger:remove_handler(Name). send_burst(NorT, Type, {chars,Sz}, Class) -> Text = [34 + rand:uniform(126-34) || _ <- lists:seq(1,Sz)], @@ -1331,7 +1332,7 @@ start_and_add(Name, Config, LogOpts) -> [Name,Config#{disk_log_opts=>LogOpts}]), ok = logger:add_handler(Name, logger_disk_log_h, Config#{disk_log_opts=>LogOpts}), - Pid = whereis(Name), + Pid = whereis(h_proc_name(Name)), true = is_pid(Pid), Name = proplists:get_value(name, disk_log:info(Name)), ok. @@ -1339,7 +1340,7 @@ start_and_add(Name, Config, LogOpts) -> remove_and_stop(Handler) -> ok = logger:remove_handler(Handler), timer:sleep(500), - undefined = whereis(Handler), + undefined = whereis(h_proc_name(Handler)), ok. try_read_file(FileName, Expected, Time) -> @@ -1442,7 +1443,7 @@ repeat_until_ok(Fun, C, Stop, FirstReason) -> start_tracer(Trace,Expected) -> Pid = self(), dbg:tracer(process,{fun tracer/2,{Pid,Expected}}), - dbg:p(whereis(?MODULE),[c]), + dbg:p(h_proc_name(),[c]), tpl(Trace), ok. @@ -1498,3 +1499,8 @@ escape([H|T]) -> [H|escape(T)]; escape([]) -> []. + +h_proc_name() -> + h_proc_name(?MODULE). +h_proc_name(Name) -> + list_to_atom(lists:concat([logger_disk_log_h,"_",Name])). diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index c6391e55fc..fa90be0fc8 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -170,7 +170,7 @@ add_remove_instance_file(Log, Type) -> filter_default=>stop, filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,self()}}), - Pid = whereis(?MODULE), + Pid = whereis(h_proc_name()), true = is_pid(Pid), logger:info(M1=?msg,?domain), ?check(M1), @@ -178,7 +178,7 @@ add_remove_instance_file(Log, Type) -> try_read_file(Log, {ok,B1}, ?FILESYNC_REP_INT), ok = logger:remove_handler(?MODULE), timer:sleep(500), - undefined = whereis(?MODULE), + undefined = whereis(h_proc_name()), logger:info(?msg,?domain), ?check_no_log, try_read_file(Log, {ok,B1}, ?FILESYNC_REP_INT), @@ -240,7 +240,7 @@ formatter_fail(Config) -> #{logger_std_h => #{type => {file,Log}}, filter_default=>stop, filters=>?DEFAULT_HANDLER_FILTERS([?MODULE])}), - Pid = whereis(?MODULE), + Pid = whereis(h_proc_name()), true = is_pid(Pid), #{handlers:=HC1} = logger:i(), H = [Id || {Id,_,_} <- HC1], @@ -271,7 +271,7 @@ formatter_fail(Config) -> 5000), %% Check that handler is still alive and was never dead - Pid = whereis(?MODULE), + Pid = whereis(h_proc_name()), #{handlers:=HC2} = logger:i(), H = [Id || {Id,_,_} <- HC2], @@ -354,7 +354,12 @@ crash_std_h(Config,Func,Var,Type,Log) -> 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}]), - Pid = rpc:call(Node,erlang,whereis,[?STANDARD_HANDLER]), + HProcName = + case Type of + file -> ?name_to_reg_name(logger_std_h,?STANDARD_HANDLER); + disk_log -> ?name_to_reg_name(logger_disk_log_h,?STANDARD_HANDLER) + end, + Pid = rpc:call(Node,erlang,whereis,[HProcName]), ok = rpc:call(Node,logger,set_handler_config,[?STANDARD_HANDLER,formatter, {?MODULE,self()}]), ok = log_on_remote_node(Node,"dummy1"), @@ -367,7 +372,7 @@ crash_std_h(Config,Func,Var,Type,Log) -> %% Wait a bit, then check that it is gone timer:sleep(2000), - undefined = rpc:call(Node,erlang,whereis,[?STANDARD_HANDLER]), + undefined = rpc:call(Node,erlang,whereis,[HProcName]), %% Check that file is not empty {ok,Bin2} = sync_and_read(Node,Type,Log), @@ -940,14 +945,14 @@ kill_disabled(Config) -> Logged = count_lines(Log), ct:pal("Number of messages logged = ~w", [Logged]), ok = file:delete(Log), - true = is_pid(whereis(?MODULE)), + true = is_pid(whereis(h_proc_name())), ok. kill_disabled(cleanup, _Config) -> ok = stop_handler(?MODULE). qlen_kill_new(Config) -> {_Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), - Pid0 = whereis(?MODULE), + Pid0 = whereis(h_proc_name()), {_,Mem0} = process_info(Pid0, memory), RestartAfter = ?HANDLER_RESTART_AFTER, NewHConfig = @@ -970,7 +975,7 @@ qlen_kill_new(Config) -> ct:pal("Slow shutdown, handler process was killed!", []) end, timer:sleep(RestartAfter + 2000), - true = is_pid(whereis(?MODULE)), + true = is_pid(whereis(h_proc_name())), ok after 5000 -> @@ -998,7 +1003,7 @@ qlen_kill_std(_Config) -> mem_kill_new(Config) -> {_Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), - Pid0 = whereis(?MODULE), + Pid0 = whereis(h_proc_name()), {_,Mem0} = process_info(Pid0, memory), RestartAfter = ?HANDLER_RESTART_AFTER, NewHConfig = @@ -1021,7 +1026,7 @@ mem_kill_new(Config) -> ct:pal("Slow shutdown, handler process was killed!", []) end, timer:sleep(RestartAfter * 3), - true = is_pid(whereis(?MODULE)), + true = is_pid(whereis(h_proc_name())), ok after 5000 -> @@ -1044,13 +1049,13 @@ restart_after(Config) -> handler_overloaded_qlen=>10, handler_restart_after=>never}}, ok = logger:set_handler_config(?MODULE, NewHConfig1), - MRef1 = erlang:monitor(process, whereis(?MODULE)), + MRef1 = erlang:monitor(process, whereis(h_proc_name())), %% kill handler send_burst({n,100}, {spawn,2,0}, {chars,79}, info), receive {'DOWN', MRef1, _, _, _Info1} -> timer:sleep(?HANDLER_RESTART_AFTER + 1000), - undefined = whereis(?MODULE), + undefined = whereis(h_proc_name()), ok after 5000 -> @@ -1064,14 +1069,14 @@ restart_after(Config) -> handler_overloaded_qlen=>10, handler_restart_after=>RestartAfter}}, ok = logger:set_handler_config(?MODULE, NewHConfig2), - Pid0 = whereis(?MODULE), + Pid0 = whereis(h_proc_name()), MRef2 = erlang:monitor(process, Pid0), %% kill handler send_burst({n,100}, {spawn,2,0}, {chars,79}, info), receive {'DOWN', MRef2, _, _, _Info2} -> timer:sleep(RestartAfter + 2000), - Pid1 = whereis(?MODULE), + Pid1 = whereis(h_proc_name()), true = is_pid(Pid1), false = (Pid1 == Pid0), ok @@ -1286,7 +1291,7 @@ add_remove_instance_nofile(Type) -> filter_default=>stop, filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,self()}}), - Pid = whereis(?MODULE), + Pid = whereis(h_proc_name()), true = is_pid(Pid), group_leader(group_leader(),Pid), % to get printouts in test log logger:info(M1=?msg,?domain), @@ -1295,7 +1300,7 @@ add_remove_instance_nofile(Type) -> ok = logger_std_h:sync(?MODULE), ok = logger:remove_handler(?MODULE), timer:sleep(500), - undefined = whereis(?MODULE), + undefined = whereis(h_proc_name()), logger:info(?msg,?domain), ?check_no_log, ok. @@ -1379,7 +1384,7 @@ start_op_trace() -> TRecvPid = spawn_link(fun() -> trace_receiver(5000) end), {ok,_} = dbg:tracer(process, {TraceFun, TRecvPid}), - {ok,_} = dbg:p(whereis(?MODULE), [c]), + {ok,_} = dbg:p(whereis(h_proc_name()), [c]), {ok,_} = dbg:p(self(), [c]), MS1 = dbg:fun2ms(fun([_]) -> return_trace() end), @@ -1459,7 +1464,7 @@ 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(whereis(h_proc_name()),[c]), dbg:p(FileCtrlPid,[c]), tpl(Trace), ok. @@ -1525,3 +1530,8 @@ escape([H|T]) -> [H|escape(T)]; escape([]) -> []. + +h_proc_name() -> + h_proc_name(?MODULE). +h_proc_name(Name) -> + ?name_to_reg_name(logger_std_h,Name). -- cgit v1.2.3 From 983a945b84e15f58c3205c37b7bfcad18537325c Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 4 Jun 2018 19:53:02 +0200 Subject: [logger] Add configurable log levels 'all' and 'none' New default for handlers is 'all'. --- lib/kernel/src/logger.erl | 12 +++---- lib/kernel/src/logger_config.erl | 8 +++-- lib/kernel/src/logger_internal.hrl | 8 +++-- lib/kernel/src/logger_server.erl | 2 +- lib/kernel/test/logger_SUITE.erl | 55 ++++++++++++++++++++++++++++---- lib/kernel/test/logger_env_var_SUITE.erl | 36 ++++++++++----------- 6 files changed, 85 insertions(+), 36 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 395ec448ff..f18f101930 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -96,7 +96,7 @@ -type filter_arg() :: term(). -type filter_return() :: stop | ignore | log_event(). -type config() :: #{id => handler_id(), - level => level(), + level => level() | all | none, filter_default => log | stop, filters => [{filter_id(),filter()}], formatter => {module(),formatter_config()}, @@ -412,7 +412,7 @@ update_formatter_config(HandlerId,Key,Value) -> -spec set_module_level(Modules,Level) -> ok | {error,term()} when Modules :: [module()] | module(), - Level :: level(). + Level :: level() | all | none. set_module_level(Module,Level) when is_atom(Module) -> set_module_level([Module],Level); set_module_level(Modules,Level) -> @@ -432,7 +432,7 @@ unset_module_level() -> -spec get_module_level(Modules) -> [{Module,Level}] when Modules :: [Module] | Module, Module :: module(), - Level :: level(). + Level :: level() | all | none. get_module_level(Module) when is_atom(Module) -> get_module_level([Module]); get_module_level(Modules) when is_list(Modules) -> @@ -441,7 +441,7 @@ get_module_level(Modules) when is_list(Modules) -> -spec get_module_level() -> [{Module,Level}] when Module :: module(), - Level :: level(). + Level :: level() | all | none. get_module_level() -> logger_config:get_module_level(?LOGGER_TABLE). @@ -494,13 +494,13 @@ unset_process_metadata() -> -spec i() -> #{logger=>config(), handlers=>[{handler_id(),module(),config()}], - module_levels=>[{module(),level()}]}. + module_levels=>[{module(),level() | all | none}]}. i() -> i(term). -spec i(term) -> #{logger=>config(), handlers=>[{handler_id(),module(),config()}], - module_levels=>[{module(),level()}]}; + module_levels=>[{module(),level() | all | none}]}; (print) -> ok; (string) -> iolist(). i(_Action = print) -> diff --git a/lib/kernel/src/logger_config.erl b/lib/kernel/src/logger_config.erl index 8d05648109..113c8140cf 100644 --- a/lib/kernel/src/logger_config.erl +++ b/lib/kernel/src/logger_config.erl @@ -136,6 +136,7 @@ get(Tid) -> Modules = get_module_level(Tid), {Logger,Handlers,Modules}. +level_to_int(none) -> ?LOG_NONE; level_to_int(emergency) -> ?EMERGENCY; level_to_int(alert) -> ?ALERT; level_to_int(critical) -> ?CRITICAL; @@ -143,8 +144,10 @@ level_to_int(error) -> ?ERROR; level_to_int(warning) -> ?WARNING; level_to_int(notice) -> ?NOTICE; level_to_int(info) -> ?INFO; -level_to_int(debug) -> ?DEBUG. +level_to_int(debug) -> ?DEBUG; +level_to_int(all) -> ?LOG_ALL. +int_to_level(?LOG_NONE) -> none; int_to_level(?EMERGENCY) -> emergency; int_to_level(?ALERT) -> alert; int_to_level(?CRITICAL) -> critical; @@ -152,7 +155,8 @@ int_to_level(?ERROR) -> error; int_to_level(?WARNING) -> warning; int_to_level(?NOTICE) -> notice; int_to_level(?INFO) -> info; -int_to_level(?DEBUG) -> debug. +int_to_level(?DEBUG) -> debug; +int_to_level(?LOG_ALL) -> all. %%%----------------------------------------------------------------- %%% Internal diff --git a/lib/kernel/src/logger_internal.hrl b/lib/kernel/src/logger_internal.hrl index 1325e38f74..a6648d2b2c 100644 --- a/lib/kernel/src/logger_internal.hrl +++ b/lib/kernel/src/logger_internal.hrl @@ -54,14 +54,17 @@ %%%----------------------------------------------------------------- %%% Levels %%% Using same as syslog --define(LEVELS,[emergency, +-define(LEVELS,[none, + emergency, alert, critical, error, warning, notice, info, - debug]). + debug, + all]). +-define(LOG_NONE,-1). -define(EMERGENCY,0). -define(ALERT,1). -define(CRITICAL,2). @@ -70,6 +73,7 @@ -define(NOTICE,5). -define(INFO,6). -define(DEBUG,7). +-define(LOG_ALL,10). -define(IS_LEVEL(L), (L=:=emergency orelse diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index 164e193fa0..adbb2bc009 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -361,7 +361,7 @@ default_config(logger) -> filter_default=>log}; default_config(Id) -> #{id=>Id, - level=>debug, + level=>all, filters=>[], filter_default=>log, formatter=>{?DEFAULT_FORMATTER,#{}}}. diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index a8769c450c..91aaceada5 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -82,6 +82,7 @@ all() -> add_remove_filter, change_config, set_formatter, + log_no_levels, log_all_levels_api, macros, set_level, @@ -111,7 +112,7 @@ add_remove_handler(_Config) -> [add] = test_server:messages_get(), #{handlers:=Hs} = logger:i(), {value,_,Hs0} = lists:keytake(h1,1,Hs), - {ok,{?MODULE,#{level:=debug,filters:=[],filter_default:=log}}} = % defaults + {ok,{?MODULE,#{level:=all,filters:=[],filter_default:=log}}} = % defaults logger:get_handler_config(h1), ok = logger:set_handler_config(h1,filter_default,stop), [changing_config] = test_server:messages_get(), @@ -215,7 +216,7 @@ change_config(_Config) -> register(callback_receiver,self()), ok = logger:set_handler_config(h1,#{filter_default=>stop}), [changing_config] = test_server:messages_get(), - {ok,{?MODULE,#{level:=debug,filter_default:=stop}=C2}} = + {ok,{?MODULE,#{level:=all,filter_default:=stop}=C2}} = logger:get_handler_config(h1), false = maps:is_key(custom,C2), {error,fail} = logger:set_handler_config(h1,#{conf_call=>fun() -> {error,fail} end}), @@ -291,9 +292,50 @@ set_formatter(cleanup,_Config) -> logger:remove_handler(h1), ok. +log_no_levels(_Config) -> + ok = logger:add_handler(h1,?MODULE,#{level=>all,filter_default=>log}), + logger:notice(M1=?map_rep), + ok = check_logged(notice,M1,#{}), + + Levels = [emergency,alert,critical,error,warning,notice,info,debug], + ok = logger:set_logger_config(level,none), + [logger:Level(#{Level=>rep}) || Level <- Levels], + ok = check_no_log(), + + ok = logger:set_logger_config(level,all), + M2 = ?map_rep, + ?LOG_NOTICE(M2), + ok = check_logged(notice,M2,#{}), + + ok = logger:set_module_level(?MODULE,none), + ?LOG_EMERGENCY(?map_rep), + ?LOG_ALERT(?map_rep), + ?LOG_CRITICAL(?map_rep), + ?LOG_ERROR(?map_rep), + ?LOG_WARNING(?map_rep), + ?LOG_NOTICE(?map_rep), + ?LOG_INFO(?map_rep), + ?LOG_DEBUG(?map_rep), + ok = check_no_log(), + + ok = logger:unset_module_level(?MODULE), + logger:notice(M3=?map_rep), + ok = check_logged(notice,M3,#{}), + + ok = logger:set_handler_config(h1,level,none), + [logger:Level(#{Level=>rep}) || Level <- Levels], + ok = check_no_log(), + + ok. +log_no_levels(cleanup,_Config) -> + logger:remove_handler(h1), + logger:set_logger_level(level,info), + logger:unset_module_level(?MODULE), + ok. + log_all_levels_api(_Config) -> - ok = logger:set_logger_config(level,debug), - ok = logger:add_handler(h1,?MODULE,#{level=>debug,filter_default=>log}), + ok = logger:set_logger_config(level,all), + ok = logger:add_handler(h1,?MODULE,#{level=>all,filter_default=>log}), test_api(emergency), test_api(alert), test_api(critical), @@ -311,8 +353,7 @@ log_all_levels_api(cleanup,_Config) -> ok. macros(_Config) -> - ok = logger:set_module_level(?MODULE,debug), - ok = logger:add_handler(h1,?MODULE,#{level=>debug,filter_default=>log}), + ok = logger:add_handler(h1,?MODULE,#{level=>all,filter_default=>log}), test_macros(emergency), ok. @@ -322,7 +363,7 @@ macros(cleanup,_Config) -> ok. set_level(_Config) -> - ok = logger:add_handler(h1,?MODULE,#{level=>debug,filter_default=>log}), + ok = logger:add_handler(h1,?MODULE,#{level=>all,filter_default=>log}), logger:debug(?map_rep), ok = check_no_log(), logger:info(M1=?map_rep), diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl index 03827c9825..f7f293c7fe 100644 --- a/lib/kernel/test/logger_env_var_SUITE.erl +++ b/lib/kernel/test/logger_env_var_SUITE.erl @@ -80,7 +80,7 @@ all() -> default(Config) -> {ok,#{handlers:=Hs},_Node} = setup(Config,[]), {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - debug = maps:get(level,StdC), + all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), true = lists:keymember(stop_progress,1,StdFilters), @@ -92,7 +92,7 @@ default_sasl_compatible(Config) -> {ok,#{handlers:=Hs},_Node} = setup(Config, [{logger_sasl_compatible,true}]), {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - debug = maps:get(level,StdC), + all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), @@ -103,7 +103,7 @@ default_sasl_compatible(Config) -> 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), - debug = maps:get(level,StdC), + all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), true = lists:keymember(stop_progress,1,StdFilters), @@ -116,7 +116,7 @@ error_logger_tty_sasl_compatible(Config) -> [{error_logger,tty}, {logger_sasl_compatible,true}]), {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - debug = maps:get(level,StdC), + all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), @@ -131,7 +131,7 @@ error_logger_false(Config) -> {logger_level,notice}]), false = lists:keymember(?STANDARD_HANDLER,1,Hs), {simple,logger_simple_h,SimpleC} = lists:keyfind(simple,1,Hs), - debug = maps:get(level,SimpleC), + all = maps:get(level,SimpleC), notice = maps:get(level,L), SimpleFilters = maps:get(filters,SimpleC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), @@ -147,7 +147,7 @@ error_logger_false_progress(Config) -> {logger_progress_reports,log}]), false = lists:keymember(?STANDARD_HANDLER,1,Hs), {simple,logger_simple_h,SimpleC} = lists:keyfind(simple,1,Hs), - debug = maps:get(level,SimpleC), + all = maps:get(level,SimpleC), notice = maps:get(level,L), SimpleFilters = maps:get(filters,SimpleC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), @@ -163,7 +163,7 @@ error_logger_false_sasl_compatible(Config) -> {logger_sasl_compatible,true}]), false = lists:keymember(?STANDARD_HANDLER,1,Hs), {simple,logger_simple_h,SimpleC} = lists:keyfind(simple,1,Hs), - debug = maps:get(level,SimpleC), + all = maps:get(level,SimpleC), notice = maps:get(level,L), SimpleFilters = maps:get(filters,SimpleC), {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,SimpleFilters), @@ -211,7 +211,7 @@ logger_file(Config) -> 0),% progress in std logger {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - debug = maps:get(level,StdC), + all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), true = lists:keymember(stop_progress,1,StdFilters), @@ -233,7 +233,7 @@ logger_file_sasl_compatible(Config) -> 0),% progress in std logger {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - debug = maps:get(level,StdC), + all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), @@ -255,7 +255,7 @@ logger_file_log_progress(Config) -> 6),% progress in std logger {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - debug = maps:get(level,StdC), + all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), @@ -277,7 +277,7 @@ logger_file_no_filter(Config) -> 6),% progress in std logger {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - debug = maps:get(level,StdC), + all = maps:get(level,StdC), [] = maps:get(filters,StdC), false = lists:keymember(simple,1,Hs), false = lists:keymember(sasl,1,Hs), @@ -319,7 +319,7 @@ logger_file_formatter(Config) -> 6),% progress in std logger {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - debug = maps:get(level,StdC), + all = maps:get(level,StdC), [] = maps:get(filters,StdC), false = lists:keymember(simple,1,Hs), false = lists:keymember(sasl,1,Hs), @@ -341,7 +341,7 @@ logger_filters(Config) -> 0),% progress in std logger {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - debug = maps:get(level,StdC), + all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), @@ -369,7 +369,7 @@ logger_filters_stop(Config) -> notice), {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - debug = maps:get(level,StdC), + all = maps:get(level,StdC), [] = maps:get(filters,StdC), false = lists:keymember(simple,1,Hs), false = lists:keymember(sasl,1,Hs), @@ -393,7 +393,7 @@ logger_module_level(Config) -> 3),% progress in std logger {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - debug = maps:get(level,StdC), + all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), @@ -414,7 +414,7 @@ logger_disk_log(Config) -> 0),% progress in std logger {?STANDARD_HANDLER,logger_disk_log_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - debug = maps:get(level,StdC), + all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), true = lists:keymember(stop_progress,1,StdFilters), @@ -437,7 +437,7 @@ logger_disk_log_formatter(Config) -> 6),% progress in std logger {?STANDARD_HANDLER,logger_disk_log_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), - debug = maps:get(level,StdC), + all = maps:get(level,StdC), [] = maps:get(filters,StdC), false = lists:keymember(simple,1,Hs), false = lists:keymember(sasl,1,Hs), @@ -449,7 +449,7 @@ logger_undefined(Config) -> setup(Config,[{logger,[{handler,?STANDARD_HANDLER,undefined}]}]), false = lists:keymember(?STANDARD_HANDLER,1,Hs), {simple,logger_simple_h,SimpleC} = lists:keyfind(simple,1,Hs), - debug = maps:get(level,SimpleC), + all = maps:get(level,SimpleC), info = maps:get(level,L), SimpleFilters = maps:get(filters,SimpleC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), -- cgit v1.2.3 From 437d46f34b141af8e6361bfc5b2fe8dab9d88efd Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 5 Jun 2018 12:35:37 +0200 Subject: [logger] Add if-exist functionality for metadata keys in formatter template * Nested metadata keys are now expressed as list of atoms (was earlier tuples). * If-exist is expressed as: {Key,IfExist,Else} Key :: atom() | [atom()] IfExist :: template() Else :: template() --- lib/kernel/src/logger_formatter.erl | 100 ++++++++++++++------- lib/kernel/src/logger_internal.hrl | 2 +- lib/kernel/test/logger_SUITE.erl | 2 +- lib/kernel/test/logger_formatter_SUITE.erl | 139 ++++++++++++++++++++++++++--- 4 files changed, 200 insertions(+), 43 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl index 4d727b3da0..6eeac994ea 100644 --- a/lib/kernel/src/logger_formatter.erl +++ b/lib/kernel/src/logger_formatter.erl @@ -53,8 +53,8 @@ format(#{level:=Level,msg:=Msg0,meta:=Meta},Config0) [msg|Rest] -> {true,Rest}; _ ->{false,AT0} end, - B = do_format(Level,"",Meta1,BT,Config), - A = do_format(Level,"",Meta1,AT,Config), + B = do_format(Level,Meta1,BT,Config), + A = do_format(Level,Meta1,AT,Config), MsgStr = if DoMsg -> Config1 = @@ -84,26 +84,37 @@ format(#{level:=Level,msg:=Msg0,meta:=Meta},Config0) end, truncate(B ++ MsgStr ++ A,maps:get(max_size,Config)). -do_format(Level,Msg,Data,[level|Format],Config) -> - [to_string(level,Level,Config)|do_format(Level,Msg,Data,Format,Config)]; -do_format(Level,Msg,Data,[Key|Format],Config) when is_atom(Key); is_tuple(Key) -> - Value = value(Key,Data), - [to_string(Key,Value,Config)|do_format(Level,Msg,Data,Format,Config)]; -do_format(Level,Msg,Data,[Str|Format],Config) -> - [Str|do_format(Level,Msg,Data,Format,Config)]; -do_format(_Level,_Msg,_Data,[],_Config) -> +do_format(Level,Data,[level|Format],Config) -> + [to_string(level,Level,Config)|do_format(Level,Data,Format,Config)]; +do_format(Level,Data,[{Key,IfExist,Else}|Format],Config) -> + String = + case value(Key,Data) of + {ok,Value} -> do_format(Level,Data#{Key=>Value},IfExist,Config); + error -> do_format(Level,Data,Else,Config) + end, + [String|do_format(Level,Data,Format,Config)]; +do_format(Level,Data,[Key|Format],Config) + when is_atom(Key) orelse + (is_list(Key) andalso is_atom(hd(Key))) -> + String = + case value(Key,Data) of + {ok,Value} -> to_string(Key,Value,Config); + error -> "" + end, + [String|do_format(Level,Data,Format,Config)]; +do_format(Level,Data,[Str|Format],Config) -> + [Str|do_format(Level,Data,Format,Config)]; +do_format(_Level,_Data,[],_Config) -> []. -value(Key,Meta) when is_atom(Key), is_map(Meta) -> - maps:get(Key,Meta,""); -value(Key,_) when is_atom(Key) -> - ""; -value(Keys,Meta) when is_tuple(Keys) -> - value(tuple_to_list(Keys),Meta); -value([Key|Keys],Meta) -> - value(Keys,value(Key,Meta)); +value(Key,Meta) when is_map_key(Key,Meta) -> + {ok,maps:get(Key,Meta)}; +value([Key|Keys],Meta) when is_map_key(Key,Meta) -> + value(Keys,maps:get(Key,Meta)); value([],Value) -> - Value. + {ok,Value}; +value(_,_) -> + error. to_string(time,Time,Config) -> format_time(Time,Config); @@ -343,17 +354,10 @@ do_check_config([{legacy_header,LH}|Config]) when is_boolean(LH) -> do_check_config(Config); do_check_config([{report_cb,RCB}|Config]) when is_function(RCB,1) -> do_check_config(Config); -do_check_config([{template,T}|Config]) when is_list(T) -> - case lists:all(fun(X) when is_atom(X) -> true; - (X) when is_tuple(X), is_atom(element(1,X)) -> true; - (X) when is_list(X) -> io_lib:printable_unicode_list(X); - (_) -> false - end, - T) of - true -> - do_check_config(Config); - false -> - {error,{invalid_formatter_template,?MODULE,T}} +do_check_config([{template,T}|Config]) -> + case check_template(T) of + ok -> do_check_config(Config); + error -> {error,{invalid_formatter_template,?MODULE,T}} end; do_check_config([{time_offset,Offset}|Config]) -> case check_offset(Offset) of @@ -381,6 +385,42 @@ check_limit(unlimited) -> check_limit(_) -> error. +check_template([Key|T]) when is_atom(Key) -> + check_template(T); +check_template([Key|T]) when is_list(Key), is_atom(hd(Key)) -> + case lists:all(fun(X) when is_atom(X) -> true; + (_) -> false + end, + Key) of + true -> + check_template(T); + false -> + error + end; +check_template([{Key,IfExist,Else}|T]) + when is_atom(Key) orelse + (is_list(Key) andalso is_atom(hd(Key))) -> + case check_template(IfExist) of + ok -> + case check_template(Else) of + ok -> + check_template(T); + error -> + error + end; + error -> + error + end; +check_template([Str|T]) when is_list(Str) -> + case io_lib:printable_unicode_list(Str) of + true -> check_template(T); + false -> error + end; +check_template([]) -> + ok; +check_template(_) -> + error. + check_offset(I) when is_integer(I) -> ok; check_offset(Tz) when Tz=:=""; Tz=:="Z"; Tz=:="z" -> diff --git a/lib/kernel/src/logger_internal.hrl b/lib/kernel/src/logger_internal.hrl index a6648d2b2c..a0962cb8e0 100644 --- a/lib/kernel/src/logger_internal.hrl +++ b/lib/kernel/src/logger_internal.hrl @@ -32,7 +32,7 @@ -define(DEFAULT_FORMAT_CONFIG,#{legacy_header=>true, single_line=>false}). -define(DEFAULT_FORMAT_TEMPLATE_HEADER, - [{logger_formatter,header},"\n",msg,"\n"]). + [[logger_formatter,header],"\n",msg,"\n"]). -define(DEFAULT_FORMAT_TEMPLATE_SINGLE, [time," ",level,": ",msg,"\n"]). -define(DEFAULT_FORMAT_TEMPLATE, diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index 91aaceada5..1057768eeb 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -617,7 +617,7 @@ config_sanity_check(_Config) -> logger:set_handler_config(h1,formatter,{logger_formatter,bad}), {error,{invalid_formatter_config,logger_formatter,{bad,bad}}} = logger:set_handler_config(h1,formatter,{logger_formatter,#{bad=>bad}}), - {error,{invalid_formatter_config,logger_formatter,{template,bad}}} = + {error,{invalid_formatter_template,logger_formatter,bad}} = logger:set_handler_config(h1,formatter,{logger_formatter, #{template=>bad}}), {error,{invalid_formatter_template,logger_formatter,[1]}} = diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl index 7a93f2ca79..00fa89271b 100644 --- a/lib/kernel/test/logger_formatter_SUITE.erl +++ b/lib/kernel/test/logger_formatter_SUITE.erl @@ -69,6 +69,7 @@ all() -> faulty_log, faulty_config, faulty_msg, + check_config, update_config]. default(_Config) -> @@ -158,7 +159,7 @@ template(_Config) -> Template4 = ["string\nnewline"], String4 = format(info,{"~p",[term]},#{time=>Time},#{template=>Template4, - single_line=>true}), + single_line=>true}), ct:log(String4), "string\nnewline" = String4, @@ -179,7 +180,7 @@ template(_Config) -> tuple=>{1,atom,"list"}, nested=>#{subkey=>subvalue}}, Template6 = lists:join(";",maps:keys(maps:remove(nested,Meta6)) ++ - [{nested,subkey}]), + [[nested,subkey]]), String6 = format(info,{"~p",[term]},Meta6,#{template=>Template6, single_line=>true}), ct:log(String6), @@ -202,16 +203,16 @@ template(_Config) -> nested=>#{key1=>#{subkey1=>value1}, key2=>value2}}, Template7 = lists:join(";",[nested, - {nested,key1}, - {nested,key1,subkey1}, - {nested,key2}, - {nested,key2,subkey2}, - {nested,key3}, - {nested,key3,subkey3}]), + [nested,key1], + [nested,key1,subkey1], + [nested,key2], + [nested,key2,subkey2], + [nested,key3], + [nested,key3,subkey3]]), String7 = format(info,{"~p",[term]},Meta7,#{template=>Template7, single_line=>true}), ct:log(String7), - [MultipleKeysStr, + [MultipleKeysStr7, "#{subkey1 => value1}", "value1", "value2", @@ -219,11 +220,42 @@ template(_Config) -> "", ""] = string:split(String7,";",all), %% Order of keys is not fixed - case MultipleKeysStr of + case MultipleKeysStr7 of "#{key2 => value2,key1 => #{subkey1 => value1}}" -> ok; "#{key1 => #{subkey1 => value1},key2 => value2}" -> ok; - _ -> ct:fail({full_nested_map_unexpected,MultipleKeysStr}) + _ -> ct:fail({full_nested_map_unexpected,MultipleKeysStr7}) end, + + Meta8 = #{time=>Time, + nested=>#{key1=>#{subkey1=>value1}, + key2=>value2}}, + Template8 = + lists:join( + ";", + [{nested,["exist:",nested],["noexist"]}, + {[nested,key1],["exist:",[nested,key1]],["noexist"]}, + {[nested,key1,subkey1],["exist:",[nested,key1,subkey1]],["noexist"]}, + {[nested,key2],["exist:",[nested,key2]],["noexist"]}, + {[nested,key2,subkey2],["exist:",[nested,key2,subkey2]],["noexist"]}, + {[nested,key3],["exist:",[nested,key3]],["noexist"]}, + {[nested,key3,subkey3],["exist:",[nested,key3,subkey3]],["noexist"]}]), + String8 = format(info,{"~p",[term]},Meta8,#{template=>Template8, + single_line=>true}), + ct:log(String8), + [MultipleKeysStr8, + "exist:#{subkey1 => value1}", + "exist:value1", + "exist:value2", + "noexist", + "noexist", + "noexist"] = string:split(String8,";",all), + %% Order of keys is not fixed + case MultipleKeysStr8 of + "exist:#{key2 => value2,key1 => #{subkey1 => value1}}" -> ok; + "exist:#{key1 => #{subkey1 => value1},key2 => value2}" -> ok; + _ -> ct:fail({full_nested_map_unexpected,MultipleKeysStr8}) + end, + ok. format_msg(_Config) -> @@ -541,6 +573,91 @@ faulty_msg(_Config) -> #{})), ok. +-define(cfgerr(X), {error,{invalid_formatter_config,logger_formatter,X}}). +check_config(_Config) -> + ok = logger_formatter:check_config(#{}), + ?cfgerr(bad) = logger_formatter:check_config(bad), + + C1 = #{chars_limit => 1, + depth => 1, + legacy_header => true, + max_size => 1, + report_cb => fun(R) -> {"~p",[R]} end, + single_line => false, + template => [], + time_designator => $T, + time_offset => 0}, + ok = logger_formatter:check_config(C1), + + ok = logger_formatter:check_config(#{chars_limit => unlimited}), + ?cfgerr({chars_limit,bad}) = + logger_formatter:check_config(#{chars_limit => bad}), + + ok = logger_formatter:check_config(#{depth => unlimited}), + ?cfgerr({depth,bad}) = + logger_formatter:check_config(#{depth => bad}), + + ok = logger_formatter:check_config(#{legacy_header => false}), + ?cfgerr({legacy_header,bad}) = + logger_formatter:check_config(#{legacy_header => bad}), + + ok = logger_formatter:check_config(#{max_size => unlimited}), + ?cfgerr({max_size,bad}) = + logger_formatter:check_config(#{max_size => bad}), + + ?cfgerr({report_cb,F}) = + logger_formatter:check_config(#{report_cb => F=fun(_,_) -> {"",[]} end}), + ?cfgerr({report_cb,bad}) = + logger_formatter:check_config(#{report_cb => bad}), + + ok = logger_formatter:check_config(#{single_line => true}), + ?cfgerr({single_line,bad}) = + logger_formatter:check_config(#{single_line => bad}), + + Ts = [[key], + [[key1,key2]], + [{key,[key],[]}], + [{[key1,key2],[[key1,key2]],["noexist"]}], + ["string"]], + [begin + ct:log("check template: ~p",[T]), + ok = logger_formatter:check_config(#{template => T}) + end + || T <- Ts], + + ETs = [bad, + [{key,bad}], + [{key,[key],bad}], + [{key,[key],"bad"}], + "bad", + [[key,$a,$b,$c]], + [[$a,$b,$c,key]]], + [begin + ct:log("check template: ~p",[T]), + {error,{invalid_formatter_template,logger_formatter,T}} = + logger_formatter:check_config(#{template => T}) + end + || T <- ETs], + + ?cfgerr({time_designator,bad}) = + logger_formatter:check_config(#{time_designator => bad}), + ?cfgerr({time_designator,"b"}) = + logger_formatter:check_config(#{time_designator => "b"}), + + ok = logger_formatter:check_config(#{time_offset => -1}), + ok = logger_formatter:check_config(#{time_offset => "+02:00"}), + ok = logger_formatter:check_config(#{time_offset => "-23:59"}), + ok = logger_formatter:check_config(#{time_offset => "+24:00"}), + ok = logger_formatter:check_config(#{time_offset => "-25:00"}), + ?cfgerr({time_offset,bad}) = + logger_formatter:check_config(#{time_offset => bad}), + ?cfgerr({time_offset,"02:00"}) = + logger_formatter:check_config(#{time_offset => "02:00"}), + ?cfgerr({time_offset,"+02"}) = + logger_formatter:check_config(#{time_offset => "+02"}), + + ok. + %% Test that formatter config can be changed, and that the default %% template is updated accordingly update_config(_Config) -> -- cgit v1.2.3 From 6f753ccf5178a809a7a26f692249bdfb5985335e Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 5 Jun 2018 19:22:16 +0200 Subject: [logger] Don't use error_logger:warning_map/0 The warning map was added to allow backwards compatiblity with error_logger event handlers that were not prepared to receive warning reports. error_logger still uses this internally, if legacy error logger event handlers are installed. But Logger does not use it for new Logger handlers. --- lib/kernel/src/error_logger.erl | 53 +++++++++++++---------------------------- lib/kernel/src/logger.erl | 2 -- 2 files changed, 17 insertions(+), 38 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index 9238029cef..4b3922cdd7 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -169,16 +169,25 @@ do_log(_Level,_Msg,_Meta) -> -spec notify(logger:level(), msg_tag(), any(), any(), map()) -> 'ok'. notify(Level,Tag0,FormatOrType0,ArgsOrReport,#{pid:=Pid0,gl:=GL,?MODULE:=My}) -> - Tag = fix_warning_tag(Level,Tag0), + {Tag,FormatOrType} = maybe_map_warnings(Level,Tag0,FormatOrType0), Pid = case maps:get(emulator,My,false) of true -> emulator; _ -> Pid0 end, - FormatOrType = fix_warning_type(Level,FormatOrType0), gen_event:notify(?MODULE,{Tag,GL,{Pid,FormatOrType,ArgsOrReport}}). -%% This is to fix the case when the client has explicitly added the -%% error logger tag and type in metadata, and not checked the warning map. +%% For backwards compatibility with really old even handlers, check +%% the warning map and update tag and type. +maybe_map_warnings(warning,Tag,FormatOrType) -> + case error_logger:warning_map() of + warning -> + {Tag,FormatOrType}; + Level -> + {fix_warning_tag(Level,Tag),fix_warning_type(Level,FormatOrType)} + end; +maybe_map_warnings(_,Tag,FormatOrType) -> + {Tag,FormatOrType}. + fix_warning_tag(error,warning_msg) -> error; fix_warning_tag(error,warning_report) -> error_report; fix_warning_tag(info,warning_msg) -> info_msg; @@ -263,29 +272,10 @@ warning_report(Report) -> Report :: report(). warning_report(Type, Report) -> - Level = error_logger:warning_map(), - {Tag, NType} = case Level of - info -> - if - Type =:= std_warning -> - {info_report, std_info}; - true -> - {info_report, Type} - end; - warning -> - {warning_report, Type}; - error -> - if - Type =:= std_warning -> - {error_report, std_error}; - true -> - {error_report, Type} - end - end, - logger:log(Level, + logger:log(warning, #{label=>{?MODULE,warning_report}, report=>Report}, - meta(Tag,NType)). + meta(warning_report,Type)). %%----------------------------------------------------------------- %% This function provides similar functions as error_msg for @@ -304,20 +294,11 @@ warning_msg(Format) -> Data :: list(). warning_msg(Format, Args) -> - Level = error_logger:warning_map(), - Tag = case Level of - warning -> - warning_msg; - info -> - info_msg; - error -> - error - end, - logger:log(Level, + logger:log(warning, #{label=>{?MODULE,warning_msg}, format=>Format, args=>Args}, - meta(Tag)). + meta(warning_msg)). %%----------------------------------------------------------------- %% This function should be used for information reports. Events diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index f18f101930..f8661ea228 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -775,8 +775,6 @@ get_logger_env() -> %%%----------------------------------------------------------------- %%% Internal -do_log(warning,Msg,Meta) -> - do_log_1(error_logger:warning_map(),Msg,Meta); do_log(Level,Msg,Meta) -> do_log_1(Level,Msg,Meta). -- cgit v1.2.3 From 35c311607ed5b5a9ddea8c9a9be7c3b41845c737 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 5 Jun 2018 20:03:21 +0200 Subject: [logger] Change concept of 'global' configuration to 'primary' configuration Function names changed: get/set/update_logger_config -> get/set/update_primary_config add/remove_logger_filter -> add/remove_primary_filter --- lib/kernel/src/logger.erl | 68 ++++++++++---------- lib/kernel/src/logger_backend.erl | 4 +- lib/kernel/src/logger_config.erl | 14 ++--- lib/kernel/src/logger_internal.hrl | 2 +- lib/kernel/src/logger_server.erl | 32 +++++----- lib/kernel/test/logger_SUITE.erl | 96 ++++++++++++++--------------- lib/kernel/test/logger_bench_SUITE.erl | 18 +++--- lib/kernel/test/logger_disk_log_h_SUITE.erl | 2 +- lib/kernel/test/logger_env_var_SUITE.erl | 24 ++++---- 9 files changed, 130 insertions(+), 130 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index f8661ea228..9f96a537bc 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -35,16 +35,16 @@ %% Configuration -export([add_handler/3, remove_handler/1, - add_logger_filter/2, add_handler_filter/3, - remove_logger_filter/1, remove_handler_filter/2, + add_primary_filter/2, add_handler_filter/3, + remove_primary_filter/1, remove_handler_filter/2, set_module_level/2, unset_module_level/1, unset_module_level/0, get_module_level/0, get_module_level/1, - set_logger_config/1, set_logger_config/2, + set_primary_config/1, set_primary_config/2, set_handler_config/2, set_handler_config/3, - update_logger_config/1, update_handler_config/2, + update_primary_config/1, update_handler_config/2, update_formatter_config/2, update_formatter_config/3, - get_logger_config/0, get_handler_config/1, + get_primary_config/0, get_handler_config/1, add_handlers/1]). %% Private configuration @@ -310,11 +310,11 @@ internal_log(Level,Term) when is_atom(Level) -> %%%----------------------------------------------------------------- %%% Configuration --spec add_logger_filter(FilterId,Filter) -> ok | {error,term()} when +-spec add_primary_filter(FilterId,Filter) -> ok | {error,term()} when FilterId :: filter_id(), Filter :: filter(). -add_logger_filter(FilterId,Filter) -> - logger_server:add_filter(logger,{FilterId,Filter}). +add_primary_filter(FilterId,Filter) -> + logger_server:add_filter(primary,{FilterId,Filter}). -spec add_handler_filter(HandlerId,FilterId,Filter) -> ok | {error,term()} when HandlerId :: handler_id(), @@ -324,10 +324,10 @@ add_handler_filter(HandlerId,FilterId,Filter) -> logger_server:add_filter(HandlerId,{FilterId,Filter}). --spec remove_logger_filter(FilterId) -> ok | {error,term()} when +-spec remove_primary_filter(FilterId) -> ok | {error,term()} when FilterId :: filter_id(). -remove_logger_filter(FilterId) -> - logger_server:remove_filter(logger,FilterId). +remove_primary_filter(FilterId) -> + logger_server:remove_filter(primary,FilterId). -spec remove_handler_filter(HandlerId,FilterId) -> ok | {error,term()} when HandlerId :: handler_id(), @@ -347,16 +347,16 @@ add_handler(HandlerId,Module,Config) -> remove_handler(HandlerId) -> logger_server:remove_handler(HandlerId). --spec set_logger_config(Key,Value) -> ok | {error,term()} when +-spec set_primary_config(Key,Value) -> ok | {error,term()} when Key :: atom(), Value :: term(). -set_logger_config(Key,Value) -> - logger_server:set_config(logger,Key,Value). +set_primary_config(Key,Value) -> + logger_server:set_config(primary,Key,Value). --spec set_logger_config(Config) -> ok | {error,term()} when +-spec set_primary_config(Config) -> ok | {error,term()} when Config :: config(). -set_logger_config(Config) -> - logger_server:set_config(logger,Config). +set_primary_config(Config) -> + logger_server:set_config(primary,Config). -spec set_handler_config(HandlerId,Key,Value) -> ok | {error,term()} when HandlerId :: handler_id(), @@ -371,10 +371,10 @@ set_handler_config(HandlerId,Key,Value) -> set_handler_config(HandlerId,Config) -> logger_server:set_config(HandlerId,Config). --spec update_logger_config(Config) -> ok | {error,term()} when +-spec update_primary_config(Config) -> ok | {error,term()} when Config :: config(). -update_logger_config(Config) -> - logger_server:update_config(logger,Config). +update_primary_config(Config) -> + logger_server:update_config(primary,Config). -spec update_handler_config(HandlerId,Config) -> ok | {error,term()} when HandlerId :: handler_id(), @@ -382,10 +382,10 @@ update_logger_config(Config) -> update_handler_config(HandlerId,Config) -> logger_server:update_config(HandlerId,Config). --spec get_logger_config() -> {ok,Config} when +-spec get_primary_config() -> {ok,Config} when Config :: config(). -get_logger_config() -> - {ok,Config} = logger_config:get(?LOGGER_TABLE,logger), +get_primary_config() -> + {ok,Config} = logger_config:get(?LOGGER_TABLE,primary), {ok,maps:remove(handlers,Config)}. -spec get_handler_config(HandlerId) -> {ok,{Module,Config}} | {error,term()} when @@ -492,13 +492,13 @@ unset_process_metadata() -> _ = erase(?LOGGER_META_KEY), ok. --spec i() -> #{logger=>config(), +-spec i() -> #{primary=>config(), handlers=>[{handler_id(),module(),config()}], module_levels=>[{module(),level() | all | none}]}. i() -> i(term). --spec i(term) -> #{logger=>config(), +-spec i(term) -> #{primary=>config(), handlers=>[{handler_id(),module(),config()}], module_levels=>[{module(),level() | all | none}]}; (print) -> ok; @@ -506,7 +506,7 @@ i() -> i(_Action = print) -> io:put_chars(i(string)); i(_Action = string) -> - #{logger := #{level := Level, + #{primary := #{level := Level, filters := Filters, filter_default := FilterDefault}, handlers := HandlerConfigs, @@ -522,8 +522,8 @@ i(_Action = string) -> print_module_levels(Modules) ]; i(_Action = term) -> - {Logger, Handlers, Modules} = logger_config:get(?LOGGER_TABLE), - #{logger=>maps:remove(handlers,Logger), + {Primary, Handlers, Modules} = logger_config:get(?LOGGER_TABLE), + #{primary=>maps:remove(handlers,Primary), handlers=>lists:keysort(1,Handlers), module_levels=>lists:keysort(1,Modules)}. @@ -576,13 +576,13 @@ print_module_levels(ModuleLevels) -> %% 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()), + ok = logger:set_primary_config(level, get_logger_level()), + ok = logger:set_primary_config(filter_default, get_primary_filter_default()), - [case logger:add_logger_filter(Id, Filter) of + [case logger:add_primary_filter(Id, Filter) of ok -> ok; {error, Reason} -> throw(Reason) - end || {Id, Filter} <- get_logger_filters()], + end || {Id, Filter} <- get_primary_filters()], _ = [[case logger:set_module_level(Module, Level) of ok -> ok; @@ -703,7 +703,7 @@ get_logger_level() -> throw({logger_level, Level}) end. -get_logger_filter_default() -> +get_primary_filter_default() -> case lists:keyfind(filters,1,get_logger_env()) of {filters,Default,_} -> Default; @@ -711,7 +711,7 @@ get_logger_filter_default() -> log end. -get_logger_filters() -> +get_primary_filters() -> lists:foldl( fun({filters, _, Filters}, _Acc) -> Filters; diff --git a/lib/kernel/src/logger_backend.erl b/lib/kernel/src/logger_backend.erl index b3cf7d67dd..a73b681e0d 100644 --- a/lib/kernel/src/logger_backend.erl +++ b/lib/kernel/src/logger_backend.erl @@ -28,9 +28,9 @@ %%%----------------------------------------------------------------- %%% The default logger backend log_allowed(Log, Tid) -> - {ok,Config} = logger_config:get(Tid,logger), + {ok,Config} = logger_config:get(Tid,primary), Filters = maps:get(filters,Config,[]), - case apply_filters(logger,Log,Filters,Config) of + case apply_filters(primary,Log,Filters,Config) of stop -> ok; Log1 -> diff --git a/lib/kernel/src/logger_config.erl b/lib/kernel/src/logger_config.erl index 113c8140cf..7b2148d034 100644 --- a/lib/kernel/src/logger_config.erl +++ b/lib/kernel/src/logger_config.erl @@ -54,7 +54,7 @@ allow(Tid,Level,Module) -> end. allow(Tid,Level) -> - GlobalLevelInt = ets:lookup_element(Tid,?LOGGER_KEY,2), + GlobalLevelInt = ets:lookup_element(Tid,?PRIMARY_KEY,2), level_to_int(Level) =< GlobalLevelInt. exist(Tid,What) -> @@ -71,7 +71,7 @@ get(Tid,What) -> end. get(Tid,What,Level) -> - MS = [{{table_key(What),'$1','$2'}, % logger config + MS = [{{table_key(What),'$1','$2'}, % primary config [{'>=','$1',level_to_int(Level)}], ['$2']}, {{table_key(What),'$1','$2','$3'}, % handler config @@ -94,7 +94,7 @@ set(Tid,What,Config) -> %% Should do this only if the level has actually changed. Possibly %% overwrite instead of delete? case What of - logger -> + primary -> _ = ets:select_delete(Tid,[{{'_',{'$1',cached}}, [{'=/=','$1',LevelInt}], [true]}]), @@ -125,16 +125,16 @@ get_module_level(Tid) -> lists:sort([{M,int_to_level(L)} || {M,L} <- Modules]). cache_module_level(Tid,Module) -> - GlobalLevelInt = ets:lookup_element(Tid,?LOGGER_KEY,2), + GlobalLevelInt = ets:lookup_element(Tid,?PRIMARY_KEY,2), ets:insert_new(Tid,{Module,{GlobalLevelInt,cached}}), ok. get(Tid) -> - {ok,Logger} = get(Tid,logger), + {ok,Primary} = get(Tid,primary), HMS = [{{table_key('$1'),'_','$2','$3'},[],[{{'$1','$3','$2'}}]}], Handlers = ets:select(Tid,HMS), Modules = get_module_level(Tid), - {Logger,Handlers,Modules}. + {Primary,Handlers,Modules}. level_to_int(none) -> ?LOG_NONE; level_to_int(emergency) -> ?EMERGENCY; @@ -161,5 +161,5 @@ int_to_level(?LOG_ALL) -> all. %%%----------------------------------------------------------------- %%% Internal -table_key(logger) -> ?LOGGER_KEY; +table_key(primary) -> ?PRIMARY_KEY; table_key(HandlerId) -> {?HANDLER_KEY,HandlerId}. diff --git a/lib/kernel/src/logger_internal.hrl b/lib/kernel/src/logger_internal.hrl index a0962cb8e0..8941409a25 100644 --- a/lib/kernel/src/logger_internal.hrl +++ b/lib/kernel/src/logger_internal.hrl @@ -19,7 +19,7 @@ %% -include_lib("kernel/include/logger.hrl"). -define(LOGGER_TABLE,logger). --define(LOGGER_KEY,'$logger_config$'). +-define(PRIMARY_KEY,'$primary_config$'). -define(HANDLER_KEY,'$handler_config$'). -define(LOGGER_META_KEY,'$logger_metadata$'). -define(STANDARD_HANDLER, default). diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index adbb2bc009..c7d386ab82 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -78,7 +78,7 @@ remove_filter(Owner,FilterId) -> set_module_level(Modules,Level) when is_list(Modules) -> case lists:all(fun(M) -> is_atom(M) end,Modules) of true -> - case sanity_check(logger,level,Level) of + case sanity_check(primary,level,Level) of ok -> call({set_module_level,Modules,Level}); Error -> Error end; @@ -138,9 +138,9 @@ update_formatter_config(_HandlerId, FormatterConfig) -> init([]) -> process_flag(trap_exit, true), Tid = logger_config:new(?LOGGER_TABLE), - LoggerConfig = maps:merge(default_config(logger), + PrimaryConfig = maps:merge(default_config(primary), #{handlers=>[simple]}), - logger_config:create(Tid,logger,LoggerConfig), + logger_config:create(Tid,primary,PrimaryConfig), SimpleConfig0 = maps:merge(default_config(simple), #{filter_default=>stop, filters=>?DEFAULT_HANDLER_FILTERS}), @@ -166,9 +166,9 @@ handle_call({add_handler,Id,Module,HConfig}, From, #state{tid=Tid}=State) -> case erlang:function_exported(Module, log, 2) of true -> logger_config:create(Tid,Id,Module,HConfig1), - {ok,Config} = do_get_config(Tid,logger), + {ok,Config} = do_get_config(Tid,primary), Handlers = maps:get(handlers,Config,[]), - do_set_config(Tid,logger, + do_set_config(Tid,primary, Config#{handlers=>[Id|Handlers]}); false -> {error,{invalid_handler, @@ -182,7 +182,7 @@ handle_call({add_handler,Id,Module,HConfig}, From, #state{tid=Tid}=State) -> handle_call({remove_handler,HandlerId}, From, #state{tid=Tid}=State) -> case logger_config:get(Tid,HandlerId) of {ok,{Module,HConfig}} -> - {ok,Config} = do_get_config(Tid,logger), + {ok,Config} = do_get_config(Tid,primary), Handlers0 = maps:get(handlers,Config,[]), Handlers = lists:delete(HandlerId,Handlers0), call_h_async( @@ -191,7 +191,7 @@ handle_call({remove_handler,HandlerId}, From, #state{tid=Tid}=State) -> call_h(Module,removing_handler,[HConfig],ok) end, fun(_Res) -> - do_set_config(Tid,logger,Config#{handlers=>Handlers}), + do_set_config(Tid,primary,Config#{handlers=>Handlers}), logger_config:delete(Tid,HandlerId), ok end,From,State); @@ -215,9 +215,9 @@ handle_call({update_config,Id,NewConfig}, From, #state{tid=Tid}=State) -> Error -> {reply,Error,State} end; -handle_call({set_config,logger,Config}, _From, #state{tid=Tid}=State) -> - {ok,#{handlers:=Handlers}} = logger_config:get(Tid,logger), - Reply = do_set_config(Tid,logger,Config#{handlers=>Handlers}), +handle_call({set_config,primary,Config}, _From, #state{tid=Tid}=State) -> + {ok,#{handlers:=Handlers}} = logger_config:get(Tid,primary), + Reply = do_set_config(Tid,primary,Config#{handlers=>Handlers}), {reply,Reply,State}; handle_call({set_config,HandlerId,Config}, From, #state{tid=Tid}=State) -> case logger_config:get(Tid,HandlerId) of @@ -282,7 +282,7 @@ handle_info({Ref,_Reply},State) when is_reference(Ref) -> handle_info({'DOWN',_Ref,_Proc,_Pid,_Reason} = Down,State) -> call_h_reply(Down,State); handle_info(Unexpected,State) when element(1,Unexpected) == 'EXIT' -> - %% The simple logger will send an 'EXIT' message when it is replaced + %% The simple handler will send an 'EXIT' message when it is replaced %% We may as well ignore all 'EXIT' messages that we get ?LOG_INTERNAL(debug, [{logger,got_unexpected_message}, @@ -355,7 +355,7 @@ do_set_config(Tid,Id,Config) -> logger_config:set(Tid,Id,Config), ok. -default_config(logger) -> +default_config(primary) -> #{level=>info, filters=>[], filter_default=>log}; @@ -381,8 +381,8 @@ sanity_check_1(Owner,Config) when is_list(Config) -> catch throw:Error -> {error,Error} end. -get_type(logger) -> - logger; +get_type(primary) -> + primary; get_type(Id) -> check_id(Id), handler. @@ -399,8 +399,8 @@ check_config(Owner,[{filter_default,FD}|Config]) -> check_config(handler,[{formatter,Formatter}|Config]) -> check_formatter(Formatter), check_config(handler,Config); -check_config(logger,[C|_]) -> - throw({invalid_logger_config,C}); +check_config(primary,[C|_]) -> + throw({invalid_primary_config,C}); check_config(handler,[{_,_}|Config]) -> %% Arbitrary config elements are allowed for handlers check_config(handler,Config); diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index 1057768eeb..bbf60cad74 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -63,7 +63,7 @@ end_per_group(_Group, _Config) -> ok. init_per_testcase(_TestCase, Config) -> - {ok,LC} = logger:get_logger_config(), + {ok,LC} = logger:get_primary_config(), [{logger_config,LC}|Config]. end_per_testcase(Case, Config) -> @@ -155,11 +155,11 @@ multiple_handlers(cleanup,_Config) -> add_remove_filter(_Config) -> ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log}), LF = {fun(Log,_) -> Log#{level=>error} end, []}, - ok = logger:add_logger_filter(lf,LF), - {error,{already_exist,lf}} = logger:add_logger_filter(lf,LF), - {error,{already_exist,lf}} = logger:add_logger_filter(lf,{fun(Log,_) -> - Log - end, []}), + ok = logger:add_primary_filter(lf,LF), + {error,{already_exist,lf}} = logger:add_primary_filter(lf,LF), + {error,{already_exist,lf}} = logger:add_primary_filter(lf,{fun(Log,_) -> + Log + end, []}), ?LOG_INFO("hello",[]), ok = check_logged(error,"hello",[],?MY_LOC(1)), ok = check_no_log(), @@ -180,8 +180,8 @@ add_remove_filter(_Config) -> ok = check_logged(mylevel,"hello",[],?MY_LOC(1)), ok = check_logged(error,"hello",[],?MY_LOC(2)), - ok = logger:remove_logger_filter(lf), - {error,{not_found,lf}} = logger:remove_logger_filter(lf), + ok = logger:remove_primary_filter(lf), + {error,{not_found,lf}} = logger:remove_primary_filter(lf), ?LOG_INFO("hello",[]), ok = check_logged(info,"hello",[],?MY_LOC(1)), @@ -203,7 +203,7 @@ add_remove_filter(_Config) -> ok. add_remove_filter(cleanup,_Config) -> - logger:remove_logger_filter(lf), + logger:remove_primary_filter(lf), logger:remove_handler(h1), logger:remove_handler(h2), ok. @@ -241,30 +241,30 @@ change_config(_Config) -> {ok,{_,C4}} = logger:get_handler_config(h1), C4 = C3#{custom:=new_custom}, - %% Change logger config: Single key - {ok,LConfig0} = logger:get_logger_config(), - ok = logger:set_logger_config(level,warning), - {ok,LConfig1} = logger:get_logger_config(), + %% Change primary config: Single key + {ok,LConfig0} = logger:get_primary_config(), + ok = logger:set_primary_config(level,warning), + {ok,LConfig1} = logger:get_primary_config(), LConfig1 = LConfig0#{level:=warning}, - %% Change logger config: Map - ok = logger:update_logger_config(#{level=>error}), - {ok,LConfig2} = logger:get_logger_config(), + %% Change primary config: Map + ok = logger:update_primary_config(#{level=>error}), + {ok,LConfig2} = logger:get_primary_config(), LConfig2 = LConfig1#{level:=error}, - %% Overwrite logger config - check that defaults are added - ok = logger:set_logger_config(#{filter_default=>stop}), + %% Overwrite primary config - check that defaults are added + ok = logger:set_primary_config(#{filter_default=>stop}), {ok,#{level:=info,filters:=[],filter_default:=stop}=LC1} = - logger:get_logger_config(), + logger:get_primary_config(), 3 = maps:size(LC1), %% Check that internal 'handlers' field has not been changed #{handlers:=HCs} = logger:i(), HIds1 = [Id || {Id,_,_} <- HCs], - {ok,#{handlers:=HIds2}} = logger_config:get(?LOGGER_TABLE,logger), + {ok,#{handlers:=HIds2}} = logger_config:get(?LOGGER_TABLE,primary), HIds1 = lists:sort(HIds2), %% Cleanup - ok = logger:set_logger_config(LConfig0), + ok = logger:set_primary_config(LConfig0), [] = test_server:messages_get(), ok. @@ -272,7 +272,7 @@ change_config(_Config) -> change_config(cleanup,Config) -> logger:remove_handler(h1), LC = ?config(logger_config,Config), - logger:set_logger_config(LC), + logger:set_primary_config(LC), ok. set_formatter(_Config) -> @@ -298,11 +298,11 @@ log_no_levels(_Config) -> ok = check_logged(notice,M1,#{}), Levels = [emergency,alert,critical,error,warning,notice,info,debug], - ok = logger:set_logger_config(level,none), + ok = logger:set_primary_config(level,none), [logger:Level(#{Level=>rep}) || Level <- Levels], ok = check_no_log(), - ok = logger:set_logger_config(level,all), + ok = logger:set_primary_config(level,all), M2 = ?map_rep, ?LOG_NOTICE(M2), ok = check_logged(notice,M2,#{}), @@ -329,12 +329,12 @@ log_no_levels(_Config) -> ok. log_no_levels(cleanup,_Config) -> logger:remove_handler(h1), - logger:set_logger_level(level,info), + logger:set_primary_config(level,info), logger:unset_module_level(?MODULE), ok. log_all_levels_api(_Config) -> - ok = logger:set_logger_config(level,all), + ok = logger:set_primary_config(level,all), ok = logger:add_handler(h1,?MODULE,#{level=>all,filter_default=>log}), test_api(emergency), test_api(alert), @@ -349,7 +349,7 @@ log_all_levels_api(_Config) -> log_all_levels_api(cleanup,_Config) -> logger:remove_handler(h1), - logger:set_logger_config(level,info), + logger:set_primary_config(level,info), ok. macros(_Config) -> @@ -368,14 +368,14 @@ set_level(_Config) -> ok = check_no_log(), logger:info(M1=?map_rep), ok = check_logged(info,M1,#{}), - ok = logger:set_logger_config(level,debug), + ok = logger:set_primary_config(level,debug), logger:debug(M2=?map_rep), ok = check_logged(debug,M2,#{}), ok. set_level(cleanup,_Config) -> logger:remove_handler(h1), - logger:set_logger_config(level,info), + logger:set_primary_config(level,info), ok. set_module_level(_Config) -> @@ -424,13 +424,13 @@ set_module_level(cleanup,_Config) -> cache_module_level(_Config) -> ok = logger:unset_module_level(?MODULE), - [] = ets:lookup(logger,?MODULE), %dirty - add API in logger_config? + [] = ets:lookup(?LOGGER_TABLE,?MODULE), %dirty - add API in logger_config? ?LOG_INFO(?map_rep), %% Caching is done asynchronously, so wait a bit for the update timer:sleep(100), - [_] = ets:lookup(logger,?MODULE), %dirty - add API in logger_config? + [_] = ets:lookup(?LOGGER_TABLE,?MODULE), %dirty - add API in logger_config? ok = logger:unset_module_level(?MODULE), - [] = ets:lookup(logger,?MODULE), %dirty - add API in logger_config? + [] = ets:lookup(?LOGGER_TABLE,?MODULE), %dirty - add API in logger_config? ok. cache_module_level(cleanup,_Config) -> @@ -468,18 +468,18 @@ filter_failed(_Config) -> %% Logger filters {error,{invalid_filter,_}} = - logger:add_logger_filter(lf,{fun(_) -> ok end,args}), - ok = logger:add_logger_filter(lf,{fun(_,_) -> a=b end,args}), - {ok,#{filters:=[_]}} = logger:get_logger_config(), + logger:add_primary_filter(lf,{fun(_) -> ok end,args}), + ok = logger:add_primary_filter(lf,{fun(_,_) -> a=b end,args}), + {ok,#{filters:=[_]}} = logger:get_primary_config(), ok = logger:info(M1=?map_rep), ok = check_logged(info,M1,#{}), - {error,{not_found,lf}} = logger:remove_logger_filter(lf), + {error,{not_found,lf}} = logger:remove_primary_filter(lf), - ok = logger:add_logger_filter(lf,{fun(_,_) -> faulty_return end,args}), - {ok,#{filters:=[_]}} = logger:get_logger_config(), + ok = logger:add_primary_filter(lf,{fun(_,_) -> faulty_return end,args}), + {ok,#{filters:=[_]}} = logger:get_primary_config(), ok = logger:info(M2=?map_rep), ok = check_logged(info,M2,#{}), - {error,{not_found,lf}} = logger:remove_logger_filter(lf), + {error,{not_found,lf}} = logger:remove_primary_filter(lf), %% Handler filters {error,{not_found,h0}} = @@ -582,18 +582,18 @@ handler_failed(cleanup,_Config) -> config_sanity_check(_Config) -> %% Logger config {error,{invalid_filter_default,bad}} = - logger:set_logger_config(filter_default,bad), - {error,{invalid_level,bad}} = logger:set_logger_config(level,bad), - {error,{invalid_filters,bad}} = logger:set_logger_config(filters,bad), - {error,{invalid_filter,bad}} = logger:set_logger_config(filters,[bad]), + logger:set_primary_config(filter_default,bad), + {error,{invalid_level,bad}} = logger:set_primary_config(level,bad), + {error,{invalid_filters,bad}} = logger:set_primary_config(filters,bad), + {error,{invalid_filter,bad}} = logger:set_primary_config(filters,[bad]), {error,{invalid_filter,{_,_}}} = - logger:set_logger_config(filters,[{id,bad}]), + logger:set_primary_config(filters,[{id,bad}]), {error,{invalid_filter,{_,{_,_}}}} = - logger:set_logger_config(filters,[{id,{bad,args}}]), + logger:set_primary_config(filters,[{id,{bad,args}}]), {error,{invalid_filter,{_,{_,_}}}} = - logger:set_logger_config(filters,[{id,{fun() -> ok end,args}}]), - {error,{invalid_logger_config,{bad,bad}}} = - logger:set_logger_config(bad,bad), + logger:set_primary_config(filters,[{id,{fun() -> ok end,args}}]), + {error,{invalid_primary_config,{bad,bad}}} = + logger:set_primary_config(bad,bad), %% Handler config {error,{not_found,h1}} = logger:set_handler_config(h1,a,b), diff --git a/lib/kernel/test/logger_bench_SUITE.erl b/lib/kernel/test/logger_bench_SUITE.erl index d47122ea9d..63fd4191af 100644 --- a/lib/kernel/test/logger_bench_SUITE.erl +++ b/lib/kernel/test/logger_bench_SUITE.erl @@ -181,16 +181,16 @@ log(Config) -> log_drop(Config) -> Times = ?TIMES*100, - ok = logger:set_logger_config(level,error), + ok = logger:set_primary_config(level,error), run_benchmark(Config,?FUNCTION_NAME,fun do_log_func/2, [info,?msg], Times). log_drop(cleanup,_Config) -> - ok = logger:set_logger_config(level,info). + ok = logger:set_primary_config(level,info). log_drop_by_handler(Config) -> Times = ?TIMES, %% just ensure correct levels - ok = logger:set_logger_config(level,info), + ok = logger:set_primary_config(level,info), ok = logger:set_handler_config(?MODULE,level,error), run_benchmark(Config,?FUNCTION_NAME,fun do_log_func/2, [info,?msg], Times). @@ -209,16 +209,16 @@ macro(Config) -> macro_drop(Config) -> Times = ?TIMES*100, - ok = logger:set_logger_config(level,error), + ok = logger:set_primary_config(level,error), run_benchmark(Config,?FUNCTION_NAME,fun do_log_macro/2,[info,?msg], Times). macro_drop(cleanup,_Config) -> - ok = logger:set_logger_config(level,info). + ok = logger:set_primary_config(level,info). macro_drop_by_handler(Config) -> Times = ?TIMES, %% just ensure correct levels - ok = logger:set_logger_config(level,info), + ok = logger:set_primary_config(level,info), ok = logger:set_handler_config(?MODULE,level,error), run_benchmark(Config,?FUNCTION_NAME,fun do_log_macro/2, [info,?msg], Times). @@ -237,16 +237,16 @@ error_logger(Config) -> error_logger_drop(Config) -> Times = ?TIMES*100, - ok = logger:set_logger_config(level,error), + ok = logger:set_primary_config(level,error), run_benchmark(Config,?FUNCTION_NAME,fun do_error_logger/2, [info,?msg], Times). error_logger_drop(cleanup,_Config) -> - ok = logger:set_logger_config(level,info). + ok = logger:set_primary_config(level,info). error_logger_drop_by_handler(Config) -> Times = ?TIMES, %% just ensure correct levels - ok = logger:set_logger_config(level,info), + ok = logger:set_primary_config(level,info), ok = logger:set_handler_config(?MODULE,level,error), run_benchmark(Config,?FUNCTION_NAME,fun do_log_func/2, [info,?msg], Times). diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index e1b1f16414..b2aa8fa51f 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -1323,7 +1323,7 @@ remove(Handler, LogName) -> HState = #{log_names := Logs} = logger_disk_log_h:info(), false = maps:is_key(LogName, HState), false = lists:member(LogName, Logs), - false = logger_config:exist(logger, LogName), + false = logger_config:exist(?LOGGER_TABLE, LogName), {error,no_such_log} = disk_log:info(LogName), ok. diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl index f7f293c7fe..7ef6341377 100644 --- a/lib/kernel/test/logger_env_var_SUITE.erl +++ b/lib/kernel/test/logger_env_var_SUITE.erl @@ -125,14 +125,14 @@ error_logger_tty_sasl_compatible(Config) -> ok. error_logger_false(Config) -> - {ok,#{handlers:=Hs,logger:=L},_Node} = + {ok,#{handlers:=Hs,primary:=P},_Node} = setup(Config, [{error_logger,false}, {logger_level,notice}]), false = lists:keymember(?STANDARD_HANDLER,1,Hs), {simple,logger_simple_h,SimpleC} = lists:keyfind(simple,1,Hs), all = maps:get(level,SimpleC), - notice = maps:get(level,L), + notice = maps:get(level,P), SimpleFilters = maps:get(filters,SimpleC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), true = lists:keymember(stop_progress,1,SimpleFilters), @@ -140,7 +140,7 @@ error_logger_false(Config) -> ok. error_logger_false_progress(Config) -> - {ok,#{handlers:=Hs,logger:=L},_Node} = + {ok,#{handlers:=Hs,primary:=P},_Node} = setup(Config, [{error_logger,false}, {logger_level,notice}, @@ -148,7 +148,7 @@ error_logger_false_progress(Config) -> false = lists:keymember(?STANDARD_HANDLER,1,Hs), {simple,logger_simple_h,SimpleC} = lists:keyfind(simple,1,Hs), all = maps:get(level,SimpleC), - notice = maps:get(level,L), + notice = maps:get(level,P), SimpleFilters = maps:get(filters,SimpleC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), false = lists:keymember(stop_progress,1,SimpleFilters), @@ -156,7 +156,7 @@ error_logger_false_progress(Config) -> ok. error_logger_false_sasl_compatible(Config) -> - {ok,#{handlers:=Hs,logger:=L},_Node} = + {ok,#{handlers:=Hs,primary:=P},_Node} = setup(Config, [{error_logger,false}, {logger_level,notice}, @@ -164,7 +164,7 @@ error_logger_false_sasl_compatible(Config) -> false = lists:keymember(?STANDARD_HANDLER,1,Hs), {simple,logger_simple_h,SimpleC} = lists:keyfind(simple,1,Hs), all = maps:get(level,SimpleC), - notice = maps:get(level,L), + notice = maps:get(level,P), SimpleFilters = maps:get(filters,SimpleC), {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,SimpleFilters), false = lists:keymember(stop_progress,1,SimpleFilters), @@ -328,7 +328,7 @@ logger_file_formatter(Config) -> logger_filters(Config) -> Log = file(Config,?FUNCTION_NAME), - {ok,#{handlers:=Hs,logger:=Logger},Node} + {ok,#{handlers:=Hs,primary:=P},Node} = setup(Config, [{logger_progress_reports,log}, {logger, @@ -347,14 +347,14 @@ logger_filters(Config) -> false = lists:keymember(stop_progress,1,StdFilters), false = lists:keymember(simple,1,Hs), false = lists:keymember(sasl,1,Hs), - LoggerFilters = maps:get(filters,Logger), + LoggerFilters = maps:get(filters,P), true = lists:keymember(stop_progress,1,LoggerFilters), ok. logger_filters_stop(Config) -> Log = file(Config,?FUNCTION_NAME), - {ok,#{handlers:=Hs,logger:=Logger},Node} + {ok,#{handlers:=Hs,primary:=P},Node} = setup(Config, [{logger_progress_reports,log}, {logger, @@ -373,7 +373,7 @@ logger_filters_stop(Config) -> [] = maps:get(filters,StdC), false = lists:keymember(simple,1,Hs), false = lists:keymember(sasl,1,Hs), - LoggerFilters = maps:get(filters,Logger), + LoggerFilters = maps:get(filters,P), true = lists:keymember(log_error,1,LoggerFilters), ok. @@ -445,12 +445,12 @@ logger_disk_log_formatter(Config) -> ok. logger_undefined(Config) -> - {ok,#{handlers:=Hs,logger:=L},_Node} = + {ok,#{handlers:=Hs,primary:=P},_Node} = setup(Config,[{logger,[{handler,?STANDARD_HANDLER,undefined}]}]), false = lists:keymember(?STANDARD_HANDLER,1,Hs), {simple,logger_simple_h,SimpleC} = lists:keyfind(simple,1,Hs), all = maps:get(level,SimpleC), - info = maps:get(level,L), + info = maps:get(level,P), SimpleFilters = maps:get(filters,SimpleC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), true = lists:keymember(stop_progress,1,SimpleFilters), -- cgit v1.2.3 From 2ab3e91a44032cab2b173efc33b5d9589eec4864 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 5 Jun 2018 20:50:01 +0200 Subject: [logger] Add more functions for reading configuration Added: * logger:get_config() - replaces i(), returns all Logger configuration, i.e. primary and handler config, and module levels * logger:get_handler_ids() -> [HandlerId] * logger:get_handler_config() -> [{HandlerId,Module,Config}] Removed: * logger:i/1, will probably be replaced in a later release. --- lib/kernel/src/logger.erl | 105 +++++++--------------------- lib/kernel/test/logger_SUITE.erl | 44 ++++++------ lib/kernel/test/logger_bench_SUITE.erl | 2 +- lib/kernel/test/logger_disk_log_h_SUITE.erl | 4 +- lib/kernel/test/logger_simple_h_SUITE.erl | 2 +- lib/kernel/test/logger_std_h_SUITE.erl | 6 +- lib/kernel/test/logger_test_lib.erl | 2 +- 7 files changed, 55 insertions(+), 110 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 9f96a537bc..b251b21bff 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -45,6 +45,7 @@ update_primary_config/1, update_handler_config/2, update_formatter_config/2, update_formatter_config/3, get_primary_config/0, get_handler_config/1, + get_handler_config/0, get_handler_ids/0, get_config/0, add_handlers/1]). %% Private configuration @@ -54,7 +55,6 @@ -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]). %% Basic report formatting -export([format_report/1, format_otp_report/1]). @@ -382,11 +382,11 @@ update_primary_config(Config) -> update_handler_config(HandlerId,Config) -> logger_server:update_config(HandlerId,Config). --spec get_primary_config() -> {ok,Config} when +-spec get_primary_config() -> Config when Config :: config(). get_primary_config() -> {ok,Config} = logger_config:get(?LOGGER_TABLE,primary), - {ok,maps:remove(handlers,Config)}. + maps:remove(handlers,Config). -spec get_handler_config(HandlerId) -> {ok,{Module,Config}} | {error,term()} when HandlerId :: handler_id(), @@ -395,6 +395,22 @@ get_primary_config() -> get_handler_config(HandlerId) -> logger_config:get(?LOGGER_TABLE,HandlerId). +-spec get_handler_config() -> [{HandlerId,Module,Config}] when + HandlerId :: handler_id(), + Module :: module(), + Config :: config(). +get_handler_config() -> + [begin + {ok,{Module,Config}} = get_handler_config(HandlerId), + {HandlerId,Module,Config} + end || HandlerId <- get_handler_ids()]. + +-spec get_handler_ids() -> [HandlerId] when + HandlerId :: handler_id(). +get_handler_ids() -> + {ok,#{handlers:=HandlerIds}} = logger_config:get(?LOGGER_TABLE,primary), + HandlerIds. + -spec update_formatter_config(HandlerId,FormatterConfig) -> ok | {error,term()} when HandlerId :: config(), @@ -492,82 +508,13 @@ unset_process_metadata() -> _ = erase(?LOGGER_META_KEY), ok. --spec i() -> #{primary=>config(), - handlers=>[{handler_id(),module(),config()}], - module_levels=>[{module(),level() | all | none}]}. -i() -> - i(term). - --spec i(term) -> #{primary=>config(), - handlers=>[{handler_id(),module(),config()}], - module_levels=>[{module(),level() | all | none}]}; - (print) -> ok; - (string) -> iolist(). -i(_Action = print) -> - io:put_chars(i(string)); -i(_Action = string) -> - #{primary := #{level := Level, - filters := Filters, - filter_default := FilterDefault}, - handlers := HandlerConfigs, - module_levels := Modules} = i(term), - [io_lib:format("Current logger configuration:~n", []), - io_lib:format(" Level: ~p~n",[Level]), - io_lib:format(" Filter Default: ~p~n", [FilterDefault]), - io_lib:format(" Filters: ~n", []), - print_filters(4, Filters), - io_lib:format(" Handlers: ~n", []), - print_handlers(HandlerConfigs), - io_lib:format(" Level set per module: ~n", []), - print_module_levels(Modules) - ]; -i(_Action = term) -> - {Primary, Handlers, Modules} = logger_config:get(?LOGGER_TABLE), - #{primary=>maps:remove(handlers,Primary), - handlers=>lists:keysort(1,Handlers), - module_levels=>lists:keysort(1,Modules)}. - -print_filters(Indent, {Id, {Fun, Config}}) -> - io_lib:format("~sId: ~p~n" - "~s Fun: ~p~n" - "~s Config: ~p~n",[Indent, Id, Indent, Fun, Indent, Config]); -print_filters(Indent, Filters) -> - IndentStr = io_lib:format("~.*s",[Indent, ""]), - lists:map(fun(Filter) ->print_filters(IndentStr, Filter) end, Filters). - - -print_handlers({Id,Module, - #{level := Level, - filters := Filters, filter_default := FilterDefault, - formatter := {FormatterModule,FormatterConfig}} = Config}) -> - MyKeys = [filter_default, filters, formatter, level, id], - UnhandledConfig = maps:filter(fun(Key, _) -> - not lists:member(Key, MyKeys) - end, Config), - Unhandled = lists:map(fun({Key, Value}) -> - io_lib:format(" ~p: ~p~n",[Key, Value]) - end, maps:to_list(UnhandledConfig)), - io_lib:format(" Id: ~p~n" - " Module: ~p~n" - " Level: ~p~n" - " Formatter:~n" - " Module: ~p~n" - " Config: ~p~n" - " Filter Default: ~p~n" - " Filters:~n~s" - " Handler Config:~n" - "~s" - "",[Id, Module, Level, FormatterModule, FormatterConfig, - FilterDefault, print_filters(8, Filters), Unhandled]); -print_handlers(Handlers) -> - lists:map(fun print_handlers/1, Handlers). - -print_module_levels({Module,Level}) -> - io_lib:format(" Module: ~p~n" - " Level: ~p~n", - [Module,Level]); -print_module_levels(ModuleLevels) -> - lists:map(fun print_module_levels/1, ModuleLevels). +-spec get_config() -> #{primary=>config(), + handlers=>[{handler_id(),module(),config()}], + module_levels=>[{module(),level() | all | none}]}. +get_config() -> + #{primary=>get_primary_config(), + handlers=>get_handler_config(), + module_levels=>lists:keysort(1,get_module_level())}. -spec internal_init_logger() -> ok | {error,term()}. %% This function is responsible for config of the logger diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index bbf60cad74..c24738718e 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -63,8 +63,8 @@ end_per_group(_Group, _Config) -> ok. init_per_testcase(_TestCase, Config) -> - {ok,LC} = logger:get_primary_config(), - [{logger_config,LC}|Config]. + PC = logger:get_primary_config(), + [{logger_config,PC}|Config]. end_per_testcase(Case, Config) -> try apply(?MODULE,Case,[cleanup,Config]) @@ -106,11 +106,11 @@ start_stop(_Config) -> add_remove_handler(_Config) -> register(callback_receiver,self()), - #{handlers:=Hs0} = logger:i(), + Hs0 = logger:get_handler_config(), {error,{not_found,h1}} = logger:get_handler_config(h1), ok = logger:add_handler(h1,?MODULE,#{}), [add] = test_server:messages_get(), - #{handlers:=Hs} = logger:i(), + Hs = logger:get_handler_config(), {value,_,Hs0} = lists:keytake(h1,1,Hs), {ok,{?MODULE,#{level:=all,filters:=[],filter_default:=log}}} = % defaults logger:get_handler_config(h1), @@ -125,7 +125,7 @@ add_remove_handler(_Config) -> ok = check_logged(info,"hello",[],?MY_LOC(1)), ok = logger:remove_handler(h1), [remove] = test_server:messages_get(), - #{handlers:=Hs0} = logger:i(), + Hs0 = logger:get_handler_config(), {error,{not_found,h1}} = logger:get_handler_config(h1), {error,{not_found,h1}} = logger:remove_handler(h1), logger:info("hello",[]), @@ -242,37 +242,37 @@ change_config(_Config) -> C4 = C3#{custom:=new_custom}, %% Change primary config: Single key - {ok,LConfig0} = logger:get_primary_config(), + PConfig0 = logger:get_primary_config(), ok = logger:set_primary_config(level,warning), - {ok,LConfig1} = logger:get_primary_config(), - LConfig1 = LConfig0#{level:=warning}, + PConfig1 = logger:get_primary_config(), + PConfig1 = PConfig0#{level:=warning}, %% Change primary config: Map ok = logger:update_primary_config(#{level=>error}), - {ok,LConfig2} = logger:get_primary_config(), - LConfig2 = LConfig1#{level:=error}, + PConfig2 = logger:get_primary_config(), + PConfig2 = PConfig1#{level:=error}, %% Overwrite primary config - check that defaults are added ok = logger:set_primary_config(#{filter_default=>stop}), - {ok,#{level:=info,filters:=[],filter_default:=stop}=LC1} = + #{level:=info,filters:=[],filter_default:=stop}=PC1 = logger:get_primary_config(), - 3 = maps:size(LC1), + 3 = maps:size(PC1), %% Check that internal 'handlers' field has not been changed - #{handlers:=HCs} = logger:i(), - HIds1 = [Id || {Id,_,_} <- HCs], - {ok,#{handlers:=HIds2}} = logger_config:get(?LOGGER_TABLE,primary), + MS = [{{{?HANDLER_KEY,'$1'},'_','_','_'},[],['$1']}], + HIds1 = ets:select(?LOGGER_TABLE,MS), % dirty, checking internal data + HIds2 = logger:get_handler_ids(), HIds1 = lists:sort(HIds2), %% Cleanup - ok = logger:set_primary_config(LConfig0), + ok = logger:set_primary_config(PConfig0), [] = test_server:messages_get(), ok. change_config(cleanup,Config) -> logger:remove_handler(h1), - LC = ?config(logger_config,Config), - logger:set_primary_config(LC), + PC = ?config(logger_config,Config), + logger:set_primary_config(PC), ok. set_formatter(_Config) -> @@ -470,13 +470,13 @@ filter_failed(_Config) -> {error,{invalid_filter,_}} = logger:add_primary_filter(lf,{fun(_) -> ok end,args}), ok = logger:add_primary_filter(lf,{fun(_,_) -> a=b end,args}), - {ok,#{filters:=[_]}} = logger:get_primary_config(), + #{filters:=[_]} = logger:get_primary_config(), ok = logger:info(M1=?map_rep), ok = check_logged(info,M1,#{}), {error,{not_found,lf}} = logger:remove_primary_filter(lf), ok = logger:add_primary_filter(lf,{fun(_,_) -> faulty_return end,args}), - {ok,#{filters:=[_]}} = logger:get_primary_config(), + #{filters:=[_]} = logger:get_primary_config(), ok = logger:info(M2=?map_rep), ok = check_logged(info,M2,#{}), {error,{not_found,lf}} = logger:remove_primary_filter(lf), @@ -519,7 +519,7 @@ handler_failed(_Config) -> {error,{invalid_handler,_}} = logger:add_handler(h1,nomodule,#{filter_default=>log}), logger:info(?map_rep), check_no_log(), - #{handlers:=H1} = logger:i(), + H1 = logger:get_handler_config(), false = lists:keymember(h1,1,H1), {error,{not_found,h1}} = logger:remove_handler(h1), @@ -529,7 +529,7 @@ handler_failed(_Config) -> logger:info(?map_rep), [remove] = test_server:messages_get(), - #{handlers:=H2} = logger:i(), + H2 = logger:get_handler_config(), false = lists:keymember(h2,1,H2), {error,{not_found,h2}} = logger:remove_handler(h2), diff --git a/lib/kernel/test/logger_bench_SUITE.erl b/lib/kernel/test/logger_bench_SUITE.erl index 63fd4191af..5ac0ace41b 100644 --- a/lib/kernel/test/logger_bench_SUITE.erl +++ b/lib/kernel/test/logger_bench_SUITE.erl @@ -365,7 +365,7 @@ calc_and_report(Config,Tag,MSecs,Times) -> {save_config,[{bench,[{Tag,IOPS,MSecs}|Bench]}]}. remove_all_handlers() -> - #{handlers:=Hs} = logger:i(), + Hs = logger:get_handler_config(), [logger:remove_handler(Id) || {Id,_,_} <- Hs], Hs. diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index b2aa8fa51f..4fada8ab3a 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -328,7 +328,7 @@ formatter_fail(Config) -> logger:add_handler(Name, logger_disk_log_h, HConfig), Pid = whereis(h_proc_name(Name)), true = is_pid(Pid), - #{handlers:=HC1} = logger:i(), + HC1 = logger:get_handler_config(), H = [Id || {Id,_,_} <- HC1], true = lists:member(Name,H), @@ -358,7 +358,7 @@ formatter_fail(Config) -> %% Check that handler is still alive and was never dead Pid = whereis(h_proc_name(Name)), - #{handlers:=HC2} = logger:i(), + HC2 = logger:get_handler_config(), H = [Id || {Id,_,_} <- HC2], ok. diff --git a/lib/kernel/test/logger_simple_h_SUITE.erl b/lib/kernel/test/logger_simple_h_SUITE.erl index 271a2126de..264cddb069 100644 --- a/lib/kernel/test/logger_simple_h_SUITE.erl +++ b/lib/kernel/test/logger_simple_h_SUITE.erl @@ -48,7 +48,7 @@ suite() -> {ct_hooks, [logger_test_lib]}]. init_per_suite(Config) -> - #{handlers:=Hs0} = logger:i(), + Hs0 = logger:get_handler_config(), Hs = lists:keydelete(cth_log_redirect,1,Hs0), [ok = logger:remove_handler(Id) || {Id,_,_} <- Hs], Env = [{App,Key,application:get_env(App,Key)} || diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index fa90be0fc8..332d58eac8 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -242,8 +242,7 @@ formatter_fail(Config) -> filters=>?DEFAULT_HANDLER_FILTERS([?MODULE])}), Pid = whereis(h_proc_name()), true = is_pid(Pid), - #{handlers:=HC1} = logger:i(), - H = [Id || {Id,_,_} <- HC1], + H = logger:get_handler_ids(), true = lists:member(?MODULE,H), %% Formatter is added automatically @@ -272,8 +271,7 @@ formatter_fail(Config) -> %% Check that handler is still alive and was never dead Pid = whereis(h_proc_name()), - #{handlers:=HC2} = logger:i(), - H = [Id || {Id,_,_} <- HC2], + H = logger:get_handler_ids(), ok. diff --git a/lib/kernel/test/logger_test_lib.erl b/lib/kernel/test/logger_test_lib.erl index 5ad6336925..9097453c10 100644 --- a/lib/kernel/test/logger_test_lib.erl +++ b/lib/kernel/test/logger_test_lib.erl @@ -37,7 +37,7 @@ setup(Config,Vars) -> " -boot start_sasl -kernel start_timer true " "-config ",ConfigFileName]}]) of {ok, Node} -> - L = rpc:call(Node, logger, i, []), + L = rpc:call(Node, logger, get_config, []), ct:log("~p",[L]), {ok, L, Node}; {error, Reason} -> -- cgit v1.2.3 From 7f6e61ca0d752c780ffadd7aacf29aaab0783c78 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 6 Jun 2018 15:58:12 +0200 Subject: [logger] Change name of handler specific config field from ?MODULE to 'config' And add field 'module' in handler config. --- lib/kernel/src/error_logger.erl | 6 +- lib/kernel/src/logger.erl | 4 +- lib/kernel/src/logger_disk_log_h.erl | 30 ++--- lib/kernel/src/logger_server.erl | 6 +- lib/kernel/src/logger_std_h.erl | 30 ++--- lib/kernel/test/logger_SUITE.erl | 2 +- lib/kernel/test/logger_bench_SUITE.erl | 2 +- lib/kernel/test/logger_disk_log_h_SUITE.erl | 100 ++++++++-------- lib/kernel/test/logger_env_var_SUITE.erl | 30 ++--- lib/kernel/test/logger_simple_h_SUITE.erl | 2 +- lib/kernel/test/logger_std_h_SUITE.erl | 180 ++++++++++++++-------------- 11 files changed, 198 insertions(+), 194 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index 4b3922cdd7..3bdd70fa96 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -513,7 +513,7 @@ tty(true) -> _ = case lists:member(error_logger_tty_h, which_report_handlers()) of false -> case logger:get_handler_config(default) of - {ok,{logger_std_h,#{logger_std_h:=#{type:=standard_io}}}} -> + {ok,{logger_std_h,#{config:=#{type:=standard_io}}}} -> logger:remove_handler_filter(default, error_logger_tty_false); _ -> @@ -523,7 +523,7 @@ tty(true) -> [otp]), formatter=>{?DEFAULT_FORMATTER, ?DEFAULT_FORMAT_CONFIG}, - logger_std_h=>#{type=>standard_io}}) + config=>#{type=>standard_io}}) end; true -> ok @@ -533,7 +533,7 @@ tty(false) -> delete_report_handler(error_logger_tty_h), _ = logger:remove_handler(error_logger_tty_true), _ = case logger:get_handler_config(default) of - {ok,{logger_std_h,#{logger_std_h:=#{type:=standard_io}}}} -> + {ok,{logger_std_h,#{config:=#{type:=standard_io}}}} -> logger:add_handler_filter(default,error_logger_tty_false, {fun(_,_) -> stop end, ok}); _ -> diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index b251b21bff..df7ce22e2a 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -96,7 +96,9 @@ -type filter_arg() :: term(). -type filter_return() :: stop | ignore | log_event(). -type config() :: #{id => handler_id(), + config => map(), level => level() | all | none, + module => module(), filter_default => log | stop, filters => [{filter_id(),filter()}], formatter => {module(),formatter_config()}, @@ -673,7 +675,7 @@ init_default_config(Type) when Type==standard_io; element(1,Type)==file -> Env = get_logger_env(), DefaultFormatter = #{formatter=>{?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}}, - DefaultConfig = DefaultFormatter#{logger_std_h=>#{type=>Type}}, + DefaultConfig = DefaultFormatter#{config=>#{type=>Type}}, NewLoggerEnv = case lists:keyfind(default, 2, Env) of {handler, default, Module, Config} -> diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index ba90fefcd3..d339957ac8 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -115,7 +115,7 @@ adding_handler(#{id:=Name}=Config) -> case check_config(adding, Config) of {ok, Config1} -> %% create initial handler state by merging defaults with config - HConfig = maps:get(?MODULE, Config1, #{}), + HConfig = maps:get(config, Config1, #{}), HState = maps:merge(get_init_state(), HConfig), case logger_h_common:overload_levels_ok(HState) of true -> @@ -139,15 +139,15 @@ adding_handler(#{id:=Name}=Config) -> %%%----------------------------------------------------------------- %%% Updating handler config -changing_config(OldConfig=#{id:=Name, disk_log_opts:=DLOpts, ?MODULE:=HConfig}, +changing_config(OldConfig=#{id:=Name, disk_log_opts:=DLOpts, config:=HConfig}, NewConfig=#{id:=Name, disk_log_opts:=DLOpts}) -> case check_config(changing, NewConfig) of - {ok,NewConfig1 = #{?MODULE:=NewHConfig}} -> + {ok,NewConfig1 = #{config:=NewHConfig}} -> #{handler_pid:=HPid, mode_tab:=ModeTab} = HConfig, NewHConfig1 = NewHConfig#{handler_pid=>HPid, mode_tab=>ModeTab}, - NewConfig2 = NewConfig1#{?MODULE=>NewHConfig1}, + NewConfig2 = NewConfig1#{config=>NewHConfig1}, try gen_server:call(HPid, {change_config,OldConfig,NewConfig2}, ?DEFAULT_CALL_TIMEOUT) of ok -> {ok,NewConfig2}; @@ -167,11 +167,11 @@ check_config(adding, #{id:=Name}=Config) -> LogOpts = merge_default_logopts(Name, LogOpts0), case check_log_opts(maps:to_list(LogOpts)) of ok -> - MyConfig = maps:get(?MODULE, Config, #{}), + MyConfig = maps:get(config, Config, #{}), case check_my_config(maps:to_list(MyConfig)) of ok -> {ok,Config#{disk_log_opts=>LogOpts, - ?MODULE=>MyConfig}}; + config=>MyConfig}}; Error -> Error end; @@ -179,7 +179,7 @@ check_config(adding, #{id:=Name}=Config) -> Error end; check_config(changing, Config) -> - MyConfig = maps:get(?MODULE, Config, #{}), + MyConfig = maps:get(config, Config, #{}), case check_my_config(maps:to_list(MyConfig)) of ok -> {ok,Config}; Error -> Error @@ -248,8 +248,8 @@ swap_buffer(Name, Buffer) -> Config :: logger:config(). log(LogEvent, Config = #{id := Name, - ?MODULE := #{handler_pid := HPid, - mode_tab := ModeTab}}) -> + config := #{handler_pid := HPid, + mode_tab := ModeTab}}) -> %% if the handler has crashed, we must drop this request %% and hope the handler restarts so we can try again true = is_process_alive(HPid), @@ -260,7 +260,7 @@ log(LogEvent, Config = #{id := Name, %%% gen_server callbacks %%%=================================================================== -init([Name, Config = #{?MODULE := HConfig, disk_log_opts := LogOpts}, +init([Name, Config = #{config := HConfig, disk_log_opts := LogOpts}, State = #{dl_sync_int := DLSyncInt}]) -> register(?name_to_reg_name(?MODULE,Name), self()), process_flag(trap_exit, true), @@ -291,8 +291,8 @@ init([Name, Config = #{?MODULE := HConfig, disk_log_opts := LogOpts}, prev_sync_result => ok, prev_disk_log_info => undefined}), Config1 = - Config#{?MODULE => HConfig#{handler_pid => self(), - mode_tab => ModeTab}}, + Config#{config => HConfig#{handler_pid => self(), + mode_tab => ModeTab}}, proc_lib:init_ack({ok,self(),Config1}), gen_server:cast(self(), repeated_disk_log_sync), enter_loop(Config1, State1) @@ -335,7 +335,7 @@ handle_call(disk_log_sync, _From, State = #{id := Name}) -> handle_call({change_config,_OldConfig,NewConfig}, _From, State = #{filesync_repeat_interval := FSyncInt0}) -> - HConfig = maps:get(?MODULE, NewConfig, #{}), + HConfig = maps:get(config, NewConfig, #{}), State1 = #{toggle_sync_qlen := TSQL, drop_new_reqs_qlen := DNRQL, flush_reqs_qlen := FRQL} = maps:merge(State, HConfig), @@ -482,9 +482,9 @@ get_init_state() -> %%% ignored. %%% %%% Handler specific config should be provided with a sub map associated -%%% with a key named the same as this module, e.g: +%%% with a key named 'config', e.g: %%% -%%% Config = #{logger_disk_log_h => #{toggle_sync_qlen => 50} +%%% Config = #{config => #{toggle_sync_qlen => 50} %%% %%% The disk_log handler process is linked to logger_sup, which is %%% part of the kernel application's supervision tree. diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index c7d386ab82..7df762053e 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -53,7 +53,7 @@ add_handler(Id,Module,Config0) -> {ok,ok} -> case sanity_check(Id,Config0) of ok -> - Default = default_config(Id), + Default = default_config(Id,Module), Config = maps:merge(Default,Config0), call({add_handler,Id,Module,Config}); Error -> @@ -141,7 +141,7 @@ init([]) -> PrimaryConfig = maps:merge(default_config(primary), #{handlers=>[simple]}), logger_config:create(Tid,primary,PrimaryConfig), - SimpleConfig0 = maps:merge(default_config(simple), + SimpleConfig0 = maps:merge(default_config(simple,logger_simple_h), #{filter_default=>stop, filters=>?DEFAULT_HANDLER_FILTERS}), %% If this fails, then the node should crash @@ -365,6 +365,8 @@ default_config(Id) -> filters=>[], filter_default=>log, formatter=>{?DEFAULT_FORMATTER,#{}}}. +default_config(Id,Module) -> + (default_config(Id))#{module=>Module}. sanity_check(Owner,Key,Value) -> sanity_check_1(Owner,[{Key,Value}]). diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index 5ec9eced12..bebd5300b4 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -116,7 +116,7 @@ adding_handler(#{id:=Name}=Config) -> case check_config(adding, Config) of {ok, Config1} -> %% create initial handler state by merging defaults with config - HConfig = maps:get(?MODULE, Config1, #{}), + HConfig = maps:get(config, Config1, #{}), HState = maps:merge(get_init_state(), HConfig), case logger_h_common:overload_levels_ok(HState) of true -> @@ -140,17 +140,17 @@ adding_handler(#{id:=Name}=Config) -> %%%----------------------------------------------------------------- %%% Updating handler config -changing_config(OldConfig=#{id:=Name, ?MODULE:=HConfig}, +changing_config(OldConfig=#{id:=Name, config:=HConfig}, NewConfig=#{id:=Name}) -> #{type:=Type, handler_pid:=HPid, mode_tab:=ModeTab} = HConfig, - MyConfig = maps:get(?MODULE, NewConfig, #{}), + MyConfig = maps:get(config, NewConfig, #{}), case maps:get(type, MyConfig, Type) of Type -> MyConfig1 = MyConfig#{type=>Type, handler_pid=>HPid, mode_tab=>ModeTab}, changing_config1(HPid, OldConfig, - NewConfig#{?MODULE=>MyConfig1}); + NewConfig#{config=>MyConfig1}); _ -> {error,{illegal_config_change,OldConfig,NewConfig}} end; @@ -173,17 +173,17 @@ changing_config1(HPid, OldConfig, NewConfig) -> check_config(adding, Config) -> %% Merge in defaults on handler level - MyConfig0 = maps:get(?MODULE, Config, #{}), + MyConfig0 = maps:get(config, Config, #{}), MyConfig = maps:merge(#{type => standard_io}, MyConfig0), case check_my_config(maps:to_list(MyConfig)) of ok -> - {ok,Config#{?MODULE=>MyConfig}}; + {ok,Config#{config=>MyConfig}}; Error -> Error end; check_config(changing, Config) -> - MyConfig = maps:get(?MODULE, Config, #{}), + MyConfig = maps:get(config, Config, #{}), case check_my_config(maps:to_list(MyConfig)) of ok -> {ok,Config}; Error -> Error @@ -230,8 +230,8 @@ swap_buffer(Name, Buffer) -> Config :: logger:config(). log(LogEvent, Config = #{id := Name, - ?MODULE := #{handler_pid := HPid, - mode_tab := ModeTab}}) -> + config := #{handler_pid := HPid, + mode_tab := ModeTab}}) -> %% if the handler has crashed, we must drop this request %% and hope the handler restarts so we can try again true = is_process_alive(HPid), @@ -242,7 +242,7 @@ log(LogEvent, Config = #{id := Name, %%% gen_server callbacks %%%=================================================================== -init([Name, Config = #{?MODULE := HConfig}, +init([Name, Config = #{config := HConfig}, State0 = #{type := Type, file_ctrl_sync_int := FileCtrlSyncInt}]) -> register(?name_to_reg_name(?MODULE,Name), self()), process_flag(trap_exit, true), @@ -269,8 +269,8 @@ init([Name, Config = #{?MODULE := HConfig}, burst_win_ts => T0, burst_msg_count => 0}), Config1 = - Config#{?MODULE => HConfig#{handler_pid => self(), - mode_tab => ModeTab}}, + Config#{config => HConfig#{handler_pid => self(), + mode_tab => ModeTab}}, proc_lib:init_ack({ok,self(),Config1}), gen_server:cast(self(), repeated_filesync), enter_loop(Config1, State1) @@ -332,7 +332,7 @@ handle_call(filesync, _From, State = #{type := Type, handle_call({change_config,_OldConfig,NewConfig}, _From, State = #{filesync_repeat_interval := FSyncInt0}) -> - HConfig = maps:get(?MODULE, NewConfig, #{}), + HConfig = maps:get(config, NewConfig, #{}), State1 = maps:merge(State, HConfig), case logger_h_common:overload_levels_ok(State1) of true -> @@ -470,9 +470,9 @@ get_init_state() -> %%% exist if the handler is not registered). %%% %%% Handler specific config should be provided with a sub map associated -%%% with a key named the same as this module, e.g: +%%% with a key named 'config', e.g: %%% -%%% Config = #{logger_std_h => #{toggle_sync_qlen => 50} +%%% Config = #{config => #{toggle_sync_qlen => 50} %%% %%% The standard handler process is linked to logger_sup, which is %%% part of the kernel application's supervision tree. diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index c24738718e..ca0e7e23b5 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -260,7 +260,7 @@ change_config(_Config) -> %% Check that internal 'handlers' field has not been changed MS = [{{{?HANDLER_KEY,'$1'},'_','_','_'},[],['$1']}], HIds1 = ets:select(?LOGGER_TABLE,MS), % dirty, checking internal data - HIds2 = logger:get_handler_ids(), + HIds2 = lists:sort(logger:get_handler_ids()), HIds1 = lists:sort(HIds2), %% Cleanup diff --git a/lib/kernel/test/logger_bench_SUITE.erl b/lib/kernel/test/logger_bench_SUITE.erl index 5ac0ace41b..b60fc42741 100644 --- a/lib/kernel/test/logger_bench_SUITE.erl +++ b/lib/kernel/test/logger_bench_SUITE.erl @@ -67,7 +67,7 @@ do_init_per_group(console_handler) -> ok = logger:add_handler(?MODULE,logger_std_h, #{filter_default=>stop, filters=>?DEFAULT_HANDLER_FILTERS, - logger_std_h=>#{type=>standard_io, + config=>#{type=>standard_io, toggle_sync_qlen => ?TIMES+1, drop_new_reqs_qlen => ?TIMES+2, flush_reqs_qlen => ?TIMES+3, diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 4fada8ab3a..552cb05797 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -369,21 +369,21 @@ formatter_fail(cleanup,_Config) -> config_fail(_Config) -> {error,{handler_not_added,{invalid_config,logger_disk_log_h,{bad,bad}}}} = logger:add_handler(?MODULE,logger_disk_log_h, - #{logger_disk_log_h => #{bad => bad}, + #{config => #{bad => bad}, filter_default=>log, formatter=>{?MODULE,self()}}), {error,{handler_not_added,{invalid_levels,{_,1,_}}}} = logger:add_handler(?MODULE,logger_disk_log_h, - #{logger_disk_log_h => #{drop_new_reqs_qlen=>1}}), + #{config => #{drop_new_reqs_qlen=>1}}), {error,{handler_not_added,{invalid_levels,{43,42,_}}}} = logger:add_handler(?MODULE,logger_disk_log_h, - #{logger_disk_log_h => #{toggle_sync_qlen=>43, - drop_new_reqs_qlen=>42}}), + #{config => #{toggle_sync_qlen=>43, + drop_new_reqs_qlen=>42}}), {error,{handler_not_added,{invalid_levels,{_,43,42}}}} = logger:add_handler(?MODULE,logger_disk_log_h, - #{logger_disk_log_h => #{drop_new_reqs_qlen=>43, - flush_reqs_qlen=>42}}), + #{config => #{drop_new_reqs_qlen=>43, + flush_reqs_qlen=>42}}), ok = logger:add_handler(?MODULE,logger_disk_log_h, #{filter_default=>log, @@ -397,12 +397,12 @@ config_fail(_Config) -> logger:set_handler_config(?MODULE,id,bad), %% incorrect values of OP params {error,{invalid_levels,_}} = - logger:set_handler_config(?MODULE,logger_disk_log_h, + logger:set_handler_config(?MODULE,config, #{toggle_sync_qlen=>100, flush_reqs_qlen=>99}), %% invalid name of config parameter {error,{invalid_config,logger_disk_log_h,{filesync_rep_int,2000}}} = - logger:set_handler_config(?MODULE, logger_disk_log_h, + logger:set_handler_config(?MODULE, config, #{filesync_rep_int => 2000}), ok. config_fail(cleanup,_Config) -> @@ -448,7 +448,7 @@ reconfig(Config) -> file := _DiskLogFile}} = logger_disk_log_h:info(?MODULE), - ok = logger:set_handler_config(?MODULE, logger_disk_log_h, + ok = logger:set_handler_config(?MODULE, config, #{toggle_sync_qlen => 1, drop_new_reqs_qlen => 2, flush_reqs_qlen => 3, @@ -532,7 +532,7 @@ sync(Config) -> %% check that if there's no repeated disk_log_sync active, %% a disk_log_sync is still performed when handler goes idle - logger:set_handler_config(?MODULE, logger_disk_log_h, + logger:set_handler_config(?MODULE, config, #{filesync_repeat_interval => no_repeat}), no_repeat = maps:get(filesync_repeat_interval, logger_disk_log_h:info(?MODULE)), @@ -559,12 +559,12 @@ sync(Config) -> start_tracer([{logger_disk_log_h,handle_cast,2}], [OneSync || _ <- lists:seq(1, 1 + trunc(WaitT/SyncInt))]), - logger:set_handler_config(?MODULE, logger_disk_log_h, + logger:set_handler_config(?MODULE, config, #{filesync_repeat_interval => SyncInt}), SyncInt = maps:get(filesync_repeat_interval, logger_disk_log_h:info(?MODULE)), timer:sleep(WaitT), - logger:set_handler_config(?MODULE, logger_disk_log_h, + logger:set_handler_config(?MODULE, config, #{filesync_repeat_interval => no_repeat}), check_tracer(100), ok. @@ -782,7 +782,7 @@ sync_failure(Config) -> SyncInt = 500, ok = rpc:call(Node, logger, set_handler_config, - [?STANDARD_HANDLER, logger_disk_log_h, + [?STANDARD_HANDLER, config, #{filesync_repeat_interval => SyncInt}]), Info = rpc:call(Node, logger_disk_log_h, info, [?STANDARD_HANDLER]), SyncInt = maps:get(filesync_repeat_interval, Info), @@ -852,10 +852,10 @@ op_switch_to_sync(Config) -> {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NumOfReqs = 500, NewHConfig = - HConfig#{logger_disk_log_h => DLHConfig#{toggle_sync_qlen => 2, - drop_new_reqs_qlen => NumOfReqs+1, - flush_reqs_qlen => 2*NumOfReqs, - enable_burst_limit => false}}, + HConfig#{config => DLHConfig#{toggle_sync_qlen => 2, + drop_new_reqs_qlen => NumOfReqs+1, + flush_reqs_qlen => 2*NumOfReqs, + enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), send_burst({n,NumOfReqs}, seq, {chars,79}, info), Lines = count_lines(Log), @@ -876,7 +876,7 @@ op_switch_to_drop(Config) -> Procs = 2, Bursts = 10, NewHConfig = - HConfig#{logger_disk_log_h => + HConfig#{config => DLHConfig#{toggle_sync_qlen => 1, drop_new_reqs_qlen => 2, flush_reqs_qlen => Procs*NumOfReqs*Bursts, @@ -921,7 +921,7 @@ op_switch_to_flush(Config) -> %% (verify with coverage of flush_log_requests/2) NewHConfig = - HConfig#{logger_disk_log_h => + HConfig#{config => DLHConfig#{toggle_sync_qlen => 2, %% disable drop mode drop_new_reqs_qlen => 300, @@ -964,11 +964,11 @@ op_switch_to_flush(cleanup, _Config) -> limit_burst_disabled(Config) -> {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NewHConfig = - HConfig#{logger_disk_log_h => DLHConfig#{enable_burst_limit => false, - burst_limit_size => 10, - burst_window_time => 2000, - drop_new_reqs_qlen => 200, - flush_reqs_qlen => 300}}, + HConfig#{config => DLHConfig#{enable_burst_limit => false, + burst_limit_size => 10, + burst_window_time => 2000, + drop_new_reqs_qlen => 200, + flush_reqs_qlen => 300}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, send_burst({n,NumOfReqs}, seq, {chars,79}, info), @@ -983,11 +983,11 @@ limit_burst_enabled_one(Config) -> {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), ReqLimit = 10, NewHConfig = - HConfig#{logger_disk_log_h => DLHConfig#{enable_burst_limit => true, - burst_limit_size => ReqLimit, - burst_window_time => 2000, - drop_new_reqs_qlen => 200, - flush_reqs_qlen => 300}}, + HConfig#{config => DLHConfig#{enable_burst_limit => true, + burst_limit_size => ReqLimit, + burst_window_time => 2000, + drop_new_reqs_qlen => 200, + flush_reqs_qlen => 300}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, send_burst({n,NumOfReqs}, seq, {chars,79}, info), @@ -1003,11 +1003,11 @@ limit_burst_enabled_period(Config) -> ReqLimit = 10, BurstTWin = 1000, NewHConfig = - HConfig#{logger_disk_log_h => DLHConfig#{enable_burst_limit => true, - burst_limit_size => ReqLimit, - burst_window_time => BurstTWin, - drop_new_reqs_qlen => 20000, - flush_reqs_qlen => 20001}}, + HConfig#{config => DLHConfig#{enable_burst_limit => true, + burst_limit_size => ReqLimit, + burst_window_time => BurstTWin, + drop_new_reqs_qlen => 20000, + flush_reqs_qlen => 20001}}, ok = logger:set_handler_config(?MODULE, NewHConfig), Windows = 3, @@ -1024,9 +1024,9 @@ limit_burst_enabled_period(cleanup, _Config) -> kill_disabled(Config) -> {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NewHConfig = - HConfig#{logger_disk_log_h=>DLHConfig#{enable_kill_overloaded=>false, - handler_overloaded_qlen=>10, - handler_overloaded_mem=>100}}, + HConfig#{config=>DLHConfig#{enable_kill_overloaded=>false, + handler_overloaded_qlen=>10, + handler_overloaded_mem=>100}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, send_burst({n,NumOfReqs}, seq, {chars,79}, info), @@ -1044,7 +1044,7 @@ qlen_kill_new(Config) -> {_,Mem0} = process_info(Pid0, memory), RestartAfter = ?HANDLER_RESTART_AFTER, NewHConfig = - HConfig#{logger_disk_log_h => + HConfig#{config => DLHConfig#{enable_kill_overloaded=>true, handler_overloaded_qlen=>10, handler_overloaded_mem=>Mem0+50000, @@ -1081,7 +1081,7 @@ mem_kill_new(Config) -> {_,Mem0} = process_info(Pid0, memory), RestartAfter = ?HANDLER_RESTART_AFTER, NewHConfig = - HConfig#{logger_disk_log_h => + HConfig#{config => DLHConfig#{enable_kill_overloaded=>true, handler_overloaded_qlen=>50000, handler_overloaded_mem=>Mem0+500, @@ -1115,9 +1115,9 @@ mem_kill_new(cleanup, _Config) -> restart_after(Config) -> {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NewHConfig1 = - HConfig#{logger_disk_log_h=>DLHConfig#{enable_kill_overloaded=>true, - handler_overloaded_qlen=>10, - handler_restart_after=>never}}, + HConfig#{config=>DLHConfig#{enable_kill_overloaded=>true, + handler_overloaded_qlen=>10, + handler_restart_after=>never}}, ok = logger:set_handler_config(?MODULE, NewHConfig1), MRef1 = erlang:monitor(process, whereis(h_proc_name())), %% kill handler @@ -1135,9 +1135,9 @@ restart_after(Config) -> {Log,_,_} = start_handler(?MODULE, ?FUNCTION_NAME, Config), RestartAfter = ?HANDLER_RESTART_AFTER, NewHConfig2 = - HConfig#{logger_disk_log_h=>DLHConfig#{enable_kill_overloaded=>true, - handler_overloaded_qlen=>10, - handler_restart_after=>RestartAfter}}, + HConfig#{config=>DLHConfig#{enable_kill_overloaded=>true, + handler_overloaded_qlen=>10, + handler_restart_after=>RestartAfter}}, ok = logger:set_handler_config(?MODULE, NewHConfig2), Pid0 = whereis(h_proc_name()), MRef2 = erlang:monitor(process, Pid0), @@ -1166,10 +1166,10 @@ handler_requests_under_load() -> handler_requests_under_load(Config) -> {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NewHConfig = - HConfig#{logger_disk_log_h => DLHConfig#{toggle_sync_qlen => 2, - drop_new_reqs_qlen => 1000, - flush_reqs_qlen => 2000, - enable_burst_limit => false}}, + HConfig#{config => DLHConfig#{toggle_sync_qlen => 2, + drop_new_reqs_qlen => 1000, + flush_reqs_qlen => 2000, + enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), Pid = spawn_link(fun() -> send_requests(?MODULE, 1, [{sync,[]}, {info,[]}, @@ -1227,7 +1227,7 @@ start_handler(Name, FuncName, Config) -> filters=>?DEFAULT_HANDLER_FILTERS([Name]), formatter=>{?MODULE,op}, level => info}), - {ok,{_,HConfig = #{logger_disk_log_h := DLHConfig}}} = + {ok,{_,HConfig = #{config := DLHConfig}}} = logger:get_handler_config(Name), {lists:concat([File,".1"]),HConfig,DLHConfig}. diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl index 7ef6341377..fcdfcfd6d0 100644 --- a/lib/kernel/test/logger_env_var_SUITE.erl +++ b/lib/kernel/test/logger_env_var_SUITE.erl @@ -205,7 +205,7 @@ logger_file(Config) -> = setup(Config, [{logger, [{handler,?STANDARD_HANDLER,logger_std_h, - #{logger_std_h=>#{type=>{file,Log}}}}]}]), + #{config=>#{type=>{file,Log}}}}]}]), check_default_log(Node,Log, file,% dest 0),% progress in std logger @@ -227,7 +227,7 @@ logger_file_sasl_compatible(Config) -> [{logger_sasl_compatible,true}, {logger, [{handler,?STANDARD_HANDLER,logger_std_h, - #{logger_std_h=>#{type=>{file,Log}}}}]}]), + #{config=>#{type=>{file,Log}}}}]}]), check_default_log(Node,Log, file,% dest 0),% progress in std logger @@ -249,7 +249,7 @@ logger_file_log_progress(Config) -> [{logger_progress_reports,log}, {logger, [{handler,?STANDARD_HANDLER,logger_std_h, - #{logger_std_h=>#{type=>{file,Log}}}}]}]), + #{config=>#{type=>{file,Log}}}}]}]), check_default_log(Node,Log, file,% dest 6),% progress in std logger @@ -271,7 +271,7 @@ logger_file_no_filter(Config) -> [{logger, [{handler,?STANDARD_HANDLER,logger_std_h, #{filter_default=>log,filters=>[], - logger_std_h=>#{type=>{file,Log}}}}]}]), + config=>#{type=>{file,Log}}}}]}]), check_default_log(Node,Log, file,% dest 6),% progress in std logger @@ -291,7 +291,7 @@ logger_file_no_filter_level(Config) -> [{logger, [{handler,?STANDARD_HANDLER,logger_std_h, #{filters=>[],level=>error, - logger_std_h=>#{type=>{file,Log}}}}]}]), + config=>#{type=>{file,Log}}}}]}]), check_default_log(Node,Log, file,% dest 0,% progress in std logger @@ -313,7 +313,7 @@ logger_file_formatter(Config) -> [{handler,?STANDARD_HANDLER,logger_std_h, #{filters=>[], formatter=>{logger_formatter,#{}}, - logger_std_h=>#{type=>{file,Log}}}}]}]), + config=>#{type=>{file,Log}}}}]}]), check_single_log(Node,Log, file,% dest 6),% progress in std logger @@ -333,7 +333,7 @@ logger_filters(Config) -> [{logger_progress_reports,log}, {logger, [{handler,?STANDARD_HANDLER,logger_std_h, - #{logger_std_h=>#{type=>{file,Log}}}}, + #{config=>#{type=>{file,Log}}}}, {filters,log,[{stop_progress,{fun logger_filters:progress/2,stop}}]} ]}]), check_default_log(Node,Log, @@ -360,7 +360,7 @@ logger_filters_stop(Config) -> {logger, [{handler,?STANDARD_HANDLER,logger_std_h, #{filters=>[], - logger_std_h=>#{type=>{file,Log}}}}, + config=>#{type=>{file,Log}}}}, {filters,stop,[{log_error,{fun logger_filters:level/2,{log,gt,info}}}]} ]}]), check_default_log(Node,Log, @@ -385,7 +385,7 @@ logger_module_level(Config) -> [{logger_progress_reports,log}, {logger, [{handler,?STANDARD_HANDLER,logger_std_h, - #{logger_std_h=>#{type=>{file,Log}}}}, + #{config=>#{type=>{file,Log}}}}, {module_level,error,[supervisor]} ]}]), check_default_log(Node,Log, @@ -469,12 +469,12 @@ logger_many_handlers_default_first(Config) -> #{level=>error, filters=>[], formatter=>{logger_formatter,#{}}, - logger_std_h=>#{type=>{file,LogErr}}} + config=>#{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}}} + config=>#{type=>{file,LogInfo}}} } ]}], LogErr, LogInfo, 6). @@ -487,13 +487,13 @@ logger_many_handlers_default_last(Config) -> [{handler,info,logger_std_h, #{level=>info, filters=>[{level,{fun logger_filters:level/2,{stop,gteq,error}}}], - logger_std_h=>#{type=>{file,LogInfo}}} + config=>#{type=>{file,LogInfo}}} }, {handler,?STANDARD_HANDLER,logger_std_h, #{level=>error, filters=>[], formatter=>{logger_formatter,#{}}, - logger_std_h=>#{type=>{file,LogErr}}} + config=>#{type=>{file,LogErr}}} } ]}], LogErr, LogInfo, 7). @@ -509,13 +509,13 @@ logger_many_handlers_default_last_broken_filter(Config) -> #{level=>info, filters=>[{broken,{fun logger_filters:level/2,broken_state}}, {level,{fun logger_filters:level/2,{stop,gteq,error}}}], - logger_std_h=>#{type=>{file,LogInfo}}} + config=>#{type=>{file,LogInfo}}} }, {handler,?STANDARD_HANDLER,logger_std_h, #{level=>error, filters=>[], formatter=>{logger_formatter,#{}}, - logger_std_h=>#{type=>{file,LogErr}}} + config=>#{type=>{file,LogErr}}} } ]}], LogErr, LogInfo, 7). diff --git a/lib/kernel/test/logger_simple_h_SUITE.erl b/lib/kernel/test/logger_simple_h_SUITE.erl index 264cddb069..9f34bbbe34 100644 --- a/lib/kernel/test/logger_simple_h_SUITE.erl +++ b/lib/kernel/test/logger_simple_h_SUITE.erl @@ -138,7 +138,7 @@ replace_file(Config) -> ok = rpc:call(Node, logger, add_handlers, [[{handler, default, logger_std_h, - #{ logger_std_h => #{ type => {file, File} }, + #{ config => #{ type => {file, File} }, formatter => {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}}}]]), {ok,Bin} = sync_and_read(Node, file, File), diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index 332d58eac8..97b5c03a86 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -132,7 +132,7 @@ all() -> add_remove_instance_tty(_Config) -> {error,{handler_not_added,{invalid_config,logger_std_h,{type,tty}}}} = logger:add_handler(?MODULE,logger_std_h, - #{logger_std_h => #{type => tty}, + #{config => #{type => tty}, filter_default=>log, formatter=>{?MODULE,self()}}), ok. @@ -166,7 +166,7 @@ add_remove_instance_file2(cleanup,_Config) -> add_remove_instance_file(Log, Type) -> ok = logger:add_handler(?MODULE, logger_std_h, - #{logger_std_h => #{type => Type}, + #{config => #{type => Type}, filter_default=>stop, filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,self()}}), @@ -212,18 +212,18 @@ errors(Config) -> {handler_not_added, {invalid_config,logger_std_h,{type,faulty_type}}}} = logger:add_handler(?MODULE,logger_std_h, - #{logger_std_h => #{type => faulty_type}}), + #{config => #{type => faulty_type}}), NoDir = lists:concat(["/",?MODULE,"_dir"]), {error, {handler_not_added,{{open_failed,NoDir,eacces},_}}} = logger:add_handler(myh2,logger_std_h, - #{logger_std_h=>#{type=>{file,NoDir}}}), + #{config=>#{type=>{file,NoDir}}}), {error, {handler_not_added,{{open_failed,Log,_},_}}} = logger:add_handler(myh3,logger_std_h, - #{logger_std_h=>#{type=>{file,Log,[bad_file_opt]}}}), + #{config=>#{type=>{file,Log,[bad_file_opt]}}}), ok = logger:info(?msg). @@ -237,7 +237,7 @@ formatter_fail(Config) -> %% no formatter ok = logger:add_handler(?MODULE, logger_std_h, - #{logger_std_h => #{type => {file,Log}}, + #{config => #{type => {file,Log}}, filter_default=>stop, filters=>?DEFAULT_HANDLER_FILTERS([?MODULE])}), Pid = whereis(h_proc_name()), @@ -281,41 +281,41 @@ formatter_fail(cleanup,_Config) -> config_fail(_Config) -> {error,{handler_not_added,{invalid_config,logger_std_h,{bad,bad}}}} = logger:add_handler(?MODULE,logger_std_h, - #{logger_std_h => #{bad => bad}, + #{config => #{bad => bad}, filter_default=>log, formatter=>{?MODULE,self()}}), {error,{handler_not_added,{invalid_config,logger_std_h, {restart_type,bad}}}} = logger:add_handler(?MODULE,logger_std_h, - #{logger_std_h => #{restart_type => bad}, + #{config => #{restart_type => bad}, filter_default=>log, formatter=>{?MODULE,self()}}), {error,{handler_not_added,{invalid_levels,{_,1,_}}}} = logger:add_handler(?MODULE,logger_std_h, - #{logger_std_h => #{drop_new_reqs_qlen=>1}}), + #{config => #{drop_new_reqs_qlen=>1}}), {error,{handler_not_added,{invalid_levels,{43,42,_}}}} = logger:add_handler(?MODULE,logger_std_h, - #{logger_std_h => #{toggle_sync_qlen=>43, - drop_new_reqs_qlen=>42}}), + #{config => #{toggle_sync_qlen=>43, + drop_new_reqs_qlen=>42}}), {error,{handler_not_added,{invalid_levels,{_,43,42}}}} = logger:add_handler(?MODULE,logger_std_h, - #{logger_std_h => #{drop_new_reqs_qlen=>43, - flush_reqs_qlen=>42}}), + #{config => #{drop_new_reqs_qlen=>43, + flush_reqs_qlen=>42}}), ok = logger:add_handler(?MODULE,logger_std_h, #{filter_default=>log, formatter=>{?MODULE,self()}}), {error,{illegal_config_change,_,_}} = - logger:set_handler_config(?MODULE,logger_std_h, + logger:set_handler_config(?MODULE,config, #{type=>{file,"file"}}), {error,{illegal_config_change,_,_}} = logger:set_handler_config(?MODULE,id,bad), {error,{invalid_levels,_}} = - logger:set_handler_config(?MODULE,logger_std_h, + logger:set_handler_config(?MODULE,config, #{toggle_sync_qlen=>100, flush_reqs_qlen=>99}), {error,{invalid_config,logger_std_h,{filesync_rep_int,2000}}} = - logger:set_handler_config(?MODULE, logger_std_h, + logger:set_handler_config(?MODULE, config, #{filesync_rep_int => 2000}), ok. @@ -327,7 +327,7 @@ crash_std_h_to_file(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} }}}], + #{ config => #{ type => {file, Log} }}}], file, Log). crash_std_h_to_file(cleanup,_Config) -> crash_std_h(cleanup). @@ -426,7 +426,7 @@ reconfig(Config) -> Dir = ?config(priv_dir,Config), ok = logger:add_handler(?MODULE, logger_std_h, - #{logger_std_h => #{type => standard_io}, + #{config => #{type => standard_io}, filter_default=>log, filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,self()}}), @@ -446,7 +446,7 @@ reconfig(Config) -> filesync_repeat_interval := ?FILESYNC_REPEAT_INTERVAL} = logger_std_h:info(?MODULE), - ok = logger:set_handler_config(?MODULE, logger_std_h, + ok = logger:set_handler_config(?MODULE, config, #{toggle_sync_qlen => 1, drop_new_reqs_qlen => 2, flush_reqs_qlen => 3, @@ -485,13 +485,13 @@ file_opts(Config) -> BadType = {file,Log,BadFileOpts}, {error,{handler_not_added,{{open_failed,Log,enoent},_}}} = logger:add_handler(?MODULE, logger_std_h, - #{logger_std_h => #{type => BadType}}), + #{config => #{type => BadType}}), OkFileOpts = [raw,append], OkType = {file,Log,OkFileOpts}, ok = logger:add_handler(?MODULE, logger_std_h, - #{logger_std_h => #{type => OkType}, + #{config => #{type => OkType}, filter_default=>log, filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,self()}}), @@ -512,7 +512,7 @@ sync(Config) -> Type = {file,Log}, ok = logger:add_handler(?MODULE, logger_std_h, - #{logger_std_h => #{type => Type}, + #{config => #{type => Type}, filter_default=>log, filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,nl}}), @@ -547,7 +547,7 @@ sync(Config) -> %% 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, + logger:set_handler_config(?MODULE, config, #{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 @@ -578,11 +578,11 @@ sync(Config) -> start_tracer([{logger_std_h,handle_cast,2}], [OneSync || _ <- lists:seq(1, 1 + trunc(WaitT/SyncInt))]), - logger:set_handler_config(?MODULE, logger_std_h, + logger:set_handler_config(?MODULE, config, #{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, + logger:set_handler_config(?MODULE, config, #{filesync_repeat_interval => no_repeat}), check_tracer(100), ok. @@ -639,7 +639,7 @@ sync_failure(Config) -> SyncInt = 500, ok = rpc:call(Node, logger, set_handler_config, - [?STANDARD_HANDLER, logger_std_h, + [?STANDARD_HANDLER, config, #{filesync_repeat_interval => SyncInt}]), Info = rpc:call(Node, logger_std_h, info, [?STANDARD_HANDLER]), SyncInt = maps:get(filesync_repeat_interval, Info), @@ -672,7 +672,7 @@ start_std_h_on_new_node(Config, Log) -> logger_test_lib:setup( Config, [{logger,[{handler,default,logger_std_h, - #{ logger_std_h => #{ type => {file,Log}}}}]}]), + #{ config => #{ type => {file,Log}}}}]}]), ok = rpc:call(Node,logger,set_handler_config,[?STANDARD_HANDLER,formatter, {?MODULE,nl}]), Node. @@ -700,10 +700,10 @@ op_switch_to_sync_file(Config) -> {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NumOfReqs = 500, NewHConfig = - HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 2, - drop_new_reqs_qlen => NumOfReqs+1, - flush_reqs_qlen => 2*NumOfReqs, - enable_burst_limit => false}}, + HConfig#{config => StdHConfig#{toggle_sync_qlen => 2, + drop_new_reqs_qlen => NumOfReqs+1, + flush_reqs_qlen => 2*NumOfReqs, + enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), %% TRecvPid = start_op_trace(), send_burst({n,NumOfReqs}, seq, {chars,79}, info), @@ -729,10 +729,10 @@ op_switch_to_sync_tty(Config) -> {HConfig,StdHConfig} = start_handler(?MODULE, standard_io, Config), NumOfReqs = 500, NewHConfig = - HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 3, - drop_new_reqs_qlen => NumOfReqs+1, - flush_reqs_qlen => 2*NumOfReqs, - enable_burst_limit => false}}, + HConfig#{config => StdHConfig#{toggle_sync_qlen => 3, + drop_new_reqs_qlen => NumOfReqs+1, + flush_reqs_qlen => 2*NumOfReqs, + enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), send_burst({n,NumOfReqs}, seq, {chars,79}, info), ok. @@ -750,7 +750,7 @@ op_switch_to_drop_file(Config) -> Procs = 2, Bursts = 10, NewHConfig = - HConfig#{logger_std_h => + HConfig#{config => StdHConfig#{toggle_sync_qlen => 1, drop_new_reqs_qlen => 2, flush_reqs_qlen => @@ -788,11 +788,11 @@ op_switch_to_drop_tty(Config) -> NumOfReqs = 300, Procs = 2, NewHConfig = - HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 1, - drop_new_reqs_qlen => 2, - flush_reqs_qlen => - Procs*NumOfReqs+1, - enable_burst_limit => false}}, + HConfig#{config => StdHConfig#{toggle_sync_qlen => 1, + drop_new_reqs_qlen => 2, + flush_reqs_qlen => + Procs*NumOfReqs+1, + enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), ok. @@ -812,7 +812,7 @@ op_switch_to_flush_file(Config) -> %% (verify with coverage of flush_log_requests/2) NewHConfig = - HConfig#{logger_std_h => + HConfig#{config => StdHConfig#{toggle_sync_qlen => 2, %% disable drop mode drop_new_reqs_qlen => 300, @@ -858,11 +858,11 @@ op_switch_to_flush_tty(Config) -> %% when the flush happens (verify with coverage of flush_log_requests/2) NewHConfig = - HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 2, - %% disable drop mode - drop_new_reqs_qlen => 100, - flush_reqs_qlen => 100, - enable_burst_limit => false}}, + HConfig#{config => StdHConfig#{toggle_sync_qlen => 2, + %% disable drop mode + drop_new_reqs_qlen => 100, + flush_reqs_qlen => 100, + enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 1000, Procs = 100, @@ -874,11 +874,11 @@ op_switch_to_flush_tty(cleanup, _Config) -> limit_burst_disabled(Config) -> {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NewHConfig = - HConfig#{logger_std_h => StdHConfig#{enable_burst_limit => false, - burst_limit_size => 10, - burst_window_time => 2000, - drop_new_reqs_qlen => 200, - flush_reqs_qlen => 300}}, + HConfig#{config => StdHConfig#{enable_burst_limit => false, + burst_limit_size => 10, + burst_window_time => 2000, + drop_new_reqs_qlen => 200, + flush_reqs_qlen => 300}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, send_burst({n,NumOfReqs}, seq, {chars,79}, info), @@ -893,11 +893,11 @@ limit_burst_enabled_one(Config) -> {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), ReqLimit = 10, NewHConfig = - HConfig#{logger_std_h => StdHConfig#{enable_burst_limit => true, - burst_limit_size => ReqLimit, - burst_window_time => 2000, - drop_new_reqs_qlen => 200, - flush_reqs_qlen => 300}}, + HConfig#{config => StdHConfig#{enable_burst_limit => true, + burst_limit_size => ReqLimit, + burst_window_time => 2000, + drop_new_reqs_qlen => 200, + flush_reqs_qlen => 300}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, send_burst({n,NumOfReqs}, seq, {chars,79}, info), @@ -913,11 +913,11 @@ limit_burst_enabled_period(Config) -> ReqLimit = 10, BurstTWin = 1000, NewHConfig = - HConfig#{logger_std_h => StdHConfig#{enable_burst_limit => true, - burst_limit_size => ReqLimit, - burst_window_time => BurstTWin, - drop_new_reqs_qlen => 20000, - flush_reqs_qlen => 20001}}, + HConfig#{config => StdHConfig#{enable_burst_limit => true, + burst_limit_size => ReqLimit, + burst_window_time => BurstTWin, + drop_new_reqs_qlen => 20000, + flush_reqs_qlen => 20001}}, ok = logger:set_handler_config(?MODULE, NewHConfig), Windows = 3, @@ -934,9 +934,9 @@ limit_burst_enabled_period(cleanup, _Config) -> kill_disabled(Config) -> {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NewHConfig = - HConfig#{logger_std_h=>StdHConfig#{enable_kill_overloaded=>false, - handler_overloaded_qlen=>10, - handler_overloaded_mem=>100}}, + HConfig#{config=>StdHConfig#{enable_kill_overloaded=>false, + handler_overloaded_qlen=>10, + handler_overloaded_mem=>100}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, send_burst({n,NumOfReqs}, seq, {chars,79}, info), @@ -954,10 +954,10 @@ qlen_kill_new(Config) -> {_,Mem0} = process_info(Pid0, memory), RestartAfter = ?HANDLER_RESTART_AFTER, NewHConfig = - HConfig#{logger_std_h=>StdHConfig#{enable_kill_overloaded=>true, - handler_overloaded_qlen=>10, - handler_overloaded_mem=>Mem0+50000, - handler_restart_after=>RestartAfter}}, + HConfig#{config=>StdHConfig#{enable_kill_overloaded=>true, + handler_overloaded_qlen=>10, + handler_overloaded_mem=>Mem0+50000, + handler_restart_after=>RestartAfter}}, ok = logger:set_handler_config(?MODULE, NewHConfig), MRef = erlang:monitor(process, Pid0), NumOfReqs = 100, @@ -993,7 +993,7 @@ qlen_kill_std(_Config) -> %% Log = filename:join(Dir, File), %% Node = start_std_h_on_new_node(Config, ?FUNCTION_NAME, Log), %% ok = rpc:call(Node, logger, set_handler_config, - %% [?STANDARD_HANDLER, logger_std_h, + %% [?STANDARD_HANDLER, config, %% #{enable_kill_overloaded=>true, %% handler_overloaded_qlen=>10, %% handler_overloaded_mem=>100000}]), @@ -1005,10 +1005,10 @@ mem_kill_new(Config) -> {_,Mem0} = process_info(Pid0, memory), RestartAfter = ?HANDLER_RESTART_AFTER, NewHConfig = - HConfig#{logger_std_h=>StdHConfig#{enable_kill_overloaded=>true, - handler_overloaded_qlen=>50000, - handler_overloaded_mem=>Mem0+500, - handler_restart_after=>RestartAfter}}, + HConfig#{config=>StdHConfig#{enable_kill_overloaded=>true, + handler_overloaded_qlen=>50000, + handler_overloaded_mem=>Mem0+500, + handler_restart_after=>RestartAfter}}, ok = logger:set_handler_config(?MODULE, NewHConfig), MRef = erlang:monitor(process, Pid0), NumOfReqs = 100, @@ -1043,9 +1043,9 @@ mem_kill_std(_Config) -> restart_after(Config) -> {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NewHConfig1 = - HConfig#{logger_std_h=>StdHConfig#{enable_kill_overloaded=>true, - handler_overloaded_qlen=>10, - handler_restart_after=>never}}, + HConfig#{config=>StdHConfig#{enable_kill_overloaded=>true, + handler_overloaded_qlen=>10, + handler_restart_after=>never}}, ok = logger:set_handler_config(?MODULE, NewHConfig1), MRef1 = erlang:monitor(process, whereis(h_proc_name())), %% kill handler @@ -1063,9 +1063,9 @@ restart_after(Config) -> {Log,_,_} = start_handler(?MODULE, ?FUNCTION_NAME, Config), RestartAfter = ?HANDLER_RESTART_AFTER, NewHConfig2 = - HConfig#{logger_std_h=>StdHConfig#{enable_kill_overloaded=>true, - handler_overloaded_qlen=>10, - handler_restart_after=>RestartAfter}}, + HConfig#{config=>StdHConfig#{enable_kill_overloaded=>true, + handler_overloaded_qlen=>10, + handler_restart_after=>RestartAfter}}, ok = logger:set_handler_config(?MODULE, NewHConfig2), Pid0 = whereis(h_proc_name()), MRef2 = erlang:monitor(process, Pid0), @@ -1095,10 +1095,10 @@ handler_requests_under_load(Config) -> {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NewHConfig = - HConfig#{logger_std_h => StdHConfig#{toggle_sync_qlen => 2, - drop_new_reqs_qlen => 1000, - flush_reqs_qlen => 2000, - enable_burst_limit => false}}, + HConfig#{config => StdHConfig#{toggle_sync_qlen => 2, + drop_new_reqs_qlen => 1000, + flush_reqs_qlen => 2000, + enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), Pid = spawn_link(fun() -> send_requests(?MODULE, 1, [{sync,[]}, {info,[]}, @@ -1131,7 +1131,7 @@ send_requests(HName, TO, Reqs = [{Req,Res}|Rs]) -> Result = case Req of change_config -> - logger:set_handler_config(HName, logger_std_h, + logger:set_handler_config(HName, config, #{enable_kill_overloaded => false}); Func -> @@ -1147,11 +1147,11 @@ start_handler(Name, TTY, Config) when TTY == standard_io; TTY == standard_error-> ok = logger:add_handler(Name, logger_std_h, - #{logger_std_h => #{type => TTY}, + #{config => #{type => TTY}, filter_default=>log, filters=>?DEFAULT_HANDLER_FILTERS([Name]), formatter=>{?MODULE,op}}), - {ok,{_,HConfig = #{logger_std_h := StdHConfig}}} = + {ok,{_,HConfig = #{config := StdHConfig}}} = logger:get_handler_config(Name), {HConfig,StdHConfig}; @@ -1162,11 +1162,11 @@ start_handler(Name, FuncName, Config) -> Type = {file,Log}, ok = logger:add_handler(Name, logger_std_h, - #{logger_std_h => #{type => Type}, + #{config => #{type => Type}, filter_default=>log, filters=>?DEFAULT_HANDLER_FILTERS([Name]), formatter=>{?MODULE,op}}), - {ok,{_,HConfig = #{logger_std_h := StdHConfig}}} = + {ok,{_,HConfig = #{config := StdHConfig}}} = logger:get_handler_config(Name), {Log,HConfig,StdHConfig}. @@ -1285,7 +1285,7 @@ format(#{msg:={string,String0}},Pid) -> add_remove_instance_nofile(Type) -> ok = logger:add_handler(?MODULE,logger_std_h, - #{logger_std_h => #{type => Type}, + #{config => #{type => Type}, filter_default=>stop, filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,self()}}), -- cgit v1.2.3 From f20c281be6fa27e2da1c543a64029531c5f00325 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 7 Jun 2018 11:47:05 +0200 Subject: [logger] Update return values from get_handler_config/0,1 and get_config/0 Module and Id are now always included as fields in Config, so these are no longer returned as separate elements. --- lib/kernel/src/error_logger.erl | 4 +- lib/kernel/src/logger.erl | 20 ++-- lib/kernel/src/logger_h_common.erl | 2 +- lib/kernel/src/logger_server.erl | 11 +- lib/kernel/test/logger_SUITE.erl | 30 ++--- lib/kernel/test/logger_bench_SUITE.erl | 2 +- lib/kernel/test/logger_disk_log_h_SUITE.erl | 12 +- lib/kernel/test/logger_env_var_SUITE.erl | 170 +++++++++++++++------------- lib/kernel/test/logger_std_h_SUITE.erl | 11 +- 9 files changed, 138 insertions(+), 124 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index 3bdd70fa96..ef930317b1 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -513,7 +513,7 @@ tty(true) -> _ = case lists:member(error_logger_tty_h, which_report_handlers()) of false -> case logger:get_handler_config(default) of - {ok,{logger_std_h,#{config:=#{type:=standard_io}}}} -> + {ok,#{module:=logger_std_h,config:=#{type:=standard_io}}} -> logger:remove_handler_filter(default, error_logger_tty_false); _ -> @@ -533,7 +533,7 @@ tty(false) -> delete_report_handler(error_logger_tty_h), _ = logger:remove_handler(error_logger_tty_true), _ = case logger:get_handler_config(default) of - {ok,{logger_std_h,#{config:=#{type:=standard_io}}}} -> + {ok,#{module:=logger_std_h,config:=#{type:=standard_io}}} -> logger:add_handler_filter(default,error_logger_tty_false, {fun(_,_) -> stop end, ok}); _ -> diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index df7ce22e2a..f6dc8ea397 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -390,21 +390,23 @@ get_primary_config() -> {ok,Config} = logger_config:get(?LOGGER_TABLE,primary), maps:remove(handlers,Config). --spec get_handler_config(HandlerId) -> {ok,{Module,Config}} | {error,term()} when +-spec get_handler_config(HandlerId) -> {ok,Config} | {error,term()} when HandlerId :: handler_id(), - Module :: module(), Config :: config(). get_handler_config(HandlerId) -> - logger_config:get(?LOGGER_TABLE,HandlerId). + case logger_config:get(?LOGGER_TABLE,HandlerId) of + {ok,{_,Config}} -> + {ok,Config}; + Error -> + Error + end. --spec get_handler_config() -> [{HandlerId,Module,Config}] when - HandlerId :: handler_id(), - Module :: module(), +-spec get_handler_config() -> [Config] when Config :: config(). get_handler_config() -> [begin - {ok,{Module,Config}} = get_handler_config(HandlerId), - {HandlerId,Module,Config} + {ok,Config} = get_handler_config(HandlerId), + Config end || HandlerId <- get_handler_ids()]. -spec get_handler_ids() -> [HandlerId] when @@ -511,7 +513,7 @@ unset_process_metadata() -> ok. -spec get_config() -> #{primary=>config(), - handlers=>[{handler_id(),module(),config()}], + handlers=>[config()], module_levels=>[{module(),level() | all | none}]}. get_config() -> #{primary=>get_primary_config(), diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl index 73a5b27ea1..2a935328aa 100644 --- a/lib/kernel/src/logger_h_common.erl +++ b/lib/kernel/src/logger_h_common.erl @@ -266,7 +266,7 @@ stop_or_restart(Name, {shutdown,Reason={overloaded,_Name,_QLen,_Mem}}, exit(HandlerPid, kill) end, case logger:get_handler_config(Name) of - {ok,{HMod,HConfig}} when is_integer(RestartAfter) -> + {ok,#{module:=HMod}=HConfig} when is_integer(RestartAfter) -> _ = logger:remove_handler(Name), _ = timer:apply_after(RestartAfter, logger, add_handler, [Name,HMod,HConfig]); diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index 7df762053e..6d4ab0925d 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -107,10 +107,9 @@ cache_module_level(Module) -> set_config(Owner,Key,Value) -> update_config(Owner,#{Key=>Value}). -set_config(Owner,Config0) -> - case sanity_check(Owner,Config0) of +set_config(Owner,Config) -> + case sanity_check(Owner,Config) of ok -> - Config = maps:merge(default_config(Owner),Config0), call({set_config,Owner,Config}); Error -> Error @@ -215,13 +214,15 @@ handle_call({update_config,Id,NewConfig}, From, #state{tid=Tid}=State) -> Error -> {reply,Error,State} end; -handle_call({set_config,primary,Config}, _From, #state{tid=Tid}=State) -> +handle_call({set_config,primary,Config0}, _From, #state{tid=Tid}=State) -> + Config = maps:merge(default_config(primary),Config0), {ok,#{handlers:=Handlers}} = logger_config:get(Tid,primary), Reply = do_set_config(Tid,primary,Config#{handlers=>Handlers}), {reply,Reply,State}; -handle_call({set_config,HandlerId,Config}, From, #state{tid=Tid}=State) -> +handle_call({set_config,HandlerId,Config0}, From, #state{tid=Tid}=State) -> case logger_config:get(Tid,HandlerId) of {ok,{Module,OldConfig}} -> + Config = maps:merge(default_config(HandlerId,Module),Config0), call_h_async( fun() -> call_h(Module,changing_config,[OldConfig,Config], diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index ca0e7e23b5..da3345524a 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -50,7 +50,7 @@ init_per_suite(Config) -> end_per_suite(Config) -> case ?config(default_handler,Config) of - {HMod,HConfig} -> + #{module:=HMod} = HConfig -> ok = logger:add_handler(?STANDARD_HANDLER,HMod,HConfig); _ -> ok @@ -111,8 +111,8 @@ add_remove_handler(_Config) -> ok = logger:add_handler(h1,?MODULE,#{}), [add] = test_server:messages_get(), Hs = logger:get_handler_config(), - {value,_,Hs0} = lists:keytake(h1,1,Hs), - {ok,{?MODULE,#{level:=all,filters:=[],filter_default:=log}}} = % defaults + Hs0 = lists:filter(fun(#{id:=h1}) -> false; (_) -> true end, Hs), + {ok,#{module:=?MODULE,level:=all,filters:=[],filter_default:=log}} = %defaults logger:get_handler_config(h1), ok = logger:set_handler_config(h1,filter_default,stop), [changing_config] = test_server:messages_get(), @@ -120,7 +120,7 @@ add_remove_handler(_Config) -> ok = check_no_log(), ok = logger:set_handler_config(h1,filter_default,log), [changing_config] = test_server:messages_get(), - {ok,{?MODULE,#{filter_default:=log}}} = logger:get_handler_config(h1), + {ok,#{filter_default:=log}} = logger:get_handler_config(h1), ?LOG_INFO("hello",[]), ok = check_logged(info,"hello",[],?MY_LOC(1)), ok = logger:remove_handler(h1), @@ -211,12 +211,12 @@ add_remove_filter(cleanup,_Config) -> change_config(_Config) -> %% Overwrite handler config - check that defaults are added ok = logger:add_handler(h1,?MODULE,#{level=>info,custom=>custom}), - {ok,{?MODULE,#{level:=info,filter_default:=log,custom:=custom}}} = + {ok,#{module:=?MODULE,level:=info,filter_default:=log,custom:=custom}} = logger:get_handler_config(h1), register(callback_receiver,self()), ok = logger:set_handler_config(h1,#{filter_default=>stop}), [changing_config] = test_server:messages_get(), - {ok,{?MODULE,#{level:=all,filter_default:=stop}=C2}} = + {ok,#{module:=?MODULE,level:=all,filter_default:=stop}=C2} = logger:get_handler_config(h1), false = maps:is_key(custom,C2), {error,fail} = logger:set_handler_config(h1,#{conf_call=>fun() -> {error,fail} end}), @@ -226,19 +226,19 @@ change_config(_Config) -> ok = logger:set_handler_config( h1,#{conf_call=>fun() -> logger:set_module_level(?MODULE,debug) end}), - {ok,{?MODULE,C2}} = logger:get_handler_config(h1), + {ok,C2} = logger:get_handler_config(h1), %% Change handler config: Single key {error,fail} = logger:set_handler_config(h1,conf_call,fun() -> {error,fail} end), ok = logger:set_handler_config(h1,custom,custom), [changing_config] = test_server:messages_get(), - {ok,{?MODULE,#{custom:=custom}=C3}} = logger:get_handler_config(h1), + {ok,#{custom:=custom}=C3} = logger:get_handler_config(h1), C2 = maps:remove(custom,C3), %% Change handler config: Map ok = logger:update_handler_config(h1,#{custom=>new_custom}), [changing_config] = test_server:messages_get(), - {ok,{_,C4}} = logger:get_handler_config(h1), + {ok,C4} = logger:get_handler_config(h1), C4 = C3#{custom:=new_custom}, %% Change primary config: Single key @@ -259,9 +259,9 @@ change_config(_Config) -> 3 = maps:size(PC1), %% Check that internal 'handlers' field has not been changed MS = [{{{?HANDLER_KEY,'$1'},'_','_','_'},[],['$1']}], - HIds1 = ets:select(?LOGGER_TABLE,MS), % dirty, checking internal data + HIds1 = lists:sort(ets:select(?LOGGER_TABLE,MS)), % dirty, internal data HIds2 = lists:sort(logger:get_handler_ids()), - HIds1 = lists:sort(HIds2), + HIds1 = HIds2, %% Cleanup ok = logger:set_primary_config(PConfig0), @@ -488,13 +488,13 @@ filter_failed(_Config) -> {error,{invalid_filter,_}} = logger:add_handler_filter(h1,hf,{fun(_) -> ok end,args}), ok = logger:add_handler_filter(h1,hf,{fun(_,_) -> a=b end,args}), - {ok,{?MODULE,#{filters:=[_]}}} = logger:get_handler_config(h1), + {ok,#{filters:=[_]}} = logger:get_handler_config(h1), ok = logger:info(M3=?map_rep), ok = check_logged(info,M3,#{}), {error,{not_found,hf}} = logger:remove_handler_filter(h1,hf), ok = logger:add_handler_filter(h1,hf,{fun(_,_) -> faulty_return end,args}), - {ok,{?MODULE,#{filters:=[_]}}} = logger:get_handler_config(h1), + {ok,#{filters:=[_]}} = logger:get_handler_config(h1), ok = logger:info(M4=?map_rep), ok = check_logged(info,M4,#{}), {error,{not_found,hf}} = logger:remove_handler_filter(h1,hf), @@ -520,7 +520,7 @@ handler_failed(_Config) -> logger:info(?map_rep), check_no_log(), H1 = logger:get_handler_config(), - false = lists:keymember(h1,1,H1), + false = lists:search(fun(#{id:=h1}) -> true; (_) -> false end,H1), {error,{not_found,h1}} = logger:remove_handler(h1), ok = logger:add_handler(h2,?MODULE,#{filter_default=>log,log_call=>fun() -> a = b end}), @@ -530,7 +530,7 @@ handler_failed(_Config) -> logger:info(?map_rep), [remove] = test_server:messages_get(), H2 = logger:get_handler_config(), - false = lists:keymember(h2,1,H2), + false = lists:search(fun(#{id:=h2}) -> true; (_) -> false end,H2), {error,{not_found,h2}} = logger:remove_handler(h2), CallAddHandler = fun() -> logger:add_handler(h2,?MODULE,#{}) end, diff --git a/lib/kernel/test/logger_bench_SUITE.erl b/lib/kernel/test/logger_bench_SUITE.erl index b60fc42741..c6aa541a94 100644 --- a/lib/kernel/test/logger_bench_SUITE.erl +++ b/lib/kernel/test/logger_bench_SUITE.erl @@ -366,7 +366,7 @@ calc_and_report(Config,Tag,MSecs,Times) -> remove_all_handlers() -> Hs = logger:get_handler_config(), - [logger:remove_handler(Id) || {Id,_,_} <- Hs], + [logger:remove_handler(Id) || #{id:=Id} <- Hs], Hs. add_all_handlers(Hs) -> diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 552cb05797..8fefbd1426 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -328,13 +328,11 @@ formatter_fail(Config) -> logger:add_handler(Name, logger_disk_log_h, HConfig), Pid = whereis(h_proc_name(Name)), true = is_pid(Pid), - HC1 = logger:get_handler_config(), - H = [Id || {Id,_,_} <- HC1], + H = logger:get_handler_ids(), true = lists:member(Name,H), %% Formatter is added automatically - {ok,{_,#{formatter:={logger_formatter,_}}}} = - logger:get_handler_config(Name), + {ok,#{formatter:={logger_formatter,_}}} = logger:get_handler_config(Name), logger:info(M1=?msg,?domain), Got1 = try_match_file(?log_no(LogFile,1),"[0-9\\+\\-T:\\.]* info: "++M1,5000), @@ -358,8 +356,7 @@ formatter_fail(Config) -> %% Check that handler is still alive and was never dead Pid = whereis(h_proc_name(Name)), - HC2 = logger:get_handler_config(), - H = [Id || {Id,_,_} <- HC2], + H = logger:get_handler_ids(), ok. formatter_fail(cleanup,_Config) -> @@ -1227,8 +1224,7 @@ start_handler(Name, FuncName, Config) -> filters=>?DEFAULT_HANDLER_FILTERS([Name]), formatter=>{?MODULE,op}, level => info}), - {ok,{_,HConfig = #{config := DLHConfig}}} = - logger:get_handler_config(Name), + {ok,HConfig = #{config := DLHConfig}} = logger:get_handler_config(Name), {lists:concat([File,".1"]),HConfig,DLHConfig}. stop_handler(Name) -> diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl index fcdfcfd6d0..329b4c4b88 100644 --- a/lib/kernel/test/logger_env_var_SUITE.erl +++ b/lib/kernel/test/logger_env_var_SUITE.erl @@ -79,49 +79,49 @@ all() -> default(Config) -> {ok,#{handlers:=Hs},_Node} = setup(Config,[]), - {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), true = lists:keymember(stop_progress,1,StdFilters), - false = lists:keymember(simple,1,Hs), - false = lists:keymember(sasl,1,Hs), + false = exists(simple,Hs), + false = exists(sasl,Hs), ok. default_sasl_compatible(Config) -> {ok,#{handlers:=Hs},_Node} = setup(Config, [{logger_sasl_compatible,true}]), - {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), - false = lists:keymember(simple,1,Hs), - true = lists:keymember(sasl,1,Hs), + false = exists(simple,Hs), + true = exists(sasl,Hs), ok. 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), + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), true = lists:keymember(stop_progress,1,StdFilters), - false = lists:keymember(simple,1,Hs), - false = lists:keymember(sasl,1,Hs), + false = exists(simple,Hs), + false = exists(sasl,Hs), ok. 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), + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), - false = lists:keymember(simple,1,Hs), - true = lists:keymember(sasl,1,Hs), + false = exists(simple,Hs), + true = exists(sasl,Hs), ok. error_logger_false(Config) -> @@ -129,14 +129,14 @@ error_logger_false(Config) -> setup(Config, [{error_logger,false}, {logger_level,notice}]), - false = lists:keymember(?STANDARD_HANDLER,1,Hs), - {simple,logger_simple_h,SimpleC} = lists:keyfind(simple,1,Hs), + false = exists(?STANDARD_HANDLER,Hs), + #{module:=logger_simple_h} = SimpleC = find(simple,Hs), all = maps:get(level,SimpleC), notice = maps:get(level,P), SimpleFilters = maps:get(filters,SimpleC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), true = lists:keymember(stop_progress,1,SimpleFilters), - false = lists:keymember(sasl,1,Hs), + false = exists(sasl,Hs), ok. error_logger_false_progress(Config) -> @@ -145,14 +145,14 @@ error_logger_false_progress(Config) -> [{error_logger,false}, {logger_level,notice}, {logger_progress_reports,log}]), - false = lists:keymember(?STANDARD_HANDLER,1,Hs), - {simple,logger_simple_h,SimpleC} = lists:keyfind(simple,1,Hs), + false = exists(?STANDARD_HANDLER,Hs), + #{module:=logger_simple_h} = SimpleC = find(simple,Hs), all = maps:get(level,SimpleC), notice = maps:get(level,P), SimpleFilters = maps:get(filters,SimpleC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), false = lists:keymember(stop_progress,1,SimpleFilters), - false = lists:keymember(sasl,1,Hs), + false = exists(sasl,Hs), ok. error_logger_false_sasl_compatible(Config) -> @@ -161,38 +161,38 @@ error_logger_false_sasl_compatible(Config) -> [{error_logger,false}, {logger_level,notice}, {logger_sasl_compatible,true}]), - false = lists:keymember(?STANDARD_HANDLER,1,Hs), - {simple,logger_simple_h,SimpleC} = lists:keyfind(simple,1,Hs), + false = exists(?STANDARD_HANDLER,Hs), + #{module:=logger_simple_h} = SimpleC = find(simple,Hs), all = maps:get(level,SimpleC), notice = maps:get(level,P), SimpleFilters = maps:get(filters,SimpleC), {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,SimpleFilters), false = lists:keymember(stop_progress,1,SimpleFilters), - true = lists:keymember(sasl,1,Hs), + true = exists(sasl,Hs), ok. error_logger_silent(Config) -> {ok,#{handlers:=Hs},_Node} = setup(Config, [{error_logger,silent}]), - false = lists:keymember(?STANDARD_HANDLER,1,Hs), - false = lists:keymember(simple,1,Hs), - false = lists:keymember(sasl,1,Hs), + false = exists(?STANDARD_HANDLER,Hs), + false = exists(simple,Hs), + false = exists(sasl,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(simple,1,Hs), - true = lists:keymember(sasl,1,Hs), + false = exists(?STANDARD_HANDLER,Hs), + false = exists(simple,Hs), + true = exists(sasl,Hs), ok. error_logger_file(Config) -> Log = file(Config,?FUNCTION_NAME), - {ok,_Hs,Node} = setup(Config, - [{error_logger,{file,Log}}]), + {ok,_,Node} = setup(Config, + [{error_logger,{file,Log}}]), check_default_log(Node,Log, file,% dest 0),% progress in std logger @@ -210,13 +210,13 @@ logger_file(Config) -> file,% dest 0),% progress in std logger - {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), true = lists:keymember(stop_progress,1,StdFilters), - false = lists:keymember(simple,1,Hs), - false = lists:keymember(sasl,1,Hs), + false = exists(simple,Hs), + false = exists(sasl,Hs), ok. @@ -232,13 +232,13 @@ logger_file_sasl_compatible(Config) -> file,% dest 0),% progress in std logger - {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), - false = lists:keymember(simple,1,Hs), - true = lists:keymember(sasl,1,Hs), + false = exists(simple,Hs), + true = exists(sasl,Hs), ok. @@ -254,13 +254,13 @@ logger_file_log_progress(Config) -> file,% dest 6),% progress in std logger - {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), - false = lists:keymember(simple,1,Hs), - false = lists:keymember(sasl,1,Hs), + false = exists(simple,Hs), + false = exists(sasl,Hs), ok. @@ -276,11 +276,11 @@ logger_file_no_filter(Config) -> file,% dest 6),% progress in std logger - {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), [] = maps:get(filters,StdC), - false = lists:keymember(simple,1,Hs), - false = lists:keymember(sasl,1,Hs), + false = exists(simple,Hs), + false = exists(sasl,Hs), ok. @@ -297,11 +297,11 @@ logger_file_no_filter_level(Config) -> 0,% progress in std logger error),% level - {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), error = maps:get(level,StdC), [] = maps:get(filters,StdC), - false = lists:keymember(simple,1,Hs), - false = lists:keymember(sasl,1,Hs), + false = exists(simple,Hs), + false = exists(sasl,Hs), ok. @@ -318,11 +318,11 @@ logger_file_formatter(Config) -> file,% dest 6),% progress in std logger - {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), [] = maps:get(filters,StdC), - false = lists:keymember(simple,1,Hs), - false = lists:keymember(sasl,1,Hs), + false = exists(simple,Hs), + false = exists(sasl,Hs), ok. @@ -340,13 +340,13 @@ logger_filters(Config) -> file,% dest 0),% progress in std logger - {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), - false = lists:keymember(simple,1,Hs), - false = lists:keymember(sasl,1,Hs), + false = exists(simple,Hs), + false = exists(sasl,Hs), LoggerFilters = maps:get(filters,P), true = lists:keymember(stop_progress,1,LoggerFilters), @@ -368,11 +368,11 @@ logger_filters_stop(Config) -> 0,% progress in std logger notice), - {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), [] = maps:get(filters,StdC), - false = lists:keymember(simple,1,Hs), - false = lists:keymember(sasl,1,Hs), + false = exists(simple,Hs), + false = exists(sasl,Hs), LoggerFilters = maps:get(filters,P), true = lists:keymember(log_error,1,LoggerFilters), @@ -392,13 +392,13 @@ logger_module_level(Config) -> file,% dest 3),% progress in std logger - {?STANDARD_HANDLER,logger_std_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), + #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), false = lists:keymember(stop_progress,1,StdFilters), - false = lists:keymember(simple,1,Hs), - false = lists:keymember(sasl,1,Hs), + false = exists(simple,Hs), + false = exists(sasl,Hs), [{supervisor,error}] = ModuleLevels, ok. @@ -413,13 +413,13 @@ logger_disk_log(Config) -> disk_log,% dest 0),% progress in std logger - {?STANDARD_HANDLER,logger_disk_log_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), + #{module:=logger_disk_log_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), true = lists:keymember(stop_progress,1,StdFilters), - false = lists:keymember(simple,1,Hs), - false = lists:keymember(sasl,1,Hs), + false = exists(simple,Hs), + false = exists(sasl,Hs), ok. @@ -436,25 +436,25 @@ logger_disk_log_formatter(Config) -> disk_log,% dest 6),% progress in std logger - {?STANDARD_HANDLER,logger_disk_log_h,StdC} = lists:keyfind(?STANDARD_HANDLER,1,Hs), + #{module:=logger_disk_log_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), [] = maps:get(filters,StdC), - false = lists:keymember(simple,1,Hs), - false = lists:keymember(sasl,1,Hs), + false = exists(simple,Hs), + false = exists(sasl,Hs), ok. logger_undefined(Config) -> {ok,#{handlers:=Hs,primary:=P},_Node} = setup(Config,[{logger,[{handler,?STANDARD_HANDLER,undefined}]}]), - false = lists:keymember(?STANDARD_HANDLER,1,Hs), - {simple,logger_simple_h,SimpleC} = lists:keyfind(simple,1,Hs), + false = exists(?STANDARD_HANDLER,Hs), + #{module:=logger_simple_h} = SimpleC = find(simple,Hs), all = maps:get(level,SimpleC), info = maps:get(level,P), SimpleFilters = maps:get(filters,SimpleC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), true = lists:keymember(stop_progress,1,SimpleFilters), - false = lists:keymember(sasl,1,Hs), + false = exists(sasl,Hs), ok. @@ -535,10 +535,10 @@ logger_many_handlers(Config, Env, LogErr, LogInfo, NumProgress) -> sasl_compatible_false(Config) -> Log = file(Config,?FUNCTION_NAME), - {ok,_Hs,Node} = setup(Config, - [{error_logger,{file,Log}}, - {logger_sasl_compatible,false}, - {logger_progress_reports,log}]), + {ok,_,Node} = setup(Config, + [{error_logger,{file,Log}}, + {logger_sasl_compatible,false}, + {logger_progress_reports,log}]), check_default_log(Node,Log, file,% dest 6),% progress in std logger @@ -546,10 +546,10 @@ sasl_compatible_false(Config) -> sasl_compatible_false_no_progress(Config) -> Log = file(Config,?FUNCTION_NAME), - {ok,_Hs,Node} = setup(Config, - [{error_logger,{file,Log}}, - {logger_sasl_compatible,false}, - {logger_progress_reports,stop}]), + {ok,_,Node} = setup(Config, + [{error_logger,{file,Log}}, + {logger_sasl_compatible,false}, + {logger_progress_reports,stop}]), check_default_log(Node,Log, file,% dest 0),% progress in std logger @@ -557,9 +557,9 @@ sasl_compatible_false_no_progress(Config) -> sasl_compatible(Config) -> Log = file(Config,?FUNCTION_NAME), - {ok,_Hs,Node} = setup(Config, - [{error_logger,{file,Log}}, - {sasl_compatible,true}]), + {ok,_,Node} = setup(Config, + [{error_logger,{file,Log}}, + {sasl_compatible,true}]), check_default_log(Node,Log, file,% dest 0),% progress in std logger @@ -640,3 +640,21 @@ match(Bin,Pattern,N,LogLevel,ConfLevel) -> {match,M} = re:run(Bin,Pattern,[{capture,all},global]), N = length(M) end. + +find(Id,Handlers) -> + case lists:search(fun(#{id:=Id0}) when Id0=:=Id-> true; + (_) -> false end, + Handlers) of + {value,Config} -> + Config; + false -> + false + end. + +exists(Id,Handlers) -> + case find(Id,Handlers) of + false -> + false; + _ -> + true + end. diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index 97b5c03a86..fd1840b9ff 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -55,7 +55,7 @@ suite() -> init_per_suite(Config) -> timer:start(), % to avoid progress report - {ok,{logger_std_h,#{formatter:=OrigFormatter}}} = + {ok,#{formatter:=OrigFormatter}} = logger:get_handler_config(?STANDARD_HANDLER), [{formatter,OrigFormatter}|Config]. @@ -246,8 +246,7 @@ formatter_fail(Config) -> true = lists:member(?MODULE,H), %% Formatter is added automatically - {ok,{_,#{formatter:={logger_formatter,_}}}} = - logger:get_handler_config(?MODULE), + {ok,#{formatter:={logger_formatter,_}}} = logger:get_handler_config(?MODULE), logger:info(M1=?msg,?domain), Got1 = try_match_file(Log,"[0-9\\+\\-T:\\.]* info: "++M1,5000), @@ -1151,8 +1150,7 @@ start_handler(Name, TTY, Config) when TTY == standard_io; filter_default=>log, filters=>?DEFAULT_HANDLER_FILTERS([Name]), formatter=>{?MODULE,op}}), - {ok,{_,HConfig = #{config := StdHConfig}}} = - logger:get_handler_config(Name), + {ok,HConfig = #{config := StdHConfig}} = logger:get_handler_config(Name), {HConfig,StdHConfig}; start_handler(Name, FuncName, Config) -> @@ -1166,8 +1164,7 @@ start_handler(Name, FuncName, Config) -> filter_default=>log, filters=>?DEFAULT_HANDLER_FILTERS([Name]), formatter=>{?MODULE,op}}), - {ok,{_,HConfig = #{config := StdHConfig}}} = - logger:get_handler_config(Name), + {ok,HConfig = #{config := StdHConfig}} = logger:get_handler_config(Name), {Log,HConfig,StdHConfig}. stop_handler(Name) -> -- cgit v1.2.3 From 794df8cbba8d7942dcb3bf2cbdfa526b04d41dd3 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Fri, 8 Jun 2018 15:49:06 +0200 Subject: Use public_key to verify client hostname --- lib/kernel/src/net_kernel.erl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index 669adefdf8..c4e1a0ce1e 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -53,7 +53,7 @@ %% Documented API functions. --export([allow/1, +-export([allow/1, allowed/0, connect_node/1, monitor_nodes/1, monitor_nodes/2, @@ -171,6 +171,8 @@ kernel_apply(M,F,A) -> request({apply,M,F,A}). Nodes :: [node()]. allow(Nodes) -> request({allow, Nodes}). +allowed() -> request(allowed). + longnames() -> request(longnames). -spec stop() -> ok | {error, Reason} when @@ -528,6 +530,9 @@ handle_call({allow, Nodes}, From, State) -> async_reply({reply,error,State}, From) end; +handle_call(allowed, From, #state{allowed = Allowed} = State) -> + async_reply({reply,{ok,Allowed},State}, From); + %% %% authentication, used by auth. Simply works as this: %% if the message comes through, the other node IS authorized. -- cgit v1.2.3 From b91eceaf71e0009346d119346a33894ed53be7b0 Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Thu, 7 Jun 2018 10:59:04 +0200 Subject: [logger] Implement logging of handler overload status Conflicts: lib/kernel/src/logger_disk_log_h.erl lib/kernel/src/logger_std_h.erl --- lib/kernel/src/logger_disk_log_h.erl | 89 +++++++++++++++++------- lib/kernel/src/logger_h_common.erl | 71 ++++++++++++------- lib/kernel/src/logger_std_h.erl | 102 +++++++++++++++++++--------- lib/kernel/test/logger_disk_log_h_SUITE.erl | 43 +++++++----- lib/kernel/test/logger_std_h_SUITE.erl | 47 ++++++++----- 5 files changed, 239 insertions(+), 113 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index d339957ac8..c3d2204630 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -37,6 +37,9 @@ adding_handler/1, removing_handler/1, changing_config/2, swap_buffer/2]). +%% handler internal +-export([log_handler_info/4]). + %%%=================================================================== %%% API %%%=================================================================== @@ -250,7 +253,7 @@ swap_buffer(Name, Buffer) -> log(LogEvent, Config = #{id := Name, config := #{handler_pid := HPid, mode_tab := ModeTab}}) -> - %% if the handler has crashed, we must drop this request + %% if the handler has crashed, we must drop this event %% and hope the handler restarts so we can try again true = is_process_alive(HPid), Bin = logger_h_common:log_to_binary(LogEvent, Config), @@ -295,6 +298,16 @@ init([Name, Config = #{config := HConfig, disk_log_opts := LogOpts}, mode_tab => ModeTab}}, proc_lib:init_ack({ok,self(),Config1}), gen_server:cast(self(), repeated_disk_log_sync), + case logger_h_common:unset_restart_flag(Name, ?MODULE) of + true -> + %% inform about restart + gen_server:cast(self(), {log_handler_info, + "Handler ~p restarted", + [Name]}); + false -> + %% initial start + ok + end, enter_loop(Config1, State1) catch _:Error -> @@ -315,15 +328,13 @@ enter_loop(#{wait_for_buffer:=true}=Config,State) -> Bin = logger_h_common:log_to_binary(Log,Config), {_,S1} = do_log(Bin,cast,S), S1 - end, - State, - Buffer) + end, State, Buffer) end, gen_server:enter_loop(?MODULE,[],State1); enter_loop(_Config,State) -> gen_server:enter_loop(?MODULE,[],State). -%% This is the synchronous log request. +%% This is the synchronous log event. handle_call({log, Bin}, _From, State) -> {Result,State1} = do_log(Bin, call, State), %% Result == ok | dropped @@ -377,15 +388,19 @@ handle_call(stop, _From, State) -> {stop, {shutdown,stopped}, ok, State}. -%% This is the asynchronous log request. +%% This is the asynchronous log event. handle_cast({log, Bin}, State) -> {_,State1} = do_log(Bin, cast, State), {noreply, State1}; +handle_cast({log_handler_info, Format, Args}, State = #{id:=Name}) -> + log_handler_info(Name, Format, Args, State), + {noreply, State}; + %% If FILESYNC_REPEAT_INTERVAL is set to a millisec value, this %% clause gets called repeatedly by the handler. In order to %% guarantee that a filesync *always* happens after the last log -%% request, the repeat operation must be active! +%% event, the repeat operation must be active! handle_cast(repeated_disk_log_sync, State = #{id := Name, filesync_repeat_interval := FSyncInt, @@ -526,21 +541,31 @@ stop(Name) -> if C == 0 -> Interval; true -> C-1 end). -%% check for overload between every request (and set Mode to async, +%% check for overload between every event (and set Mode to async, %% sync or drop accordingly), but never flush the whole mailbox -%% before LogWindowSize requests have been handled -do_log(Bin, CallOrCast, State = #{id:=Name, mode := _Mode0}) -> +%% before LogWindowSize events have been handled +do_log(Bin, CallOrCast, State = #{id:=Name, mode := Mode0}) -> T1 = ?timestamp(), %% check if the handler is getting overloaded, or if it's %% recovering from overload (the check must be done for each - %% request to react quickly to large bursts of requests and + %% event to react quickly to large bursts of events and %% to ensure that the handler can never end up in drop mode %% with an empty mailbox, which would stop operation) {Mode1,QLen,Mem,State1} = logger_h_common:check_load(State), + if (Mode1 == drop) andalso (Mode0 =/= drop) -> + log_handler_info(Name, "Handler ~p switched to drop mode", + [Name], State); + (Mode0 == drop) andalso ((Mode1 == async) orelse (Mode1 == sync)) -> + log_handler_info(Name, "Handler ~p switched to ~w mode", + [Name,Mode1], State); + true -> + ok + end, + %% kill the handler if it can't keep up with the load - logger_h_common:kill_if_choked(Name, QLen, Mem, State), + logger_h_common:kill_if_choked(Name, QLen, Mem, ?MODULE, State), if Mode1 == flush -> flush(Name, QLen, T1, State1); @@ -550,22 +575,26 @@ do_log(Bin, CallOrCast, State = #{id:=Name, mode := _Mode0}) -> %% this function is called by do_log/3 after an overload check %% has been performed, where QLen > FlushQLen -flush(_Name, _QLen0, T1, State=#{last_log_ts := _T0, mode_tab := ModeTab}) -> +flush(Name, _QLen0, T1, State=#{last_log_ts := _T0, mode_tab := ModeTab}) -> %% flush messages in the mailbox (a limited number in %% order to not cause long delays) - _NewFlushed = logger_h_common:flush_log_requests(?FLUSH_MAX_N), + NewFlushed = logger_h_common:flush_log_events(?FLUSH_MAX_N), + + %% write info in log about flushed messages + log_handler_info(Name, "Handler ~p flushed ~w log events", + [Name,NewFlushed], State), %% because of the receive loop when flushing messages, the %% handler will be scheduled out often and the mailbox could %% grow very large, so we'd better check the queue again here {_,_QLen1} = process_info(self(), message_queue_len), - ?observe(_Name,{max_qlen,_QLen1}), + ?observe(Name,{max_qlen,_QLen1}), - %% Add 1 for the current log request - ?observe(_Name,{flushed,_NewFlushed+1}), + %% Add 1 for the current log event + ?observe(Name,{flushed,NewFlushed+1}), State1 = ?update_max_time(?diff_time(T1,_T0),State), - {dropped,?update_other(flushed,FLUSHED,_NewFlushed, + {dropped,?update_other(flushed,FLUSHED,NewFlushed, State1#{mode => ?set_mode(ModeTab,async), last_qlen => 0, last_log_ts => T1})}. @@ -578,10 +607,10 @@ write(Name, Mode, T1, Bin, _CallOrCast, last_qlen := LastQLen, last_log_ts := T0}) -> %% check if we need to limit the number of writes - %% during a burst of log requests + %% during a burst of log events {DoWrite,BurstWinT,BurstMsgCount} = logger_h_common:limit_burst(State), - %% only send a synhrounous request to the disk_log process + %% only send a synhrounous event to the disk_log process %% every DLSyncInt time, to give the handler time between %% writes so it can keep up with incoming messages {Status,LastQLen1,State1} = @@ -599,13 +628,13 @@ write(Name, Mode, T1, Bin, _CallOrCast, {dropped, LastQLen, State} end, - %% Check if the time since the previous log request is long enough - + %% Check if the time since the previous log event is long enough - %% and the queue length small enough - to assume the mailbox has %% been emptied, and if so, do filesync operation and reset mode to %% async. Note that this is the best we can do to detect an idle %% handler without setting a timer after each log call/cast. If the - %% time between two consecutive log requests is fast and no new - %% request comes in after the last one, idle state won't be detected! + %% time between two consecutive log events is fast and no new + %% event comes in after the last one, idle state won't be detected! Time = ?diff_time(T1,T0), {Mode1,BurstMsgCount1,State2} = if (LastQLen1 < ?FILESYNC_OK_QLEN) andalso @@ -628,6 +657,20 @@ write(Name, Mode, T1, Bin, _CallOrCast, {Status,State4}. +log_handler_info(Name, Format, Args, State) -> + Config = + case logger:get_handler_config(Name) of + {ok,Conf} -> Conf; + _ -> #{formatter=>{?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}} + end, + Meta = #{time=>erlang:system_time(microsecond)}, + Bin = logger_h_common:log_to_binary(#{level => notice, + msg => {Format,Args}, + meta => Meta}, Config), + _ = disk_log_write(Name, Bin, State), + ok. + + open_disk_log(Name, LogOpts) -> #{file := File, max_no_bytes := MaxNoBytes, diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl index 2a935328aa..d2fcc7e115 100644 --- a/lib/kernel/src/logger_h_common.erl +++ b/lib/kernel/src/logger_h_common.erl @@ -27,10 +27,12 @@ call_cast_or_drop/4, check_load/1, limit_burst/1, - kill_if_choked/4, - flush_log_requests/0, - flush_log_requests/1, + kill_if_choked/5, + flush_log_events/0, + flush_log_events/1, handler_exit/2, + set_restart_flag/2, + unset_restart_flag/2, cancel_timer/1, stop_or_restart/3, overload_levels_ok/1, @@ -52,7 +54,8 @@ log_to_binary(Log,Config) -> do_log_to_binary(Log,Config). do_log_to_binary(Log,Config) -> - {Formatter,FormatterConfig} = maps:get(formatter,Config), + {Formatter,FormatterConfig} = + maps:get(formatter,Config,{?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}), String = try_format(Log,Formatter,FormatterConfig), try unicode:characters_to_binary(String) catch _:_ -> @@ -121,10 +124,10 @@ check_common_config(_) -> %%%----------------------------------------------------------------- %%% Overload Protection call_cast_or_drop(_Name, HandlerPid, ModeTab, Bin) -> - %% If the handler process is getting overloaded, the log request + %% If the handler process is getting overloaded, the log event %% will be synchronous instead of asynchronous (slows down the %% logging tempo of a process doing lots of logging. If the - %% handler is choked, drop mode is set and no request will be sent. + %% handler is choked, drop mode is set and no event will be sent. try ?get_mode(ModeTab) of async -> gen_server:cast(HandlerPid, {log,Bin}); @@ -143,7 +146,7 @@ call_cast_or_drop(_Name, HandlerPid, ModeTab, Bin) -> ?observe(_Name,{dropped,1}) catch %% if the ETS table doesn't exist (maybe because of a - %% handler restart), we can only drop the request + %% handler restart), we can only drop the event _:_ -> ?observe(_Name,{dropped,1}) end, ok. @@ -151,6 +154,24 @@ call_cast_or_drop(_Name, HandlerPid, ModeTab, Bin) -> handler_exit(_Name, Reason) -> exit(Reason). +set_restart_flag(Name, Module) -> + Flag = list_to_atom(lists:concat([Module,"_",Name,"_restarting"])), + spawn(fun() -> + register(Flag, self()), + timer:sleep(infinity) + end), + ok. + +unset_restart_flag(Name, Module) -> + Flag = list_to_atom(lists:concat([Module,"_",Name,"_restarting"])), + case whereis(Flag) of + undefined -> + false; + Pid -> + exit(Pid, kill), + true + end. + check_load(State = #{id:=_Name, mode_tab := ModeTab, mode := Mode, toggle_sync_qlen := ToggleSyncQLen, drop_new_reqs_qlen := DropNewQLen, @@ -162,7 +183,7 @@ check_load(State = #{id:=_Name, mode_tab := ModeTab, mode := Mode, %% When the handler process gets scheduled in, it's impossible %% to predict the QLen. We could jump "up" arbitrarily from say %% async to sync, async to drop, sync to flush, etc. However, when - %% the handler process manages the log requests (without flushing), + %% the handler process manages the log events (without flushing), %% one after the other, we will move "down" from drop to sync and %% from sync to async. This way we don't risk getting stuck in %% drop or sync mode with an empty mailbox. @@ -171,7 +192,7 @@ check_load(State = #{id:=_Name, mode_tab := ModeTab, mode := Mode, QLen >= FlushQLen -> {flush, 0,1}; QLen >= DropNewQLen -> - %% Note that drop mode will force log requests to + %% Note that drop mode will force log events to %% be dropped on the client side (never sent get to %% the handler). IncDrops = if Mode == drop -> 0; true -> 1 end, @@ -208,38 +229,42 @@ limit_burst(#{burst_win_ts := BurstWinT0, {true,BurstWinT0,BurstMsgCount+1} end. -kill_if_choked(Name, QLen, Mem, - #{enable_kill_overloaded := KillIfOL, - handler_overloaded_qlen := HOLQLen, - handler_overloaded_mem := HOLMem}) -> +kill_if_choked(Name, QLen, Mem, HandlerMod, + State = #{enable_kill_overloaded := KillIfOL, + handler_overloaded_qlen := HOLQLen, + handler_overloaded_mem := HOLMem}) -> if KillIfOL andalso - ((QLen > HOLQLen) orelse (Mem > HOLMem)) -> + ((QLen > HOLQLen) orelse (Mem > HOLMem)) -> + HandlerMod:log_handler_info(Name, + "Handler ~p overloaded and stopping", + [Name], State), + set_restart_flag(Name, HandlerMod), handler_exit(Name, {shutdown,{overloaded,Name,QLen,Mem}}); true -> ok end. -flush_log_requests() -> - flush_log_requests(-1). +flush_log_events() -> + flush_log_events(-1). -flush_log_requests(Limit) -> +flush_log_events(Limit) -> process_flag(priority, high), - Flushed = flush_log_requests(0, Limit), + Flushed = flush_log_events(0, Limit), process_flag(priority, normal), Flushed. -flush_log_requests(Limit, Limit) -> +flush_log_events(Limit, Limit) -> Limit; -flush_log_requests(N, Limit) -> - %% flush log requests but leave other requests, such as +flush_log_events(N, Limit) -> + %% flush log events but leave other events, such as %% file/disk_log_sync, info and change_config, so that these %% have a chance to be processed even under heavy load receive {'$gen_cast',{log,_}} -> - flush_log_requests(N+1, Limit); + flush_log_events(N+1, Limit); {'$gen_call',{Pid,MRef},{log,_}} -> Pid ! {MRef, dropped}, - flush_log_requests(N+1, Limit) + flush_log_events(N+1, Limit) after 0 -> N end. diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index bebd5300b4..77b054a9b7 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -38,6 +38,9 @@ -export([log/2, adding_handler/1, removing_handler/1, changing_config/2, swap_buffer/2]). +%% handler internal +-export([log_handler_info/4]). + %%%=================================================================== %%% API %%%=================================================================== @@ -232,7 +235,7 @@ swap_buffer(Name, Buffer) -> log(LogEvent, Config = #{id := Name, config := #{handler_pid := HPid, mode_tab := ModeTab}}) -> - %% if the handler has crashed, we must drop this request + %% if the handler has crashed, we must drop this event %% and hope the handler restarts so we can try again true = is_process_alive(HPid), Bin = logger_h_common:log_to_binary(LogEvent, Config), @@ -284,17 +287,20 @@ init([Name, Config = #{config := HConfig}, proc_lib:init_ack(Error) end. -do_init(Name, Std) when Std=:=standard_io; Std=:=standard_error -> - case open_log_file(Name, Std) of - {ok,FileCtrlPid} -> - {ok,#{id=>Name,type=>Std,file_ctrl_pid=>FileCtrlPid}}; - Error -> - Error - end; -do_init(Name, FileInfo) when is_tuple(FileInfo) -> - case open_log_file(Name, FileInfo) of +do_init(Name, Type) -> + case open_log_file(Name, Type) of {ok,FileCtrlPid} -> - {ok,#{id=>Name,type=>FileInfo,file_ctrl_pid=>FileCtrlPid}}; + case logger_h_common:unset_restart_flag(Name, ?MODULE) of + true -> + %% inform about restart + gen_server:cast(self(), {log_handler_info, + "Handler ~p restarted", + [Name]}); + false -> + %% initial start + ok + end, + {ok,#{id=>Name,type=>Type,file_ctrl_pid=>FileCtrlPid}}; Error -> Error end. @@ -316,7 +322,7 @@ enter_loop(#{wait_for_buffer:=true}=Config,State) -> enter_loop(_Config,State) -> gen_server:enter_loop(?MODULE,[],State). -%% This is the synchronous log request. +%% This is the synchronous log event. handle_call({log, Bin}, _From, State) -> {Result,State1} = do_log(Bin, call, State), %% Result == ok | dropped @@ -371,15 +377,19 @@ handle_call(reset, _From, State) -> handle_call(stop, _From, State) -> {stop, {shutdown,stopped}, ok, State}. -%% This is the asynchronous log request. +%% This is the asynchronous log event. handle_cast({log, Bin}, State) -> {_,State1} = do_log(Bin, cast, State), {noreply, State1}; +handle_cast({log_handler_info, Format, Args}, State = #{id:=Name}) -> + log_handler_info(Name, Format, Args, State), + {noreply, State}; + %% If FILESYNC_REPEAT_INTERVAL is set to a millisec value, this %% clause gets called repeatedly by the handler. In order to %% guarantee that a filesync *always* happens after the last log -%% request, the repeat operation must be active! +%% event, the repeat operation must be active! handle_cast(repeated_filesync, State = #{type := Type, file_ctrl_pid := FileCtrlPid, @@ -514,21 +524,31 @@ stop(Name) -> if C == 0 -> Interval; true -> C-1 end). -%% check for overload between every request (and set Mode to async, +%% check for overload between every event (and set Mode to async, %% sync or drop accordingly), but never flush the whole mailbox -%% before LogWindowSize requests have been handled -do_log(Bin, CallOrCast, State = #{id:=Name}) -> +%% before LogWindowSize events have been handled +do_log(Bin, CallOrCast, State = #{id:=Name, mode:=Mode0}) -> T1 = ?timestamp(), %% check if the handler is getting overloaded, or if it's %% recovering from overload (the check must be done for each - %% request to react quickly to large bursts of requests and + %% event to react quickly to large bursts of events and %% to ensure that the handler can never end up in drop mode %% with an empty mailbox, which would stop operation) {Mode1,QLen,Mem,State1} = logger_h_common:check_load(State), + if (Mode1 == drop) andalso (Mode0 =/= drop) -> + log_handler_info(Name, "Handler ~p switched to drop mode", + [Name], State); + (Mode0 == drop) andalso ((Mode1 == async) orelse (Mode1 == sync)) -> + log_handler_info(Name, "Handler ~p switched to ~w mode", + [Name,Mode1], State); + true -> + ok + end, + %% kill the handler if it can't keep up with the load - logger_h_common:kill_if_choked(Name, QLen, Mem, State), + logger_h_common:kill_if_choked(Name, QLen, Mem, ?MODULE, State), if Mode1 == flush -> flush(Name, QLen, T1, State1); @@ -538,22 +558,26 @@ do_log(Bin, CallOrCast, State = #{id:=Name}) -> %% this clause is called by do_log/3 after an overload check %% has been performed, where QLen > FlushQLen -flush(_Name, _QLen0, T1, State=#{last_log_ts := _T0, mode_tab := ModeTab}) -> +flush(Name, _QLen0, T1, State=#{last_log_ts := _T0, mode_tab := ModeTab}) -> %% flush messages in the mailbox (a limited number in %% order to not cause long delays) - _NewFlushed = logger_h_common:flush_log_requests(?FLUSH_MAX_N), + NewFlushed = logger_h_common:flush_log_events(?FLUSH_MAX_N), + + %% write info in log about flushed messages + log_handler_info(Name, "Handler ~p flushed ~w log events", + [Name,NewFlushed], State), %% because of the receive loop when flushing messages, the %% handler will be scheduled out often and the mailbox could %% grow very large, so we'd better check the queue again here {_,_QLen1} = process_info(self(), message_queue_len), - ?observe(_Name,{max_qlen,_QLen1}), + ?observe(Name,{max_qlen,_QLen1}), - %% Add 1 for the current log request - ?observe(_Name,{flushed,_NewFlushed+1}), + %% Add 1 for the current log event + ?observe(Name,{flushed,NewFlushed+1}), State1 = ?update_max_time(?diff_time(T1,_T0),State), - {dropped,?update_other(flushed,FLUSHED,_NewFlushed, + {dropped,?update_other(flushed,FLUSHED,NewFlushed, State1#{mode => ?set_mode(ModeTab,async), last_qlen => 0, last_log_ts => T1})}. @@ -567,10 +591,10 @@ write(_Name, Mode, T1, Bin, _CallOrCast, last_log_ts := T0, file_ctrl_sync_int := FileCtrlSyncInt}) -> %% check if we need to limit the number of writes - %% during a burst of log requests + %% during a burst of log events {DoWrite,BurstWinT,BurstMsgCount} = logger_h_common:limit_burst(State), - %% only send a synhrounous request to the file controller process + %% only send a synhrounous event to the file controller process %% every FileCtrlSyncInt time, to give the handler time between %% file writes so it can keep up with incoming messages {Result,LastQLen1} = @@ -587,13 +611,13 @@ write(_Name, Mode, T1, Bin, _CallOrCast, {dropped,LastQLen} end, - %% Check if the time since the previous log request is long enough - + %% Check if the time since the previous log event is long enough - %% and the queue length small enough - to assume the mailbox has %% been emptied, and if so, do filesync operation and reset mode to %% async. Note that this is the best we can do to detect an idle %% handler without setting a timer after each log call/cast. If the - %% time between two consecutive log requests is fast and no new - %% request comes in after the last one, idle state won't be detected! + %% time between two consecutive log events is fast and no new + %% event comes in after the last one, idle state won't be detected! Time = ?diff_time(T1,T0), {Mode1,BurstMsgCount1} = if (LastQLen1 < ?FILESYNC_OK_QLEN) andalso @@ -654,6 +678,20 @@ close_log_file(Fd) -> _ = file:datasync(Fd), _ = file:close(Fd). + +log_handler_info(Name, Format, Args, #{file_ctrl_pid := FileCtrlPid}) -> + Config = + case logger:get_handler_config(Name) of + {ok,Conf} -> Conf; + _ -> #{formatter=>{?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}} + end, + Meta = #{time=>erlang:system_time(microsecond)}, + Bin = logger_h_common:log_to_binary(#{level => notice, + msg => {Format,Args}, + meta => Meta}, Config), + _ = file_write_async(FileCtrlPid, Bin), + ok. + %%%----------------------------------------------------------------- %%% File control process @@ -726,7 +764,7 @@ file_ctrl_init(HandlerName, StdDev, Starter) -> file_ctrl_loop(Fd, Type, DevName, Synced, PrevWriteResult, PrevSyncResult, HandlerName) -> receive - %% asynchronous request + %% asynchronous event {log,Bin} -> Result = if Type == file -> write_to_dev(Fd, Bin, DevName, @@ -737,7 +775,7 @@ file_ctrl_loop(Fd, Type, DevName, Synced, file_ctrl_loop(Fd, Type, DevName, false, Result, PrevSyncResult, HandlerName); - %% synchronous request + %% synchronous event {{log,From,Bin,FileSync},MRef} -> if Type == file -> %% check that file hasn't been deleted diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 8fefbd1426..2b3551c3f7 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -856,7 +856,7 @@ op_switch_to_sync(Config) -> ok = logger:set_handler_config(?MODULE, NewHConfig), send_burst({n,NumOfReqs}, seq, {chars,79}, info), Lines = count_lines(Log), - ok = file:delete(Log), + ok = file_delete(Log), NumOfReqs = Lines, ok. op_switch_to_sync(cleanup, _Config) -> @@ -887,7 +887,7 @@ op_switch_to_drop(Config) -> _ <- lists:seq(1, Bursts)], Logged = count_lines(Log), ok = stop_handler(?MODULE), - _ = file:delete(Log), + _ = file_delete(Log), ct:pal("Number of messages dropped = ~w (~w)", [Procs*NumOfReqs*Bursts-Logged,Procs*NumOfReqs*Bursts]), true = (Logged < (Procs*NumOfReqs*Bursts)), @@ -939,7 +939,7 @@ op_switch_to_flush(Config) -> _ <- lists:seq(1,Bursts)], Logged = count_lines(Log), ok= stop_handler(?MODULE), - _ = file:delete(Log), + _ = file_delete(Log), ct:pal("Number of messages flushed/dropped = ~w (~w)", [NumOfReqs*Procs*Bursts-Logged,NumOfReqs*Procs*Bursts]), true = (Logged < (NumOfReqs*Procs*Bursts)), @@ -971,7 +971,7 @@ limit_burst_disabled(Config) -> send_burst({n,NumOfReqs}, seq, {chars,79}, info), Logged = count_lines(Log), ct:pal("Number of messages logged = ~w", [Logged]), - ok = file:delete(Log), + ok = file_delete(Log), NumOfReqs = Logged. limit_burst_disabled(cleanup, _Config) -> ok = stop_handler(?MODULE). @@ -990,7 +990,7 @@ limit_burst_enabled_one(Config) -> send_burst({n,NumOfReqs}, seq, {chars,79}, info), Logged = count_lines(Log), ct:pal("Number of messages logged = ~w", [Logged]), - ok = file:delete(Log), + ok = file_delete(Log), ReqLimit = Logged. limit_burst_enabled_one(cleanup, _Config) -> ok = stop_handler(?MODULE). @@ -1012,7 +1012,7 @@ limit_burst_enabled_period(Config) -> Logged = count_lines(Log), ct:pal("Number of messages sent = ~w~nNumber of messages logged = ~w", [Sent,Logged]), - ok = file:delete(Log), + ok = file_delete(Log), true = (Logged > (ReqLimit*Windows)) andalso (Logged < (ReqLimit*(Windows+2))). limit_burst_enabled_period(cleanup, _Config) -> @@ -1029,7 +1029,7 @@ kill_disabled(Config) -> send_burst({n,NumOfReqs}, seq, {chars,79}, info), Logged = count_lines(Log), ct:pal("Number of messages logged = ~w", [Logged]), - ok = file:delete(Log), + ok = file_delete(Log), true = is_pid(whereis(h_proc_name())), ok. kill_disabled(cleanup, _Config) -> @@ -1187,7 +1187,7 @@ handler_requests_under_load(Config) -> Errors = [{Req,FindError(Res)} || {Req,Res} <- ReqResult], 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). + ok = file_delete(Log). handler_requests_under_load(cleanup, _Config) -> ok = stop_handler(?MODULE). @@ -1301,9 +1301,11 @@ format(#{msg:={report,#{label:={gen_server,terminate}}}},op) -> ""; format(#{msg:={report,#{label:={proc_lib,crash}}}},op) -> ""; -format(#{msg:={F,A}},Pid) when is_list(F), is_list(A) -> +format(#{msg:={F,A}},OpOrPid) when is_list(F), is_list(A) -> String = lists:flatten(io_lib:format(F,A)), - Pid ! {log,String}, + if is_pid(OpOrPid) -> OpOrPid ! {log,String}; + true -> ok + end, String++"\n"; format(#{msg:={string,String0}},Pid) -> String = unicode:characters_to_list(String0), @@ -1402,17 +1404,21 @@ wait_until_written(File, Sz) -> end. count_lines1(File) -> - Counter = fun Cnt(Dev,LC) -> - case file:read_line(Dev) of - eof -> LC; - _ -> Cnt(Dev,LC+1) - end - end, {_,Dev} = file:open(File, [read]), - Lines = Counter(Dev, 0), + Lines = count_lines2(Dev, 0), file:close(Dev), Lines. +count_lines2(Dev, LC) -> + case file:read_line(Dev) of + {ok,"Handler logger_disk_log_h_SUITE " ++_} -> + %% Not counting handler info + count_lines2(Dev,LC); + {ok,_} -> + count_lines2(Dev,LC+1); + eof -> LC + end. + repeat_until_ok(Fun, N) -> repeat_until_ok(Fun, 0, N, undefined). @@ -1500,3 +1506,6 @@ h_proc_name() -> h_proc_name(?MODULE). h_proc_name(Name) -> list_to_atom(lists:concat([logger_disk_log_h,"_",Name])). + +file_delete(Log) -> + file:delete(Log). diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index fd1840b9ff..a67b55eee1 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -707,7 +707,7 @@ op_switch_to_sync_file(Config) -> %% TRecvPid = start_op_trace(), send_burst({n,NumOfReqs}, seq, {chars,79}, info), Lines = count_lines(Log), - ok = file:delete(Log), + ok = file_delete(Log), %% true = analyse_trace(TRecvPid, %% fun(Events) -> find_mode(async,Events) end), %% true = analyse_trace(TRecvPid, @@ -764,7 +764,7 @@ op_switch_to_drop_file(Config) -> _ <- lists:seq(1, Bursts)], Logged = count_lines(Log), ok = stop_handler(?MODULE), - _ = file:delete(Log), + _ = file_delete(Log), ct:pal("Number of messages dropped = ~w (~w)", [Procs*NumOfReqs*Bursts-Logged,Procs*NumOfReqs*Bursts]), true = (Logged < (Procs*NumOfReqs*Bursts)), @@ -799,7 +799,7 @@ op_switch_to_drop_tty(cleanup, _Config) -> ok = stop_handler(?MODULE). op_switch_to_flush_file() -> - [{timetrap,{minutes,3}}]. + [{timetrap,{minutes,5}}]. op_switch_to_flush_file(Config) -> Test = fun() -> @@ -832,7 +832,7 @@ op_switch_to_flush_file(Config) -> _ <- lists:seq(1,Bursts)], Logged = count_lines(Log), ok = stop_handler(?MODULE), - _ = file:delete(Log), + _ = file_delete(Log), ct:pal("Number of messages flushed/dropped = ~w (~w)", [NumOfReqs*Procs*Bursts-Logged,NumOfReqs*Procs*Bursts]), true = (Logged < (NumOfReqs*Procs*Bursts)), @@ -850,6 +850,8 @@ op_switch_to_flush_file(Config) -> op_switch_to_flush_file(cleanup, _Config) -> _ = stop_handler(?MODULE). +op_switch_to_flush_tty() -> + [{timetrap,{minutes,5}}]. op_switch_to_flush_tty(Config) -> {HConfig,StdHConfig} = start_handler(?MODULE, standard_io, Config), @@ -883,7 +885,7 @@ limit_burst_disabled(Config) -> send_burst({n,NumOfReqs}, seq, {chars,79}, info), Logged = count_lines(Log), ct:pal("Number of messages logged = ~w", [Logged]), - ok = file:delete(Log), + ok = file_delete(Log), NumOfReqs = Logged. limit_burst_disabled(cleanup, _Config) -> ok = stop_handler(?MODULE). @@ -902,7 +904,7 @@ limit_burst_enabled_one(Config) -> send_burst({n,NumOfReqs}, seq, {chars,79}, info), Logged = count_lines(Log), ct:pal("Number of messages logged = ~w", [Logged]), - ok = file:delete(Log), + ok = file_delete(Log), ReqLimit = Logged. limit_burst_enabled_one(cleanup, _Config) -> ok = stop_handler(?MODULE). @@ -924,7 +926,7 @@ limit_burst_enabled_period(Config) -> Logged = count_lines(Log), ct:pal("Number of messages sent = ~w~nNumber of messages logged = ~w", [Sent,Logged]), - ok = file:delete(Log), + ok = file_delete(Log), true = (Logged > (ReqLimit*Windows)) andalso (Logged < (ReqLimit*(Windows+2))). limit_burst_enabled_period(cleanup, _Config) -> @@ -941,7 +943,7 @@ kill_disabled(Config) -> send_burst({n,NumOfReqs}, seq, {chars,79}, info), Logged = count_lines(Log), ct:pal("Number of messages logged = ~w", [Logged]), - ok = file:delete(Log), + ok = file_delete(Log), true = is_pid(whereis(h_proc_name())), ok. kill_disabled(cleanup, _Config) -> @@ -1117,7 +1119,7 @@ handler_requests_under_load(Config) -> Errors = [{Req,FindError(Res)} || {Req,Res} <- ReqResult], 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). + ok = file_delete(Log). handler_requests_under_load(cleanup, _Config) -> ok = stop_handler(?MODULE). @@ -1192,17 +1194,21 @@ wait_until_written(File, Sz) -> end. count_lines1(File) -> - Counter = fun Cnt(Dev,LC) -> - case file:read_line(Dev) of - eof -> LC; - _ -> Cnt(Dev,LC+1) - end - end, {_,Dev} = file:open(File, [read]), - Lines = Counter(Dev, 0), + Lines = count_lines2(Dev, 0), file:close(Dev), Lines. +count_lines2(Dev, LC) -> + case file:read_line(Dev) of + {ok,"Handler logger_std_h_SUITE " ++_} -> + %% Not counting handler info + count_lines2(Dev,LC); + {ok,_} -> + count_lines2(Dev,LC+1); + eof -> LC + end. + send_burst(NorT, Type, {chars,Sz}, Class) -> Text = [34 + rand:uniform(126-34) || _ <- lists:seq(1,Sz)], case NorT of @@ -1271,9 +1277,11 @@ format(#{msg:={report,#{label:={gen_server,terminate}}}},op) -> ""; format(#{msg:={report,#{label:={proc_lib,crash}}}},op) -> ""; -format(#{msg:={F,A}},Pid) when is_list(F), is_list(A) -> +format(#{msg:={F,A}},OpOrPid) when is_list(F), is_list(A) -> String = lists:flatten(io_lib:format(F,A)), - Pid ! {log,String}, + if is_pid(OpOrPid) -> OpOrPid ! {log,String}; + true -> ok + end, String++"\n"; format(#{msg:={string,String0}},Pid) -> String = unicode:characters_to_list(String0), @@ -1530,3 +1538,6 @@ h_proc_name() -> h_proc_name(?MODULE). h_proc_name(Name) -> ?name_to_reg_name(logger_std_h,Name). + +file_delete(Log) -> + file:delete(Log). -- cgit v1.2.3 From c026109009942d7a877242f161860cc824223cf1 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 7 Jun 2018 16:23:19 +0200 Subject: [logger] Change default primary log level to 'notice' Log events issued via error_logger:info_msg or error_logger:info_report are now forwarded to Logger with level 'notice' instead of 'info'. Log events issued by gen_* behaviours are also changed from level 'info' to level 'notice'. Progress reports are still 'info', and can therefore easily be included/excluded by changing the primary log level. By default, they are not logged. --- lib/kernel/src/application_controller.erl | 14 +-- lib/kernel/src/error_logger.erl | 6 +- lib/kernel/src/kernel.app.src | 5 +- lib/kernel/src/logger.erl | 17 +-- lib/kernel/src/logger_formatter.erl | 26 ++++- lib/kernel/src/logger_server.erl | 2 +- lib/kernel/test/logger_SUITE.erl | 154 ++++++++++++++-------------- lib/kernel/test/logger_disk_log_h_SUITE.erl | 91 ++++++++-------- lib/kernel/test/logger_env_var_SUITE.erl | 121 +++++++++++----------- lib/kernel/test/logger_formatter_SUITE.erl | 62 +++++++++-- lib/kernel/test/logger_legacy_SUITE.erl | 13 ++- lib/kernel/test/logger_simple_h_SUITE.erl | 16 +-- lib/kernel/test/logger_std_h_SUITE.erl | 74 ++++++------- 13 files changed, 330 insertions(+), 271 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/application_controller.erl b/lib/kernel/src/application_controller.erl index d5a632ef6f..36479f4be6 100644 --- a/lib/kernel/src/application_controller.erl +++ b/lib/kernel/src/application_controller.erl @@ -1920,13 +1920,13 @@ info_started(Name, Node) -> error_logger=>#{tag=>info_report,type=>progress}}). info_exited(Name, Reason, Type) -> - ?LOG_INFO(#{label=>{application_controller,exit}, - report=>[{application, Name}, - {exited, Reason}, - {type, Type}]}, - #{domain=>[otp], - report_cb=>fun logger:format_otp_report/1, - error_logger=>#{tag=>info_report,type=>std_info}}). + ?LOG_NOTICE(#{label=>{application_controller,exit}, + report=>[{application, Name}, + {exited, Reason}, + {type, Type}]}, + #{domain=>[otp], + report_cb=>fun logger:format_otp_report/1, + error_logger=>#{tag=>info_report,type=>std_info}}). %%----------------------------------------------------------------- %% Reply to all processes waiting this application to be started. diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index ef930317b1..a0bf87796b 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -317,7 +317,7 @@ info_report(Report) -> Report :: report(). info_report(Type, Report) -> - logger:log(info, + logger:log(notice, #{label=>{?MODULE,info_report}, report=>Report}, meta(info_report,Type)). @@ -338,7 +338,7 @@ info_msg(Format) -> Data :: list(). info_msg(Format, Args) -> - logger:log(info, + logger:log(notice, #{label=>{?MODULE,info_msg}, format=>Format, args=>Args}, @@ -358,7 +358,7 @@ error_info(Error) -> false -> {"~p",[Error]} end, MyMeta = #{tag=>info,type=>Error}, - logger:log(info, Format, Args, #{?MODULE=>MyMeta,domain=>[Error]}). + logger:log(notice, Format, Args, #{?MODULE=>MyMeta,domain=>[Error]}). %%----------------------------------------------------------------- %% Create metadata diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index d873178f55..390f1fa42a 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -140,9 +140,8 @@ inet_db, pg2]}, {applications, []}, - {env, [{logger_level, info}, - {logger_sasl_compatible, false}, - {logger_progress_reports, stop} + {env, [{logger_level, notice}, + {logger_sasl_compatible, false} ]}, {mod, {kernel, []}}, {runtime_dependencies, ["erts-10.0", "stdlib-3.5", "sasl-3.0"]} diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index f6dc8ea397..e0832bf31c 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -710,15 +710,7 @@ get_default_handler_filters() -> true -> ?DEFAULT_HANDLER_FILTERS([otp]); false -> - Extra = - case application:get_env(kernel, logger_progress_reports, stop) of - log -> - []; - stop -> - [{stop_progress, - {fun logger_filters:progress/2,stop}}] - end, - Extra ++ ?DEFAULT_HANDLER_FILTERS([otp,sasl]) + ?DEFAULT_HANDLER_FILTERS([otp,sasl]) end. get_logger_env() -> @@ -726,17 +718,14 @@ get_logger_env() -> %%%----------------------------------------------------------------- %%% Internal -do_log(Level,Msg,Meta) -> - do_log_1(Level,Msg,Meta). - -do_log_1(Level,Msg,#{mfa:={Module,_,_}}=Meta) -> +do_log(Level,Msg,#{mfa:={Module,_,_}}=Meta) -> case logger_config:allow(?LOGGER_TABLE,Level,Module) of true -> log_allowed(#{},Level,Msg,Meta); false -> ok end; -do_log_1(Level,Msg,Meta) -> +do_log(Level,Msg,Meta) -> case logger_config:allow(?LOGGER_TABLE,Level) of true -> log_allowed(#{},Level,Msg,Meta); diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl index 6eeac994ea..4ebd88ac2d 100644 --- a/lib/kernel/src/logger_formatter.erl +++ b/lib/kernel/src/logger_formatter.erl @@ -234,7 +234,7 @@ format_mfa(MFA) -> maybe_add_legacy_header(Level, #{time:=Timestamp}=Meta, #{legacy_header:=true}=Config) -> - #{title:=Title}=MyMeta = add_legacy_title(Level,maps:get(?MODULE,Meta,#{})), + #{title:=Title}=MyMeta = add_legacy_title(Level,Meta,Config), {{Y,Mo,D},{H,Mi,S},Micro,UtcStr} = timestamp_to_datetimemicro(Timestamp,Config), Header = @@ -244,11 +244,23 @@ maybe_add_legacy_header(Level, maybe_add_legacy_header(_,Meta,_) -> Meta. -add_legacy_title(_Level,#{title:=_}=MyMeta) -> +add_legacy_title(_Level,#{?MODULE:=#{title:=_}=MyMeta},_) -> MyMeta; -add_legacy_title(Level,MyMeta) -> - Title = string:uppercase(atom_to_list(Level)) ++ " REPORT", - MyMeta#{title=>Title}. +add_legacy_title(Level,Meta,Config) -> + case maps:get(?MODULE,Meta,#{}) of + #{title:=_}=MyMeta -> + MyMeta; + MyMeta -> + TitleLevel = + case (Level=:=notice andalso maps:find(error_logger,Meta)) of + {ok,_} -> + maps:get(error_logger_notice_header,Config); + _ -> + Level + end, + Title = string:uppercase(atom_to_list(TitleLevel)) ++ " REPORT", + MyMeta#{title=>Title} + end. month(1) -> "Jan"; month(2) -> "Feb"; @@ -268,6 +280,7 @@ month(12) -> "Dec". add_default_config(Config0) -> Default = #{legacy_header=>false, + error_logger_notice_header=>info, single_line=>true, chars_limit=>unlimited, time_designator=>$T}, @@ -352,6 +365,9 @@ do_check_config([{single_line,SL}|Config]) when is_boolean(SL) -> do_check_config(Config); do_check_config([{legacy_header,LH}|Config]) when is_boolean(LH) -> do_check_config(Config); +do_check_config([{error_logger_notice_header,ELNH}|Config]) when ELNH == info; + ELNH == notice -> + do_check_config(Config); do_check_config([{report_cb,RCB}|Config]) when is_function(RCB,1) -> do_check_config(Config); do_check_config([{template,T}|Config]) -> diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index 6d4ab0925d..5da8f71420 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -357,7 +357,7 @@ do_set_config(Tid,Id,Config) -> ok. default_config(primary) -> - #{level=>info, + #{level=>notice, filters=>[], filter_default=>log}; default_config(Id) -> diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index da3345524a..f837c31e64 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -116,19 +116,19 @@ add_remove_handler(_Config) -> logger:get_handler_config(h1), ok = logger:set_handler_config(h1,filter_default,stop), [changing_config] = test_server:messages_get(), - ?LOG_INFO("hello",[]), + ?LOG_NOTICE("hello",[]), ok = check_no_log(), ok = logger:set_handler_config(h1,filter_default,log), [changing_config] = test_server:messages_get(), {ok,#{filter_default:=log}} = logger:get_handler_config(h1), - ?LOG_INFO("hello",[]), - ok = check_logged(info,"hello",[],?MY_LOC(1)), + ?LOG_NOTICE("hello",[]), + ok = check_logged(notice,"hello",[],?MY_LOC(1)), ok = logger:remove_handler(h1), [remove] = test_server:messages_get(), Hs0 = logger:get_handler_config(), {error,{not_found,h1}} = logger:get_handler_config(h1), {error,{not_found,h1}} = logger:remove_handler(h1), - logger:info("hello",[]), + logger:notice("hello",[]), ok = check_no_log(), ok. @@ -137,13 +137,13 @@ add_remove_handler(cleanup,_Config) -> ok. multiple_handlers(_Config) -> - ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log}), + ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log}), ok = logger:add_handler(h2,?MODULE,#{level=>error,filter_default=>log}), ?LOG_ERROR("hello",[]), ok = check_logged(error,"hello",[],?MY_LOC(1)), ok = check_logged(error,"hello",[],?MY_LOC(2)), - ?LOG_INFO("hello",[]), - ok = check_logged(info,"hello",[],?MY_LOC(1)), + ?LOG_NOTICE("hello",[]), + ok = check_logged(notice,"hello",[],?MY_LOC(1)), ok = check_no_log(), ok. @@ -153,18 +153,18 @@ multiple_handlers(cleanup,_Config) -> ok. add_remove_filter(_Config) -> - ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log}), + ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log}), LF = {fun(Log,_) -> Log#{level=>error} end, []}, ok = logger:add_primary_filter(lf,LF), {error,{already_exist,lf}} = logger:add_primary_filter(lf,LF), {error,{already_exist,lf}} = logger:add_primary_filter(lf,{fun(Log,_) -> Log end, []}), - ?LOG_INFO("hello",[]), + ?LOG_NOTICE("hello",[]), ok = check_logged(error,"hello",[],?MY_LOC(1)), ok = check_no_log(), - ok = logger:add_handler(h2,?MODULE,#{level=>info,filter_default=>log}), + ok = logger:add_handler(h2,?MODULE,#{level=>notice,filter_default=>log}), HF = {fun(#{level:=error}=Log,_) -> Log#{level=>mylevel}; (_,_) -> @@ -176,16 +176,16 @@ add_remove_filter(_Config) -> {error,{already_exist,hf}} = logger:add_handler_filter(h1,hf,{fun(Log,_) -> Log end, []}), - ?LOG_INFO("hello",[]), + ?LOG_NOTICE("hello",[]), ok = check_logged(mylevel,"hello",[],?MY_LOC(1)), ok = check_logged(error,"hello",[],?MY_LOC(2)), ok = logger:remove_primary_filter(lf), {error,{not_found,lf}} = logger:remove_primary_filter(lf), - ?LOG_INFO("hello",[]), - ok = check_logged(info,"hello",[],?MY_LOC(1)), - ok = check_logged(info,"hello",[],?MY_LOC(2)), + ?LOG_NOTICE("hello",[]), + ok = check_logged(notice,"hello",[],?MY_LOC(1)), + ok = check_logged(notice,"hello",[],?MY_LOC(2)), ?LOG_ERROR("hello",[]), ok = check_logged(mylevel,"hello",[],?MY_LOC(1)), @@ -193,9 +193,9 @@ add_remove_filter(_Config) -> ok = logger:remove_handler_filter(h1,hf), {error,{not_found,hf}} = logger:remove_handler_filter(h1,hf), - ?LOG_INFO("hello",[]), - ok = check_logged(info,"hello",[],?MY_LOC(1)), - ok = check_logged(info,"hello",[],?MY_LOC(2)), + ?LOG_NOTICE("hello",[]), + ok = check_logged(notice,"hello",[],?MY_LOC(1)), + ok = check_logged(notice,"hello",[],?MY_LOC(2)), ?LOG_ERROR("hello",[]), ok = check_logged(error,"hello",[],?MY_LOC(1)), @@ -210,8 +210,8 @@ add_remove_filter(cleanup,_Config) -> change_config(_Config) -> %% Overwrite handler config - check that defaults are added - ok = logger:add_handler(h1,?MODULE,#{level=>info,custom=>custom}), - {ok,#{module:=?MODULE,level:=info,filter_default:=log,custom:=custom}} = + ok = logger:add_handler(h1,?MODULE,#{level=>notice,custom=>custom}), + {ok,#{module:=?MODULE,level:=notice,filter_default:=log,custom:=custom}} = logger:get_handler_config(h1), register(callback_receiver,self()), ok = logger:set_handler_config(h1,#{filter_default=>stop}), @@ -254,7 +254,7 @@ change_config(_Config) -> %% Overwrite primary config - check that defaults are added ok = logger:set_primary_config(#{filter_default=>stop}), - #{level:=info,filters:=[],filter_default:=stop}=PC1 = + #{level:=notice,filters:=[],filter_default:=stop}=PC1 = logger:get_primary_config(), 3 = maps:size(PC1), %% Check that internal 'handlers' field has not been changed @@ -277,9 +277,9 @@ change_config(cleanup,Config) -> set_formatter(_Config) -> {error,{not_found,h1}}=logger:set_handler_config(h1,formatter,{?MODULE,[]}), - ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log}), + ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log}), ok = logger:set_handler_config(h1,formatter,{?MODULE,[]}), - logger:info("hello",[]), + logger:notice("hello",[]), receive {_Log,#{formatter:={?MODULE,[]}}} -> ok @@ -349,7 +349,7 @@ log_all_levels_api(_Config) -> log_all_levels_api(cleanup,_Config) -> logger:remove_handler(h1), - logger:set_primary_config(level,info), + logger:set_primary_config(level,notice), ok. macros(_Config) -> @@ -366,8 +366,8 @@ set_level(_Config) -> ok = logger:add_handler(h1,?MODULE,#{level=>all,filter_default=>log}), logger:debug(?map_rep), ok = check_no_log(), - logger:info(M1=?map_rep), - ok = check_logged(info,M1,#{}), + logger:notice(M1=?map_rep), + ok = check_logged(notice,M1,#{}), ok = logger:set_primary_config(level,debug), logger:debug(M2=?map_rep), ok = check_logged(debug,M2,#{}), @@ -375,7 +375,7 @@ set_level(_Config) -> set_level(cleanup,_Config) -> logger:remove_handler(h1), - logger:set_primary_config(level,info), + logger:set_primary_config(level,notice), ok. set_module_level(_Config) -> @@ -383,7 +383,7 @@ set_module_level(_Config) -> [] = logger:get_module_level(?MODULE), [] = logger:get_module_level(), - ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log}), + ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log}), {error,{invalid_level,bad}} = logger:set_module_level(?MODULE,bad), {error,{not_a_list_of_modules,{bad}}} = logger:set_module_level({bad},warning), @@ -391,16 +391,16 @@ set_module_level(_Config) -> [{?MODULE,warning}] = logger:get_module_level([?MODULE,other]), [{?MODULE,warning}] = logger:get_module_level(?MODULE), [{?MODULE,warning}] = logger:get_module_level(), - logger:info(?map_rep,?MY_LOC(0)), + logger:notice(?map_rep,?MY_LOC(0)), ok = check_no_log(), logger:warning(M1=?map_rep,?MY_LOC(0)), ok = check_logged(warning,M1,?MY_LOC(1)), - ok = logger:set_module_level(?MODULE,info), - [{?MODULE,info}] = logger:get_module_level([?MODULE,other]), - [{?MODULE,info}] = logger:get_module_level(?MODULE), - [{?MODULE,info}] = logger:get_module_level(), - logger:info(M2=?map_rep,?MY_LOC(0)), - ok = check_logged(info,M2,?MY_LOC(1)), + ok = logger:set_module_level(?MODULE,notice), + [{?MODULE,notice}] = logger:get_module_level([?MODULE,other]), + [{?MODULE,notice}] = logger:get_module_level(?MODULE), + [{?MODULE,notice}] = logger:get_module_level(), + logger:notice(M2=?map_rep,?MY_LOC(0)), + ok = check_logged(notice,M2,?MY_LOC(1)), {error,{not_a_list_of_modules,{bad}}} = logger:unset_module_level({bad}), ok = logger:unset_module_level(?MODULE), @@ -408,10 +408,10 @@ set_module_level(_Config) -> [] = logger:get_module_level(?MODULE), [] = logger:get_module_level(), - ok = logger:set_module_level([m1,m2,m3],info), - [{m1,info},{m2,info},{m3,info}] = logger:get_module_level(), + ok = logger:set_module_level([m1,m2,m3],notice), + [{m1,notice},{m2,notice},{m3,notice}] = logger:get_module_level(), ok = logger:unset_module_level(m2), - [{m1,info},{m3,info}] = logger:get_module_level(), + [{m1,notice},{m3,notice}] = logger:get_module_level(), ok = logger:unset_module_level(), [] = logger:get_module_level(), @@ -425,7 +425,7 @@ set_module_level(cleanup,_Config) -> cache_module_level(_Config) -> ok = logger:unset_module_level(?MODULE), [] = ets:lookup(?LOGGER_TABLE,?MODULE), %dirty - add API in logger_config? - ?LOG_INFO(?map_rep), + ?LOG_NOTICE(?map_rep), %% Caching is done asynchronously, so wait a bit for the update timer:sleep(100), [_] = ets:lookup(?LOGGER_TABLE,?MODULE), %dirty - add API in logger_config? @@ -464,21 +464,21 @@ format_report(_Config) -> ok. filter_failed(_Config) -> - ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log}), + ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log}), %% Logger filters {error,{invalid_filter,_}} = logger:add_primary_filter(lf,{fun(_) -> ok end,args}), ok = logger:add_primary_filter(lf,{fun(_,_) -> a=b end,args}), #{filters:=[_]} = logger:get_primary_config(), - ok = logger:info(M1=?map_rep), - ok = check_logged(info,M1,#{}), + ok = logger:notice(M1=?map_rep), + ok = check_logged(notice,M1,#{}), {error,{not_found,lf}} = logger:remove_primary_filter(lf), ok = logger:add_primary_filter(lf,{fun(_,_) -> faulty_return end,args}), #{filters:=[_]} = logger:get_primary_config(), - ok = logger:info(M2=?map_rep), - ok = check_logged(info,M2,#{}), + ok = logger:notice(M2=?map_rep), + ok = check_logged(notice,M2,#{}), {error,{not_found,lf}} = logger:remove_primary_filter(lf), %% Handler filters @@ -489,14 +489,14 @@ filter_failed(_Config) -> logger:add_handler_filter(h1,hf,{fun(_) -> ok end,args}), ok = logger:add_handler_filter(h1,hf,{fun(_,_) -> a=b end,args}), {ok,#{filters:=[_]}} = logger:get_handler_config(h1), - ok = logger:info(M3=?map_rep), - ok = check_logged(info,M3,#{}), + ok = logger:notice(M3=?map_rep), + ok = check_logged(notice,M3,#{}), {error,{not_found,hf}} = logger:remove_handler_filter(h1,hf), ok = logger:add_handler_filter(h1,hf,{fun(_,_) -> faulty_return end,args}), {ok,#{filters:=[_]}} = logger:get_handler_config(h1), - ok = logger:info(M4=?map_rep), - ok = check_logged(info,M4,#{}), + ok = logger:notice(M4=?map_rep), + ok = check_logged(notice,M4,#{}), {error,{not_found,hf}} = logger:remove_handler_filter(h1,hf), ok. @@ -517,7 +517,7 @@ handler_failed(_Config) -> {error,{invalid_formatter,[]}} = logger:add_handler(h1,?MODULE,#{formatter=>[]}), {error,{invalid_handler,_}} = logger:add_handler(h1,nomodule,#{filter_default=>log}), - logger:info(?map_rep), + logger:notice(?map_rep), check_no_log(), H1 = logger:get_handler_config(), false = lists:search(fun(#{id:=h1}) -> true; (_) -> false end,H1), @@ -527,7 +527,7 @@ handler_failed(_Config) -> {error,{already_exist,h2}} = logger:add_handler(h2,othermodule,#{}), [add] = test_server:messages_get(), - logger:info(?map_rep), + logger:notice(?map_rep), [remove] = test_server:messages_get(), H2 = logger:get_handler_config(), false = lists:search(fun(#{id:=h2}) -> true; (_) -> false end,H2), @@ -710,30 +710,30 @@ config_sanity_check(cleanup,_Config) -> ok. log_failed(_Config) -> - ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log}), + ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log}), {error,function_clause} = ?TRY(logger:log(bad,?map_rep)), - {error,function_clause} = ?TRY(logger:log(info,?map_rep,bad)), - {error,function_clause} = ?TRY(logger:log(info,fun() -> ?map_rep end,bad)), - {error,function_clause} = ?TRY(logger:log(info,fun() -> ?map_rep end,bad,#{})), - {error,function_clause} = ?TRY(logger:log(info,bad,bad,bad)), - {error,function_clause} = ?TRY(logger:log(info,bad,bad,#{})), + {error,function_clause} = ?TRY(logger:log(notice,?map_rep,bad)), + {error,function_clause} = ?TRY(logger:log(notice,fun() -> ?map_rep end,bad)), + {error,function_clause} = ?TRY(logger:log(notice,fun() -> ?map_rep end,bad,#{})), + {error,function_clause} = ?TRY(logger:log(notice,bad,bad,bad)), + {error,function_clause} = ?TRY(logger:log(notice,bad,bad,#{})), check_no_log(), - ok = logger:log(info,M1=?str,#{}), - check_logged(info,M1,#{}), - ok = logger:log(info,M2=?map_rep,#{}), - check_logged(info,M2,#{}), - ok = logger:log(info,M3=?keyval_rep,#{}), - check_logged(info,M3,#{}), + ok = logger:log(notice,M1=?str,#{}), + check_logged(notice,M1,#{}), + ok = logger:log(notice,M2=?map_rep,#{}), + check_logged(notice,M2,#{}), + ok = logger:log(notice,M3=?keyval_rep,#{}), + check_logged(notice,M3,#{}), %% Should we check report input more thoroughly? - ok = logger:log(info,M4=?keyval_rep++[other,stuff,in,list],#{}), - check_logged(info,M4,#{}), + ok = logger:log(notice,M4=?keyval_rep++[other,stuff,in,list],#{}), + check_logged(notice,M4,#{}), %% This might break a handler since it is assumed to be a format %% string and args, so it depends how the handler protects itself %% against something like io_lib:format("ok","ok") - ok = logger:log(info,"ok","ok",#{}), - check_logged(info,"ok","ok",#{}), + ok = logger:log(notice,"ok","ok",#{}), + check_logged(notice,"ok","ok",#{}), ok. @@ -742,7 +742,7 @@ log_failed(cleanup,_Config) -> ok. emulator(_Config) -> - ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log, + ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log, tc_proc=>self()}), Msg = "Error in process ~p on node ~p with exit value:~n~p~n", Error = {badmatch,4}, @@ -761,7 +761,7 @@ generate_error(Error, Stack) -> erlang:raise(error, Error, Stack). via_logger_process(Config) -> - ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log, + ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log, tc_proc=>self()}), %% Explicitly send a message to the logger process @@ -798,7 +798,7 @@ via_logger_process(cleanup, Config) -> ok. other_node(_Config) -> - ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log, + ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log, tc_proc=>self()}), {ok,Node} = test_server:start_node(?FUNCTION_NAME,slave,[]), rpc:call(Node,logger,error,[Msg=?str,#{}]), @@ -815,8 +815,8 @@ compare_levels(_Config) -> Levels = [emergency,alert,critical,error,warning,notice,info,debug], ok = compare(Levels), {error,badarg} = ?TRY(logger:compare_levels(bad,bad)), - {error,badarg} = ?TRY(logger:compare_levels({bad},info)), - {error,badarg} = ?TRY(logger:compare_levels(info,"bad")), + {error,badarg} = ?TRY(logger:compare_levels({bad},notice)), + {error,badarg} = ?TRY(logger:compare_levels(notice,"bad")), ok. compare([L|Rest]) -> @@ -830,21 +830,21 @@ compare([]) -> process_metadata(_Config) -> undefined = logger:get_process_metadata(), {error,badarg} = ?TRY(logger:set_process_metadata(bad)), - ok = logger:add_handler(h1,?MODULE,#{level=>info,filter_default=>log}), + ok = logger:add_handler(h1,?MODULE,#{level=>notice,filter_default=>log}), Time = erlang:system_time(microsecond), ProcMeta = #{time=>Time,line=>0,custom=>proc}, ok = logger:set_process_metadata(ProcMeta), S1 = ?str, - ?LOG_INFO(S1,#{custom=>macro}), - check_logged(info,S1,#{time=>Time,line=>0,custom=>macro}), + ?LOG_NOTICE(S1,#{custom=>macro}), + check_logged(notice,S1,#{time=>Time,line=>0,custom=>macro}), Time2 = erlang:system_time(microsecond), S2 = ?str, - ?LOG_INFO(S2,#{time=>Time2,line=>1,custom=>macro}), - check_logged(info,S2,#{time=>Time2,line=>1,custom=>macro}), + ?LOG_NOTICE(S2,#{time=>Time2,line=>1,custom=>macro}), + check_logged(notice,S2,#{time=>Time2,line=>1,custom=>macro}), - logger:info(S3=?str,#{custom=>func}), - check_logged(info,S3,#{time=>Time,line=>0,custom=>func}), + logger:notice(S3=?str,#{custom=>func}), + check_logged(notice,S3,#{time=>Time,line=>0,custom=>func}), ProcMeta = logger:get_process_metadata(), ok = logger:update_process_metadata(#{custom=>changed,custom2=>added}), diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 2b3551c3f7..3f3b483b7d 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -123,7 +123,7 @@ create_log(Config) -> filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,self()}}, #{file=>LogFile1}), - logger:info("hello", ?domain), + logger:notice("hello", ?domain), logger_disk_log_h:sync(Name1), ct:pal("Checking contents of ~p", [?log_no(LogFile1,1)]), try_read_file(?log_no(LogFile1,1), {ok,<<"hello\n">>}, 5000), @@ -136,7 +136,7 @@ create_log(Config) -> filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,self()}}, #{file=>LogFile2}), - logger:info("dummy", ?domain), + logger:notice("dummy", ?domain), logger_disk_log_h:sync(Name2), ct:pal("Checking contents of ~p", [?log_no(LogFile2,1)]), try_read_file(?log_no(LogFile2,1), {ok,<<"dummy\n">>}, 5000), @@ -157,21 +157,21 @@ open_existing_log(Config) -> filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,self()}}, #{file=>LogFile1}), - logger:info("one", ?domain), + logger:notice("one", ?domain), logger_disk_log_h:sync(HName), ct:pal("Checking contents of ~p", [?log_no(LogFile1,1)]), try_read_file(?log_no(LogFile1,1), {ok,<<"one\n">>}, 5000), - logger:info("two", ?domain), + logger:notice("two", ?domain), ok = remove_and_stop(HName), try_read_file(?log_no(LogFile1,1), {ok,<<"one\ntwo\n">>}, 5000), - logger:info("two and a half", ?domain), + logger:notice("two and a half", ?domain), ok = start_and_add(HName, #{filter_default=>stop, filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,self()}}, #{file=>LogFile1}), - logger:info("three", ?domain), + logger:notice("three", ?domain), logger_disk_log_h:sync(HName), try_read_file(?log_no(LogFile1,1), {ok,<<"one\ntwo\nthree\n">>}, 5000), remove_and_stop(HName), @@ -196,22 +196,22 @@ disk_log_opts(Config) -> ct:log("Fullname = ~s", [WFileFull]), {WFileFull,wrap,{Size,2},1} = {Get(file,WInfo1),Get(type,WInfo1), Get(size,WInfo1),Get(current_file,WInfo1)}, - logger:info("123", ?domain), + logger:notice("123", ?domain), logger_disk_log_h:sync(WName), timer:sleep(500), 1 = Get(current_file, disk_log:info(WName)), - logger:info("45", ?domain), + logger:notice("45", ?domain), logger_disk_log_h:sync(WName), timer:sleep(500), 1 = Get(current_file, disk_log:info(WName)), - logger:info("6", ?domain), + logger:notice("6", ?domain), logger_disk_log_h:sync(WName), timer:sleep(500), 2 = Get(current_file, disk_log:info(WName)), - logger:info("7890", ?domain), + logger:notice("7890", ?domain), logger_disk_log_h:sync(WName), timer:sleep(500), 2 = Get(current_file, disk_log:info(WName)), @@ -229,7 +229,7 @@ disk_log_opts(Config) -> ct:log("Fullname = ~s", [HFile1Full]), {HFile1Full,halt,infinity} = {Get(file,HInfo1),Get(type,HInfo1), Get(size,HInfo1)}, - logger:info("12345", ?domain), + logger:notice("12345", ?domain), logger_disk_log_h:sync(HName1), timer:sleep(500), 1 = Get(no_written_items, disk_log:info(HName1)), @@ -260,10 +260,10 @@ default_formatter(Config) -> ok = logger:set_handler_config(?MODULE,formatter, {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}), LogName = lists:concat([LogFile, ".1"]), - logger:info("dummy"), + logger:notice("dummy"), wait_until_written(LogName), {ok,Bin} = file:read_file(LogName), - match = re:run(Bin, "=INFO REPORT====.*\ndummy", [{capture,none}]), + match = re:run(Bin, "=NOTICE REPORT====.*\ndummy", [{capture,none}]), ok. default_formatter(cleanup, _Config) -> logger:remove_handler(?MODULE). @@ -277,8 +277,8 @@ logging(Config) -> formatter=>{?MODULE,self()}}, #{file => LogFile}), MsgFormatter = fun(Term) -> {io_lib:format("Term:~p",[Term]),[]} end, - logger:info([{x,y}], #{report_cb => MsgFormatter}), - logger:info([{x,y}], #{}), + logger:notice([{x,y}], #{report_cb => MsgFormatter}), + logger:notice([{x,y}], #{}), ct:pal("Checking contents of ~p", [?log_no(LogFile,1)]), try_read_file(?log_no(LogFile,1), {ok,<<"Term:[{x,y}]\n x: y\n">>}, 5000). @@ -333,23 +333,23 @@ formatter_fail(Config) -> %% Formatter is added automatically {ok,#{formatter:={logger_formatter,_}}} = logger:get_handler_config(Name), - logger:info(M1=?msg,?domain), - Got1 = try_match_file(?log_no(LogFile,1),"[0-9\\+\\-T:\\.]* info: "++M1,5000), + logger:notice(M1=?msg,?domain), + Got1 = try_match_file(?log_no(LogFile,1),"[0-9\\+\\-T:\\.]* notice: "++M1,5000), ok = logger:set_handler_config(Name,formatter,{nonexistingmodule,#{}}), - logger:info(M2=?msg,?domain), + logger:notice(M2=?msg,?domain), Got2 = try_match_file(?log_no(LogFile,1), - escape(Got1)++"[0-9\\+\\-T:\\.]* info: FORMATTER CRASH: .*"++M2, + escape(Got1)++"[0-9\\+\\-T:\\.]* notice: FORMATTER CRASH: .*"++M2, 5000), ok = logger:set_handler_config(Name,formatter,{?MODULE,crash}), - logger:info(M3=?msg,?domain), + logger:notice(M3=?msg,?domain), Got3 = try_match_file(?log_no(LogFile,1), - escape(Got2)++"[0-9\\+\\-T:\\.]* info: FORMATTER CRASH: .*"++M3, + escape(Got2)++"[0-9\\+\\-T:\\.]* notice: FORMATTER CRASH: .*"++M3, 5000), ok = logger:set_handler_config(Name,formatter,{?MODULE,bad_return}), - logger:info(?msg,?domain), + logger:notice(?msg,?domain), try_match_file(?log_no(LogFile,1), escape(Got3)++"FORMATTER ERROR: bad_return_value", 5000), @@ -510,7 +510,7 @@ sync(Config) -> [{disk_log,blog,<<"first\n">>}, {disk_log,sync}]), - logger:info("first", ?domain), + logger:notice("first", ?domain), %% wait for automatic disk_log_sync check_tracer(?FILESYNC_REPEAT_INTERVAL*2), @@ -521,8 +521,8 @@ sync(Config) -> {disk_log,sync}]), %% two log requests in fast succession will make the handler skip %% an automatic disk log sync - logger:info("second", ?domain), - logger:info("third", ?domain), + logger:notice("second", ?domain), + logger:notice("third", ?domain), %% do explicit sync logger_disk_log_h:sync(?MODULE), check_tracer(100), @@ -540,9 +540,9 @@ sync(Config) -> {disk_log,blog,<<"fifth\n">>}, {disk_log,sync}]), - logger:info("fourth", ?domain), + logger:notice("fourth", ?domain), timer:sleep(?IDLE_DETECT_TIME_MSEC*2), - logger:info("fifth", ?domain), + logger:notice("fifth", ?domain), %% wait for automatic disk_log_sync check_tracer(?IDLE_DETECT_TIME_MSEC*2), @@ -603,7 +603,7 @@ disk_log_wrap(Config) -> %% fill first file lists:foreach(fun(N) -> Log = lists:concat([File,".",N]), - logger:info(Text, ?domain), + logger:notice(Text, ?domain), wait_until_written(Log), ct:pal("N = ~w", [N = Get(current_file, @@ -654,7 +654,7 @@ disk_log_full(Config) -> NoOfChars = 5, Text = [34 + rand:uniform(126-34) || _ <- lists:seq(1,NoOfChars)], - [logger:info(Text, ?domain) || _ <- lists:seq(1,trunc(MaxBytes/NoOfChars)+1)], + [logger:notice(Text, ?domain) || _ <- lists:seq(1,trunc(MaxBytes/NoOfChars)+1)], %% wait for trace messages timer:sleep(2000), @@ -822,7 +822,7 @@ start_h_on_new_node(Config, File) -> log_on_remote_node(Node,Msg) -> _ = spawn_link(Node, fun() -> erlang:group_leader(whereis(user),self()), - logger:info(Msg) + logger:notice(Msg) end), ok. @@ -854,7 +854,7 @@ op_switch_to_sync(Config) -> flush_reqs_qlen => 2*NumOfReqs, enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), - send_burst({n,NumOfReqs}, seq, {chars,79}, info), + send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Lines = count_lines(Log), ok = file_delete(Log), NumOfReqs = Lines, @@ -883,7 +883,7 @@ op_switch_to_drop(Config) -> %% the requests in a slow enough pace so that dropping %% never occurs. Therefore, lets generate a number of %% bursts to increase the chance of message buildup. - [send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info) || + [send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice) || _ <- lists:seq(1, Bursts)], Logged = count_lines(Log), ok = stop_handler(?MODULE), @@ -935,7 +935,7 @@ op_switch_to_flush(Config) -> %% sync messages gets tested). Therefore, lets %% generate a number of bursts to increase the chance %% of message buildup in some random fashion. - [send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info) || + [send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice) || _ <- lists:seq(1,Bursts)], Logged = count_lines(Log), ok= stop_handler(?MODULE), @@ -968,7 +968,7 @@ limit_burst_disabled(Config) -> flush_reqs_qlen => 300}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, - send_burst({n,NumOfReqs}, seq, {chars,79}, info), + send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Logged = count_lines(Log), ct:pal("Number of messages logged = ~w", [Logged]), ok = file_delete(Log), @@ -987,7 +987,7 @@ limit_burst_enabled_one(Config) -> flush_reqs_qlen => 300}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, - send_burst({n,NumOfReqs}, seq, {chars,79}, info), + send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Logged = count_lines(Log), ct:pal("Number of messages logged = ~w", [Logged]), ok = file_delete(Log), @@ -1008,7 +1008,7 @@ limit_burst_enabled_period(Config) -> ok = logger:set_handler_config(?MODULE, NewHConfig), Windows = 3, - Sent = send_burst({t,BurstTWin*Windows}, seq, {chars,79}, info), + Sent = send_burst({t,BurstTWin*Windows}, seq, {chars,79}, notice), Logged = count_lines(Log), ct:pal("Number of messages sent = ~w~nNumber of messages logged = ~w", [Sent,Logged]), @@ -1026,7 +1026,7 @@ kill_disabled(Config) -> handler_overloaded_mem=>100}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, - send_burst({n,NumOfReqs}, seq, {chars,79}, info), + send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Logged = count_lines(Log), ct:pal("Number of messages logged = ~w", [Logged]), ok = file_delete(Log), @@ -1050,8 +1050,8 @@ qlen_kill_new(Config) -> MRef = erlang:monitor(process, Pid0), NumOfReqs = 100, Procs = 2, - send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), - %% send_burst({n,NumOfReqs}, seq, {chars,79}, info), + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice), + %% send_burst({n,NumOfReqs}, seq, {chars,79}, notice), receive {'DOWN', MRef, _, _, Info} -> case Info of @@ -1087,8 +1087,8 @@ mem_kill_new(Config) -> MRef = erlang:monitor(process, Pid0), NumOfReqs = 100, Procs = 2, - send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), - %% send_burst({n,NumOfReqs}, seq, {chars,79}, info), + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice), + %% send_burst({n,NumOfReqs}, seq, {chars,79}, notice), receive {'DOWN', MRef, _, _, Info} -> case Info of @@ -1118,7 +1118,7 @@ restart_after(Config) -> ok = logger:set_handler_config(?MODULE, NewHConfig1), MRef1 = erlang:monitor(process, whereis(h_proc_name())), %% kill handler - send_burst({n,100}, {spawn,2,0}, {chars,79}, info), + send_burst({n,100}, {spawn,2,0}, {chars,79}, notice), receive {'DOWN', MRef1, _, _, _Info1} -> timer:sleep(?HANDLER_RESTART_AFTER + 1000), @@ -1139,7 +1139,7 @@ restart_after(Config) -> Pid0 = whereis(h_proc_name()), MRef2 = erlang:monitor(process, Pid0), %% kill handler - send_burst({n,100}, {spawn,2,0}, {chars,79}, info), + send_burst({n,100}, {spawn,2,0}, {chars,79}, notice), receive {'DOWN', MRef2, _, _, _Info2} -> timer:sleep(RestartAfter + 2000), @@ -1174,7 +1174,7 @@ handler_requests_under_load(Config) -> {change_config,[]}]) end), Procs = 100, - Sent = Procs * send_burst({n,5000}, {spawn,Procs,10}, {chars,79}, info), + Sent = Procs * send_burst({n,5000}, {spawn,Procs,10}, {chars,79}, notice), Pid ! {self(),finish}, ReqResult = receive {Pid,Result} -> Result end, Logged = count_lines(Log), @@ -1222,8 +1222,7 @@ start_handler(Name, FuncName, Config) -> max_no_bytes => 100000000}, filter_default=>log, filters=>?DEFAULT_HANDLER_FILTERS([Name]), - formatter=>{?MODULE,op}, - level => info}), + formatter=>{?MODULE,op}}), {ok,HConfig = #{config := DLHConfig}} = logger:get_handler_config(Name), {lists:concat([File,".1"]),HConfig,DLHConfig}. diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl index 329b4c4b88..4f052b456e 100644 --- a/lib/kernel/test/logger_env_var_SUITE.erl +++ b/lib/kernel/test/logger_env_var_SUITE.erl @@ -63,8 +63,7 @@ groups() -> ]}, {bad,[],[bad_error_logger, bad_level, - bad_sasl_compatibility, - bad_progress]}]. + bad_sasl_compatibility]}]. all() -> [default, @@ -78,54 +77,60 @@ all() -> ]. default(Config) -> - {ok,#{handlers:=Hs},_Node} = setup(Config,[]), + {ok,#{primary:=P,handlers:=Hs,module_levels:=ML},_Node} = setup(Config,[]), + notice = maps:get(level,P), #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), - true = lists:keymember(stop_progress,1,StdFilters), false = exists(simple,Hs), false = exists(sasl,Hs), + [] = ML, ok. default_sasl_compatible(Config) -> - {ok,#{handlers:=Hs},_Node} = setup(Config, - [{logger_sasl_compatible,true}]), + {ok,#{primary:=P,handlers:=Hs,module_levels:=ML},_Node} = + setup(Config,[{logger_sasl_compatible,true}]), + info = maps:get(level,P), #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,StdFilters), - false = lists:keymember(stop_progress,1,StdFilters), false = exists(simple,Hs), true = exists(sasl,Hs), + [] = ML, ok. error_logger_tty(Config) -> - {ok,#{handlers:=Hs},_Node} = setup(Config,[{error_logger,tty}]), + {ok,#{primary:=P,handlers:=Hs,module_levels:=ML},_Node} = + setup(Config,[{error_logger,tty}]), + notice = maps:get(level,P), #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), - true = lists:keymember(stop_progress,1,StdFilters), false = exists(simple,Hs), false = exists(sasl,Hs), + [] = ML, ok. error_logger_tty_sasl_compatible(Config) -> - {ok,#{handlers:=Hs},_Node} = setup(Config, - [{error_logger,tty}, - {logger_sasl_compatible,true}]), + {ok,#{primary:=P,handlers:=Hs,module_levels:=ML},_Node} = + setup(Config, + [{error_logger,tty}, + {logger_sasl_compatible,true}]), + info = maps:get(level,P), #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,StdFilters), - false = lists:keymember(stop_progress,1,StdFilters), false = exists(simple,Hs), true = exists(sasl,Hs), + [] = ML, ok. error_logger_false(Config) -> - {ok,#{handlers:=Hs,primary:=P},_Node} = + {ok,#{handlers:=Hs,primary:=P,module_levels:=ML},_Node} = setup(Config, [{error_logger,false}, {logger_level,notice}]), @@ -135,28 +140,27 @@ error_logger_false(Config) -> notice = maps:get(level,P), SimpleFilters = maps:get(filters,SimpleC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), - true = lists:keymember(stop_progress,1,SimpleFilters), false = exists(sasl,Hs), + [] = ML, ok. error_logger_false_progress(Config) -> - {ok,#{handlers:=Hs,primary:=P},_Node} = + {ok,#{handlers:=Hs,primary:=P,module_levels:=ML},_Node} = setup(Config, [{error_logger,false}, - {logger_level,notice}, - {logger_progress_reports,log}]), + {logger_level,notice}]), false = exists(?STANDARD_HANDLER,Hs), #{module:=logger_simple_h} = SimpleC = find(simple,Hs), all = maps:get(level,SimpleC), notice = maps:get(level,P), SimpleFilters = maps:get(filters,SimpleC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), - false = lists:keymember(stop_progress,1,SimpleFilters), false = exists(sasl,Hs), + [] = ML, ok. error_logger_false_sasl_compatible(Config) -> - {ok,#{handlers:=Hs,primary:=P},_Node} = + {ok,#{handlers:=Hs,primary:=P,module_levels:=ML},_Node} = setup(Config, [{error_logger,false}, {logger_level,notice}, @@ -164,11 +168,11 @@ error_logger_false_sasl_compatible(Config) -> false = exists(?STANDARD_HANDLER,Hs), #{module:=logger_simple_h} = SimpleC = find(simple,Hs), all = maps:get(level,SimpleC), - notice = maps:get(level,P), + info = maps:get(level,P), SimpleFilters = maps:get(filters,SimpleC), {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,SimpleFilters), - false = lists:keymember(stop_progress,1,SimpleFilters), true = exists(sasl,Hs), + [] = ML, ok. error_logger_silent(Config) -> @@ -201,7 +205,7 @@ error_logger_file(Config) -> logger_file(Config) -> Log = file(Config,?FUNCTION_NAME), - {ok,#{handlers:=Hs},Node} + {ok,#{primary:=P,handlers:=Hs,module_levels:=ML},Node} = setup(Config, [{logger, [{handler,?STANDARD_HANDLER,logger_std_h, @@ -214,15 +218,14 @@ logger_file(Config) -> all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), - true = lists:keymember(stop_progress,1,StdFilters), false = exists(simple,Hs), false = exists(sasl,Hs), - + [] = ML, ok. logger_file_sasl_compatible(Config) -> Log = file(Config,?FUNCTION_NAME), - {ok,#{handlers:=Hs},Node} + {ok,#{primary:=P,handlers:=Hs,module_levels:=ML},Node} = setup(Config, [{logger_sasl_compatible,true}, {logger, @@ -232,36 +235,37 @@ logger_file_sasl_compatible(Config) -> file,% dest 0),% progress in std logger + info = maps:get(level,P), #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp]}}} = lists:keyfind(domain,1,StdFilters), - false = lists:keymember(stop_progress,1,StdFilters), false = exists(simple,Hs), true = exists(sasl,Hs), - + [] = ML, ok. logger_file_log_progress(Config) -> Log = file(Config,?FUNCTION_NAME), - {ok,#{handlers:=Hs},Node} + {ok,#{primary:=P,handlers:=Hs,module_levels:=ML},Node} = setup(Config, - [{logger_progress_reports,log}, + [{logger_level,info}, {logger, [{handler,?STANDARD_HANDLER,logger_std_h, #{config=>#{type=>{file,Log}}}}]}]), check_default_log(Node,Log, file,% dest - 6),% progress in std logger + 6,% progress in std logger + info), + info = maps:get(level,P), #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), - false = lists:keymember(stop_progress,1,StdFilters), false = exists(simple,Hs), false = exists(sasl,Hs), - + [] = ML, ok. logger_file_no_filter(Config) -> @@ -330,7 +334,7 @@ logger_filters(Config) -> Log = file(Config,?FUNCTION_NAME), {ok,#{handlers:=Hs,primary:=P},Node} = setup(Config, - [{logger_progress_reports,log}, + [{logger_level,info}, {logger, [{handler,?STANDARD_HANDLER,logger_std_h, #{config=>#{type=>{file,Log}}}}, @@ -338,13 +342,13 @@ logger_filters(Config) -> ]}]), check_default_log(Node,Log, file,% dest - 0),% progress in std logger + 0,% progress in std logger + info), #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), - false = lists:keymember(stop_progress,1,StdFilters), false = exists(simple,Hs), false = exists(sasl,Hs), LoggerFilters = maps:get(filters,P), @@ -356,7 +360,7 @@ logger_filters_stop(Config) -> Log = file(Config,?FUNCTION_NAME), {ok,#{handlers:=Hs,primary:=P},Node} = setup(Config, - [{logger_progress_reports,log}, + [{logger_level,info}, {logger, [{handler,?STANDARD_HANDLER,logger_std_h, #{filters=>[], @@ -366,7 +370,7 @@ logger_filters_stop(Config) -> check_default_log(Node,Log, file,% dest 0,% progress in std logger - notice), + info), #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), @@ -382,7 +386,7 @@ logger_module_level(Config) -> Log = file(Config,?FUNCTION_NAME), {ok,#{handlers:=Hs,module_levels:=ModuleLevels},Node} = setup(Config, - [{logger_progress_reports,log}, + [{logger_level,info}, {logger, [{handler,?STANDARD_HANDLER,logger_std_h, #{config=>#{type=>{file,Log}}}}, @@ -390,13 +394,13 @@ logger_module_level(Config) -> ]}]), check_default_log(Node,Log, file,% dest - 3),% progress in std logger + 3,% progress in std logger + info), #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), - false = lists:keymember(stop_progress,1,StdFilters), false = exists(simple,Hs), false = exists(sasl,Hs), [{supervisor,error}] = ModuleLevels, @@ -417,7 +421,6 @@ logger_disk_log(Config) -> all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,StdFilters), - true = lists:keymember(stop_progress,1,StdFilters), false = exists(simple,Hs), false = exists(sasl,Hs), @@ -450,10 +453,9 @@ logger_undefined(Config) -> false = exists(?STANDARD_HANDLER,Hs), #{module:=logger_simple_h} = SimpleC = find(simple,Hs), all = maps:get(level,SimpleC), - info = maps:get(level,P), + notice = maps:get(level,P), SimpleFilters = maps:get(filters,SimpleC), {domain,{_,{log,super,[otp,sasl]}}} = lists:keyfind(domain,1,SimpleFilters), - true = lists:keymember(stop_progress,1,SimpleFilters), false = exists(sasl,Hs), ok. @@ -476,7 +478,8 @@ logger_many_handlers_default_first(Config) -> filters=>[{level,{fun logger_filters:level/2,{stop,gteq,error}}}], config=>#{type=>{file,LogInfo}}} } - ]}], LogErr, LogInfo, 6). + ]}, + {logger_level,info}], LogErr, LogInfo, 6). %% Test that we can add multiple handlers with the default last logger_many_handlers_default_last(Config) -> @@ -495,7 +498,8 @@ logger_many_handlers_default_last(Config) -> formatter=>{logger_formatter,#{}}, config=>#{type=>{file,LogErr}}} } - ]}], LogErr, LogInfo, 7). + ]}, + {logger_level,info}], LogErr, LogInfo, 7). %% Check that we can handle that an added logger has a broken filter %% This used to cause a deadlock. @@ -517,7 +521,8 @@ logger_many_handlers_default_last_broken_filter(Config) -> formatter=>{logger_formatter,#{}}, config=>#{type=>{file,LogErr}}} } - ]}], LogErr, LogInfo, 7). + ]}, + {logger_level,info}], LogErr, LogInfo, 7). logger_many_handlers(Config, Env, LogErr, LogInfo, NumProgress) -> {ok,#{handlers:=Hs},Node} = setup(Config,Env), @@ -528,7 +533,8 @@ logger_many_handlers(Config, Env, LogErr, LogInfo, NumProgress) -> ok = rpc:call(Node,logger_std_h,sync,[info]), {ok, Bin} = file:read_file(LogInfo), ct:log("Log content:~n~s",[Bin]), - match(Bin,<<"info:">>,NumProgress+1,info,info), + match(Bin,<<"info:">>,NumProgress,info,info), + match(Bin,<<"notice:">>,1,notice,info), match(Bin,<<"alert:">>,0,alert,info), ok. @@ -538,18 +544,18 @@ sasl_compatible_false(Config) -> {ok,_,Node} = setup(Config, [{error_logger,{file,Log}}, {logger_sasl_compatible,false}, - {logger_progress_reports,log}]), + {logger_level,info}]), % to get progress check_default_log(Node,Log, file,% dest - 6),% progress in std logger + 6,% progress in std logger + info), ok. sasl_compatible_false_no_progress(Config) -> Log = file(Config,?FUNCTION_NAME), {ok,_,Node} = setup(Config, [{error_logger,{file,Log}}, - {logger_sasl_compatible,false}, - {logger_progress_reports,stop}]), + {logger_sasl_compatible,false}]), check_default_log(Node,Log, file,% dest 0),% progress in std logger @@ -574,9 +580,6 @@ bad_level(Config) -> bad_sasl_compatibility(Config) -> error = setup(Config,[{logger_sasl_compatible,badcomp}]). -bad_progress(Config) -> - error = setup(Config,[{logger_progress_reports,badprogress}]). - %%%----------------------------------------------------------------- %%% Internal file(Config,Func) -> @@ -584,22 +587,22 @@ file(Config,Func) -> lists:concat([Func,".log"])). check_default_log(Node,Log,Dest,NumProgress) -> - check_default_log(Node,Log,Dest,NumProgress,info). + check_default_log(Node,Log,Dest,NumProgress,notice). 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,<<"INFO REPORT">>,0,notice,Level), match(Bin1,<<"DEBUG REPORT">>,0,debug,Level), - match(Bin2,<<"INFO REPORT">>,1,info,Level), + match(Bin2,<<"INFO REPORT">>,1,notice,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,notice). check_single_log(Node,Log,Dest,NumProgress,Level) -> {ok,Bin1,Bin2} = check_log(Node,Log,Dest), diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl index 00fa89271b..864a40b618 100644 --- a/lib/kernel/test/logger_formatter_SUITE.erl +++ b/lib/kernel/test/logger_formatter_SUITE.erl @@ -56,6 +56,7 @@ groups() -> all() -> [default, legacy_header, + error_logger_notice_header, single_line, template, format_msg, @@ -125,6 +126,34 @@ legacy_header(_Config) -> "=INFO REPORT==== "++_ = String5, ok. +error_logger_notice_header(_Config) -> + Meta1 = #{error_logger=>#{tag => info_report,type => std_info}}, + String1 = format(notice,{"~p",[term]},Meta1, + #{legacy_header=>true, + error_logger_notice_header=>notice}), + ct:log(String1), + "=NOTICE REPORT==== "++_ = String1, + + String2 = format(notice,{"~p",[term]},Meta1, + #{legacy_header=>true, + error_logger_notice_header=>info}), + ct:log(String2), + "=INFO REPORT==== "++_ = String2, + + String3 = format(notice,{"~p",[term]},#{}, + #{legacy_header=>true, + error_logger_notice_header=>notice}), + ct:log(String3), + "=NOTICE REPORT==== "++_ = String3, + + String4 = format(notice,{"~p",[term]},#{}, + #{legacy_header=>true, + error_logger_notice_header=>info}), + ct:log(String4), + "=NOTICE REPORT==== "++_ = String4, + + ok. + single_line(_Config) -> Time = timestamp(), ExpectedTimestamp = default_time_format(Time), @@ -581,6 +610,7 @@ check_config(_Config) -> C1 = #{chars_limit => 1, depth => 1, legacy_header => true, + error_logger_notice_header => info, max_size => 1, report_cb => fun(R) -> {"~p",[R]} end, single_line => false, @@ -601,6 +631,10 @@ check_config(_Config) -> ?cfgerr({legacy_header,bad}) = logger_formatter:check_config(#{legacy_header => bad}), + ok = logger_formatter:check_config(#{error_logger_notice_header => notice}), + ?cfgerr({error_logger_notice_header,bad}) = + logger_formatter:check_config(#{error_logger_notice_header => bad}), + ok = logger_formatter:check_config(#{max_size => unlimited}), ?cfgerr({max_size,bad}) = logger_formatter:check_config(#{max_size => bad}), @@ -664,17 +698,17 @@ update_config(_Config) -> logger:add_handler_filter(default,silence,{fun(_,_) -> stop end,ok}), ok = logger:add_handler(?MODULE,?MODULE,#{}), D = lists:seq(1,1000), - logger:info("~p~n",[D]), + logger:notice("~p~n",[D]), {Lines1,C1} = check_log(), [ct:log(L) || L <- Lines1], ct:log("~p",[C1]), [Line1] = Lines1, - [_Time,"info: "++D1] = string:split(Line1," "), + [_Time,"notice: "++D1] = string:split(Line1," "), true = length(D1)>3000, true = #{}==C1, ok = logger:update_formatter_config(?MODULE,single_line,false), - logger:info("~p~n",[D]), + logger:notice("~p~n",[D]), {Lines2,C2} = check_log(), [ct:log(L) || L <- Lines2], ct:log("~p",[C2]), @@ -682,23 +716,37 @@ update_config(_Config) -> true = #{single_line=>false}==C2, ok = logger:update_formatter_config(?MODULE,#{legacy_header=>true}), - logger:info("~p~n",[D]), + logger:notice("~p~n",[D]), {Lines3,C3} = check_log(), [ct:log(L) || L <- Lines3], ct:log("~p",[C3]), - ["=INFO REPORT==== "++_|D3] = Lines3, + ["=NOTICE REPORT==== "++_|D3] = Lines3, true = length(D3)>50, true = #{legacy_header=>true,single_line=>false}==C3, ok = logger:update_formatter_config(?MODULE,single_line,true), - logger:info("~p~n",[D]), + logger:notice("~p~n",[D]), {Lines4,C4} = check_log(), [ct:log(L) || L <- Lines4], ct:log("~p",[C4]), - ["=INFO REPORT==== "++_,D4] = Lines4, + ["=NOTICE REPORT==== "++_,D4] = Lines4, true = length(D4)>3000, true = #{legacy_header=>true,single_line=>true}==C4, + %% Finally, check that error_logger_notice_header works, default=info + error_logger:info_msg("~p",[D]), + {Lines5,C5} = check_log(), + [ct:log(L) || L <- Lines5], + ct:log("~p",[C5]), + ["=INFO REPORT==== "++_,_D5] = Lines5, + + ok=logger:update_formatter_config(?MODULE,error_logger_notice_header,notice), + error_logger:info_msg("~p",[D]), + {Lines6,C6} = check_log(), + [ct:log(L) || L <- Lines6], + ct:log("~p",[C6]), + ["=NOTICE REPORT==== "++_,_D6] = Lines6, + ok. update_config(cleanup,_Config) -> diff --git a/lib/kernel/test/logger_legacy_SUITE.erl b/lib/kernel/test/logger_legacy_SUITE.erl index 180db9f51a..de73b6152a 100644 --- a/lib/kernel/test/logger_legacy_SUITE.erl +++ b/lib/kernel/test/logger_legacy_SUITE.erl @@ -70,6 +70,10 @@ init_per_group(std, Config) -> [{domain,{fun logger_filters:domain/2,{log,super,[otp]}}}]), Config; init_per_group(sasl, Config) -> + %% Since default level is notice, and progress reports are info, + %% we need to raise the global logger level to info in order to + %% receive these. + ok = logger:set_primary_config(level,info), ok = logger:set_handler_config( error_logger,filters, [{domain,{fun logger_filters:domain/2,{log,super,[otp,sasl]}}}]), @@ -77,13 +81,14 @@ init_per_group(sasl, Config) -> %% cth_log_redirect checks if sasl is started before displaying %% any sasl reports - so just to see the real sasl reports in tc %% log: - application:start(sasl), - Config; + {ok,Apps} = application:ensure_all_started(sasl), + [{stop_apps,Apps}|Config]; init_per_group(_Group, Config) -> Config. -end_per_group(sasl, _Config) -> - application:stop(sasl), +end_per_group(sasl, Config) -> + Apps = ?config(stop_apps,Config), + [application:stop(App) || App <- Apps], ok; end_per_group(_Group, _Config) -> ok. diff --git a/lib/kernel/test/logger_simple_h_SUITE.erl b/lib/kernel/test/logger_simple_h_SUITE.erl index 9f34bbbe34..3a761a257a 100644 --- a/lib/kernel/test/logger_simple_h_SUITE.erl +++ b/lib/kernel/test/logger_simple_h_SUITE.erl @@ -128,8 +128,8 @@ replace_file(Config) -> 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, warning, [M4=?keyval_rep]), + log(Node, warning, [M41=?keyval_rep++[not_key_val]]), log(Node, critical, [M7=?str,[A7=?keyval_rep]]), log(Node, notice, [M8=["fake",string,"line:",?LINE]]), @@ -151,10 +151,10 @@ replace_file(Config) -> "=ERROR REPORT===="++_, _, _, - "=INFO REPORT===="++_, + "=WARNING REPORT===="++_, _, _, - "=INFO REPORT===="++_, + "=WARNING REPORT===="++_, _, _, _, @@ -172,8 +172,8 @@ replace_disk_log(Config) -> 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, warning, [M4=?keyval_rep]), + log(Node, warning, [M41=?keyval_rep++[not_key_val]]), log(Node, critical, [M7=?str,[A7=?keyval_rep]]), log(Node, notice, [M8=["fake",string,"line:",?LINE]]), @@ -194,10 +194,10 @@ replace_disk_log(Config) -> "=ERROR REPORT===="++_, _, _, - "=INFO REPORT===="++_, + "=WARNING REPORT===="++_, _, _, - "=INFO REPORT===="++_, + "=WARNING REPORT===="++_, _, _, _, diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index a67b55eee1..e71b75fe66 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -172,14 +172,14 @@ add_remove_instance_file(Log, Type) -> formatter=>{?MODULE,self()}}), Pid = whereis(h_proc_name()), true = is_pid(Pid), - logger:info(M1=?msg,?domain), + logger:notice(M1=?msg,?domain), ?check(M1), B1 = ?bin(M1), try_read_file(Log, {ok,B1}, ?FILESYNC_REP_INT), ok = logger:remove_handler(?MODULE), timer:sleep(500), undefined = whereis(h_proc_name()), - logger:info(?msg,?domain), + logger:notice(?msg,?domain), ?check_no_log, try_read_file(Log, {ok,B1}, ?FILESYNC_REP_INT), ok. @@ -188,11 +188,11 @@ default_formatter(_Config) -> ok = logger:set_handler_config(?STANDARD_HANDLER,formatter, {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}), ct:capture_start(), - logger:info(M1=?msg), + logger:notice(M1=?msg), timer:sleep(100), ct:capture_stop(), [Msg] = ct:capture_get(), - match = re:run(Msg,"=INFO REPORT====.*\n"++M1,[{capture,none}]), + match = re:run(Msg,"=NOTICE REPORT====.*\n"++M1,[{capture,none}]), ok. errors(Config) -> @@ -225,7 +225,7 @@ errors(Config) -> logger:add_handler(myh3,logger_std_h, #{config=>#{type=>{file,Log,[bad_file_opt]}}}), - ok = logger:info(?msg). + ok = logger:notice(?msg). errors(cleanup,_Config) -> logger:remove_handler(?MODULE). @@ -247,23 +247,23 @@ formatter_fail(Config) -> %% Formatter is added automatically {ok,#{formatter:={logger_formatter,_}}} = logger:get_handler_config(?MODULE), - logger:info(M1=?msg,?domain), - Got1 = try_match_file(Log,"[0-9\\+\\-T:\\.]* info: "++M1,5000), + logger:notice(M1=?msg,?domain), + Got1 = try_match_file(Log,"[0-9\\+\\-T:\\.]* notice: "++M1,5000), ok = logger:set_handler_config(?MODULE,formatter,{nonexistingmodule,#{}}), - logger:info(M2=?msg,?domain), + logger:notice(M2=?msg,?domain), Got2 = try_match_file(Log, - escape(Got1)++"[0-9\\+\\-T:\\.]* info: FORMATTER CRASH: .*"++M2, + escape(Got1)++"[0-9\\+\\-T:\\.]* notice: FORMATTER CRASH: .*"++M2, 5000), ok = logger:set_handler_config(?MODULE,formatter,{?MODULE,crash}), - logger:info(M3=?msg,?domain), + logger:notice(M3=?msg,?domain), Got3 = try_match_file(Log, - escape(Got2)++"[0-9\\+\\-T:\\.]* info: FORMATTER CRASH: .*"++M3, + escape(Got2)++"[0-9\\+\\-T:\\.]* notice: FORMATTER CRASH: .*"++M3, 5000), ok = logger:set_handler_config(?MODULE,formatter,{?MODULE,bad_return}), - logger:info(?msg,?domain), + logger:notice(?msg,?domain), try_match_file(Log, escape(Got3)++"FORMATTER ERROR: bad_return_value", 5000), @@ -383,7 +383,7 @@ crash_std_h(Config,Func,Var,Type,Log) -> log_on_remote_node(Node,Msg) -> _ = spawn_link(Node, fun() -> erlang:group_leader(whereis(user),self()), - logger:info(Msg) + logger:notice(Msg) end), ok. @@ -496,7 +496,7 @@ file_opts(Config) -> formatter=>{?MODULE,self()}}), #{type := OkType} = logger_std_h:info(?MODULE), - logger:info(M1=?msg,?domain), + logger:notice(M1=?msg,?domain), ?check(M1), B1 = ?bin(M1), try_read_file(Log, {ok,B1}, ?FILESYNC_REP_INT), @@ -524,7 +524,7 @@ sync(Config) -> {logger_std_h, sync_dev}, {file,datasync}]), - logger:info("first", ?domain), + logger:notice("first", ?domain), %% wait for automatic filesync check_tracer(?FILESYNC_REP_INT*2), @@ -537,7 +537,7 @@ sync(Config) -> {file,datasync}, {no_more,500} ]), - logger:info("second", ?domain), + logger:notice("second", ?domain), %% do explicit sync logger_std_h:sync(?MODULE), %% a second sync should be ignored @@ -562,10 +562,10 @@ sync(Config) -> {logger_std_h, write_to_dev, <<"fourth\n">>}, {logger_std_h, sync_dev}, {file,datasync}]), - logger:info("third", ?domain), + logger:notice("third", ?domain), %% wait for automatic filesync timer:sleep(?IDLE_DETECT_TIME_MSEC*2), - logger:info("fourth", ?domain), + logger:notice("fourth", ?domain), %% wait for automatic filesync check_tracer(?IDLE_DETECT_TIME_MSEC*2), @@ -705,7 +705,7 @@ op_switch_to_sync_file(Config) -> enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), %% TRecvPid = start_op_trace(), - send_burst({n,NumOfReqs}, seq, {chars,79}, info), + send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Lines = count_lines(Log), ok = file_delete(Log), %% true = analyse_trace(TRecvPid, @@ -733,7 +733,7 @@ op_switch_to_sync_tty(Config) -> flush_reqs_qlen => 2*NumOfReqs, enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), - send_burst({n,NumOfReqs}, seq, {chars,79}, info), + send_burst({n,NumOfReqs}, seq, {chars,79}, notice), ok. op_switch_to_sync_tty(cleanup, _Config) -> ok = stop_handler(?MODULE). @@ -760,7 +760,7 @@ op_switch_to_drop_file(Config) -> %% requests in a slow enough pace so that dropping %% never occurs. Therefore, lets generate a number of %% bursts to increase the chance of message buildup. - [send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info) || + [send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice) || _ <- lists:seq(1, Bursts)], Logged = count_lines(Log), ok = stop_handler(?MODULE), @@ -793,7 +793,7 @@ op_switch_to_drop_tty(Config) -> Procs*NumOfReqs+1, enable_burst_limit => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), - send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice), ok. op_switch_to_drop_tty(cleanup, _Config) -> ok = stop_handler(?MODULE). @@ -828,7 +828,7 @@ op_switch_to_flush_file(Config) -> %% sync messages gets tested). Therefore, lets %% generate a number of bursts to increase the chance %% of message buildup in some random fashion. - [send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info) || + [send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice) || _ <- lists:seq(1,Bursts)], Logged = count_lines(Log), ok = stop_handler(?MODULE), @@ -867,7 +867,7 @@ op_switch_to_flush_tty(Config) -> ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 1000, Procs = 100, - send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice), ok. op_switch_to_flush_tty(cleanup, _Config) -> ok = stop_handler(?MODULE). @@ -882,7 +882,7 @@ limit_burst_disabled(Config) -> flush_reqs_qlen => 300}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, - send_burst({n,NumOfReqs}, seq, {chars,79}, info), + send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Logged = count_lines(Log), ct:pal("Number of messages logged = ~w", [Logged]), ok = file_delete(Log), @@ -901,7 +901,7 @@ limit_burst_enabled_one(Config) -> flush_reqs_qlen => 300}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, - send_burst({n,NumOfReqs}, seq, {chars,79}, info), + send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Logged = count_lines(Log), ct:pal("Number of messages logged = ~w", [Logged]), ok = file_delete(Log), @@ -922,7 +922,7 @@ limit_burst_enabled_period(Config) -> ok = logger:set_handler_config(?MODULE, NewHConfig), Windows = 3, - Sent = send_burst({t,BurstTWin*Windows}, seq, {chars,79}, info), + Sent = send_burst({t,BurstTWin*Windows}, seq, {chars,79}, notice), Logged = count_lines(Log), ct:pal("Number of messages sent = ~w~nNumber of messages logged = ~w", [Sent,Logged]), @@ -940,7 +940,7 @@ kill_disabled(Config) -> handler_overloaded_mem=>100}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, - send_burst({n,NumOfReqs}, seq, {chars,79}, info), + send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Logged = count_lines(Log), ct:pal("Number of messages logged = ~w", [Logged]), ok = file_delete(Log), @@ -963,8 +963,8 @@ qlen_kill_new(Config) -> MRef = erlang:monitor(process, Pid0), NumOfReqs = 100, Procs = 2, - send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), - %% send_burst({n,NumOfReqs}, seq, {chars,79}, info), + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice), + %% send_burst({n,NumOfReqs}, seq, {chars,79}, notice), receive {'DOWN', MRef, _, _, Info} -> case Info of @@ -1014,8 +1014,8 @@ mem_kill_new(Config) -> MRef = erlang:monitor(process, Pid0), NumOfReqs = 100, Procs = 2, - send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, info), - %% send_burst({n,NumOfReqs}, seq, {chars,79}, info), + send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice), + %% send_burst({n,NumOfReqs}, seq, {chars,79}, notice), receive {'DOWN', MRef, _, _, Info} -> case Info of @@ -1050,7 +1050,7 @@ restart_after(Config) -> ok = logger:set_handler_config(?MODULE, NewHConfig1), MRef1 = erlang:monitor(process, whereis(h_proc_name())), %% kill handler - send_burst({n,100}, {spawn,2,0}, {chars,79}, info), + send_burst({n,100}, {spawn,2,0}, {chars,79}, notice), receive {'DOWN', MRef1, _, _, _Info1} -> timer:sleep(?HANDLER_RESTART_AFTER + 1000), @@ -1071,7 +1071,7 @@ restart_after(Config) -> Pid0 = whereis(h_proc_name()), MRef2 = erlang:monitor(process, Pid0), %% kill handler - send_burst({n,100}, {spawn,2,0}, {chars,79}, info), + send_burst({n,100}, {spawn,2,0}, {chars,79}, notice), receive {'DOWN', MRef2, _, _, _Info2} -> timer:sleep(RestartAfter + 2000), @@ -1106,7 +1106,7 @@ handler_requests_under_load(Config) -> {reset,[]}, {change_config,[]}]) end), - Sent = send_burst({t,10000}, seq, {chars,79}, info), + Sent = send_burst({t,10000}, seq, {chars,79}, notice), Pid ! {self(),finish}, ReqResult = receive {Pid,Result} -> Result end, Logged = count_lines(Log), @@ -1297,14 +1297,14 @@ add_remove_instance_nofile(Type) -> Pid = whereis(h_proc_name()), true = is_pid(Pid), group_leader(group_leader(),Pid), % to get printouts in test log - logger:info(M1=?msg,?domain), + logger:notice(M1=?msg,?domain), ?check(M1), %% check that sync doesn't do damage even if not relevant ok = logger_std_h:sync(?MODULE), ok = logger:remove_handler(?MODULE), timer:sleep(500), undefined = whereis(h_proc_name()), - logger:info(?msg,?domain), + logger:notice(?msg,?domain), ?check_no_log, ok. -- cgit v1.2.3 From d8a16e264fcdbe5b13643b81987e7afe0a633dd8 Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Thu, 7 Jun 2018 23:52:01 +0200 Subject: [logger] Change names of overload config parameters in build-in handlers --- lib/kernel/src/logger_disk_log_h.erl | 44 +++--- lib/kernel/src/logger_h_common.erl | 66 +++++---- lib/kernel/src/logger_h_common.hrl | 30 ++-- lib/kernel/src/logger_std_h.erl | 44 +++--- lib/kernel/test/logger_disk_log_h_SUITE.erl | 180 ++++++++++++------------ lib/kernel/test/logger_std_h_SUITE.erl | 210 ++++++++++++++-------------- 6 files changed, 289 insertions(+), 285 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index c3d2204630..e4b583c269 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -131,10 +131,10 @@ adding_handler(#{id:=Name}=Config) -> Error end; false -> - #{toggle_sync_qlen := TSQL, - drop_new_reqs_qlen := DNRQL, - flush_reqs_qlen := FRQL} = HState, - {error,{invalid_levels,{TSQL,DNRQL,FRQL}}} + #{sync_mode_qlen := SMQL, + drop_mode_qlen := DMQL, + flush_qlen := FQL} = HState, + {error,{invalid_levels,{SMQL,DMQL,FQL}}} end; Error -> Error @@ -347,9 +347,9 @@ handle_call(disk_log_sync, _From, State = #{id := Name}) -> handle_call({change_config,_OldConfig,NewConfig}, _From, State = #{filesync_repeat_interval := FSyncInt0}) -> HConfig = maps:get(config, NewConfig, #{}), - State1 = #{toggle_sync_qlen := TSQL, - drop_new_reqs_qlen := DNRQL, - flush_reqs_qlen := FRQL} = maps:merge(State, HConfig), + State1 = #{sync_mode_qlen := SMQL, + drop_mode_qlen := DMQL, + flush_qlen := FQL} = maps:merge(State, HConfig), case logger_h_common:overload_levels_ok(State1) of true -> _ = @@ -370,7 +370,7 @@ handle_call({change_config,_OldConfig,NewConfig}, _From, end, {reply, ok, State1}; false -> - {reply, {error,{invalid_levels,{TSQL,DNRQL,FRQL}}}, State} + {reply, {error,{invalid_levels,{SMQL,DMQL,FQL}}}, State} end; handle_call(info, _From, State) -> @@ -464,19 +464,19 @@ code_change(_OldVsn, State, _Extra) -> %%%----------------------------------------------------------------- %%% get_init_state() -> - #{toggle_sync_qlen => ?TOGGLE_SYNC_QLEN, - drop_new_reqs_qlen => ?DROP_NEW_REQS_QLEN, - flush_reqs_qlen => ?FLUSH_REQS_QLEN, - enable_burst_limit => ?ENABLE_BURST_LIMIT, - burst_limit_size => ?BURST_LIMIT_SIZE, - burst_window_time => ?BURST_WINDOW_TIME, - enable_kill_overloaded => ?ENABLE_KILL_OVERLOADED, - handler_overloaded_qlen => ?HANDLER_OVERLOADED_QLEN, - handler_overloaded_mem => ?HANDLER_OVERLOADED_MEM, - handler_restart_after => ?HANDLER_RESTART_AFTER, - dl_sync_int => ?CONTROLLER_SYNC_INTERVAL, - filesync_ok_qlen => ?FILESYNC_OK_QLEN, - filesync_repeat_interval => ?FILESYNC_REPEAT_INTERVAL}. + #{sync_mode_qlen => ?SYNC_MODE_QLEN, + drop_mode_qlen => ?DROP_MODE_QLEN, + flush_qlen => ?FLUSH_QLEN, + burst_limit_enable => ?BURST_LIMIT_ENABLE, + burst_limit_max_count => ?BURST_LIMIT_MAX_COUNT, + burst_limit_window_time => ?BURST_LIMIT_WINDOW_TIME, + overload_kill_enable => ?OVERLOAD_KILL_ENABLE, + overload_kill_qlen => ?OVERLOAD_KILL_QLEN, + overload_kill_mem_size => ?OVERLOAD_KILL_MEM_SIZE, + overload_kill_restart_after => ?OVERLOAD_KILL_RESTART_AFTER, + dl_sync_int => ?CONTROLLER_SYNC_INTERVAL, + filesync_ok_qlen => ?FILESYNC_OK_QLEN, + filesync_repeat_interval => ?FILESYNC_REPEAT_INTERVAL}. %%%----------------------------------------------------------------- %%% Add a disk_log handler to the logger. @@ -499,7 +499,7 @@ get_init_state() -> %%% Handler specific config should be provided with a sub map associated %%% with a key named 'config', e.g: %%% -%%% Config = #{config => #{toggle_sync_qlen => 50} +%%% Config = #{config => #{sync_mode_qlen => 50} %%% %%% The disk_log handler process is linked to logger_sup, which is %%% part of the kernel application's supervision tree. diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl index d2fcc7e115..b145043522 100644 --- a/lib/kernel/src/logger_h_common.erl +++ b/lib/kernel/src/logger_h_common.erl @@ -91,29 +91,33 @@ check_common_config({mode_tab,_Tid}) -> valid; check_common_config({handler_pid,Pid}) when is_pid(Pid) -> valid; -check_common_config({toggle_sync_qlen,N}) when is_integer(N) -> + +check_common_config({sync_mode_qlen,N}) when is_integer(N) -> valid; -check_common_config({drop_new_reqs_qlen,N}) when is_integer(N) -> +check_common_config({drop_mode_qlen,N}) when is_integer(N) -> valid; -check_common_config({flush_reqs_qlen,N}) when is_integer(N) -> +check_common_config({flush_qlen,N}) when is_integer(N) -> valid; -check_common_config({enable_burst_limit,Bool}) when Bool == true; + +check_common_config({burst_limit_enable,Bool}) when Bool == true; Bool == false -> valid; -check_common_config({burst_limit_size,N}) when is_integer(N) -> +check_common_config({burst_limit_max_count,N}) when is_integer(N) -> valid; -check_common_config({burst_window_time,N}) when is_integer(N) -> +check_common_config({burst_limit_window_time,N}) when is_integer(N) -> valid; -check_common_config({enable_kill_overloaded,Bool}) when Bool == true; - Bool == false -> + +check_common_config({overload_kill_enable,Bool}) when Bool == true; + Bool == false -> valid; -check_common_config({handler_overloaded_qlen,N}) when is_integer(N) -> +check_common_config({overload_kill_qlen,N}) when is_integer(N) -> valid; -check_common_config({handler_overloaded_mem,N}) when is_integer(N) -> +check_common_config({overload_kill_mem_size,N}) when is_integer(N) -> valid; -check_common_config({handler_restart_after,NorA}) when is_integer(NorA); - NorA == never -> +check_common_config({overload_kill_restart_after,NorA}) when is_integer(NorA); + NorA == never -> valid; + check_common_config({filesync_repeat_interval,NorA}) when is_integer(NorA); NorA == no_repeat -> valid; @@ -173,9 +177,9 @@ unset_restart_flag(Name, Module) -> end. check_load(State = #{id:=_Name, mode_tab := ModeTab, mode := Mode, - toggle_sync_qlen := ToggleSyncQLen, - drop_new_reqs_qlen := DropNewQLen, - flush_reqs_qlen := FlushQLen}) -> + sync_mode_qlen := SyncModeQLen, + drop_mode_qlen := DropModeQLen, + flush_qlen := FlushQLen}) -> {_,Mem} = process_info(self(), memory), ?observe(_Name,{max_mem,Mem}), {_,QLen} = process_info(self(), message_queue_len), @@ -191,13 +195,13 @@ check_load(State = #{id:=_Name, mode_tab := ModeTab, mode := Mode, if QLen >= FlushQLen -> {flush, 0,1}; - QLen >= DropNewQLen -> + QLen >= DropModeQLen -> %% Note that drop mode will force log events to %% be dropped on the client side (never sent get to %% the handler). IncDrops = if Mode == drop -> 0; true -> 1 end, {?change_mode(ModeTab, Mode, drop), IncDrops,0}; - QLen >= ToggleSyncQLen -> + QLen >= SyncModeQLen -> {?change_mode(ModeTab, Mode, sync), 0,0}; true -> {?change_mode(ModeTab, Mode, async), 0,0} @@ -207,17 +211,17 @@ check_load(State = #{id:=_Name, mode_tab := ModeTab, mode := Mode, ?update_other(flushes,FLUSHES,_NewFlushes, State1#{last_qlen => QLen})}. -limit_burst(#{enable_burst_limit := false}) -> +limit_burst(#{burst_limit_enable := false}) -> {true,0,0}; limit_burst(#{burst_win_ts := BurstWinT0, burst_msg_count := BurstMsgCount, - burst_window_time := BurstWinTime, - burst_limit_size := BurstLimitSz}) -> - if (BurstMsgCount >= BurstLimitSz) -> + burst_limit_window_time := BurstLimitWinTime, + burst_limit_max_count := BurstLimitMaxCnt}) -> + if (BurstMsgCount >= BurstLimitMaxCnt) -> %% the limit for allowed messages has been reached BurstWinT1 = ?timestamp(), case ?diff_time(BurstWinT1,BurstWinT0) of - BurstCheckTime when BurstCheckTime < (BurstWinTime*1000) -> + BurstCheckTime when BurstCheckTime < (BurstLimitWinTime*1000) -> %% we're still within the burst time frame {false,BurstWinT0,BurstMsgCount}; _BurstCheckTime -> @@ -230,11 +234,11 @@ limit_burst(#{burst_win_ts := BurstWinT0, end. kill_if_choked(Name, QLen, Mem, HandlerMod, - State = #{enable_kill_overloaded := KillIfOL, - handler_overloaded_qlen := HOLQLen, - handler_overloaded_mem := HOLMem}) -> + State = #{overload_kill_enable := KillIfOL, + overload_kill_qlen := OLKillQLen, + overload_kill_mem_size := OLKillMem}) -> if KillIfOL andalso - ((QLen > HOLQLen) orelse (Mem > HOLMem)) -> + ((QLen > OLKillQLen) orelse (Mem > OLKillMem)) -> HandlerMod:log_handler_info(Name, "Handler ~p overloaded and stopping", [Name], State), @@ -274,7 +278,7 @@ cancel_timer(TRef) -> timer:cancel(TRef). stop_or_restart(Name, {shutdown,Reason={overloaded,_Name,_QLen,_Mem}}, - #{handler_restart_after := RestartAfter}) -> + #{overload_kill_restart_after := RestartAfter}) -> %% If we're terminating because of an overload situation (see %% logger_h_common:kill_if_choked/4), we need to remove the handler %% and set a restart timer. A separate process must perform this @@ -322,10 +326,10 @@ stop_or_restart(_Name, _Reason, _State) -> ok. overload_levels_ok(HandlerConfig) -> - TSQL = maps:get(toggle_sync_qlen, HandlerConfig, ?TOGGLE_SYNC_QLEN), - DNRQL = maps:get(drop_new_reqs_qlen, HandlerConfig, ?DROP_NEW_REQS_QLEN), - FRQL = maps:get(flush_reqs_qlen, HandlerConfig, ?FLUSH_REQS_QLEN), - (DNRQL > 1) andalso (TSQL =< DNRQL) andalso (DNRQL =< FRQL). + SMQL = maps:get(sync_mode_qlen, HandlerConfig, ?SYNC_MODE_QLEN), + DMQL = maps:get(drop_mode_qlen, HandlerConfig, ?DROP_MODE_QLEN), + FQL = maps:get(flush_qlen, HandlerConfig, ?FLUSH_QLEN), + (DMQL > 1) andalso (SMQL =< DMQL) andalso (DMQL =< FQL). error_notify(Term) -> ?internal_log(error, Term). diff --git a/lib/kernel/src/logger_h_common.hrl b/lib/kernel/src/logger_h_common.hrl index e4d3431468..ad80b51109 100644 --- a/lib/kernel/src/logger_h_common.hrl +++ b/lib/kernel/src/logger_h_common.hrl @@ -4,47 +4,47 @@ %%! *** NOTE *** %%! It's important that: -%%! TOGGLE_SYNC_QLEN < DROP_NEW_REQS_QLEN < FLUSH_REQS_QLEN -%%! and that DROP_NEW_REQS_QLEN >= 2. +%%! SYNC_MODE_QLEN =< DROP_MODE_QLEN =< FLUSH_QLEN +%%! and that DROP_MODE_QLEN >= 2. %%! Otherwise the handler could end up in drop mode with no new %%! log requests to process. This would cause all future requests %%! to be dropped (no switch to async mode would ever take place). %% This specifies the message_queue_len value where the log %% requests switch from asynchronous casts to synchronous calls. --define(TOGGLE_SYNC_QLEN, 10). +-define(SYNC_MODE_QLEN, 10). %% Above this message_queue_len, log requests will be dropped, %% i.e. no log requests get sent to the handler process. --define(DROP_NEW_REQS_QLEN, 200). +-define(DROP_MODE_QLEN, 200). %% Above this message_queue_len, the handler process will flush %% its mailbox and only leave this number of messages in it. --define(FLUSH_REQS_QLEN, 1000). +-define(FLUSH_QLEN, 1000). %% Never flush more than this number of messages in one go, %% or the handler will be unresponsive for seconds (keep this %% number as large as possible or the mailbox could grow large). -define(FLUSH_MAX_N, 5000). -%% BURST_LIMIT is the max number of log requests allowed to be -%% written within a BURST_WINDOW_TIME time frame. --define(ENABLE_BURST_LIMIT, true). --define(BURST_LIMIT_SIZE, 500). --define(BURST_WINDOW_TIME, 1000). +%% BURST_LIMIT_MAX_COUNT is the max number of log requests allowed +%% to be written within a BURST_LIMIT_WINDOW_TIME time frame. +-define(BURST_LIMIT_ENABLE, true). +-define(BURST_LIMIT_MAX_COUNT, 500). +-define(BURST_LIMIT_WINDOW_TIME, 1000). %% This enables/disables the feature to automatically get the %% handler terminated if it gets too loaded (and can't keep up). --define(ENABLE_KILL_OVERLOADED, false). +-define(OVERLOAD_KILL_ENABLE, false). %% If the message_queue_len goes above this size even after %% flushing has been performed, the handler is terminated. --define(HANDLER_OVERLOADED_QLEN, 20000). +-define(OVERLOAD_KILL_QLEN, 20000). %% If the memory usage exceeds this level --define(HANDLER_OVERLOADED_MEM, 3000000). +-define(OVERLOAD_KILL_MEM_SIZE, 3000000). %% This is the default time that the handler will wait before %% restarting and accepting new requests. The value 'never' %% disables restarts. --define(HANDLER_RESTART_AFTER, 5000). -%%-define(HANDLER_RESTART_AFTER, never). +-define(OVERLOAD_KILL_RESTART_AFTER, 5000). +%%-define(OVERLOAD_KILL_RESTART_AFTER, never). %% The handler sends asynchronous write requests to the process %% controlling the i/o device, but every once in this interval diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index 77b054a9b7..e087178211 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -132,10 +132,10 @@ adding_handler(#{id:=Name}=Config) -> Error end; false -> - #{toggle_sync_qlen := TSQL, - drop_new_reqs_qlen := DNRQL, - flush_reqs_qlen := FRQL} = HState, - {error,{invalid_levels,{TSQL,DNRQL,FRQL}}} + #{sync_mode_qlen := SMQL, + drop_mode_qlen := DMQL, + flush_qlen := FQL} = HState, + {error,{invalid_levels,{SMQL,DMQL,FQL}}} end; Error -> Error @@ -360,10 +360,10 @@ handle_call({change_config,_OldConfig,NewConfig}, _From, end, {reply, ok, State1}; false -> - #{toggle_sync_qlen := TSQL, - drop_new_reqs_qlen := DNRQL, - flush_reqs_qlen := FRQL} = State1, - {reply, {error,{invalid_levels,{TSQL,DNRQL,FRQL}}}, State} + #{sync_mode_qlen := SMQL, + drop_mode_qlen := DMQL, + flush_qlen := FQL} = State1, + {reply, {error,{invalid_levels,{SMQL,DMQL,FQL}}}, State} end; handle_call(info, _From, State) -> @@ -459,19 +459,19 @@ code_change(_OldVsn, State, _Extra) -> %%%----------------------------------------------------------------- %%% get_init_state() -> - #{toggle_sync_qlen => ?TOGGLE_SYNC_QLEN, - drop_new_reqs_qlen => ?DROP_NEW_REQS_QLEN, - flush_reqs_qlen => ?FLUSH_REQS_QLEN, - enable_burst_limit => ?ENABLE_BURST_LIMIT, - burst_limit_size => ?BURST_LIMIT_SIZE, - burst_window_time => ?BURST_WINDOW_TIME, - enable_kill_overloaded => ?ENABLE_KILL_OVERLOADED, - handler_overloaded_qlen => ?HANDLER_OVERLOADED_QLEN, - handler_overloaded_mem => ?HANDLER_OVERLOADED_MEM, - handler_restart_after => ?HANDLER_RESTART_AFTER, - file_ctrl_sync_int => ?CONTROLLER_SYNC_INTERVAL, - filesync_ok_qlen => ?FILESYNC_OK_QLEN, - filesync_repeat_interval => ?FILESYNC_REPEAT_INTERVAL}. + #{sync_mode_qlen => ?SYNC_MODE_QLEN, + drop_mode_qlen => ?DROP_MODE_QLEN, + flush_qlen => ?FLUSH_QLEN, + burst_limit_enable => ?BURST_LIMIT_ENABLE, + burst_limit_max_count => ?BURST_LIMIT_MAX_COUNT, + burst_limit_window_time => ?BURST_LIMIT_WINDOW_TIME, + overload_kill_enable => ?OVERLOAD_KILL_ENABLE, + overload_kill_qlen => ?OVERLOAD_KILL_QLEN, + overload_kill_mem_size => ?OVERLOAD_KILL_MEM_SIZE, + overload_kill_restart_after => ?OVERLOAD_KILL_RESTART_AFTER, + file_ctrl_sync_int => ?CONTROLLER_SYNC_INTERVAL, + filesync_ok_qlen => ?FILESYNC_OK_QLEN, + filesync_repeat_interval => ?FILESYNC_REPEAT_INTERVAL}. %%%----------------------------------------------------------------- %%% Add a standard handler to the logger. @@ -482,7 +482,7 @@ get_init_state() -> %%% Handler specific config should be provided with a sub map associated %%% with a key named 'config', e.g: %%% -%%% Config = #{config => #{toggle_sync_qlen => 50} +%%% Config = #{config => #{sync_mode_qlen => 50} %%% %%% The standard handler process is linked to logger_sup, which is %%% part of the kernel application's supervision tree. diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 3f3b483b7d..c3b8237f82 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -372,15 +372,15 @@ config_fail(_Config) -> {error,{handler_not_added,{invalid_levels,{_,1,_}}}} = logger:add_handler(?MODULE,logger_disk_log_h, - #{config => #{drop_new_reqs_qlen=>1}}), + #{config => #{drop_mode_qlen=>1}}), {error,{handler_not_added,{invalid_levels,{43,42,_}}}} = logger:add_handler(?MODULE,logger_disk_log_h, - #{config => #{toggle_sync_qlen=>43, - drop_new_reqs_qlen=>42}}), + #{config => #{sync_mode_qlen=>43, + drop_mode_qlen=>42}}), {error,{handler_not_added,{invalid_levels,{_,43,42}}}} = logger:add_handler(?MODULE,logger_disk_log_h, - #{config => #{drop_new_reqs_qlen=>43, - flush_reqs_qlen=>42}}), + #{config => #{drop_mode_qlen=>43, + flush_qlen=>42}}), ok = logger:add_handler(?MODULE,logger_disk_log_h, #{filter_default=>log, @@ -395,8 +395,8 @@ config_fail(_Config) -> %% incorrect values of OP params {error,{invalid_levels,_}} = logger:set_handler_config(?MODULE,config, - #{toggle_sync_qlen=>100, - flush_reqs_qlen=>99}), + #{sync_mode_qlen=>100, + flush_qlen=>99}), %% invalid name of config parameter {error,{invalid_config,logger_disk_log_h,{filesync_rep_int,2000}}} = logger:set_handler_config(?MODULE, config, @@ -428,16 +428,16 @@ reconfig(Config) -> filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,self()}}), #{id := ?MODULE, - toggle_sync_qlen := ?TOGGLE_SYNC_QLEN, - drop_new_reqs_qlen := ?DROP_NEW_REQS_QLEN, - flush_reqs_qlen := ?FLUSH_REQS_QLEN, - enable_burst_limit := ?ENABLE_BURST_LIMIT, - burst_limit_size := ?BURST_LIMIT_SIZE, - burst_window_time := ?BURST_WINDOW_TIME, - enable_kill_overloaded := ?ENABLE_KILL_OVERLOADED, - handler_overloaded_qlen := ?HANDLER_OVERLOADED_QLEN, - handler_overloaded_mem := ?HANDLER_OVERLOADED_MEM, - handler_restart_after := ?HANDLER_RESTART_AFTER, + sync_mode_qlen := ?SYNC_MODE_QLEN, + drop_mode_qlen := ?DROP_MODE_QLEN, + flush_qlen := ?FLUSH_QLEN, + burst_limit_enable := ?BURST_LIMIT_ENABLE, + burst_limit_max_count := ?BURST_LIMIT_MAX_COUNT, + burst_limit_window_time := ?BURST_LIMIT_WINDOW_TIME, + overload_kill_enable := ?OVERLOAD_KILL_ENABLE, + overload_kill_qlen := ?OVERLOAD_KILL_QLEN, + overload_kill_mem_size := ?OVERLOAD_KILL_MEM_SIZE, + overload_kill_restart_after := ?OVERLOAD_KILL_RESTART_AFTER, filesync_repeat_interval := ?FILESYNC_REPEAT_INTERVAL, log_opts := #{type := ?DISK_LOG_TYPE, max_no_files := ?DISK_LOG_MAX_NO_FILES, @@ -446,28 +446,28 @@ reconfig(Config) -> logger_disk_log_h:info(?MODULE), ok = logger:set_handler_config(?MODULE, config, - #{toggle_sync_qlen => 1, - drop_new_reqs_qlen => 2, - flush_reqs_qlen => 3, - enable_burst_limit => false, - burst_limit_size => 10, - burst_window_time => 10, - enable_kill_overloaded => true, - handler_overloaded_qlen => 100000, - handler_overloaded_mem => 10000000, - handler_restart_after => never, + #{sync_mode_qlen => 1, + drop_mode_qlen => 2, + flush_qlen => 3, + burst_limit_enable => false, + burst_limit_max_count => 10, + burst_limit_window_time => 10, + overload_kill_enable => true, + overload_kill_qlen => 100000, + overload_kill_mem_size => 10000000, + overload_kill_restart_after => never, filesync_repeat_interval => no_repeat}), #{id := ?MODULE, - toggle_sync_qlen := 1, - drop_new_reqs_qlen := 2, - flush_reqs_qlen := 3, - enable_burst_limit := false, - burst_limit_size := 10, - burst_window_time := 10, - enable_kill_overloaded := true, - handler_overloaded_qlen := 100000, - handler_overloaded_mem := 10000000, - handler_restart_after := never, + sync_mode_qlen := 1, + drop_mode_qlen := 2, + flush_qlen := 3, + burst_limit_enable := false, + burst_limit_max_count := 10, + burst_limit_window_time := 10, + overload_kill_enable := true, + overload_kill_qlen := 100000, + overload_kill_mem_size := 10000000, + overload_kill_restart_after := never, filesync_repeat_interval := no_repeat} = logger_disk_log_h:info(?MODULE), @@ -849,10 +849,10 @@ op_switch_to_sync(Config) -> {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NumOfReqs = 500, NewHConfig = - HConfig#{config => DLHConfig#{toggle_sync_qlen => 2, - drop_new_reqs_qlen => NumOfReqs+1, - flush_reqs_qlen => 2*NumOfReqs, - enable_burst_limit => false}}, + HConfig#{config => DLHConfig#{sync_mode_qlen => 2, + drop_mode_qlen => NumOfReqs+1, + flush_qlen => 2*NumOfReqs, + burst_limit_enable => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Lines = count_lines(Log), @@ -874,10 +874,10 @@ op_switch_to_drop(Config) -> Bursts = 10, NewHConfig = HConfig#{config => - DLHConfig#{toggle_sync_qlen => 1, - drop_new_reqs_qlen => 2, - flush_reqs_qlen => Procs*NumOfReqs*Bursts, - enable_burst_limit => false}}, + DLHConfig#{sync_mode_qlen => 1, + drop_mode_qlen => 2, + flush_qlen => Procs*NumOfReqs*Bursts, + burst_limit_enable => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), %% It sometimes happens that the handler either gets %% the requests in a slow enough pace so that dropping @@ -919,11 +919,11 @@ op_switch_to_flush(Config) -> NewHConfig = HConfig#{config => - DLHConfig#{toggle_sync_qlen => 2, + DLHConfig#{sync_mode_qlen => 2, %% disable drop mode - drop_new_reqs_qlen => 300, - flush_reqs_qlen => 300, - enable_burst_limit => false}}, + drop_mode_qlen => 300, + flush_qlen => 300, + burst_limit_enable => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 1500, Procs = 10, @@ -961,11 +961,11 @@ op_switch_to_flush(cleanup, _Config) -> limit_burst_disabled(Config) -> {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NewHConfig = - HConfig#{config => DLHConfig#{enable_burst_limit => false, - burst_limit_size => 10, - burst_window_time => 2000, - drop_new_reqs_qlen => 200, - flush_reqs_qlen => 300}}, + HConfig#{config => DLHConfig#{burst_limit_enable => false, + burst_limit_max_count => 10, + burst_limit_window_time => 2000, + drop_mode_qlen => 200, + flush_qlen => 300}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, send_burst({n,NumOfReqs}, seq, {chars,79}, notice), @@ -980,11 +980,11 @@ limit_burst_enabled_one(Config) -> {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), ReqLimit = 10, NewHConfig = - HConfig#{config => DLHConfig#{enable_burst_limit => true, - burst_limit_size => ReqLimit, - burst_window_time => 2000, - drop_new_reqs_qlen => 200, - flush_reqs_qlen => 300}}, + HConfig#{config => DLHConfig#{burst_limit_enable => true, + burst_limit_max_count => ReqLimit, + burst_limit_window_time => 2000, + drop_mode_qlen => 200, + flush_qlen => 300}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, send_burst({n,NumOfReqs}, seq, {chars,79}, notice), @@ -1000,11 +1000,11 @@ limit_burst_enabled_period(Config) -> ReqLimit = 10, BurstTWin = 1000, NewHConfig = - HConfig#{config => DLHConfig#{enable_burst_limit => true, - burst_limit_size => ReqLimit, - burst_window_time => BurstTWin, - drop_new_reqs_qlen => 20000, - flush_reqs_qlen => 20001}}, + HConfig#{config => DLHConfig#{burst_limit_enable => true, + burst_limit_max_count => ReqLimit, + burst_limit_window_time => BurstTWin, + drop_mode_qlen => 20000, + flush_qlen => 20001}}, ok = logger:set_handler_config(?MODULE, NewHConfig), Windows = 3, @@ -1021,9 +1021,9 @@ limit_burst_enabled_period(cleanup, _Config) -> kill_disabled(Config) -> {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NewHConfig = - HConfig#{config=>DLHConfig#{enable_kill_overloaded=>false, - handler_overloaded_qlen=>10, - handler_overloaded_mem=>100}}, + HConfig#{config=>DLHConfig#{overload_kill_enable=>false, + overload_kill_qlen=>10, + overload_kill_mem_size=>100}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, send_burst({n,NumOfReqs}, seq, {chars,79}, notice), @@ -1039,13 +1039,13 @@ qlen_kill_new(Config) -> {_Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), Pid0 = whereis(h_proc_name()), {_,Mem0} = process_info(Pid0, memory), - RestartAfter = ?HANDLER_RESTART_AFTER, + RestartAfter = ?OVERLOAD_KILL_RESTART_AFTER, NewHConfig = HConfig#{config => - DLHConfig#{enable_kill_overloaded=>true, - handler_overloaded_qlen=>10, - handler_overloaded_mem=>Mem0+50000, - handler_restart_after=>RestartAfter}}, + DLHConfig#{overload_kill_enable=>true, + overload_kill_qlen=>10, + overload_kill_mem_size=>Mem0+50000, + overload_kill_restart_after=>RestartAfter}}, ok = logger:set_handler_config(?MODULE, NewHConfig), MRef = erlang:monitor(process, Pid0), NumOfReqs = 100, @@ -1076,13 +1076,13 @@ mem_kill_new(Config) -> {_Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), Pid0 = whereis(h_proc_name()), {_,Mem0} = process_info(Pid0, memory), - RestartAfter = ?HANDLER_RESTART_AFTER, + RestartAfter = ?OVERLOAD_KILL_RESTART_AFTER, NewHConfig = HConfig#{config => - DLHConfig#{enable_kill_overloaded=>true, - handler_overloaded_qlen=>50000, - handler_overloaded_mem=>Mem0+500, - handler_restart_after=>RestartAfter}}, + DLHConfig#{overload_kill_enable=>true, + overload_kill_qlen=>50000, + overload_kill_mem_size=>Mem0+500, + overload_kill_restart_after=>RestartAfter}}, ok = logger:set_handler_config(?MODULE, NewHConfig), MRef = erlang:monitor(process, Pid0), NumOfReqs = 100, @@ -1112,16 +1112,16 @@ mem_kill_new(cleanup, _Config) -> restart_after(Config) -> {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NewHConfig1 = - HConfig#{config=>DLHConfig#{enable_kill_overloaded=>true, - handler_overloaded_qlen=>10, - handler_restart_after=>never}}, + HConfig#{config=>DLHConfig#{overload_kill_enable=>true, + overload_kill_qlen=>10, + overload_kill_restart_after=>never}}, ok = logger:set_handler_config(?MODULE, NewHConfig1), MRef1 = erlang:monitor(process, whereis(h_proc_name())), %% kill handler send_burst({n,100}, {spawn,2,0}, {chars,79}, notice), receive {'DOWN', MRef1, _, _, _Info1} -> - timer:sleep(?HANDLER_RESTART_AFTER + 1000), + timer:sleep(?OVERLOAD_KILL_RESTART_AFTER + 1000), undefined = whereis(h_proc_name()), ok after @@ -1130,11 +1130,11 @@ restart_after(Config) -> end, {Log,_,_} = start_handler(?MODULE, ?FUNCTION_NAME, Config), - RestartAfter = ?HANDLER_RESTART_AFTER, + RestartAfter = ?OVERLOAD_KILL_RESTART_AFTER, NewHConfig2 = - HConfig#{config=>DLHConfig#{enable_kill_overloaded=>true, - handler_overloaded_qlen=>10, - handler_restart_after=>RestartAfter}}, + HConfig#{config=>DLHConfig#{overload_kill_enable=>true, + overload_kill_qlen=>10, + overload_kill_restart_after=>RestartAfter}}, ok = logger:set_handler_config(?MODULE, NewHConfig2), Pid0 = whereis(h_proc_name()), MRef2 = erlang:monitor(process, Pid0), @@ -1163,10 +1163,10 @@ handler_requests_under_load() -> handler_requests_under_load(Config) -> {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NewHConfig = - HConfig#{config => DLHConfig#{toggle_sync_qlen => 2, - drop_new_reqs_qlen => 1000, - flush_reqs_qlen => 2000, - enable_burst_limit => false}}, + HConfig#{config => DLHConfig#{sync_mode_qlen => 2, + drop_mode_qlen => 1000, + flush_qlen => 2000, + burst_limit_enable => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), Pid = spawn_link(fun() -> send_requests(?MODULE, 1, [{sync,[]}, {info,[]}, @@ -1201,7 +1201,7 @@ send_requests(HName, TO, Reqs = [{Req,Res}|Rs]) -> case Req of change_config -> logger:set_handler_config(HName, logger_disk_log_h, - #{enable_kill_overloaded => + #{overload_kill_enable => false}); Func -> logger_disk_log_h:Func(HName) diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index e71b75fe66..2efe2ce6f6 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -291,15 +291,15 @@ config_fail(_Config) -> formatter=>{?MODULE,self()}}), {error,{handler_not_added,{invalid_levels,{_,1,_}}}} = logger:add_handler(?MODULE,logger_std_h, - #{config => #{drop_new_reqs_qlen=>1}}), + #{config => #{drop_mode_qlen=>1}}), {error,{handler_not_added,{invalid_levels,{43,42,_}}}} = logger:add_handler(?MODULE,logger_std_h, - #{config => #{toggle_sync_qlen=>43, - drop_new_reqs_qlen=>42}}), + #{config => #{sync_mode_qlen=>43, + drop_mode_qlen=>42}}), {error,{handler_not_added,{invalid_levels,{_,43,42}}}} = logger:add_handler(?MODULE,logger_std_h, - #{config => #{drop_new_reqs_qlen=>43, - flush_reqs_qlen=>42}}), + #{config => #{drop_mode_qlen=>43, + flush_qlen=>42}}), ok = logger:add_handler(?MODULE,logger_std_h, #{filter_default=>log, @@ -311,8 +311,8 @@ config_fail(_Config) -> logger:set_handler_config(?MODULE,id,bad), {error,{invalid_levels,_}} = logger:set_handler_config(?MODULE,config, - #{toggle_sync_qlen=>100, - flush_reqs_qlen=>99}), + #{sync_mode_qlen=>100, + flush_qlen=>99}), {error,{invalid_config,logger_std_h,{filesync_rep_int,2000}}} = logger:set_handler_config(?MODULE, config, #{filesync_rep_int => 2000}), @@ -432,44 +432,44 @@ reconfig(Config) -> #{id := ?MODULE, type := standard_io, file_ctrl_pid := FileCtrlPid, - toggle_sync_qlen := ?TOGGLE_SYNC_QLEN, - drop_new_reqs_qlen := ?DROP_NEW_REQS_QLEN, - flush_reqs_qlen := ?FLUSH_REQS_QLEN, - enable_burst_limit := ?ENABLE_BURST_LIMIT, - burst_limit_size := ?BURST_LIMIT_SIZE, - burst_window_time := ?BURST_WINDOW_TIME, - enable_kill_overloaded := ?ENABLE_KILL_OVERLOADED, - handler_overloaded_qlen := ?HANDLER_OVERLOADED_QLEN, - handler_overloaded_mem := ?HANDLER_OVERLOADED_MEM, - handler_restart_after := ?HANDLER_RESTART_AFTER, + sync_mode_qlen := ?SYNC_MODE_QLEN, + drop_mode_qlen := ?DROP_MODE_QLEN, + flush_qlen := ?FLUSH_QLEN, + burst_limit_enable := ?BURST_LIMIT_ENABLE, + burst_limit_max_count := ?BURST_LIMIT_MAX_COUNT, + burst_limit_window_time := ?BURST_LIMIT_WINDOW_TIME, + overload_kill_enable := ?OVERLOAD_KILL_ENABLE, + overload_kill_qlen := ?OVERLOAD_KILL_QLEN, + overload_kill_mem_size := ?OVERLOAD_KILL_MEM_SIZE, + overload_kill_restart_after := ?OVERLOAD_KILL_RESTART_AFTER, filesync_repeat_interval := ?FILESYNC_REPEAT_INTERVAL} = logger_std_h:info(?MODULE), ok = logger:set_handler_config(?MODULE, config, - #{toggle_sync_qlen => 1, - drop_new_reqs_qlen => 2, - flush_reqs_qlen => 3, - enable_burst_limit => false, - burst_limit_size => 10, - burst_window_time => 10, - enable_kill_overloaded => true, - handler_overloaded_qlen => 100000, - handler_overloaded_mem => 10000000, - handler_restart_after => never, + #{sync_mode_qlen => 1, + drop_mode_qlen => 2, + flush_qlen => 3, + burst_limit_enable => false, + burst_limit_max_count => 10, + burst_limit_window_time => 10, + overload_kill_enable => true, + overload_kill_qlen => 100000, + overload_kill_mem_size => 10000000, + overload_kill_restart_after => never, filesync_repeat_interval => no_repeat}), #{id := ?MODULE, type := standard_io, file_ctrl_pid := FileCtrlPid, - toggle_sync_qlen := 1, - drop_new_reqs_qlen := 2, - flush_reqs_qlen := 3, - enable_burst_limit := false, - burst_limit_size := 10, - burst_window_time := 10, - enable_kill_overloaded := true, - handler_overloaded_qlen := 100000, - handler_overloaded_mem := 10000000, - handler_restart_after := never, + sync_mode_qlen := 1, + drop_mode_qlen := 2, + flush_qlen := 3, + burst_limit_enable := false, + burst_limit_max_count := 10, + burst_limit_window_time := 10, + overload_kill_enable := true, + overload_kill_qlen := 100000, + overload_kill_mem_size := 10000000, + overload_kill_restart_after := never, filesync_repeat_interval := no_repeat} = logger_std_h:info(?MODULE), ok. @@ -699,10 +699,10 @@ op_switch_to_sync_file(Config) -> {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NumOfReqs = 500, NewHConfig = - HConfig#{config => StdHConfig#{toggle_sync_qlen => 2, - drop_new_reqs_qlen => NumOfReqs+1, - flush_reqs_qlen => 2*NumOfReqs, - enable_burst_limit => false}}, + HConfig#{config => StdHConfig#{sync_mode_qlen => 2, + drop_mode_qlen => NumOfReqs+1, + flush_qlen => 2*NumOfReqs, + burst_limit_enable => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), %% TRecvPid = start_op_trace(), send_burst({n,NumOfReqs}, seq, {chars,79}, notice), @@ -728,10 +728,10 @@ op_switch_to_sync_tty(Config) -> {HConfig,StdHConfig} = start_handler(?MODULE, standard_io, Config), NumOfReqs = 500, NewHConfig = - HConfig#{config => StdHConfig#{toggle_sync_qlen => 3, - drop_new_reqs_qlen => NumOfReqs+1, - flush_reqs_qlen => 2*NumOfReqs, - enable_burst_limit => false}}, + HConfig#{config => StdHConfig#{sync_mode_qlen => 3, + drop_mode_qlen => NumOfReqs+1, + flush_qlen => 2*NumOfReqs, + burst_limit_enable => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), send_burst({n,NumOfReqs}, seq, {chars,79}, notice), ok. @@ -750,11 +750,11 @@ op_switch_to_drop_file(Config) -> Bursts = 10, NewHConfig = HConfig#{config => - StdHConfig#{toggle_sync_qlen => 1, - drop_new_reqs_qlen => 2, - flush_reqs_qlen => + StdHConfig#{sync_mode_qlen => 1, + drop_mode_qlen => 2, + flush_qlen => Procs*NumOfReqs*Bursts, - enable_burst_limit => false}}, + burst_limit_enable => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), %% It sometimes happens that the handler gets the %% requests in a slow enough pace so that dropping @@ -787,11 +787,11 @@ op_switch_to_drop_tty(Config) -> NumOfReqs = 300, Procs = 2, NewHConfig = - HConfig#{config => StdHConfig#{toggle_sync_qlen => 1, - drop_new_reqs_qlen => 2, - flush_reqs_qlen => + HConfig#{config => StdHConfig#{sync_mode_qlen => 1, + drop_mode_qlen => 2, + flush_qlen => Procs*NumOfReqs+1, - enable_burst_limit => false}}, + burst_limit_enable => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice), ok. @@ -812,11 +812,11 @@ op_switch_to_flush_file(Config) -> NewHConfig = HConfig#{config => - StdHConfig#{toggle_sync_qlen => 2, + StdHConfig#{sync_mode_qlen => 2, %% disable drop mode - drop_new_reqs_qlen => 300, - flush_reqs_qlen => 300, - enable_burst_limit => false}}, + drop_mode_qlen => 300, + flush_qlen => 300, + burst_limit_enable => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 1500, Procs = 10, @@ -859,11 +859,11 @@ op_switch_to_flush_tty(Config) -> %% when the flush happens (verify with coverage of flush_log_requests/2) NewHConfig = - HConfig#{config => StdHConfig#{toggle_sync_qlen => 2, + HConfig#{config => StdHConfig#{sync_mode_qlen => 2, %% disable drop mode - drop_new_reqs_qlen => 100, - flush_reqs_qlen => 100, - enable_burst_limit => false}}, + drop_mode_qlen => 100, + flush_qlen => 100, + burst_limit_enable => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 1000, Procs = 100, @@ -875,11 +875,11 @@ op_switch_to_flush_tty(cleanup, _Config) -> limit_burst_disabled(Config) -> {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NewHConfig = - HConfig#{config => StdHConfig#{enable_burst_limit => false, - burst_limit_size => 10, - burst_window_time => 2000, - drop_new_reqs_qlen => 200, - flush_reqs_qlen => 300}}, + HConfig#{config => StdHConfig#{burst_limit_enable => false, + burst_limit_max_count => 10, + burst_limit_window_time => 2000, + drop_mode_qlen => 200, + flush_qlen => 300}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, send_burst({n,NumOfReqs}, seq, {chars,79}, notice), @@ -894,11 +894,11 @@ limit_burst_enabled_one(Config) -> {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), ReqLimit = 10, NewHConfig = - HConfig#{config => StdHConfig#{enable_burst_limit => true, - burst_limit_size => ReqLimit, - burst_window_time => 2000, - drop_new_reqs_qlen => 200, - flush_reqs_qlen => 300}}, + HConfig#{config => StdHConfig#{burst_limit_enable => true, + burst_limit_max_count => ReqLimit, + burst_limit_window_time => 2000, + drop_mode_qlen => 200, + flush_qlen => 300}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, send_burst({n,NumOfReqs}, seq, {chars,79}, notice), @@ -914,11 +914,11 @@ limit_burst_enabled_period(Config) -> ReqLimit = 10, BurstTWin = 1000, NewHConfig = - HConfig#{config => StdHConfig#{enable_burst_limit => true, - burst_limit_size => ReqLimit, - burst_window_time => BurstTWin, - drop_new_reqs_qlen => 20000, - flush_reqs_qlen => 20001}}, + HConfig#{config => StdHConfig#{burst_limit_enable => true, + burst_limit_max_count => ReqLimit, + burst_limit_window_time => BurstTWin, + drop_mode_qlen => 20000, + flush_qlen => 20001}}, ok = logger:set_handler_config(?MODULE, NewHConfig), Windows = 3, @@ -935,9 +935,9 @@ limit_burst_enabled_period(cleanup, _Config) -> kill_disabled(Config) -> {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NewHConfig = - HConfig#{config=>StdHConfig#{enable_kill_overloaded=>false, - handler_overloaded_qlen=>10, - handler_overloaded_mem=>100}}, + HConfig#{config=>StdHConfig#{overload_kill_enable=>false, + overload_kill_qlen=>10, + overload_kill_mem_size=>100}}, ok = logger:set_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, send_burst({n,NumOfReqs}, seq, {chars,79}, notice), @@ -953,12 +953,12 @@ qlen_kill_new(Config) -> {_Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), Pid0 = whereis(h_proc_name()), {_,Mem0} = process_info(Pid0, memory), - RestartAfter = ?HANDLER_RESTART_AFTER, + RestartAfter = ?OVERLOAD_KILL_RESTART_AFTER, NewHConfig = - HConfig#{config=>StdHConfig#{enable_kill_overloaded=>true, - handler_overloaded_qlen=>10, - handler_overloaded_mem=>Mem0+50000, - handler_restart_after=>RestartAfter}}, + HConfig#{config=>StdHConfig#{overload_kill_enable=>true, + overload_kill_qlen=>10, + overload_kill_mem_size=>Mem0+50000, + overload_kill_restart_after=>RestartAfter}}, ok = logger:set_handler_config(?MODULE, NewHConfig), MRef = erlang:monitor(process, Pid0), NumOfReqs = 100, @@ -995,21 +995,21 @@ qlen_kill_std(_Config) -> %% Node = start_std_h_on_new_node(Config, ?FUNCTION_NAME, Log), %% ok = rpc:call(Node, logger, set_handler_config, %% [?STANDARD_HANDLER, config, - %% #{enable_kill_overloaded=>true, - %% handler_overloaded_qlen=>10, - %% handler_overloaded_mem=>100000}]), + %% #{overload_kill_enable=>true, + %% overload_kill_qlen=>10, + %% overload_kill_mem_size=>100000}]), {skip,"Not done yet"}. mem_kill_new(Config) -> {_Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), Pid0 = whereis(h_proc_name()), {_,Mem0} = process_info(Pid0, memory), - RestartAfter = ?HANDLER_RESTART_AFTER, + RestartAfter = ?OVERLOAD_KILL_RESTART_AFTER, NewHConfig = - HConfig#{config=>StdHConfig#{enable_kill_overloaded=>true, - handler_overloaded_qlen=>50000, - handler_overloaded_mem=>Mem0+500, - handler_restart_after=>RestartAfter}}, + HConfig#{config=>StdHConfig#{overload_kill_enable=>true, + overload_kill_qlen=>50000, + overload_kill_mem_size=>Mem0+500, + overload_kill_restart_after=>RestartAfter}}, ok = logger:set_handler_config(?MODULE, NewHConfig), MRef = erlang:monitor(process, Pid0), NumOfReqs = 100, @@ -1044,16 +1044,16 @@ mem_kill_std(_Config) -> restart_after(Config) -> {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NewHConfig1 = - HConfig#{config=>StdHConfig#{enable_kill_overloaded=>true, - handler_overloaded_qlen=>10, - handler_restart_after=>never}}, + HConfig#{config=>StdHConfig#{overload_kill_enable=>true, + overload_kill_qlen=>10, + overload_kill_restart_after=>never}}, ok = logger:set_handler_config(?MODULE, NewHConfig1), MRef1 = erlang:monitor(process, whereis(h_proc_name())), %% kill handler send_burst({n,100}, {spawn,2,0}, {chars,79}, notice), receive {'DOWN', MRef1, _, _, _Info1} -> - timer:sleep(?HANDLER_RESTART_AFTER + 1000), + timer:sleep(?OVERLOAD_KILL_RESTART_AFTER + 1000), undefined = whereis(h_proc_name()), ok after @@ -1062,11 +1062,11 @@ restart_after(Config) -> end, {Log,_,_} = start_handler(?MODULE, ?FUNCTION_NAME, Config), - RestartAfter = ?HANDLER_RESTART_AFTER, + RestartAfter = ?OVERLOAD_KILL_RESTART_AFTER, NewHConfig2 = - HConfig#{config=>StdHConfig#{enable_kill_overloaded=>true, - handler_overloaded_qlen=>10, - handler_restart_after=>RestartAfter}}, + HConfig#{config=>StdHConfig#{overload_kill_enable=>true, + overload_kill_qlen=>10, + overload_kill_restart_after=>RestartAfter}}, ok = logger:set_handler_config(?MODULE, NewHConfig2), Pid0 = whereis(h_proc_name()), MRef2 = erlang:monitor(process, Pid0), @@ -1096,10 +1096,10 @@ handler_requests_under_load(Config) -> {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NewHConfig = - HConfig#{config => StdHConfig#{toggle_sync_qlen => 2, - drop_new_reqs_qlen => 1000, - flush_reqs_qlen => 2000, - enable_burst_limit => false}}, + HConfig#{config => StdHConfig#{sync_mode_qlen => 2, + drop_mode_qlen => 1000, + flush_qlen => 2000, + burst_limit_enable => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), Pid = spawn_link(fun() -> send_requests(?MODULE, 1, [{sync,[]}, {info,[]}, @@ -1133,7 +1133,7 @@ send_requests(HName, TO, Reqs = [{Req,Res}|Rs]) -> case Req of change_config -> logger:set_handler_config(HName, config, - #{enable_kill_overloaded => + #{overload_kill_enable => false}); Func -> logger_std_h:Func(HName) -- cgit v1.2.3 From cca41722c8dc7b83500f14588643ccd82bf3020f Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 8 Jun 2018 15:48:17 +0200 Subject: [logger] Remove logger_bench_SUITE This test suite was used during early development of Logger, but is no longer upto date. --- lib/kernel/test/Makefile | 1 - lib/kernel/test/kernel.spec | 1 - lib/kernel/test/logger_bench_SUITE.erl | 500 --------------------- lib/kernel/test/logger_bench_SUITE_data/Emakefile | 1 - .../test/logger_bench_SUITE_data/lager_helper.erl | 73 --- 5 files changed, 576 deletions(-) delete mode 100644 lib/kernel/test/logger_bench_SUITE.erl delete mode 100644 lib/kernel/test/logger_bench_SUITE_data/Emakefile delete mode 100644 lib/kernel/test/logger_bench_SUITE_data/lager_helper.erl (limited to 'lib/kernel') diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile index 2ad1e3107c..0646395800 100644 --- a/lib/kernel/test/Makefile +++ b/lib/kernel/test/Makefile @@ -71,7 +71,6 @@ MODULES= \ init_SUITE \ kernel_config_SUITE \ logger_SUITE \ - logger_bench_SUITE \ logger_disk_log_h_SUITE \ logger_env_var_SUITE \ logger_filters_SUITE \ diff --git a/lib/kernel/test/kernel.spec b/lib/kernel/test/kernel.spec index 86d2155828..62afc9f97b 100644 --- a/lib/kernel/test/kernel.spec +++ b/lib/kernel/test/kernel.spec @@ -2,4 +2,3 @@ {config, "../test_server/ts.unix.config"}. {suites,"../kernel_test", all}. -{skip_suites,"../kernel_test",[logger_bench_SUITE],"Not ready"}. diff --git a/lib/kernel/test/logger_bench_SUITE.erl b/lib/kernel/test/logger_bench_SUITE.erl deleted file mode 100644 index c6aa541a94..0000000000 --- a/lib/kernel/test/logger_bench_SUITE.erl +++ /dev/null @@ -1,500 +0,0 @@ -%% -%% %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_bench_SUITE). - --compile(export_all). - -%%%----------------------------------------------------------------- -%%% To include lager tests, add paths to lager and goldrush -%%% (goldrush is a dependency inside the lager repo) -%%% -%%% To print data to .csv files, add the following to a config file: -%%% {print_csv,[{console_handler,[{path,"/some/dir/"}]}]}. -%%% -%%%----------------------------------------------------------------- - --include_lib("common_test/include/ct.hrl"). --include_lib("common_test/include/ct_event.hrl"). --include_lib("kernel/include/logger.hrl"). --include_lib("kernel/src/logger_internal.hrl"). - --define(msg,lists:flatten(string:pad("Log from "++atom_to_list(?FUNCTION_NAME)++ - ":"++integer_to_list(?LINE), - 80,trailing,$*))). --define(meta,#{mfa=>{?MODULE,?FUNCTION_NAME,?FUNCTION_ARITY}, - pid=>self()}). - --define(NO_COMPARE,[profile]). - --define(TIMES,100000). - -suite() -> - [{timetrap,{seconds,120}}]. - -init_per_suite(Config) -> - DataDir = ?config(data_dir,Config), - have_lager() andalso make(DataDir), - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(Group, Config) -> - H = remove_all_handlers(), - do_init_per_group(Group), - [{handlers,H}|Config]. - -do_init_per_group(minimal_handler) -> - ok = logger:add_handler(?MODULE,?MODULE,#{level=>error,filter_default=>log}); -do_init_per_group(console_handler) -> - ok = logger:add_handler(?MODULE,logger_std_h, - #{filter_default=>stop, - filters=>?DEFAULT_HANDLER_FILTERS, - config=>#{type=>standard_io, - toggle_sync_qlen => ?TIMES+1, - drop_new_reqs_qlen => ?TIMES+2, - flush_reqs_qlen => ?TIMES+3, - enable_burst_limit => false}}), - have_lager() andalso lager_helper:start(), - ok. - -end_per_group(Group, Config) -> - case ?config(saved_config,Config) of - {_,[{bench,Bench}]} -> - print_compare_chart(Group,Bench); - _ -> - ok - end, - add_all_handlers(?config(handlers,Config)), - do_end_per_group(Group). - -do_end_per_group(minimal_handler) -> - ok = logger:remove_handler(?MODULE); -do_end_per_group(console_handler) -> - ok = logger:remove_handler(?MODULE), - have_lager() andalso lager_helper:stop(), - ok. - -init_per_testcase(_TestCase, Config) -> - Config. - -end_per_testcase(Case, Config) -> - try apply(?MODULE,Case,[cleanup,Config]) - catch error:undef -> ok - end, - wait_for_handlers(), - ok. - -wait_for_handlers() -> - wait_for_handler(?MODULE), - wait_for_handler(lager_event). - -wait_for_handler(Handler) -> - case whereis(Handler) of - undefined -> - io:format("~p: noproc1",[Handler]), - ok; - Pid -> - case process_info(Pid,message_queue_len) of - {_,0} -> - io:format("~p: queue=~p",[Handler,0]), - ok; - {_,Q} -> - io:format("~p: queue=~p",[Handler,Q]), - timer:sleep(2000), - wait_for_handler(Handler); - undefined -> - io:format("~p: noproc2",[Handler]), - ok - end - end. - -groups() -> - [{minimal_handler,[],[log, - log_drop, - log_drop_by_handler, - macro, - macro_drop, - macro_drop_by_handler, - error_logger, - error_logger_drop, - error_logger_drop_by_handler - ]}, - {console_handler,[],[%profile, - log, - log_drop, - log_drop_by_handler, - %% log_handler_complete, - macro, - macro_drop, - macro_drop_by_handler, - %% macro_handler_complete, - error_logger, - error_logger_drop, - error_logger_drop_by_handler%% , - %% error_logger_handler_complete - ] ++ lager_cases()} - ]. - -lager_cases() -> - case have_lager() of - true -> - [lager_log, - lager_log_drop, - lager_log_drop_by_handler, - %% lager_log_handler_complete, - lager_parsetrans, - lager_parsetrans_drop, - lager_parsetrans_drop_by_handler%% , - %% lager_parsetrans_handler_complete - ]; - false -> - [] - end. - - -all() -> - [{group,minimal_handler}, - {group,console_handler} - ]. - -log(Config) -> - Times = ?TIMES, - run_benchmark(Config,?FUNCTION_NAME,fun do_log_func/2, [error,?msg], Times). - -log_drop(Config) -> - Times = ?TIMES*100, - ok = logger:set_primary_config(level,error), - run_benchmark(Config,?FUNCTION_NAME,fun do_log_func/2, [info,?msg], Times). - -log_drop(cleanup,_Config) -> - ok = logger:set_primary_config(level,info). - -log_drop_by_handler(Config) -> - Times = ?TIMES, - %% just ensure correct levels - ok = logger:set_primary_config(level,info), - ok = logger:set_handler_config(?MODULE,level,error), - run_benchmark(Config,?FUNCTION_NAME,fun do_log_func/2, [info,?msg], Times). - -log_handler_complete(Config) -> - ok = logger:set_handler_config(?MODULE,formatter, - {?MODULE,?DEFAULT_FORMAT_CONFIG}), - handler_complete(Config, ?FUNCTION_NAME, fun do_log_func/2, [error,?msg]). - -log_handler_complete(cleanup,_Config) -> - ok=logger:set_handler_config(?MODULE,formatter, - {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}). - -macro(Config) -> - Times = ?TIMES, - run_benchmark(Config,?FUNCTION_NAME,fun do_log_macro/2,[error,?msg], Times). - -macro_drop(Config) -> - Times = ?TIMES*100, - ok = logger:set_primary_config(level,error), - run_benchmark(Config,?FUNCTION_NAME,fun do_log_macro/2,[info,?msg], Times). - -macro_drop(cleanup,_Config) -> - ok = logger:set_primary_config(level,info). - -macro_drop_by_handler(Config) -> - Times = ?TIMES, - %% just ensure correct levels - ok = logger:set_primary_config(level,info), - ok = logger:set_handler_config(?MODULE,level,error), - run_benchmark(Config,?FUNCTION_NAME,fun do_log_macro/2, [info,?msg], Times). - -macro_handler_complete(Config) -> - ok = logger:set_handler_config(?MODULE,formatter, - {?MODULE,?DEFAULT_FORMAT_CONFIG}), - handler_complete(Config, ?FUNCTION_NAME, fun do_log_macro/2, [error,?msg]). - -macro_handler_complete(cleanup,_Config) -> - ok=logger:set_handler_config(?MODULE,formatter, - {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}). - -error_logger(Config) -> - Times = ?TIMES, - run_benchmark(Config,?FUNCTION_NAME,fun do_error_logger/2, [error,?msg], Times). - -error_logger_drop(Config) -> - Times = ?TIMES*100, - ok = logger:set_primary_config(level,error), - run_benchmark(Config,?FUNCTION_NAME,fun do_error_logger/2, [info,?msg], Times). - -error_logger_drop(cleanup,_Config) -> - ok = logger:set_primary_config(level,info). - -error_logger_drop_by_handler(Config) -> - Times = ?TIMES, - %% just ensure correct levels - ok = logger:set_primary_config(level,info), - ok = logger:set_handler_config(?MODULE,level,error), - run_benchmark(Config,?FUNCTION_NAME,fun do_log_func/2, [info,?msg], Times). - -error_logger_handler_complete(Config) -> - ok = logger:set_handler_config(?MODULE,formatter, - {?MODULE,?DEFAULT_FORMAT_CONFIG}), - handler_complete(Config, ?FUNCTION_NAME, fun do_error_logger/2, [error,?msg]). - -error_logger_handler_complete(cleanup,_Config) -> - ok=logger:set_handler_config(?MODULE,formatter, - {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}). - -lager_log(Config) -> - Times = ?TIMES, - run_benchmark(Config,?FUNCTION_NAME,fun lager_helper:do_func/2, [error,?msg], Times). - -lager_log_drop(Config) -> - Times = ?TIMES*100, - run_benchmark(Config,?FUNCTION_NAME,fun lager_helper:do_func/2, [info,?msg], Times). - -lager_log_drop_by_handler(Config) -> - %% This concept does not exist, so doing same as lager_log_drop/1 - Times = ?TIMES, - run_benchmark(Config,?FUNCTION_NAME,fun lager_helper:do_func/2, [info,?msg], Times). - -lager_log_handler_complete(Config) -> - handler_complete(Config, ?FUNCTION_NAME, fun lager_helper:do_func/2, [error,?msg]). - -lager_parsetrans(Config) -> - Times = ?TIMES, - run_benchmark(Config,?FUNCTION_NAME,fun lager_helper:do_parsetrans/2, [error,?msg], Times). - -lager_parsetrans_drop(Config) -> - Times = ?TIMES*100, - run_benchmark(Config,?FUNCTION_NAME,fun lager_helper:do_parsetrans/2, [info,?msg], Times). - -lager_parsetrans_drop_by_handler(Config) -> - %% This concept does not exist, so doing same as lager_parsetrans_drop/1 - Times = ?TIMES, - run_benchmark(Config,?FUNCTION_NAME,fun lager_helper:do_parsetrans/2, [info,?msg], Times). - -lager_parsetrans_handler_complete(Config) -> - handler_complete(Config, ?FUNCTION_NAME, fun lager_helper:do_parsetrans/2, [error,?msg]). - - -profile(Config) -> - Times = ?TIMES, - %% fprof:apply(fun repeated_apply/3,[fun lager_helper:do_func/2,[error,?msg],Times]), - fprof:apply(fun repeated_apply/3,[fun do_log_func/2,[error,?msg],Times]), - ok = fprof:profile(), - ok = fprof:analyse(dest,"../fprof.analyse"), - ok. - -%%%----------------------------------------------------------------- -%%% Internal -%% Handler -log(_Log,_Config) -> - ok. - -format(Log=#{meta:=#{pid:=Pid}},Config) when is_pid(Pid) -> - String = ?DEFAULT_FORMATTER:format(Log,Config), - Pid ! done, - String; -format(Log=#{meta:=#{pid:=PidStr}},Config) when is_list(PidStr) -> - String = ?DEFAULT_FORMATTER:format(Log,Config), - list_to_pid(PidStr) ! done, - String. - -handler_complete(Config, TC, Fun, Args) -> - Times = ?TIMES, - Start = os:perf_counter(microsecond), - repeated_apply(Fun, Args, Times), - MSecs = wait_for_done(Start,Times), - calc_and_report(Config,TC,MSecs,Times). - -wait_for_done(Start,0) -> - os:perf_counter(microsecond) - Start; -wait_for_done(Start,N) -> - receive - done -> - wait_for_done(Start,N-1) - after 20000 -> - ct:fail("missing " ++ integer_to_list(N) ++ " replys") - end. - -%%%----------------------------------------------------------------- -%%% Benchmark stuff -run_benchmark(Config,Tag,Fun,Args,Times) -> - _ = erlang:apply(Fun, Args), % apply once to ensure level is cached - MSecs = measure_repeated_op(Fun, Args, Times), - %% fprof:profile(), - %% fprof:analyse(dest,"../"++atom_to_list(Tag)++".prof"), - calc_and_report(Config,Tag,MSecs,Times). - -measure_repeated_op(Fun, Args, Times) -> - Start = os:perf_counter(microsecond), - %% fprof:apply(fun repeated_apply/3, [Fun, Args, Times]), - repeated_apply(Fun, Args, Times), - os:perf_counter(microsecond) - Start. - -repeated_apply(_F, _Args, Times) when Times =< 0 -> - ok; -repeated_apply(F, Args, Times) -> - erlang:apply(F, Args), - repeated_apply(F, Args, Times - 1). - -calc_and_report(Config,Tag,MSecs,Times) -> - IOPS = trunc(Times * (1000000 / MSecs)), - ct_event:notify(#event{ name = benchmark_data, data = [{value,IOPS}] }), - ct:print("~p:~n~p IOPS, ~p us", [Tag, IOPS, MSecs]), - ct:comment("~p IOPS, ~p us", [IOPS, MSecs]), - Bench = case ?config(saved_config,Config) of - {_,[{bench,B}]} -> B; - undefined -> [] - end, - {save_config,[{bench,[{Tag,IOPS,MSecs}|Bench]}]}. - -remove_all_handlers() -> - Hs = logger:get_handler_config(), - [logger:remove_handler(Id) || #{id:=Id} <- Hs], - Hs. - -add_all_handlers(Hs) -> - [logger:add_handler(Id,Mod,Config) || {Id,Mod,Config} <- Hs], - ok. - -%%%----------------------------------------------------------------- -%%% Call logger in different ways -do_log_func(Level,Msg) -> - logger:Level(Msg,[],?meta). - -do_log_macro(error,Msg) -> - ?LOG_ERROR(Msg,[]); -do_log_macro(info,Msg) -> - ?LOG_INFO(Msg,[]); -do_log_macro(debug,Msg) -> - ?LOG_DEBUG(Msg,[]). - -do_error_logger(error,Msg) -> - error_logger:error_msg(Msg,[]); -do_error_logger(info,Msg) -> - error_logger:info_msg(Msg,[]). - -%%%----------------------------------------------------------------- -%%% -print_compare_chart(Group,Bench) -> - io:format("~-20s~12s~12s~12s~12s", - ["Microseconds:","Log","Drop","HDrop","Complete"]), - io:format(user,"~-20s~12s~12s~12s~12s~n", - ["Microseconds:","Log","Drop","HDrop","Complete"]), - {Log,Drop,HDrop,Comp} = sort_bench(Bench,[],[],[],[]), - print_compare_chart(Log,Drop,HDrop,Comp), - io:format(user,"~n",[]), - maybe_print_csv_files(Group, - [{log,Log},{drop,Drop},{hdrop,HDrop},{comp,Comp}]). - -print_compare_chart([{What,LIOPS,LMSecs}|Log], - [{What,DIOPS,DMSecs}|Drop], - [{What,HIOPS,HMSecs}|HDrop], - [{What,CIOPS,CMSecs}|Comp]) -> - io:format("~-20w~12w~12w~12w~12w",[What,LMSecs,DMSecs,HMSecs,CMSecs]), - io:format(user,"~-20w~12w~12w~12w~12w~n",[What,LMSecs,DMSecs,HMSecs,CMSecs]), - print_compare_chart(Log,Drop,HDrop,Comp); -print_compare_chart([{What,LIOPS,LMSecs}|Log], - [{What,DIOPS,DMSecs}|Drop], - [{What,HIOPS,HMSecs}|HDrop], - []=Comp) -> - io:format("~-20w~12w~12w~12w",[What,LMSecs,DMSecs,HMSecs]), - io:format(user,"~-20w~12w~12w~12w~n",[What,LMSecs,DMSecs,HMSecs]), - print_compare_chart(Log,Drop,HDrop,Comp); -print_compare_chart([],[],[],[]) -> - ok; -print_compare_chart(Log,Drop,HDrop,Comp) -> - ct:fail({Log,Drop,HDrop,Comp}). - -sort_bench([{TC,IOPS,MSecs}|Bench],Log,Drop,HDrop,Comp) -> - case lists:member(TC,?NO_COMPARE) of - true -> - sort_bench(Bench,Log,Drop,HDrop,Comp); - false -> - TCStr = atom_to_list(TC), - {What,Type} = - case re:run(TCStr,"(.*)_(drop.*)", - [{capture,all_but_first,list}]) of - {match,[WhatStr,TypeStr]} -> - {list_to_atom(WhatStr),list_to_atom(TypeStr)}; - nomatch -> - case re:run(TCStr,"(.*)_(handler_complete.*)", - [{capture,all_but_first,list}]) of - {match,[WhatStr,TypeStr]} -> - {list_to_atom(WhatStr),list_to_atom(TypeStr)}; - nomatch -> - {TC,log} - end - end, - case Type of - log -> - sort_bench(Bench,[{What,IOPS,MSecs}|Log],Drop,HDrop,Comp); - drop -> - sort_bench(Bench,Log,[{What,IOPS,MSecs}|Drop],HDrop,Comp); - drop_by_handler -> - sort_bench(Bench,Log,Drop,[{What,IOPS,MSecs}|HDrop],Comp); - handler_complete -> - sort_bench(Bench,Log,Drop,HDrop,[{What,IOPS,MSecs}|Comp]) - end - end; -sort_bench([],Log,Drop,HDrop,Comp) -> - {lists:keysort(1,Log), - lists:keysort(1,Drop), - lists:keysort(1,HDrop), - lists:keysort(1,Comp)}. - -maybe_print_csv_files(Group,Data) -> - case ct:get_config({print_csv,Group}) of - undefined -> - ok; - Cfg -> - Path = proplists:get_value(path,Cfg,".."), - Files = [begin - File = filename:join(Path,F)++".csv", - case filelib:is_regular(File) of - true -> - {ok,Fd} = file:open(File,[append]), - Fd; - false -> - {ok,Fd} = file:open(File,[write]), - ok = file:write(Fd, - "error_logger,lager_log," - "lager_parsetrans,logger_log," - "logger_macro\n"), - Fd - end - end || {F,_} <- Data], - [print_csv_file(F,D) || {F,D} <- lists:zip(Files,Data)], - [file:close(Fd) || Fd <- Files], - ok - end. - -print_csv_file(Fd,{_,Data}) -> - AllIOPS = [integer_to_list(IOPS) || {_,IOPS,_} <- Data], - ok = file:write(Fd,lists:join(",",AllIOPS)++"\n"). - -have_lager() -> - code:ensure_loaded(lager) == {module,lager}. - -make(Dir) -> - {ok,Cwd} = file:get_cwd(), - ok = file:set_cwd(Dir), - up_to_date = make:all([load]), - ok = file:set_cwd(Cwd), - code:add_path(Dir). diff --git a/lib/kernel/test/logger_bench_SUITE_data/Emakefile b/lib/kernel/test/logger_bench_SUITE_data/Emakefile deleted file mode 100644 index 85c82bdaab..0000000000 --- a/lib/kernel/test/logger_bench_SUITE_data/Emakefile +++ /dev/null @@ -1 +0,0 @@ -{['lager_helper'],[{outdir,"."},debug_info,{i,"/home/uabshan/Work/git/otp/lib/kernel/src"},{i,"/home/uabshan/Work/git/otp/lib/kernel/include"}]}. diff --git a/lib/kernel/test/logger_bench_SUITE_data/lager_helper.erl b/lib/kernel/test/logger_bench_SUITE_data/lager_helper.erl deleted file mode 100644 index 296ced4276..0000000000 --- a/lib/kernel/test/logger_bench_SUITE_data/lager_helper.erl +++ /dev/null @@ -1,73 +0,0 @@ --module(lager_helper). - --compile(export_all). --compile({parse_transform,lager_transform}). - --include_lib("kernel/src/logger_internal.hrl"). - -start() -> - application:load(lager), - application:set_env(lager, error_logger_redirect, false), - application:set_env(lager, async_threshold, 100010), - application:set_env(lager, async_threshold_window, 100), - application:set_env(lager,handlers,[{?MODULE,[{level,error}]}]), - lager:start(). - -stop() -> - application:stop(lager). - -do_func(Level,Msg) -> - lager:log(Level,[{pid,self()}],Msg,[]). - -do_parsetrans(error,Msg) -> - lager:error(Msg,[]); -do_parsetrans(info,Msg) -> - lager:info(Msg,[]). - -%%%----------------------------------------------------------------- -%%% Dummy handler for lager --record(state, {level :: {'mask', integer()}, - formatter :: atom(), - format_config :: any()}). -init(Opts) -> - Level = proplists:get_value(level,Opts,info), - Formatter = proplists:get_value(formatter,Opts,logger_bench_SUITE), - FormatConfig = proplists:get_value(format_config,Opts,?DEFAULT_FORMAT_CONFIG), - {ok,#state{level=lager_util:config_to_mask(Level), - formatter=Formatter, - format_config=FormatConfig}}. - -handle_call(get_loglevel, #state{level=Level} = State) -> - {ok, Level, State}; -handle_call({set_loglevel, Level}, State) -> - try lager_util:config_to_mask(Level) of - Levels -> - {ok, ok, State#state{level=Levels}} - catch - _:_ -> - {ok, {error, bad_log_level}, State} - end; -handle_call(_Request, State) -> - {ok, ok, State}. - -handle_event({log, Message}, - #state{level=L,formatter=Formatter,format_config=FormatConfig} = State) -> - case lager_util:is_loggable(Message, L, ?MODULE) of - true -> - Metadata = - case maps:from_list(lager_msg:metadata(Message)) of - Meta = #{pid:=Pid} when is_pid(Pid) -> - Meta; - Meta = #{pid:=PidStr} when is_list(PidStr) -> - Meta - end, - Log = #{level=>lager_msg:severity(Message), - msg=>{report,lager_msg:message(Message)}, - meta=>Metadata}, - io:put_chars(user, Formatter:format(Log,FormatConfig)), - {ok, State}; - false -> - {ok, State} - end; -handle_event(_Event, State) -> - {ok, State}. -- cgit v1.2.3 From 4d3fe8ee8d5b079df6172232d4f6864286048028 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 8 Jun 2018 17:53:47 +0200 Subject: [logger] Split config() in two: primary_config() and handler_config() --- lib/kernel/src/error_logger.erl | 8 +++--- lib/kernel/src/logger.erl | 50 +++++++++++++++++++----------------- lib/kernel/src/logger_disk_log_h.erl | 8 +++--- lib/kernel/src/logger_h_common.erl | 2 +- lib/kernel/src/logger_std_h.erl | 4 +-- 5 files changed, 38 insertions(+), 34 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index a0bf87796b..918a915729 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -101,8 +101,8 @@ stop() -> %%%----------------------------------------------------------------- %%% Callbacks for logger --spec adding_handler(logger:config()) -> - {ok,logger:config()} | {error,term()}. +-spec adding_handler(logger:handler_config()) -> + {ok,logger:handler_config()} | {error,term()}. adding_handler(#{id:=?MODULE}=Config) -> case start() of ok -> @@ -111,12 +111,12 @@ adding_handler(#{id:=?MODULE}=Config) -> Error end. --spec removing_handler(logger:config()) -> ok. +-spec removing_handler(logger:handler_config()) -> ok. removing_handler(#{id:=?MODULE}) -> stop(), ok. --spec log(logger:log_event(),logger:config()) -> ok. +-spec log(logger:log_event(),logger:handler_config()) -> ok. log(#{level:=Level,msg:=Msg,meta:=Meta},_Config) -> do_log(Level,Msg,Meta). diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index e0832bf31c..7f10c27bae 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -95,21 +95,25 @@ filter_return()),filter_arg()}. -type filter_arg() :: term(). -type filter_return() :: stop | ignore | log_event(). --type config() :: #{id => handler_id(), - config => map(), - level => level() | all | none, - module => module(), - filter_default => log | stop, - filters => [{filter_id(),filter()}], - formatter => {module(),formatter_config()}, - atom() => term()}. +-type primary_config() :: #{level => level() | all | none, + filter_default => log | stop, + filters => [{filter_id(),filter()}]}. +-type handler_config() :: #{id => handler_id(), + config => term(), + level => level() | all | none, + module => module(), + filter_default => log | stop, + filters => [{filter_id(),filter()}], + formatter => {module(),formatter_config()}, + atom() => term()}. -type timestamp() :: integer(). -type formatter_config() :: #{atom() => term()}. --type config_handler() :: {handler, handler_id(), module(), config()}. +-type config_handler() :: {handler, handler_id(), module(), handler_config()}. --export_type([log_event/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, +-export_type([log_event/0,level/0,report/0,msg_fun/0,metadata/0, + primary_config/0,handler_config/0,handler_id/0, + filter_id/0,filter/0,filter_arg/0,filter_return/0, config_handler/0,formatter_config/0]). %%%----------------------------------------------------------------- @@ -340,7 +344,7 @@ remove_handler_filter(HandlerId,FilterId) -> -spec add_handler(HandlerId,Module,Config) -> ok | {error,term()} when HandlerId :: handler_id(), Module :: module(), - Config :: config(). + Config :: handler_config(). add_handler(HandlerId,Module,Config) -> logger_server:add_handler(HandlerId,Module,Config). @@ -356,7 +360,7 @@ set_primary_config(Key,Value) -> logger_server:set_config(primary,Key,Value). -spec set_primary_config(Config) -> ok | {error,term()} when - Config :: config(). + Config :: primary_config(). set_primary_config(Config) -> logger_server:set_config(primary,Config). @@ -369,30 +373,30 @@ set_handler_config(HandlerId,Key,Value) -> -spec set_handler_config(HandlerId,Config) -> ok | {error,term()} when HandlerId :: handler_id(), - Config :: config(). + Config :: handler_config(). set_handler_config(HandlerId,Config) -> logger_server:set_config(HandlerId,Config). -spec update_primary_config(Config) -> ok | {error,term()} when - Config :: config(). + Config :: primary_config(). update_primary_config(Config) -> logger_server:update_config(primary,Config). -spec update_handler_config(HandlerId,Config) -> ok | {error,term()} when HandlerId :: handler_id(), - Config :: config(). + Config :: handler_config(). update_handler_config(HandlerId,Config) -> logger_server:update_config(HandlerId,Config). -spec get_primary_config() -> Config when - Config :: config(). + Config :: primary_config(). get_primary_config() -> {ok,Config} = logger_config:get(?LOGGER_TABLE,primary), maps:remove(handlers,Config). -spec get_handler_config(HandlerId) -> {ok,Config} | {error,term()} when HandlerId :: handler_id(), - Config :: config(). + Config :: handler_config(). get_handler_config(HandlerId) -> case logger_config:get(?LOGGER_TABLE,HandlerId) of {ok,{_,Config}} -> @@ -402,7 +406,7 @@ get_handler_config(HandlerId) -> end. -spec get_handler_config() -> [Config] when - Config :: config(). + Config :: handler_config(). get_handler_config() -> [begin {ok,Config} = get_handler_config(HandlerId), @@ -417,14 +421,14 @@ get_handler_ids() -> -spec update_formatter_config(HandlerId,FormatterConfig) -> ok | {error,term()} when - HandlerId :: config(), + HandlerId :: handler_id(), FormatterConfig :: formatter_config(). update_formatter_config(HandlerId,FormatterConfig) -> logger_server:update_formatter_config(HandlerId,FormatterConfig). -spec update_formatter_config(HandlerId,Key,Value) -> ok | {error,term()} when - HandlerId :: config(), + HandlerId :: handler_id(), Key :: atom(), Value :: term(). update_formatter_config(HandlerId,Key,Value) -> @@ -512,8 +516,8 @@ unset_process_metadata() -> _ = erase(?LOGGER_META_KEY), ok. --spec get_config() -> #{primary=>config(), - handlers=>[config()], +-spec get_config() -> #{primary=>primary_config(), + handlers=>[handler_config()], module_levels=>[{module(),level() | all | none}]}. get_config() -> #{primary=>get_primary_config(), diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index e4b583c269..2676b50580 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -50,7 +50,7 @@ %%% handler process gets added (as a result of calling add/3). -spec start_link(Name, Config, HandlerState) -> {ok,Pid} | {error,Reason} when Name :: atom(), - Config :: logger:config(), + Config :: logger:handler_config(), HandlerState :: map(), Pid :: pid(), Reason :: term(). @@ -248,7 +248,7 @@ swap_buffer(Name, Buffer) -> %%% Log a string or report -spec log(LogEvent, Config) -> ok | dropped when LogEvent :: logger:log_event(), - Config :: logger:config(). + Config :: logger:handler_config(). log(LogEvent, Config = #{id := Name, config := #{handler_pid := HPid, @@ -484,8 +484,8 @@ get_init_state() -> %%% exist if the handler is registered with logger (and should not %%% exist if the handler is not registered). %%% -%%% Config is the logger:config() map containing a sub map with any of -%%% the following associations: +%%% Config is the logger:handler_config() map containing a sub map +%%% with any of the following associations: %%% %%% Config = #{disk_log_opts => #{file => file:filename(), %%% max_no_bytes => integer(), diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl index b145043522..d556938f02 100644 --- a/lib/kernel/src/logger_h_common.erl +++ b/lib/kernel/src/logger_h_common.erl @@ -43,7 +43,7 @@ %%% Covert log data on any form to binary -spec log_to_binary(LogEvent,Config) -> LogString when LogEvent :: logger:log_event(), - Config :: logger:config(), + Config :: logger:handler_config(), LogString :: binary(). log_to_binary(#{msg:={report,_},meta:=#{report_cb:=_}}=Log,Config) -> do_log_to_binary(Log,Config); diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index e087178211..827503ddcf 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -51,7 +51,7 @@ %%% handler process gets added -spec start_link(Name, Config, HandlerState) -> {ok,Pid} | {error,Reason} when Name :: atom(), - Config :: logger:config(), + Config :: logger:handler_config(), HandlerState :: map(), Pid :: pid(), Reason :: term(). @@ -230,7 +230,7 @@ swap_buffer(Name, Buffer) -> %%% Log a string or report -spec log(LogEvent, Config) -> ok | dropped when LogEvent :: logger:log_event(), - Config :: logger:config(). + Config :: logger:handler_config(). log(LogEvent, Config = #{id := Name, config := #{handler_pid := HPid, -- cgit v1.2.3 From e721674309b683f37306e9dd206aa8521fa871cf Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 8 Jun 2018 18:15:06 +0200 Subject: [logger] Removed unused code in logger_std_h and logger_disk_log_h --- lib/kernel/src/logger_disk_log_h.erl | 35 ++--------------------------------- lib/kernel/src/logger_std_h.erl | 36 ++---------------------------------- 2 files changed, 4 insertions(+), 67 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index 2676b50580..8a456f72b1 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -33,9 +33,7 @@ terminate/2, code_change/3]). %% logger callbacks --export([log/2, - adding_handler/1, removing_handler/1, - changing_config/2, swap_buffer/2]). +-export([log/2, adding_handler/1, removing_handler/1, changing_config/2]). %% handler internal -export([log_handler_info/4]). @@ -122,14 +120,7 @@ adding_handler(#{id:=Name}=Config) -> HState = maps:merge(get_init_state(), HConfig), case logger_h_common:overload_levels_ok(HState) of true -> - case start(Name, Config1, HState) of - {ok,Config2} -> - %% Make sure wait_for_buffer is not stored, so we - %% won't hang and wait for buffer on a restart - {ok, maps:remove(wait_for_buffer,Config2)}; - Error -> - Error - end; + start(Name, Config1, HState); false -> #{sync_mode_qlen := SMQL, drop_mode_qlen := DMQL, @@ -234,16 +225,6 @@ check_my_config([]) -> removing_handler(#{id:=Name}) -> stop(Name). -%%%----------------------------------------------------------------- -%%% Get buffer when swapping from simple handler -swap_buffer(Name, Buffer) -> - case whereis(?name_to_reg_name(?MODULE,Name)) of - undefined -> - ok; - Pid -> - Pid ! {buffer,Buffer} - end. - %%%----------------------------------------------------------------- %%% Log a string or report -spec log(LogEvent, Config) -> ok | dropped when @@ -319,18 +300,6 @@ init([Name, Config = #{config := HConfig, disk_log_opts := LogOpts}, proc_lib:init_ack(Error) end. -enter_loop(#{wait_for_buffer:=true}=Config,State) -> - State1 = - receive - {buffer,Buffer} -> - lists:foldl( - fun(Log,S) -> - Bin = logger_h_common:log_to_binary(Log,Config), - {_,S1} = do_log(Bin,cast,S), - S1 - end, State, Buffer) - end, - gen_server:enter_loop(?MODULE,[],State1); enter_loop(_Config,State) -> gen_server:enter_loop(?MODULE,[],State). diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index 827503ddcf..29efde52a9 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -35,8 +35,7 @@ terminate/2, code_change/3]). %% logger callbacks --export([log/2, adding_handler/1, removing_handler/1, - changing_config/2, swap_buffer/2]). +-export([log/2, adding_handler/1, removing_handler/1, changing_config/2]). %% handler internal -export([log_handler_info/4]). @@ -123,14 +122,7 @@ adding_handler(#{id:=Name}=Config) -> HState = maps:merge(get_init_state(), HConfig), case logger_h_common:overload_levels_ok(HState) of true -> - case start(Name, Config1, HState) of - {ok,Config2} -> - %% Make sure wait_for_buffer is not stored, so we - %% won't hang and wait for buffer on a restart - {ok, maps:remove(wait_for_buffer,Config2)}; - Error -> - Error - end; + start(Name, Config1, HState); false -> #{sync_mode_qlen := SMQL, drop_mode_qlen := DMQL, @@ -216,16 +208,6 @@ check_my_config([]) -> removing_handler(#{id:=Name}) -> stop(Name). -%%%----------------------------------------------------------------- -%%% Get buffer when swapping from simple handler -swap_buffer(Name, Buffer) -> - case whereis(?name_to_reg_name(?MODULE,Name)) of - undefined -> - ok; - Pid -> - Pid ! {buffer,Buffer} - end. - %%%----------------------------------------------------------------- %%% Log a string or report -spec log(LogEvent, Config) -> ok | dropped when @@ -305,20 +287,6 @@ do_init(Name, Type) -> Error end. -enter_loop(#{wait_for_buffer:=true}=Config,State) -> - State1 = - receive - {buffer,Buffer} -> - lists:foldl( - fun(Log,S) -> - Bin = logger_h_common:log_to_binary(Log,Config), - {_,S1} = do_log(Bin,cast,S), - S1 - end, - State, - Buffer) - end, - gen_server:enter_loop(?MODULE,[],State1); enter_loop(_Config,State) -> gen_server:enter_loop(?MODULE,[],State). -- cgit v1.2.3 From 98371223a4056459957552f1243f499c4003628e Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Fri, 8 Jun 2018 18:41:16 +0200 Subject: [logger] Move the disk log options to the handler config map Conflicts: lib/kernel/src/logger_disk_log_h.erl --- lib/kernel/src/logger.erl | 3 +- lib/kernel/src/logger_disk_log_h.erl | 135 ++++++++++++++-------------- lib/kernel/src/logger_std_h.erl | 52 +++++------ lib/kernel/test/logger_disk_log_h_SUITE.erl | 100 +++++++++++---------- lib/kernel/test/logger_env_var_SUITE.erl | 4 +- lib/kernel/test/logger_simple_h_SUITE.erl | 2 +- lib/kernel/test/logger_std_h_SUITE.erl | 2 +- 7 files changed, 151 insertions(+), 147 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 7f10c27bae..7d121f22fe 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -104,8 +104,7 @@ module => module(), filter_default => log | stop, filters => [{filter_id(),filter()}], - formatter => {module(),formatter_config()}, - atom() => term()}. + formatter => {module(),formatter_config()}}. -type timestamp() :: integer(). -type formatter_config() :: #{atom() => term()}. diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index 8a456f72b1..a074d0210e 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -133,12 +133,25 @@ adding_handler(#{id:=Name}=Config) -> %%%----------------------------------------------------------------- %%% Updating handler config -changing_config(OldConfig=#{id:=Name, disk_log_opts:=DLOpts, config:=HConfig}, - NewConfig=#{id:=Name, disk_log_opts:=DLOpts}) -> +changing_config(OldConfig = #{id:=Name, config:=OldHConfig}, + NewConfig = #{id:=Name, config:=NewHConfig}) -> + #{type:=Type, file:=File, max_no_files:=MaxFs, + max_no_bytes:=MaxBytes} = OldHConfig, + case NewHConfig of + #{type:=Type, file:=File, max_no_files:=MaxFs, + max_no_bytes:=MaxBytes} -> + changing_config1(OldConfig, NewConfig); + _ -> + {error,{illegal_config_change,OldConfig,NewConfig}} + end; +changing_config(OldConfig, NewConfig) -> + {error,{illegal_config_change,OldConfig,NewConfig}}. + +changing_config1(OldConfig=#{config:=OldHConfig}, NewConfig) -> case check_config(changing, NewConfig) of {ok,NewConfig1 = #{config:=NewHConfig}} -> #{handler_pid:=HPid, - mode_tab:=ModeTab} = HConfig, + mode_tab:=ModeTab} = OldHConfig, NewHConfig1 = NewHConfig#{handler_pid=>HPid, mode_tab=>ModeTab}, NewConfig2 = NewConfig1#{config=>NewHConfig1}, @@ -151,73 +164,58 @@ changing_config(OldConfig=#{id:=Name, disk_log_opts:=DLOpts, config:=HConfig}, end; Error -> Error - end; -changing_config(OldConfig, NewConfig) -> - {error,{illegal_config_change,OldConfig,NewConfig}}. + end. check_config(adding, #{id:=Name}=Config) -> - %% Merge in defaults on handler level - LogOpts0 = maps:get(disk_log_opts, Config, #{}), - LogOpts = merge_default_logopts(Name, LogOpts0), - case check_log_opts(maps:to_list(LogOpts)) of + %% merge handler specific config data + HConfig = merge_default_logopts(Name, maps:get(config, Config, #{})), + case check_h_config(maps:to_list(HConfig)) of ok -> - MyConfig = maps:get(config, Config, #{}), - case check_my_config(maps:to_list(MyConfig)) of - ok -> - {ok,Config#{disk_log_opts=>LogOpts, - config=>MyConfig}}; - Error -> - Error - end; + {ok,Config#{config=>HConfig}}; Error -> Error end; check_config(changing, Config) -> - MyConfig = maps:get(config, Config, #{}), - case check_my_config(maps:to_list(MyConfig)) of + HConfig = maps:get(config, Config, #{}), + case check_h_config(maps:to_list(HConfig)) of ok -> {ok,Config}; Error -> Error end. -merge_default_logopts(Name, LogOpts) -> - Type = maps:get(type, LogOpts, wrap), +merge_default_logopts(Name, HConfig) -> + Type = maps:get(type, HConfig, wrap), {DefaultNoFiles,DefaultNoBytes} = case Type of halt -> {undefined,infinity}; _wrap -> {10,1048576} end, {ok,Dir} = file:get_cwd(), - Default = #{file => filename:join(Dir,Name), - max_no_files => DefaultNoFiles, - max_no_bytes => DefaultNoBytes, - type => Type}, - maps:merge(Default,LogOpts). - -check_log_opts([{file,File}|Opts]) when is_list(File) -> - check_log_opts(Opts); -check_log_opts([{max_no_files,undefined}|Opts]) -> - check_log_opts(Opts); -check_log_opts([{max_no_files,N}|Opts]) when is_integer(N), N>0 -> - check_log_opts(Opts); -check_log_opts([{max_no_bytes,infinity}|Opts]) -> - check_log_opts(Opts); -check_log_opts([{max_no_bytes,N}|Opts]) when is_integer(N), N>0 -> - check_log_opts(Opts); -check_log_opts([{type,Type}|Opts]) when Type==wrap; Type==halt -> - check_log_opts(Opts); -check_log_opts([Invalid|_]) -> - {error,{invalid_config,disk_log_opt,Invalid}}; -check_log_opts([]) -> - ok. - -check_my_config([Other | Config]) -> + Defaults = #{file => filename:join(Dir,Name), + max_no_files => DefaultNoFiles, + max_no_bytes => DefaultNoBytes, + type => Type}, + maps:merge(Defaults, HConfig). + +check_h_config([{file,File}|Config]) when is_list(File) -> + check_h_config(Config); +check_h_config([{max_no_files,undefined}|Config]) -> + check_h_config(Config); +check_h_config([{max_no_files,N}|Config]) when is_integer(N), N>0 -> + check_h_config(Config); +check_h_config([{max_no_bytes,infinity}|Config]) -> + check_h_config(Config); +check_h_config([{max_no_bytes,N}|Config]) when is_integer(N), N>0 -> + check_h_config(Config); +check_h_config([{type,Type}|Config]) when Type==wrap; Type==halt -> + check_h_config(Config); +check_h_config([Other | Config]) -> case logger_h_common:check_common_config(Other) of valid -> - check_my_config(Config); + check_h_config(Config); invalid -> {error,{invalid_config,?MODULE,Other}} end; -check_my_config([]) -> +check_h_config([]) -> ok. %%%----------------------------------------------------------------- @@ -244,16 +242,22 @@ log(LogEvent, Config = #{id := Name, %%% gen_server callbacks %%%=================================================================== -init([Name, Config = #{config := HConfig, disk_log_opts := LogOpts}, +init([Name, + Config = #{config := HConfig = #{file:=File, + type:=Type, + max_no_bytes:=MNB, + max_no_files:=MNF}}, State = #{dl_sync_int := DLSyncInt}]) -> + register(?name_to_reg_name(?MODULE,Name), self()), process_flag(trap_exit, true), process_flag(message_queue_data, off_heap), ?init_test_hooks(), ?start_observation(Name), - - case open_disk_log(Name, LogOpts) of + + LogOpts = #{file=>File, type=>Type, max_no_bytes=>MNB, max_no_files=>MNF}, + case open_disk_log(Name, File, Type, MNB, MNF) of ok -> try ets:new(Name, [public]) of ModeTab -> @@ -453,22 +457,21 @@ get_init_state() -> %%% exist if the handler is registered with logger (and should not %%% exist if the handler is not registered). %%% -%%% Config is the logger:handler_config() map containing a sub map -%%% with any of the following associations: +%%% Config is the logger:handler_config() map. Handler specific parameters +%%% should be provided with a sub map associated with a key named +%%% 'config', e.g: %%% -%%% Config = #{disk_log_opts => #{file => file:filename(), -%%% max_no_bytes => integer(), -%%% max_no_files => integer(), -%%% type => wrap | halt}}. +%%% Config = #{config => #{sync_mode_qlen => 50} %%% -%%% This map will be merged with the logger configuration data for -%%% the disk_log LogName. If type == halt, then max_no_files is -%%% ignored. +%%% The 'config' sub map will also contain parameters for configuring +%%% the disk_log: %%% -%%% Handler specific config should be provided with a sub map associated -%%% with a key named 'config', e.g: +%%% Config = #{config => #{file => file:filename(), +%%% max_no_bytes => integer(), +%%% max_no_files => integer(), +%%% type => wrap | halt}}. %%% -%%% Config = #{config => #{sync_mode_qlen => 50} +%%% If type == halt, then max_no_files is ignored. %%% %%% The disk_log handler process is linked to logger_sup, which is %%% part of the kernel application's supervision tree. @@ -640,11 +643,7 @@ log_handler_info(Name, Format, Args, State) -> ok. -open_disk_log(Name, LogOpts) -> - #{file := File, - max_no_bytes := MaxNoBytes, - max_no_files := MaxNoFiles, - type := Type} = LogOpts, +open_disk_log(Name, File, Type, MaxNoBytes, MaxNoFiles) -> case filelib:ensure_dir(File) of ok -> Size = diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index 29efde52a9..ce9daa50ab 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -135,17 +135,17 @@ adding_handler(#{id:=Name}=Config) -> %%%----------------------------------------------------------------- %%% Updating handler config -changing_config(OldConfig=#{id:=Name, config:=HConfig}, +changing_config(OldConfig=#{id:=Name, config:=OldHConfig}, NewConfig=#{id:=Name}) -> - #{type:=Type, handler_pid:=HPid, mode_tab:=ModeTab} = HConfig, - MyConfig = maps:get(config, NewConfig, #{}), - case maps:get(type, MyConfig, Type) of + #{type:=Type, handler_pid:=HPid, mode_tab:=ModeTab} = OldHConfig, + NewHConfig = maps:get(config, NewConfig, #{}), + case maps:get(type, NewHConfig, Type) of Type -> - MyConfig1 = MyConfig#{type=>Type, - handler_pid=>HPid, - mode_tab=>ModeTab}, + NewHConfig1 = NewHConfig#{type=>Type, + handler_pid=>HPid, + mode_tab=>ModeTab}, changing_config1(HPid, OldConfig, - NewConfig#{config=>MyConfig1}); + NewConfig#{config=>NewHConfig1}); _ -> {error,{illegal_config_change,OldConfig,NewConfig}} end; @@ -168,38 +168,38 @@ changing_config1(HPid, OldConfig, NewConfig) -> check_config(adding, Config) -> %% Merge in defaults on handler level - MyConfig0 = maps:get(config, Config, #{}), - MyConfig = maps:merge(#{type => standard_io}, - MyConfig0), - case check_my_config(maps:to_list(MyConfig)) of + HConfig0 = maps:get(config, Config, #{}), + HConfig = maps:merge(#{type => standard_io}, + HConfig0), + case check_h_config(maps:to_list(HConfig)) of ok -> - {ok,Config#{config=>MyConfig}}; + {ok,Config#{config=>HConfig}}; Error -> Error end; check_config(changing, Config) -> - MyConfig = maps:get(config, Config, #{}), - case check_my_config(maps:to_list(MyConfig)) of + HConfig = maps:get(config, Config, #{}), + case check_h_config(maps:to_list(HConfig)) of ok -> {ok,Config}; Error -> Error end. -check_my_config([{type,Type} | Config]) when Type == standard_io; - Type == standard_error -> - check_my_config(Config); -check_my_config([{type,{file,File}} | Config]) when is_list(File) -> - check_my_config(Config); -check_my_config([{type,{file,File,Modes}} | Config]) when is_list(File), - is_list(Modes) -> - check_my_config(Config); -check_my_config([Other | Config]) -> +check_h_config([{type,Type} | Config]) when Type == standard_io; + Type == standard_error -> + check_h_config(Config); +check_h_config([{type,{file,File}} | Config]) when is_list(File) -> + check_h_config(Config); +check_h_config([{type,{file,File,Modes}} | Config]) when is_list(File), + is_list(Modes) -> + check_h_config(Config); +check_h_config([Other | Config]) -> case logger_h_common:check_common_config(Other) of valid -> - check_my_config(Config); + check_h_config(Config); invalid -> {error,{invalid_config,?MODULE,Other}} end; -check_my_config([]) -> +check_h_config([]) -> ok. diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index c3b8237f82..5f7b505ecb 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -253,10 +253,10 @@ disk_log_opts(Config) -> default_formatter(Config) -> PrivDir = ?config(priv_dir,Config), LogFile = filename:join(PrivDir,atom_to_list(?FUNCTION_NAME)), - HConfig = #{disk_log_opts => #{file=>LogFile}, - filter_default=>log}, + HandlerConfig = #{config => #{file=>LogFile}, + filter_default=>log}, ct:pal("Log: ~p", [LogFile]), - ok = logger:add_handler(?MODULE, logger_disk_log_h, HConfig), + ok = logger:add_handler(?MODULE, logger_disk_log_h, HandlerConfig), ok = logger:set_handler_config(?MODULE,formatter, {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}), LogName = lists:concat([LogFile, ".1"]), @@ -290,10 +290,10 @@ errors(Config) -> PrivDir = ?config(priv_dir,Config), Name1 = list_to_atom(lists:concat([?FUNCTION_NAME,"_1"])), LogFile1 = filename:join(PrivDir,Name1), - HConfig = #{disk_log_opts=>#{file=>LogFile1}, - filter_default=>log, - formatter=>{?MODULE,self()}}, - ok = logger:add_handler(Name1, logger_disk_log_h, HConfig), + HandlerConfig = #{config=>#{file=>LogFile1}, + filter_default=>log, + formatter=>{?MODULE,self()}}, + ok = logger:add_handler(Name1, logger_disk_log_h, HandlerConfig), {error,{already_exist,Name1}} = logger:add_handler(Name1, logger_disk_log_h, #{}), @@ -302,7 +302,7 @@ errors(Config) -> {error,{illegal_config_change,_,_}} = logger:set_handler_config(Name1, - disk_log_opts, + config, #{file=>LogFile1, type=>halt}), {error,{illegal_config_change,_,_}} = @@ -321,11 +321,11 @@ formatter_fail(Config) -> Name = ?FUNCTION_NAME, LogFile = filename:join(PrivDir,Name), ct:pal("Log = ~p", [LogFile]), - HConfig = #{disk_log_opts => #{file=>LogFile}, - filter_default=>stop, - filters=>?DEFAULT_HANDLER_FILTERS([?MODULE])}, + HandlerConfig = #{config => #{file=>LogFile}, + filter_default=>stop, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE])}, %% no formatter! - logger:add_handler(Name, logger_disk_log_h, HConfig), + logger:add_handler(Name, logger_disk_log_h, HandlerConfig), Pid = whereis(h_proc_name(Name)), true = is_pid(Pid), H = logger:get_handler_ids(), @@ -387,20 +387,21 @@ config_fail(_Config) -> formatter=>{?MODULE,self()}}), %% can't change the disk log options for a log already in use {error,{illegal_config_change,_,_}} = - logger:set_handler_config(?MODULE,disk_log_opts, + logger:set_handler_config(?MODULE,config, #{max_no_files=>2}), %% can't change name of an existing handler {error,{illegal_config_change,_,_}} = logger:set_handler_config(?MODULE,id,bad), - %% incorrect values of OP params + %% incorrect values of OP params + {ok,#{config := HConfig}} = logger:get_handler_config(?MODULE), {error,{invalid_levels,_}} = logger:set_handler_config(?MODULE,config, - #{sync_mode_qlen=>100, - flush_qlen=>99}), + HConfig#{sync_mode_qlen=>100, + flush_qlen=>99}), %% invalid name of config parameter {error,{invalid_config,logger_disk_log_h,{filesync_rep_int,2000}}} = logger:set_handler_config(?MODULE, config, - #{filesync_rep_int => 2000}), + HConfig#{filesync_rep_int => 2000}), ok. config_fail(cleanup,_Config) -> logger:remove_handler(?MODULE). @@ -445,18 +446,19 @@ reconfig(Config) -> file := _DiskLogFile}} = logger_disk_log_h:info(?MODULE), - ok = logger:set_handler_config(?MODULE, config, - #{sync_mode_qlen => 1, - drop_mode_qlen => 2, - flush_qlen => 3, - burst_limit_enable => false, - burst_limit_max_count => 10, - burst_limit_window_time => 10, - overload_kill_enable => true, - overload_kill_qlen => 100000, - overload_kill_mem_size => 10000000, - overload_kill_restart_after => never, - filesync_repeat_interval => no_repeat}), + {ok,#{config := HConfig0}} = logger:get_handler_config(?MODULE), + HConfig1 = HConfig0#{sync_mode_qlen => 1, + drop_mode_qlen => 2, + flush_qlen => 3, + burst_limit_enable => false, + burst_limit_max_count => 10, + burst_limit_window_time => 10, + overload_kill_enable => true, + overload_kill_qlen => 100000, + overload_kill_mem_size => 10000000, + overload_kill_restart_after => never, + filesync_repeat_interval => no_repeat}, + ok = logger:set_handler_config(?MODULE, config, HConfig1), #{id := ?MODULE, sync_mode_qlen := 1, drop_mode_qlen := 2, @@ -479,7 +481,7 @@ reconfig(Config) -> #{filter_default=>log, filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,self()}, - disk_log_opts=> + config=> #{type => halt, max_no_files => 1, max_no_bytes => 1024, @@ -500,7 +502,7 @@ sync(Config) -> Log = lists:concat([File,".1"]), ok = logger:add_handler(?MODULE, logger_disk_log_h, - #{disk_log_opts => #{file => File}, + #{config => #{file => File}, filter_default=>log, filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,nl}}), @@ -529,8 +531,10 @@ sync(Config) -> %% check that if there's no repeated disk_log_sync active, %% a disk_log_sync is still performed when handler goes idle - logger:set_handler_config(?MODULE, config, - #{filesync_repeat_interval => no_repeat}), + {ok,#{config := HConfig}} = logger:get_handler_config(?MODULE), + HConfig1 = HConfig#{filesync_repeat_interval => no_repeat}, + ok = logger:set_handler_config(?MODULE, config, HConfig1), + no_repeat = maps:get(filesync_repeat_interval, logger_disk_log_h:info(?MODULE)), @@ -556,13 +560,14 @@ sync(Config) -> start_tracer([{logger_disk_log_h,handle_cast,2}], [OneSync || _ <- lists:seq(1, 1 + trunc(WaitT/SyncInt))]), - logger:set_handler_config(?MODULE, config, - #{filesync_repeat_interval => SyncInt}), + HConfig2 = HConfig#{filesync_repeat_interval => SyncInt}, + ok = logger:set_handler_config(?MODULE, config, HConfig2), + SyncInt = maps:get(filesync_repeat_interval, logger_disk_log_h:info(?MODULE)), timer:sleep(WaitT), - logger:set_handler_config(?MODULE, config, - #{filesync_repeat_interval => no_repeat}), + HConfig3 = HConfig#{filesync_repeat_interval => no_repeat}, + ok = logger:set_handler_config(?MODULE, config, HConfig3), check_tracer(100), ok. sync(cleanup,_Config) -> @@ -581,7 +586,7 @@ disk_log_wrap(Config) -> #{filter_default=>log, filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,self()}, - disk_log_opts=> + config=> #{type => wrap, max_no_files => MaxFiles, max_no_bytes => MaxBytes, @@ -637,7 +642,7 @@ disk_log_full(Config) -> #{filter_default=>log, filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,self()}, - disk_log_opts=> + config=> #{type => halt, max_no_files => 1, max_no_bytes => MaxBytes, @@ -814,7 +819,7 @@ start_h_on_new_node(Config, File) -> logger_test_lib:setup( Config, [{logger,[{handler,default,logger_disk_log_h, - #{ disk_log_opts => #{ file => File }}}]}]), + #{ config => #{ file => File }}}]}]), ok = rpc:call(Node,logger,set_handler_config,[?STANDARD_HANDLER,formatter, {?MODULE,nl}]), Node. @@ -1217,9 +1222,9 @@ start_handler(Name, FuncName, Config) -> ct:pal("Logging to ~tp", [File]), ok = logger:add_handler(Name, logger_disk_log_h, - #{disk_log_opts=>#{file => File, - max_no_files => 1, - max_no_bytes => 100000000}, + #{config=>#{file => File, + max_no_files => 1, + max_no_bytes => 100000000}, filter_default=>log, filters=>?DEFAULT_HANDLER_FILTERS([Name]), formatter=>{?MODULE,op}}), @@ -1325,10 +1330,11 @@ remove(Handler, LogName) -> ok. start_and_add(Name, Config, LogOpts) -> - ct:pal("Adding handler ~w with: ~p", - [Name,Config#{disk_log_opts=>LogOpts}]), - ok = logger:add_handler(Name, logger_disk_log_h, - Config#{disk_log_opts=>LogOpts}), + HConfig = maps:get(config, Config, #{}), + HConfig1 = maps:merge(HConfig, LogOpts), + Config1 = Config#{config=>HConfig1}, + ct:pal("Adding handler ~w with: ~p", [Name,Config1]), + ok = logger:add_handler(Name, logger_disk_log_h, Config1), Pid = whereis(h_proc_name(Name)), true = is_pid(Pid), Name = proplists:get_value(name, disk_log:info(Name)), diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl index 4f052b456e..6e182d4bca 100644 --- a/lib/kernel/test/logger_env_var_SUITE.erl +++ b/lib/kernel/test/logger_env_var_SUITE.erl @@ -412,7 +412,7 @@ logger_disk_log(Config) -> = setup(Config, [{logger, [{handler,?STANDARD_HANDLER,logger_disk_log_h, - #{disk_log_opts=>#{file=>Log}}}]}]), + #{config=>#{file=>Log}}}]}]), check_default_log(Node,Log, disk_log,% dest 0),% progress in std logger @@ -434,7 +434,7 @@ logger_disk_log_formatter(Config) -> [{handler,?STANDARD_HANDLER,logger_disk_log_h, #{filters=>[], formatter=>{logger_formatter,#{}}, - disk_log_opts=>#{file=>Log}}}]}]), + config=>#{file=>Log}}}]}]), check_single_log(Node,Log, disk_log,% dest 6),% progress in std logger diff --git a/lib/kernel/test/logger_simple_h_SUITE.erl b/lib/kernel/test/logger_simple_h_SUITE.erl index 3a761a257a..e4e48b538a 100644 --- a/lib/kernel/test/logger_simple_h_SUITE.erl +++ b/lib/kernel/test/logger_simple_h_SUITE.erl @@ -182,7 +182,7 @@ replace_disk_log(Config) -> ok = rpc:call(Node, logger, add_handlers, [[{handler, default, logger_disk_log_h, - #{ disk_log_opts => #{ file => File }, + #{ config => #{ file => File }, formatter => {?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}}}]]), {ok,Bin} = sync_and_read(Node, disk_log, File), Lines = [unicode:characters_to_list(L) || diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index 2efe2ce6f6..36b123b07d 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -336,7 +336,7 @@ crash_std_h_to_disk_log(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 }}}], + #{ config => #{ file => Log }}}], disk_log,Log). crash_std_h_to_disk_log(cleanup,_Config) -> crash_std_h(cleanup). -- cgit v1.2.3 From b9d2cb688e562d200663bdbedfa65adc5a29aeae Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 8 Jun 2018 14:18:36 +0200 Subject: [logger] Update documentation --- lib/kernel/doc/src/error_logger.xml | 35 +- lib/kernel/doc/src/kernel_app.xml | 117 ++-- lib/kernel/doc/src/logger.xml | 883 +++++++++++++++++-------------- lib/kernel/doc/src/logger_arch.png | Bin 32377 -> 32407 bytes lib/kernel/doc/src/logger_chapter.xml | 843 +++++++++++++++++------------ lib/kernel/doc/src/logger_disk_log_h.xml | 20 +- lib/kernel/doc/src/logger_filters.xml | 164 +++--- lib/kernel/doc/src/logger_formatter.xml | 148 ++++-- lib/kernel/doc/src/logger_std_h.xml | 21 +- lib/kernel/doc/src/part.xml | 11 +- lib/kernel/src/logger_formatter.erl | 3 +- 11 files changed, 1269 insertions(+), 976 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml index f418aa5bbe..27fb1488c7 100644 --- a/lib/kernel/doc/src/error_logger.xml +++ b/lib/kernel/doc/src/error_logger.xml @@ -305,14 +305,22 @@ ok

    Enables or disables printout of standard events to a file.

    -

    This is done by adding or deleting the standard event handler - for output to file. Thus, calling this function overrides - the value of the Kernel error_logger configuration - parameter.

    -

    Enabling file logging can be used together with calling - tty(false), to have a silent system where - all standard events are logged to a file only. - Only one log file can be active at a time.

    +

    This is done by adding or deleting + the error_logger_file_h event handler, and thus + indirectly adding error_logger as a Logger + handler.

    +

    Notice that this function does not manipulate the Logger + configuration directly, meaning that if the default Logger + handler is already logging to a file, this function can + potentially cause logging to a second file.

    +

    This function is useful as a shortcut during development + and testing, but must not be used in a production + system. See + section Logging + in the Kernel User's Guide, and + the logger(3) + manual page for information about how to configure Logger + for live systems.

    Request is one of the following:

    {open, Filename} @@ -344,9 +352,14 @@ ok

    Enables (Flag == true) or disables (Flag == false) printout of standard events to the terminal.

    -

    This is done by adding or deleting the standard event handler - for output to the terminal. Thus, calling this function overrides - the value of the Kernel error_logger configuration parameter.

    +

    This is done by manipulating the Logger configuration. The + function is useful as a shortcut during development and + testing, but must not be used in a production system. See + section Logging + in the Kernel User's Guide, and + the logger(3) + manual page for information about how to configure Logger + for live systems.

    diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index e2a6d30249..3914226a3e 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -54,9 +54,9 @@

    Two standard logger handlers are defined in the Kernel application. These are described in the Kernel User's Guide, - and in logger_std_h(3) + and in the logger_std_h(3) and logger_disk_log_h(3) - .

    + manual pages.

    @@ -166,57 +166,71 @@ logger = [Config] -

    Specifies how logger should be - configured.

    -

    For more details and examples, see the - Configuration section in the - Logger User's Guide. -

    +

    Specifies the configuration + for Logger, except the + primary log level, which is specified + with logger_level, + and the compatibility + with SASL Error + Logging, which is specified + with + logger_sasl_compatible.

    +

    The logger parameter is described in + section + Logging in the Kernel User's Guide.

    logger_level = Level -

    Level = emergency | alert | critical | error | warning | - notice | info | debug

    -

    This parameter specifies which log levels to log. The - specified level, and all levels that are more severe, will - be logged.

    -

    The default value is info.

    -

    To change the global log level at run-time, use - - logger:set_logger_config(level, error).

    +

    Specifies the primary log level for Logger. Log events with + the same, or a more severe level, pass through the primary + log level check. See + section Logging + in the Kernel User's Guide for more information about Logger + and log levels.

    +

    Level = emergency | alert | critical | error | warning | + notice | info | debug | all | none

    +

    To change the primary log level at runtime, use + + logger:set_primary_config(level, Level).

    +

    Defaults to info.

    - logger_sasl_compatible = boolean() - -

    If this parameter is set to true, then the default 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 sasl, according to values of the SASL - configuration parameter sasl_error_logger - and sasl_errlog_type, see - sasl(6) -

    -

    The default value is false.

    -

    See chapter Backwards - compatibility with error_logger for more - information about handling of the so called SASL reports.

    -

    This configuration option only effects the default - and sasl handler. Any other handlers are uneffected.

    -
    - - logger_progress_reports = stop | log - -

    If logger_sasl_compatible = false, - then logger_progress_reports specifies if progress - reports from supervisor - and application_controller shall be logged by the - default logger.

    -

    If logger_sasl_compatible = true, - then logger_progress_reports is ignored.

    -

    The default value is stop

    -

    This configuration option only effects the default - and sasl handler. Any other handlers are uneffected.

    + logger_sasl_compatible = true | false + +

    Specifies if Logger behaves backwards compatible with the + SASL error logging functionality from releases prior to + Erlang/OTP 21.0.

    +

    If this parameter is set to true, the default Logger + handler does not log any progress-, crash-, or supervisor + reports. If the SASL application is then started, it adds a + Logger handler named sasl, which logs these events + according to values of the SASL configuration + parameter sasl_error_logger + and sasl_errlog_type.

    +

    See section + + Deprecated Error Logger Event Handlers and + Configuration in the sasl(6) manual page for + information about the SASL configuration parameters.

    +

    See section SASL Error + Logging in the SASL User's Guide, and + section Backwards + Compatibility with error_logger in the Kernel + User's Guide for information about the SASL error logging + functionality, and how Logger can be backwards compatible + with this.

    +

    Defaults to false.

    + +

    If this parameter is set to true, + sasl_errlog_type indicates that progress reports + shall be logged, and the configured primary log level + is notice or more severe, then SASL automatically + sets the primary log level to info. That is, this + setting can potentially overwrite the value of the Kernel + configuration parameter logger_level. This is to + allow progress reports, which have log level info, + to be forwarded to the handlers.

    +
    global_groups = [GroupTuple] @@ -269,9 +283,8 @@ inet_parse_error_log = silent -

    If set, no - logger messages are generated when erroneous - lines are found and skipped in the various Inet configuration +

    If set, no log events are issued when erroneous lines are + found and skipped in the various Inet configuration files.

    inetrc = Filename @@ -494,7 +507,7 @@ MaxT = TickTime + TickTime / 4
    logger_std_h to the same value. Example: -erl -kernel logger '[{handler,default,logger_std_h,#{logger_std_h=>#{type=>{file,"/tmp/erlang.log"}}}}]' +erl -kernel logger '[{handler,default,logger_std_h,#{config=>#{type=>{file,"/tmp/erlang.log"}}}}]' error_logger_format_depth diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index 911eb158da..7f35a5d752 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -33,73 +33,150 @@ logger.xml logger - API module for logging in Erlang/OTP. + API module for Logger, the standard logging facility + in Erlang/OTP. -

    - This module is the main API for logging in Erlang/OTP. It - contains functions that allow applications to use a single log - API and the system to manage those log events independently. Use - the API functions or the log - macros to log events. For instance, - to log a new error event:

    - -?LOG_ERROR("error happened because: ~p",[Reason]). %% With macro -logger:error("error happened because: ~p",[Reason]). %% Without macro - -

    This log event is then sent to the configured log handlers which - by default means that it is be printed to the console. If you want - your systems logs to be printed to a file instead of the console you - must configure the default handler to do so. The simplest way is - to include the following in your sys.config.

    +

    This module implements the main API for logging in + Erlang/OTP. To create a log event, use the + API functions or the + log + macros, for example:

    + +?LOG_ERROR("error happened because: ~p", [Reason]). % With macro +logger:error("error happened because: ~p", [Reason]). % Without macro + +

    To configure the Logger backend, + use Kernel configuration + parameters + or configuration + functions in the Logger API.

    + +

    By default, the Kernel application installs one log handler at + system start. This handler is named default. It receives + and processes standard log events produced by the Erlang runtime + system, standard behaviours and different Erlang/OTP + applications. The log events are by default printed to the + terminal.

    +

    If you want your systems logs to be printed to a file instead, + you must configure the default handler to do so. The simplest + way is to include the following in + your sys.config:

    [{kernel, [{logger, - [{handler,default,logger_std_h, - #{logger_std_h=>#{type=>{file,"path/to/file.log"}}}}]}]}]. + [{handler, default, logger_std_h, + #{config => #{type => {file, "path/to/file.log"}}}}]}]}].

    For more information about:

    - how to use the API, - see the User's Guide. - how to configure Logger, - see the Configuration + the Logger facility in general, see + the User's + Guide. + how to configure Logger, see + the Configuration section in the User's Guide. - the convinience macros in logger.hrl, - see the macro section. - what the builtin formatter can do, - see logger_formatter. - what the builtin handlers can do, + the built-in handlers, see logger_std_h and logger_disk_log_h. - what builtin filters are available, + the built-in formatter, + see logger_formatter. + built-in filters, see logger_filters.
    - + -

    The severity level for the message to be logged.

    +

    Primary configuration data for Logger. The following + default values apply:

    + + level => info + filter_default => log + filters => [] +
    - + -

    +

    Handler configuration data for Logger. The following + default values apply:

    + + level => all + filter_default => log + filters => [] + formatter => {logger_formatter, DefaultFormatterConfig} + +

    In addition to these, the following fields are + automatically inserted by Logger, values taken from the + two first parameters + to add_handler/3:

    + + id => HandlerId + module => Module + +

    Handler specific configuration data is inserted by the + handler callback itself, in a sub structure associated with + the field named config.

    +

    See the + logger_formatter(3) manual page for + information about the default configuration for this + formatter.

    - + -

    +

    A filter which can be installed as a handler filter, or as + a primary filter in Logger.

    - + + +

    The second argument to the filter fun.

    +
    +
    + + + +

    A unique identifier for a filter.

    +
    +
    + + + +

    The return value from the filter fun.

    +
    +
    + + + +

    Configuration data for the + formatter. See + logger_formatter(3) + for an example of a formatter implementation.

    +
    +
    + + + +

    A unique identifier for a handler instance.

    +
    +
    + + + +

    The severity level for the message to be logged.

    +
    +
    + +

    @@ -117,7 +194,7 @@ logger:error("error happened because: ~p",[Reason]). %% Without macro

    When a log macro is used, Logger also inserts location information:

    - mfa => {?MODULE,?FUNCTION_NAME,?FUNCTION_ARITY} + mfa => {?MODULE, ?FUNCTION_NAME, ?FUNCTION_ARITY} file => ?FILE line => ?LINE @@ -158,51 +235,15 @@ logger:error("error happened because: ~p",[Reason]). %% Without macro
    - - -

    Configuration data for the logger part of Logger, or for a handler.

    -

    The following default values apply:

    - - level => info - filter_default => log - filters => [] - formatter => {logger_formatter,DefaultFormatterConfig} - -

    See the - logger_formatter(3) manual page for - information about the default configuration for this - formatter.

    -
    -
    - - - -

    A unique identifier for a handler instance.

    -
    -
    - - - -

    A unique identifier for a filter.

    -
    -
    - - - -

    A filter which can be installed for the logger part of - Logger, or for a handler.

    -
    -
    - - + -

    The second argument to the filter fun.

    +

    - + -

    The return value from the filter fun.

    +

    @@ -213,15 +254,6 @@ logger:error("error happened because: ~p",[Reason]). %% Without macro erlang:system_time(microsecond).

    - - - -

    Configuration data for the - formatter. See - logger_formatter(3) - for an example of a formatter implementation.

    -
    -
    @@ -258,6 +290,10 @@ logger:error("error happened because: ~p",[Reason]). %% Without macro level.

    +
    + + Logging API functions +
    emergency(StringOrReport[,Metadata]) @@ -317,7 +353,7 @@ logger:error("error happened because: ~p",[Reason]). %% Without macro notice(StringOrReport[,Metadata]) notice(Format,Args[,Metadata]) - notice(Fun,FunArgs[,Metadata]) + notice(Fun,FunArgs[,Metadata]) Logs the given message as level notice.

    Equivalent to @@ -366,136 +402,21 @@ logger:error("error happened because: ~p",[Reason]). %% Without macro

    Log the given message.

    +
    +
    + + Configuration API functions +
    + - - Look up the current configuration for the logger part - of Logger. - -

    Look up the current configuration for the logger part of - Logger.

    -
    -
    - - - - Look up the current configuration for the given - handler. - -

    Look up the current configuration for the given handler.

    -
    -
    - - - - Get all Logger configurations - -

    Same as logger:i(term)

    -
    -
    - - - - - - Get all Logger configurations - -

    Display or return all current Logger configurations.

    - - Action = string - -

    Return the pretty printed current Logger configuration - as iodata.

    -
    - Action = term - -

    Return the current Logger configuration as a term. The - format of this term may change between releases. For a - stable format use - logger:get_handler_config/1 - and - logger:get_logger_config/0.

    -

    The same as calling logger:i().

    -
    - Action = print - -

    Pretty print all the current Logger configuration to - standard out. Example:

    - logger:i(print). -Current logger configuration: - Level: info - Filter Default: log - Filters: - Handlers: - Id: default - Module: logger_std_h - Level: info - Formatter: - Module: logger_formatter - Config: #{legacy_header => true,single_line => false, - template => [{logger_formatter,header},"\n",msg,"\n"]} - Filter Default: stop - Filters: - Id: stop_progress - Fun: fun logger_filters:progress/2 - Config: stop - Id: remote_gl - Fun: fun logger_filters:remote_gl/2 - Config: stop - Id: domain - Fun: fun logger_filters:domain/2 - Config: {log,super,[beam,erlang,otp,sasl]} - Id: no_domain - Fun: fun logger_filters:domain/2 - Config: {log,undefined,[]} - Handler Config: - logger_std_h: #{type => standard_io} - Level set per module: - Module: my_module - Level: debug]]> -
    -
    -
    -
    - - - - Add a filter to the logger part of Logger. + + Add a handler with the given configuration. -

    Add a filter to the logger part of Logger.

    -

    The filter fun is called with the log event as the first - parameter, and the specified filter_args() as the - second parameter.

    -

    The return value of the fun specifies if a log event is to - be discarded or forwarded to the handlers:

    - - log_event() - -

    The filter passed. The next logger filter, if - any, is applied. If no more logger filters exist, the - log event is forwarded to the handler part of Logger, - where handler filters are applied.

    -
    - stop - -

    The filter did not pass, and the log event is - immediately discarded.

    -
    - ignore - -

    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 filter_default - configuration parameter for the logger part specifies if - the log event shall be discarded or forwarded to the - handler part.

    -
    -
    -

    See section - Filters in the User's Guide for more information - about filters.

    -

    Some built-in filters exist. These are defined - in logger_filters.

    +

    Add a handler with the given configuration.

    +

    HandlerId is a unique identifier which + must be used in all subsequent calls referring to this + handler.

    @@ -540,99 +461,13 @@ Current logger configuration: - - - Remove a filter from the logger part of Logger. - -

    Remove the filter identified - by FilterId from the logger part of Logger.

    -
    -
    - - - - Remove a filter from the specified handler. - -

    Remove the filter identified - by FilterId from the handler identified - by HandlerId.

    -
    -
    - - - - Add a handler with the given configuration. - -

    Add a handler with the given configuration.

    -

    HandlerId is a unique identifier which - must be used in all subsequent calls referring to this - handler.

    -
    -
    - - - - Remove the handler with the specified identity. - -

    Remove the handler identified by HandlerId.

    -
    -
    - - - - Set the log level for the specified module. - -

    Set the log level for the - specified Module.

    -

    The log level for a module overrides the global log level - of Logger for log events originating from the module in - question. Notice, however, that it does not override the - level configuration for any handler.

    -

    For example: Assume that the global log level for Logger - is info, and there is one handler, h1, with - level info and one handler, h2, with - level debug.

    -

    With this configuration, no debug messages will be logged, - since they are all stopped by the global log level.

    -

    If the level for mymodule is now set - to debug, then debug events from this module will be - logged by the handler h2, but not by - handler h1.

    -

    Debug events from other modules are still not logged.

    -

    To change the global log level for Logger, use - - logger:set_logger_config(level,Level).

    -

    To change the log level for a handler, use - - logger:set_handler_config(HandlerId,level,Level).

    - -

    The originating module for a log event is only detected - if mfa=>{Module,Function,Arity} exists in the - metadata. When log macros are used, this association is - automatically added to all log events. If an API function - is called directly, without using a macro, the logging - client must explicitly add this information if module - levels shall have any effect.

    -
    -
    -
    - - - - Remove a module specific log setting. - -

    Remove a module specific log setting. After this, the - global log level is used for the specified module.

    -
    -
    - Set up log handlers from the application's configuration parameters.

    Reads the application configuration parameter logger and - calls logger:add_handlers/1 with its contents.

    + calls add_handlers/1 with its contents.

    @@ -670,61 +505,158 @@ start(_, []) -> [{kernel, [{logger, %% Disable the default Kernel handler - [{handler,default,undefined}]}]}, + [{handler, default, undefined}]}]}, {my_app, [{logger, %% Enable this handler as the default - [{handler,default,my_handler,#{}}]}]}]. + [{handler, default, my_handler, #{}}]}]}].
    - - Set configuration data for the logger part of Logger. + + Add a primary filter to Logger. -

    Set configuration data for the logger part of Logger. This - overwrites the current logger configuration.

    -

    To modify the existing configuration, - use - update_logger_config/1, or, if a more - complex merge is needed, read the current configuration - with get_logger_config/0 - , then do the merge before writing the new - configuration back with this function.

    -

    If a key is removed compared to the current configuration, - the default value is used.

    +

    Add a primary filter to Logger.

    +

    The filter fun is called with the log event as the first + parameter, and the specified filter_args() as the + second parameter.

    +

    The return value of the fun specifies if a log event is to + be discarded or forwarded to the handlers:

    + + log_event() + +

    The filter passed. The next primary filter, if + any, is applied. If no more primary filters exist, the + log event is forwarded to the handler part of Logger, + where handler filters are applied.

    +
    + stop + +

    The filter did not pass, and the log event is + immediately discarded.

    +
    + ignore + +

    The filter has no knowledge of the log event. The next + primary filter, if any, is applied. If no more primary + filters exist, the value of the + primary filter_default configuration parameter + specifies if the log event shall be discarded or + forwarded to the handler part.

    +
    +
    +

    See section + Filters in the User's Guide for more information + about filters.

    +

    Some built-in filters exist. These are defined + in logger_filters.

    - - Add or update configuration data for the logger part - of Logger. + + Look up the current Logger configuration -

    Add or update configuration data for the logger part of - Logger. If the given Key already exists, - its associated value will be changed - to Value. If it does not exist, it will - be added.

    +

    Look up all current Logger configuration, including primary + and handler configuration, and module level settings.

    - - Update configuration data for the logger part of - Logger. + + Look up the current configuration for all handlers. -

    Update configuration data for the logger part of - Logger. This function behaves as if it was implemented as - follows:

    - -{ok,Old} = logger:get_logger_config(), -logger:set_logger_config(maps:merge(Old,Config)). - -

    To overwrite the existing configuration without any merge, - use set_logger_config/1 - .

    +

    Look up the current configuration for all handlers.

    +
    +
    + + + + Look up the current configuration for the given + handler. + +

    Look up the current configuration for the given handler.

    +
    +
    + + + + Look up the identities for all installed handlers. + +

    Look up the identities for all installed handlers.

    +
    +
    + + + + Look up the current primary configuration for Logger. + +

    Look up the current primary configuration for Logger.

    +
    +
    + + + + Look up all current module levels. + +

    Look up all current module levels. Returns a list + containing one {Module,Level} element for each module + for which the module level was previously set + with + set_module_level/2.

    +
    +
    + + + + Look up the current level for the given modules. + +

    Look up the current level for the given modules. Returns a + list containing one {Module,Level} element for each + of the given modules for which the module level was + previously set with + set_module_level/2.

    +
    +
    + + + + Retrieve data set with set_process_metadata/1. + +

    Retrieve data set + with + set_process_metadata/1 or + + update_process_metadata/1.

    +
    +
    + + + + Remove the handler with the specified identity. + +

    Remove the handler identified by HandlerId.

    +
    +
    + + + + Remove a filter from the specified handler. + +

    Remove the filter identified + by FilterId from the handler identified + by HandlerId.

    +
    +
    + + + + Remove a primary filter from Logger. + +

    Remove the primary filter identified + by FilterId from Logger.

    @@ -763,18 +695,122 @@ logger:set_logger_config(maps:merge(Old,Config)). - - Update configuration data for the specified handler. + + Set primary configuration data for Logger. -

    Update configuration data for the specified handler. This function - behaves as if it was implemented as follows:

    - -{ok,{_,Old}} = logger:get_handler_config(HandlerId), -logger:set_handler_config(HandlerId,maps:merge(Old,Config)). - -

    To overwrite the existing configuration without any merge, - use set_handler_config/2 +

    Set primary configuration data for Logger. This + overwrites the current configuration.

    +

    To modify the existing configuration, + use + update_primary_config/1, or, if a more + complex merge is needed, read the current configuration + with get_primary_config/0 + , then do the merge before writing the new + configuration back with this function.

    +

    If a key is removed compared to the current configuration, + the default value is used.

    +
    +
    + + + + Add or update primary configuration data for Logger. + +

    Add or update primary configuration data for Logger. If the + given Key already exists, its associated + value will be changed to Value. If it + does not exist, it will be added.

    +
    +
    + + + + Set the log level for the specified modules. + +

    Set the log level for the + specified modules.

    +

    The log level for a module overrides the primary log level + of Logger for log events originating from the module in + question. Notice, however, that it does not override the + level configuration for any handler.

    +

    For example: Assume that the primary log level for Logger + is info, and there is one handler, h1, with + level info and one handler, h2, with + level debug.

    +

    With this configuration, no debug messages will be logged, + since they are all stopped by the primary log level.

    +

    If the level for mymodule is now set + to debug, then debug events from this module will be + logged by the handler h2, but not by + handler h1.

    +

    Debug events from other modules are still not logged.

    +

    To change the primary log level for Logger, use + + set_primary_config(level, Level).

    +

    To change the log level for a handler, use + + set_handler_config(HandlerId, level, Level) .

    + +

    The originating module for a log event is only detected + if the key mfa exists in the metadata, and is + associated with {Module, Function, Arity}. When log + macros are used, this association is automatically added + to all log events. If an API function is called directly, + without using a macro, the logging client must explicitly + add this information if module levels shall have any + effect.

    +
    +
    +
    + + + + Set metadata to use when logging from current process. + +

    Set metadata which Logger shall automatically insert in + all log events produced on the current process.

    +

    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.

    +

    Subsequent calls to this function overwrites previous data + set. To update existing data instead of overwriting it, + see + update_process_metadata/1.

    +
    +
    + + + + Remove module specific log settings for all modules. + +

    Remove module specific log settings. After this, the + primary log level is used for all modules.

    +
    +
    + + + + Remove module specific log settings for the given + modules. + +

    Remove module specific log settings. After this, the + primary log level is used for the specified modules.

    +
    +
    + + + + Delete data set with set_process_metadata/1. + +

    Delete data set + with + set_process_metadata/1 or + + update_process_metadata/1.

    @@ -786,9 +822,10 @@ logger:set_handler_config(HandlerId,maps:merge(Old,Config)).

    The new configuration is merged with the existing formatter configuration.

    To overwrite the existing configuration without any merge, - use - set_handler_config(HandlerId,formatter, - {FormatterModule,FormatterConfig}).

    + use

    +
    +set_handler_config(HandlerId, formatter,
    +	      {FormatterModule, FormatterConfig}).
    @@ -797,39 +834,41 @@ logger:set_handler_config(HandlerId,maps:merge(Old,Config)). Update the formatter configuration for the specified handler.

    Update the formatter configuration for the specified handler.

    -

    This is equivalent - to
    - update_formatter_config(HandlerId,#{Key=>Value})

    +

    This is equivalent to

    +
    +update_formatter_config(HandlerId, #{Key => Value})
    - - Compare the severity of two log levels. + + Update configuration data for the specified handler. -

    Compare the severity of two log levels. Returns gt - if Level1 is more severe than - Level2, lt if Level1 is less severe, - and eq if the levels are equal.

    +

    Update configuration data for the specified handler. This function + behaves as if it was implemented as follows:

    + +{ok, {_, Old}} = logger:get_handler_config(HandlerId), +logger:set_handler_config(HandlerId, maps:merge(Old, Config)). + +

    To overwrite the existing configuration without any merge, + use set_handler_config/2 + .

    - - Set metadata to use when logging from current process. + + Update primary configuration data for Logger. -

    Set metadata which Logger shall automatically insert in - all log events produced on the current process.

    -

    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.

    -

    Subsequent calls to this function overwrites previous data - set. To update existing data instead of overwriting it, - see - update_process_metadata/1.

    +

    Update primary configuration data for Logger. This function + behaves as if it was implemented as follows:

    + +Old = logger:get_primary_config(), +logger:set_primary_config(maps:merge(Old, Config)). + +

    To overwrite the existing configuration without any merge, + use set_primary_config/1 + .

    @@ -843,7 +882,7 @@ logger:set_handler_config(HandlerId,maps:merge(Old,Config)).

    If process metadata exists for the current process, this function behaves as if it was implemented as follows:

    -logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)). +logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)).

    If no process metadata exists, the function behaves as @@ -851,48 +890,44 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)). .

    + +
    + + Miscellaneous API functions +
    + - - Retrieve data set with set_process_metadata/1. - -

    Retrieve data set - with - set_process_metadata/1 or - - update_process_metadata/1.

    -
    -
    - - - - Delete data set with set_process_metadata/1. + + Compare the severity of two log levels. -

    Delete data set - with - set_process_metadata/1 or - - update_process_metadata/1.

    +

    Compare the severity of two log levels. Returns gt + if Level1 is more severe than + Level2, lt if Level1 is less severe, + and eq if the levels are equal.

    - Convert a log message on report form to {Format,Args}. + Convert a log message on report form to {Format, Args}. -

    Convert a log message on report form to {Format,Args}.

    -

    This is the default report callback used +

    Convert a log message on report form to {Format, + Args}. This is the default report callback used by logger_formatter when no custom report - callback is found.

    + callback is found. See + section Log + Message in the Kernel User's Guide for + information about report callbacks and valid forms of log + messages.

    The function produces lines of Key: Value from key-value lists. Strings are printed with ~ts and other terms with ~tp.

    -

    If the Report is a map, it is - converted to a key-value list before formatting as such.

    +

    If Report is a map, it is converted to + a key-value list before formatting as such.

    -
    @@ -904,11 +939,11 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)). - HModule:adding_handler(Config1) -> {ok,Config2} | {error,Reason} + HModule:adding_handler(Config1) -> {ok, Config2} | {error, Reason} An instance of this handler is about to be added. Config1 = Config2 = - config() + handler_config() Reason = term() @@ -928,11 +963,11 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)). - HModule:changing_config(Config1,Config2) -> {ok,Config3} | {error,Reason} + HModule:changing_config(Config1, Config2) -> {ok, Config3} | {error, Reason} The configuration for this handler is about to change. Config1 = Config2 = Config3 = - config() + handler_config() Reason = term() @@ -952,17 +987,17 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)). - HModule:log(LogEvent,Config) -> void() + HModule:log(LogEvent, Config) -> void() Log the given log event. LogEvent = log_event() Config = - config() + handler_config()

    This callback function is mandatory.

    -

    The function is called when all global filters and all +

    The function is called when all primary filters and all handler filters for the handler in question have passed for the given log event.

    The handler identity is associated with the id key @@ -978,7 +1013,7 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)). The given handler is about to be removed. Config = - config() + handler_config()

    This callback function is optional.

    @@ -1002,18 +1037,30 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)). - FModule:check_config(FConfig) -> ok | {error,term()} + FModule:check_config(FConfig) -> ok | {error, Reason} Validate the given formatter configuration. FConfig = formatter_config() + Reason = term()

    This callback function is optional.

    The function is called by a Logger when formatter configuration is set or modified. The formatter must validate the given configuration and return ok if it - is correct, and {error,term()} if it is faulty.

    + is correct, and {error,Reason} if it is faulty.

    +

    The following Logger API functions can trigger this callback:

    + + + logger:add_handler/3 + + logger:set_handler_config/2,3 + + logger:updata_handler_config/2 + + logger:update_formatter_config/2 +

    See logger_formatter(3) for an example implementation. logger_formatter is the @@ -1021,7 +1068,7 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)). - FModule:format(LogEvent,FConfig) -> FormattedLogEntry + FModule:format(LogEvent, FConfig) -> FormattedLogEntry Format the given log event. LogEvent = @@ -1045,6 +1092,20 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(),Meta)). + +

    + See Also +

    + config(4), + erlang(3), + io(3), + logger_disk_log_h(3), + logger_filters(3), + logger_formatter(3), + logger_std_h(3), + unicode(3) +

    +
    diff --git a/lib/kernel/doc/src/logger_arch.png b/lib/kernel/doc/src/logger_arch.png index 901122193a..a9b9a658b4 100644 Binary files a/lib/kernel/doc/src/logger_arch.png and b/lib/kernel/doc/src/logger_arch.png differ diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index a3eec7bd4b..f7df0a3e6e 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -30,7 +30,7 @@ logger_chapter.xml -

    Erlang/OTP 21.0 provides a new standard API for logging +

    Erlang/OTP 21.0 provides a standard API for logging through Logger, which is part of the Kernel application. Logger consists of the API for issuing log events, and a customizable backend where log handlers, filters and @@ -44,7 +44,7 @@

    You can also configure the system so that the default handler prints log events to a single file, or to a set of wrap logs via disk_log.

    -

    By confiugration, you can aslo modify or disable the default +

    By configuration, you can also modify or disable the default handler, replace it by a custom handler, and install additional handlers.

    @@ -53,11 +53,12 @@

    A log event consists of a log level, the message to be logged, and metadata.

    The Logger backend forwards log events from the API, first - through a set of global filters, then through a set - of handler filters for each log handler.

    + through a set of primary filters, then through a set of + secondary filters attached to each log handler. The secondary + filters are in the following named handler filters.

    Each filter set consists of a log level check, followed by zero or more filter functions.

    -

    The following figure show a conseptual overview of Logger. The +

    The following figure shows a conceptual overview of Logger. The figure shows two log handlers, but any number of handlers can be installed.

    @@ -69,11 +70,11 @@ atoms are mapped to integer values, and a log event passes the log level check if the integer value of its log level is less than or equal to the currently configured log level. That is, - the check pases if the event is equally or more severe than the + the check passes if the event is equally or more severe than the configured level. See section Log Level for a listing and description of all log levels.

    -

    The global log level can be overridden by a log level +

    The primary log level can be overridden by a log level configured per module. This is to, for instance, allow more verbose logging from a specific part of the system.

    Filter functions can be used for more sophisticated filtering @@ -82,7 +83,7 @@ also modify all parts of the log event. See see section Filters for more details.

    -

    If a log event passes through all global filters and all +

    If a log event passes through all primary filters and all handler filters for a specific handler, Logger forwards the event to the handler callback. The handler formats and prints the event to its destination. See @@ -107,7 +108,7 @@ functions is that macros add location (originator) information to the metadata, and performs lazy evaluation by wrapping the logger call in a case statement, so it is only evaluated if the - log level of the event passes the global log level check.

    + log level of the event passes the primary log level check.

    Log Level @@ -136,7 +137,7 @@ critical 2 - critical contidions + critical conditions error @@ -178,9 +179,9 @@ message can consist of a format string and arguments (given as two separate parameters in the Logger API), a string or a report. The latter, which is either a map or a key-value list, - can be accompanied by a report callback specified in the log - event's metadata. The - report callback is a convenience function that + can be accompanied by a report callback specified in + the log event's metadata. + The report callback is a convenience function that the formatter can use to convert the report to a format string and arguments. The formatter can also use its own conversion function, if no @@ -195,7 +196,7 @@ logger:debug(#{got => connection_request, id => Id, state => State}, #{report_cb => fun(R) -> {"~p",[R]} end})

    The log message can also be provided through a fun for lazy - evaluation. The fun is only evaluated if the global log level + evaluation. The fun is only evaluated if the primary log level check passes, and is therefore recommended if it is expensive to generate the message. The lazy fun must return a string, a report, or a tuple with format string and arguments.

    @@ -217,9 +218,9 @@ logger:debug(#{got => connection_request, id => Id, state => State}, which these calls are made, and Logger adds the metadata to all log events issued on that process.

    - Add metadata to a specifc log event + Add metadata to a specific log event -

    Metadata associated with one specifc log event is given +

    Metadata associated with one specific log event is given as the last parameter to the log macro or Logger API function when the event is issued. For example:

    ?LOG_ERROR("Connection closed",#{context => server}) @@ -235,8 +236,8 @@ logger:debug(#{got => connection_request, id => Id, state => State},
    Filters -

    Filters can be global, or attached to a specific - handler. Logger calls the global filters first, and if they all +

    Filters can be primary, or attached to a specific + handler. Logger calls the primary filters first, and if they all pass, it calls the handler filters for each handler. Logger calls the handler callback only if all filters attached to the handler in question also pass.

    @@ -251,7 +252,7 @@ logger:debug(#{got => connection_request, id => Id, state => State},

    The filter function can return stop, ignore or the (possibly modified) log event.

    If stop is returned, the log event is immediately - discarded. If the filter is global, no handler filters or + discarded. If the filter is primary, no handler filters or callbacks are called. If it is a handler filter, the corresponding handler callback is not called, but the log event is forwarded to filters attached to the next handler, if @@ -265,23 +266,21 @@ logger:debug(#{got => connection_request, id => Id, state => State},

    If the filter function returns ignore, it means that it did not recognize the log event, and thus leaves to other filters to decide the event's destiny.

    -

    The configuration - option filter_default - specifies the behaviour if all filter functions - return ignore, or if no filters - exist. filter_default is by default set to log, - meaning that if all existing filters ignore a log event, Logger - forwards the event to the handler +

    The configuration option filter_default specifies the + behaviour if all filter functions return ignore, or if no + filters exist. filter_default is by default set + to log, meaning that if all existing filters ignore a log + event, Logger forwards the event to the handler callback. If filter_default is set to stop, Logger discards such events.

    -

    Global filters are added - with - logger:add_logger_filter/2 +

    Primary filters are added + with + logger:add_primary_filter/2 and removed - with - logger:remove_logger_filter/1. They can also + with + logger:remove_primary_filter/1. They can also be added at system start via the Kernel configuration - parameter logger.

    + parameter logger.

    Handler filters are added with logger:add_handler_filter/3 @@ -292,12 +291,13 @@ logger:debug(#{got => connection_request, id => Id, state => State}, with logger:add_handler/3 or via the Kernel configuration - parameter logger.

    + parameter logger.

    To see which filters are currently installed in the system, - use logger:i/0, - or - logger:get_logger_config/0 + use + logger:get_config/0, + or + logger:get_primary_config/0 and logger:get_handler_config/1. Filters are listed in the order they are applied, that is, the first @@ -342,7 +342,7 @@ logger:debug(#{got => connection_request, id => Id, state => State},

    log(LogEvent, Config) -> void()

    This function is called when a log event has passed through all - global filters, and all handler filters attached to the handler + primary filters, and all handler filters attached to the handler in question. The function call is executed on the client process, and it is up to the handler implementation if other processes are involved or not.

    @@ -358,8 +358,8 @@ logger:debug(#{got => connection_request, id => Id, state => State}, functions adding_handler/1, changing_config/2 and removing_handler/1. See section Handler - Callback Functions in the logger(3) manual for more - information about these function.

    + Callback Functions in the logger(3) manual page for + more information about these function.

    The following built-in handlers exist:

    @@ -404,7 +404,7 @@ logger:debug(#{got => connection_request, id => Id, state => State}, which is passed as the second argument to HModule:log/2.

    -

    The formatter information consits of a formatter +

    The formatter information consist of a formatter module, FModule and its configuration, FConfig. FModule must export the following function, which can be called by the handler:

    @@ -425,152 +425,92 @@ logger:debug(#{got => connection_request, id => Id, state => State}, function when the formatter information is set or modified, to verify the validity of the formatter configuration.

    If no formatter information is specified for a handler, Logger - uses - logger_formatter(3) as default.

    + uses logger_formatter as default. See + the logger_formatter(3) + manual page for more information about this module.

    Configuration -

    Logger can be configured either when the system starts through - configuration parameters, - or at run-time by using the logger(3) - API. The recommended approach is to do the initial configuration in - the sys.config file and then use the API when some configuration - has to be changed at runtime, such as the log level.

    +

    At system start, Logger is configured through Kernel + configuration parameters. The parameters that apply to Logger + are described in + section Kernel + Configuration Parameters. Examples are found in + section Configuration + Examples.

    +

    During runtime, Logger configuration is changed via API + functions. See + section Configuration + API Functions in the logger(3) manual page.

    - Kernel Configuration Parameters -

    Logger is best configured by using the configuration parameters - of Kernel. There are four possible configuration parameters: - logger, - logger_level, - logger_sasl_compatible and - logger_progress_reports. - logger_level, logger_sasl_compatible and logger_progress_reports are described in the - Kernel Configuration, - while logger is described below.

    - - -

    logger

    -

    The application configuration parameter logger 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:

    - - DisableHandler = {handler,default,undefined} - -

    Disable the default handler. This allows another application - to add its own default handler. See - logger:add_handlers/1 for more details.

    -

    Only one entry of this option is allowed.

    - AddHandler = {handler,HandlerId,Module,HandlerConfig} - -

    Add a handler as if - logger:add_handler(HandlerId,Module,HandlerConfig) is - called.

    -

    It is allowed to have multiple entries of this option.

    - Filters = {filters, default, [Filter]}
    - FilterDefault = log | stop
    - Filter = {FilterId, {FilterFun, FilterConfig}}
    - -

    Add the specified - logger filters.

    -

    Only one entry of this option is allowed.

    - ModuleLevel = {module_level, Level, [Module]} - -

    This option configures - module log level.

    -

    It is allowed to have multiple entries of this option.

    -
    -

    Examples:

    - - -

    Output logs into the file "logs/erlang.log"

    - -[{kernel, - [{logger, - [{handler, default, logger_std_h, - #{ logger_std_h => #{ type => {file,"log/erlang.log"}}}}]}]}]. - -
    - -

    Output logs in single line format

    - -[{kernel, - [{logger, - [{handler, default, logger_std_h, - #{ formatter => { logger_formatter,#{ single_line => true}}}}]}]}]. - -
    - -

    Add the pid to each log event

    - -[{kernel, - [{logger, - [{handler, default, logger_std_h, - #{ formatter => { logger_formatter, - #{ template => [time," ",pid," ",msg,"\n"]}} - }}]}]}]. - -
    - -

    Use a different file for debug logging

    - -[{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"}}}} - ]}]}]. - -
    -
    -
    - -
    - Global Logger Configuration - + Primary Logger Configuration +

    Logger API functions that apply to the primary Logger + configuration are:

    + + + get_primary_config/0 + + set_primary_config/1,2 + + update_primary_config/1 + + add_primary_filter/2 + + remove_primary_filter/1 + +

    The primary Logger configuration is a map with the following + keys:

    - level = - logger:level() + + level = + logger:level() | all | none -

    Specifies the global log level to log.

    +

    Specifies the primary log level, that is, log event that + are equally or more severe than this level, are forwarded + to the primary filters. Less severe log events are + immediately discarded.

    See section Log Level for a listing and description of possible log levels.

    The initial value of this option is set by the Kernel - configuration - parameter - logger_level. It can be changed during - runtime - with - logger:set_logger_config(level,NewLevel).

    + configuration parameter + logger_level. It is changed during + runtime with + logger:set_primary_config(level,Level).

    +

    Defaults to info.

    - filters = [{ - logger:filter_id(), - - logger:filter()}] + filters = [{FilterId,Filter}] -

    Global filters are added and removed with - - logger:add_logger_filter/2 and - - logger:remove_logger_filter/1, +

    Specifies the primary filters.

    + + FilterId = + logger:filter_id() + Filter = + logger:filter() + +

    The initial value of this option is set by the Kernel + configuration + parameter logger. + During runtime, primary filters are added and removed with + + logger:add_primary_filter/2 and + + logger:remove_primary_filter/1, respectively.

    See section Filters - for more information.

    -

    Default is [], that is, no filters exist.

    + for more detailed information.

    +

    Defaults to [].

    - filter_default = log | stop + filter_default = log | stop -

    Specifies what to do with an event if all filters +

    Specifies what happens to a log event if all filters return ignore, or if no filters exist.

    See section Filters for more information about how this option is used.

    -

    Default is log.

    +

    Defaults to log.

    @@ -578,26 +518,64 @@ logger:debug(#{got => connection_request, id => Id, state => State},
    Handler Configuration +

    Logger API functions that apply to handler configuration + are:

    + + + get_handler_config/0,1 + + set_handler_config/2,3 + + update_handler_config/2 + + add_handler_filter/3 + + remove_handler_filter/2 + + update_formatter_config/2,3 + +

    The configuration for a handler is a map with the following keys:

    + id = + logger:handler_id() + +

    Automatically inserted by Logger. The value is the same + as the HandlerId specified when adding the handler, + and it cannot be changed.

    +
    + module = module() + +

    Automatically inserted by Logger. The value is the same + as the Module specified when adding the handler, + and it cannot be changed.

    +
    level = - logger:level() + logger:level() | all | none -

    Specifies the log level which the handler logs.

    +

    Specifies the log level for the handler, that is, log + events that are equally or more severe than this level, + are forwarded to the handler filters for this + handler.

    See section Log Level for a listing and description of possible log levels.

    -

    The log level can be specified when adding the handler, - or changed during runtime with, for +

    The log level is specified when adding the handler, or + changed during runtime with, for instance, - logger:set_handler_config/3.

    -

    Default is info.

    + logger:set_handler_config(HandlerId,level,Level). +

    +

    Defaults to all.

    - filters = [{ - logger:filter_id(), - - logger:filter()}] + filters = [{FilterId,Filter}] -

    Handler filters can be specified when adding the handler, +

    Specifies the handler filters.

    + + FilterId = + logger:filter_id() + Filter = + logger:filter() + +

    Handler filters are specified when adding the handler, or added or removed during runtime with logger:add_handler_filter/3 and @@ -605,49 +583,214 @@ logger:debug(#{got => connection_request, id => Id, state => State}, logger:remove_handler_filter/2, respectively.

    See Filters for more - information.

    -

    Default is [], that is, no filters exist.

    + detailed information.

    +

    Defaults to [].

    - filter_default = log | stop + filter_default = log | stop -

    Specifies what to do with an event if all filters +

    Specifies what happens to a log event if all filters return ignore, or if no filters exist.

    See section Filters for more information about how this option is used.

    -

    Default is log.

    +

    Defaults to log.

    - formatter = {module(), - logger:formatter_config()} + formatter = {FormatterModule,FormatterConfig} -

    The formatter which the handler can use for converting - the log event term to a printable string.

    -

    See Formatters for more - information.

    -

    Default - is {logger_formatter,DefaultFormatterConfig}, see +

    Specifies a formatter that the handler can use for + converting the log event term to a printable string.

    + + FormatterModule = module() + FormatterConfig = + + logger:formatter_config() + +

    The formatter information is specified when adding the + handler. The formatter configuration can be changed during + runtime + with + logger:update_formatter_config/2,3, + or the complete formatter information can be overwritten + with, for + instance, + logger:set_handler_config/3.

    +

    See + section Formatters + for more detailed information.

    +

    Defaults + to {logger_formatter,DefaultFormatterConfig}. See the - logger_formatter(3) - manual for information about this formatter and its - default configuration.

    + logger_formatter(3) manual page for + information about this formatter and its default + configuration.

    - HandlerConfig, atom() = term() - -

    Any keys not listed above are considered to be handler - specific configuration. The configuration of the Kernel - handlers can be found in + config = term() + +

    Handler specific configuration, that is, configuration + data related to a specific handler implementation.

    +

    The configuration for the built-in handlers is described + in the logger_std_h(3) and logger_disk_log_h(3) manual pages.

    -
    +

    Notice that level and filters are obeyed by Logger itself before forwarding the log events to each - handler, while formatter and all handle specific + handler, while formatter and all handler specific options are left to the handler implementation.

    -

    All Logger's built-in handlers will call the given formatter - before printing.

    +
    + +
    + + Kernel Configuration Parameters + +

    The following Kernel configuration parameters apply to + Logger:

    + + logger = [Config] + +

    Specifies the configuration + for Logger, except the + primary log level, which is specified + with logger_level, + and the compatibility + with SASL Error + Logging, which is specified + with + logger_sasl_compatible.

    +

    With this parameter, you can modify or disable the default + handler, add custom handlers and primary logger filters, and + set log levels per module.

    +

    Config is any (zero or more) of the following:

    + + {handler, default, undefined} + +

    Disables the default handler. This allows another + application to add its own default handler.

    +

    Only one entry of this type is allowed.

    +
    + {handler, HandlerId, Module, HandlerConfig} + +

    If HandlerId is default, then this entry + modifies the default handler, equivalent to calling

    +
    
    +		logger:set_handler_config(default, Module, HandlerConfig)
    +	      
    +

    For all other values of HandlerId, this entry + adds a new handler, equivalent to calling

    +
    
    +		logger:add_handler(HandlerId, Module, HandlerConfig)
    +	    
    +

    Multiple entries of this type are allowed.

    + {filters, FilterDefault, [Filter]} + +

    Adds the specified primary filters.

    + + FilterDefault = log | stop + Filter = {FilterId, {FilterFun, FilterConfig}} + +

    Equivalent to calling

    +
    
    +		logger:add_primary_filter(FilterId, {FilterFun, FilterConfig})
    +	    
    +

    for each Filter.

    +

    FilterDefault specifies the behaviour if all + primary filters return ignore, see + section Filters.

    +

    Only one entry of this type is allowed.

    +
    + {module_level, Level, [Module]} + +

    Sets module log level for the given modules. Equivalent + to calling

    +
    
    +		logger:set_module_level(Module, Level)
    +

    for each Module.

    +

    Multiple entries of this type are allowed.

    +
    +
    +

    See + section Configuration + Examples for examples using the logger + parameter for system configuration.

    +
    + + logger_level = Level + +

    Specifies the primary log level. See + the kernel(6) + manual page for more information about this parameter.

    +
    + + logger_sasl_compatible = true | false + +

    Specifies Logger's compatibility + with SASL Error + Logging. See + the + kernel(6) manual page for more + information about this parameter.

    +
    +
    +
    + +
    + + Configuration Examples +

    The value of the Kernel configuration parameter logger + is a list of tuples. It is possible to write the term on the + command line when starting an erlang node, but as the term + grows, a better approach is to use the system configuration + file. See + the config(4) manual + page for more information about this file.

    +

    Each of the following examples shows a simple system + configuration file that configures Logger according to the + description.

    +

    Modify the default handler to print to a file instead of + standard_io:

    + +[{kernel, + [{logger, + [{handler, default, logger_std_h, % {handler, HandlerId, Module, + #{config => #{type => {file,"log/erlang.log"}}}} % Config} + ]}]}]. + +

    Modify the default handler to print each log event as a + single line:

    + +[{kernel, + [{logger, + [{handler, default, logger_std_h, + #{formatter => {logger_formatter, #{single_line => true}}}} + ]}]}]. + +

    Modify the default handler to print the pid of the logging + process for each log event:

    + +[{kernel, + [{logger, + [{handler, default, logger_std_h, + #{formatter => {logger_formatter, + #{template => [time," ",pid," ",msg,"\n"]}}}} + ]}]}]. + +

    Modify the default handler to only print errors and more + severe log events to "log/erlang.log", and add another handler + to print all log events to "log/debug.log".

    + +[{kernel, + [{logger, + [{handler, default, logger_std_h, + #{level => error, + config => #{type => {file, "log/erlang.log"}}}}, + {handler, info, logger_std_h, + #{level => debug, + config => #{type => {file, "log/debug.log"}}}} + ]}]}]. +
    @@ -687,8 +830,9 @@ logger:debug(#{got => connection_request, id => Id, state => State},

    To get log events on the same format as produced by error_logger_tty_h and error_logger_file_h, use the default formatter, logger_formatter, with - configuration parameter legacy_header => true. This is - also the default.

    + configuration parameter legacy_header set + to true. This is the default configuration of + the default handler started by Kernel.

    Default Format of Log Events from OTP @@ -700,12 +844,11 @@ logger:debug(#{got => connection_request, id => Id, state => State},

    By SASL reports we mean supervisor reports, crash reports and progress reports.

    -

    In earlier releases, these reports were only logged when - the SASL application was running, and they were printed - trough specific event handlers - named sasl_report_tty_h +

    Prior to Erlang/OTP 21.0, these reports were only logged + when the SASL application was running, and they were printed + trough SASL's own event handlers sasl_report_tty_h and sasl_report_file_h.

    -

    The destination of these log events were configured by +

    The destination of these log events was configured by SASL configuration parameters.

    Due to the specific event handlers, the output format @@ -716,17 +859,20 @@ logger:debug(#{got => connection_request, id => Id, state => State}, Supervisor reports, crash reports, and progress reports are no longer connected to the SASL application. - Supervisor reports and crash reports are logged by - default. - Progress reports are not logged by default, but can be - enabled with the Kernel configuration - parameter - logger_progress_reports. + Supervisor reports and crash reports are issued + as error level log events, and are logged through + the default handler started by Kernel. + Progress reports are issued as info level log + events, and since the default primary log level + is notice, these are not logged by default. To + enable printing of progress reports, set + the primary log + level to info. The output format is the same for all log events. -

    If the old behaviour is preferred, the Kernel configuation - parameter +

    If the old behaviour is preferred, the Kernel configuration + parameter logger_sasl_compatible can be set to true. The SASL @@ -734,11 +880,10 @@ logger:debug(#{got => connection_request, id => Id, state => State}, before, and the SASL reports will only be printed if the SASL application is running, through a second log handler named sasl.

    -

    All SASL reports have a metadata - field domain => [beam,erlang,otp,sasl], which can be - used, for example, by filters to stop or allow the - log events.

    -

    See the SASL User's +

    All SASL reports have a metadata field domain which + is set to [otp,sasl]. This field can be + used by filters to stop or allow the log events.

    +

    See section SASL User's Guide for more information about the old SASL error logging functionality.

    @@ -749,21 +894,22 @@ logger:debug(#{got => connection_request, id => Id, state => State}, error_logger:add_report_handler/1,2. -

    This will automatically start the error_logger - event manager, and add error_logger as a - handler to logger, with configuration

    +

    This automatically starts the error logger event manager, + and adds error_logger as a handler to Logger, with + the following configuration:

    #{level => info, filter_default => log, filters => []}. -

    Notice that this handler will ignore events that do not - originate from the error_logger API, or from within - OTP. This means that if your code uses the Logger API for - logging, then your log events will be discarded by this - handler.

    -

    Also notice that error_logger is not overload - protected.

    + +

    This handler ignores events that do not originate from + the error_logger API, or from within OTP. This + means that if your code uses the Logger API for logging, + then your log events will be discarded by this + handler.

    +

    The handler is not overload protected.

    +
    @@ -771,100 +917,108 @@ error_logger:add_report_handler/1,2.
    Error Handling -

    Log data is expected to be either a format string and - arguments, a string - ( - unicode:chardata()), or a report (map or - key-value list) which can be converted to a format string and - arguments by the handler. If a report is given, a default report - callback can be included in the log event's metadata. The - handler can use this callback for converting the report to a - format string and arguments. If the format obtained by the - provided callback is not desired, or if there is no provided - callback, the handler must do a custom conversion.

    -

    Logger does, to a certain extent, check its input data - before forwarding a log event to the handlers, but it does not - evaluate conversion funs or check the validity of format strings - and arguments. This means that any filter or handler must be - careful when formatting the data of a log event, making sure - that it does not crash due to bad input data or faulty - callbacks.

    +

    Logger does, to a certain extent, check its input data before + forwarding a log event to filters and handlers. It does, + however, not evaluate report callbacks, or check the validity of + format strings and arguments. This means that all filters and + handlers must be careful when formatting the data of a log + event, making sure that it does not crash due to bad input data + or faulty callbacks.

    If a filter or handler still crashes, Logger will remove the filter or handler in question from the configuration, and print a short error message to the terminal. A debug event containing - the crash reason and other details is also issued, and can be - seen if a handler logging debug events is installed.

    + the crash reason and other details is also issued.

    +

    See section Log + Message for more information about report callbacks + and valid forms of log messages.

    - Example: add a handler to log debug events to file + Example: Add a handler to log debug events to file

    When starting an Erlang node, the default behaviour is that all - log events with level info and above are logged to the - terminal. In order to also log debug events, you can either - change the global log level to debug or add a separate - handler to take care of this. In this example we will add a new - handler which prints the debug events to a separate file.

    -

    First, we add an instance of logger_std_h with - type {file,File}, and we set the handler's level - to debug:

    + log events on level info or more severe, are logged to the + terminal via the default handler. To also log debug events, you + can either change the primary log level to debug:

    -1> Config = #{level => debug, logger_std_h => #{type => {file,"./debug.log"}}}.
    -#{logger_std_h => #{type => {file,"./debug.log"}},
    -  level => debug}
    -2> logger:add_handler(debug_handler,logger_std_h,Config).
    +1> logger:set_primary_config(level, debug).
     ok
    -

    By default, the handler receives all events - (filter_default=log, see - section Filters for more - details), so we need to add a filter to stop all non-debug - events. The built-in - filter - logger_filters:level/2 - is used for this:

    +

    or set the level for one or a few modules only:

    -3> logger:add_handler_filter(debug_handler,stop_non_debug,
    -                             {fun logger_filters:level/2,{stop,neq,debug}}).
    +2> logger:set_module_level(mymodule, debug).
     ok
    -

    And finally, we need to make sure that Logger itself allows - debug events. This can either be done by setting the global - log level:

    +

    This allows debug events to pass through to the default handler, + and be printed to the terminal as well. If there are many debug + events, it can be useful to print these to a file instead.

    +

    First, set the log level of the default handler to info, + preventing it from printing debug events to the terminal:

    -4> logger:set_logger_config(level,debug).
    +3> logger:set_handler_config(default, level, info).
     ok
    -

    Or by allowing debug events from one or a few modules only:

    +

    Then, add a new handler which prints to file. You can use the + handler + module logger_std_h, + and specify type {file,File}. The default handler level + is all, so you don't need to specify that:

    +
    +4> Config = #{config => #{type => {file,"./debug.log"}}}.
    +#{config => #{type => {file,"./debug.log"}}}
    +5> logger:add_handler(debugger, logger_std_h, Config).
    +ok
    +

    Since filter_default defaults to log, this + handler now receives all log events. If you want debug events + only in the file, you must add a filter to stop all non-debug + events. The built-in + filter + logger_filters:level/2 + can do this:

    -5> logger:set_module_level(mymodule,debug).
    +6> logger:add_handler_filter(debugger, stop_non_debug,
    +                             {fun logger_filters:level/2, {stop, neq, debug}}).
     ok
    +

    See section Filters for + more information about the filters and the filter_default + configuration parameter.

    - Example: implement a handler -

    The only requirement that a handler MUST fulfill is to export - the following function:

    - log(logger:log_event(),logger:config()) -> ok -

    It can optionally also implement the following callbacks:

    - -adding_handler(logger:config()) -> {ok,logger:config()} | {error,term()} -removing_handler(logger:config()) -> ok -changing_config(logger:config(),logger:config()) -> {ok,logger:config()} | {error,term()} - -

    When logger:add_handler(Id,Module,Config) is called, + Example: Implement a handler +

    Section Handler + Callback Functions in the logger(3) manual page + describes the callback functions that can be implemented for a + Logger handler.

    +

    A handler callback module must export:

    + + log(Log, Config) + +

    It can optionally also export some, or all, of the following:

    + + adding_handler(Config) + removing_handler(Config) + changing_config(OldConfig, NewConfig) + +

    When a handler is added, by for example a call + to + logger:add_handler(Id, HModule, Config), Logger first calls HModule:adding_handler(Config). If - this function returns {ok,NewConfig}, Logger - writes NewConfig to the configuration database, and + this function returns {ok,Config1}, Logger + writes Config1 to the configuration database, and the logger:add_handler/3 call returns. After this, the handler is installed and must be ready to receive log events as calls to HModule:log/2.

    A handler can be removed by calling - logger:remove_handler(Id). Logger calls + + logger:remove_handler(Id). Logger calls HModule:removing_handler(Config), and removes the handler's configuration from the configuration database.

    -

    When logger:set_handler_config/2,3 - or logger:update_handler_config/2 is called, Logger - calls HModule:changing_config(OldConfig,NewConfig). If - this function returns {ok,NewConfig}, Logger - writes NewConfig to the configuration database.

    +

    When + logger:set_handler_config/2,3 + or + logger:update_handler_config/2 is called, + Logger + calls HModule:changing_config(OldConfig, NewConfig). If + this function returns {ok,NewConfig1}, Logger + writes NewConfig1 to the configuration database.

    A simple handler that prints to the terminal can be implemented as follows:

    @@ -872,11 +1026,11 @@ changing_config(logger:config(),logger:config()) -> {ok,logger:config()} | {erro -module(myhandler). -export([log/2]). -log(LogEvent,#{formatter:={FModule,FConfig}) -> - io:put_chars(FModule:format(LogEvent,FConfig)). +log(LogEvent, #{formatter := {FModule, FConfig}) -> + io:put_chars(FModule:format(LogEvent, FConfig)). -

    A simple handler which prints to file could be implemented like +

    A simple handler which prints to file can be implemented like this:

    -module(myhandler). @@ -884,28 +1038,29 @@ log(LogEvent,#{formatter:={FModule,FConfig}) -> -export([init/1, handle_call/3, handle_cast/2, terminate/2]). adding_handler(Config) -> - {ok,Fd} = file:open(File,[append,{encoding,utf8}]), - {ok,Config#{myhandler_fd => Fd}}. + {ok, Fd} = file:open(File, [append, {encoding, utf8}]), + {ok, Config#{myhandler_fd => Fd}}. -removing_handler(#{myhandler_fd:=Fd}) -> +removing_handler(#{myhandler_fd := Fd}) -> _ = file:close(Fd), ok. -log(LogEvent,#{myhandler_fd:=Fd,formatter:={FModule,FConfig}}) -> - io:put_chars(Fd,FModule:format(LogEvent,FConfig)). +log(LogEvent,#{myhandler_fd := Fd, formatter := {FModule, FConfig}}) -> + io:put_chars(Fd, FModule:format(LogEvent, FConfig)). -

    The above handlers do not have any overload - protection, and all log events are printed directly from the - client process.

    - -

    For information and examples of overload protection, please - refer to - section Protecting the - Handler from Overload, and the implementation - of logger_std_h(3) - and logger_disk_log_h(3) - .

    + +

    The above handlers do not have any overload + protection, and all log events are printed directly from the + client process.

    +

    For information and examples of overload protection, please + refer to + section Protecting the + Handler from Overload, and the implementation + of logger_std_h + and logger_disk_log_h + .

    +

    Below is a simpler example of a handler which logs through one single process.

    @@ -915,33 +1070,33 @@ log(LogEvent,#{myhandler_fd:=Fd,formatter:={FModule,FConfig}}) -> -export([init/1, handle_call/3, handle_cast/2, terminate/2]). adding_handler(Config) -> - {ok,Pid} = gen_server:start(?MODULE,Config), - {ok,Config#{myhandler_pid => Pid}}. + {ok, Pid} = gen_server:start(?MODULE, Config), + {ok, Config#{myhandler_pid => Pid}}. -removing_handler(#{myhandler_pid:=Pid}) -> +removing_handler(#{myhandler_pid := Pid}) -> gen_server:stop(Pid). -log(LogEvent,#{myhandler_pid:=Pid} = Config) -> - gen_server:cast(Pid,{log,LogEvent,Config}). +log(LogEvent,#{myhandler_pid := Pid} = Config) -> + gen_server:cast(Pid, {log, LogEvent, Config}). -init(#{myhandler_file:=File}) -> - {ok,Fd} = file:open(File,[append,{encoding,utf8}]), - {ok,#{file => File, fd => Fd}}. +init(#{myhandler_file := File}) -> + {ok, Fd} = file:open(File, [append, {encoding, utf8}]), + {ok, #{file => File, fd => Fd}}. -handle_call(_,_,State) -> - {reply,{error,bad_request},State}. +handle_call(_, _, State) -> + {reply, {error, bad_request}, State}. -handle_cast({log,LogEvent,Config},#{fd:=Fd} = State) -> - do_log(Fd,LogEvent,Config), - {noreply,State}. +handle_cast({log, LogEvent, Config}, #{fd := Fd} = State) -> + do_log(Fd, LogEvent, Config), + {noreply, State}. -terminate(Reason,#{fd:=Fd}) -> +terminate(Reason, #{fd := Fd}) -> _ = file:close(Fd), ok. -do_log(Fd,LogEvent,#{formatter:={FModule,FConfig}}) -> - String = FModule:format(LogEvent,FConfig), - io:put_chars(Fd,String). +do_log(Fd, LogEvent, #{formatter := {FModule, FConfig}}) -> + String = FModule:format(LogEvent, FConfig), + io:put_chars(Fd, String).
    @@ -985,7 +1140,7 @@ do_log(Fd,LogEvent,#{formatter:={FModule,FConfig}}) -> level below the toggle_sync_qlen threshold, asynchronous operation is resumed. The switch from asynchronous to synchronous mode will force the logging tempo of few busy senders to slow down, - but can not protect the handler sufficiently in situations of many + but cannot protect the handler sufficiently in situations of many concurrent senders.

    drop_new_reqs_qlen @@ -1044,11 +1199,11 @@ do_log(Fd,LogEvent,#{formatter:={FModule,FConfig}}) ->

    A configuration example:

    logger:add_handler(my_standard_h, logger_std_h, - #{logger_std_h => - #{type => {file,"./system_info.log"}, - toggle_sync_qlen => 100, - drop_new_reqs_qlen => 1000, - flush_reqs_qlen => 2000}}). + #{config => + #{type => {file,"./system_info.log"}, + toggle_sync_qlen => 100, + drop_new_reqs_qlen => 1000, + flush_reqs_qlen => 2000}}).
    @@ -1086,10 +1241,10 @@ logger:add_handler(my_standard_h, logger_std_h, logger:add_handler(my_disk_log_h, logger_disk_log_h, #{disk_log_opts => - #{file => "./my_disk_log"}, - logger_disk_log_h => - #{burst_limit_size => 10, - burst_window_time => 500}}). + #{file => "./my_disk_log"}, + config => + #{burst_limit_size => 10, + burst_window_time => 500}}).
    diff --git a/lib/kernel/doc/src/logger_disk_log_h.xml b/lib/kernel/doc/src/logger_disk_log_h.xml index 20b49b8ca0..63c29cb010 100644 --- a/lib/kernel/doc/src/logger_disk_log_h.xml +++ b/lib/kernel/doc/src/logger_disk_log_h.xml @@ -44,7 +44,7 @@ the handler configuration.

    The default standard handler, logger_std_h, can be - replaced by a disk_log handler at startup of the Kernel application. + replaced by a disk_log handler at start up of the Kernel application. See an example of this below.

    The handler has an overload protection mechanism that will keep the handler process and the Kernel application alive during a high load of log @@ -60,7 +60,7 @@ , as well as handler specific parameters.

    The settings for the disk_log log file should be specified with the key disk_log_opts. These settings are a subset of the disk_log - datatype + data type dlog_option().

    Parameters in the disk_log_opts map:

    @@ -81,7 +81,7 @@ log.

    Specific configuration for the handler (represented as a sub map) - is specified with the key logger_disk_log_h. It may contain the + is specified with the key config. It may contain the following parameter:

    filesync_repeat_interval @@ -93,7 +93,7 @@ since the last sync. The default value is 5000 milliseconds. If no_repeat is set as value, the repeated sync operation is disabled. The user can also call the - disk_log_sync/1 + sync/1 function to perform a disk_log sync.

    There are a number of other configuration parameters available, that are @@ -117,7 +117,7 @@ logger:add_handler(my_disk_log_h, logger_disk_log_h, type => wrap, max_no_files => 4, max_no_bytes => 10000}, - logger_disk_log_h => + config => #{filesync_repeat_interval => 1000}}).

    In order to use the disk_log handler instead of the default standard @@ -125,14 +125,14 @@ logger:add_handler(my_disk_log_h, logger_disk_log_h, use disk_log. Example:

    erl -kernel logger '[{handler,default,logger_disk_log_h, - #{ disk_log_opts => #{ file => "./system_disk_log"}}}]' + #{disk_log_opts => #{file => "./system_disk_log"}}}]' - + Writes buffered data to disk.

    Write buffered data to disk.

    @@ -143,9 +143,9 @@ erl -kernel logger '[{handler,default,logger_disk_log_h,
    See Also -

    logger(3)

    -

    logger_std_h(3)

    -

    disk_log(3)

    +

    logger(3), + logger_std_h(3), + disk_log(3)

    diff --git a/lib/kernel/doc/src/logger_filters.xml b/lib/kernel/doc/src/logger_filters.xml index f92181ea3f..90f1fcc270 100644 --- a/lib/kernel/doc/src/logger_filters.xml +++ b/lib/kernel/doc/src/logger_filters.xml @@ -36,15 +36,15 @@ Filters to use with Logger. -

    All functions exported from this module can be used as logger +

    All functions exported from this module can be used as primary or handler - filters. See - logger:add_logger_filter/2 + filters. See + logger:add_primary_filter/2 and logger:add_handler_filter/3 for more information about how filters are added.

    -

    Filters are removed with - logger:remove_logger_filter/1 +

    Filters are removed with + logger:remove_primary_filter/1 and logger:remove_handler_filter/2.

    @@ -64,18 +64,18 @@

    A domain field must be a list of atoms, creating smaller and more specialized domains as the list grows longer. The - biggest domain is [], which comprices all - possible domains.

    + greatest domain is [], which comprises all possible + domains.

    For example, consider the following domains:

    -D1 = [beam,erlang,otp]
    -D2 = [beam,erlang,otp,sasl]
    +D1 = [otp] +D2 = [otp, sasl] -

    D1 is the biggest of the two, and is said to be a +

    D1 is the greatest of the two, and is said to be a super-domain of D2. D2 is a sub-domain D1. Both D1 and D2 are - sub-domains of []

    + sub-domains of [].

    The above domains are used for logs originating from Erlang/OTP. D1 specifies that the log event comes from @@ -86,50 +86,50 @@ D2 = [beam,erlang,otp,sasl]

    The Extra parameter to the domain/2 function is specified when adding the - filter via - logger:add_logger_filter/2 + filter via + logger:add_primary_filter/2 or logger:add_handler_filter/3.

    -

    The filter compares the value of the domain field - in the log event's metadata (Domain) - to MatchDomain as follows:

    +

    The filter compares the value of the domain field in + the log event's metadata (Domain) against + MatchDomain. The filter matches if the + value of Compare is:

    - Compare = sub + sub -

    The filter matches if Domain is equal to or - a sub-domain of MatchDomain, that is, - if MatchDomain is a prefix of Domain.

    +

    and Domain is equal to or a sub-domain + of MatchDomain, that is, if MatchDomain is + a prefix of Domain.

    - Compare = super + super -

    The filter matches if Domain is equal to or a - super-domain of MatchDomain, that is, - if Domain is a prefix of MatchDomain.

    +

    and Domain is equal to or a super-domain + of MatchDomain, that is, if Domain is a + prefix of MatchDomain.

    - Compare = equal + equal -

    The filter matches if Domain is equal - to MatchDomain.

    +

    and Domain is equal to MatchDomain.

    - Compare = not_equal + not_equal -

    The filter matches if Domain is not equal - to MatchDomain, or if there is no domain field in - metadata.

    +

    and Domain differs from MatchDomain, or + if there is no domain field in metadata.

    - Compare = undefined -

    The filter matches if there is no domain field in - metadata. In this case MatchDomain - must be set to [].

    + undefined + +

    and there is no domain field in metadata. In this + case MatchDomain must be set + to [].

    -

    If the filter matches and Action = log, - the log event is allowed. If the filter matches - and Action = stop, the log event is - stopped.

    +

    If the filter matches and Action is + log, the log event is allowed. If the filter matches + and Action is stop, the log event + is stopped.

    If the filter does not match, it returns ignore, meaning that other filters, or the value of the @@ -137,16 +137,16 @@ D2 = [beam,erlang,otp,sasl] event is allowed or not.

    Log events that do not contain any domain field, match only - when Compare = undefined - or Compare = not_equal.

    + when Compare is equal + to undefined or not_equal.

    -

    Example: stop all events with - domain [beam,erlang,otp,sasl|_]

    +

    Example: stop all events with domain [otp, + sasl | _]

    -logger:set_handler_config(h1,filter_default,log). % this is the default -Filter = {fun logger_filters:domain/2,{stop,sub,[beam,erlang,otp,sasl]}}. -logger:add_handler_filter(h1,no_sasl,Filter). +logger:set_handler_config(h1, filter_default, log). % this is the default +Filter = {fun logger_filters:domain/2, {stop, sub, [otp, sasl]}}. +logger:add_handler_filter(h1, no_sasl, Filter). ok
    @@ -157,34 +157,44 @@ ok

    This filter provides a way of filtering log events based on the log level. It matches log events by comparing the - log level with a predefined MatchLevel

    + log level with a specified MatchLevel

    The Extra parameter is specified when adding the filter - via - logger:add_logger_filter/2 + via + logger:add_primary_filter/2 or logger:add_handler_filter/3.

    The filter compares the value of the event's log level (Level) to MatchLevel by calling - logger:compare_levels(Level,MatchLevel) -> CmpRet. It - matches the event if:

    - - - CmpRet = eq and Operator = - eq | lteq | gteq - CmpRet = lt and Operator = - lt | lteq | neq - CmpRet = gt and Operator = - gt | gteq | neq - - -

    If the filter matches and Action = - log, the log event is allowed. If the filter matches - and Action = stop, the log event is - stopped.

    + logger:compare_levels(Level, MatchLevel). + The filter matches if the value + of Operator is:

    + + + neq +

    and the compare function returns lt + or gt.

    + eq +

    and the compare function returns eq.

    + lt +

    and the compare function returns lt.

    + gt +

    and the compare function returns gt.

    + lteq +

    and the compare function returns lt + or eq.

    + gteq +

    and the compare function returns gt + or eq.

    +
    + +

    If the filter matches and Action is + log, the log event is allowed. If the filter + matches and Action is stop, the + log event is stopped.

    If the filter does not match, it returns ignore, meaning that other filters, or the value of the @@ -194,9 +204,9 @@ ok

    Example: only allow debug level log events

    -logger:set_handler_config(h1,filter_default,stop). -Filter = {fun logger_filters:level/2,{log,eq,debug}}. -logger:add_handler_filter(h1,debug_only,Filter). +logger:set_handler_config(h1, filter_default, stop). +Filter = {fun logger_filters:level/2, {log, eq, debug}}. +logger:add_handler_filter(h1, debug_only, Filter). ok
    @@ -208,9 +218,9 @@ ok

    This filter matches all progress reports from supervisor and application_controller.

    -

    If Extra = log, the progress reports - are allowed. If Extra = stop, the - progress reports are stopped.

    +

    If Extra is log, the progress + reports are allowed. If Extra + is stop, the progress reports are stopped.

    The filter returns ignore for all other log events.

    @@ -223,9 +233,9 @@ ok

    This filter matches all events originating from a process that has its group leader on a remote node.

    -

    If Extra = log, the matching events - are allowed. If Extra = stop, the - matching events are stopped.

    +

    If Extra is log, the matching + events are allowed. If Extra + is stop, the matching events are stopped.

    The filter returns ignore for all other log events.

    @@ -233,6 +243,12 @@ ok
    +
    + See Also +

    + logger(3) +

    +
    diff --git a/lib/kernel/doc/src/logger_formatter.xml b/lib/kernel/doc/src/logger_formatter.xml index 02f89b26be..59f5dbe367 100644 --- a/lib/kernel/doc/src/logger_formatter.xml +++ b/lib/kernel/doc/src/logger_formatter.xml @@ -36,12 +36,15 @@ Default formatter for Logger. -

    Each log handler has a configured formatter specified as a +

    Each Logger 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 (unicode:chardata() - ) which can be written to the output - device of the handler.

    + ) which can be written to the output device of the + handler. See + sections Handlers + and Formatters + in the Kernel User's Guide for more information.

    logger_formatter is the default formatter used by Logger.

    @@ -55,7 +58,8 @@ map, and the following keys can be set as configuration parameters:

    - chars_limit = pos_integer() | unlimited + + chars_limit = integer() > 0 | unlimited

    A positive integer representing the value of the option with the same name to be used when calling @@ -65,14 +69,8 @@ for each log event. Notice that this is a soft limit. For a hard truncation limit, see option max_size.

    Defaults to unlimited.

    - -

    chars_limit has no effect on log messages on - string form. These are expected to be short, but can - still be truncated by the max_size - parameter.

    -
    - depth = pos_integer() | unlimited + depth = integer() > 0 | unlimited

    A positive integer representing the maximum depth to which terms shall be printed by this formatter. Format @@ -83,26 +81,23 @@ io:format/2,3 in STDLIB.

    Defaults to unlimited.

    - -

    depth has no effect on log messages on string - form. These are expected to be short, but can still be - truncated by the max_size parameter.

    -
    legacy_header = boolean()

    If set to true a header field is added to logger_formatter's part of Metadata. The value of this field is a string similar to the header created by - the old error_logger event handlers. It can be - included in the log event by adding the - tuple {logger_formatter,header} to the + the + old error_logger + event handlers. It can be included in the log event by + adding the list [logger_formatter,header] to the template. See the description of the template() type for more information.

    Defaults to false.

    - max_size = pos_integer() | unlimited + + max_size = integer() > 0 | unlimited

    A positive integer representing the absolute maximum size a string returned from this formatter can have. If the @@ -110,12 +105,12 @@ by chars_limit or depth, it is truncated.

    Defaults to unlimited.

    - report_cb = fun((logger:report()) -> {io:format(),[term()]}) + report_cb = fun((logger:report()) -> {io:format(), [term()]})

    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 + metadata for the log event. If no report callback exists in metadata, logger_formatter will use logger:format_report/1 as default @@ -131,12 +126,14 @@ single_line = boolean()

    If set to true, all newlines in the message are - replaced with ", ", and whitespaces following - directly after newlines are removed. Note that newlines + replaced with ", ", and white spaces following + directly after newlines are removed. Notice that newlines added by the template parameter are not replaced.

    Defaults to true.

    - template = template() + + template = template() +

    The template describes how the formatted string is composed by combining different data values from the log @@ -183,32 +180,43 @@ + + + +

    + + -

    The template is a list of atoms, tuples and strings. The +

    The template is a list of atoms, atom lists, tuples and strings. The atoms level or msg, are treated as placeholders for the severity level and the log message, - respectively. Other atoms or tuples are interpreted as + respectively. Other atoms or atom lists are interpreted as placeholders for metadata, where atoms are expected to match - top level keys, and tuples represent paths to sub keys when + top level keys, and atom lists represent paths to sub keys when the metadata is a nested map. For example the - tuple {key1,key2} is replaced by the value of + list [key1,key2] is replaced by the value of the key2 field in the nested map below. The atom key1 on its own is replaced by the complete value of the key1 field. The values are converted to strings.

    - -#{key1=>#{key2=>my_value, - ...} + +#{key1 => #{key2 => my_value, + ...} ...} -

    Strings in the template are printed literally.

    -

    The default template differs depending on the values - of legacy_header - and single_line:

    +

    Tuples in the template express if-exist tests for metadata + keys. For example, the following tuple says that + if key1 exists in the metadata map, + print "key1=Value", where Value is the value + that key1 is associated with in the metadata map. If + key1 does not exist, print nothing.

    + +{key1, ["key1=",key1], []} +

    Strings in the template are printed literally.

    The default value for the template configuration parameter depends on the value of the single_line and legacy_header configuration parameters as @@ -216,13 +224,13 @@

    The log event used in the examples is:

    -?LOG_ERROR("name: ~p~nexit_reason: ~p",[my_name,"It crashed"]) +?LOG_ERROR("name: ~p~nexit_reason: ~p", [my_name, "It crashed"])
    - legacy_header=true, single_line=false + legacy_header = true, single_line = false

    Default - template: [{logger_formatter,header},"\n",msg,"\n"]

    + template: [[logger_formatter,header],"\n",msg,"\n"]

    Example log entry:

    @@ -231,15 +239,16 @@ name: my_name exit_reason: "It crashed"

    Notice that all eight levels can occur in the heading, - not only ERROR, WARNING or INFO as the - old error_logger produced. And microseconds are - added at the end of the timestamp.

    + not only ERROR, WARNING or INFO as + error_logger + produces. And microseconds are added at the end of the + timestamp.

    - legacy_header=true, single_line=true + legacy_header = true, single_line = true

    Default - template: [{logger_formatter,header},"\n",msg,"\n"]

    + template: [[logger_formatter,header],"\n",msg,"\n"]

    Notice that the template is here the same as for single_line=false, but the resulting log entry @@ -250,7 +259,7 @@ exit_reason: "It crashed" name: my_name, exit_reason: "It crashed" - legacy_header=false, single_line=true + legacy_header = false, single_line = true

    Default template: [time," ",level,": ",msg,"\n"]

    @@ -259,7 +268,7 @@ name: my_name, exit_reason: "It crashed" 2018-05-17T18:31:31.152864+02:00 error: name: my_name, exit_reason: "It crashed"
    - legacy_header=false, single_line=false + legacy_header = false, single_line = false

    Default template: [time," ",level,":\n",msg,"\n"]

    @@ -279,10 +288,21 @@ exit_reason: "It crashed" Validates the given formatter configuration. -

    This callback function is called by Logger when the - formatter configuration for a handler is set or modified. It +

    The function is called by Logger when the formatter + configuration for a handler is set or modified. It returns ok if the configuration is valid, and {error,term()} if it is faulty.

    +

    The following Logger API functions can trigger this callback:

    + + + logger:add_handler/3 + + logger:set_handler_config/2,3 + + logger:updata_handler_config/2 + + logger:update_formatter_config/2 +
    @@ -293,22 +313,38 @@ exit_reason: "It crashed" handlers. The log event is processed as follows:

    If the message is on report form, it is converted to - {Format,Args} by calling the report - callback. - The size is limited according to the values of - configuration parameters chars_limit - and depth. Notice that this does not apply to - messages on string form. + {Format,Args} by calling the report callback. See + section Log + Message in the Kernel User's Guide for more + information about report callbacks and valid forms of log + messages.
    + The message size is limited according to the values of + configuration parameters + chars_limit + and depth. The full log entry is composed according to - the template. + the template.
    If the final string is too long, it is truncated according to the value of configuration - parameter max_size. + parameter max_size. +
    + See Also +

    + calendar(3), + error_logger(3), + io(3), + io_lib(3), + logger(3), + maps(3), + sasl(6), + unicode(3) +

    +
    diff --git a/lib/kernel/doc/src/logger_std_h.xml b/lib/kernel/doc/src/logger_std_h.xml index a4f2848037..89e11389c5 100644 --- a/lib/kernel/doc/src/logger_std_h.xml +++ b/lib/kernel/doc/src/logger_std_h.xml @@ -54,7 +54,7 @@ general configuration parameters, as documented in the User's Guide , as well as handler specific parameters. The specific parameters - are stored in a sub map with the key logger_std_h. The following + are stored in a sub map with the key config. The following keys and values may be specified:

    type @@ -78,7 +78,7 @@ If no_repeat is set as value, the repeated file sync operation is disabled, and it will be the operating system settings that determine how quickly or slowly data gets written to disk. The user can also call - the filesync/1 + the sync/1 function to perform a file sync.

    There are a number of other configuration parameters available, that are @@ -99,26 +99,26 @@ logger:add_handler(my_standard_h, logger_std_h, #{level => info, filter_default => log, - logger_std_h => - #{type => {file,"./system_info.log"}, - filesync_repeat_interval => 1000}}). + config => + #{type => {file,"./system_info.log"}, + filesync_repeat_interval => 1000}}).

    In order to configure the default handler (that starts initially with the Kernel application) to log to file instead of standard_io, change the Kernel default logger to use a file. Example:

    erl -kernel logger '[{handler,default,logger_std_h, - #{ logger_std_h => #{ type => {file,"./log.log"}}}}]' + #{config => #{type => {file,"./log.log"}}}}]'

    An example of how to replace the standard handler with a disk_log handler - at startup can be found in the manual of + at start up can be found in the manual of logger_disk_log_h.

    - + Writes buffered data to disk.

    Write buffered data to disk.

    @@ -129,8 +129,9 @@ erl -kernel logger '[{handler,default,logger_std_h,
    See Also -

    logger(3)

    -

    logger_disk_log_h(3)

    +

    logger(3), + + logger_disk_log_h(3)

    diff --git a/lib/kernel/doc/src/part.xml b/lib/kernel/doc/src/part.xml index 68eb4530e2..fa7e92835f 100644 --- a/lib/kernel/doc/src/part.xml +++ b/lib/kernel/doc/src/part.xml @@ -4,7 +4,7 @@
    - 19962017 + 2018 Ericsson AB. All Rights Reserved. @@ -22,17 +22,14 @@ - Logger User's Guide + Kernel User's Guide OTP Team - 2017-12-01 - 0.1 + 2018-06-06 part.xml
    -

    The System Architecture Support Libraries SASL application - provides support for alarm handling, release handling, and - related functions.

    +

    diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl index 4ebd88ac2d..33b250eef6 100644 --- a/lib/kernel/src/logger_formatter.erl +++ b/lib/kernel/src/logger_formatter.erl @@ -35,7 +35,8 @@ template=>template(), time_designator=>byte(), time_offset=>integer()|[byte()]}. --type template() :: [atom()|tuple()|string()]. +-type template() :: [metakey()|{metakey(),template(),template()}|string()]. +-type metakey() :: atom() | [atom()]. %%%----------------------------------------------------------------- %%% API -- cgit v1.2.3 From 4e429c8ec783947b84ef7e8e337e3af0cd5603ed Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 11 Jun 2018 14:56:34 +0200 Subject: [logger] Skip some overload protection tests when using dirty schedulers --- lib/kernel/test/logger_std_h_SUITE.erl | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index 36b123b07d..27a8db1db6 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -79,6 +79,18 @@ init_per_testcase(TestHooksCase, Config) when ct:print("********** ~w **********", [TestHooksCase]), Config end; +init_per_testcase(OPCase, Config) when + OPCase == qlen_kill_new; + OPCase == restart_after -> + case re:run(erlang:system_info(system_version), + "dirty-schedulers-TEST", + [{capture,none}]) of + match -> + {skip,"Overload protection test skipped on dirty-schedulers-TEST"}; + nomatch -> + ct:print("********** ~w **********", [OPCase]), + Config + end; init_per_testcase(TestCase, Config) -> ct:print("********** ~w **********", [TestCase]), Config. -- cgit v1.2.3 From 42f0d79cf02b4d74508bd4fdeac95574ae811949 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 11 Jun 2018 14:58:03 +0200 Subject: [logger] Skip test involving file access rights on windows --- lib/kernel/test/logger_std_h_SUITE.erl | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index 27a8db1db6..27b1cfc5ea 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -226,11 +226,17 @@ errors(Config) -> logger:add_handler(?MODULE,logger_std_h, #{config => #{type => faulty_type}}), - NoDir = lists:concat(["/",?MODULE,"_dir"]), - {error, - {handler_not_added,{{open_failed,NoDir,eacces},_}}} = - logger:add_handler(myh2,logger_std_h, - #{config=>#{type=>{file,NoDir}}}), + case os:type() of + {win32,_} -> + %% No use in testing file access on windows + ok; + _ -> + NoDir = lists:concat(["/",?MODULE,"_dir"]), + {error, + {handler_not_added,{{open_failed,NoDir,eacces},_}}} = + logger:add_handler(myh2,logger_std_h, + #{config=>#{type=>{file,NoDir}}}) + end, {error, {handler_not_added,{{open_failed,Log,_},_}}} = -- cgit v1.2.3 From 6154d847b67f726f0a459d24bc8128578b1f79f9 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 11 Jun 2018 15:52:47 +0200 Subject: [logger] Sort keys when testing formatter template --- lib/kernel/test/logger_formatter_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl index 864a40b618..8fe8d5199b 100644 --- a/lib/kernel/test/logger_formatter_SUITE.erl +++ b/lib/kernel/test/logger_formatter_SUITE.erl @@ -208,7 +208,7 @@ template(_Config) -> time=>Time, tuple=>{1,atom,"list"}, nested=>#{subkey=>subvalue}}, - Template6 = lists:join(";",maps:keys(maps:remove(nested,Meta6)) ++ + Template6 = lists:join(";",lists:sort(maps:keys(maps:remove(nested,Meta6))) ++ [[nested,subkey]]), String6 = format(info,{"~p",[term]},Meta6,#{template=>Template6, single_line=>true}), -- cgit v1.2.3 From 158bbc210378c53281306d295208ec562b3718af Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 11 Jun 2018 15:59:37 +0200 Subject: [logger] Remove some compiler warnings in test suites --- lib/kernel/test/logger_env_var_SUITE.erl | 3 ++- lib/kernel/test/logger_legacy_SUITE.erl | 12 ++++++---- lib/kernel/test/logger_simple_h_SUITE.erl | 38 +++++++++++++++---------------- lib/kernel/test/logger_std_h_SUITE.erl | 8 +++---- 4 files changed, 32 insertions(+), 29 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl index 6e182d4bca..3a8560bbf5 100644 --- a/lib/kernel/test/logger_env_var_SUITE.erl +++ b/lib/kernel/test/logger_env_var_SUITE.erl @@ -214,6 +214,7 @@ logger_file(Config) -> file,% dest 0),% progress in std logger + notice = maps:get(level,P), #{module:=logger_std_h} = StdC = find(?STANDARD_HANDLER,Hs), all = maps:get(level,StdC), StdFilters = maps:get(filters,StdC), @@ -525,7 +526,7 @@ logger_many_handlers_default_last_broken_filter(Config) -> {logger_level,info}], LogErr, LogInfo, 7). logger_many_handlers(Config, Env, LogErr, LogInfo, NumProgress) -> - {ok,#{handlers:=Hs},Node} = setup(Config,Env), + {ok,_,Node} = setup(Config,Env), check_single_log(Node,LogErr, file,% dest 0,% progress in std logger diff --git a/lib/kernel/test/logger_legacy_SUITE.erl b/lib/kernel/test/logger_legacy_SUITE.erl index de73b6152a..a0d133b7e9 100644 --- a/lib/kernel/test/logger_legacy_SUITE.erl +++ b/lib/kernel/test/logger_legacy_SUITE.erl @@ -20,6 +20,8 @@ -module(logger_legacy_SUITE). -compile(export_all). +-compile({nowarn_deprecated_function,[{gen_fsm,start,3}, + {gen_fsm,send_all_state_event,2}]}). -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/logger.hrl"). @@ -122,7 +124,7 @@ all() -> gen_server(_Config) -> {ok,Pid} = gen_server:start(?MODULE,gen_server,[]), - Msg = fun() -> a=b end, + Msg = fun() -> erlang:error({badmatch,b}) end, Pid ! Msg, ?check({warning_msg,"** Undefined handle_info in ~p"++_,[?MODULE,Msg]}), ok = gen_server:cast(Pid,Msg), @@ -132,7 +134,7 @@ gen_server(_Config) -> gen_event(_Config) -> {ok,Pid} = gen_event:start(), ok = gen_event:add_handler(Pid,?MODULE,gen_event), - Msg = fun() -> a=b end, + Msg = fun() -> erlang:error({badmatch,b}) end, Pid ! Msg, ?check({warning_msg,"** Undefined handle_info in ~tp"++_,[?MODULE,Msg]}), gen_event:notify(Pid,Msg), @@ -141,7 +143,7 @@ gen_event(_Config) -> gen_fsm(_Config) -> {ok,Pid} = gen_fsm:start(?MODULE,gen_fsm,[]), - Msg = fun() -> a=b end, + Msg = fun() -> erlang:error({badmatch,b}) end, Pid ! Msg, ?check({warning_msg,"** Undefined handle_info in ~p"++_,[?MODULE,Msg]}), gen_fsm:send_all_state_event(Pid,Msg), @@ -150,7 +152,7 @@ gen_fsm(_Config) -> gen_statem(_Config) -> {ok,Pid} = gen_statem:start(?MODULE,gen_statem,[]), - Msg = fun() -> a=b end, + Msg = fun() -> erlang:error({badmatch,b}) end, Pid ! Msg, ?check({error,"** State machine ~tp terminating"++_, [Pid,{info,Msg},{mystate,gen_statem},error,{badmatch,b}|_]}). @@ -179,7 +181,7 @@ sasl_reports(Config) -> ok = gen_server:cast(ChPid, fun() -> spawn_link(fun() -> receive x->ok end end) end), - Msg = fun() -> a=b end, + Msg = fun() -> erlang:error({badmatch,b}) end, ok = gen_server:cast(ChPid,Msg), ?check_no_flush({error,"** Generic server ~tp terminating"++_, [ChPid,{'$gen_cast',Msg},gen_server,{{badmatch,b},_}]}), diff --git a/lib/kernel/test/logger_simple_h_SUITE.erl b/lib/kernel/test/logger_simple_h_SUITE.erl index e4e48b538a..79e5c057ad 100644 --- a/lib/kernel/test/logger_simple_h_SUITE.erl +++ b/lib/kernel/test/logger_simple_h_SUITE.erl @@ -107,15 +107,15 @@ start_stop(cleanup,_Config) -> replace_default(Config) -> {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]]), + log(Node, emergency, [?str]), + log(Node, alert, [?str,[]]), + log(Node, error, [?map_rep]), + log(Node, info, [?keyval_rep]), + log(Node, info, [?keyval_rep++[not_key_val]]), + rpc:call(Node, error_logger, error_report, [some_type,?map_rep]), + rpc:call(Node, error_logger, warning_report, ["some_type",?map_rep]), + log(Node, critical, [?str,[?keyval_rep]]), + log(Node, notice, [["fake",string,"line:",?LINE]]), Env = rpc:call(Node, application, get_env, [kernel, logger, []]), ok = rpc:call(Node, logger, add_handlers, [Env]), @@ -127,11 +127,11 @@ replace_file(Config) -> {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, warning, [M4=?keyval_rep]), - log(Node, warning, [M41=?keyval_rep++[not_key_val]]), - log(Node, critical, [M7=?str,[A7=?keyval_rep]]), - log(Node, notice, [M8=["fake",string,"line:",?LINE]]), + log(Node, error, [?map_rep]), + log(Node, warning, [?keyval_rep]), + log(Node, warning, [?keyval_rep++[not_key_val]]), + log(Node, critical, [?str,[?keyval_rep]]), + log(Node, notice, [["fake",string,"line:",?LINE]]), File = filename:join(proplists:get_value(priv_dir,Config), atom_to_list(?FUNCTION_NAME)++".log"), @@ -171,11 +171,11 @@ replace_disk_log(Config) -> {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, warning, [M4=?keyval_rep]), - log(Node, warning, [M41=?keyval_rep++[not_key_val]]), - log(Node, critical, [M7=?str,[A7=?keyval_rep]]), - log(Node, notice, [M8=["fake",string,"line:",?LINE]]), + log(Node, error, [?map_rep]), + log(Node, warning, [?keyval_rep]), + log(Node, warning, [?keyval_rep++[not_key_val]]), + log(Node, critical, [?str,[?keyval_rep]]), + log(Node, notice, [["fake",string,"line:",?LINE]]), File = filename:join(proplists:get_value(priv_dir,Config), atom_to_list(?FUNCTION_NAME)++".log"), diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index 27b1cfc5ea..0bdd7fa00e 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -695,10 +695,10 @@ start_std_h_on_new_node(Config, Log) -> Node. %% 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(). -- cgit v1.2.3 From 686a45b34520c84d9ebfab427a036fa2021371b9 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 11 Jun 2018 18:29:14 +0200 Subject: [logger] Stress overload_kill tests a bit more --- lib/kernel/test/logger_std_h_SUITE.erl | 57 +++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 18 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index 0bdd7fa00e..5253051b02 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -968,7 +968,7 @@ kill_disabled(cleanup, _Config) -> ok = stop_handler(?MODULE). qlen_kill_new(Config) -> - {_Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), Pid0 = whereis(h_proc_name()), {_,Mem0} = process_info(Pid0, memory), RestartAfter = ?OVERLOAD_KILL_RESTART_AFTER, @@ -980,7 +980,7 @@ qlen_kill_new(Config) -> ok = logger:set_handler_config(?MODULE, NewHConfig), MRef = erlang:monitor(process, Pid0), NumOfReqs = 100, - Procs = 2, + Procs = 4, send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice), %% send_burst({n,NumOfReqs}, seq, {chars,79}, notice), receive @@ -991,8 +991,8 @@ qlen_kill_new(Config) -> killed -> ct:pal("Slow shutdown, handler process was killed!", []) end, - timer:sleep(RestartAfter + 2000), - true = is_pid(whereis(h_proc_name())), + file_delete(Log), + {ok,_} = wait_for_process_up(h_proc_name(), RestartAfter * 3), ok after 5000 -> @@ -1019,7 +1019,7 @@ qlen_kill_std(_Config) -> {skip,"Not done yet"}. mem_kill_new(Config) -> - {_Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), + {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), Pid0 = whereis(h_proc_name()), {_,Mem0} = process_info(Pid0, memory), RestartAfter = ?OVERLOAD_KILL_RESTART_AFTER, @@ -1031,7 +1031,7 @@ mem_kill_new(Config) -> ok = logger:set_handler_config(?MODULE, NewHConfig), MRef = erlang:monitor(process, Pid0), NumOfReqs = 100, - Procs = 2, + Procs = 4, send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice), %% send_burst({n,NumOfReqs}, seq, {chars,79}, notice), receive @@ -1042,8 +1042,8 @@ mem_kill_new(Config) -> killed -> ct:pal("Slow shutdown, handler process was killed!", []) end, - timer:sleep(RestartAfter * 3), - true = is_pid(whereis(h_proc_name())), + file_delete(Log), + {ok,_} = wait_for_process_up(h_proc_name(), RestartAfter * 3), ok after 5000 -> @@ -1059,6 +1059,8 @@ mem_kill_new(cleanup, _Config) -> mem_kill_std(_Config) -> {skip,"Not done yet"}. +restart_after() -> + [{timetrap,{minutes,2}}]. restart_after(Config) -> {Log,HConfig,StdHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NewHConfig1 = @@ -1068,14 +1070,16 @@ restart_after(Config) -> ok = logger:set_handler_config(?MODULE, NewHConfig1), MRef1 = erlang:monitor(process, whereis(h_proc_name())), %% kill handler - send_burst({n,100}, {spawn,2,0}, {chars,79}, notice), + send_burst({n,100}, {spawn,4,0}, {chars,79}, notice), receive - {'DOWN', MRef1, _, _, _Info1} -> - timer:sleep(?OVERLOAD_KILL_RESTART_AFTER + 1000), - undefined = whereis(h_proc_name()), + {'DOWN', MRef1, _, _, _Reason1} -> + file_delete(Log), + error = wait_for_process_up(h_proc_name(),?OVERLOAD_KILL_RESTART_AFTER * 3), ok after 5000 -> + Info1 = logger_std_h:info(?MODULE), + ct:pal("Handler state = ~p", [Info1]), ct:fail("Handler not dead! It should not have survived this!") end, @@ -1089,16 +1093,17 @@ restart_after(Config) -> Pid0 = whereis(h_proc_name()), MRef2 = erlang:monitor(process, Pid0), %% kill handler - send_burst({n,100}, {spawn,2,0}, {chars,79}, notice), + send_burst({n,100}, {spawn,4,0}, {chars,79}, notice), receive - {'DOWN', MRef2, _, _, _Info2} -> - timer:sleep(RestartAfter + 2000), - Pid1 = whereis(h_proc_name()), - true = is_pid(Pid1), + {'DOWN', MRef2, _, _, _Reason2} -> + file_delete(Log), + {ok,Pid1} = wait_for_process_up(h_proc_name(),RestartAfter * 3), false = (Pid1 == Pid0), ok after 5000 -> + Info2 = logger_std_h:info(?MODULE), + ct:pal("Handler state = ~p", [Info2]), ct:fail("Handler not dead! It should not have survived this!") end, ok. @@ -1248,7 +1253,8 @@ send_n_burst(N, seq, Text, Class) -> send_n_burst(N-1, seq, Text, Class); send_n_burst(N, {spawn,Ps,TO}, Text, Class) -> ct:pal("~w processes each sending ~w messages", [Ps,N]), - PerProc = fun() -> + PerProc = fun() -> + process_flag(priority,high), send_n_burst(N, seq, Text, Class) end, MRefs = [begin if TO == 0 -> ok; true -> timer:sleep(TO) end, @@ -1559,3 +1565,18 @@ h_proc_name(Name) -> file_delete(Log) -> file:delete(Log). + +wait_for_process_up(Name,T) -> + N = (T div 500) + 1, + wait_for_process_up1(Name,N). + +wait_for_process_up1(Name,0) -> + error; +wait_for_process_up1(Name,N) -> + timer:sleep(500), + case whereis(Name) of + Pid when is_pid(Pid) -> + {ok,Pid}; + undefined -> + wait_for_process_up1(Name,N-1) + end. -- cgit v1.2.3 From e15b8683b041330ce36c86729f72d7c0922a1844 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 11 Jun 2018 19:53:00 +0200 Subject: [logger] Set up priority for processes generatig test bursts --- lib/kernel/test/logger_disk_log_h_SUITE.erl | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 5f7b505ecb..5c23960c90 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -1,3 +1,22 @@ +%% +%% %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_disk_log_h_SUITE). -compile(export_all). @@ -1256,7 +1275,8 @@ send_n_burst(N, seq, Text, Class) -> send_n_burst(N-1, seq, Text, Class); send_n_burst(N, {spawn,Ps,TO}, Text, Class) -> ct:pal("~w processes each sending ~w messages", [Ps,N]), - PerProc = fun() -> + PerProc = fun() -> + process_flag(priority,high), send_n_burst(N, seq, Text, Class) end, MRefs = [begin if TO == 0 -> ok; true -> timer:sleep(TO) end, -- cgit v1.2.3 From ad55c4b112f5231e66f4e61dbdb5bede505fb2e2 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 12 Jun 2018 11:10:28 +0200 Subject: [logger] Don't delete log file of failing test cases --- lib/kernel/test/logger_disk_log_h_SUITE.erl | 21 +++++++++++++-------- lib/kernel/test/logger_std_h_SUITE.erl | 18 +++++++++++------- 2 files changed, 24 insertions(+), 15 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 5c23960c90..7225d4b57c 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -880,8 +880,8 @@ op_switch_to_sync(Config) -> ok = logger:set_handler_config(?MODULE, NewHConfig), send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Lines = count_lines(Log), - ok = file_delete(Log), NumOfReqs = Lines, + ok = file_delete(Log), ok. op_switch_to_sync(cleanup, _Config) -> ok = stop_handler(?MODULE). @@ -911,11 +911,11 @@ op_switch_to_drop(Config) -> _ <- lists:seq(1, Bursts)], Logged = count_lines(Log), ok = stop_handler(?MODULE), - _ = file_delete(Log), ct:pal("Number of messages dropped = ~w (~w)", [Procs*NumOfReqs*Bursts-Logged,Procs*NumOfReqs*Bursts]), true = (Logged < (Procs*NumOfReqs*Bursts)), true = (Logged > 0), + _ = file_delete(Log), ok end, %% As it's tricky to get the timing right in only one go, we perform the @@ -963,11 +963,11 @@ op_switch_to_flush(Config) -> _ <- lists:seq(1,Bursts)], Logged = count_lines(Log), ok= stop_handler(?MODULE), - _ = file_delete(Log), ct:pal("Number of messages flushed/dropped = ~w (~w)", [NumOfReqs*Procs*Bursts-Logged,NumOfReqs*Procs*Bursts]), true = (Logged < (NumOfReqs*Procs*Bursts)), true = (Logged > 0), + _ = file_delete(Log), ok end, %% As it's tricky to get the timing right in only one go, we perform the @@ -995,8 +995,9 @@ limit_burst_disabled(Config) -> send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Logged = count_lines(Log), ct:pal("Number of messages logged = ~w", [Logged]), + NumOfReqs = Logged, ok = file_delete(Log), - NumOfReqs = Logged. + ok. limit_burst_disabled(cleanup, _Config) -> ok = stop_handler(?MODULE). @@ -1014,8 +1015,9 @@ limit_burst_enabled_one(Config) -> send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Logged = count_lines(Log), ct:pal("Number of messages logged = ~w", [Logged]), + ReqLimit = Logged, ok = file_delete(Log), - ReqLimit = Logged. + ok. limit_burst_enabled_one(cleanup, _Config) -> ok = stop_handler(?MODULE). @@ -1036,9 +1038,10 @@ limit_burst_enabled_period(Config) -> Logged = count_lines(Log), ct:pal("Number of messages sent = ~w~nNumber of messages logged = ~w", [Sent,Logged]), - ok = file_delete(Log), true = (Logged > (ReqLimit*Windows)) andalso - (Logged < (ReqLimit*(Windows+2))). + (Logged < (ReqLimit*(Windows+2))), + ok = file_delete(Log), + ok. limit_burst_enabled_period(cleanup, _Config) -> ok = stop_handler(?MODULE). @@ -1239,6 +1242,8 @@ start_handler(Name, FuncName, Config) -> Dir = ?config(priv_dir,Config), File = filename:join(Dir, FuncName), ct:pal("Logging to ~tp", [File]), + FullFile = lists:concat([File,".1"]), + _ = file_delete(FullFile), ok = logger:add_handler(Name, logger_disk_log_h, #{config=>#{file => File, @@ -1248,7 +1253,7 @@ start_handler(Name, FuncName, Config) -> filters=>?DEFAULT_HANDLER_FILTERS([Name]), formatter=>{?MODULE,op}}), {ok,HConfig = #{config := DLHConfig}} = logger:get_handler_config(Name), - {lists:concat([File,".1"]),HConfig,DLHConfig}. + {FullFile,HConfig,DLHConfig}. stop_handler(Name) -> ct:pal("Stopping handler ~p!", [Name]), diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index 5253051b02..e0bc79012c 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -725,7 +725,6 @@ op_switch_to_sync_file(Config) -> %% TRecvPid = start_op_trace(), send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Lines = count_lines(Log), - ok = file_delete(Log), %% true = analyse_trace(TRecvPid, %% fun(Events) -> find_mode(async,Events) end), %% true = analyse_trace(TRecvPid, @@ -738,6 +737,7 @@ op_switch_to_sync_file(Config) -> %% fun(Events) -> find_mode(flush,Events) end), %% stop_op_trace(TRecvPid), NumOfReqs = Lines, + ok = file_delete(Log), ok. op_switch_to_sync_file(cleanup, _Config) -> ok = stop_handler(?MODULE). @@ -782,11 +782,11 @@ op_switch_to_drop_file(Config) -> _ <- lists:seq(1, Bursts)], Logged = count_lines(Log), ok = stop_handler(?MODULE), - _ = file_delete(Log), ct:pal("Number of messages dropped = ~w (~w)", [Procs*NumOfReqs*Bursts-Logged,Procs*NumOfReqs*Bursts]), true = (Logged < (Procs*NumOfReqs*Bursts)), true = (Logged > 0), + _ = file_delete(Log), ok end, %% As it's tricky to get the timing right in only one go, we perform the @@ -850,11 +850,11 @@ op_switch_to_flush_file(Config) -> _ <- lists:seq(1,Bursts)], Logged = count_lines(Log), ok = stop_handler(?MODULE), - _ = file_delete(Log), ct:pal("Number of messages flushed/dropped = ~w (~w)", [NumOfReqs*Procs*Bursts-Logged,NumOfReqs*Procs*Bursts]), true = (Logged < (NumOfReqs*Procs*Bursts)), true = (Logged > 0), + _ = file_delete(Log), ok end, %% As it's tricky to get the timing right in only one go, we perform the @@ -903,8 +903,9 @@ limit_burst_disabled(Config) -> send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Logged = count_lines(Log), ct:pal("Number of messages logged = ~w", [Logged]), + NumOfReqs = Logged, ok = file_delete(Log), - NumOfReqs = Logged. + ok. limit_burst_disabled(cleanup, _Config) -> ok = stop_handler(?MODULE). @@ -922,8 +923,9 @@ limit_burst_enabled_one(Config) -> send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Logged = count_lines(Log), ct:pal("Number of messages logged = ~w", [Logged]), + ReqLimit = Logged, ok = file_delete(Log), - ReqLimit = Logged. + ok. limit_burst_enabled_one(cleanup, _Config) -> ok = stop_handler(?MODULE). @@ -944,9 +946,10 @@ limit_burst_enabled_period(Config) -> Logged = count_lines(Log), ct:pal("Number of messages sent = ~w~nNumber of messages logged = ~w", [Sent,Logged]), - ok = file_delete(Log), true = (Logged > (ReqLimit*Windows)) andalso - (Logged < (ReqLimit*(Windows+2))). + (Logged < (ReqLimit*(Windows+2))), + ok = file_delete(Log), + ok. limit_burst_enabled_period(cleanup, _Config) -> ok = stop_handler(?MODULE). @@ -1183,6 +1186,7 @@ start_handler(Name, FuncName, Config) -> Log = filename:join(Dir, lists:concat([FuncName,".log"])), ct:pal("Logging to ~tp", [Log]), Type = {file,Log}, + _ = file_delete(Log), ok = logger:add_handler(Name, logger_std_h, #{config => #{type => Type}, -- cgit v1.2.3 From a575d83ba9c4336523baa741b22d1de8d6c1ff87 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 12 Jun 2018 14:03:12 +0200 Subject: [logger] Reset logger config after tests --- lib/kernel/test/logger_SUITE.erl | 2 +- lib/kernel/test/logger_legacy_SUITE.erl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index f837c31e64..b367b4dd54 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -329,7 +329,7 @@ log_no_levels(_Config) -> ok. log_no_levels(cleanup,_Config) -> logger:remove_handler(h1), - logger:set_primary_config(level,info), + logger:set_primary_config(level,notice), logger:unset_module_level(?MODULE), ok. diff --git a/lib/kernel/test/logger_legacy_SUITE.erl b/lib/kernel/test/logger_legacy_SUITE.erl index a0d133b7e9..c3cab07d81 100644 --- a/lib/kernel/test/logger_legacy_SUITE.erl +++ b/lib/kernel/test/logger_legacy_SUITE.erl @@ -91,6 +91,7 @@ init_per_group(_Group, Config) -> end_per_group(sasl, Config) -> Apps = ?config(stop_apps,Config), [application:stop(App) || App <- Apps], + ok = logger:set_primary_config(level,notice), ok; end_per_group(_Group, _Config) -> ok. -- cgit v1.2.3 From b0900fc2f7daab1c77dd8878c39ab24591bb076b Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Thu, 5 Apr 2018 01:40:20 +0200 Subject: Explain why the AM becomes group leader --- lib/kernel/doc/src/application.xml | 10 +++++++--- lib/kernel/src/application_master.erl | 4 ++++ 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/application.xml b/lib/kernel/doc/src/application.xml index 886286b76d..be914aee87 100644 --- a/lib/kernel/doc/src/application.xml +++ b/lib/kernel/doc/src/application.xml @@ -318,8 +318,13 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}] {error,{not_started,App}} is returned, where App is the name of the missing application.

    The application controller then creates an application master - for the application. The application master is - the group leader of all the processes in the application. + for the application. The application master becomes the + group leader of all the processes in the application. I/O is + forwarded to the previous group leader, though, this is just + a way to identify processes that belong to the application. + Used for example to find itself from any process, or, + reciprocally, to kill them all when it terminates.

    +

    The application master starts the application by calling the application callback function Module:start/2 as defined by the application specification key mod.

    @@ -608,4 +613,3 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}] app(4)

    - diff --git a/lib/kernel/src/application_master.erl b/lib/kernel/src/application_master.erl index 5da2b0b06c..06991b45e1 100644 --- a/lib/kernel/src/application_master.erl +++ b/lib/kernel/src/application_master.erl @@ -118,6 +118,10 @@ init(Parent, Starter, ApplData, Type) -> link(Parent), process_flag(trap_exit, true), OldGleader = group_leader(), + %% We become the group leader, but forward all I/O to OldGleader. + %% This is just a way to identify processes that belong to the + %% application. Used for example to find ourselves from any + %% process, or, reciprocally, to kill them all when we terminate. group_leader(self(), self()), %% Insert ourselves as master for the process. This ensures that %% the processes in the application can use get_env/1 at startup. -- cgit v1.2.3 From 950a7175e2d5f2f2c5c50c81ef4a6a0ecbb38530 Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Wed, 13 Jun 2018 12:18:37 +0200 Subject: [logger] Change name of function sync/1 to filesync/1 --- lib/kernel/src/logger_disk_log_h.erl | 12 +++++----- lib/kernel/src/logger_h_common.erl | 2 +- lib/kernel/src/logger_std_h.erl | 12 +++++----- lib/kernel/test/logger_disk_log_h_SUITE.erl | 34 ++++++++++++++--------------- lib/kernel/test/logger_std_h_SUITE.erl | 22 +++++++++---------- 5 files changed, 41 insertions(+), 41 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index a074d0210e..f08621c9c5 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -26,7 +26,7 @@ -include("logger_h_common.hrl"). %%% API --export([start_link/3, info/1, sync/1, reset/1]). +-export([start_link/3, info/1, filesync/1, reset/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, @@ -58,19 +58,19 @@ start_link(Name, Config, HandlerState) -> %%%----------------------------------------------------------------- %%% --spec sync(Name) -> ok | {error,Reason} when +-spec filesync(Name) -> ok | {error,Reason} when Name :: atom(), Reason :: handler_busy | {badarg,term()}. -sync(Name) when is_atom(Name) -> +filesync(Name) when is_atom(Name) -> try gen_server:call(?name_to_reg_name(?MODULE,Name), disk_log_sync, ?DEFAULT_CALL_TIMEOUT) catch _:{timeout,_} -> {error,handler_busy} end; -sync(Name) -> - {error,{badarg,{sync,[Name]}}}. +filesync(Name) -> + {error,{badarg,{filesync,[Name]}}}. %%%----------------------------------------------------------------- %%% @@ -704,7 +704,7 @@ disk_log_sync(Name, State) -> ok; _ -> LogOpts = maps:get(log_opts, State), - logger_h_common:error_notify({Name,sync, + logger_h_common:error_notify({Name,filesync, LogOpts, SyncError}) end, diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl index d556938f02..1458c55e80 100644 --- a/lib/kernel/src/logger_h_common.erl +++ b/lib/kernel/src/logger_h_common.erl @@ -261,7 +261,7 @@ flush_log_events(Limit, Limit) -> Limit; flush_log_events(N, Limit) -> %% flush log events but leave other events, such as - %% file/disk_log_sync, info and change_config, so that these + %% filesync, info and change_config, so that these %% have a chance to be processed even under heavy load receive {'$gen_cast',{log,_}} -> diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index ce9daa50ab..a03e1ffc2a 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -28,7 +28,7 @@ -include_lib("kernel/include/file.hrl"). %% API --export([start_link/3, info/1, sync/1, reset/1]). +-export([start_link/3, info/1, filesync/1, reset/1]). %% gen_server and proc_lib callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, @@ -60,19 +60,19 @@ start_link(Name, Config, HandlerState) -> %%%----------------------------------------------------------------- %%% --spec sync(Name) -> ok | {error,Reason} when +-spec filesync(Name) -> ok | {error,Reason} when Name :: atom(), Reason :: handler_busy | {badarg,term()}. -sync(Name) when is_atom(Name) -> +filesync(Name) when is_atom(Name) -> try gen_server:call(?name_to_reg_name(?MODULE,Name), filesync, ?DEFAULT_CALL_TIMEOUT) catch _:{timeout,_} -> {error,handler_busy} end; -sync(Name) -> - {error,{badarg,{sync,[Name]}}}. +filesync(Name) -> + {error,{badarg,{filesync,[Name]}}}. %%%----------------------------------------------------------------- %%% @@ -816,7 +816,7 @@ sync_dev(Fd, DevName, PrevSyncResult, HandlerName) -> %% don't report same error twice PrevSyncResult; Error -> - logger_h_common:error_notify({HandlerName,sync,DevName,Error}), + logger_h_common:error_notify({HandlerName,filesync,DevName,Error}), Error end. diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 7225d4b57c..e1b1cbcb1a 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -143,7 +143,7 @@ create_log(Config) -> formatter=>{?MODULE,self()}}, #{file=>LogFile1}), logger:notice("hello", ?domain), - logger_disk_log_h:sync(Name1), + logger_disk_log_h:filesync(Name1), ct:pal("Checking contents of ~p", [?log_no(LogFile1,1)]), try_read_file(?log_no(LogFile1,1), {ok,<<"hello\n">>}, 5000), @@ -156,7 +156,7 @@ create_log(Config) -> formatter=>{?MODULE,self()}}, #{file=>LogFile2}), logger:notice("dummy", ?domain), - logger_disk_log_h:sync(Name2), + logger_disk_log_h:filesync(Name2), ct:pal("Checking contents of ~p", [?log_no(LogFile2,1)]), try_read_file(?log_no(LogFile2,1), {ok,<<"dummy\n">>}, 5000), @@ -177,7 +177,7 @@ open_existing_log(Config) -> formatter=>{?MODULE,self()}}, #{file=>LogFile1}), logger:notice("one", ?domain), - logger_disk_log_h:sync(HName), + logger_disk_log_h:filesync(HName), ct:pal("Checking contents of ~p", [?log_no(LogFile1,1)]), try_read_file(?log_no(LogFile1,1), {ok,<<"one\n">>}, 5000), logger:notice("two", ?domain), @@ -191,7 +191,7 @@ open_existing_log(Config) -> formatter=>{?MODULE,self()}}, #{file=>LogFile1}), logger:notice("three", ?domain), - logger_disk_log_h:sync(HName), + logger_disk_log_h:filesync(HName), try_read_file(?log_no(LogFile1,1), {ok,<<"one\ntwo\nthree\n">>}, 5000), remove_and_stop(HName), try_read_file(?log_no(LogFile1,1), {ok,<<"one\ntwo\nthree\n">>}, 5000). @@ -216,22 +216,22 @@ disk_log_opts(Config) -> {WFileFull,wrap,{Size,2},1} = {Get(file,WInfo1),Get(type,WInfo1), Get(size,WInfo1),Get(current_file,WInfo1)}, logger:notice("123", ?domain), - logger_disk_log_h:sync(WName), + logger_disk_log_h:filesync(WName), timer:sleep(500), 1 = Get(current_file, disk_log:info(WName)), logger:notice("45", ?domain), - logger_disk_log_h:sync(WName), + logger_disk_log_h:filesync(WName), timer:sleep(500), 1 = Get(current_file, disk_log:info(WName)), logger:notice("6", ?domain), - logger_disk_log_h:sync(WName), + logger_disk_log_h:filesync(WName), timer:sleep(500), 2 = Get(current_file, disk_log:info(WName)), logger:notice("7890", ?domain), - logger_disk_log_h:sync(WName), + logger_disk_log_h:filesync(WName), timer:sleep(500), 2 = Get(current_file, disk_log:info(WName)), @@ -249,7 +249,7 @@ disk_log_opts(Config) -> {HFile1Full,halt,infinity} = {Get(file,HInfo1),Get(type,HInfo1), Get(size,HInfo1)}, logger:notice("12345", ?domain), - logger_disk_log_h:sync(HName1), + logger_disk_log_h:filesync(HName1), timer:sleep(500), 1 = Get(no_written_items, disk_log:info(HName1)), @@ -426,8 +426,8 @@ config_fail(cleanup,_Config) -> logger:remove_handler(?MODULE). bad_input(_Config) -> - {error,{badarg,{sync,["BadType"]}}} = - logger_disk_log_h:sync("BadType"), + {error,{badarg,{filesync,["BadType"]}}} = + logger_disk_log_h:filesync("BadType"), {error,{badarg,{info,["BadType"]}}} = logger_disk_log_h:info("BadType"), {error,{badarg,{reset,["BadType"]}}} = logger_disk_log_h:reset("BadType"). @@ -545,7 +545,7 @@ sync(Config) -> logger:notice("second", ?domain), logger:notice("third", ?domain), %% do explicit sync - logger_disk_log_h:sync(?MODULE), + logger_disk_log_h:filesync(?MODULE), check_tracer(100), %% check that if there's no repeated disk_log_sync active, @@ -758,7 +758,7 @@ write_failure(Config) -> ct:pal("LogOpts = ~p", [LogOpts = maps:get(log_opts, HState)]), ok = log_on_remote_node(Node, "Logged1"), - rpc:call(Node, logger_disk_log_h, sync, [?STANDARD_HANDLER]), + rpc:call(Node, logger_disk_log_h, filesync, [?STANDARD_HANDLER]), ?check_no_log, try_read_file(Log, {ok,<<"Logged1\n">>}, ?SYNC_REP_INT), @@ -778,7 +778,7 @@ write_failure(Config) -> rpc:call(Node, ?MODULE, set_result, [disk_log_blog,ok]), ok = log_on_remote_node(Node, "Logged2"), - rpc:call(Node, logger_disk_log_h, sync, [?STANDARD_HANDLER]), + rpc:call(Node, logger_disk_log_h, filesync, [?STANDARD_HANDLER]), ?check_no_log, try_read_file(Log, {ok,<<"Logged1\nLogged2\n">>}, ?SYNC_REP_INT), ok. @@ -814,7 +814,7 @@ sync_failure(Config) -> rpc:call(Node, ?MODULE, set_result, [disk_log_sync,{error,no_such_log}]), ok = log_on_remote_node(Node, "Cause simple error printout"), - ?check({error,{?STANDARD_HANDLER,sync,LogOpts,{error,no_such_log}}}), + ?check({error,{?STANDARD_HANDLER,filesync,LogOpts,{error,no_such_log}}}), ok = log_on_remote_node(Node, "No second error printout"), ?check_no_log, @@ -822,7 +822,7 @@ sync_failure(Config) -> rpc:call(Node, ?MODULE, set_result, [disk_log_sync,{error,{blocked_log,?STANDARD_HANDLER}}]), ok = log_on_remote_node(Node, "Cause simple error printout"), - ?check({error,{?STANDARD_HANDLER,sync,LogOpts, + ?check({error,{?STANDARD_HANDLER,filesync,LogOpts, {error,{blocked_log,?STANDARD_HANDLER}}}}), rpc:call(Node, ?MODULE, set_result, [disk_log_sync,ok]), @@ -1195,7 +1195,7 @@ handler_requests_under_load(Config) -> flush_qlen => 2000, burst_limit_enable => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), - Pid = spawn_link(fun() -> send_requests(?MODULE, 1, [{sync,[]}, + Pid = spawn_link(fun() -> send_requests(?MODULE, 1, [{filesync,[]}, {info,[]}, {reset,[]}, {change_config,[]}]) diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index e0bc79012c..5227cac4e7 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -411,7 +411,7 @@ crash_std_h(cleanup) -> [test_server:stop_node(Node) || Node <- Nodes]. sync_and_read(Node,disk_log,Log) -> - rpc:call(Node,logger_disk_log_h,sync,[?STANDARD_HANDLER]), + rpc:call(Node,logger_disk_log_h,filesync,[?STANDARD_HANDLER]), case file:read_file(Log ++ ".1") of {ok,<<>>} -> timer:sleep(5000), @@ -420,7 +420,7 @@ sync_and_read(Node,disk_log,Log) -> Ok end; sync_and_read(Node,file,Log) -> - rpc:call(Node,logger_std_h,sync,[?STANDARD_HANDLER]), + rpc:call(Node,logger_std_h,filesync,[?STANDARD_HANDLER]), case file:read_file(Log) of {ok,<<>>} -> timer:sleep(5000), @@ -430,7 +430,7 @@ sync_and_read(Node,file,Log) -> end. bad_input(_Config) -> - {error,{badarg,{sync,["BadType"]}}} = logger_std_h:sync("BadType"), + {error,{badarg,{filesync,["BadType"]}}} = logger_std_h:filesync("BadType"), {error,{badarg,{info,["BadType"]}}} = logger_std_h:info("BadType"), {error,{badarg,{reset,["BadType"]}}} = logger_std_h:reset("BadType"). @@ -557,9 +557,9 @@ sync(Config) -> ]), logger:notice("second", ?domain), %% do explicit sync - logger_std_h:sync(?MODULE), + logger_std_h:filesync(?MODULE), %% a second sync should be ignored - logger_std_h:sync(?MODULE), + logger_std_h:filesync(?MODULE), check_tracer(100), %% check that if there's no repeated filesync active, @@ -618,7 +618,7 @@ write_failure(Config) -> rpc:call(Node, ?MODULE, set_result, [file_write,ok]), ok = log_on_remote_node(Node, "Logged1"), - rpc:call(Node, logger_std_h, sync, [?STANDARD_HANDLER]), + rpc:call(Node, logger_std_h, filesync, [?STANDARD_HANDLER]), ?check_no_log, try_read_file(Log, {ok,<<"Logged1\n">>}, ?FILESYNC_REP_INT), @@ -636,7 +636,7 @@ write_failure(Config) -> rpc:call(Node, ?MODULE, set_result, [file_write,ok]), ok = log_on_remote_node(Node, "Logged2"), - rpc:call(Node, logger_std_h, sync, [?STANDARD_HANDLER]), + rpc:call(Node, logger_std_h, filesync, [?STANDARD_HANDLER]), ?check_no_log, try_read_file(Log, {ok,<<"Logged1\nLogged2\n">>}, ?FILESYNC_REP_INT), ok. @@ -667,14 +667,14 @@ sync_failure(Config) -> rpc:call(Node, ?MODULE, set_result, [file_datasync,{error,terminated}]), ok = log_on_remote_node(Node, "Cause simple error printout"), - ?check({error,{?STANDARD_HANDLER,sync,Log,{error,terminated}}}), + ?check({error,{?STANDARD_HANDLER,filesync,Log,{error,terminated}}}), ok = log_on_remote_node(Node, "No second error printout"), ?check_no_log, rpc:call(Node, ?MODULE, set_result, [file_datasync,{error,eacces}]), ok = log_on_remote_node(Node, "Cause simple error printout"), - ?check({error,{?STANDARD_HANDLER,sync,Log,{error,eacces}}}), + ?check({error,{?STANDARD_HANDLER,filesync,Log,{error,eacces}}}), rpc:call(Node, ?MODULE, set_result, [file_datasync,ok]), ok = log_on_remote_node(Node, "Logged2"), @@ -1127,7 +1127,7 @@ handler_requests_under_load(Config) -> flush_qlen => 2000, burst_limit_enable => false}}, ok = logger:set_handler_config(?MODULE, NewHConfig), - Pid = spawn_link(fun() -> send_requests(?MODULE, 1, [{sync,[]}, + Pid = spawn_link(fun() -> send_requests(?MODULE, 1, [{filesync,[]}, {info,[]}, {reset,[]}, {change_config,[]}]) @@ -1328,7 +1328,7 @@ add_remove_instance_nofile(Type) -> logger:notice(M1=?msg,?domain), ?check(M1), %% check that sync doesn't do damage even if not relevant - ok = logger_std_h:sync(?MODULE), + ok = logger_std_h:filesync(?MODULE), ok = logger:remove_handler(?MODULE), timer:sleep(500), undefined = whereis(h_proc_name()), -- cgit v1.2.3 From 46a02602af6265888c426b3ca6eb426600437406 Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Wed, 13 Jun 2018 12:30:37 +0200 Subject: [logger] Change overload_kill_restart_after value to infinity --- lib/kernel/src/logger_h_common.erl | 2 +- lib/kernel/src/logger_h_common.hrl | 4 ++-- lib/kernel/test/logger_disk_log_h_SUITE.erl | 6 +++--- lib/kernel/test/logger_std_h_SUITE.erl | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl index 1458c55e80..f9f762405c 100644 --- a/lib/kernel/src/logger_h_common.erl +++ b/lib/kernel/src/logger_h_common.erl @@ -115,7 +115,7 @@ check_common_config({overload_kill_qlen,N}) when is_integer(N) -> check_common_config({overload_kill_mem_size,N}) when is_integer(N) -> valid; check_common_config({overload_kill_restart_after,NorA}) when is_integer(NorA); - NorA == never -> + NorA == infinity -> valid; check_common_config({filesync_repeat_interval,NorA}) when is_integer(NorA); diff --git a/lib/kernel/src/logger_h_common.hrl b/lib/kernel/src/logger_h_common.hrl index ad80b51109..e0a7b6e3ca 100644 --- a/lib/kernel/src/logger_h_common.hrl +++ b/lib/kernel/src/logger_h_common.hrl @@ -41,10 +41,10 @@ -define(OVERLOAD_KILL_MEM_SIZE, 3000000). %% This is the default time that the handler will wait before -%% restarting and accepting new requests. The value 'never' +%% restarting and accepting new requests. The value 'infinity' %% disables restarts. -define(OVERLOAD_KILL_RESTART_AFTER, 5000). -%%-define(OVERLOAD_KILL_RESTART_AFTER, never). +%%-define(OVERLOAD_KILL_RESTART_AFTER, infinity). %% The handler sends asynchronous write requests to the process %% controlling the i/o device, but every once in this interval diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index e1b1cbcb1a..2f4ee504ea 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -475,7 +475,7 @@ reconfig(Config) -> overload_kill_enable => true, overload_kill_qlen => 100000, overload_kill_mem_size => 10000000, - overload_kill_restart_after => never, + overload_kill_restart_after => infinity, filesync_repeat_interval => no_repeat}, ok = logger:set_handler_config(?MODULE, config, HConfig1), #{id := ?MODULE, @@ -488,7 +488,7 @@ reconfig(Config) -> overload_kill_enable := true, overload_kill_qlen := 100000, overload_kill_mem_size := 10000000, - overload_kill_restart_after := never, + overload_kill_restart_after := infinity, filesync_repeat_interval := no_repeat} = logger_disk_log_h:info(?MODULE), @@ -1141,7 +1141,7 @@ restart_after(Config) -> NewHConfig1 = HConfig#{config=>DLHConfig#{overload_kill_enable=>true, overload_kill_qlen=>10, - overload_kill_restart_after=>never}}, + overload_kill_restart_after=>infinity}}, ok = logger:set_handler_config(?MODULE, NewHConfig1), MRef1 = erlang:monitor(process, whereis(h_proc_name())), %% kill handler diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index 5227cac4e7..675e8d731f 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -473,7 +473,7 @@ reconfig(Config) -> overload_kill_enable => true, overload_kill_qlen => 100000, overload_kill_mem_size => 10000000, - overload_kill_restart_after => never, + overload_kill_restart_after => infinity, filesync_repeat_interval => no_repeat}), #{id := ?MODULE, type := standard_io, @@ -487,7 +487,7 @@ reconfig(Config) -> overload_kill_enable := true, overload_kill_qlen := 100000, overload_kill_mem_size := 10000000, - overload_kill_restart_after := never, + overload_kill_restart_after := infinity, filesync_repeat_interval := no_repeat} = logger_std_h:info(?MODULE), ok. @@ -1069,7 +1069,7 @@ restart_after(Config) -> NewHConfig1 = HConfig#{config=>StdHConfig#{overload_kill_enable=>true, overload_kill_qlen=>10, - overload_kill_restart_after=>never}}, + overload_kill_restart_after=>infinity}}, ok = logger:set_handler_config(?MODULE, NewHConfig1), MRef1 = erlang:monitor(process, whereis(h_proc_name())), %% kill handler -- cgit v1.2.3 From e7b7ae45884002c075697ff250400b247d158405 Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Thu, 14 Jun 2018 11:15:21 +0200 Subject: [logger] Fix failing tests --- lib/kernel/test/logger_env_var_SUITE.erl | 2 +- lib/kernel/test/logger_test_lib.erl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl index 3a8560bbf5..04a4364947 100644 --- a/lib/kernel/test/logger_env_var_SUITE.erl +++ b/lib/kernel/test/logger_env_var_SUITE.erl @@ -531,7 +531,7 @@ logger_many_handlers(Config, Env, LogErr, LogInfo, NumProgress) -> file,% dest 0,% progress in std logger error), % level - ok = rpc:call(Node,logger_std_h,sync,[info]), + ok = rpc:call(Node,logger_std_h,filesync,[info]), {ok, Bin} = file:read_file(LogInfo), ct:log("Log content:~n~s",[Bin]), match(Bin,<<"info:">>,NumProgress,info,info), diff --git a/lib/kernel/test/logger_test_lib.erl b/lib/kernel/test/logger_test_lib.erl index 9097453c10..81eb9ce5eb 100644 --- a/lib/kernel/test/logger_test_lib.erl +++ b/lib/kernel/test/logger_test_lib.erl @@ -52,10 +52,10 @@ log(Node, M, F, A) -> rpc:call(Node, M, F, A ++ [MD]). sync_and_read(Node,disk_log,Log) -> - rpc:call(Node,logger_disk_log_h,sync,[?STANDARD_HANDLER]), + rpc:call(Node,logger_disk_log_h,filesync,[?STANDARD_HANDLER]), file:read_file(Log ++ ".1"); sync_and_read(Node, file,Log) -> - ok = rpc:call(Node,logger_std_h,sync,[?STANDARD_HANDLER]), + ok = rpc:call(Node,logger_std_h,filesync,[?STANDARD_HANDLER]), file:read_file(Log). -- cgit v1.2.3 From 2ed5c964d31805b431d70e2d8b59260a76a809d5 Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Thu, 14 Jun 2018 15:23:01 +0200 Subject: [logger] Fix test suite compilation warnings --- lib/kernel/test/logger_disk_log_h_SUITE.erl | 25 ++++++++++++--------- lib/kernel/test/logger_std_h_SUITE.erl | 35 ++++++++++++++++------------- 2 files changed, 33 insertions(+), 27 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 2f4ee504ea..4c6fa1b5af 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -45,10 +45,6 @@ -define(log_no(File,N), lists:concat([File,".",N])). -define(domain,#{domain=>[?MODULE]}). --define(SYNC_REP_INT, if is_atom(?FILESYNC_REPEAT_INTERVAL) -> 5500; - true -> ?FILESYNC_REPEAT_INTERVAL + 500 - end). - suite() -> [{timetrap,{seconds,30}}, {ct_hooks,[logger_test_lib]}]. @@ -69,9 +65,10 @@ end_per_group(_Group, _Config) -> init_per_testcase(TestHooksCase, Config) when TestHooksCase == write_failure; TestHooksCase == sync_failure -> - if ?TEST_HOOKS_TAB == undefined -> + case (fun() -> ?TEST_HOOKS_TAB == undefined end)() of + true -> {skip,"Define the TEST_HOOKS macro to run this test"}; - true -> + false -> ct:print("********** ~w **********", [TestHooksCase]), Config end; @@ -760,7 +757,13 @@ write_failure(Config) -> ok = log_on_remote_node(Node, "Logged1"), rpc:call(Node, logger_disk_log_h, filesync, [?STANDARD_HANDLER]), ?check_no_log, - try_read_file(Log, {ok,<<"Logged1\n">>}, ?SYNC_REP_INT), + + SyncRepInt = case (fun() -> is_atom(?FILESYNC_REPEAT_INTERVAL) end)() of + true -> 5500; + false -> ?FILESYNC_REPEAT_INTERVAL + 500 + end, + + try_read_file(Log, {ok,<<"Logged1\n">>}, SyncRepInt), rpc:call(Node, ?MODULE, set_result, [disk_log_blog,{error,no_such_log}]), ok = log_on_remote_node(Node, "Cause simple error printout"), @@ -780,7 +783,7 @@ write_failure(Config) -> ok = log_on_remote_node(Node, "Logged2"), rpc:call(Node, logger_disk_log_h, filesync, [?STANDARD_HANDLER]), ?check_no_log, - try_read_file(Log, {ok,<<"Logged1\nLogged2\n">>}, ?SYNC_REP_INT), + try_read_file(Log, {ok,<<"Logged1\nLogged2\n">>}, SyncRepInt), ok. write_failure(cleanup, _Config) -> Nodes = nodes(), @@ -1424,10 +1427,10 @@ wait_until_written(File, Sz) -> {ok,#file_info{size = Sz}} -> timer:sleep(1000), case file:read_file_info(File) of - {ok,#file_info{size = Sz1}} -> + {ok,#file_info{size = Sz}} -> ok; - {ok,#file_info{size = Sz2}} -> - wait_until_written(File, Sz2) + {ok,#file_info{size = Sz1}} -> + wait_until_written(File, Sz1) end; {ok,#file_info{size = Sz1}} -> wait_until_written(File, Sz1) diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index 675e8d731f..134358bb64 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -45,10 +45,6 @@ -define(bin(Msg), list_to_binary(Msg++"\n")). -define(domain,#{domain=>[?MODULE]}). --define(FILESYNC_REP_INT, if is_atom(?FILESYNC_REPEAT_INTERVAL) -> 5500; - true -> ?FILESYNC_REPEAT_INTERVAL + 500 - end). - suite() -> [{timetrap,{seconds,30}}, {ct_hooks,[logger_test_lib]}]. @@ -73,9 +69,10 @@ end_per_group(_Group, _Config) -> init_per_testcase(TestHooksCase, Config) when TestHooksCase == write_failure; TestHooksCase == sync_failure -> - if ?TEST_HOOKS_TAB == undefined -> + case (fun() -> ?TEST_HOOKS_TAB == undefined end)() of + true -> {skip,"Define the TEST_HOOKS macro to run this test"}; - true -> + false -> ct:print("********** ~w **********", [TestHooksCase]), Config end; @@ -187,13 +184,13 @@ add_remove_instance_file(Log, Type) -> logger:notice(M1=?msg,?domain), ?check(M1), B1 = ?bin(M1), - try_read_file(Log, {ok,B1}, ?FILESYNC_REP_INT), + try_read_file(Log, {ok,B1}, filesync_rep_int()), ok = logger:remove_handler(?MODULE), timer:sleep(500), undefined = whereis(h_proc_name()), logger:notice(?msg,?domain), ?check_no_log, - try_read_file(Log, {ok,B1}, ?FILESYNC_REP_INT), + try_read_file(Log, {ok,B1}, filesync_rep_int()), ok. default_formatter(_Config) -> @@ -517,7 +514,7 @@ file_opts(Config) -> logger:notice(M1=?msg,?domain), ?check(M1), B1 = ?bin(M1), - try_read_file(Log, {ok,B1}, ?FILESYNC_REP_INT), + try_read_file(Log, {ok,B1}, filesync_rep_int()), ok. file_opts(cleanup, _Config) -> logger:remove_handler(?MODULE). @@ -544,7 +541,7 @@ sync(Config) -> logger:notice("first", ?domain), %% wait for automatic filesync - check_tracer(?FILESYNC_REP_INT*2), + check_tracer(filesync_rep_int()*2), %% check that explicit filesync is only done once start_tracer([{logger_std_h, write_to_dev, 5}, @@ -620,7 +617,7 @@ write_failure(Config) -> ok = log_on_remote_node(Node, "Logged1"), rpc:call(Node, logger_std_h, filesync, [?STANDARD_HANDLER]), ?check_no_log, - try_read_file(Log, {ok,<<"Logged1\n">>}, ?FILESYNC_REP_INT), + try_read_file(Log, {ok,<<"Logged1\n">>}, filesync_rep_int()), rpc:call(Node, ?MODULE, set_result, [file_write,{error,terminated}]), ok = log_on_remote_node(Node, "Cause simple error printout"), @@ -638,7 +635,7 @@ write_failure(Config) -> ok = log_on_remote_node(Node, "Logged2"), rpc:call(Node, logger_std_h, filesync, [?STANDARD_HANDLER]), ?check_no_log, - try_read_file(Log, {ok,<<"Logged1\nLogged2\n">>}, ?FILESYNC_REP_INT), + try_read_file(Log, {ok,<<"Logged1\nLogged2\n">>}, filesync_rep_int()), ok. write_failure(cleanup, _Config) -> Nodes = nodes(), @@ -1211,10 +1208,10 @@ wait_until_written(File, Sz) -> {ok,#file_info{size = Sz}} -> timer:sleep(1000), case file:read_file_info(File) of - {ok,#file_info{size = Sz1}} -> + {ok,#file_info{size = Sz}} -> ok; - {ok,#file_info{size = Sz2}} -> - wait_until_written(File, Sz2) + {ok,#file_info{size = Sz1}} -> + wait_until_written(File, Sz1) end; {ok,#file_info{size = Sz1}} -> wait_until_written(File, Sz1) @@ -1574,7 +1571,7 @@ wait_for_process_up(Name,T) -> N = (T div 500) + 1, wait_for_process_up1(Name,N). -wait_for_process_up1(Name,0) -> +wait_for_process_up1(_Name,0) -> error; wait_for_process_up1(Name,N) -> timer:sleep(500), @@ -1584,3 +1581,9 @@ wait_for_process_up1(Name,N) -> undefined -> wait_for_process_up1(Name,N-1) end. + +filesync_rep_int() -> + case (fun() -> is_atom(?FILESYNC_REPEAT_INTERVAL) end)() of + true -> 5500; + false -> ?FILESYNC_REPEAT_INTERVAL + 500 + end. -- cgit v1.2.3 From 9af8bca495c1704d7ce010939c7e54df7bfdef71 Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Mon, 11 Jun 2018 22:48:25 +0200 Subject: [logger] Update handler documentation --- lib/kernel/doc/src/logger_chapter.xml | 243 +++++++++++++++++-------------- lib/kernel/doc/src/logger_disk_log_h.xml | 123 ++++++++-------- lib/kernel/doc/src/logger_std_h.xml | 71 ++++----- 3 files changed, 234 insertions(+), 203 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index f7df0a3e6e..5eab48f63e 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -1103,184 +1103,205 @@ do_log(Fd, LogEvent, #{formatter := {FModule, FConfig}}) ->
    Protecting the Handler from Overload -

    In order for the built-in handlers to survive, and stay responsive, - during periods of high load (i.e. when huge numbers of incoming - log requests must be handled), a mechanism for overload protection - has been implemented in the - logger_std_h - and logger_disk_log_h - handler. The mechanism, used by both handlers, works - as follows:

    +

    The default handlers, + logger_std_h and + logger_disk_log_h, feature an overload protection + mechanism, which makes it possible for the handlers to survive, + and stay responsive, during periods of high load (i.e. when huge + numbers of incoming log requests must be handled). + The mechanism works as follows:

    Message Queue Length

    The handler process keeps track of the length of its message - queue and reacts in different ways depending on the current status. - The purpose is to keep the handler in, or (as quickly as possible), - get the handler into, a state where it can keep up with the pace - of incoming log requests. The memory usage of the handler must never - keep growing larger and larger, since that would eventually cause the - handler to crash. Three thresholds with associated actions have been - defined:

    + queue and takes some form of action when the current length exceeds a + configurable threshold. The purpose is to keep the handler in, or to + as quickly as possible get the handler into, a state where it can + keep up with the pace of incoming log events. The memory use of the + handler must never grow larger and larger, since that will eventually + cause the handler to crash. These three thresholds, with associated + actions, exist:

    - toggle_sync_qlen + sync_mode_qlen -

    The default value of this level is 10 messages, - and as long as the length of the message queue is lower, all log - requests are handled asynchronously. This simply means that the - process sending the log request (by calling a log function in the - Logger API) does not wait for a response from the handler but - continues executing immediately after the request (i.e. it will not - be affected by the time it takes the handler to print to the log - device). If the message queue grows larger than this value, however, - the handler starts handling the log requests synchronously instead, - meaning the process sending the request will have to wait for a - response. When the handler manages to reduce the message queue to a - level below the toggle_sync_qlen threshold, asynchronous +

    As long as the length of the message queue is lower than this + value, all log events are handled asynchronously. This means that + the process sending the log event, by calling a log function in the + Logger API, does not wait for a response from the handler, but + continues executing immediately after the event is sent (that is, it + is not affected by the time it takes the handler to print the event + to the log device). If the message queue grows larger than this value, + the handler starts handling log events synchronously instead, + meaning that the process sending the event, must wait for a + response. When the handler reduces the message queue to a + level below the sync_mode_qlen threshold, asynchronous operation is resumed. The switch from asynchronous to synchronous - mode will force the logging tempo of few busy senders to slow down, - but cannot protect the handler sufficiently in situations of many - concurrent senders.

    + mode can slow down the logging tempo of one, or a few, busy senders, + but cannot protect the handler sufficiently in a situation of many + busy concurrent senders.

    +

    Default value of the threshold: 10 messages.

    - drop_new_reqs_qlen + drop_mode_qlen -

    When the message queue has grown larger than this threshold, which - defaults to 200 messages, the handler switches to a mode in - which it drops any new requests being made. Dropping a message in - this state means that the log function never actually sends a message - to the handler. The log call simply returns without an action. When - the length of the message queue has been reduced to a level below this - threshold, synchronous or asynchronous request handling mode is - resumed.

    +

    When the message queue grows larger than this threshold, the + handler switches to a mode in which it drops all new events that + senders want to send. Dropping an event in ths mode, means that the + log function never sends a message to the handler, but returns + without taking any action. The handler keeps logging events already + in the message queue, and when the length of the message queue is + reduced to a level below the threshold, synchronous or asynchronous + mode is resumed. Note that when the handler activates, or deactivates, + drop mode, information about it is printed to the log.

    +

    Default value of the threshold: 200 messages.

    - flush_reqs_qlen + flush_qlen -

    Above this threshold, which defaults to 1000 messages, a - flush operation takes place, in which all messages buffered in the - process mailbox get deleted without any logging actually taking - place. (Processes waiting for a response from a synchronous log request - will receive a reply indicating that the request has been dropped).

    +

    If the length of the message queue grows larger than this threshold, + a flush (delete) operation takes place. To flush events, the handler receives + the messages in the process mailbox in a loop without logging (that is, the + handler deletes the events). Processes waiting for a response from a + synchronous log request will receive a reply from the handler indicating + that the request has been dropped. The handler process will set its own priority + to high during the flush loop to make sure that no new events come in + during the operation. Note that after the flush operation is performed, + the handler prints information in the log about how many events have been + deleted

    +

    Default value of the threshold: 1000 messages.

    For the overload protection algorithm to work properly, it is required that:

    -

    toggle_sync_qlen =< drop_new_reqs_qlen =< flush_reqs_qlen

    +

    sync_mode_qlen =< drop_mode_qlen =< flush_qlen

    and that:

    -

    drop_new_reqs_qlen > 1

    +

    drop_mode_qlen > 1

    -

    If toggle_sync_qlen is set to 0, the handler will handle all - requests synchronously. Setting the value of toggle_sync_qlen to the same - as drop_new_reqs_qlen, disables the synchronous mode. Likewise, setting - the value of drop_new_reqs_qlen to the same as flush_reqs_qlen, - disables the drop mode.

    +

    To disable certain modes, do the following:

    + + If sync_mode_qlen is set to 0, all log events are handled + synchronously. That is, asynchronous logging is disabled. + If sync_mode_qlen is set to the same value as + drop_mode_qlen, synchronous mode is disabled. That is, the handler + always runs in asychronous mode, unless dropping or flushing is invoked. + If drop_mode_qlen is set to the same value as flush_qlen, + drop mode is disabled and can never occur. +

    During high load scenarios, the length of the handler message queue rarely grows in a linear and predictable way. Instead, whenever the handler process gets scheduled in, it can have an almost arbitrary number of messages waiting in the mailbox. It's for this reason that the overload - protection mechanism is focused on acting quickly and quite drastically - (such as immediately dropping or flushing messages) as soon as a large - queue length is detected.

    - -

    The thresholds listed above may be modified by the user if, e.g, a handler - shouldn't drop or flush messages unless the message queue length grows - extremely large. (The handler must be allowed to use large amounts of memory - under such circumstances however). Another example of when the user might want - to change the settings is if, for performance reasons, the logging processes must - never get blocked by synchronous log requests, while dropping or flushing requests - is perfectly acceptable (since it doesn't affect the performance of the - loggers).

    + protection mechanism is focused on acting quickly, and quite drastically, + such as immediately dropping or flushing messages, when a large queue length + is detected.

    + +

    The values of the previously listed thresholds can be specified by the user. + This way, a handler can be configured to, for example, not drop or flush + messages unless the message queue length of the handler process grows extremely + large. Note that large amounts of memory can be required for the node under such + circumstances. Another example of user configuration is when, for performance + reasons, the logging processes must never get blocked by synchronous log requests. + It's possible, perhaps, that dropping or flushing events is still acceptable (since + it does not affect the performance of the processes sending the log events).

    A configuration example:

    logger:add_handler(my_standard_h, logger_std_h, #{config => #{type => {file,"./system_info.log"}, - toggle_sync_qlen => 100, - drop_new_reqs_qlen => 1000, - flush_reqs_qlen => 2000}}). + sync_mode_qlen => 100, + drop_mode_qlen => 1000, + flush_qlen => 2000}}).
    Controlling Bursts of Log Requests -

    A potential problem with large bursts of log requests, is that log files - may get full or wrapped too quickly (in the latter case overwriting - previously logged data that could be of great importance). For this reason, - both built-in handlers offer the possibility to set a maximum level of how - many requests to process with a certain time frame. With this burst control - feature enabled, the handler will take care of bursts of log requests - without choking log files, or the terminal, with massive amounts of - printouts. These are the configuration parameters:

    +

    Large bursts of log events (that is, many events received by the handler + under a short period of time), can potentially cause problems, such as:

    + + Log files grow very large, very quickly. + Circular logs wrap too quickly so that important data gets overwritten. + Write buffers grow large, which slows down file sync operations. + + +

    For this reason, both built-in handlers offer the possibility to specify the + maximum number of events to be handled within a certain time frame. + With this burst control feature enabled, the handler can avoid choking the log with + massive amounts of printouts. These are the configuration parameters:

    - enable_burst_limit + burst_limit_enable -

    This is set to true by default. The value false - disables the burst control feature.

    +

    Value true enables burst control and false disables it.

    +

    Defaults to true.

    - burst_limit_size + burst_limit_max_count -

    This is how many requests should be processed within the - burst_window_time time frame. After this maximum has been - reached, successive requests will be dropped until the end of the - time frame. The default value is 500 messages.

    +

    This is the maximum number of events to handle within a + burst_limit_window_time time frame. After the limit has been + reached, successive events get dropped until the end of the time frame.

    +

    Defaults to 500 events.

    - burst_window_time + burst_limit_window_time -

    The default window is 1000 milliseconds long.

    +

    See the previous description of burst_limit_window_time.

    +

    Defaults to 1000 milliseconds.

    A configuration example:

    logger:add_handler(my_disk_log_h, logger_disk_log_h, - #{disk_log_opts => - #{file => "./my_disk_log"}, - config => - #{burst_limit_size => 10, - burst_window_time => 500}}). + #{config => + #{file => "./my_disk_log", + burst_limit_enable => true, + burst_limit_max_count => 20, + burst_limit_window_time => 500}}).
    - Terminating a Large Handler -

    A handler process may grow large even if it can manage peaks of high load - without crashing. The overload protection mechanism includes user configurable - levels for a maximum allowed message queue length and maximum allowed memory - usage. This feature is disabled by default, but can be switched on by means - of the following configuration parameters:

    - + Terminating an Overloaded Handler +

    It is possible that a handler, even if it can successfully manage peaks + of high load without crashing, can build up a large message queue, or use a + large amount of memory. The overload protection mechanism includes an + automatic termination and restart feature for the purpose of guaranteeing + that a handler does not grow out of bounds. The feature can be configured + with the following parameters:

    - enable_kill_overloaded + overload_kill_enable -

    This is set to false by default. The value true - enables the feature.

    +

    Value true enables the feature and false disables it.

    +

    Defaults to false.

    - handler_overloaded_qlen + overload_kill_qlen

    This is the maximum allowed queue length. If the mailbox grows larger than this, the handler process gets terminated.

    +

    Defaults to 20000 messages.

    - handler_overloaded_mem + overload_kill_mem_size -

    This is the maximum allowed memory usage of the handler process. If - the handler grows any larger, the process gets terminated.

    +

    This is the maximum memory size that the handler process is allowed to use. + If the handler grows larger than this, the process gets terminated.

    +

    Defaults to 3000000 bytes.

    - handler_restart_after + overload_kill_restart_after -

    If the handler gets terminated because of its queue length or - memory usage, it can get automatically restarted again after a - configurable delay time. The time is specified in milliseconds - and 5000 is the default value. The value never can - also be set, which prevents a restart.

    +

    If the handler gets terminated, it can restart automatically after a + delay, specified in milliseconds. The value infinity can also be set, + which prevents restarts.

    +

    Defaults to 5000 milliseconds.

    +

    If the handler process gets terminated because of overload, information about + this event is printed in the log. Information about when a restart has taken + place, and the handler is back in action, is also printed.

    diff --git a/lib/kernel/doc/src/logger_disk_log_h.xml b/lib/kernel/doc/src/logger_disk_log_h.xml index 63c29cb010..c60cf14cc7 100644 --- a/lib/kernel/doc/src/logger_disk_log_h.xml +++ b/lib/kernel/doc/src/logger_disk_log_h.xml @@ -38,101 +38,110 @@

    This is a handler for Logger that offers circular (wrapped) logs by using disk_log. - Multiple instances - of this handler can be added to Logger, and each instance prints to - its own disk_log file, created with the name and settings specified in - the handler configuration.

    + Multiple instances of this handler can be added to Logger, and each instance + prints to its own disk_log file, created with the name and settings specified + in the handler configuration.

    The default standard handler, logger_std_h, can be - replaced by a disk_log handler at start up of the Kernel application. + replaced by a disk_log handler at startup of the Kernel application. See an example of this below.

    -

    The handler has an overload protection mechanism that will keep the handler - process and the Kernel application alive during a high load of log - requests. How this feature works, and how to modify the configuration, - is described in the +

    The handler has an overload protection mechanism that keeps the handler + process and the Kernel application alive during high loads of log + events. How overload protection works, and how to configure it, is + described in the User's Guide .

    To add a new instance of the disk_log handler, use logger:add_handler/3 - . The handler configuration argument is a map which may contain + . The handler configuration argument is a map which can contain general configuration parameters, as documented in the User's Guide - , as well as handler specific parameters.

    -

    The settings for the disk_log log file should be specified with the - key disk_log_opts. These settings are a subset of the disk_log - data type - dlog_option().

    -

    Parameters in the disk_log_opts map:

    + , and handler specific parameters. The specific data + is stored in a sub map with the key config, and can contain the + following parameters:

    file - This is the full name of the disk_log log file. + +

    This is the full name of the disk_log log file. The option + corresponds to the name property in the + dlog_option() + datatype.

    +
    type - This is the disk_log type, wrap or halt. The - default value is wrap. + +

    This is the disk_log type, wrap or halt. The option + corresponds to the type property in the + dlog_option() + datatype.

    +

    Defaults to wrap.

    +
    max_no_files - This is the maximum number of files that disk_log will use - for its circular logging. The default value is 10. (The setting - has no effect on a halt log). + +

    This is the maximum number of files that disk_log will use + for its circular logging. The option + corresponds to the MaxNoFiles element in the size property in the + dlog_option() + datatype.

    +

    Defaults to 10. (The setting has no effect on a halt log).

    +
    max_no_bytes - This is the maximum number of bytes that will be written to - a log file before disk_log proceeds with the next file in order (or - generates an error in case of a full halt log). The default value for - a wrap log is 1048576 bytes, and infinity for a halt - log. -
    -

    Specific configuration for the handler (represented as a sub map) - is specified with the key config. It may contain the - following parameter:

    - + +

    This is the maximum number of bytes that will be written to + a log file before disk_log proceeds with the next file in order (or + generates an error in case of a full halt log). The option + corresponds to the MaxNoBytes element in the size property in the + dlog_option() + datatype.

    +

    Defaults to 1048576 bytes for a wrap log, and + infinity for a halt log.

    +
    filesync_repeat_interval -

    This value (in milliseconds) specifies how often the handler will - do a disk_log sync operation in order to make sure that buffered data - gets written to disk. The handler will repeatedly attempt this - operation, but only perform it if something has actually been logged - since the last sync. The default value is 5000 milliseconds. - If no_repeat is set as value, the repeated sync operation is - disabled. The user can also call the - sync/1 - function to perform a disk_log sync.

    +

    This value, in milliseconds, specifies how often the handler does + a disk_log sync operation to write buffered data to disk. The handler attempts + the operation repeatedly, but only performs a new sync if something has + actually been logged.

    +

    Defaults to 5000 milliseconds.

    +

    If no_repeat is set as value, the repeated sync operation + is disabled. The user can also call the + filesync/1 + function to perform a disk_log sync.

    +
    -

    There are a number of other configuration parameters available, that are - to be used for customizing the overload protection behaviour. The same - parameters are used both in the standard handler and the disk_log handler, - and are documented in the +

    Other configuration parameters exist, to be used for customizing + the overload protection behaviour. The same parameters are used both in the + standard handler and the disk_log handler, and are documented in the User's Guide .

    Note that when changing the configuration of the handler in runtime, by calling logger:set_handler_config/2 - or logger:set_handler_config/3, the disk_log_opts - settings may not be modified.

    + or logger:set_handler_config/3, the disk_log options (file, + type, max_no_files, max_no_bytes) must not be modified.

    Example of adding a disk_log handler:

    logger:add_handler(my_disk_log_h, logger_disk_log_h, #{level => error, filter_default => log, - disk_log_opts => - #{file => "./my_disk_log", - type => wrap, - max_no_files => 4, - max_no_bytes => 10000}, - config => - #{filesync_repeat_interval => 1000}}). + config => #{file => "./my_disk_log", + type => wrap, + max_no_files => 4, + max_no_bytes => 10000}, + filesync_repeat_interval => 1000}}). -

    In order to use the disk_log handler instead of the default standard +

    To use the disk_log handler instead of the default standard handler when starting an Erlang node, change the Kernel default logger to use disk_log. Example:

    erl -kernel logger '[{handler,default,logger_disk_log_h, - #{disk_log_opts => #{file => "./system_disk_log"}}}]' + #{config => #{file => "./system_disk_log"}}}]'
    - + Writes buffered data to disk.

    Write buffered data to disk.

    diff --git a/lib/kernel/doc/src/logger_std_h.xml b/lib/kernel/doc/src/logger_std_h.xml index 89e11389c5..08667ae7f9 100644 --- a/lib/kernel/doc/src/logger_std_h.xml +++ b/lib/kernel/doc/src/logger_std_h.xml @@ -38,62 +38,63 @@

    This is the default handler for Logger. Multiple instances of this handler can be added to - Logger, and each instance will print logs to standard_io, + Logger, and each instance prints logs to standard_io, standard_error or to file. The default instance that starts - with Kernel is named default - which is the name to be used + with Kernel is named default, which is the name to be used for reconfiguration.

    -

    The handler has an overload protection mechanism that will keep the handler - process and the Kernel application alive during a high load of log - requests. How this feature works, and how to modify the configuration, - is described in the +

    The handler has an overload protection mechanism that keeps the handler + process and the Kernel application alive during high loads of log + events. How overload protection works, and how to configure it, is + described in the User's Guide .

    To add a new instance of the standard handler, use logger:add_handler/3 - . The handler configuration argument is a map which may contain - general configuration parameters, as documented in the + . The handler configuration argument is a map which can contain + general configuration parameters, as documented in the User's Guide - , as well as handler specific parameters. The specific parameters - are stored in a sub map with the key config. The following - keys and values may be specified:

    + , and handler specific parameters. The specific data + is stored in a sub map with the key config, and can contain the + following parameters:

    type -

    This will have the value standard_io, standard_error, - {file,LogFileName}, or {file,LogFileName,LogFileOpts}, - where standard_io is the default value for type. It's recommended - to not specify LogFileOpts if not absolutely necessary. The - default options used by the handler to open a file for logging are: - raw, append and delayed_write. The standard +

    This has the value standard_io, standard_error, + {file,LogFileName}, or {file,LogFileName,LogFileOpts}.

    +

    Defaults to standard_io.

    +

    It is recommended to not specify LogFileOpts, unless absolutely + necessary. The default options used by the handler to open a file for logging are: + raw, append and delayed_write. Note that the standard handler does not have support for circular logging. Use the logger_disk_log_h - handler for this.

    + handler for this.

    + filesync_repeat_interval -

    This value (in milliseconds) specifies how often the handler will - do a file sync operation in order to make sure that buffered data gets - written to disk. The handler will repeatedly attempt this - operation, but only perform it if something has actually been logged - since the last sync. The default value is 5000 milliseconds. - If no_repeat is set as value, the repeated file sync operation +

    This value, in milliseconds, specifies how often the handler does + a file sync operation to write buffered data to disk. The handler attempts + the operation repeatedly, but only performs a new sync if something has + actually been logged.

    +

    Defaults to 5000 milliseconds.

    +

    If no_repeat is set as value, the repeated file sync operation is disabled, and it will be the operating system settings that determine how quickly or slowly data gets written to disk. The user can also call - the sync/1 - function to perform a file sync.

    + the filesync/1 + function to perform a file sync.

    +
    -

    There are a number of other configuration parameters available, that are - to be used for customizing the overload protection behaviour. The same - parameters are used both in the standard handler and the disk_log handler, - and are documented in the +

    Other configuration parameters exist, to be used for customizing + the overload protection behaviour. The same parameters are used both in the + standard handler and the disk_log handler, and are documented in the User's Guide .

    -

    Note that when changing the configuration of the handler in runtime, by +

    Note that if changing the configuration of the handler in runtime, by calling logger:set_handler_config/2 , or logger:set_handler_config/3 , - the type parameter may not be modified.

    + the type parameter must not be modified.

    Example of adding a standard handler:

    logger:add_handler(my_standard_h, logger_std_h, @@ -103,9 +104,9 @@ logger:add_handler(my_standard_h, logger_std_h, #{type => {file,"./system_info.log"}, filesync_repeat_interval => 1000}}). -

    In order to configure the default handler (that starts initially with +

    To set the default handler (that starts initially with the Kernel application) to log to file instead of standard_io, - change the Kernel default logger to use a file. Example:

    + change the Kernel default logger configuration. Example:

    erl -kernel logger '[{handler,default,logger_std_h, #{config => #{type => {file,"./log.log"}}}}]' @@ -118,7 +119,7 @@ erl -kernel logger '[{handler,default,logger_std_h, - + Writes buffered data to disk.

    Write buffered data to disk.

    -- cgit v1.2.3 From 343298ef2ef349e7b74265751fd6151ada224470 Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Wed, 13 Jun 2018 23:56:38 +0200 Subject: [logger] Correct documentation --- lib/kernel/doc/src/logger_chapter.xml | 128 ++++++++++++++++--------------- lib/kernel/doc/src/logger_disk_log_h.xml | 33 ++++---- lib/kernel/doc/src/logger_std_h.xml | 47 +++++------- 3 files changed, 101 insertions(+), 107 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 5eab48f63e..a4951022bb 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -97,6 +97,7 @@ defined.

    + Logger API

    The API for logging consists of a set of macros, and a set @@ -1107,7 +1108,7 @@ do_log(Fd, LogEvent, #{formatter := {FModule, FConfig}}) -> logger_std_h and logger_disk_log_h, feature an overload protection mechanism, which makes it possible for the handlers to survive, - and stay responsive, during periods of high load (i.e. when huge + and stay responsive, during periods of high load (when huge numbers of incoming log requests must be handled). The mechanism works as follows:

    @@ -1127,47 +1128,49 @@ do_log(Fd, LogEvent, #{formatter := {FModule, FConfig}}) ->

    As long as the length of the message queue is lower than this value, all log events are handled asynchronously. This means that - the process sending the log event, by calling a log function in the - Logger API, does not wait for a response from the handler, but - continues executing immediately after the event is sent (that is, it - is not affected by the time it takes the handler to print the event - to the log device). If the message queue grows larger than this value, + the client process sending the log event, by calling a log function + in the Logger API, + does not wait for a response from the handler but continues + executing immediately after the event is sent. It is not affected + by the time it takes the handler to print the event to the log + device. If the message queue grows larger than this value, the handler starts handling log events synchronously instead, - meaning that the process sending the event, must wait for a + meaning that the client process sending the event must wait for a response. When the handler reduces the message queue to a level below the sync_mode_qlen threshold, asynchronous operation is resumed. The switch from asynchronous to synchronous mode can slow down the logging tempo of one, or a few, busy senders, but cannot protect the handler sufficiently in a situation of many busy concurrent senders.

    -

    Default value of the threshold: 10 messages.

    +

    Defaults to 10 messages.

    drop_mode_qlen

    When the message queue grows larger than this threshold, the handler switches to a mode in which it drops all new events that - senders want to send. Dropping an event in ths mode, means that the - log function never sends a message to the handler, but returns - without taking any action. The handler keeps logging events already - in the message queue, and when the length of the message queue is - reduced to a level below the threshold, synchronous or asynchronous - mode is resumed. Note that when the handler activates, or deactivates, - drop mode, information about it is printed to the log.

    -

    Default value of the threshold: 200 messages.

    + senders want to log. Dropping an event in this mode means that the + call to the log function never results in a message being sent to + the handler, but the function returns without taking any action. + The handler keeps logging the events that are already in its message + queue, and when the length of the message queue is reduced to a level + below the threshold, synchronous or asynchronous mode is resumed. + Notice that when the handler activates or deactivates drop mode, + information about it is printed in the log.

    +

    Defaults to 200 messages.

    flush_qlen

    If the length of the message queue grows larger than this threshold, - a flush (delete) operation takes place. To flush events, the handler receives - the messages in the process mailbox in a loop without logging (that is, the - handler deletes the events). Processes waiting for a response from a - synchronous log request will receive a reply from the handler indicating - that the request has been dropped. The handler process will set its own priority - to high during the flush loop to make sure that no new events come in - during the operation. Note that after the flush operation is performed, - the handler prints information in the log about how many events have been - deleted

    -

    Default value of the threshold: 1000 messages.

    + a flush (delete) operation takes place. To flush events, the handler + discards the messages in the message queue by receiving them in a + loop without logging. Client processes waiting for a response from a + synchronous log request receive a reply from the handler indicating + that the request is dropped. The handler process increases its + priority during the flush loop to make sure that no new events + are received during the operation. Notice that after the flush operation + is performed, the handler prints information in the log about how many + events have been deleted.

    +

    Defaults to 1000 messages.

    @@ -1186,15 +1189,15 @@ do_log(Fd, LogEvent, #{formatter := {FModule, FConfig}}) -> synchronously. That is, asynchronous logging is disabled. If sync_mode_qlen is set to the same value as drop_mode_qlen, synchronous mode is disabled. That is, the handler - always runs in asychronous mode, unless dropping or flushing is invoked. + always runs in asynchronous mode, unless dropping or flushing is invoked. If drop_mode_qlen is set to the same value as flush_qlen, drop mode is disabled and can never occur.

    During high load scenarios, the length of the handler message queue rarely grows in a linear and predictable way. Instead, whenever the - handler process gets scheduled in, it can have an almost arbitrary number - of messages waiting in the mailbox. It's for this reason that the overload + handler process is scheduled in, it can have an almost arbitrary number + of messages waiting in the message queue. It is for this reason that the overload protection mechanism is focused on acting quickly, and quite drastically, such as immediately dropping or flushing messages, when a large queue length is detected.

    @@ -1202,38 +1205,36 @@ do_log(Fd, LogEvent, #{formatter := {FModule, FConfig}}) ->

    The values of the previously listed thresholds can be specified by the user. This way, a handler can be configured to, for example, not drop or flush messages unless the message queue length of the handler process grows extremely - large. Note that large amounts of memory can be required for the node under such + large. Notice that large amounts of memory can be required for the node under such circumstances. Another example of user configuration is when, for performance - reasons, the logging processes must never get blocked by synchronous log requests. - It's possible, perhaps, that dropping or flushing events is still acceptable (since - it does not affect the performance of the processes sending the log events).

    + reasons, the client processes must never be blocked by synchronous log requests. + It is possible, perhaps, that dropping or flushing events is still acceptable, since + it does not affect the performance of the client processes sending the log events.

    A configuration example:

    logger:add_handler(my_standard_h, logger_std_h, - #{config => - #{type => {file,"./system_info.log"}, - sync_mode_qlen => 100, - drop_mode_qlen => 1000, - flush_qlen => 2000}}). + #{config => #{type => {file,"./system_info.log"}, + sync_mode_qlen => 100, + drop_mode_qlen => 1000, + flush_qlen => 2000}}).
    Controlling Bursts of Log Requests -

    Large bursts of log events (that is, many events received by the handler - under a short period of time), can potentially cause problems, such as:

    +

    Large bursts of log events - many events received by the handler + under a short period of time - can potentially cause problems, such as:

    Log files grow very large, very quickly. - Circular logs wrap too quickly so that important data gets overwritten. + Circular logs wrap too quickly so that important data is overwritten. Write buffers grow large, which slows down file sync operations.

    For this reason, both built-in handlers offer the possibility to specify the maximum number of events to be handled within a certain time frame. With this burst control feature enabled, the handler can avoid choking the log with - massive amounts of printouts. These are the configuration parameters:

    - + massive amounts of printouts. The configuration parameters are:

    burst_limit_enable @@ -1243,13 +1244,13 @@ logger:add_handler(my_standard_h, logger_std_h, burst_limit_max_count

    This is the maximum number of events to handle within a - burst_limit_window_time time frame. After the limit has been - reached, successive events get dropped until the end of the time frame.

    + burst_limit_window_time time frame. After the limit is + reached, successive events are dropped until the end of the time frame.

    Defaults to 500 events.

    burst_limit_window_time -

    See the previous description of burst_limit_window_time.

    +

    See the previous description of burst_limit_max_count.

    Defaults to 1000 milliseconds.

    @@ -1257,11 +1258,10 @@ logger:add_handler(my_standard_h, logger_std_h,

    A configuration example:

    logger:add_handler(my_disk_log_h, logger_disk_log_h, - #{config => - #{file => "./my_disk_log", - burst_limit_enable => true, - burst_limit_max_count => 20, - burst_limit_window_time => 500}}). + #{config => #{file => "./my_disk_log", + burst_limit_enable => true, + burst_limit_max_count => 20, + burst_limit_window_time => 500}}).
    @@ -1271,7 +1271,7 @@ logger:add_handler(my_disk_log_h, logger_disk_log_h, of high load without crashing, can build up a large message queue, or use a large amount of memory. The overload protection mechanism includes an automatic termination and restart feature for the purpose of guaranteeing - that a handler does not grow out of bounds. The feature can be configured + that a handler does not grow out of bounds. The feature is configured with the following parameters:

    overload_kill_enable @@ -1281,27 +1281,33 @@ logger:add_handler(my_disk_log_h, logger_disk_log_h, overload_kill_qlen -

    This is the maximum allowed queue length. If the mailbox grows larger - than this, the handler process gets terminated.

    +

    This is the maximum allowed queue length. If the message queue grows + larger than this, the handler process is terminated.

    Defaults to 20000 messages.

    overload_kill_mem_size

    This is the maximum memory size that the handler process is allowed to use. - If the handler grows larger than this, the process gets terminated.

    + If the handler grows larger than this, the process is terminated.

    Defaults to 3000000 bytes.

    overload_kill_restart_after -

    If the handler gets terminated, it can restart automatically after a - delay, specified in milliseconds. The value infinity can also be set, - which prevents restarts.

    +

    If the handler is terminated, it restarts automatically after a + delay specified in milliseconds. The value infinity prevents + restarts.

    Defaults to 5000 milliseconds.

    -

    If the handler process gets terminated because of overload, information about - this event is printed in the log. Information about when a restart has taken - place, and the handler is back in action, is also printed.

    +

    If the handler process is terminated because of overload, it prints + information about it in the log. It also prints information about when a + restart has taken place, and the handler is back in action.

    + +

    The sizes of the log events affect the memory needs of the handler. + For information about how to limit the size of log events, see the + logger_formatter(3) + manual page.

    +
    diff --git a/lib/kernel/doc/src/logger_disk_log_h.xml b/lib/kernel/doc/src/logger_disk_log_h.xml index c60cf14cc7..98439983cf 100644 --- a/lib/kernel/doc/src/logger_disk_log_h.xml +++ b/lib/kernel/doc/src/logger_disk_log_h.xml @@ -33,13 +33,13 @@ logger_disk_log_h.xml logger_disk_log_h - A disk_log based handler for the Logger. + A disk_log based handler for Logger

    This is a handler for Logger that offers circular (wrapped) logs by using disk_log. Multiple instances of this handler can be added to Logger, and each instance - prints to its own disk_log file, created with the name and settings specified + prints to its own disk log file, created with the name and settings specified in the handler configuration.

    The default standard handler, logger_std_h, can be @@ -62,14 +62,14 @@ file -

    This is the full name of the disk_log log file. The option +

    This is the full name of the disk log file. The option corresponds to the name property in the dlog_option() datatype.

    type -

    This is the disk_log type, wrap or halt. The option +

    This is the disk log type, wrap or halt. The option corresponds to the type property in the dlog_option() datatype.

    @@ -77,18 +77,19 @@
    max_no_files -

    This is the maximum number of files that disk_log will use +

    This is the maximum number of files that disk_log uses for its circular logging. The option corresponds to the MaxNoFiles element in the size property in the dlog_option() datatype.

    -

    Defaults to 10. (The setting has no effect on a halt log).

    +

    Defaults to 10.

    +

    The setting has no effect on a halt log.

    max_no_bytes -

    This is the maximum number of bytes that will be written to - a log file before disk_log proceeds with the next file in order (or - generates an error in case of a full halt log). The option +

    This is the maximum number of bytes that is written to + a log file before disk_log proceeds with the next file in order, or + generates an error in case of a full halt log. The option corresponds to the MaxNoBytes element in the size property in the dlog_option() datatype.

    @@ -113,17 +114,13 @@ standard handler and the disk_log handler, and are documented in the User's Guide .

    -

    Note that when changing the configuration of the handler in runtime, by - calling - logger:set_handler_config/2 - or logger:set_handler_config/3, the disk_log options (file, - type, max_no_files, max_no_bytes) must not be modified.

    +

    Notice that when changing the configuration of the handler in runtime, the + disk_log options (file, type, max_no_files, + max_no_bytes) must not be modified.

    Example of adding a disk_log handler:

    logger:add_handler(my_disk_log_h, logger_disk_log_h, - #{level => error, - filter_default => log, - config => #{file => "./my_disk_log", + #{config => #{file => "./my_disk_log", type => wrap, max_no_files => 4, max_no_bytes => 10000}, @@ -131,7 +128,7 @@ logger:add_handler(my_disk_log_h, logger_disk_log_h,

    To use the disk_log handler instead of the default standard handler when starting an Erlang node, change the Kernel default logger to - use disk_log. Example:

    + use logger_disk_log_h. Example:

    erl -kernel logger '[{handler,default,logger_disk_log_h, #{config => #{file => "./system_disk_log"}}}]' diff --git a/lib/kernel/doc/src/logger_std_h.xml b/lib/kernel/doc/src/logger_std_h.xml index 08667ae7f9..95b4baf160 100644 --- a/lib/kernel/doc/src/logger_std_h.xml +++ b/lib/kernel/doc/src/logger_std_h.xml @@ -33,15 +33,13 @@ logger_std_h.xml logger_std_h - Default handler for Logger. + Standard handler for Logger. -

    This is the default handler for Logger. +

    This is the standard handler for Logger. Multiple instances of this handler can be added to Logger, and each instance prints logs to standard_io, - standard_error or to file. The default instance that starts - with Kernel is named default, which is the name to be used - for reconfiguration.

    + standard_error, or to file.

    The handler has an overload protection mechanism that keeps the handler process and the Kernel application alive during high loads of log events. How overload protection works, and how to configure it, is @@ -62,12 +60,12 @@

    This has the value standard_io, standard_error, {file,LogFileName}, or {file,LogFileName,LogFileOpts}.

    Defaults to standard_io.

    -

    It is recommended to not specify LogFileOpts, unless absolutely - necessary. The default options used by the handler to open a file for logging are: - raw, append and delayed_write. Note that the standard - handler does not have support for circular logging. Use the - logger_disk_log_h - handler for this.

    +

    It is recommended not to specify LogFileOpts unless absolutely + necessary. The default options used by the handler to open a file for logging are + raw, append, and delayed_write. Notice that the standard + handler does not have support for circular logging. Use the disk_log handler, + logger_disk_log_h, + for this.

    filesync_repeat_interval @@ -77,8 +75,8 @@ actually been logged.

    Defaults to 5000 milliseconds.

    If no_repeat is set as value, the repeated file sync operation - is disabled, and it will be the operating system settings that determine - how quickly or slowly data gets written to disk. The user can also call + is disabled, and it is the operating system settings that determine + how quickly or slowly data is written to disk. The user can also call the filesync/1 function to perform a file sync.

    @@ -88,32 +86,25 @@ standard handler and the disk_log handler, and are documented in the User's Guide .

    -

    Note that if changing the configuration of the handler in runtime, by - calling - logger:set_handler_config/2 - , or - logger:set_handler_config/3 - , +

    Notice that if changing the configuration of the handler in runtime, the type parameter must not be modified.

    Example of adding a standard handler:

    logger:add_handler(my_standard_h, logger_std_h, - #{level => info, - filter_default => log, - config => - #{type => {file,"./system_info.log"}, - filesync_repeat_interval => 1000}}). + #{config => #{type => {file,"./system_info.log"}, + filesync_repeat_interval => 1000}}). -

    To set the default handler (that starts initially with - the Kernel application) to log to file instead of standard_io, +

    To set the default handler, that starts initially with + the Kernel application, to log to file instead of standard_io, change the Kernel default logger configuration. Example:

    erl -kernel logger '[{handler,default,logger_std_h, #{config => #{type => {file,"./log.log"}}}}]'

    An example of how to replace the standard handler with a disk_log handler - at start up can be found in the manual of - logger_disk_log_h.

    + at startup is found in the + logger_disk_log_h + manual.

    -- cgit v1.2.3 From afee0fcc6a23da8ac31310606004fa42b6cf65a9 Mon Sep 17 00:00:00 2001 From: crownedgrouse Date: Thu, 14 Jun 2018 01:02:33 +0200 Subject: Included config files relative to sys.config dir Change the way included config files are searched in sys.config. Search first relative to sys.config directory, then relative to current working directory, for backward compatibility. This permit same result when using a sys.config file in a release or starting manually a node with -config. Credit to Siri Hansen for test case. --- lib/kernel/doc/src/config.xml | 9 +++++-- lib/kernel/src/application_controller.erl | 30 +++++++++++++++------- lib/kernel/test/application_SUITE.erl | 41 +++++++++++++++++++++++++++++-- 3 files changed, 67 insertions(+), 13 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/config.xml b/lib/kernel/doc/src/config.xml index fdb2d29f63..d331b755f5 100644 --- a/lib/kernel/doc/src/config.xml +++ b/lib/kernel/doc/src/config.xml @@ -86,8 +86,13 @@ File = string() Name of another .config file. Extension .config can be omitted. It is - recommended to use absolute paths. A relative path is - relative the current working directory of the emulator. + recommended to use absolute paths. If a relative path is used, + File is searched, first, relative from sys.config directory, then relative + to the current working directory of the emulator, for backward compatibility. + This allow to use a sys.config pointing out other .config files in a release + or in a node started manually using -config ... with same result whatever + the current working directory. +

    When traversing the contents of sys.config and a filename is encountered, its contents are read and merged with the result diff --git a/lib/kernel/src/application_controller.erl b/lib/kernel/src/application_controller.erl index 3b642f5873..abea329f84 100644 --- a/lib/kernel/src/application_controller.erl +++ b/lib/kernel/src/application_controller.erl @@ -1819,8 +1819,9 @@ check_conf() -> %% Therefore read and merge contents. if BFName =:= "sys" -> + DName = filename:dirname(FName), {ok, SysEnv, Errors} = - check_conf_sys(NewEnv), + check_conf_sys(NewEnv, [], [], DName), %% Report first error, if any, and %% terminate @@ -1842,20 +1843,31 @@ check_conf() -> end. check_conf_sys(Env) -> - check_conf_sys(Env, [], []). + check_conf_sys(Env, [], [], []). -check_conf_sys([File|T], SysEnv, Errors) when is_list(File) -> +check_conf_sys([File|T], SysEnv, Errors, DName) when is_list(File),is_list(DName) -> BFName = filename:basename(File, ".config"), FName = filename:join(filename:dirname(File), BFName ++ ".config"), - case load_file(FName) of + LName = case filename:pathtype(FName) of + relative when (DName =/= []) -> + % Check if relative to sys.config dir otherwise use legacy mode, + % i.e relative to cwd. + RName = filename:join(DName, FName), + case erl_prim_loader:read_file_info(RName) of + {ok, _} -> RName ; + error -> FName + end; + _ -> FName + end, + case load_file(LName) of {ok, NewEnv} -> - check_conf_sys(T, merge_env(SysEnv, NewEnv), Errors); + check_conf_sys(T, merge_env(SysEnv, NewEnv), Errors, DName); {error, {Line, _Mod, Str}} -> - check_conf_sys(T, SysEnv, [{error, {FName, Line, Str}}|Errors]) + check_conf_sys(T, SysEnv, [{error, {LName, Line, Str}}|Errors], DName) end; -check_conf_sys([Tuple|T], SysEnv, Errors) -> - check_conf_sys(T, merge_env(SysEnv, [Tuple]), Errors); -check_conf_sys([], SysEnv, Errors) -> +check_conf_sys([Tuple|T], SysEnv, Errors, DName) -> + check_conf_sys(T, merge_env(SysEnv, [Tuple]), Errors, DName); +check_conf_sys([], SysEnv, Errors, _) -> {ok, SysEnv, lists:reverse(Errors)}. load_file(File) -> diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl index 866043cfb4..e497ff65ad 100644 --- a/lib/kernel/test/application_SUITE.erl +++ b/lib/kernel/test/application_SUITE.erl @@ -37,7 +37,8 @@ -export([config_change/1, persistent_env/1, distr_changed_tc1/1, distr_changed_tc2/1, ensure_started/1, ensure_all_started/1, - shutdown_func/1, do_shutdown/1, shutdown_timeout/1, shutdown_deadlock/1]). + shutdown_func/1, do_shutdown/1, shutdown_timeout/1, shutdown_deadlock/1, + config_relative_paths/1]). -define(TESTCASE, testcase_name). -define(testcase, proplists:get_value(?TESTCASE, Config)). @@ -55,7 +56,7 @@ all() -> script_start, nodedown_start, permit_false_start_local, permit_false_start_dist, get_key, get_env, ensure_all_started, {group, distr_changed}, config_change, shutdown_func, shutdown_timeout, - shutdown_deadlock, + shutdown_deadlock, config_relative_paths, persistent_env]. groups() -> @@ -2077,6 +2078,42 @@ shutdown_deadlock(Config) when is_list(Config) -> ok. +%%----------------------------------------------------------------- +%% Relative paths in sys.config +%%----------------------------------------------------------------- +config_relative_paths(Config) -> + Dir = ?config(priv_dir,Config), + SubDir = filename:join(Dir,"subdir"), + Sys = filename:join(SubDir,"sys.config"), + ok = filelib:ensure_dir(Sys), + ok = file:write_file(Sys,"[\"../up.config\",\"current\"].\n"), + + Up = filename:join(Dir,"up.config"), + ok = file:write_file(Up,"[{app1,[{key1,value}]}].\n"), + + {ok,Cwd} = file:get_cwd(), + Current1 = filename:join(Cwd,"current.config"), + ok = file:write_file(Current1,"[{app1,[{key2,value1}]}].\n"), + + N1 = list_to_atom(lists:concat([?FUNCTION_NAME,"_1"])), + {ok,Node1} = start_node(N1,filename:rootname(Sys)), + ok = rpc:call(Node1, application, load, [app1()]), + {ok, value} = rpc:call(Node1, application, get_env,[app1,key1]), + {ok, value1} = rpc:call(Node1, application, get_env,[app1,key2]), + + Current2 = filename:join(SubDir,"current.config"), + ok = file:write_file(Current2,"[{app1,[{key2,value2}]}].\n"), + + N2 = list_to_atom(lists:concat([?FUNCTION_NAME,"_2"])), + {ok, Node2} = start_node(N2,filename:rootname(Sys)), + ok = rpc:call(Node2, application, load, [app1()]), + {ok, value} = rpc:call(Node2, application, get_env,[app1,key1]), + {ok, value2} = rpc:call(Node2, application, get_env,[app1,key2]), + + stop_node_nice([Node1,Node2]), + + ok. + %%----------------------------------------------------------------- %% Utility functions %%----------------------------------------------------------------- -- cgit v1.2.3 From 0c6c4c13b31f2a91568bb3a04e63bc3c878d503c Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 14 Jun 2018 12:04:52 +0200 Subject: [logger] Stress overload_kill tests in disk_log handler --- lib/kernel/test/logger_disk_log_h_SUITE.erl | 59 ++++++++++++++++++++--------- lib/kernel/test/logger_std_h_SUITE.erl | 13 +++++-- 2 files changed, 51 insertions(+), 21 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 7225d4b57c..492730bd89 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -1063,7 +1063,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(h_proc_name()), {_,Mem0} = process_info(Pid0, memory), RestartAfter = ?OVERLOAD_KILL_RESTART_AFTER, @@ -1076,7 +1076,7 @@ qlen_kill_new(Config) -> ok = logger:set_handler_config(?MODULE, NewHConfig), MRef = erlang:monitor(process, Pid0), NumOfReqs = 100, - Procs = 2, + Procs = 4, send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice), %% send_burst({n,NumOfReqs}, seq, {chars,79}, notice), receive @@ -1087,8 +1087,8 @@ qlen_kill_new(Config) -> killed -> ct:pal("Slow shutdown, handler process was killed!", []) end, - timer:sleep(RestartAfter + 2000), - true = is_pid(whereis(h_proc_name())), + file_delete(Log), + {ok,_} = wait_for_process_up(RestartAfter * 3), ok after 5000 -> @@ -1100,7 +1100,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(h_proc_name()), {_,Mem0} = process_info(Pid0, memory), RestartAfter = ?OVERLOAD_KILL_RESTART_AFTER, @@ -1113,7 +1113,7 @@ mem_kill_new(Config) -> ok = logger:set_handler_config(?MODULE, NewHConfig), MRef = erlang:monitor(process, Pid0), NumOfReqs = 100, - Procs = 2, + Procs = 4, send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice), %% send_burst({n,NumOfReqs}, seq, {chars,79}, notice), receive @@ -1124,8 +1124,8 @@ mem_kill_new(Config) -> killed -> ct:pal("Slow shutdown, handler process was killed!", []) end, - timer:sleep(RestartAfter + 2000), - true = is_pid(whereis(h_proc_name())), + file_delete(Log), + {ok,_} = wait_for_process_up(RestartAfter * 3), ok after 5000 -> @@ -1136,6 +1136,8 @@ mem_kill_new(Config) -> mem_kill_new(cleanup, _Config) -> ok = stop_handler(?MODULE). +restart_after() -> + [{timetrap,{minutes,2}}]. restart_after(Config) -> {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NewHConfig1 = @@ -1145,14 +1147,16 @@ restart_after(Config) -> ok = logger:set_handler_config(?MODULE, NewHConfig1), MRef1 = erlang:monitor(process, whereis(h_proc_name())), %% kill handler - send_burst({n,100}, {spawn,2,0}, {chars,79}, notice), + send_burst({n,100}, {spawn,4,0}, {chars,79}, notice), receive - {'DOWN', MRef1, _, _, _Info1} -> - timer:sleep(?OVERLOAD_KILL_RESTART_AFTER + 1000), - undefined = whereis(h_proc_name()), + {'DOWN', MRef1, _, _, _Reason1} -> + file_delete(Log), + error = wait_for_process_up(?OVERLOAD_KILL_RESTART_AFTER * 3), ok after 5000 -> + Info1 = logger_std_h:info(?MODULE), + ct:pal("Handler state = ~p", [Info1]), ct:fail("Handler not dead! It should not have survived this!") end, @@ -1166,16 +1170,17 @@ restart_after(Config) -> Pid0 = whereis(h_proc_name()), MRef2 = erlang:monitor(process, Pid0), %% kill handler - send_burst({n,100}, {spawn,2,0}, {chars,79}, notice), + send_burst({n,100}, {spawn,4,0}, {chars,79}, notice), receive - {'DOWN', MRef2, _, _, _Info2} -> - timer:sleep(RestartAfter + 2000), - Pid1 = whereis(h_proc_name()), - true = is_pid(Pid1), + {'DOWN', MRef2, _, _, _Reason2} -> + file_delete(Log), + {ok,Pid1} = wait_for_process_up(RestartAfter * 3), false = (Pid1 == Pid0), ok after 5000 -> + Info2 = logger_std_h:info(?MODULE), + ct:pal("Handler state = ~p", [Info2]), ct:fail("Handler not dead! It should not have survived this!") end, ok. @@ -1539,3 +1544,23 @@ h_proc_name(Name) -> file_delete(Log) -> file:delete(Log). + +wait_for_process_up(T) -> + wait_for_process_up(h_proc_name(),T). + +wait_for_process_up(Name,T) -> + N = (T div 500) + 1, + wait_for_process_up1(Name,N). + +wait_for_process_up1(Name,0) -> + error; +wait_for_process_up1(Name,N) -> + timer:sleep(500), + case whereis(Name) of + Pid when is_pid(Pid) -> + %% ct:pal("Process ~p up (~p tries left)",[Name,N]), + {ok,Pid}; + undefined -> + %% ct:pal("Waiting for process ~p (~p tries left)",[Name,N]), + wait_for_process_up1(Name,N-1) + end. diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index e0bc79012c..c2cc7653ea 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -995,7 +995,7 @@ qlen_kill_new(Config) -> ct:pal("Slow shutdown, handler process was killed!", []) end, file_delete(Log), - {ok,_} = wait_for_process_up(h_proc_name(), RestartAfter * 3), + {ok,_} = wait_for_process_up(RestartAfter * 3), ok after 5000 -> @@ -1046,7 +1046,7 @@ mem_kill_new(Config) -> ct:pal("Slow shutdown, handler process was killed!", []) end, file_delete(Log), - {ok,_} = wait_for_process_up(h_proc_name(), RestartAfter * 3), + {ok,_} = wait_for_process_up(RestartAfter * 3), ok after 5000 -> @@ -1077,7 +1077,7 @@ restart_after(Config) -> receive {'DOWN', MRef1, _, _, _Reason1} -> file_delete(Log), - error = wait_for_process_up(h_proc_name(),?OVERLOAD_KILL_RESTART_AFTER * 3), + error = wait_for_process_up(?OVERLOAD_KILL_RESTART_AFTER * 3), ok after 5000 -> @@ -1100,7 +1100,7 @@ restart_after(Config) -> receive {'DOWN', MRef2, _, _, _Reason2} -> file_delete(Log), - {ok,Pid1} = wait_for_process_up(h_proc_name(),RestartAfter * 3), + {ok,Pid1} = wait_for_process_up(RestartAfter * 3), false = (Pid1 == Pid0), ok after @@ -1570,6 +1570,9 @@ h_proc_name(Name) -> file_delete(Log) -> file:delete(Log). +wait_for_process_up(T) -> + wait_for_process_up(h_proc_name(),T). + wait_for_process_up(Name,T) -> N = (T div 500) + 1, wait_for_process_up1(Name,N). @@ -1580,7 +1583,9 @@ wait_for_process_up1(Name,N) -> timer:sleep(500), case whereis(Name) of Pid when is_pid(Pid) -> + %% ct:pal("Process ~p up (~p tries left)",[Name,N]), {ok,Pid}; undefined -> + %% ct:pal("Waiting for process ~p (~p tries left)",[Name,N]), wait_for_process_up1(Name,N-1) end. -- cgit v1.2.3 From 40f29ea1f77dec1cc4920b3593091b35b597f8ff Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 14 Jun 2018 12:21:18 +0200 Subject: [logger] Unregister handler names before terminating --- lib/kernel/src/logger_disk_log_h.erl | 6 +++++- lib/kernel/src/logger_std_h.erl | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index a074d0210e..394c7b7320 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -249,7 +249,8 @@ init([Name, max_no_files:=MNF}}, State = #{dl_sync_int := DLSyncInt}]) -> - register(?name_to_reg_name(?MODULE,Name), self()), + RegName = ?name_to_reg_name(?MODULE,Name), + register(RegName, self()), process_flag(trap_exit, true), process_flag(message_queue_data, off_heap), @@ -296,10 +297,12 @@ init([Name, enter_loop(Config1, State1) catch _:Error -> + unregister(RegName), logger_h_common:error_notify({open_disk_log,Name,Error}), proc_lib:init_ack(Error) end; Error -> + unregister(RegName), logger_h_common:error_notify({open_disk_log,Name,Error}), proc_lib:init_ack(Error) end. @@ -426,6 +429,7 @@ terminate(Reason, State = #{id := Name}) -> _ = logger_h_common:cancel_timer(maps:get(rep_sync_tref, State, undefined)), _ = close_disk_log(Name, normal), + unregister(?name_to_reg_name(?MODULE, Name)), logger_h_common:stop_or_restart(Name, Reason, State). code_change(_OldVsn, State, _Extra) -> diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index ce9daa50ab..801d05853a 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -229,7 +229,8 @@ log(LogEvent, Config = #{id := Name, init([Name, Config = #{config := HConfig}, State0 = #{type := Type, file_ctrl_sync_int := FileCtrlSyncInt}]) -> - register(?name_to_reg_name(?MODULE,Name), self()), + RegName = ?name_to_reg_name(?MODULE,Name), + register(RegName, self()), process_flag(trap_exit, true), process_flag(message_queue_data, off_heap), @@ -261,10 +262,12 @@ init([Name, Config = #{config := HConfig}, enter_loop(Config1, State1) catch _:Error -> + unregister(RegName), logger_h_common:error_notify({init_handler,Name,Error}), proc_lib:init_ack(Error) end; Error -> + unregister(RegName), logger_h_common:error_notify({init_handler,Name,Error}), proc_lib:init_ack(Error) end. @@ -415,6 +418,7 @@ terminate(Reason, State = #{id:=Name, file_ctrl_pid:=FWPid, false -> ok end, + unregister(?name_to_reg_name(?MODULE, Name)), logger_h_common:stop_or_restart(Name, Reason, State). code_change(_OldVsn, State, _Extra) -> -- cgit v1.2.3 From 1a07345694834bcb68ca2f27d2dc940e0f036f9c Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 14 Jun 2018 15:19:50 +0200 Subject: [logger] Adjust priority settings in test Now only setting high priority on every second burst sending process, to allow for handler process to be scheduled in every now and then. --- lib/kernel/test/logger_disk_log_h_SUITE.erl | 18 ++++++++++++------ lib/kernel/test/logger_std_h_SUITE.erl | 18 ++++++++++++------ 2 files changed, 24 insertions(+), 12 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 492730bd89..bb2b5eed57 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -1285,13 +1285,9 @@ send_n_burst(N, seq, Text, Class) -> send_n_burst(N-1, seq, Text, Class); send_n_burst(N, {spawn,Ps,TO}, Text, Class) -> ct:pal("~w processes each sending ~w messages", [Ps,N]), - PerProc = fun() -> - process_flag(priority,high), - send_n_burst(N, seq, Text, Class) - end, MRefs = [begin if TO == 0 -> ok; true -> timer:sleep(TO) end, - monitor(process,spawn_link(PerProc)) end || - _ <- lists:seq(1,Ps)], + monitor(process,spawn_link(per_proc_fun(N,Text,Class,X))) + end || X <- lists:seq(1,Ps)], lists:foreach(fun(MRef) -> receive {'DOWN', MRef, _, _, _} -> @@ -1310,6 +1306,16 @@ send_t_burst(T0, T, Text, Class, N) -> send_t_burst(T0, T, Text, Class, N+1) end. +per_proc_fun(N,Text,Class,X) when X rem 2 == 0 -> + fun() -> + process_flag(priority,high), + send_n_burst(N, seq, Text, Class) + end; +per_proc_fun(N,Text,Class,_) -> + fun() -> + send_n_burst(N, seq, Text, Class) + end. + %%%----------------------------------------------------------------- %%% Formatter callback %%% Using this to send the formatted string back to the test case diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index c2cc7653ea..76f8a35406 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -1257,13 +1257,9 @@ send_n_burst(N, seq, Text, Class) -> send_n_burst(N-1, seq, Text, Class); send_n_burst(N, {spawn,Ps,TO}, Text, Class) -> ct:pal("~w processes each sending ~w messages", [Ps,N]), - PerProc = fun() -> - process_flag(priority,high), - send_n_burst(N, seq, Text, Class) - end, MRefs = [begin if TO == 0 -> ok; true -> timer:sleep(TO) end, - monitor(process,spawn_link(PerProc)) end || - _ <- lists:seq(1,Ps)], + monitor(process,spawn_link(per_proc_fun(N,Text,Class,X))) + end || X <- lists:seq(1,Ps)], lists:foreach(fun(MRef) -> receive {'DOWN', MRef, _, _, _} -> @@ -1282,6 +1278,16 @@ send_t_burst(T0, T, Text, Class, N) -> send_t_burst(T0, T, Text, Class, N+1) end. +per_proc_fun(N,Text,Class,X) when X rem 2 == 0 -> + fun() -> + process_flag(priority,high), + send_n_burst(N, seq, Text, Class) + end; +per_proc_fun(N,Text,Class,_) -> + fun() -> + send_n_burst(N, seq, Text, Class) + end. + %%%----------------------------------------------------------------- %%% Formatter callback %%% Using this to send the formatted string back to the test case -- cgit v1.2.3 From 5e23103186cb71c64f7f2fa28cbfd8c62d7ee21b Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 13 Jun 2018 11:48:58 +0200 Subject: [logger] Update documentation --- lib/kernel/doc/src/logger.xml | 33 +++++--- lib/kernel/doc/src/logger_arch.png | Bin 32407 -> 32187 bytes lib/kernel/doc/src/logger_chapter.xml | 137 +++++++++++++++------------------- 3 files changed, 84 insertions(+), 86 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index 7f35a5d752..8940c89ab8 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -86,6 +86,14 @@ logger:error("error happened because: ~p", [Reason]). % Without macro built-in filters, see logger_filters. + + +

    Since Logger is new in Erlang/OTP 21.0, we do reserve the right + to introduce changes to the Logger API and functionality in + patches following this release. These changes might or might not + be backwards compatible with the initial version.

    + + @@ -939,7 +947,8 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)). - HModule:adding_handler(Config1) -> {ok, Config2} | {error, Reason} + HModule:adding_handler(Config1) -> {ok, Config2} | {error, + Reason} An instance of this handler is about to be added. Config1 = Config2 = @@ -948,9 +957,10 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)).

    This callback function is optional.

    -

    The function is called when an new handler is about to be - added, and the purpose is to verify the configuration and - initiate all resources needed by the handler.

    +

    The function is called on a temporary process when an new + handler is about to be added. The purpose is to verify the + configuration and initiate all resources needed by the + handler.

    The handler identity is associated with the id key in Config1.

    If everything succeeds, the callback function can add @@ -972,9 +982,9 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)).

    This callback function is optional.

    -

    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.

    +

    The function is called on a temporary process when the + configuration for a handler is about to change. The purpose + is to verify and act on the new configuration.

    Config1 is the existing configuration and Config2 is the new configuration.

    The handler identity is associated with the id key @@ -999,7 +1009,8 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)).

    This callback function is mandatory.

    The function is called when all primary filters and all handler filters for the handler in question have passed for - the given log event.

    + the given log event. It is called on the client process, that + is, the process that issued the log event.

    The handler identity is associated with the id key in Config.

    The handler must log the event.

    @@ -1017,9 +1028,9 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)).

    This callback function is optional.

    -

    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 function is called on a temporary process when a + handler is about to be removed. The purpose is to release + all resources used by the handler.

    The handler identity is associated with the id key in Config.

    The return value is ignored by Logger.

    diff --git a/lib/kernel/doc/src/logger_arch.png b/lib/kernel/doc/src/logger_arch.png index a9b9a658b4..a3a863c511 100644 Binary files a/lib/kernel/doc/src/logger_arch.png and b/lib/kernel/doc/src/logger_arch.png differ diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index f7df0a3e6e..6e1eab8a8e 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -48,6 +48,13 @@ handler, replace it by a custom handler, and install additional handlers.

    + +

    Since Logger is new in Erlang/OTP 21.0, we do reserve the right + to introduce changes to the Logger API and functionality in + patches following this release. These changes might or might not + be backwards compatible with the initial version.

    +
    +
    Overview

    A log event consists of a log level, the @@ -84,11 +91,11 @@ section Filters for more details.

    If a log event passes through all primary filters and all - handler filters for a specific handler, Logger forwards the event - to the handler callback. The handler formats and prints the - event to its destination. See - section Handlers for - more details.

    + handler filters for a specific handler, Logger forwards the + event to the handler callback. The handler formats and + prints the event to its destination. See + section Handlers for more + details.

    Everything up to and including the call to the handler callbacks is executed on the client process, that is, the process where the log event was issued. It is up to the handler @@ -113,10 +120,11 @@ Log Level

    The log level indicates the severity of a event. In - accordance with the Syslog protocol, RFC-5424, eight log - levels can be specified. The following table lists all - possible log levels by name (atom), integer value, and - description:

    + accordance with the Syslog protocol, + RFC + 5424, eight log levels can be specified. The following + table lists all possible log levels by name (atom), integer + value, and description:

    @@ -337,7 +345,7 @@ logger:debug(#{got => connection_request, id => Id, state => State}, Handlers

    A handler is defined as a module exporting at least the - following function:

    + following callback function:

    log(LogEvent, Config) -> void()
    @@ -934,50 +942,50 @@ error_logger:add_report_handler/1,2.
    - Example: Add a handler to log debug events to file + Example: Add a handler to log info events to file

    When starting an Erlang node, the default behaviour is that all - log events on level info or more severe, are logged to the - terminal via the default handler. To also log debug events, you - can either change the primary log level to debug:

    + log events on level notice or more severe, are logged to + the terminal via the default handler. To also log info events, + you can either change the primary log level to info:

    -1> logger:set_primary_config(level, debug).
    +1> logger:set_primary_config(level, info).
     ok

    or set the level for one or a few modules only:

    -2> logger:set_module_level(mymodule, debug).
    +2> logger:set_module_level(mymodule, info).
     ok
    -

    This allows debug events to pass through to the default handler, - and be printed to the terminal as well. If there are many debug +

    This allows info events to pass through to the default handler, + and be printed to the terminal as well. If there are many info events, it can be useful to print these to a file instead.

    -

    First, set the log level of the default handler to info, - preventing it from printing debug events to the terminal:

    +

    First, set the log level of the default handler + to notice, preventing it from printing info events to the + terminal:

    -3> logger:set_handler_config(default, level, info).
    +3> logger:set_handler_config(default, level, notice).
     ok

    Then, add a new handler which prints to file. You can use the handler module logger_std_h, - and specify type {file,File}. The default handler level - is all, so you don't need to specify that:

    + and specify type {file,File}.:

    -4> Config = #{config => #{type => {file,"./debug.log"}}}.
    -#{config => #{type => {file,"./debug.log"}}}
    -5> logger:add_handler(debugger, logger_std_h, Config).
    +4> Config = #{config => #{type => {file,"./info.log"}}, level => info}.
    +#{config => #{type => {file,"./info.log"}},level => info}
    +5> logger:add_handler(myhandler, logger_std_h, Config).
     ok

    Since filter_default defaults to log, this - handler now receives all log events. If you want debug events - only in the file, you must add a filter to stop all non-debug + handler now receives all log events. If you want info events + only in the file, you must add a filter to stop all non-info events. The built-in filter logger_filters:level/2 can do this:

    -6> logger:add_handler_filter(debugger, stop_non_debug,
    -                             {fun logger_filters:level/2, {stop, neq, debug}}).
    +6> logger:add_handler_filter(myhandler, stop_non_info,
    +                             {fun logger_filters:level/2, {stop, neq, info}}).
     ok

    See section Filters for - more information about the filters and the filter_default - configuration parameter.

    + more information about the filters and the filter_default + configuration parameter.

    @@ -1023,63 +1031,42 @@ ok

    A simple handler that prints to the terminal can be implemented as follows:

    --module(myhandler). +-module(myhandler1). -export([log/2]). -log(LogEvent, #{formatter := {FModule, FConfig}) -> +log(LogEvent, #{formatter := {FModule, FConfig}}) -> io:put_chars(FModule:format(LogEvent, FConfig)). -

    A simple handler which prints to file can be implemented like - this:

    - --module(myhandler). --export([adding_handler/1, removing_handler/1, log/2]). --export([init/1, handle_call/3, handle_cast/2, terminate/2]). - -adding_handler(Config) -> - {ok, Fd} = file:open(File, [append, {encoding, utf8}]), - {ok, Config#{myhandler_fd => Fd}}. - -removing_handler(#{myhandler_fd := Fd}) -> - _ = file:close(Fd), - ok. - -log(LogEvent,#{myhandler_fd := Fd, formatter := {FModule, FConfig}}) -> - io:put_chars(Fd, FModule:format(LogEvent, FConfig)). - - - -

    The above handlers do not have any overload - protection, and all log events are printed directly from the - client process.

    -

    For information and examples of overload protection, please - refer to - section Protecting the - Handler from Overload, and the implementation - of logger_std_h - and logger_disk_log_h - .

    -
    - -

    Below is a simpler example of a handler which logs through one - single process.

    +

    Notice that the above handler does not have any overload + protection, and all log events are printed directly from the + client process.

    +

    For information and examples of overload protection, please + refer to + section Protecting the + Handler from Overload, and the implementation + of logger_std_h + and logger_disk_log_h + .

    +

    The following is a simpler example of a handler which logs to a + file through one single process:

    --module(myhandler). +-module(myhandler2). -export([adding_handler/1, removing_handler/1, log/2]). -export([init/1, handle_call/3, handle_cast/2, terminate/2]). adding_handler(Config) -> - {ok, Pid} = gen_server:start(?MODULE, Config), - {ok, Config#{myhandler_pid => Pid}}. + MyConfig = maps:get(config,Config,#{file => "myhandler2.log"}), + {ok, Pid} = gen_server:start(?MODULE, MyConfig, []), + {ok, Config#{config => MyConfig#{pid => Pid}}}. -removing_handler(#{myhandler_pid := Pid}) -> +removing_handler(#{config := #{pid := Pid}}) -> gen_server:stop(Pid). -log(LogEvent,#{myhandler_pid := Pid} = Config) -> +log(LogEvent,#{config := #{pid := Pid}} = Config) -> gen_server:cast(Pid, {log, LogEvent, Config}). -init(#{myhandler_file := File}) -> +init(#{file := File}) -> {ok, Fd} = file:open(File, [append, {encoding, utf8}]), {ok, #{file => File, fd => Fd}}. @@ -1090,7 +1077,7 @@ handle_cast({log, LogEvent, Config}, #{fd := Fd} = State) -> do_log(Fd, LogEvent, Config), {noreply, State}. -terminate(Reason, #{fd := Fd}) -> +terminate(_Reason, #{fd := Fd}) -> _ = file:close(Fd), ok. -- cgit v1.2.3 From 5ca92e2eac1e84fd22f60e7abc3aa2b0ff1cb42b Mon Sep 17 00:00:00 2001 From: Henrik Nord Date: Mon, 18 Jun 2018 14:51:18 +0200 Subject: Update copyright year --- lib/kernel/doc/src/Makefile | 2 +- lib/kernel/doc/src/application.xml | 2 +- lib/kernel/doc/src/book.xml | 2 +- lib/kernel/doc/src/code.xml | 2 +- lib/kernel/doc/src/config.xml | 2 +- lib/kernel/doc/src/error_logger.xml | 2 +- lib/kernel/doc/src/file.xml | 2 +- lib/kernel/doc/src/heart.xml | 2 +- lib/kernel/doc/src/introduction_chapter.xml | 2 +- lib/kernel/doc/src/logger.xml | 2 +- lib/kernel/doc/src/logger_chapter.xml | 2 +- lib/kernel/doc/src/logger_disk_log_h.xml | 2 +- lib/kernel/doc/src/logger_formatter.xml | 2 +- lib/kernel/doc/src/logger_std_h.xml | 2 +- lib/kernel/doc/src/notes.xml | 2 +- lib/kernel/doc/src/os.xml | 2 +- lib/kernel/doc/src/ref_man.xml | 2 +- lib/kernel/doc/src/seq_trace.xml | 2 +- lib/kernel/include/dist.hrl | 2 +- lib/kernel/include/dist_util.hrl | 2 +- lib/kernel/src/Makefile | 2 +- lib/kernel/src/application_master.erl | 2 +- lib/kernel/src/auth.erl | 2 +- lib/kernel/src/code_server.erl | 2 +- lib/kernel/src/disk_log_1.erl | 2 +- lib/kernel/src/erl_epmd.erl | 2 +- lib/kernel/src/erl_signal_handler.erl | 2 +- lib/kernel/src/error_handler.erl | 2 +- lib/kernel/src/error_logger.erl | 2 +- lib/kernel/src/gen_sctp.erl | 2 +- lib/kernel/src/gen_tcp.erl | 2 +- lib/kernel/src/gen_udp.erl | 2 +- lib/kernel/src/group.erl | 2 +- lib/kernel/src/inet_tcp_dist.erl | 2 +- lib/kernel/src/kernel.app.src | 2 +- lib/kernel/src/kernel.erl | 2 +- lib/kernel/src/kernel_config.erl | 2 +- lib/kernel/src/logger.erl | 2 +- lib/kernel/src/logger_backend.erl | 2 +- lib/kernel/src/logger_config.erl | 2 +- lib/kernel/src/logger_disk_log_h.erl | 2 +- lib/kernel/src/logger_filters.erl | 2 +- lib/kernel/src/logger_formatter.erl | 2 +- lib/kernel/src/logger_h_common.erl | 2 +- lib/kernel/src/logger_internal.hrl | 2 +- lib/kernel/src/logger_server.erl | 2 +- lib/kernel/src/logger_simple_h.erl | 2 +- lib/kernel/src/logger_std_h.erl | 2 +- lib/kernel/src/logger_sup.erl | 2 +- lib/kernel/src/os.erl | 2 +- lib/kernel/src/seq_trace.erl | 2 +- lib/kernel/src/user_drv.erl | 2 +- lib/kernel/test/Makefile | 2 +- lib/kernel/test/application_SUITE.erl | 2 +- lib/kernel/test/code_SUITE.erl | 2 +- lib/kernel/test/erl_prim_loader_SUITE.erl | 2 +- lib/kernel/test/error_logger_SUITE.erl | 2 +- lib/kernel/test/error_logger_warn_SUITE.erl | 2 +- lib/kernel/test/file_SUITE.erl | 2 +- lib/kernel/test/gen_sctp_SUITE.erl | 2 +- lib/kernel/test/gen_tcp_api_SUITE.erl | 2 +- lib/kernel/test/gen_tcp_misc_SUITE.erl | 2 +- lib/kernel/test/gen_udp_SUITE.erl | 2 +- lib/kernel/test/heart_SUITE.erl | 2 +- lib/kernel/test/inet_res_SUITE.erl | 2 +- lib/kernel/test/kernel_SUITE.erl | 2 +- lib/kernel/test/kernel_config_SUITE.erl | 2 +- lib/kernel/test/os_SUITE.erl | 2 +- lib/kernel/test/pdict_SUITE.erl | 2 +- lib/kernel/test/prim_file_SUITE.erl | 2 +- lib/kernel/test/seq_trace_SUITE.erl | 2 +- lib/kernel/test/wrap_log_reader_SUITE.erl | 2 +- lib/kernel/test/wrap_log_reader_SUITE_data/wrap_log_test.erl | 2 +- lib/kernel/test/zlib_SUITE.erl | 2 +- 74 files changed, 74 insertions(+), 74 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile index 29dc73a523..f34eee71ba 100644 --- a/lib/kernel/doc/src/Makefile +++ b/lib/kernel/doc/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2016. All Rights Reserved. +# Copyright Ericsson AB 1997-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. diff --git a/lib/kernel/doc/src/application.xml b/lib/kernel/doc/src/application.xml index be914aee87..38c7b5acf1 100644 --- a/lib/kernel/doc/src/application.xml +++ b/lib/kernel/doc/src/application.xml @@ -4,7 +4,7 @@
    - 19962016 + 19962018 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/book.xml b/lib/kernel/doc/src/book.xml index 0b69b547e7..4b3573b9fe 100644 --- a/lib/kernel/doc/src/book.xml +++ b/lib/kernel/doc/src/book.xml @@ -4,7 +4,7 @@
    - 19972016 + 19972018 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml index bd95819636..aff3e8133c 100644 --- a/lib/kernel/doc/src/code.xml +++ b/lib/kernel/doc/src/code.xml @@ -4,7 +4,7 @@
    - 19962017 + 19962018 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/config.xml b/lib/kernel/doc/src/config.xml index 3f01170508..714af93f4d 100644 --- a/lib/kernel/doc/src/config.xml +++ b/lib/kernel/doc/src/config.xml @@ -4,7 +4,7 @@
    - 19972017 + 19972018 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml index 27fb1488c7..c3d68fd79f 100644 --- a/lib/kernel/doc/src/error_logger.xml +++ b/lib/kernel/doc/src/error_logger.xml @@ -4,7 +4,7 @@
    - 19962017 + 19962018 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index 1b72769ce3..9acaf6b41e 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -4,7 +4,7 @@
    - 19962016 + 19962018 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/heart.xml b/lib/kernel/doc/src/heart.xml index 46c7ce60b6..ad1a2ffeb9 100644 --- a/lib/kernel/doc/src/heart.xml +++ b/lib/kernel/doc/src/heart.xml @@ -4,7 +4,7 @@
    - 19962016 + 19962018 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/introduction_chapter.xml b/lib/kernel/doc/src/introduction_chapter.xml index 2eadc70abf..d02b1a2ee5 100644 --- a/lib/kernel/doc/src/introduction_chapter.xml +++ b/lib/kernel/doc/src/introduction_chapter.xml @@ -4,7 +4,7 @@
    - 2017 + 20172018 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index 8940c89ab8..33f1919de0 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -4,7 +4,7 @@
    - 2017 + 20172018 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 208193ee42..f4a752bde9 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -4,7 +4,7 @@
    - 2017 + 20172018 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/logger_disk_log_h.xml b/lib/kernel/doc/src/logger_disk_log_h.xml index 98439983cf..dfe2ab3275 100644 --- a/lib/kernel/doc/src/logger_disk_log_h.xml +++ b/lib/kernel/doc/src/logger_disk_log_h.xml @@ -4,7 +4,7 @@
    - 2017 + 20172018 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/logger_formatter.xml b/lib/kernel/doc/src/logger_formatter.xml index 59f5dbe367..e777afaf86 100644 --- a/lib/kernel/doc/src/logger_formatter.xml +++ b/lib/kernel/doc/src/logger_formatter.xml @@ -4,7 +4,7 @@
    - 2017 + 20172018 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/logger_std_h.xml b/lib/kernel/doc/src/logger_std_h.xml index 95b4baf160..b526ed037d 100644 --- a/lib/kernel/doc/src/logger_std_h.xml +++ b/lib/kernel/doc/src/logger_std_h.xml @@ -4,7 +4,7 @@
    - 2017 + 20172018 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 09844f1502..3d433e7efa 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -4,7 +4,7 @@
    - 20042017 + 20042018 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml index ef416ed233..c95e615c6b 100644 --- a/lib/kernel/doc/src/os.xml +++ b/lib/kernel/doc/src/os.xml @@ -4,7 +4,7 @@
    - 19972017 + 19972018 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/ref_man.xml b/lib/kernel/doc/src/ref_man.xml index b6c2714664..d3b947527f 100644 --- a/lib/kernel/doc/src/ref_man.xml +++ b/lib/kernel/doc/src/ref_man.xml @@ -4,7 +4,7 @@
    - 19962016 + 19962018 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/doc/src/seq_trace.xml b/lib/kernel/doc/src/seq_trace.xml index 69eb12a8a0..1a4a74419a 100644 --- a/lib/kernel/doc/src/seq_trace.xml +++ b/lib/kernel/doc/src/seq_trace.xml @@ -4,7 +4,7 @@
    - 19982017 + 19982018 Ericsson AB. All Rights Reserved. diff --git a/lib/kernel/include/dist.hrl b/lib/kernel/include/dist.hrl index 6baaa35d72..003852f1b0 100644 --- a/lib/kernel/include/dist.hrl +++ b/lib/kernel/include/dist.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-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. diff --git a/lib/kernel/include/dist_util.hrl b/lib/kernel/include/dist_util.hrl index eeb0f8dd43..56f775f060 100644 --- a/lib/kernel/include/dist_util.hrl +++ b/lib/kernel/include/dist_util.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-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. diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile index f265fdd272..c595c25341 100644 --- a/lib/kernel/src/Makefile +++ b/lib/kernel/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2017. All Rights Reserved. +# Copyright Ericsson AB 1996-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. diff --git a/lib/kernel/src/application_master.erl b/lib/kernel/src/application_master.erl index 06991b45e1..8697143dfb 100644 --- a/lib/kernel/src/application_master.erl +++ b/lib/kernel/src/application_master.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. diff --git a/lib/kernel/src/auth.erl b/lib/kernel/src/auth.erl index a2116d8e8a..4d18daf9e4 100644 --- a/lib/kernel/src/auth.erl +++ b/lib/kernel/src/auth.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index 1a7677295b..1b4a67ecb7 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2016. All Rights Reserved. +%% Copyright Ericsson AB 1998-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. diff --git a/lib/kernel/src/disk_log_1.erl b/lib/kernel/src/disk_log_1.erl index b456b53d20..41ef33c613 100644 --- a/lib/kernel/src/disk_log_1.erl +++ b/lib/kernel/src/disk_log_1.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2017. All Rights Reserved. +%% Copyright Ericsson AB 1997-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. diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl index 9a0939972d..b7e8868911 100644 --- a/lib/kernel/src/erl_epmd.erl +++ b/lib/kernel/src/erl_epmd.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2016. All Rights Reserved. +%% Copyright Ericsson AB 1998-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. diff --git a/lib/kernel/src/erl_signal_handler.erl b/lib/kernel/src/erl_signal_handler.erl index b76c2a217a..5be905d8ae 100644 --- a/lib/kernel/src/erl_signal_handler.erl +++ b/lib/kernel/src/erl_signal_handler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-2018. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in diff --git a/lib/kernel/src/error_handler.erl b/lib/kernel/src/error_handler.erl index a9582c6225..a89ef83261 100644 --- a/lib/kernel/src/error_handler.erl +++ b/lib/kernel/src/error_handler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index 918a915729..a7e7f19167 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl index 3526df3600..bf795ee9c6 100644 --- a/lib/kernel/src/gen_sctp.erl +++ b/lib/kernel/src/gen_sctp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2016. All Rights Reserved. +%% Copyright Ericsson AB 2007-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. diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl index 253c63528f..c61411e814 100644 --- a/lib/kernel/src/gen_tcp.erl +++ b/lib/kernel/src/gen_tcp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-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. diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl index 9ab58011ec..44eef9f3c5 100644 --- a/lib/kernel/src/gen_udp.erl +++ b/lib/kernel/src/gen_udp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-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. diff --git a/lib/kernel/src/group.erl b/lib/kernel/src/group.erl index 2c0518ccad..5625ae6eb7 100644 --- a/lib/kernel/src/group.erl +++ b/lib/kernel/src/group.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl index b4b50899f7..d1701afdaa 100644 --- a/lib/kernel/src/inet_tcp_dist.erl +++ b/lib/kernel/src/inet_tcp_dist.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2017. All Rights Reserved. +%% Copyright Ericsson AB 1997-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. diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 390f1fa42a..47dd7c03d5 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl index b0e8c00bbf..c68d04e279 100644 --- a/lib/kernel/src/kernel.erl +++ b/lib/kernel/src/kernel.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. diff --git a/lib/kernel/src/kernel_config.erl b/lib/kernel/src/kernel_config.erl index c5ff1887c2..691a266c2d 100644 --- a/lib/kernel/src/kernel_config.erl +++ b/lib/kernel/src/kernel_config.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 7d121f22fe..0a9b1672ec 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2017. All Rights Reserved. +%% Copyright Ericsson AB 2017-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. diff --git a/lib/kernel/src/logger_backend.erl b/lib/kernel/src/logger_backend.erl index a73b681e0d..4d7bd6b2a0 100644 --- a/lib/kernel/src/logger_backend.erl +++ b/lib/kernel/src/logger_backend.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2017. All Rights Reserved. +%% Copyright Ericsson AB 2017-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. diff --git a/lib/kernel/src/logger_config.erl b/lib/kernel/src/logger_config.erl index 7b2148d034..55427dce5a 100644 --- a/lib/kernel/src/logger_config.erl +++ b/lib/kernel/src/logger_config.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2017. All Rights Reserved. +%% Copyright Ericsson AB 2017-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. diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index cf8ea658e3..0a72654e11 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2017. All Rights Reserved. +%% Copyright Ericsson AB 2017-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. diff --git a/lib/kernel/src/logger_filters.erl b/lib/kernel/src/logger_filters.erl index 7359b3b4b7..0664c598e1 100644 --- a/lib/kernel/src/logger_filters.erl +++ b/lib/kernel/src/logger_filters.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2017. All Rights Reserved. +%% Copyright Ericsson AB 2017-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. diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl index 33b250eef6..456b0c9e8d 100644 --- a/lib/kernel/src/logger_formatter.erl +++ b/lib/kernel/src/logger_formatter.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2017. All Rights Reserved. +%% Copyright Ericsson AB 2017-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. diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl index f9f762405c..a40345dddc 100644 --- a/lib/kernel/src/logger_h_common.erl +++ b/lib/kernel/src/logger_h_common.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2017. All Rights Reserved. +%% Copyright Ericsson AB 2017-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. diff --git a/lib/kernel/src/logger_internal.hrl b/lib/kernel/src/logger_internal.hrl index 8941409a25..d96a4ac78b 100644 --- a/lib/kernel/src/logger_internal.hrl +++ b/lib/kernel/src/logger_internal.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2017. All Rights Reserved. +%% Copyright Ericsson AB 2017-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. diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index e50954fc40..28e31d46ea 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2017. All Rights Reserved. +%% Copyright Ericsson AB 2017-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. diff --git a/lib/kernel/src/logger_simple_h.erl b/lib/kernel/src/logger_simple_h.erl index 19fb3b54ba..8b51dd8569 100644 --- a/lib/kernel/src/logger_simple_h.erl +++ b/lib/kernel/src/logger_simple_h.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2017. All Rights Reserved. +%% Copyright Ericsson AB 2017-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. diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index 2a37076dda..480fafd6d8 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2017. All Rights Reserved. +%% Copyright Ericsson AB 2017-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. diff --git a/lib/kernel/src/logger_sup.erl b/lib/kernel/src/logger_sup.erl index 4e4de94d5c..dcdcdad0bd 100644 --- a/lib/kernel/src/logger_sup.erl +++ b/lib/kernel/src/logger_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2017. All Rights Reserved. +%% Copyright Ericsson AB 2017-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. diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl index 77c883f57f..29a26674ba 100644 --- a/lib/kernel/src/os.erl +++ b/lib/kernel/src/os.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2017. All Rights Reserved. +%% Copyright Ericsson AB 1997-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. diff --git a/lib/kernel/src/seq_trace.erl b/lib/kernel/src/seq_trace.erl index 8d7aba0f27..14fe21e9de 100644 --- a/lib/kernel/src/seq_trace.erl +++ b/lib/kernel/src/seq_trace.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2016. All Rights Reserved. +%% Copyright Ericsson AB 1998-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. diff --git a/lib/kernel/src/user_drv.erl b/lib/kernel/src/user_drv.erl index 99ea4210bd..9f914aa222 100644 --- a/lib/kernel/src/user_drv.erl +++ b/lib/kernel/src/user_drv.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile index 0646395800..4a86265a4a 100644 --- a/lib/kernel/test/Makefile +++ b/lib/kernel/test/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2016. All Rights Reserved. +# Copyright Ericsson AB 1997-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. diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl index 3d07d6d70d..5c35b82207 100644 --- a/lib/kernel/test/application_SUITE.erl +++ b/lib/kernel/test/application_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 902196def2..1314316c13 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. diff --git a/lib/kernel/test/erl_prim_loader_SUITE.erl b/lib/kernel/test/erl_prim_loader_SUITE.erl index 3502a4ad08..16a127aa3e 100644 --- a/lib/kernel/test/erl_prim_loader_SUITE.erl +++ b/lib/kernel/test/erl_prim_loader_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. diff --git a/lib/kernel/test/error_logger_SUITE.erl b/lib/kernel/test/error_logger_SUITE.erl index 895478f170..eab72e58a7 100644 --- a/lib/kernel/test/error_logger_SUITE.erl +++ b/lib/kernel/test/error_logger_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. diff --git a/lib/kernel/test/error_logger_warn_SUITE.erl b/lib/kernel/test/error_logger_warn_SUITE.erl index ef55a2d339..8f1eb2ba0a 100644 --- a/lib/kernel/test/error_logger_warn_SUITE.erl +++ b/lib/kernel/test/error_logger_warn_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. +%% Copyright Ericsson AB 2003-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. diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index ff93f25e25..23913ac56a 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl index 9dde00652c..a0ae792ba9 100644 --- a/lib/kernel/test/gen_sctp_SUITE.erl +++ b/lib/kernel/test/gen_sctp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2016. All Rights Reserved. +%% Copyright Ericsson AB 2007-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. diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl index 0fe44e8a88..1be016444f 100644 --- a/lib/kernel/test/gen_tcp_api_SUITE.erl +++ b/lib/kernel/test/gen_tcp_api_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2017. All Rights Reserved. +%% Copyright Ericsson AB 1998-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. diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index e47023d201..950f5bea6f 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2016. All Rights Reserved. +%% Copyright Ericsson AB 1998-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. diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index 6a50239c2a..b39399b18a 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2017. All Rights Reserved. +%% Copyright Ericsson AB 1998-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. diff --git a/lib/kernel/test/heart_SUITE.erl b/lib/kernel/test/heart_SUITE.erl index e95635b800..f5ca6d0e1d 100644 --- a/lib/kernel/test/heart_SUITE.erl +++ b/lib/kernel/test/heart_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. diff --git a/lib/kernel/test/inet_res_SUITE.erl b/lib/kernel/test/inet_res_SUITE.erl index 2a5b8d0044..df6e48abae 100644 --- a/lib/kernel/test/inet_res_SUITE.erl +++ b/lib/kernel/test/inet_res_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2016. All Rights Reserved. +%% Copyright Ericsson AB 2009-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. diff --git a/lib/kernel/test/kernel_SUITE.erl b/lib/kernel/test/kernel_SUITE.erl index 7898988dbe..3e5ed855b5 100644 --- a/lib/kernel/test/kernel_SUITE.erl +++ b/lib/kernel/test/kernel_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-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. diff --git a/lib/kernel/test/kernel_config_SUITE.erl b/lib/kernel/test/kernel_config_SUITE.erl index a21020ff97..9207025a2c 100644 --- a/lib/kernel/test/kernel_config_SUITE.erl +++ b/lib/kernel/test/kernel_config_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl index abbc301360..710b9b115c 100644 --- a/lib/kernel/test/os_SUITE.erl +++ b/lib/kernel/test/os_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2017. All Rights Reserved. +%% Copyright Ericsson AB 1997-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. diff --git a/lib/kernel/test/pdict_SUITE.erl b/lib/kernel/test/pdict_SUITE.erl index a891451c82..3685e51c10 100644 --- a/lib/kernel/test/pdict_SUITE.erl +++ b/lib/kernel/test/pdict_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-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. diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl index 5bb230d1c4..a02b5f87d1 100644 --- a/lib/kernel/test/prim_file_SUITE.erl +++ b/lib/kernel/test/prim_file_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2017. All Rights Reserved. +%% Copyright Ericsson AB 2000-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. diff --git a/lib/kernel/test/seq_trace_SUITE.erl b/lib/kernel/test/seq_trace_SUITE.erl index aae8a83304..ceb4e9cc49 100644 --- a/lib/kernel/test/seq_trace_SUITE.erl +++ b/lib/kernel/test/seq_trace_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2016. All Rights Reserved. +%% Copyright Ericsson AB 1998-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. diff --git a/lib/kernel/test/wrap_log_reader_SUITE.erl b/lib/kernel/test/wrap_log_reader_SUITE.erl index b1ee29a11f..59b088ca73 100644 --- a/lib/kernel/test/wrap_log_reader_SUITE.erl +++ b/lib/kernel/test/wrap_log_reader_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2016. All Rights Reserved. +%% Copyright Ericsson AB 1998-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. diff --git a/lib/kernel/test/wrap_log_reader_SUITE_data/wrap_log_test.erl b/lib/kernel/test/wrap_log_reader_SUITE_data/wrap_log_test.erl index 2b24ccc66f..d2bac40192 100644 --- a/lib/kernel/test/wrap_log_reader_SUITE_data/wrap_log_test.erl +++ b/lib/kernel/test/wrap_log_reader_SUITE_data/wrap_log_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2016. All Rights Reserved. +%% Copyright Ericsson AB 1998-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. diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl index f203ef878f..52ae1b3ae6 100644 --- a/lib/kernel/test/zlib_SUITE.erl +++ b/lib/kernel/test/zlib_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-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. -- cgit v1.2.3 From bb19e24a485e9ef43bcca3fbe9757da83f70c3a0 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Tue, 19 Jun 2018 08:24:54 +0200 Subject: Prepare release --- lib/kernel/doc/src/notes.xml | 283 +++++++++++++++++++++++++++++++++++++++++++ lib/kernel/vsn.mk | 2 +- 2 files changed, 284 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 3d433e7efa..e1ef8ab387 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,289 @@

    This document describes the changes made to the Kernel application.

    +
    Kernel 6.0 + +
    Fixed Bugs and Malfunctions + + +

    Clarify the documentation of rpc:multicall/5. +

    +

    + Own Id: OTP-10551

    +
    + +

    + The DNS resolver when getting econnrefused from a server + retained an invalid socket so look up towards the next + server(s) also failed.

    +

    + Own Id: OTP-13133 Aux Id: PR-1557

    +
    + +

    + No resolver backend returns V4Mapped IPv6 addresses any + more. This was inconsistent before, some did, some did + not. To facilitate working with such addresses a new + function inet:ipv4_mapped_ipv6_address/1 has been + added.

    +

    + *** POTENTIAL INCOMPATIBILITY ***

    +

    + Own Id: OTP-13761 Aux Id: ERL-503

    +
    + +

    + The type specifications for file:posix/0 and + inet:posix/0 have been updated according to which + errors file and socket operations should be able to + return.

    +

    + Own Id: OTP-14019 Aux Id: ERL-550

    +
    + +

    + Fix name resolving in IPv6 only environments when doing + the initial distributed connection.

    +

    + Own Id: OTP-14501

    +
    + +

    File operations used to accept filenames + containing null characters (integer value zero). This + caused the name to be truncated and in some cases + arguments to primitive operations to be mixed up. + Filenames containing null characters inside the filename + are now rejected and will cause primitive file + operations to fail.

    Also environment variable + operations used to accept names and + values of + environment variables containing null characters (integer + value zero). This caused operations to silently produce + erroneous results. Environment variable names and values + containing null characters inside the name or value are + now rejected and will cause environment variable + operations to fail.

    Primitive environment + variable operations also used to accept the $= + character in environment variable names causing various + problems. $= characters in environment variable + names are now also rejected.

    Also + os:cmd/1 now + reject null characters inside its command. +

    erlang:open_port/2 + will also reject null characters inside the port name + from now on.

    +

    + *** POTENTIAL INCOMPATIBILITY ***

    +

    + Own Id: OTP-14543 Aux Id: ERL-370

    +
    + +

    os:putenv and os:getenv no longer access + the process environment directly and instead work on a + thread-safe emulation. The only observable difference is + that it's not kept in sync with libc + getenv(3) / putenv(3), so those who relied + on that behavior in drivers or NIFs will need to add + manual synchronization.

    On Windows this means that + you can no longer resolve DLL dependencies by modifying + the PATH just before loading the driver/NIF. To + make this less of a problem, the emulator now adds the + target DLL's folder to the DLL search path.

    +

    + *** POTENTIAL INCOMPATIBILITY ***

    +

    + Own Id: OTP-14666

    +
    + +

    + Fixed connection tick toward primitive hidden nodes + (erl_interface) that could cause faulty tick timeout in + rare cases when payload data is sent to hidden node but + not received.

    +

    + Own Id: OTP-14681

    +
    + +

    + Make group react immediately on an EXIT-signal from shell + in e.g ssh.

    +

    + Own Id: OTP-14991 Aux Id: PR1705

    +
    + +

    + Calls to gen_tcp:send/2 on closed sockets now + returns {error, closed} instead of + {error,enotconn}.

    +

    + Own Id: OTP-15001

    +
    + +

    + The included_applications key are no longer + duplicated as application environment variable. Earlier, + the included applications could be read both with + application:get[_all]_env(...) and + application:get[_all]_key(...) functions. Now, it + can only be read with + application:get[_all]_key(...).

    +

    + *** POTENTIAL INCOMPATIBILITY ***

    +

    + Own Id: OTP-15071

    +
    + +

    Owner and group changes through + file:write_file_info, file:change_owner, + and file:change_group will no longer report + success on permission errors.

    +

    + *** POTENTIAL INCOMPATIBILITY ***

    +

    + Own Id: OTP-15118

    +
    +
    +
    + + +
    Improvements and New Features + + +

    A new logging API is added to Erlang/OTP, see the + logger(3) manual + page, and section Logging in the + Kernel User's Guide.

    +

    Calls to error_logger are automatically + redirected to the new API, and legacy error logger event + handlers can still be used. It is, however, recommended + to use the Logger API directly when writing new code.

    +

    Notice the following potential incompatibilities:

    +

    Kernel configuration parameters + error_logger still works, but is overruled if the + default handler's output destination is configured with + Kernel configuration parameter logger.

    In + general, parameters for configuring error logger are + overwritten by new parameters for configuring + Logger.

    The concept of SASL error + logging is deprecated, meaning that by default the SASL + application does not affect which log events are + logged.

    By default, supervisor reports and crash + reports are logged by the default Logger handler started + by Kernel, and end up at the same destination (terminal + or file) as other standard log event from Erlang/OTP.

    +

    Progress reports are not logged by default, but can be + enabled by setting the primary log level to info, for + example with the Kernel configuration parameter + logger_level.

    To obtain backwards + compatibility with the SASL error logging functionality + from earlier releases, set Kernel configuration parameter + logger_sasl_compatible to true. This + prevents the default Logger handler from logging any + supervisor-, crash-, or progress reports. Instead, SASL + adds a separate Logger handler during application start, + which takes care of these log events. The SASL + configuration parameters sasl_error_logger and + sasl_errlog_type specify the destination (terminal + or file) and severity level to log for these + events.

    +

    + Since Logger is new in Erlang/OTP 21.0, we do reserve the + right to introduce changes to the Logger API and + functionality in patches following this release. These + changes might or might not be backwards compatible with + the initial version.

    +

    + *** POTENTIAL INCOMPATIBILITY ***

    +

    + Own Id: OTP-13295

    +
    + +

    + The function inet:i/0 has been documented.

    +

    + Own Id: OTP-13713 Aux Id: PR-1645

    +
    + +

    + Typespecs for netns and bind_to_device + options have been added to gen_tcp, gen_udp + and gen_sctp functions.

    +

    + Own Id: OTP-14359 Aux Id: PR-1816

    +
    + +

    + New functionality for implementation of alternative + carriers for the Erlang distribution has been introduced. + This mainly consists of support for usage of distribution + controller processes (previously only ports could be used + as distribution controllers). For more information see + ERTS + User's Guide ➜ How to implement an Alternative Carrier + for the Erlang Distribution ➜ Distribution + Module.

    +

    + Own Id: OTP-14459

    +
    + +

    seq_trace labels may now be any erlang + term.

    +

    + Own Id: OTP-14899

    +
    + +

    + The SSL distribution protocol -proto inet_tls has + stopped setting the SSL option + server_name_indication. New verify funs for client + and server in inet_tls_dist has been added, not + documented yet, that checks node name if present in peer + certificate. Usage is still also yet to be documented.

    +

    + Own Id: OTP-14969 Aux Id: OTP-14465, ERL-598

    +
    + +

    + Changed timeout of gen_server calls to auth + server from default 5 seconds to infinity.

    +

    + Own Id: OTP-15009 Aux Id: ERL-601

    +
    + +

    The callback module passed as -epmd_module to + erl has been expanded to be able to do name and port + resolving.

    Documentation has also been added in + the erl_epmd + reference manual and ERTS User's Guide How to Implement an Alternative + Service Discovery for Erlang Distribution.

    +

    + Own Id: OTP-15086 Aux Id: PR-1694

    +
    + +

    + Included config file specified with relative path in + sys.config are now first searched for relative to the + directory of sys.config itself. If not found, it is also + searched for relative to the current working directory. + The latter is for backwards compatibility.

    +

    + Own Id: OTP-15137 Aux Id: PR-1838

    +
    +
    +
    + +
    +
    Kernel 5.4.3
    Fixed Bugs and Malfunctions diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 60a1b0bff8..aa8e4dc119 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 5.4.3 +KERNEL_VSN = 6.0 -- cgit v1.2.3 From 0cd4f90dc8897f0820c9cdac0ed4db25a54aa1eb Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 18 Jun 2018 14:43:01 +0200 Subject: [logger] Update documentation --- lib/kernel/doc/src/logger_formatter.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger_formatter.xml b/lib/kernel/doc/src/logger_formatter.xml index e777afaf86..a482d02293 100644 --- a/lib/kernel/doc/src/logger_formatter.xml +++ b/lib/kernel/doc/src/logger_formatter.xml @@ -78,7 +78,7 @@ format controls ~p and ~w are replaced with ~P and ~W, respectively, and the value is used as the depth parameter. For details, see - io:format/2,3 + io:format/2,3 in STDLIB.

    Defaults to unlimited.

    -- cgit v1.2.3 From 5cc7a2d51bf0e0c928d94114bbebf85d7d57a9e0 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 25 Jun 2018 17:43:49 +0200 Subject: kernel: Send tick to hidden node even if pending writes as c-nodes need ticks to send ticks. --- lib/kernel/src/dist_util.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index b3507e5d13..86120a5483 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -731,7 +731,7 @@ send_status(#hs_data{socket = Socket, other_node = Node, %% The detection time interval is thus, by default, 45s < DT < 75s -%% A HIDDEN node is always (if not a pending write) ticked if +%% A HIDDEN node is always ticked if %% we haven't read anything as a hidden node only ticks when it receives %% a TICK !! @@ -745,8 +745,8 @@ send_tick(Socket, Tick, Type, MFTick, MFGetstat) -> case MFGetstat(Socket) of {ok, Read, _, _} when Ticked =:= T -> {error, not_responding}; - {ok, Read, W, Pend} when Type =:= hidden -> - send_tick(Socket, Pend, MFTick), + {ok, Read, W, _} when Type =:= hidden -> + MFTick(Socket), {ok, Tick#tick{write = W + 1, tick = T1}}; {ok, Read, Write, Pend} -> -- cgit v1.2.3 From 366fe98c3f685595c5b4e9c6cb19d1034d7b24f6 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 25 Jun 2018 17:13:12 +0200 Subject: kernel: Fix tick count bug when pending writes --- lib/kernel/src/dist_util.erl | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index 86120a5483..d80971f414 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -750,12 +750,12 @@ send_tick(Socket, Tick, Type, MFTick, MFGetstat) -> {ok, Tick#tick{write = W + 1, tick = T1}}; {ok, Read, Write, Pend} -> - send_tick(Socket, Pend, MFTick), - {ok, Tick#tick{write = Write + 1, + Sent = send_tick(Socket, Pend, MFTick), + {ok, Tick#tick{write = Write + Sent, tick = T1}}; {ok, R, Write, Pend} -> - send_tick(Socket, Pend, MFTick), - {ok, Tick#tick{write = Write + 1, + Sent = send_tick(Socket, Pend, MFTick), + {ok, Tick#tick{write = Write + Sent, read = R, tick = T1, ticked = T}}; @@ -772,10 +772,11 @@ send_tick(Socket, Tick, Type, MFTick, MFGetstat) -> end. send_tick(Socket, 0, MFTick) -> - MFTick(Socket); + MFTick(Socket), + 1; send_tick(_, _Pend, _) -> %% Dont send tick if pending write. - ok. + 0. %% ------------------------------------------------------------ %% Connection setup timeout timer. -- cgit v1.2.3 From b059b597cb31be08220cb967c117033f34ca49c4 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Thu, 28 Jun 2018 17:09:22 +0200 Subject: Prepare release --- lib/kernel/doc/src/notes.xml | 20 ++++++++++++++++++++ lib/kernel/vsn.mk | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 09844f1502..f7d2c93666 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,26 @@

    This document describes the changes made to the Kernel application.

    +
    Kernel 5.4.3.1 + +
    Fixed Bugs and Malfunctions + + +

    + Fix some potential buggy behavior in how ticks are sent + on inter node distribution connections. Tick is now sent + to c-node even if there are unsent buffered data, as + c-nodes need ticks in order to send reply ticks. The + amount of sent data was calculated wrongly when ticks + where suppressed due to unsent buffered data.

    +

    + Own Id: OTP-15162 Aux Id: ERIERL-191

    +
    +
    +
    + +
    +
    Kernel 5.4.3
    Fixed Bugs and Malfunctions diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 60a1b0bff8..7f2041ef55 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 5.4.3 +KERNEL_VSN = 5.4.3.1 -- cgit v1.2.3 From c95b25585d77d331b99e68713a1993299166999c Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Mon, 25 Jun 2018 11:56:34 +0200 Subject: kernel: Adjust global testcase --- lib/kernel/test/global_SUITE.erl | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/global_SUITE.erl b/lib/kernel/test/global_SUITE.erl index 0e7b7adc47..8eab36e308 100644 --- a/lib/kernel/test/global_SUITE.erl +++ b/lib/kernel/test/global_SUITE.erl @@ -1383,7 +1383,7 @@ ring(Config) when is_list(Config) -> rpc_cast(Cp8, ?MODULE, single_node, [Time, Cp7, Config]), %% sleep to make the partitioned net ready - ct:sleep(Time - msec()), + sleep(Time - msec()), pong = net_adm:ping(Cp0), pong = net_adm:ping(Cp1), @@ -1466,7 +1466,7 @@ simple_ring(Config) when is_list(Config) -> rpc_cast(Cp5, ?MODULE, single_node, [Time, Cp4, Config]), %% sleep to make the partitioned net ready - ct:sleep(Time - msec()), + sleep(Time - msec()), pong = net_adm:ping(Cp0), pong = net_adm:ping(Cp1), @@ -1542,7 +1542,7 @@ line(Config) when is_list(Config) -> rpc_cast(Cp8, ?MODULE, single_node, [Time, Cp7, Config]), %% Sleep to make the partitioned net ready - ct:sleep(Time - msec()), + sleep(Time - msec()), pong = net_adm:ping(Cp0), pong = net_adm:ping(Cp1), @@ -1626,7 +1626,7 @@ simple_line(Config) when is_list(Config) -> rpc_cast(Cp5, ?MODULE, single_node, [Time, Cp4, Config]), %% sleep to make the partitioned net ready - ct:sleep(Time - msec()), + sleep(Time - msec()), pong = net_adm:ping(Cp0), pong = net_adm:ping(Cp1), @@ -3555,7 +3555,7 @@ single_node(Time, Node, Config) -> lists:foreach(fun(N) -> _ = erlang:disconnect_node(N) end, nodes()), ?UNTIL(get_known(node()) =:= [node()]), spawn(?MODULE, init_2, []), - ct:sleep(Time - msec()), + sleep(Time - msec()), net_adm:ping(Node). init_2() -> @@ -4009,13 +4009,6 @@ collect_nodes(N, Max) -> [Node | collect_nodes(N+1, Max)] end. -only_element(_E, []) -> - true; -only_element(E, [E|R]) -> - only_element(E, R); -only_element(_E, _) -> - false. - exit_p(Pid) -> Ref = erlang:monitor(process, Pid), Pid ! die, @@ -4038,6 +4031,11 @@ wait_for_exit_fast(Pid) -> ok end. +sleep(Time) when Time > 0 -> + ct:sleep(Time); +sleep(_Time) -> + ok. + check_everywhere(Nodes, Name, Config) -> ?UNTIL(begin case rpc:multicall(Nodes, global, whereis_name, [Name]) of @@ -4162,10 +4160,10 @@ rpc_cast(Node, Module, Function, Args, File) -> %% The emulator now ensures that the node has been removed from %% nodes(). -rpc_disconnect_node(Node, DisconnectedNode, _Config) -> - True = rpc:call(Node, erlang, disconnect_node, [DisconnectedNode]), - False = lists:member(DisconnectedNode, rpc:call(Node, erlang, nodes, [])), - {true, false} = {True, False}. +rpc_disconnect_node(Node, DisconnectedNode, Config) -> + true = rpc:call(Node, erlang, disconnect_node, [DisconnectedNode]), + ?UNTIL + (not lists:member(DisconnectedNode, rpc:call(Node, erlang, nodes, []))). %%% %%% Utility -- cgit v1.2.3 From 7713d6e37f5fb3b105834ab96121d728d4bb5065 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Mon, 2 Jul 2018 10:28:02 +0200 Subject: kernel: Adjust disk_log tests --- lib/kernel/test/disk_log_SUITE.erl | 99 ++++++++++++++++++++++++++++---------- 1 file changed, 74 insertions(+), 25 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/disk_log_SUITE.erl b/lib/kernel/test/disk_log_SUITE.erl index 0709a6e766..9704c3b28c 100644 --- a/lib/kernel/test/disk_log_SUITE.erl +++ b/lib/kernel/test/disk_log_SUITE.erl @@ -1750,7 +1750,7 @@ block_queue(Conf) when is_list(Conf) -> true = [{1,a},{2,b},{3,c},{4,d},{5,e},{6,f},{7,g},{8,h}] == Terms, del(File, 2), Q = qlen(), - true = (P0 == pps()), + check_pps(P0), ok. %% OTP-4880. Blocked processes did not get disk_log_stopped message. @@ -1782,7 +1782,7 @@ block_queue2(Conf) when is_list(Conf) -> {ok,<<>>} = file:read_file(File ++ ".1"), del(File, No), Q = qlen(), - true = (P0 == pps()), + check_pps(P0), ok. @@ -2119,7 +2119,7 @@ close_block(Conf) when is_list(Conf) -> 0 = sync_do(Pid2, users), sync_do(Pid2, terminate), {error, no_such_log} = disk_log:info(n), - true = (P0 == pps()), + check_pps(P0), %% Users terminate (no link...). Pid3 = spawn_link(?MODULE, lserv, [n]), @@ -2137,7 +2137,7 @@ close_block(Conf) when is_list(Conf) -> disk_log:close(n), disk_log:close(n), {error, no_such_log} = disk_log:info(n), - true = (P0 == pps()), + check_pps(P0), %% Blocking owner terminates. Pid5 = spawn_link(?MODULE, lserv, [n]), @@ -2154,7 +2154,7 @@ close_block(Conf) when is_list(Conf) -> 1 = users(n), ok = disk_log:close(n), {error, no_such_log} = disk_log:info(n), - true = (P0 == pps()), + check_pps(P0), %% Blocking user terminates. Pid6 = spawn_link(?MODULE, lserv, [n]), @@ -2174,7 +2174,7 @@ close_block(Conf) when is_list(Conf) -> 1 = users(n), ok = disk_log:close(n), {error, no_such_log} = disk_log:info(n), - true = (P0 == pps()), + check_pps(P0), %% Blocking owner terminates. Pid7 = spawn_link(?MODULE, lserv, [n]), @@ -2192,7 +2192,7 @@ close_block(Conf) when is_list(Conf) -> 1 = users(n), ok = disk_log:close(n), {error, no_such_log} = disk_log:info(n), - true = (P0 == pps()), + check_pps(P0), %% Two owners, the blocking one terminates. Pid8 = spawn_link(?MODULE, lserv, [n]), @@ -2207,7 +2207,7 @@ close_block(Conf) when is_list(Conf) -> 0 = sync_do(Pid9, users), sync_do(Pid9, terminate), {error, no_such_log} = disk_log:info(n), - true = (P0 == pps()), + check_pps(P0), %% Blocking user closes. Pid10 = spawn_link(?MODULE, lserv, [n]), @@ -2225,7 +2225,7 @@ close_block(Conf) when is_list(Conf) -> ok = disk_log:close(n), sync_do(Pid10, terminate), {error, no_such_log} = disk_log:info(n), - true = (P0 == pps()), + check_pps(P0), %% Blocking user unblocks and closes. Pid11 = spawn_link(?MODULE, lserv, [n]), @@ -2244,7 +2244,7 @@ close_block(Conf) when is_list(Conf) -> ok = disk_log:close(n), {error, no_such_log} = disk_log:info(n), sync_do(Pid11, terminate), - true = (P0 == pps()), + check_pps(P0), %% Blocking owner closes. Pid12 = spawn_link(?MODULE, lserv, [n]), @@ -2263,7 +2263,7 @@ close_block(Conf) when is_list(Conf) -> ok = disk_log:close(n), {error, no_such_log} = disk_log:info(n), sync_do(Pid12, terminate), - true = (P0 == pps()), + check_pps(P0), %% Blocking owner unblocks and closes. Pid13 = spawn_link(?MODULE, lserv, [n]), @@ -2283,7 +2283,7 @@ close_block(Conf) when is_list(Conf) -> ok = disk_log:close(n), {error, no_such_log} = disk_log:info(n), sync_do(Pid13, terminate), - true = (P0 == pps()), + check_pps(P0), del(File, No), % cleanup ok. @@ -2487,7 +2487,7 @@ error_repair(Conf) when is_list(Conf) -> P0 = pps(), {error, {file_error, _, _}} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {size,{40,4}}]), - true = (P0 == pps()), + check_pps(P0), del(File, No), ok = file:del_dir(Dir), @@ -2506,7 +2506,7 @@ error_repair(Conf) when is_list(Conf) -> disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, internal}, {size, {40,No}}]), ok = disk_log:close(n), - true = (P1 == pps()), + check_pps(P1), del(File, No), receive {info_msg, _, "disk_log: repairing" ++ _, _} -> ok after 1000 -> ct:fail(failed) end, @@ -2524,7 +2524,7 @@ error_repair(Conf) when is_list(Conf) -> disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, internal}, {size, {4000,No}}]), ok = disk_log:close(n), - true = (P2 == pps()), + check_pps(P2), del(File, No), receive {info_msg, _, "disk_log: repairing" ++ _, _} -> ok after 1000 -> ct:fail(failed) end, @@ -2633,7 +2633,7 @@ error_log(Conf) when is_list(Conf) -> {ok, n} = disk_log:open([{name, n}, {file, File}, {type, wrap}, {format, external},{size, {100, No}}]), {error, {file_error, _, _}} = disk_log:truncate(n), - true = (P0 == pps()), + check_pps(P0), del(File, No), %% OTP-4880. @@ -2641,7 +2641,7 @@ error_log(Conf) when is_list(Conf) -> {ok, n} = disk_log:open([{name, n}, {file, File}, {type, halt}, {format, external},{size, 100000}]), {error, {file_error, _, eisdir}} = disk_log:reopen(n, LDir), - true = (P0 == pps()), + check_pps(P0), file:delete(File), B = mk_bytes(60), @@ -3003,7 +3003,7 @@ error_index(Conf) when is_list(Conf) -> {error, {invalid_index_file, _}} = disk_log:open(Args), del(File, No), - true = (P0 == pps()), + check_pps(P0), true = (Q == qlen()), ok. @@ -4436,7 +4436,7 @@ dist_open2(Conf) when is_list(Conf) -> timer:sleep(500), file:delete(File), - true = (P0 == pps()), + check_pps(P0), %% This time the first process has a naughty head_func. This test %% does not add very much. Perhaps it should be removed. However, @@ -4482,7 +4482,7 @@ dist_open2(Conf) when is_list(Conf) -> timer:sleep(100), {error, no_such_log} = disk_log:close(Log), file:delete(File), - true = (P0 == pps()), + check_pps(P0), No = 2, Log2 = n2, @@ -4511,7 +4511,7 @@ dist_open2(Conf) when is_list(Conf) -> file:delete(File2), del(File, No), - true = (P0 == pps()), + check_pps(P0), R. @@ -4556,7 +4556,7 @@ dist_open2_1(Conf, Delay) -> {error, no_such_log} = disk_log:info(Log), file:delete(File), - true = (P0 == pps()), + check_pps(P0), ok. @@ -4613,7 +4613,7 @@ dist_open2_2(Conf, Delay) -> {[{Node1,{repaired,_,_,_}}],[]}} -> ok end, - true = (P0 == pps()), + check_pps(P0), stop_node(Node1), file:delete(File), ok. @@ -4791,10 +4791,59 @@ log(Name, N) -> format_error(E) -> lists:flatten(disk_log:format_error(E)). +check_pps({Ports0,Procs0} = P0) -> + case pps() of + P0 -> + ok; + _ -> + timer:sleep(500), + case pps() of + P0 -> + ok; + {Ports1,Procs1} = P1 -> + case {Ports1 -- Ports0, Procs1 -- Procs0} of + {[], []} -> ok; + {PortsDiff,ProcsDiff} -> + io:format("failure, got ~p~n, expected ~p\n", [P1, P0]), + show("Old port", Ports0 -- Ports1), + show("New port", PortsDiff), + show("Old proc", Procs0 -- Procs1), + show("New proc", ProcsDiff), + ct:fail(failed) + end + end + end. + +show(_S, []) -> + ok; +show(S, [{Pid, Name, InitCall}|Pids]) when is_pid(Pid) -> + io:format("~s: ~w (~w), ~w: ~p~n", + [S, Pid, proc_reg_name(Name), InitCall, + erlang:process_info(Pid)]), + show(S, Pids); +show(S, [{Port, _}|Ports]) when is_port(Port)-> + io:format("~s: ~w: ~p~n", [S, Port, erlang:port_info(Port)]), + show(S, Ports). + pps() -> timer:sleep(100), - {erlang:ports(), lists:filter(fun(P) -> erlang:is_process_alive(P) end, - processes())}. + {port_list(), process_list()}. + +port_list() -> + [{P,safe_second_element(erlang:port_info(P, name))} || + P <- erlang:ports()]. + +process_list() -> + [{P,process_info(P, registered_name), + safe_second_element(process_info(P, initial_call))} || + P <- processes(), erlang:is_process_alive(P)]. + +proc_reg_name({registered_name, Name}) -> Name; +proc_reg_name([]) -> no_reg_name. + +safe_second_element({_,Info}) -> Info; +safe_second_element(Other) -> Other. + qlen() -> {_, {_, N}} = lists:keysearch(message_queue_len, 1, process_info(self())), -- cgit v1.2.3 From 465c31f1b06440c256b91ca17539f09c328f4bd5 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 3 Jul 2018 14:58:25 +0200 Subject: kernel: Silence dialyzer --- lib/kernel/src/dist_util.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index d80971f414..cddb24c34a 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -476,12 +476,12 @@ con_loop({Kernel, Node, Socket, Type, MFTick, MFGetstat, MFSetOpts, MFGetOpts}=C {Kernel, disconnect} -> ?shutdown2(Node, disconnected); {Kernel, aux_tick} -> - case MFGetstat(Socket) of - {ok, _, _, PendWrite} -> - send_tick(Socket, PendWrite, MFTick); - _ -> - ignore_it - end, + _ = case MFGetstat(Socket) of + {ok, _, _, PendWrite} -> + send_tick(Socket, PendWrite, MFTick); + _ -> + ignore_it + end, con_loop(ConData, Tick); {Kernel, tick} -> case send_tick(Socket, Tick, Type, -- cgit v1.2.3 From c4759bc60c8e5db27c070b9e826ddc0f9626b094 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 10 Jul 2018 17:51:23 +0200 Subject: kernel: Fix net_kernel:connect_node/1 to local node to be no-op and return true as it always has before OTP-21.0. --- lib/kernel/doc/src/net_kernel.xml | 6 ++++-- lib/kernel/src/net_kernel.erl | 2 +- lib/kernel/test/erl_distribution_SUITE.erl | 8 ++++++++ 3 files changed, 13 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml index a30d28d55a..d3bd7365e2 100644 --- a/lib/kernel/doc/src/net_kernel.xml +++ b/lib/kernel/doc/src/net_kernel.xml @@ -102,8 +102,10 @@ $ erl -sname foobar Establish a connection to a node.

    Establishes a connection to Node. Returns - true if successful, false if not, and ignored - if the local node is not alive.

    + true if a connection was established or was already + established or if Node is the local node + itself. Returns false if the connection attempt failed, and + ignored if the local node is not alive.

    diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index c4e1a0ce1e..bea08242d8 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -472,7 +472,7 @@ handle_call({passive_cnct, Node}, From, State) -> %% Explicit connect %% The response is delayed until the connection is up and running. %% -handle_call({connect, _, Node, _, _}, From, State) when Node =:= node() -> +handle_call({connect, _, Node}, From, State) when Node =:= node() -> async_reply({reply, true, State}, From); handle_call({connect, Type, Node}, From, State) -> verbose({connect, Type, Node}, 1, State), diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl index 9c6712ad74..0e13f0383e 100644 --- a/lib/kernel/test/erl_distribution_SUITE.erl +++ b/lib/kernel/test/erl_distribution_SUITE.erl @@ -25,6 +25,7 @@ init_per_group/2,end_per_group/2]). -export([tick/1, tick_change/1, + connect_node/1, nodenames/1, hostnames/1, illegal_nodenames/1, hidden_node/1, setopts/1, @@ -70,6 +71,7 @@ suite() -> all() -> [tick, tick_change, nodenames, hostnames, illegal_nodenames, + connect_node, hidden_node, setopts, table_waste, net_setuptime, inet_dist_options_options, {group, monitor_nodes}]. @@ -106,6 +108,12 @@ init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> end_per_testcase(_Func, _Config) -> ok. +connect_node(Config) when is_list(Config) -> + Connected = nodes(connected), + true = net_kernel:connect_node(node()), + Connected = nodes(connected), + ok. + tick(Config) when is_list(Config) -> PaDir = filename:dirname(code:which(erl_distribution_SUITE)), -- cgit v1.2.3 From 308e7677170d56c4270d82af1a37375038da5d72 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 18 Jun 2018 15:26:23 +0200 Subject: [logger] Generate .png file from .dia --- lib/kernel/doc/src/.gitignore | 1 + lib/kernel/doc/src/Makefile | 14 +++++++++++++- lib/kernel/doc/src/logger_arch.dia | Bin 0 -> 2527 bytes lib/kernel/doc/src/logger_arch.png | Bin 32187 -> 117205 bytes lib/kernel/doc/src/logger_chapter.xml | 2 ++ 5 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 lib/kernel/doc/src/.gitignore create mode 100644 lib/kernel/doc/src/logger_arch.dia (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/.gitignore b/lib/kernel/doc/src/.gitignore new file mode 100644 index 0000000000..c2813ac866 --- /dev/null +++ b/lib/kernel/doc/src/.gitignore @@ -0,0 +1 @@ +*.eps \ No newline at end of file diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile index f34eee71ba..fceba14515 100644 --- a/lib/kernel/doc/src/Makefile +++ b/lib/kernel/doc/src/Makefile @@ -84,6 +84,7 @@ XML_CHAPTER_FILES = \ BOOK_FILES = book.xml +# The .png file is generated from a .dia file with target 'update_png' IMAGE_FILES = \ logger_arch.png @@ -111,6 +112,17 @@ SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml) TOP_SPECS_FILE = specs.xml +# ---------------------------------------------------- +# FIGURES +# ---------------------------------------------------- +# In order to update the figures you have to have both dia +# and imagemagick installed. +# The generated .png file must be committed. + +update_png: + dia --export=logger_arch.eps logger_arch.dia + convert logger_arch.eps -resize 65% logger_arch.png + # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- @@ -147,7 +159,7 @@ clean clean_docs: rm -f $(MAN6DIR)/* rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) rm -f $(SPECDIR)/* - rm -f errs core *~ + rm -f errs core *~ *.eps $(SPECDIR)/specs_erl_prim_loader_stub.xml: $(gen_verbose)escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ diff --git a/lib/kernel/doc/src/logger_arch.dia b/lib/kernel/doc/src/logger_arch.dia new file mode 100644 index 0000000000..97be31856e Binary files /dev/null and b/lib/kernel/doc/src/logger_arch.dia differ diff --git a/lib/kernel/doc/src/logger_arch.png b/lib/kernel/doc/src/logger_arch.png index a3a863c511..70933a5a41 100644 Binary files a/lib/kernel/doc/src/logger_arch.png and b/lib/kernel/doc/src/logger_arch.png differ diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index f4a752bde9..23a289f2ee 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -69,6 +69,8 @@ figure shows two log handlers, but any number of handlers can be installed.

    + Conceptual Overview -- cgit v1.2.3 From 8fed5e01980996cd34be6e27370eed74b5c0ed5d Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 18 Jun 2018 17:37:27 +0200 Subject: [logger] Improve spec for set_handler_config/3 and set_primary_config/2 Specify which keys and associated values are allowed. --- lib/kernel/doc/src/logger.xml | 115 +++++++++++++++++++++++++----------------- lib/kernel/src/logger.erl | 31 +++++++++--- 2 files changed, 95 insertions(+), 51 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index 33f1919de0..b1321a93dc 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -97,46 +97,6 @@ logger:error("error happened because: ~p", [Reason]). % Without macro - - - -

    Primary configuration data for Logger. The following - default values apply:

    - - level => info - filter_default => log - filters => [] - -
    -
    - - - -

    Handler configuration data for Logger. The following - default values apply:

    - - level => all - filter_default => log - filters => [] - formatter => {logger_formatter, DefaultFormatterConfig} - -

    In addition to these, the following fields are - automatically inserted by Logger, values taken from the - two first parameters - to add_handler/3:

    - - id => HandlerId - module => Module - -

    Handler specific configuration data is inserted by the - handler callback itself, in a sub structure associated with - the field named config.

    -

    See the - logger_formatter(3) manual page for - information about the default configuration for this - formatter.

    -
    -
    @@ -171,6 +131,39 @@ logger:error("error happened because: ~p", [Reason]). % Without macro for an example of a formatter implementation.

    + + + +

    Handler configuration data for Logger. The following + default values apply:

    + + level => all + filter_default => log + filters => [] + formatter => {logger_formatter, DefaultFormatterConfig} + +

    In addition to these, the following fields are + automatically inserted by Logger, values taken from the + two first parameters + to add_handler/3:

    + + id => HandlerId + module => Module + +

    These are read-only and cannot be changed in runtime.

    +

    Handler specific configuration data is inserted by the + handler callback itself, in a sub structure associated with + the field named config. See + the logger_std_h(3) + and logger_disk_log_h + manual pages for information about the specifc configuration + for these handlers.

    +

    See the + logger_formatter(3) manual page for + information about the default configuration for this + formatter.

    +
    +
    @@ -248,6 +241,18 @@ logger:error("error happened because: ~p", [Reason]). % Without macro

    + + + +

    Primary configuration data for Logger. The following + default values apply:

    + + level => info + filter_default => log + filters => [] + +
    +
    @@ -690,15 +695,30 @@ start(_, []) -> - + + + + + Add or update configuration data for the specified handler. + + + + + + +

    Add or update configuration data for the specified handler. If the given Key already exists, its associated value will be changed - to Value. If it does not exist, it will + to the given value. If it does not exist, it will be added.

    +

    See the definition of + the + handler_config() type for more + information about the different parameters.

    @@ -721,13 +741,18 @@ start(_, []) -> - + + + Add or update primary configuration data for Logger. + + +

    Add or update primary configuration data for Logger. If the given Key already exists, its associated - value will be changed to Value. If it - does not exist, it will be added.

    + value will be changed to the given value. If it does not + exist, it will be added.

    diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 0a9b1672ec..c78f5372f0 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -352,9 +352,12 @@ add_handler(HandlerId,Module,Config) -> remove_handler(HandlerId) -> logger_server:remove_handler(HandlerId). --spec set_primary_config(Key,Value) -> ok | {error,term()} when - Key :: atom(), - Value :: term(). +-spec set_primary_config(level,Level) -> ok | {error,term()} when + Level :: level() | all | none; + (filter_default,FilterDefault) -> ok | {error,term()} when + FilterDefault :: log | stop; + (filters,Filters) -> ok | {error,term()} when + Filters :: [{filter_id(),filter()}]. set_primary_config(Key,Value) -> logger_server:set_config(primary,Key,Value). @@ -363,10 +366,26 @@ set_primary_config(Key,Value) -> set_primary_config(Config) -> logger_server:set_config(primary,Config). --spec set_handler_config(HandlerId,Key,Value) -> ok | {error,term()} when +-spec set_handler_config(HandlerId,level,Level) -> Return when HandlerId :: handler_id(), - Key :: atom(), - Value :: term(). + Level :: level() | all | none, + Return :: ok | {error,term()}; + (HandlerId,filter_default,FilterDefault) -> Return when + HandlerId :: handler_id(), + FilterDefault :: log | stop, + Return :: ok | {error,term()}; + (HandlerId,filters,Filters) -> Return when + HandlerId :: handler_id(), + Filters :: [{filter_id(),filter()}], + Return :: ok | {error,term()}; + (HandlerId,formatter,Formatter) -> Return when + HandlerId :: handler_id(), + Formatter :: {module(), formatter_config()}, + Return :: ok | {error,term()}; + (HandlerId,config,Config) -> Return when + HandlerId :: handler_id(), + Config :: term(), + Return :: ok | {error,term()}. set_handler_config(HandlerId,Key,Value) -> logger_server:set_config(HandlerId,Key,Value). -- cgit v1.2.3 From da166b76de977171243dd2ea7f86b98f451fabc5 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 18 Jun 2018 17:53:00 +0200 Subject: [logger] Add ?LOG macro which takes Level as argument --- lib/kernel/doc/src/logger.xml | 9 ++++++--- lib/kernel/include/logger.hrl | 4 ++++ lib/kernel/test/logger_SUITE.erl | 29 +++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index b1321a93dc..8bf62a6b39 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -290,13 +290,16 @@ logger:error("error happened because: ~p", [Reason]). % Without macro ?LOG_INFO(FunOrFormat,Args[,Metadata]) ?LOG_DEBUG(StringOrReport[,Metadata]) ?LOG_DEBUG(FunOrFormat,Args[,Metadata]) + ?LOG(Level,StringOrReport[,Metadata]) + ?LOG(Level,FunOrFormat,Args[,Metadata])

    All macros expand to a call to Logger, where Level is - taken from the macro name, and location data is added to the - metadata. See the description of + taken from the macro name, or from the first argument in the + case of the ?LOG macro. Location data is added to the + metadata as described under the metadata() - type for more information about the location data.

    + type definition.

    The call is wrapped in a case statement and will be evaluated only if Level is equal to or below the configured log diff --git a/lib/kernel/include/logger.hrl b/lib/kernel/include/logger.hrl index 2143ccd297..b09977e0f2 100644 --- a/lib/kernel/include/logger.hrl +++ b/lib/kernel/include/logger.hrl @@ -32,6 +32,10 @@ -define(LOG_DEBUG(A,B),?DO_LOG(debug,[A,B])). -define(LOG_DEBUG(A,B,C),?DO_LOG(debug,[A,B,C])). +-define(LOG(L,A),?DO_LOG(L,[A])). +-define(LOG(L,A,B),?DO_LOG(L,[A,B])). +-define(LOG(L,A,B,C),?DO_LOG(L,[A,B,C])). + -define(LOCATION,#{mfa=>{?MODULE,?FUNCTION_NAME,?FUNCTION_ARITY}, line=>?LINE, file=>?FILE}). diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index b367b4dd54..bb9a6acdcc 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -355,6 +355,7 @@ log_all_levels_api(cleanup,_Config) -> macros(_Config) -> ok = logger:add_handler(h1,?MODULE,#{level=>all,filter_default=>log}), test_macros(emergency), + test_log_macro(alert), ok. macros(cleanup,_Config) -> @@ -1009,6 +1010,34 @@ test_macros(emergency=Level) -> [{F2,x},{error,fun_that_crashes}],#{}), ok. +test_log_macro(Level) -> + ?LOG(Level,#{Level=>rep}), + ok = check_logged(Level,#{Level=>rep},?MY_LOC(1)), + ?LOG(Level,#{Level=>rep},#{my=>meta}), + ok = check_logged(Level,#{Level=>rep},(?MY_LOC(1))#{my=>meta}), + ?LOG(Level,"~w: ~w",[Level,fa]), + ok = check_logged(Level,"~w: ~w",[Level,fa],?MY_LOC(1)), + ?LOG(Level,"~w: ~w ~w",[Level,fa,meta],#{my=>meta}), + ok = check_logged(Level,"~w: ~w ~w",[Level,fa,meta],(?MY_LOC(1))#{my=>meta}), + ?LOG(Level,fun(x) -> {"~w: ~w ~w",[Level,fun_to_fa,meta]} end, + x, #{my=>meta}), + ok = check_logged(Level,"~w: ~w ~w",[Level,fun_to_fa,meta], + (?MY_LOC(3))#{my=>meta}), + ?LOG(Level,fun(x) -> #{Level=>fun_to_r,meta=>true} end, x, #{my=>meta}), + ok = check_logged(Level,#{Level=>fun_to_r,meta=>true}, + (?MY_LOC(2))#{my=>meta}), + ?LOG(Level,fun(x) -> <<"fun_to_s">> end,x,#{}), + ok = check_logged(Level,<<"fun_to_s">>,?MY_LOC(1)), + F1=fun(x) -> {fun_to_bad} end, + ?LOG(Level,F1,x,#{}), + ok = check_logged(Level,"LAZY_FUN ERROR: ~tp; Returned: ~tp", + [{F1,x},{fun_to_bad}],#{}), + F2=fun(x) -> erlang:error(fun_that_crashes) end, + ?LOG(Level,F2,x,#{}), + ok = check_logged(Level,"LAZY_FUN CRASH: ~tp; Reason: ~tp", + [{F2,x},{error,fun_that_crashes}],#{}), + ok. + %%%----------------------------------------------------------------- %%% Called by macro ?TRY(X) my_try(Fun) -> -- cgit v1.2.3 From ce8832d3113a78ed60fcbe186d9c177bcebff2ab Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Mon, 18 Jun 2018 13:46:47 +0200 Subject: [logger] Fix problem with test cases waiting for handler restart --- lib/kernel/test/logger_disk_log_h_SUITE.erl | 29 ++++++++++++++++----------- lib/kernel/test/logger_std_h_SUITE.erl | 31 ++++++++++++++++++----------- 2 files changed, 36 insertions(+), 24 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 7e5b574869..a4b15c841b 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -1551,25 +1551,30 @@ h_proc_name() -> h_proc_name(Name) -> list_to_atom(lists:concat([logger_disk_log_h,"_",Name])). -file_delete(Log) -> - file:delete(Log). - wait_for_process_up(T) -> - wait_for_process_up(h_proc_name(),T). + wait_for_process_up(?MODULE, h_proc_name(), T). -wait_for_process_up(Name,T) -> +wait_for_process_up(Name, RegName, T) -> N = (T div 500) + 1, - wait_for_process_up1(Name,N). + wait_for_process_up1(Name, RegName, N). -wait_for_process_up1(Name,0) -> +wait_for_process_up1(_Name, _RegName, 0) -> error; -wait_for_process_up1(Name,N) -> +wait_for_process_up1(Name, RegName, N) -> timer:sleep(500), - case whereis(Name) of + case whereis(RegName) of Pid when is_pid(Pid) -> - %% ct:pal("Process ~p up (~p tries left)",[Name,N]), - {ok,Pid}; + case logger:get_handler_config(Name) of + {ok,_} -> + %% ct:pal("Process ~p up (~p tries left)",[Name,N]), + {ok,Pid}; + _ -> + wait_for_process_up1(Name, RegName, N-1) + end; undefined -> %% ct:pal("Waiting for process ~p (~p tries left)",[Name,N]), - wait_for_process_up1(Name,N-1) + wait_for_process_up1(Name, RegName, N-1) end. + +file_delete(Log) -> + file:delete(Log). diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index ca54458ac1..0930cd4211 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -1570,27 +1570,29 @@ h_proc_name() -> h_proc_name(Name) -> ?name_to_reg_name(logger_std_h,Name). -file_delete(Log) -> - file:delete(Log). - wait_for_process_up(T) -> - wait_for_process_up(h_proc_name(),T). + wait_for_process_up(?MODULE, h_proc_name(), T). -wait_for_process_up(Name,T) -> +wait_for_process_up(Name, RegName, T) -> N = (T div 500) + 1, - wait_for_process_up1(Name,N). + wait_for_process_up1(Name, RegName, N). -wait_for_process_up1(_Name,0) -> +wait_for_process_up1(_Name, _RegName, 0) -> error; -wait_for_process_up1(Name,N) -> +wait_for_process_up1(Name, RegName, N) -> timer:sleep(500), - case whereis(Name) of + case whereis(RegName) of Pid when is_pid(Pid) -> - %% ct:pal("Process ~p up (~p tries left)",[Name,N]), - {ok,Pid}; + case logger:get_handler_config(Name) of + {ok,_} -> + %% ct:pal("Process ~p up (~p tries left)",[Name,N]), + {ok,Pid}; + _ -> + wait_for_process_up1(Name, RegName, N-1) + end; undefined -> %% ct:pal("Waiting for process ~p (~p tries left)",[Name,N]), - wait_for_process_up1(Name,N-1) + wait_for_process_up1(Name, RegName, N-1) end. filesync_rep_int() -> @@ -1598,3 +1600,8 @@ filesync_rep_int() -> true -> 5500; false -> ?FILESYNC_REPEAT_INTERVAL + 500 end. + + +file_delete(Log) -> + file:delete(Log). + -- cgit v1.2.3 From b98f8afa52a479a6730ba4da175dbd1789029722 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 26 Jun 2018 10:27:52 +0200 Subject: [logger] Remove compiler warnings in test --- lib/kernel/test/logger_SUITE.erl | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index bb9a6acdcc..8f74ebdc47 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -470,7 +470,11 @@ filter_failed(_Config) -> %% Logger filters {error,{invalid_filter,_}} = logger:add_primary_filter(lf,{fun(_) -> ok end,args}), - ok = logger:add_primary_filter(lf,{fun(_,_) -> a=b end,args}), + ok = logger:add_primary_filter(lf, + {fun(_,_) -> + erlang:error({badmatch,b}) + end, + args}), #{filters:=[_]} = logger:get_primary_config(), ok = logger:notice(M1=?map_rep), ok = check_logged(notice,M1,#{}), @@ -488,7 +492,11 @@ filter_failed(_Config) -> {error,{not_found,h0}} = logger:remove_handler_filter(h0,hf), {error,{invalid_filter,_}} = logger:add_handler_filter(h1,hf,{fun(_) -> ok end,args}), - ok = logger:add_handler_filter(h1,hf,{fun(_,_) -> a=b end,args}), + ok = logger:add_handler_filter(h1,hf, + {fun(_,_) -> + erlang:error({badmatch,b}) + end, + args}), {ok,#{filters:=[_]}} = logger:get_handler_config(h1), ok = logger:notice(M3=?map_rep), ok = check_logged(notice,M3,#{}), @@ -524,7 +532,11 @@ handler_failed(_Config) -> false = lists:search(fun(#{id:=h1}) -> true; (_) -> false end,H1), {error,{not_found,h1}} = logger:remove_handler(h1), - ok = logger:add_handler(h2,?MODULE,#{filter_default=>log,log_call=>fun() -> a = b end}), + ok = logger:add_handler(h2,?MODULE, + #{filter_default => log, + log_call => fun() -> + erlang:error({badmatch,b}) + end}), {error,{already_exist,h2}} = logger:add_handler(h2,othermodule,#{}), [add] = test_server:messages_get(), @@ -535,7 +547,7 @@ handler_failed(_Config) -> {error,{not_found,h2}} = logger:remove_handler(h2), CallAddHandler = fun() -> logger:add_handler(h2,?MODULE,#{}) end, - CrashHandler = fun() -> a = b end, + CrashHandler = fun() -> erlang:error({badmatch,b}) end, KillHandler = fun() -> exit(self(), die) end, {error,{handler_not_added,{attempting_syncronous_call_to_self,_}}} = -- cgit v1.2.3 From 270d909696a753af022df72a404c73f2895b4a02 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 28 Jun 2018 14:52:11 +0200 Subject: [logger] Allow report callback with two arguments returning a string If the report callback is a fun of arity 2, then the second argument is a map with the keys 'encoding', 'depth' and 'chars_limit', and the fun must return a string which is size limited according to the given depth and chars_limit. If the report callback is a fun of arity 1, then it must return a tuple containing a format string and a list of arguments. The formatter will produce the string, and limit it's size. --- lib/kernel/doc/src/logger.xml | 21 +++- lib/kernel/doc/src/logger_chapter.xml | 18 +++- lib/kernel/doc/src/logger_formatter.xml | 13 ++- lib/kernel/src/logger.erl | 27 +++++- lib/kernel/src/logger_formatter.erl | 151 +++++++++++++++++++---------- lib/kernel/test/logger_formatter_SUITE.erl | 44 ++++++--- 6 files changed, 200 insertions(+), 74 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index 8bf62a6b39..f1830a8224 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -227,7 +227,10 @@ logger:error("error happened because: ~p", [Reason]). % Without macro a report(), the report_cb key can be associated with a fun (report callback) that converts the report to a format - string and arguments. See + string and arguments, or directly to a string. See the + type definition + of report_cb(), + and section Log Message in the User's Guide for more information about report callbacks.

    @@ -259,6 +262,22 @@ logger:error("error happened because: ~p", [Reason]). % Without macro

    + + + +

    A fun which converts a report() + to a format string and arguments, or directly to a string. + See section Log + Message in the User's Guide for more + information.

    +
    +
    + + + +

    +
    +
    diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 23a289f2ee..dc68d5c761 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -194,10 +194,26 @@ the log event's metadata. The report callback is a convenience function that the formatter can use - to convert the report to a format string and arguments. The + to convert the report to a format string and arguments, or + directly to a string. The formatter can also use its own conversion function, if no callback is provided, or if a customized formatting is desired.

    +

    The report callback must be a fun with one or two + arguments. If it takes one argument, this is the report + itself, and the fun returns a format string and arguments:

    +
    fun((logger:report()) -> {io:format(),[term()]})
    +

    If it takes two arguments, the first is the report, and the + second is a map containing extra data that allows direct + coversion to a string:

    +
    fun((logger:report(),logger:report_cb_config()) -> unicode:chardata())
    +      
    +

    The fun must obey the encoding, depth + and chars_limit parameters provided in the second + argument, as the formatter can not do anything useful of these + parameters with the returned string. This variant is used when + the formatting of the report depends on the size and encoding + parameters.

    Example, format string and arguments:

    logger:error("The file does not exist: ~ts",[Filename])

    Example, string:

    diff --git a/lib/kernel/doc/src/logger_formatter.xml b/lib/kernel/doc/src/logger_formatter.xml index a482d02293..9226d19834 100644 --- a/lib/kernel/doc/src/logger_formatter.xml +++ b/lib/kernel/doc/src/logger_formatter.xml @@ -82,6 +82,13 @@ in STDLIB.

    Defaults to unlimited.

    + encoding = + unicode:encoding() + +

    This parameter must reflect the encoding of the device + that the handler prints to.

    +

    Defaults to utf8

    +
    legacy_header = boolean()

    If set to true a header field is added to @@ -105,7 +112,8 @@ by chars_limit or depth, it is truncated.

    Defaults to unlimited.

    - report_cb = fun((logger:report()) -> {io:format(), [term()]}) + report_cb = + logger:report_cb()

    A report callback is used by the formatter to transform log messages on report form to a format string and @@ -119,9 +127,6 @@ both the default report callback, and any report callback found in metadata. That is, all reports are converted by this configured function.

    -

    The value must be a function with arity 1, - returning {Format,Args}, and it will be called - with a report as only argument.

    single_line = boolean() diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index c78f5372f0..4924c6cb4f 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -74,6 +74,11 @@ -type level() :: emergency | alert | critical | error | warning | notice | info | debug. -type report() :: map() | [{atom(),term()}]. +-type report_cb() :: fun((report()) -> {io:format(),[term()]}) | + fun((report(),report_cb_config()) -> unicode:chardata()). +-type report_cb_config() :: #{encoding := unicode:encoding(), + depth := pos_integer() | unlimited, + chars_limit := pos_integer() | unlimited}. -type msg_fun() :: fun((term()) -> {io:format(),[term()]} | report() | unicode:chardata()). @@ -84,7 +89,7 @@ file => file:filename(), line => non_neg_integer(), domain => [atom()], - report_cb => fun((report()) -> {io:format(),[term()]}), + report_cb => report_cb(), atom() => term()}. -type location() :: #{mfa := {module(),atom(),non_neg_integer()}, file := file:filename(), @@ -110,10 +115,22 @@ -type config_handler() :: {handler, handler_id(), module(), handler_config()}. --export_type([log_event/0,level/0,report/0,msg_fun/0,metadata/0, - primary_config/0,handler_config/0,handler_id/0, - filter_id/0,filter/0,filter_arg/0,filter_return/0, - config_handler/0,formatter_config/0]). +-export_type([log_event/0, + level/0, + report/0, + report_cb/0, + report_cb_config/0, + msg_fun/0, + metadata/0, + primary_config/0, + handler_config/0, + handler_id/0, + filter_id/0, + filter/0, + filter_arg/0, + filter_return/0, + config_handler/0, + formatter_config/0]). %%%----------------------------------------------------------------- %%% API diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl index 456b0c9e8d..937e7f859b 100644 --- a/lib/kernel/src/logger_formatter.erl +++ b/lib/kernel/src/logger_formatter.erl @@ -26,16 +26,17 @@ %%%----------------------------------------------------------------- %%% Types --type config() :: #{chars_limit=>pos_integer()| unlimited, - depth=>pos_integer() | unlimited, - legacy_header=>boolean(), - max_size=>pos_integer() | unlimited, - report_cb=>fun((logger:report()) -> {io:format(),[term()]}), - single_line=>boolean(), - template=>template(), - time_designator=>byte(), - time_offset=>integer()|[byte()]}. --type template() :: [metakey()|{metakey(),template(),template()}|string()]. +-type config() :: #{chars_limit => pos_integer() | unlimited, + depth => pos_integer() | unlimited, + encoding => unicode:encoding(), + legacy_header => boolean(), + max_size => pos_integer() | unlimited, + report_cb => logger:report_cb(), + single_line => boolean(), + template => template(), + time_designator => byte(), + time_offset => integer() | [byte()]}. +-type template() :: [metakey() | {metakey(),template(),template()} | string()]. -type metakey() :: atom() | [atom()]. %%%----------------------------------------------------------------- @@ -119,65 +120,96 @@ value(_,_) -> to_string(time,Time,Config) -> format_time(Time,Config); -to_string(mfa,MFA,_Config) -> - format_mfa(MFA); -to_string(_,Value,_Config) -> - to_string(Value). +to_string(mfa,MFA,Config) -> + format_mfa(MFA,Config); +to_string(_,Value,Config) -> + to_string(Value,Config). -to_string(X) when is_atom(X) -> +to_string(X,_) when is_atom(X) -> atom_to_list(X); -to_string(X) when is_integer(X) -> +to_string(X,_) when is_integer(X) -> integer_to_list(X); -to_string(X) when is_pid(X) -> +to_string(X,_) when is_pid(X) -> pid_to_list(X); -to_string(X) when is_reference(X) -> +to_string(X,_) when is_reference(X) -> ref_to_list(X); -to_string(X) when is_list(X) -> - case io_lib:printable_unicode_list(lists:flatten(X)) of +to_string(X,Config) when is_list(X) -> + case printable_list(lists:flatten(X)) of true -> X; - _ -> io_lib:format("~tp",[X]) + _ -> io_lib:format(p(Config),[X]) end; -to_string(X) -> - io_lib:format("~tp",[X]). +to_string(X,Config) -> + io_lib:format(p(Config),[X]). + +printable_list([]) -> + false; +printable_list(X) -> + io_lib:printable_list(X). format_msg({string,Chardata},Meta,Config) -> - format_msg({"~ts",[Chardata]},Meta,Config); -format_msg({report,_}=Msg,Meta,#{report_cb:=Fun}=Config) when is_function(Fun,1) -> + format_msg({s(Config),[Chardata]},Meta,Config); +format_msg({report,_}=Msg,Meta,#{report_cb:=Fun}=Config) + when is_function(Fun,1); is_function(Fun,2) -> format_msg(Msg,Meta#{report_cb=>Fun},maps:remove(report_cb,Config)); format_msg({report,Report},#{report_cb:=Fun}=Meta,Config) when is_function(Fun,1) -> try Fun(Report) of {Format,Args} when is_list(Format), is_list(Args) -> format_msg({Format,Args},maps:remove(report_cb,Meta),Config); Other -> - format_msg({"REPORT_CB ERROR: ~tp; Returned: ~tp", + P = p(Config), + format_msg({"REPORT_CB/1 ERROR: "++P++"; Returned: "++P, + [Report,Other]},Meta,Config) + catch C:R:S -> + P = p(Config), + format_msg({"REPORT_CB/1 CRASH: "++P++"; Reason: "++P, + [Report,{C,R,logger:filter_stacktrace(?MODULE,S)}]}, + Meta,Config) + end; +format_msg({report,Report},#{report_cb:=Fun}=Meta,Config) when is_function(Fun,2) -> + try Fun(Report,maps:with([encoding,depth,chars_limit],Config)) of + String when ?IS_STRING(String) -> + try unicode:characters_to_list(String) + catch _:_ -> + P = p(Config), + format_msg({"REPORT_CB/2 ERROR: "++P++"; Returned: "++P, + [Report,String]},Meta,Config) + end; + Other -> + P = p(Config), + format_msg({"REPORT_CB/2 ERROR: "++P++"; Returned: "++P, [Report,Other]},Meta,Config) - catch C:R -> - format_msg({"REPORT_CB CRASH: ~tp; Reason: ~tp", - [Report,{C,R}]},Meta,Config) + catch C:R:S -> + P = p(Config), + format_msg({"REPORT_CB/2 CRASH: "++P++"; Reason: "++P, + [Report,{C,R,logger:filter_stacktrace(?MODULE,S)}]}, + Meta,Config) end; format_msg({report,Report},Meta,Config) -> format_msg({report,Report}, Meta#{report_cb=>fun logger:format_report/1}, Config); -format_msg(Msg,_Meta,#{depth:=Depth,chars_limit:=CharsLimit}) -> - limit_size(Msg, Depth, CharsLimit). - -limit_size(Msg,Depth,unlimited) -> - limit_size(Msg,Depth,[]); -limit_size(Msg,Depth,CharsLimit) when is_integer(CharsLimit) -> - limit_size(Msg,Depth,[{chars_limit,CharsLimit}]); -limit_size({Format,Args},unlimited,Opts) when is_list(Opts) -> +format_msg(Msg,_Meta,#{depth:=Depth,chars_limit:=CharsLimit,encoding:=Enc}) -> + limit_size(Msg, Depth, CharsLimit, Enc). + +limit_size(Msg,Depth,unlimited,Enc) -> + limit_size(Msg,Depth,[],Enc); +limit_size(Msg,Depth,CharsLimit,Enc) when is_integer(CharsLimit) -> + limit_size(Msg,Depth,[{chars_limit,CharsLimit}],Enc); +limit_size({Format,Args},unlimited,Opts,Enc) when is_list(Opts) -> try io_lib:format(Format,Args,Opts) catch _:_ -> - io_lib:format("FORMAT ERROR: ~tp - ~tp",[Format,Args],Opts) + P = p(Enc), + io_lib:format("FORMAT ERROR: "++P++" - "++P,[Format,Args],Opts) end; -limit_size({Format0,Args},Depth,Opts) when is_integer(Depth) -> +limit_size({Format0,Args},Depth,Opts,Enc) when is_integer(Depth) -> try Format1 = io_lib:scan_format(Format0, Args), Format = limit_format(Format1, Depth), io_lib:build_text(Format,Opts) catch _:_ -> - limit_size({"FORMAT ERROR: ~tp - ~tp",[Format0,Args]},Depth,Opts) + P = p(Enc), + limit_size({"FORMAT ERROR: "++P++" - "++P,[Format0,Args]}, + Depth,Opts,Enc) end. limit_format([#{control_char:=C0}=M0|T], Depth) when C0 =:= $p; @@ -225,22 +257,23 @@ timestamp_to_datetimemicro(SysTime,Config) when is_integer(SysTime) -> end, {Date,Time,Micro,UtcStr}. -format_mfa({M,F,A}) when is_atom(M), is_atom(F), is_integer(A) -> +format_mfa({M,F,A},_) when is_atom(M), is_atom(F), is_integer(A) -> atom_to_list(M)++":"++atom_to_list(F)++"/"++integer_to_list(A); -format_mfa({M,F,A}) when is_atom(M), is_atom(F), is_list(A) -> - format_mfa({M,F,length(A)}); -format_mfa(MFA) -> - to_string(MFA). +format_mfa({M,F,A},Config) when is_atom(M), is_atom(F), is_list(A) -> + format_mfa({M,F,length(A)},Config); +format_mfa(MFA,Config) -> + to_string(MFA,Config). maybe_add_legacy_header(Level, #{time:=Timestamp}=Meta, #{legacy_header:=true}=Config) -> #{title:=Title}=MyMeta = add_legacy_title(Level,Meta,Config), - {{Y,Mo,D},{H,Mi,S},Micro,UtcStr} = + {{Y,Mo,D},{H,Mi,Sec},Micro,UtcStr} = timestamp_to_datetimemicro(Timestamp,Config), + S = s(Config), Header = - io_lib:format("=~ts==== ~w-~s-~4w::~2..0w:~2..0w:~2..0w.~6..0w ~s===", - [Title,D,month(Mo),Y,H,Mi,S,Micro,UtcStr]), + io_lib:format("="++S++"==== ~w-~s-~4w::~2..0w:~2..0w:~2..0w.~6..0w ~s===", + [Title,D,month(Mo),Y,H,Mi,Sec,Micro,UtcStr]), Meta#{?MODULE=>MyMeta#{header=>Header}}; maybe_add_legacy_header(_,Meta,_) -> Meta. @@ -280,10 +313,11 @@ month(12) -> "Dec". %% configuration map add_default_config(Config0) -> Default = - #{legacy_header=>false, + #{chars_limit=>unlimited, + encoding=>utf8, error_logger_notice_header=>info, + legacy_header=>false, single_line=>true, - chars_limit=>unlimited, time_designator=>$T}, MaxSize = get_max_size(maps:get(max_size,Config0,undefined)), Depth = get_depth(maps:get(depth,Config0,undefined)), @@ -369,7 +403,8 @@ do_check_config([{legacy_header,LH}|Config]) when is_boolean(LH) -> do_check_config([{error_logger_notice_header,ELNH}|Config]) when ELNH == info; ELNH == notice -> do_check_config(Config); -do_check_config([{report_cb,RCB}|Config]) when is_function(RCB,1) -> +do_check_config([{report_cb,RCB}|Config]) when is_function(RCB,1); + is_function(RCB,2) -> do_check_config(Config); do_check_config([{template,T}|Config]) -> case check_template(T) of @@ -456,3 +491,17 @@ check_timezone(Tz) -> catch _:_ -> error end. + +p(#{encoding:=Enc}) -> + p(Enc); +p(latin1) -> + "~p"; +p(_) -> + "~tp". + +s(#{encoding:=Enc}) -> + s(Enc); +s(latin1) -> + "~s"; +s(_) -> + "~ts". diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl index 8fe8d5199b..aa8dc42691 100644 --- a/lib/kernel/test/logger_formatter_SUITE.erl +++ b/lib/kernel/test/logger_formatter_SUITE.erl @@ -312,30 +312,48 @@ format_msg(_Config) -> #{report_cb=>fun(_)-> faulty_return end}, #{template=>Template}), ct:log(String5), - "REPORT_CB ERROR: term; Returned: faulty_return" = String5, + "REPORT_CB/1 ERROR: term; Returned: faulty_return" = String5, String6 = format(info,{report,term}, #{report_cb=>fun(_)-> erlang:error(fun_crashed) end}, #{template=>Template}), ct:log(String6), - "REPORT_CB CRASH: term; Reason: {error,fun_crashed}" = String6, + "REPORT_CB/1 CRASH: term; Reason: {error,fun_crashed,"++_ = String6, + + String7 = format(info,{report,term}, + #{report_cb=>fun(_,_)-> ['not',a,string] end}, + #{template=>Template}), + ct:log(String7), + "REPORT_CB/2 ERROR: term; Returned: ['not',a,string]" = String7, + + String8 = format(info,{report,term}, + #{report_cb=>fun(_,_)-> faulty_return end}, + #{template=>Template}), + ct:log(String8), + "REPORT_CB/2 ERROR: term; Returned: faulty_return" = String8, + + String9 = format(info,{report,term}, + #{report_cb=>fun(_,_)-> erlang:error(fun_crashed) end}, + #{template=>Template}), + ct:log(String9), + "REPORT_CB/2 CRASH: term; Reason: {error,fun_crashed,"++_ = String9, %% strings are not formatted - String7 = format(info,{string,"string"}, + String10 = format(info,{string,"string"}, #{report_cb=>fun(_)-> {"formatted",[]} end}, #{template=>Template}), - ct:log(String7), - "string" = String7, + ct:log(String10), + "string" = String10, - String8 = format(info,{string,['not',printable,list]}, + String11 = format(info,{string,['not',printable,list]}, #{report_cb=>fun(_)-> {"formatted",[]} end}, #{template=>Template}), - ct:log("~ts",[String8]), % avoiding ct_log crash - "FORMAT ERROR: \"~ts\" - [['not',printable,list]]" = String8, + ct:log("~ts",[String11]), % avoiding ct_log crash + "FORMAT ERROR: \"~ts\" - [['not',printable,list]]" = String11, - String9 = format(info,{string,"string"},#{},#{template=>Template}), - ct:log(String9), - "string" = String9, + String12 = format(info,{string,"string"},#{},#{template=>Template}), + ct:log(String12), + "string" = String12, ok. @@ -639,8 +657,10 @@ check_config(_Config) -> ?cfgerr({max_size,bad}) = logger_formatter:check_config(#{max_size => bad}), + ok = + logger_formatter:check_config(#{report_cb => fun(_,_) -> "" end}), ?cfgerr({report_cb,F}) = - logger_formatter:check_config(#{report_cb => F=fun(_,_) -> {"",[]} end}), + logger_formatter:check_config(#{report_cb => F=fun(_,_,_) -> {"",[]} end}), ?cfgerr({report_cb,bad}) = logger_formatter:check_config(#{report_cb => bad}), -- cgit v1.2.3 From 4ae5136ccb68bed0e72e1f45d34d1a8b44594590 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 2 Jul 2018 11:48:09 +0200 Subject: [logger] Fix regexp replacement for unicode strings --- lib/kernel/src/logger_formatter.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl index 937e7f859b..a5c6984bc6 100644 --- a/lib/kernel/src/logger_formatter.erl +++ b/lib/kernel/src/logger_formatter.erl @@ -77,7 +77,7 @@ format(#{level:=Level,msg:=Msg0,meta:=Meta},Config0) %% Trim leading and trailing whitespaces, and replace %% newlines with ", " re:replace(string:trim(MsgStr0),",?\r?\n\s*",", ", - [{return,list},global]); + [{return,list},global,unicode]); _false -> MsgStr0 end; -- cgit v1.2.3 From 0a4834e59f2aac7b76331410bcee641d375d8ec0 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 6 Jul 2018 15:27:42 +0200 Subject: [kernel] Reduce risk of dead lock when terminating logger_sup --- lib/kernel/src/Makefile | 3 +- lib/kernel/src/error_logger.erl | 15 ++-- lib/kernel/src/kernel.app.src | 2 + lib/kernel/src/logger_disk_log_h.erl | 8 ++- lib/kernel/src/logger_h_common.erl | 13 ---- lib/kernel/src/logger_handler_watcher.erl | 113 ++++++++++++++++++++++++++++++ lib/kernel/src/logger_std_h.erl | 8 ++- lib/kernel/src/logger_sup.erl | 6 +- 8 files changed, 144 insertions(+), 24 deletions(-) create mode 100644 lib/kernel/src/logger_handler_watcher.erl (limited to 'lib/kernel') diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile index c595c25341..57f17defc8 100644 --- a/lib/kernel/src/Makefile +++ b/lib/kernel/src/Makefile @@ -112,7 +112,8 @@ MODULES = \ logger \ logger_backend \ logger_config \ - logger_std_h \ + logger_handler_watcher \ + logger_std_h \ logger_disk_log_h \ logger_h_common \ logger_filters \ diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index a7e7f19167..ad8c937882 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -74,8 +74,8 @@ start() -> type => worker, modules => dynamic}, case supervisor:start_child(logger_sup, ErrorLogger) of - {ok,_} -> - ok; + {ok,Pid} -> + ok = logger_handler_watcher:register_handler(?MODULE,Pid); Error -> Error end; @@ -95,9 +95,14 @@ start_link() -> %%% Stop the event manager -spec stop() -> ok. stop() -> - _ = supervisor:terminate_child(logger_sup,?MODULE), - _ = supervisor:delete_child(logger_sup,?MODULE), - ok. + case whereis(?MODULE) of + undefined -> + ok; + _Pid -> + _ = gen_event:stop(?MODULE,{shutdown,stopped},infinity), + _ = supervisor:delete_child(logger_sup,?MODULE), + ok + end. %%%----------------------------------------------------------------- %%% Callbacks for logger diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 47dd7c03d5..4933eae76f 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -67,6 +67,7 @@ logger_filters, logger_formatter, logger_h_common, + logger_handler_watcher, logger_server, logger_simple_h, logger_std_h, @@ -129,6 +130,7 @@ kernel_refc, kernel_sup, logger, + logger_handler_watcher, logger_sup, net_kernel, net_sup, diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index 0a72654e11..e56531c3cb 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -488,7 +488,8 @@ start(Name, Config, HandlerState) -> type => worker, modules => [?MODULE]}, case supervisor:start_child(logger_sup, LoggerDLH) of - {ok,_Pid,Config1} -> + {ok,Pid,Config1} -> + ok = logger_handler_watcher:register_handler(Name,Pid), {ok,Config1}; Error -> Error @@ -506,8 +507,11 @@ stop(Name) -> %% system termination in order to avoid circular attempts %% at removing the handler (implying deadlocks and %% timeouts). + %% And we don't need to do supervisor:delete_child, since + %% the restart type is temporary, which means that the + %% child specification is automatically removed from the + %% supervisor when the process dies. _ = gen_server:call(Pid, stop), - _ = supervisor:delete_child(logger_sup, Name), ok end. diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl index a40345dddc..854e5479b9 100644 --- a/lib/kernel/src/logger_h_common.erl +++ b/lib/kernel/src/logger_h_common.erl @@ -309,19 +309,6 @@ stop_or_restart(Name, {shutdown,Reason={overloaded,_Name,_QLen,_Mem}}, end, spawn(RemoveAndRestart), ok; - -stop_or_restart(Name, shutdown, _State) -> - %% Probably terminated by supervisor. Remove the handler to avoid - %% error printouts due to failing handler. - _ = case logger:get_handler_config(Name) of - {ok,_} -> - %% Spawning to avoid deadlock - spawn(logger,remove_handler,[Name]); - _ -> - ok - end, - ok; - stop_or_restart(_Name, _Reason, _State) -> ok. diff --git a/lib/kernel/src/logger_handler_watcher.erl b/lib/kernel/src/logger_handler_watcher.erl new file mode 100644 index 0000000000..b75c74c643 --- /dev/null +++ b/lib/kernel/src/logger_handler_watcher.erl @@ -0,0 +1,113 @@ +%% +%% %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_handler_watcher). + +-behaviour(gen_server). + +%% API +-export([start_link/0]). +-export([register_handler/2]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]). + +-define(SERVER, ?MODULE). + +-record(state, {handlers}). + +%%%=================================================================== +%%% API +%%%=================================================================== + +-spec start_link() -> {ok, Pid :: pid()} | + {error, Error :: {already_started, pid()}} | + {error, Error :: term()} | + ignore. +start_link() -> + gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). + +-spec register_handler(Id::logger:handler_id(),Pid::pid()) -> ok. +register_handler(Id,Pid) -> + gen_server:call(?SERVER,{register,Id,Pid}). + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== + +-spec init(Args :: term()) -> {ok, State :: term()} | + {ok, State :: term(), Timeout :: timeout()} | + {ok, State :: term(), hibernate} | + {stop, Reason :: term()} | + ignore. +init([]) -> + process_flag(trap_exit, true), + {ok, #state{handlers=[]}}. + +-spec handle_call(Request :: term(), From :: {pid(), term()}, State :: term()) -> + {reply, Reply :: term(), NewState :: term()} | + {reply, Reply :: term(), NewState :: term(), Timeout :: timeout()} | + {reply, Reply :: term(), NewState :: term(), hibernate} | + {noreply, NewState :: term()} | + {noreply, NewState :: term(), Timeout :: timeout()} | + {noreply, NewState :: term(), hibernate} | + {stop, Reason :: term(), Reply :: term(), NewState :: term()} | + {stop, Reason :: term(), NewState :: term()}. +handle_call({register,Id,Pid}, _From, #state{handlers=Hs}=State) -> + Ref = erlang:monitor(process,Pid), + Hs1 = lists:keystore(Id,1,Hs,{Id,Ref}), + {reply, ok, State#state{handlers=Hs1}}. + +-spec handle_cast(Request :: term(), State :: term()) -> + {noreply, NewState :: term()} | + {noreply, NewState :: term(), Timeout :: timeout()} | + {noreply, NewState :: term(), hibernate} | + {stop, Reason :: term(), NewState :: term()}. +handle_cast(_Request, State) -> + {noreply, State}. + +-spec handle_info(Info :: timeout() | term(), State :: term()) -> + {noreply, NewState :: term()} | + {noreply, NewState :: term(), Timeout :: timeout()} | + {noreply, NewState :: term(), hibernate} | + {stop, Reason :: normal | term(), NewState :: term()}. +handle_info({'DOWN',Ref,process,_,shutdown}, #state{handlers=Hs}=State) -> + case lists:keytake(Ref,2,Hs) of + {value,{Id,Ref},Hs1} -> + %% Probably terminated by supervisor. Remove the handler to avoid + %% error printouts due to failing handler. + _ = case logger:get_handler_config(Id) of + {ok,_} -> + logger:remove_handler(Id); + _ -> + ok + end, + {noreply,State#state{handlers=Hs1}}; + false -> + {noreply, State} + end; +handle_info({'DOWN',Ref,process,_,_OtherReason}, #state{handlers=Hs}=State) -> + {noreply,State#state{handlers=lists:keydelete(Ref,2,Hs)}}; +handle_info(_Other,State) -> + {noreply,State}. + +-spec terminate(Reason :: normal | shutdown | {shutdown, term()} | term(), + State :: term()) -> any(). +terminate(_Reason, _State) -> + ok. diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index 480fafd6d8..9a2a1443b3 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -467,7 +467,8 @@ start(Name, Config, HandlerState) -> type => worker, modules => [?MODULE]}, case supervisor:start_child(logger_sup, LoggerStdH) of - {ok,_Pid,Config1} -> + {ok,Pid,Config1} -> + ok = logger_handler_watcher:register_handler(Name,Pid), {ok,Config1}; Error -> Error @@ -485,8 +486,11 @@ stop(Name) -> %% system termination in order to avoid circular attempts %% at removing the handler (implying deadlocks and %% timeouts). + %% And we don't need to do supervisor:delete_child, since + %% the restart type is temporary, which means that the + %% child specification is automatically removed from the + %% supervisor when the process dies. _ = gen_server:call(Pid, stop), - _ = supervisor:delete_child(logger_sup, Name), ok end. diff --git a/lib/kernel/src/logger_sup.erl b/lib/kernel/src/logger_sup.erl index dcdcdad0bd..3d6f482e20 100644 --- a/lib/kernel/src/logger_sup.erl +++ b/lib/kernel/src/logger_sup.erl @@ -46,7 +46,11 @@ init([]) -> intensity => 1, period => 5}, - {ok, {SupFlags, []}}. + Watcher = #{id => logger_handler_watcher, + start => {logger_handler_watcher, start_link, []}, + shutdown => brutal_kill}, + + {ok, {SupFlags, [Watcher]}}. %%%=================================================================== %%% Internal functions -- cgit v1.2.3 From 48824be3d833b13a35b92652df372c6ce3c190a0 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 11 Jul 2018 16:13:13 +0200 Subject: [logger] Allow setting kernel parameter 'logger_level' to 'all' --- lib/kernel/src/logger.erl | 2 +- lib/kernel/test/logger_env_var_SUITE.erl | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 4924c6cb4f..ffc90f4fc5 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -687,7 +687,7 @@ get_logger_type() -> get_logger_level() -> case application:get_env(kernel,logger_level,info) of - Level when ?IS_LEVEL(Level) -> + Level when ?IS_LEVEL(Level); Level=:=all; Level=:=none -> Level; Level -> throw({logger_level, Level}) diff --git a/lib/kernel/test/logger_env_var_SUITE.erl b/lib/kernel/test/logger_env_var_SUITE.erl index 04a4364947..e8d1a313dc 100644 --- a/lib/kernel/test/logger_env_var_SUITE.erl +++ b/lib/kernel/test/logger_env_var_SUITE.erl @@ -71,6 +71,7 @@ all() -> sasl_compatible_false, sasl_compatible_false_no_progress, sasl_compatible, + all_logger_level, {group,bad}, {group,error_logger}, {group,logger} @@ -572,6 +573,24 @@ sasl_compatible(Config) -> 0),% progress in std logger ok. +all_logger_level(Config) -> + [all_logger_level(Config,Level) || Level <- [none, + emergency, + alert, + critical, + error, + warning, + notice, + info, + debug, + all]], + ok. + +all_logger_level(Config,Level) -> + {ok,#{primary:=#{level:=Level}},Node} = setup(Config,[{logger_level,Level}]), + true = test_server:stop_node(Node), + ok. + bad_error_logger(Config) -> error = setup(Config,[{error_logger,baddest}]). -- cgit v1.2.3 From b453f67879df577fcf5b0eb6062ec444b0c43958 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 13 Jul 2018 15:10:57 +0200 Subject: docs: make clean all XMLDIR --- lib/kernel/doc/src/Makefile | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile index f34eee71ba..fba990bf5c 100644 --- a/lib/kernel/doc/src/Makefile +++ b/lib/kernel/doc/src/Makefile @@ -142,6 +142,7 @@ debug opt: clean clean_docs: rm -rf $(HTMLDIR)/* + rm -rf $(XMLDIR) rm -f $(MAN3DIR)/* rm -f $(MAN4DIR)/* rm -f $(MAN6DIR)/* -- cgit v1.2.3 From 0d52b992c30eff8604e5a5363510fea38c712263 Mon Sep 17 00:00:00 2001 From: sunboshan Date: Fri, 13 Jul 2018 20:29:05 -0700 Subject: Fix kernel_app doc logger_level defaults The doc says the default kernel logger_level is info, it should be notice. ``` % erl Erlang/OTP 21 [erts-10.0.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace] Eshell V10.0.3 (abort with ^G) 1> application:get_env(kernel,logger_level). {ok,notice} ``` --- lib/kernel/doc/src/kernel_app.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index 3914226a3e..93756d2278 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -192,7 +192,7 @@

    To change the primary log level at runtime, use logger:set_primary_config(level, Level).

    -

    Defaults to info.

    +

    Defaults to notice.

    logger_sasl_compatible = true | false -- cgit v1.2.3 From 9dea59ca18fe832fd57f5261ede90857c304dc49 Mon Sep 17 00:00:00 2001 From: sunboshan Date: Sat, 14 Jul 2018 08:47:49 -0700 Subject: Fix typo in logger docs --- lib/kernel/doc/src/logger_chapter.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index dc68d5c761..30172f6ca6 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -240,7 +240,7 @@ logger:debug(#{got => connection_request, id => Id, state => State}, with logger:set_process_metadata/1 and - logger:update_process metadata/1, + logger:update_process_metadata/1, respectively. This metadata applies to the process on which these calls are made, and Logger adds the metadata to all log events issued on that process.

    -- cgit v1.2.3 From f49f2d060e43953c4f09e604ee2b98db33867089 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 18 Jun 2018 17:12:49 +0200 Subject: kernel: Add logger:set_application_level/2 OTP-15146 --- lib/kernel/doc/src/logger.xml | 25 +++++++++++++++++++++++-- lib/kernel/src/logger.erl | 22 ++++++++++++++++++++++ lib/kernel/src/logger_server.erl | 4 ++-- lib/kernel/test/logger_SUITE.erl | 29 +++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 4 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index f1830a8224..a4d6efa2d8 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -695,6 +695,17 @@ start(_, []) ->
    + + + Set the log level for all modules in the specified application. + +

    Set the log level for all the modules of the specified application.

    +

    This function is a convenience function that calls + logger:set_module_level/2 + for each module associated with an application.

    +
    +
    + Set configuration data for the specified handler. @@ -782,8 +793,7 @@ start(_, []) -> Set the log level for the specified modules. -

    Set the log level for the - specified modules.

    +

    Set the log level for the specified modules.

    The log level for a module overrides the primary log level of Logger for log events originating from the module in question. Notice, however, that it does not override the @@ -838,6 +848,17 @@ start(_, []) -> + + + Unset the log level for all modules in the specified application. + +

    Unset the log level for all the modules of the specified application.

    +

    This function is a convinience function that calls + logger:unset_module_level/2 + for each module associated with an application.

    +
    +
    + Remove module specific log settings for all modules. diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index ffc90f4fc5..0020fe220b 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -39,6 +39,7 @@ remove_primary_filter/1, remove_handler_filter/2, set_module_level/2, unset_module_level/1, unset_module_level/0, + set_application_level/2, unset_application_level/1, get_module_level/0, get_module_level/1, set_primary_config/1, set_primary_config/2, set_handler_config/2, set_handler_config/3, @@ -488,6 +489,27 @@ unset_module_level(Modules) -> unset_module_level() -> logger_server:unset_module_level(). +-spec set_application_level(Application,Level) -> ok | {error, not_loaded} when + Application :: atom(), + Level :: level() | all | none. +set_application_level(App,Level) -> + case application:get_key(App, modules) of + {ok, Modules} -> + set_module_level(Modules, Level); + undefined -> + {error, {not_loaded, App}} + end. + +-spec unset_application_level(Application) -> ok | {error, not_loaded} when + Application :: atom(). +unset_application_level(App) -> + case application:get_key(App, modules) of + {ok, Modules} -> + unset_module_level(Modules); + undefined -> + {error, {not_loaded, App}} + end. + -spec get_module_level(Modules) -> [{Module,Level}] when Modules :: [Module] | Module, Module :: module(), diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index 28e31d46ea..644fdd5af2 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -83,7 +83,7 @@ set_module_level(Modules,Level) when is_list(Modules) -> Error -> Error end; false -> - {error,{not_a_list_of_modles,Modules}} + {error,{not_a_list_of_modules,Modules}} end; set_module_level(Modules,_) -> {error,{not_a_list_of_modules,Modules}}. @@ -96,7 +96,7 @@ unset_module_level(Modules) when is_list(Modules) -> true -> call({unset_module_level,Modules}); false -> - {error,{not_a_list_of_modles,Modules}} + {error,{not_a_list_of_modules,Modules}} end; unset_module_level(Modules) -> {error,{not_a_list_of_modules,Modules}}. diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index 8f74ebdc47..6bd9b20c35 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -87,6 +87,7 @@ all() -> macros, set_level, set_module_level, + set_application_level, cache_module_level, format_report, filter_failed, @@ -423,6 +424,34 @@ set_module_level(cleanup,_Config) -> logger:unset_module_level(?MODULE), ok. +set_application_level(_Config) -> + + {error,{not_loaded,mnesia}} = logger:set_application_level(mnesia, warning), + {error,{not_loaded,mnesia}} = logger:unset_application_level(mnesia), + + application:load(mnesia), + {ok, Modules} = application:get_key(mnesia, modules), + [] = logger:get_module_level(Modules), + + {error,{invalid_level,warn}} = logger:set_application_level(mnesia, warn), + + ok = logger:set_application_level(mnesia, debug), + DebugModules = lists:sort([{M,debug} || M <- Modules]), + DebugModules = lists:sort(logger:get_module_level(Modules)), + + ok = logger:set_application_level(mnesia, warning), + + WarnModules = lists:sort([{M,warning} || M <- Modules]), + WarnModules = lists:sort(logger:get_module_level(Modules)), + + ok = logger:unset_application_level(mnesia), + [] = logger:get_module_level(Modules). + +set_application_level(cleanup,_Config) -> + ok = logger:unset_application_level(mnesia), + ok = application:unload(mnesia), + ok. + cache_module_level(_Config) -> ok = logger:unset_module_level(?MODULE), [] = ets:lookup(?LOGGER_TABLE,?MODULE), %dirty - add API in logger_config? -- cgit v1.2.3 From 2e5e9cf29d67db7d9f85c3ae27c88ca3824e7496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Wed, 18 Jul 2018 13:55:16 +0200 Subject: Fix node crash on passing bad arguments to file:read_file_info/2 --- lib/kernel/test/file_SUITE.erl | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 23913ac56a..e784c06865 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -1367,6 +1367,10 @@ file_info_basic_file(Config) when is_list(Config) -> io:put_chars(Fd1, "foo bar"), ok = ?FILE_MODULE:close(Fd1), + %% Don't crash the file server when passing incorrect arguments. + {error,badarg} = ?FILE_MODULE:read_file_info(Name, [{time, gurka}]), + {error,badarg} = ?FILE_MODULE:read_file_info([#{} | gaffel]), + %% Test that the file has the expected attributes. %% The times are tricky, so we will save them to a separate test case. {ok,FileInfo} = ?FILE_MODULE:read_file_info(Name), -- cgit v1.2.3 From 4f2fa2edabcc5807a879a6f60ecb2f28d5ad6085 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 18 Jul 2018 16:52:41 +0200 Subject: kernel: Improve net_ticktime docs --- lib/kernel/doc/src/kernel_app.xml | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index 3914226a3e..ccb7d17d25 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -310,24 +310,31 @@ net_ticktime = TickTime -

    Specifies the net_kernel tick time. TickTime - is specified in seconds. Once every TickTime/4 second, all - connected nodes are ticked (if anything else is written - to a node). If nothing is received from another node - within the last four tick times, that node is considered - to be down. This ensures that nodes that are not responding, - for reasons such as hardware errors, are considered to be - down.

    -

    The time T, in which a node that is not responding is - detected, is calculated as , where:

    +

    Specifies the net_kernel tick time in seconds. This is the + approximate time a connected node may be unresponsive until it is + considered down and thereby disconnected.

    +

    Once every TickTime/4 seconds, each connected node is ticked + if nothing has been sent to it during that last TickTime/4 + interval. A tick is a small package sent on the connection. A connected + node is considered to be down if no ticks or payload packages have been + received during the last four TickTime/4 intervals. This ensures + that nodes that are not responding, for reasons such as hardware errors, + are considered to be down.

    +

    As the availability is only checked every TickTime/4 seconds, + the actual time T a node have been unresponsive when + detected may vary between MinT and MaxT, + where:

    MinT = TickTime - TickTime / 4 MaxT = TickTime + TickTime / 4 -

    TickTime defaults to 60 (seconds). Thus, - seconds.

    -

    Notice that all communicating nodes are to have the same - TickTime value specified.

    -

    Normally, a terminating node is detected immediately.

    +

    TickTime defaults to 60 seconds. Thus, + seconds.

    +

    Notice that all communicating nodes are to have the + same TickTime value specified, as it determines both the + frequency of outgoing ticks and the expected frequency of incominging + ticks.

    +

    Normally, a terminating node is detected immediately by the transport + protocol (like TCP/IP).

    shutdown_timeout = integer() | infinity -- cgit v1.2.3 From 7507f47dc14093443fb8446b969f73d276339413 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 20 Jul 2018 14:10:09 +0200 Subject: Prepare release --- lib/kernel/doc/src/notes.xml | 16 ++++++++++++++++ lib/kernel/vsn.mk | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index f7d2c93666..1b7f11d4fa 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,22 @@

    This document describes the changes made to the Kernel application.

    +
    Kernel 5.4.3.2 + +
    Fixed Bugs and Malfunctions + + +

    + Non semantic change in dist_util.erl to silence dialyzer + warning.

    +

    + Own Id: OTP-15170

    +
    +
    +
    + +
    +
    Kernel 5.4.3.1
    Fixed Bugs and Malfunctions diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 7f2041ef55..d00032f89c 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 5.4.3.1 +KERNEL_VSN = 5.4.3.2 -- cgit v1.2.3 From a9d361b4be7d6329bedd7b37d454c1abf4f27233 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 21 Jun 2018 11:00:25 +0200 Subject: erts: Limit the automatic max buffer for UDP to 2^16 There is no reason to have a larger buffer than this as the recvmsg call will never return more data. OTP-15206 --- lib/kernel/doc/src/inet.xml | 19 ++++++++++--------- lib/kernel/test/gen_udp_SUITE.erl | 12 ++++++++++-- 2 files changed, 20 insertions(+), 11 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index e6a7962c5a..f281d61459 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -734,22 +734,23 @@ get_tcpi_sacked(Sock) -> {buffer, Size} -

    The size of the user-level software buffer used by - the driver. - Not to be confused with options sndbuf +

    The size of the user-level buffer used by + the driver. Not to be confused with options sndbuf and recbuf, which correspond to the - Kernel socket buffers. It is recommended - to have val(buffer) >= max(val(sndbuf),val(recbuf)) to + Kernel socket buffers. For TCP it is recommended + to have val(buffer) >= val(recbuf) to avoid performance issues because of unnecessary copying. + For UDP the same recommendation applies, but the max should + not be larger than the MTU of the network path. val(buffer) is automatically set to the above - maximum when values sndbuf or recbuf are set. - However, as the sizes set for sndbuf and recbuf + maximum when recbuf is set. + However, as the size set for recbuf usually become larger, you are encouraged to use getopts/2 to analyze the behavior of your operating system.

    Note that this is also the maximum amount of data that can be - received from a single recv call. If you are using higher than - normal MTU consider setting buffer higher.

    + received from a single recv call. If you are using higher than + normal MTU consider setting buffer higher.

    {delay_send, Boolean} diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index b39399b18a..3acfff929e 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -34,7 +34,7 @@ -export([init_per_testcase/2, end_per_testcase/2]). -export([send_to_closed/1, active_n/1, - buffer_size/1, binary_passive_recv/1, bad_address/1, + buffer_size/1, binary_passive_recv/1, max_buffer_size/1, bad_address/1, read_packets/1, open_fd/1, connect/1, implicit_inet6/1, local_basic/1, local_unbound/1, local_fdopen/1, local_fdopen_unbound/1, local_abstract/1]). @@ -44,7 +44,7 @@ suite() -> {timetrap,{minutes,1}}]. all() -> - [send_to_closed, buffer_size, binary_passive_recv, + [send_to_closed, buffer_size, binary_passive_recv, max_buffer_size, bad_address, read_packets, open_fd, connect, implicit_inet6, active_n, {group, local}]. @@ -237,6 +237,14 @@ buffer_size_server_recv(Socket, IP, Port, Cnt) -> end. +%%------------------------------------------------------------- +%% OTP-15206: Keep buffer small for udp +%%------------------------------------------------------------- +max_buffer_size(Config) when is_list(Config) -> + {ok, Socket} = gen_udp:open(0, [binary]), + ok = inet:setopts(Socket,[{recbuf, 1 bsl 20}]), + {ok, [{buffer, 65536}]} = inet:getopts(Socket,[buffer]), + gen_udp:close(Socket). %%------------------------------------------------------------- %% OTP-3823 gen_udp:recv does not return address in binary mode -- cgit v1.2.3 From d383549b6e9d9b5e06f23226a61564264204db51 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Wed, 20 Jun 2018 10:42:20 +0200 Subject: erts: Fix seq_trace to not clear token for system messages A lot of erts internal messages used behind APIs to create non-blocking calls, e.g. port_command, would cause the seq_trace token to be cleared from the caller when it should not. This commit fixes that and adds asserts that makes sure that all messages sent have to correct token set. Fixes: ERL-602 --- lib/kernel/test/seq_trace_SUITE.erl | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/test/seq_trace_SUITE.erl b/lib/kernel/test/seq_trace_SUITE.erl index ceb4e9cc49..cf4bf11328 100644 --- a/lib/kernel/test/seq_trace_SUITE.erl +++ b/lib/kernel/test/seq_trace_SUITE.erl @@ -783,6 +783,24 @@ do_shrink(N) -> erlang:garbage_collect(), do_shrink(N-1). +%% Test that messages from a port does not clear the token +port_clean_token(Config) when is_list(Config) -> + seq_trace:reset_trace(), + Label = make_ref(), + seq_trace:set_token(label, Label), + {label,Label} = seq_trace:get_token(label), + + %% Create a port and get messages from it + %% We use os:cmd as a convenience as it does + %% open_port, port_command, port_close and receives replies. + %% Maybe it is not ideal to rely on the internal implementation + %% of os:cmd but it will have to do. + os:cmd("ls"), + + %% Make sure that the seq_trace token is still there + {label,Label} = seq_trace:get_token(label), + + ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -- cgit v1.2.3 From 8df53e8de0c323efd03c959bef801216952722f8 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 10 Aug 2018 16:38:48 +0200 Subject: [logger] Avoid unwanted commas introduced by formatter's single_line option When printing a map with ~p in a log message, the formatter's single_line option in some cases introduced unwanted commas after '=>', for example: This is now corrected. When single_line==true, width 0 is added to all ~p and ~P control sequences in the format string. --- lib/kernel/doc/src/logger_formatter.xml | 14 +++-- lib/kernel/src/logger_formatter.erl | 87 ++++++++++++++++++------------ lib/kernel/test/logger_formatter_SUITE.erl | 50 +++++++++++++++++ 3 files changed, 113 insertions(+), 38 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger_formatter.xml b/lib/kernel/doc/src/logger_formatter.xml index 9226d19834..5a060fd42b 100644 --- a/lib/kernel/doc/src/logger_formatter.xml +++ b/lib/kernel/doc/src/logger_formatter.xml @@ -130,10 +130,16 @@ single_line = boolean() -

    If set to true, all newlines in the message are - replaced with ", ", and white spaces following - directly after newlines are removed. Notice that newlines - added by the template parameter are not replaced.

    +

    If set to true, each log event is printed as a + single line. To achieve this, logger_formatter + sets the field width to 0 for all ~p + and ~P control sequences in the format a string + (see + io:format/2), and replaces all + newlines in the message with ", ". White spaces + following directly after newlines are removed. Notice + that newlines added by the template parameter are + not replaced.

    Defaults to true.

    diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl index a5c6984bc6..b0d4adc14d 100644 --- a/lib/kernel/src/logger_formatter.erl +++ b/lib/kernel/src/logger_formatter.erl @@ -188,41 +188,51 @@ format_msg({report,Report},Meta,Config) -> format_msg({report,Report}, Meta#{report_cb=>fun logger:format_report/1}, Config); -format_msg(Msg,_Meta,#{depth:=Depth,chars_limit:=CharsLimit,encoding:=Enc}) -> - limit_size(Msg, Depth, CharsLimit, Enc). - -limit_size(Msg,Depth,unlimited,Enc) -> - limit_size(Msg,Depth,[],Enc); -limit_size(Msg,Depth,CharsLimit,Enc) when is_integer(CharsLimit) -> - limit_size(Msg,Depth,[{chars_limit,CharsLimit}],Enc); -limit_size({Format,Args},unlimited,Opts,Enc) when is_list(Opts) -> - try io_lib:format(Format,Args,Opts) - catch _:_ -> - P = p(Enc), - io_lib:format("FORMAT ERROR: "++P++" - "++P,[Format,Args],Opts) - end; -limit_size({Format0,Args},Depth,Opts,Enc) when is_integer(Depth) -> +format_msg(Msg,_Meta,#{depth:=Depth,chars_limit:=CharsLimit, + encoding:=Enc,single_line:=Single}) -> + Opts = chars_limit_to_opts(CharsLimit), + format_msg(Msg, Depth, Opts, Enc, Single). + +chars_limit_to_opts(unlimited) -> []; +chars_limit_to_opts(CharsLimit) -> [{chars_limit,CharsLimit}]. + +format_msg({Format0,Args},Depth,Opts,Enc,Single) -> try Format1 = io_lib:scan_format(Format0, Args), - Format = limit_format(Format1, Depth), + Format = reformat(Format1, Depth, Single), io_lib:build_text(Format,Opts) - catch _:_ -> - P = p(Enc), - limit_size({"FORMAT ERROR: "++P++" - "++P,[Format0,Args]}, - Depth,Opts,Enc) + catch C:R:S -> + P = p(Enc,Single), + FormatError = "FORMAT ERROR: "++P++" - "++P, + case Format0 of + FormatError -> + %% already been here - avoid failing cyclically + erlang:raise(C,R,S); + _ -> + format_msg({FormatError,[Format0,Args]}, + Depth,Opts,Enc,Single) + end end. -limit_format([#{control_char:=C0}=M0|T], Depth) when C0 =:= $p; - C0 =:= $w -> - C = C0 - ($a - $A), %To uppercase. - #{args:=Args} = M0, - M = M0#{control_char:=C,args:=Args++[Depth]}, - [M|limit_format(T, Depth)]; -limit_format([H|T], Depth) -> - [H|limit_format(T, Depth)]; -limit_format([], _) -> +reformat(Format,unlimited,false) -> + Format; +reformat([#{control_char:=C}=M|T], Depth, true) when C =:= $p -> + [limit_depth(M#{width => 0}, Depth)|reformat(T, Depth, true)]; +reformat([#{control_char:=C}=M|T], Depth, true) when C =:= $P -> + [M#{width => 0}|reformat(T, Depth, true)]; +reformat([#{control_char:=C}=M|T], Depth, Single) when C =:= $p; C =:= $w -> + [limit_depth(M, Depth)|reformat(T, Depth, Single)]; +reformat([H|T], Depth, Single) -> + [H|reformat(T, Depth, Single)]; +reformat([], _, _) -> []. +limit_depth(M0, unlimited) -> + M0; +limit_depth(#{control_char:=C0, args:=Args}=M0, Depth) -> + C = C0 - ($a - $A), %To uppercase. + M0#{control_char:=C,args:=Args++[Depth]}. + truncate(String,unlimited) -> String; truncate(String,Size) -> @@ -492,12 +502,21 @@ check_timezone(Tz) -> error end. -p(#{encoding:=Enc}) -> - p(Enc); -p(latin1) -> - "~p"; -p(_) -> - "~tp". +p(#{encoding:=Enc, single_line:=Single}) -> + p(Enc,Single). + +p(Enc,Single) -> + "~"++p_width(Single)++p_char(Enc). + +p_width(true) -> + "0"; +p_width(false) -> + "". + +p_char(latin1) -> + "p"; +p_char(_) -> + "tp". s(#{encoding:=Enc}) -> s(Enc); diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl index aa8dc42691..2ec4b243cf 100644 --- a/lib/kernel/test/logger_formatter_SUITE.erl +++ b/lib/kernel/test/logger_formatter_SUITE.erl @@ -166,6 +166,56 @@ single_line(_Config) -> " info:\nterm\n" = string:prefix(String2,ExpectedTimestamp), String2 = format(info,{"~p",[term]},#{time=>Time},#{single_line=>bad}), + + + %% Test that no extra commas/spaces are added when removing + %% newlines, especially not after "=>" in a map association (as + %% was the case in OTP-21.0, when the only single_line adjustment + %% was done by regexp replacement of "\n" by ", "). + Prefix = + "Some characters to fill the line ------------------------------------- ", + String3 = format(info,{"~s~p~n~s~p~n",[Prefix, + lists:seq(1,10), + Prefix, + #{a=>map,with=>a,few=>accociations}]}, + #{time=>Time}, + #{single_line=>true}), + ct:log(String3), + match = re:run(String3,"\\[1,2,3,4,5,6,7,8,9,10\\]",[{capture,none}]), + match = re:run(String3, + "#{a => map,few => accociations,with => a}", + [{capture,none}]), + + %% This part is added to make sure that the previous test made + %% sense, i.e. that there would actually be newlines inside the + %% list and map. + String4 = format(info,{"~s~p~n~s~p~n",[Prefix, + lists:seq(1,10), + Prefix, + #{a=>map,with=>a,few=>accociations}]}, + #{time=>Time}, + #{single_line=>false}), + ct:log(String4), + match = re:run(String4,"\\[1,2,3,\n",[global,{capture,none}]), + {match,Match4} = re:run(String4,"=>\n",[global,{capture,all}]), + 3 = length(Match4), + + %% Test that big metadata fields do not get line breaks + String5 = format(info,"", + #{mymeta=>lists:seq(1,100)}, + #{single_line=>true,template=>[mymeta,"\n"]}), + ct:log(String5), + [_] = string:lexemes(String5,"\n"), + + %% Ensure that the previous test made sense, i.e. that the + %% metadata field does produce multiple lines if + %% single_line==false. + String6 = format(info,"", + #{mymeta=>lists:seq(1,100)}, + #{single_line=>false,template=>[mymeta,"\n"]}), + ct:log(String6), + [_,_|_] = string:lexemes(String6,"\n"), + ok. template(_Config) -> -- cgit v1.2.3 From 6fb0f918bf1c32d4862b90015c7e18a384fa25f2 Mon Sep 17 00:00:00 2001 From: tarabit190 Date: Fri, 17 Aug 2018 22:00:32 +0800 Subject: Fix kernel_app doc logger_level default from info to notice The doc says the default kernel logger_level is info, it should be notice. Please refer to commit 0d52b992c30eff8604e5a5363510fea38c712263 for more info. --- lib/kernel/doc/src/logger_chapter.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 30172f6ca6..d58c4a4d42 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -507,7 +507,7 @@ logger:debug(#{got => connection_request, id => Id, state => State}, logger_level. It is changed during runtime with logger:set_primary_config(level,Level).

    -

    Defaults to info.

    +

    Defaults to notice.

    filters = [{FilterId,Filter}] -- cgit v1.2.3 From 64853dc28ce838583e35d5fefb0604933b6e98f9 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Fri, 6 Jul 2018 12:55:10 +0200 Subject: Implement socket option recvtos and friends Implement socket options recvtclass, recvtos, recvttl and pktoptions. Document the implemented socket options, new types and message formats. The options recvtclass, recvtos and recvttl are boolean options that when activated (true) for a socket will cause ancillary data to be received through recvmsg(). That is for packet oriented sockets (UDP and SCTP). The required options for this feature were recvtclass and recvtos, and recvttl was only added to test that the ancillary data parsing handled multiple data items in one message correctly. These options does not work on Windows since ancillary data is not handled by the Winsock2 API. For stream sockets (TCP) there is no clear connection between a received packet and what is returned when reading data from the socket, so recvmsg() is not useful. It is possible to get the same ancillary data through a getsockopt() call with the IPv6 socket option IPV6_PKTOPTIONS, on Linux named IPV6_2292PKTOPTIONS after the now obsoleted RFC where it originated. (unfortunately RFC 3542 that obsoletes it explicitly undefines this way to get packet ancillary data from a stream socket) Linux also has got a way to get packet ancillary data for IPv4 TCP sockets through a getsockopt() call with IP_PKTOPTIONS, which appears to be Linux specific. This implementation uses a flag field in the inet_drv.c socket internal data that records if any setsockopt() call with recvtclass, recvtos or recvttl (IPV6_RECVTCLASS, IP_RECVTOS or IP_RECVTTL) has been activated. If so recvmsg() is used instead of recvfrom(). Ancillary data is delivered to the application by a new return tuple format from gen_udp:recv/2,3 containing a list of ancillary data tuples [{tclass,TCLASS} | {tos,TOS} | {ttl,TTL}], as returned by recvmsg(). For a socket in active mode a new message format, containing the ancillary data list, delivers the data in the same way. For gen_sctp the ancillary data is delivered in the same way, except that the gen_sctp return tuple format already contained an ancillary data list so there are just more possible elements when using these socket options. Note that the active mode message format has got an extra tuple level for the ancillary data compared to what is now implemented gen_udp. The gen_sctp active mode format was considered to be the odd one - now all tuples containing ancillary data are flat, except for gen_sctp active mode. Note that testing has not shown that Linux SCTP sockets deliver any ancillary data for these socket options, so it is probably not implemented yet. Remains to be seen what FreeBSD does... For gen_tcp inet:getopts([pktoptions]) will deliver the latest received ancillary data for any activated socket option recvtclass, recvtos or recvttl, on platforms where IP_PKTOPTIONS is defined for an IPv4 socket, or where IPV6_PKTOPTIONS or IPV6_2292PKTOPTIONS is defined for an IPv6 socket. It will be delivered as a list of ancillary data items in the same way as for gen_udp (and gen_sctp). On some platforms, e.g the BSD:s, when you activate IP_RECVTOS you get ancillary data tagged IP_RECVTOS with the TOS value, but on Linux you get ancillary data tagged IP_TOS with the TOS value. Linux follows the style of RFC 2292, and the BSD:s use an older notion. For RFC 2292 that defines the IP_PKTOPTIONS socket option it is more logical to tag the items with the tag that is the item's, than with the tag that defines that you want the item. Therefore this implementation translates all BSD style ancillary data tags to the corresponding Linux style data tags, so the application will only see the tags 'tclass', 'tos' and 'ttl' on all platforms. --- lib/kernel/doc/src/gen_sctp.xml | 69 ++++++++++++++++-- lib/kernel/doc/src/gen_tcp.xml | 16 ++++- lib/kernel/doc/src/gen_udp.xml | 35 ++++++++-- lib/kernel/doc/src/inet.xml | 123 ++++++++++++++++++++++++++++++++- lib/kernel/src/gen_sctp.erl | 18 +++-- lib/kernel/src/gen_tcp.erl | 16 ++++- lib/kernel/src/gen_udp.erl | 19 ++++- lib/kernel/src/inet.erl | 19 +++-- lib/kernel/src/inet6_tcp.erl | 8 ++- lib/kernel/src/inet_int.hrl | 7 +- lib/kernel/src/inet_tcp.erl | 8 ++- lib/kernel/test/gen_tcp_misc_SUITE.erl | 2 +- 12 files changed, 307 insertions(+), 33 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml index 737800c6b1..1e08b25f66 100644 --- a/lib/kernel/doc/src/gen_sctp.xml +++ b/lib/kernel/doc/src/gen_sctp.xml @@ -4,7 +4,7 @@
    - 20072016 + 20072018 Ericsson AB. All Rights Reserved. @@ -331,6 +331,37 @@ connect(Socket, Ip, Port>, with SockType seqpacket, and with reasonably large kernel and driver buffers.

    +

    + If the socket is in + passive + mode data can be received through the + recv/1,2 + calls. +

    +

    + If the socket is in + active + mode data received data is delivered to the controlling process + as messages: +

    + +{sctp, Socket, FromIP, FromPort, {AncData, Data}} + +

    + See + recv/1,2 + for a description of the message fields. +

    + +

    + This message format unfortunately differs slightly from the + gen_udp + message format with ancillary data, + and from the + recv/1,2 + return tuple format. +

    +
    @@ -380,6 +411,19 @@ connect(Socket, Ip, Port>, socket option sctp_get_peer_addr_info, but this does still not produce the stream number).

    +

    + AncData may also contain + + ancillary data + + from the socket + options + recvtos, + recvtclass + or + recvttl, + if that is supported by the platform for the socket. +

    The Data received can be a binary() or a list() of bytes (integers in the range 0 through 255) depending on the socket mode, or an SCTP event.

    @@ -544,12 +588,25 @@ connect(Socket, Ip, Port>, recv call to retrieve the available data from the socket.

    + +

    + If true|once|N (active modes) + received data or events are sent to the owning process. + See open/0..2 + for the message format. +

    +
    -

    If true (full active mode), the pending data or events are - sent to the owning process.

    -

    Notice that this can cause the message queue to overflow, - as there is no way to throttle the sender in this case - (no flow control).

    +

    + If true (full active mode) there is no flow control. +

    + +

    + Note that this can cause the message queue to overflow + causing for example the virtual machine + to run out of memory and crash. +

    +

    If once, only one message is automatically placed diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index e6104b0c76..cf649991d0 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -4,7 +4,7 @@

    - 19972017 + 19972018 Ericsson AB. All Rights Reserved. @@ -69,6 +69,20 @@ do_recv(Sock, Bs) -> + + + +

    + If the platform implements the IPv4 option + IP_PKTOPTIONS (probably Linux specific), or the IPv6 option + IPV6_PKTOPTIONS or IPV6_2292PKTOPTIONS for the socket + this value is returned from + inet:getopts/2 + when called with the option name + pktoptions. +

    +
    +
    diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml index f79566ef71..840ca3c188 100644 --- a/lib/kernel/doc/src/gen_udp.xml +++ b/lib/kernel/doc/src/gen_udp.xml @@ -4,7 +4,7 @@
    - 19972016 + 19972018 Ericsson AB. All Rights Reserved. @@ -147,7 +147,21 @@ at the opened port, if the socket is in an active mode, the packets are delivered as messages to the controlling process:

    -{udp, Socket, IP, InPortNo, Packet} +{udp, Socket, IP, InPortNo, Packet} % Without ancillary data +{udp, Socket, IP, InPortNo, AncData, Packet} % With ancillary data + +

    + The message contains an AncData field + if any of the socket + options + recvtos, + recvtclass + or + recvttl + are active, otherwise it does not. +

    +

    +

    If the socket is not in an active mode, data can be retrieved through the recv/2,3 calls. @@ -179,9 +193,22 @@ Receive a packet from a passive socket. -

    Receives a packet from a socket in passive mode. Optional parameter +

    + Receives a packet from a socket in passive mode. Optional parameter Timeout specifies a time-out in milliseconds. - Defaults to infinity.

    + Defaults to infinity. +

    +

    + If any of the socket + options + recvtos, + recvtclass + or + recvttl + are active, the RecvData tuple contains an + AncData field, + otherwise it does not. +

    diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index f281d61459..ed775d67eb 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -176,6 +176,27 @@ fe80::204:acff:fe17:bf38

    + + + +

    + Ancillary data received with the data packet + or read with the socket option + + pktoptions + + from a TCP socket. +

    +

    + The value(s) correspond to the currently active socket + options + recvtos, + recvtclass + and + recvttl. +

    +
    +
    @@ -344,7 +365,11 @@ fe80::204:acff:fe17:bf38

    Gets one or more options for a socket. For a list of available options, see - setopts/2.

    + setopts/2. + See also the description for the type + + gen_tcp:pktoptions_value() + .

    The number of elements in the returned OptionValues list does not necessarily correspond to the number of options @@ -360,7 +385,7 @@ fe80::204:acff:fe17:bf38 socket options not (explicitly) supported by the emulator. The use of raw socket options makes the code non-portable, but allows the Erlang programmer to take advantage of unusual features - present on the current platform.

    + present on a particular platform.

    RawOptReq consists of tag raw followed by the protocol level, the option number, and either a binary or the size, in bytes, of the @@ -1115,6 +1140,100 @@ setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp the socket. You are encouraged to use getopts/2 to retrieve the size set by your operating system.

    + + + {recvtclass, Boolean} + +

    + If set to true activates returning the received + TCLASS value on platforms that implements + the protocol IPPROTO_IPV6 + option IPV6_RECVTCLASS or IPV6_2292RECVTCLASS + for the socket. + The value is returned as a {tclass,TCLASS} tuple + regardless of if the platform returns an IPV6_TCLASS + or an IPV6_RECVTCLASS CMSG value. +

    +

    + For packet oriented sockets that supports receiving + ancillary data with the payload data + (gen_udp and gen_sctp), + the TCLASS value is returned + in an extended return tuple contained in an + + ancillary data + + list. + For stream oriented sockets (gen_tcp) + the only way to get the TCLASS + value is if the platform supports the + + pktoptions + + option. +

    + +
    + {recvtos, Boolean} + +

    + If set to true activates returning the received + TOS value on platforms that implements + the protocol IPPROTO_IP option IP_RECVTOS + for the socket. + The value is returned as a {tos,TOS} tuple + regardless of if the platform returns an IP_TOS + or an IP_RECVTOS CMSG value. +

    +

    + For packet oriented sockets that supports receiving + ancillary data with the payload data + (gen_udp and gen_sctp), + the TOS value is returned + in an extended return tuple contained in an + + ancillary data + + list. + For stream oriented sockets (gen_tcp) + the only way to get the TOS + value is if the platform supports the + + pktoptions + + option. +

    + +
    + {recvttl, Boolean} + +

    + If set to true activates returning the received + TTL value on platforms that implements + the protocol IPPROTO_IP option IP_RECVTTL + for the socket. + The value is returned as a {ttl,TTL} tuple + regardless of if the platform returns an IP_TTL + or an IP_RECVTTL CMSG value. +

    +

    + For packet oriented sockets that supports receiving + ancillary data with the payload data + (gen_udp and gen_sctp), + the TTL value is returned + in an extended return tuple contained in an + + ancillary data + + list. + For stream oriented sockets (gen_tcp) + the only way to get the TTL + value is if the platform supports the + + pktoptions + + option. +

    {reuseaddr, Boolean} diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl index bf795ee9c6..d893d44079 100644 --- a/lib/kernel/src/gen_sctp.erl +++ b/lib/kernel/src/gen_sctp.erl @@ -66,7 +66,12 @@ {sctp_set_peer_primary_addr, #sctp_setpeerprim{}} | {sctp_status, #sctp_status{}} | {sndbuf, non_neg_integer()} | - {tos, non_neg_integer()}. + {tos, non_neg_integer()} | + {tclass, non_neg_integer()} | + {ttl, non_neg_integer()} | + {recvtos, boolean()} | + {recvtclass, boolean()} | + {recvttl, boolean()}. -type option_name() :: active | buffer | @@ -97,7 +102,12 @@ sctp_set_peer_primary_addr | sctp_status | sndbuf | - tos. + tos | + tclass | + ttl | + recvtos | + recvtclass | + recvttl. -type sctp_socket() :: port(). -export_type([assoc_id/0, option/0, option_name/0, sctp_socket/0]). @@ -365,7 +375,7 @@ send(S, AssocChange, Stream, Data) -> Socket :: sctp_socket(), FromIP :: inet:ip_address(), FromPort :: inet:port_number(), - AncData :: [#sctp_sndrcvinfo{}], + AncData :: [#sctp_sndrcvinfo{} | inet:ancillary_data()], Data :: binary() | string() | #sctp_sndrcvinfo{} | #sctp_assoc_change{} | #sctp_paddr_change{} | #sctp_adaptation_event{}, @@ -382,7 +392,7 @@ recv(S) -> Timeout :: timeout(), FromIP :: inet:ip_address(), FromPort :: inet:port_number(), - AncData :: [#sctp_sndrcvinfo{}], + AncData :: [#sctp_sndrcvinfo{} | inet:ancillary_data()], Data :: binary() | string() | #sctp_sndrcvinfo{} | #sctp_assoc_change{} | #sctp_paddr_change{} | #sctp_adaptation_event{}, diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl index c61411e814..7f7833ec23 100644 --- a/lib/kernel/src/gen_tcp.erl +++ b/lib/kernel/src/gen_tcp.erl @@ -62,7 +62,14 @@ {show_econnreset, boolean()} | {sndbuf, non_neg_integer()} | {tos, non_neg_integer()} | + {tclass, non_neg_integer()} | + {ttl, non_neg_integer()} | + {recvtos, boolean()} | + {recvtclass, boolean()} | + {recvttl, boolean()} | {ipv6_v6only, boolean()}. +-type pktoptions_value() :: + {pktoptions, inet:ancillary_data()}. -type option_name() :: active | buffer | @@ -81,6 +88,7 @@ nodelay | packet | packet_size | + pktoptions | priority | {raw, Protocol :: non_neg_integer(), @@ -94,6 +102,12 @@ show_econnreset | sndbuf | tos | + tclass | + ttl | + recvtos | + recvtclass | + recvttl | + pktoptions | ipv6_v6only. -type connect_option() :: {ip, inet:socket_address()} | @@ -119,7 +133,7 @@ -type socket() :: port(). -export_type([option/0, option_name/0, connect_option/0, listen_option/0, - socket/0]). + socket/0, pktoptions_value/0]). %% %% Connect a socket diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl index 44eef9f3c5..d6e8652e77 100644 --- a/lib/kernel/src/gen_udp.erl +++ b/lib/kernel/src/gen_udp.erl @@ -51,6 +51,11 @@ {reuseaddr, boolean()} | {sndbuf, non_neg_integer()} | {tos, non_neg_integer()} | + {tclass, non_neg_integer()} | + {ttl, non_neg_integer()} | + {recvtos, boolean()} | + {recvtclass, boolean()} | + {recvttl, boolean()} | {ipv6_v6only, boolean()}. -type option_name() :: active | @@ -76,6 +81,12 @@ reuseaddr | sndbuf | tos | + tclass | + ttl | + recvtos | + recvtclass | + recvttl | + pktoptions | ipv6_v6only. -type socket() :: port(). @@ -147,11 +158,13 @@ send(S, Packet) when is_port(S) -> end. -spec recv(Socket, Length) -> - {ok, {Address, Port, Packet}} | {error, Reason} when + {ok, RecvData} | {error, Reason} when Socket :: socket(), Length :: non_neg_integer(), + RecvData :: {Address, Port, Packet} | {Address, Port, AncData, Packet}, Address :: inet:ip_address() | inet:returned_non_ip_address(), Port :: inet:port_number(), + AncData :: inet:ancillary_data(), Packet :: string() | binary(), Reason :: not_owner | inet:posix(). @@ -164,12 +177,14 @@ recv(S,Len) when is_port(S), is_integer(Len) -> end. -spec recv(Socket, Length, Timeout) -> - {ok, {Address, Port, Packet}} | {error, Reason} when + {ok, RecvData} | {error, Reason} when Socket :: socket(), Length :: non_neg_integer(), Timeout :: timeout(), + RecvData :: {Address, Port, Packet} | {Address, Port, AncData, Packet}, Address :: inet:ip_address() | inet:returned_non_ip_address(), Port :: inet:port_number(), + AncData :: inet:ancillary_data(), Packet :: string() | binary(), Reason :: not_owner | inet:posix(). diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 73c53b9011..75dc909e3d 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -76,7 +76,7 @@ -export_type([address_family/0, socket_protocol/0, hostent/0, hostname/0, ip4_address/0, ip6_address/0, ip_address/0, port_number/0, local_address/0, socket_address/0, returned_non_ip_address/0, - socket_setopt/0, socket_getopt/0, + socket_setopt/0, socket_getopt/0, ancillary_data/0, posix/0, socket/0, stat_option/0]). %% imports -import(lists, [append/1, duplicate/2, filter/2, foldl/3]). @@ -163,6 +163,11 @@ 'recv_cnt' | 'recv_max' | 'recv_avg' | 'recv_oct' | 'recv_dvi' | 'send_cnt' | 'send_max' | 'send_avg' | 'send_oct' | 'send_pend'. +-type ancillary_data() :: + [ {'tos', TOS :: byte()} | + {'tclass', TCLASS :: byte()} | + {'ttl', TTL :: byte()} ]. + %%% --------------------------------- -spec get_rc() -> [{Par :: atom(), Val :: any()} | @@ -302,7 +307,7 @@ setopts(Socket, Opts) -> {'ok', OptionValues} | {'error', posix()} when Socket :: socket(), Options :: [socket_getopt()], - OptionValues :: [socket_setopt()]. + OptionValues :: [socket_setopt() | gen_tcp:pktoptions_value()]. getopts(Socket, Opts) -> case prim_inet:getopts(Socket, Opts) of @@ -722,6 +727,7 @@ stats() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% connect_options() -> [tos, tclass, priority, reuseaddr, keepalive, linger, sndbuf, recbuf, nodelay, + recvtos, recvtclass, ttl, recvttl, header, active, packet, packet_size, buffer, mode, deliver, line_delimiter, exit_on_close, high_watermark, low_watermark, high_msgq_watermark, low_msgq_watermark, send_timeout, send_timeout_close, delay_send, raw, @@ -790,6 +796,7 @@ con_add(Name, Val, #connect_opts{} = R, Opts, AllOpts) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% listen_options() -> [tos, tclass, priority, reuseaddr, keepalive, linger, sndbuf, recbuf, nodelay, + recvtos, recvtclass, ttl, recvttl, header, active, packet, buffer, mode, deliver, backlog, ipv6_v6only, exit_on_close, high_watermark, low_watermark, high_msgq_watermark, low_msgq_watermark, send_timeout, send_timeout_close, delay_send, @@ -870,7 +877,7 @@ tcp_module_1(Opts, Address) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% udp_options() -> [tos, tclass, priority, reuseaddr, sndbuf, recbuf, header, active, buffer, mode, - deliver, ipv6_v6only, + recvtos, recvtclass, ttl, recvttl, deliver, ipv6_v6only, broadcast, dontroute, multicast_if, multicast_ttl, multicast_loop, add_membership, drop_membership, read_packets,raw, high_msgq_watermark, low_msgq_watermark, bind_to_device]. @@ -940,8 +947,10 @@ udp_module(Opts) -> % (*) passing of open FDs ("fdopen") is not supported. sctp_options() -> [ % The following are generic inet options supported for SCTP sockets: - mode, active, buffer, tos, tclass, priority, dontroute, reuseaddr, linger, sndbuf, - recbuf, ipv6_v6only, high_msgq_watermark, low_msgq_watermark, + mode, active, buffer, tos, tclass, ttl, + priority, dontroute, reuseaddr, linger, + recvtos, recvtclass, recvttl, + sndbuf, recbuf, ipv6_v6only, high_msgq_watermark, low_msgq_watermark, bind_to_device, % Other options are SCTP-specific (though they may be similar to their diff --git a/lib/kernel/src/inet6_tcp.erl b/lib/kernel/src/inet6_tcp.erl index a0d5d3df70..347b8b9a1b 100644 --- a/lib/kernel/src/inet6_tcp.erl +++ b/lib/kernel/src/inet6_tcp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-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. @@ -167,7 +167,7 @@ listen(Port, Opts) -> %% Accept %% accept(L) -> - case prim_inet:accept(L) of + case prim_inet:accept(L, accept_family_opts()) of {ok, S} -> inet_db:register_socket(S, ?MODULE), {ok,S}; @@ -175,13 +175,15 @@ accept(L) -> end. accept(L, Timeout) -> - case prim_inet:accept(L, Timeout) of + case prim_inet:accept(L, Timeout, accept_family_opts()) of {ok, S} -> inet_db:register_socket(S, ?MODULE), {ok,S}; Error -> Error end. +accept_family_opts() -> [tclass, recvtclass]. + %% %% Create a port/socket from a file descriptor %% diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl index 357e27826c..c8e09d18ad 100644 --- a/lib/kernel/src/inet_int.hrl +++ b/lib/kernel/src/inet_int.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2017. All Rights Reserved. +%% Copyright Ericsson AB 1997-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. @@ -157,6 +157,11 @@ -define(INET_LOPT_LINE_DELIM, 40). -define(INET_OPT_TCLASS, 41). -define(INET_OPT_BIND_TO_DEVICE, 42). +-define(INET_OPT_RECVTOS, 43). +-define(INET_OPT_RECVTCLASS, 44). +-define(INET_OPT_PKTOPTIONS, 45). +-define(INET_OPT_TTL, 46). +-define(INET_OPT_RECVTTL, 47). % Specific SCTP options: separate range: -define(SCTP_OPT_RTOINFO, 100). -define(SCTP_OPT_ASSOCINFO, 101). diff --git a/lib/kernel/src/inet_tcp.erl b/lib/kernel/src/inet_tcp.erl index dac6b3119d..f1e3116856 100644 --- a/lib/kernel/src/inet_tcp.erl +++ b/lib/kernel/src/inet_tcp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-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. @@ -164,7 +164,7 @@ listen(Port, Opts) -> %% Accept %% accept(L) -> - case prim_inet:accept(L) of + case prim_inet:accept(L, accept_family_opts()) of {ok, S} -> inet_db:register_socket(S, ?MODULE), {ok,S}; @@ -172,13 +172,15 @@ accept(L) -> end. accept(L, Timeout) -> - case prim_inet:accept(L, Timeout) of + case prim_inet:accept(L, Timeout, accept_family_opts()) of {ok, S} -> inet_db:register_socket(S, ?MODULE), {ok,S}; Error -> Error end. +accept_family_opts() -> [tos, ttl, recvtos, recvttl]. + %% %% Create a port/socket from a file descriptor %% diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index 950f5bea6f..91c89e76dd 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -2205,7 +2205,7 @@ wait_until_accepting(Proc,0) -> exit({timeout_waiting_for_accepting,Proc}); wait_until_accepting(Proc,N) -> case process_info(Proc,current_function) of - {current_function,{prim_inet,accept0,2}} -> + {current_function,{prim_inet,accept0,3}} -> case process_info(Proc,status) of {status,waiting} -> ok; -- cgit v1.2.3 From 6a556ffb979273e84ae00c997cb38086ba9ef2f5 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Wed, 5 Sep 2018 15:38:02 +0200 Subject: Fix documentation due to feedback --- lib/kernel/src/inet.erl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 75dc909e3d..5dd68dc285 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -164,9 +164,7 @@ 'send_cnt' | 'send_max' | 'send_avg' | 'send_oct' | 'send_pend'. -type ancillary_data() :: - [ {'tos', TOS :: byte()} | - {'tclass', TCLASS :: byte()} | - {'ttl', TTL :: byte()} ]. + [ {'tos', byte()} | {'tclass', byte()} | {'ttl', byte()} ]. %%% --------------------------------- -- cgit v1.2.3 From 129ec8648ec42c84e315387538310f0e5eb35d66 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 5 Sep 2018 16:54:48 +0200 Subject: kernel: Fix missing abort_connection in net_kernel --- lib/kernel/src/net_kernel.erl | 74 ++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 33 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index c4e1a0ce1e..6b1cef6106 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -279,24 +279,18 @@ passive_connect_monitor(From, Node) -> ok = monitor_nodes(true,[{node_type,all}]), Reply = case lists:member(Node,nodes([connected])) of true -> - io:format("~p: passive_connect_monitor ~p\n", [self(), ?LINE]), true; _ -> receive {nodeup,Node,_} -> - io:format("~p: passive_connect_monitor ~p\n", [self(), ?LINE]), true after connecttime() -> - io:format("~p: passive_connect_monitor ~p\n", [self(), ?LINE]), false end end, ok = monitor_nodes(false,[{node_type,all}]), - io:format("~p: passive_connect_monitor ~p\n", [self(), ?LINE]), {Pid, Tag} = From, - io:format("~p: passive_connect_monitor ~p\n", [self(), ?LINE]), - erlang:send(Pid, {Tag, Reply}), - io:format("~p: passive_connect_monitor ~p\n", [self(), ?LINE]). + erlang:send(Pid, {Tag, Reply}). %% If the net_kernel isn't running we ignore all requests to the @@ -358,20 +352,34 @@ init({Name, LongOrShortNames, TickT, CleanHalt}) -> {stop, Error} end. - -do_auto_connect(Type, Node, ConnId, WaitForBarred, From, State) -> - ConnLookup = ets:lookup(sys_dist, Node), - - case ConnLookup of +do_auto_connect_1(Node, ConnId, From, State) -> + case ets:lookup(sys_dist, Node) of [#barred_connection{}] -> - case WaitForBarred of - false -> - {reply, false, State}; - true -> + case ConnId of + passive_cnct -> spawn(?MODULE,passive_connect_monitor,[From,Node]), - {noreply, State} + {noreply, State}; + _ -> + erts_internal:abort_connection(Node, ConnId), + {reply, false, State} end; + ConnLookup -> + do_auto_connect_2(Node, ConnId, From, State, ConnLookup) + end. + +do_auto_connect_2(Node, passive_cnct, From, State, ConnLookup) -> + case (catch erts_internal:new_connection(Node)) of + {Nr,_DHandle}=ConnId when is_integer(Nr) -> + do_auto_connect_2(Node, ConnId, From, State, ConnLookup); + + _Error -> + error_logger:error_msg("~n** Cannot get connection id for node ~w~n", + [Node]), + {reply, false, State} + end; +do_auto_connect_2(Node, ConnId, From, State, ConnLookup) -> + case ConnLookup of [#connection{conn_id=ConnId, state = up}] -> {reply, true, State}; [#connection{conn_id=ConnId, waiting=Waiting}=Conn] -> @@ -385,6 +393,7 @@ do_auto_connect(Type, Node, ConnId, WaitForBarred, From, State) -> case application:get_env(kernel, dist_auto_connect) of {ok, never} -> ?connect_failure(Node,{dist_auto_connect,never}), + erts_internal:abort_connection(Node, ConnId), {reply, false, State}; %% This might happen due to connection close @@ -394,14 +403,16 @@ do_auto_connect(Type, Node, ConnId, WaitForBarred, From, State) -> (hd(ConnLookup))#connection.state =:= up -> ?connect_failure(Node,{barred_connection, ets:lookup(sys_dist, Node)}), + erts_internal:abort_connection(Node, ConnId), {reply, false, State}; _ -> - case setup(ConnLookup, Node,ConnId,Type,From,State) of + case setup(ConnLookup, Node,ConnId,normal,From,State) of {ok, SetupPid} -> Owners = [{SetupPid, Node} | State#state.conn_owners], {noreply,State#state{conn_owners=Owners}}; _Error -> ?connect_failure(Node, {setup_call, failed, _Error}), + erts_internal:abort_connection(Node, ConnId), {reply, false, State} end end @@ -454,18 +465,7 @@ handle_call({passive_cnct, Node}, From, State) when Node =:= node() -> async_reply({reply, true, State}, From); handle_call({passive_cnct, Node}, From, State) -> verbose({passive_cnct, Node}, 1, State), - Type = normal, - WaitForBarred = true, - R = case (catch erts_internal:new_connection(Node)) of - {Nr,_DHandle}=ConnId when is_integer(Nr) -> - do_auto_connect(Type, Node, ConnId, WaitForBarred, From, State); - - _Error -> - error_logger:error_msg("~n** Cannot get connection id for node ~w~n", - [Node]), - {reply, false, State} - end, - + R = do_auto_connect_1(Node, passive_cnct, From, State), return_call(R, From); %% @@ -479,7 +479,16 @@ handle_call({connect, Type, Node}, From, State) -> ConnLookup = ets:lookup(sys_dist, Node), R = case (catch erts_internal:new_connection(Node)) of {Nr,_DHandle}=ConnId when is_integer(Nr) -> - do_explicit_connect(ConnLookup, Type, Node, ConnId, From, State); + R1 = do_explicit_connect(ConnLookup, Type, Node, ConnId, From, State), + case R1 of + {reply, true, _S} -> %% already connected + ok; + {noreply, _S} -> %% connection pending + ok; + {reply, false, _S} -> %% connection refused + erts_internal:abort_connection(Node, ConnId) + end, + R1; _Error -> error_logger:error_msg("~n** Cannot get connection id for node ~w~n", @@ -703,7 +712,7 @@ handle_info({auto_connect,Node, Nr, DHandle}, State) -> verbose({auto_connect, Node, Nr, DHandle}, 1, State), ConnId = {Nr, DHandle}, NewState = - case do_auto_connect(normal, Node, ConnId, false, noreply, State) of + case do_auto_connect_1(Node, ConnId, noreply, State) of {noreply, S} -> %% Pending connection S; @@ -711,7 +720,6 @@ handle_info({auto_connect,Node, Nr, DHandle}, State) -> S; {reply, false, S} -> %% Connection refused - erts_internal:abort_connection(Node, ConnId), S end, {noreply, NewState}; -- cgit v1.2.3 From 245a3e53b3d8f324b82ab56f06f8df3cf580f860 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Wed, 5 Sep 2018 20:53:24 +0200 Subject: Update version numbers --- lib/kernel/vsn.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index aa8e4dc119..fe22e2af98 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 6.0 +KERNEL_VSN = 6.0.1 -- cgit v1.2.3 From 12d2c65ed477e9fde9a411727de4cc67c53b1a1c Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Wed, 5 Sep 2018 20:53:36 +0200 Subject: Update release notes --- lib/kernel/doc/src/notes.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index e1ef8ab387..f3834d1c1c 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,23 @@

    This document describes the changes made to the Kernel application.

    +
    Kernel 6.0.1 + +
    Fixed Bugs and Malfunctions + + +

    + Fixed bug in net_kernel that could cause an + emulator crash if certain connection attempts failed. Bug + exists since kernel-6.0 (OTP-21.0).

    +

    + Own Id: OTP-15280 Aux Id: ERIERL-226, OTP-15279

    +
    +
    +
    + +
    +
    Kernel 6.0
    Fixed Bugs and Malfunctions -- cgit v1.2.3 From b6289e914c67bd786da829503b144c039303bfd3 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 14 Aug 2018 11:43:18 +0200 Subject: [logger] Refactor some logger internals --- lib/kernel/src/logger.erl | 7 +--- lib/kernel/src/logger_backend.erl | 2 +- lib/kernel/src/logger_config.erl | 23 ++--------- lib/kernel/src/logger_disk_log_h.erl | 5 +-- lib/kernel/src/logger_server.erl | 74 +++++++++++++++++------------------- lib/kernel/src/logger_std_h.erl | 5 +-- lib/kernel/test/logger_SUITE.erl | 2 +- 7 files changed, 44 insertions(+), 74 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 0020fe220b..7b1377b8ca 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -434,12 +434,7 @@ get_primary_config() -> HandlerId :: handler_id(), Config :: handler_config(). get_handler_config(HandlerId) -> - case logger_config:get(?LOGGER_TABLE,HandlerId) of - {ok,{_,Config}} -> - {ok,Config}; - Error -> - Error - end. + logger_config:get(?LOGGER_TABLE,HandlerId). -spec get_handler_config() -> [Config] when Config :: handler_config(). diff --git a/lib/kernel/src/logger_backend.erl b/lib/kernel/src/logger_backend.erl index 4d7bd6b2a0..432c671afd 100644 --- a/lib/kernel/src/logger_backend.erl +++ b/lib/kernel/src/logger_backend.erl @@ -41,7 +41,7 @@ log_allowed(Log, Tid) -> call_handlers(#{level:=Level}=Log,[Id|Handlers],Tid) -> case logger_config:get(Tid,Id,Level) of - {ok,{Module,Config}} -> + {ok,#{module:=Module}=Config} -> Filters = maps:get(filters,Config,[]), case apply_filters(Id,Log,Filters,Config) of stop -> diff --git a/lib/kernel/src/logger_config.erl b/lib/kernel/src/logger_config.erl index 55427dce5a..6bfe658552 100644 --- a/lib/kernel/src/logger_config.erl +++ b/lib/kernel/src/logger_config.erl @@ -22,8 +22,8 @@ -export([new/1,delete/2, exist/2, allow/2,allow/3, - get/2, get/3, get/1, - create/3, create/4, set/3, + get/2, get/3, + create/3, set/3, set_module_level/3,unset_module_level/2, get_module_level/1,cache_module_level/2, level_to_int/1]). @@ -64,27 +64,19 @@ get(Tid,What) -> case ets:lookup(Tid,table_key(What)) of [{_,_,Config}] -> {ok,Config}; - [{_,_,Config,Module}] -> - {ok,{Module,Config}}; [] -> {error,{not_found,What}} end. get(Tid,What,Level) -> - MS = [{{table_key(What),'$1','$2'}, % primary config + MS = [{{table_key(What),'$1','$2'}, [{'>=','$1',level_to_int(Level)}], - ['$2']}, - {{table_key(What),'$1','$2','$3'}, % handler config - [{'>=','$1',level_to_int(Level)}], - [{{'$3','$2'}}]}], + ['$2']}], case ets:select(Tid,MS) of [] -> error; [Data] -> {ok,Data} end. -create(Tid,What,Module,Config) -> - LevelInt = level_to_int(maps:get(level,Config)), - ets:insert(Tid,{table_key(What),LevelInt,Config,Module}). create(Tid,What,Config) -> LevelInt = level_to_int(maps:get(level,Config)), ets:insert(Tid,{table_key(What),LevelInt,Config}). @@ -129,13 +121,6 @@ cache_module_level(Tid,Module) -> ets:insert_new(Tid,{Module,{GlobalLevelInt,cached}}), ok. -get(Tid) -> - {ok,Primary} = get(Tid,primary), - HMS = [{{table_key('$1'),'_','$2','$3'},[],[{{'$1','$3','$2'}}]}], - Handlers = ets:select(Tid,HMS), - Modules = get_module_level(Tid), - {Primary,Handlers,Modules}. - level_to_int(none) -> ?LOG_NONE; level_to_int(emergency) -> ?EMERGENCY; level_to_int(alert) -> ?ALERT; diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index e56531c3cb..3c12c9e68b 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -294,7 +294,7 @@ init([Name, %% initial start ok end, - enter_loop(Config1, State1) + gen_server:enter_loop(?MODULE, [], State1) catch _:Error -> unregister(RegName), @@ -307,9 +307,6 @@ init([Name, proc_lib:init_ack(Error) end. -enter_loop(_Config,State) -> - gen_server:enter_loop(?MODULE,[],State). - %% This is the synchronous log event. handle_call({log, Bin}, _From, State) -> {Result,State1} = do_log(Bin, call, State), diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index 644fdd5af2..01ac1b4b50 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -146,7 +146,7 @@ init([]) -> filters=>?DEFAULT_HANDLER_FILTERS}), %% If this fails, then the node should crash {ok,SimpleConfig} = logger_simple_h:adding_handler(SimpleConfig0), - logger_config:create(Tid,simple,logger_simple_h,SimpleConfig), + logger_config:create(Tid,simple,SimpleConfig), {ok, #state{tid=Tid, async_req_queue = queue:new()}}. handle_call({add_handler,Id,Module,HConfig}, From, #state{tid=Tid}=State) -> @@ -165,11 +165,11 @@ handle_call({add_handler,Id,Module,HConfig}, From, #state{tid=Tid}=State) -> %% 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,primary), + logger_config:create(Tid,Id,HConfig1), + {ok,Config} = logger_config:get(Tid,primary), Handlers = maps:get(handlers,Config,[]), - do_set_config(Tid,primary, - Config#{handlers=>[Id|Handlers]}); + logger_config:set(Tid,primary, + Config#{handlers=>[Id|Handlers]}); false -> {error,{invalid_handler, {function_not_exported, @@ -181,8 +181,8 @@ handle_call({add_handler,Id,Module,HConfig}, From, #state{tid=Tid}=State) -> end; handle_call({remove_handler,HandlerId}, From, #state{tid=Tid}=State) -> case logger_config:get(Tid,HandlerId) of - {ok,{Module,HConfig}} -> - {ok,Config} = do_get_config(Tid,primary), + {ok,#{module:=Module}=HConfig} -> + {ok,Config} = logger_config:get(Tid,primary), Handlers0 = maps:get(handlers,Config,[]), Handlers = lists:delete(HandlerId,Handlers0), call_h_async( @@ -191,7 +191,7 @@ handle_call({remove_handler,HandlerId}, From, #state{tid=Tid}=State) -> call_h(Module,removing_handler,[HConfig],ok) end, fun(_Res) -> - do_set_config(Tid,primary,Config#{handlers=>Handlers}), + logger_config:set(Tid,primary,Config#{handlers=>Handlers}), logger_config:delete(Tid,HandlerId), ok end,From,State); @@ -204,25 +204,35 @@ handle_call({add_filter,Id,Filter}, _From,#state{tid=Tid}=State) -> handle_call({remove_filter,Id,FilterId}, _From, #state{tid=Tid}=State) -> Reply = do_remove_filter(Tid,Id,FilterId), {reply,Reply,State}; -handle_call({update_config,Id,NewConfig}, From, #state{tid=Tid}=State) -> - case logger_config:get(Tid,Id) of - {ok,{_Module,OldConfig}} -> - Config = maps:merge(OldConfig,NewConfig), - handle_call({set_config,Id,Config}, From, State); - {ok,OldConfig} -> +handle_call({update_config,primary,NewConfig}, _From, #state{tid=Tid}=State) -> + {ok,OldConfig} = logger_config:get(Tid,primary), + Config = maps:merge(OldConfig,NewConfig), + {reply,logger_config:set(Tid,primary,Config),State}; +handle_call({update_config,HandlerId,NewConfig}, From, #state{tid=Tid}=State) -> + case logger_config:get(Tid,HandlerId) of + {ok,#{module:=Module}=OldConfig} -> Config = maps:merge(OldConfig,NewConfig), - {reply,do_set_config(Tid,Id,Config),State}; + call_h_async( + fun() -> + call_h(Module,changing_config,[OldConfig,Config], + {ok,Config}) + end, + fun({ok,Config1}) -> + logger_config:set(Tid,HandlerId,Config1); + (Error) -> + Error + end,From,State); Error -> {reply,Error,State} end; handle_call({set_config,primary,Config0}, _From, #state{tid=Tid}=State) -> Config = maps:merge(default_config(primary),Config0), {ok,#{handlers:=Handlers}} = logger_config:get(Tid,primary), - Reply = do_set_config(Tid,primary,Config#{handlers=>Handlers}), + Reply = logger_config:set(Tid,primary,Config#{handlers=>Handlers}), {reply,Reply,State}; handle_call({set_config,HandlerId,Config0}, From, #state{tid=Tid}=State) -> case logger_config:get(Tid,HandlerId) of - {ok,{Module,OldConfig}} -> + {ok,#{module:=Module}=OldConfig} -> Config = maps:merge(default_config(HandlerId,Module),Config0), call_h_async( fun() -> @@ -230,7 +240,7 @@ handle_call({set_config,HandlerId,Config0}, From, #state{tid=Tid}=State) -> {ok,Config}) end, fun({ok,Config1}) -> - do_set_config(Tid,HandlerId,Config1); + logger_config:set(Tid,HandlerId,Config1); (Error) -> Error end,From,State); @@ -241,12 +251,12 @@ handle_call({update_formatter_config,HandlerId,NewFConfig},_From, #state{tid=Tid}=State) -> Reply = case logger_config:get(Tid,HandlerId) of - {ok,{_Mod,#{formatter:={FMod,OldFConfig}}=Config}} -> + {ok,#{formatter:={FMod,OldFConfig}}=Config} -> try FConfig = maps:merge(OldFConfig,NewFConfig), check_formatter({FMod,FConfig}), - do_set_config(Tid,HandlerId, - Config#{formatter=>{FMod,FConfig}}) + logger_config:set(Tid,HandlerId, + Config#{formatter=>{FMod,FConfig}}) catch throw:Reason -> {error,Reason} end; _ -> @@ -316,26 +326,26 @@ call(Request) -> end. do_add_filter(Tid,Id,{FId,_} = Filter) -> - case do_get_config(Tid,Id) of + case logger_config:get(Tid,Id) of {ok,Config} -> Filters = maps:get(filters,Config,[]), case lists:keymember(FId,1,Filters) of true -> {error,{already_exist,FId}}; false -> - do_set_config(Tid,Id,Config#{filters=>[Filter|Filters]}) + logger_config:set(Tid,Id,Config#{filters=>[Filter|Filters]}) end; Error -> Error end. do_remove_filter(Tid,Id,FilterId) -> - case do_get_config(Tid,Id) of + case logger_config:get(Tid,Id) of {ok,Config} -> Filters0 = maps:get(filters,Config,[]), case lists:keytake(FilterId,1,Filters0) of {value,_,Filters} -> - do_set_config(Tid,Id,Config#{filters=>Filters}); + logger_config:set(Tid,Id,Config#{filters=>Filters}); false -> {error,{not_found,FilterId}} end; @@ -343,20 +353,6 @@ do_remove_filter(Tid,Id,FilterId) -> Error end. -do_get_config(Tid,Id) -> - case logger_config:get(Tid,Id) of - {ok,{_,Config}} -> - {ok,Config}; - {ok,Config} -> - {ok,Config}; - Error -> - Error - end. - -do_set_config(Tid,Id,Config) -> - logger_config:set(Tid,Id,Config), - ok. - default_config(primary) -> #{level=>notice, filters=>[], diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index 9a2a1443b3..b60e7cf587 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -259,7 +259,7 @@ init([Name, Config = #{config := HConfig}, mode_tab => ModeTab}}, proc_lib:init_ack({ok,self(),Config1}), gen_server:cast(self(), repeated_filesync), - enter_loop(Config1, State1) + gen_server:enter_loop(?MODULE, [], State1) catch _:Error -> unregister(RegName), @@ -290,9 +290,6 @@ do_init(Name, Type) -> Error end. -enter_loop(_Config,State) -> - gen_server:enter_loop(?MODULE,[],State). - %% This is the synchronous log event. handle_call({log, Bin}, _From, State) -> {Result,State1} = do_log(Bin, call, State), diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index 6bd9b20c35..824c95d6df 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -259,7 +259,7 @@ change_config(_Config) -> logger:get_primary_config(), 3 = maps:size(PC1), %% Check that internal 'handlers' field has not been changed - MS = [{{{?HANDLER_KEY,'$1'},'_','_','_'},[],['$1']}], + MS = [{{{?HANDLER_KEY,'$1'},'_','_'},[],['$1']}], HIds1 = lists:sort(ets:select(?LOGGER_TABLE,MS)), % dirty, internal data HIds2 = lists:sort(logger:get_handler_ids()), HIds1 = HIds2, -- cgit v1.2.3 From 96c1aa0041b368afceef0aef88e82a6c9f8e901d Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 31 Aug 2018 17:08:24 +0200 Subject: [logger] Remove encoding option from logger_formatter The encoding option was introduced in commit 270d909696a753af022df72a404c73f2895b4a02, to allow report callbacks to format according to a given encoding. There was, however, no connection between this encoding option, and the encoding of the device to which the logger handler was writing. Since a formatter is defined to return unicode:chardata(), and in order to avoid mismatch with the encoding of the device, the encoding option is now removed from the formatter. The handler itself must make sure that it does not write illegal data to its device. --- lib/kernel/doc/src/logger_chapter.xml | 11 +++-- lib/kernel/doc/src/logger_formatter.xml | 7 --- lib/kernel/src/logger.erl | 3 +- lib/kernel/src/logger_formatter.erl | 68 ++++++++++++----------------- lib/kernel/src/logger_h_common.erl | 20 ++++++--- lib/kernel/test/logger_disk_log_h_SUITE.erl | 2 +- lib/kernel/test/logger_std_h_SUITE.erl | 2 +- 7 files changed, 51 insertions(+), 62 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index d58c4a4d42..26066d0777 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -208,12 +208,11 @@ coversion to a string:

    fun((logger:report(),logger:report_cb_config()) -> unicode:chardata())
           
    -

    The fun must obey the encoding, depth - and chars_limit parameters provided in the second - argument, as the formatter can not do anything useful of these - parameters with the returned string. This variant is used when - the formatting of the report depends on the size and encoding - parameters.

    +

    The fun must obey the depth and chars_limit + parameters provided in the second argument, as the formatter + can not do anything useful of these parameters with the + returned string. This variant is used when the formatting of + the report depends on the size parameters.

    Example, format string and arguments:

    logger:error("The file does not exist: ~ts",[Filename])

    Example, string:

    diff --git a/lib/kernel/doc/src/logger_formatter.xml b/lib/kernel/doc/src/logger_formatter.xml index 5a060fd42b..24772fd6c4 100644 --- a/lib/kernel/doc/src/logger_formatter.xml +++ b/lib/kernel/doc/src/logger_formatter.xml @@ -82,13 +82,6 @@ in STDLIB.

    Defaults to unlimited.

    - encoding = - unicode:encoding() - -

    This parameter must reflect the encoding of the device - that the handler prints to.

    -

    Defaults to utf8

    -
    legacy_header = boolean()

    If set to true a header field is added to diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 7b1377b8ca..df96367d82 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -77,8 +77,7 @@ -type report() :: map() | [{atom(),term()}]. -type report_cb() :: fun((report()) -> {io:format(),[term()]}) | fun((report(),report_cb_config()) -> unicode:chardata()). --type report_cb_config() :: #{encoding := unicode:encoding(), - depth := pos_integer() | unlimited, +-type report_cb_config() :: #{depth := pos_integer() | unlimited, chars_limit := pos_integer() | unlimited}. -type msg_fun() :: fun((term()) -> {io:format(),[term()]} | report() | diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl index b0d4adc14d..59aa6ec3af 100644 --- a/lib/kernel/src/logger_formatter.erl +++ b/lib/kernel/src/logger_formatter.erl @@ -28,7 +28,6 @@ %%% Types -type config() :: #{chars_limit => pos_integer() | unlimited, depth => pos_integer() | unlimited, - encoding => unicode:encoding(), legacy_header => boolean(), max_size => pos_integer() | unlimited, report_cb => logger:report_cb(), @@ -84,7 +83,7 @@ format(#{level:=Level,msg:=Msg0,meta:=Meta},Config0) true -> "" end, - truncate(B ++ MsgStr ++ A,maps:get(max_size,Config)). + truncate([B,MsgStr,A],maps:get(max_size,Config)). do_format(Level,Data,[level|Format],Config) -> [to_string(level,Level,Config)|do_format(Level,Data,Format,Config)]; @@ -147,7 +146,7 @@ printable_list(X) -> io_lib:printable_list(X). format_msg({string,Chardata},Meta,Config) -> - format_msg({s(Config),[Chardata]},Meta,Config); + format_msg({"~ts",[Chardata]},Meta,Config); format_msg({report,_}=Msg,Meta,#{report_cb:=Fun}=Config) when is_function(Fun,1); is_function(Fun,2) -> format_msg(Msg,Meta#{report_cb=>Fun},maps:remove(report_cb,Config)); @@ -166,13 +165,13 @@ format_msg({report,Report},#{report_cb:=Fun}=Meta,Config) when is_function(Fun,1 Meta,Config) end; format_msg({report,Report},#{report_cb:=Fun}=Meta,Config) when is_function(Fun,2) -> - try Fun(Report,maps:with([encoding,depth,chars_limit],Config)) of - String when ?IS_STRING(String) -> - try unicode:characters_to_list(String) + try Fun(Report,maps:with([depth,chars_limit],Config)) of + Chardata when ?IS_STRING(Chardata) -> + try chardata_to_list(Chardata) % already size limited by report_cb catch _:_ -> P = p(Config), format_msg({"REPORT_CB/2 ERROR: "++P++"; Returned: "++P, - [Report,String]},Meta,Config) + [Report,Chardata]},Meta,Config) end; Other -> P = p(Config), @@ -189,28 +188,27 @@ format_msg({report,Report},Meta,Config) -> Meta#{report_cb=>fun logger:format_report/1}, Config); format_msg(Msg,_Meta,#{depth:=Depth,chars_limit:=CharsLimit, - encoding:=Enc,single_line:=Single}) -> + single_line:=Single}) -> Opts = chars_limit_to_opts(CharsLimit), - format_msg(Msg, Depth, Opts, Enc, Single). + format_msg(Msg, Depth, Opts, Single). chars_limit_to_opts(unlimited) -> []; chars_limit_to_opts(CharsLimit) -> [{chars_limit,CharsLimit}]. -format_msg({Format0,Args},Depth,Opts,Enc,Single) -> +format_msg({Format0,Args},Depth,Opts,Single) -> try Format1 = io_lib:scan_format(Format0, Args), Format = reformat(Format1, Depth, Single), io_lib:build_text(Format,Opts) catch C:R:S -> - P = p(Enc,Single), + P = p(Single), FormatError = "FORMAT ERROR: "++P++" - "++P, case Format0 of FormatError -> %% already been here - avoid failing cyclically erlang:raise(C,R,S); _ -> - format_msg({FormatError,[Format0,Args]}, - Depth,Opts,Enc,Single) + format_msg({FormatError,[Format0,Args]},Depth,Opts,Single) end end. @@ -233,6 +231,14 @@ limit_depth(#{control_char:=C0, args:=Args}=M0, Depth) -> C = C0 - ($a - $A), %To uppercase. M0#{control_char:=C,args:=Args++[Depth]}. +chardata_to_list(Chardata) -> + case unicode:characters_to_list(Chardata,unicode) of + List when is_list(List) -> + List; + Error -> + throw(Error) + end. + truncate(String,unlimited) -> String; truncate(String,Size) -> @@ -278,12 +284,11 @@ maybe_add_legacy_header(Level, #{time:=Timestamp}=Meta, #{legacy_header:=true}=Config) -> #{title:=Title}=MyMeta = add_legacy_title(Level,Meta,Config), - {{Y,Mo,D},{H,Mi,Sec},Micro,UtcStr} = + {{Y,Mo,D},{H,Mi,S},Micro,UtcStr} = timestamp_to_datetimemicro(Timestamp,Config), - S = s(Config), Header = - io_lib:format("="++S++"==== ~w-~s-~4w::~2..0w:~2..0w:~2..0w.~6..0w ~s===", - [Title,D,month(Mo),Y,H,Mi,Sec,Micro,UtcStr]), + io_lib:format("=~ts==== ~w-~s-~4w::~2..0w:~2..0w:~2..0w.~6..0w ~s===", + [Title,D,month(Mo),Y,H,Mi,S,Micro,UtcStr]), Meta#{?MODULE=>MyMeta#{header=>Header}}; maybe_add_legacy_header(_,Meta,_) -> Meta. @@ -324,7 +329,6 @@ month(12) -> "Dec". add_default_config(Config0) -> Default = #{chars_limit=>unlimited, - encoding=>utf8, error_logger_notice_header=>info, legacy_header=>false, single_line=>true, @@ -502,25 +506,9 @@ check_timezone(Tz) -> error end. -p(#{encoding:=Enc, single_line:=Single}) -> - p(Enc,Single). - -p(Enc,Single) -> - "~"++p_width(Single)++p_char(Enc). - -p_width(true) -> - "0"; -p_width(false) -> - "". - -p_char(latin1) -> - "p"; -p_char(_) -> - "tp". - -s(#{encoding:=Enc}) -> - s(Enc); -s(latin1) -> - "~s"; -s(_) -> - "~ts". +p(#{single_line:=Single}) -> + p(Single); +p(true) -> + "~0tp"; +p(false) -> + "~tp". diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl index 854e5479b9..2818a460f1 100644 --- a/lib/kernel/src/logger_h_common.erl +++ b/lib/kernel/src/logger_h_common.erl @@ -40,7 +40,7 @@ info_notify/1]). %%%----------------------------------------------------------------- -%%% Covert log data on any form to binary +%%% Convert log data on any form to binary -spec log_to_binary(LogEvent,Config) -> LogString when LogEvent :: logger:log_event(), Config :: logger:handler_config(), @@ -57,13 +57,14 @@ do_log_to_binary(Log,Config) -> {Formatter,FormatterConfig} = maps:get(formatter,Config,{?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}), String = try_format(Log,Formatter,FormatterConfig), - try unicode:characters_to_binary(String) - catch _:_ -> + try string_to_binary(String) + catch C2:R2:S2 -> ?LOG_INTERNAL(debug,[{formatter_error,Formatter}, {config,FormatterConfig}, {log_event,Log}, - {bad_return_value,String}]), - <<"FORMATTER ERROR: bad_return_value">> + {bad_return_value,String}, + {catched,{C2,R2,S2}}]), + <<"FORMATTER ERROR: bad return value">> end. try_format(Log,Formatter,FormatterConfig) -> @@ -85,6 +86,15 @@ try_format(Log,Formatter,FormatterConfig) -> end end. +string_to_binary(String) -> + case unicode:characters_to_binary(String) of + Binary when is_binary(Binary) -> + Binary; + Error -> + throw(Error) + end. + + %%%----------------------------------------------------------------- %%% Check that the configuration term is valid check_common_config({mode_tab,_Tid}) -> diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index a4b15c841b..29624b16c2 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -367,7 +367,7 @@ formatter_fail(Config) -> ok = logger:set_handler_config(Name,formatter,{?MODULE,bad_return}), logger:notice(?msg,?domain), try_match_file(?log_no(LogFile,1), - escape(Got3)++"FORMATTER ERROR: bad_return_value", + escape(Got3)++"FORMATTER ERROR: bad return value", 5000), %% Check that handler is still alive and was never dead diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index 0930cd4211..3426567bbf 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -280,7 +280,7 @@ formatter_fail(Config) -> ok = logger:set_handler_config(?MODULE,formatter,{?MODULE,bad_return}), logger:notice(?msg,?domain), try_match_file(Log, - escape(Got3)++"FORMATTER ERROR: bad_return_value", + escape(Got3)++"FORMATTER ERROR: bad return value", 5000), %% Check that handler is still alive and was never dead -- cgit v1.2.3 From d9e58a29f9194ea8b2d9e1bdcf5b89a9cc29f988 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 4 Sep 2018 14:30:49 +0200 Subject: [logger] Update doc about file options in logger_std_h --- lib/kernel/doc/src/logger_std_h.xml | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger_std_h.xml b/lib/kernel/doc/src/logger_std_h.xml index b526ed037d..fcd8189bae 100644 --- a/lib/kernel/doc/src/logger_std_h.xml +++ b/lib/kernel/doc/src/logger_std_h.xml @@ -59,13 +59,22 @@

    This has the value standard_io, standard_error, {file,LogFileName}, or {file,LogFileName,LogFileOpts}.

    +

    If LogFileOpts is specified, it replaces the default + list of options used when opening the log file. The default + list is [raw,append,delayed_write]. One reason to do + so can be to change append to, for + example, write, ensuring that the old log is + truncated when a node is restarted. See the reference manual + for file:open/2 + for more information about file options.

    +

    Log files are always UTF-8 encoded. The encoding can not be + changed by setting the option {encoding,Encoding} + in LogFileOpts.

    +

    Notice that the standard handler does not have support for + circular logging. Use the disk_log handler, + logger_disk_log_h, + for this.

    Defaults to standard_io.

    -

    It is recommended not to specify LogFileOpts unless absolutely - necessary. The default options used by the handler to open a file for logging are - raw, append, and delayed_write. Notice that the standard - handler does not have support for circular logging. Use the disk_log handler, - logger_disk_log_h, - for this.

    filesync_repeat_interval @@ -73,12 +82,12 @@ a file sync operation to write buffered data to disk. The handler attempts the operation repeatedly, but only performs a new sync if something has actually been logged.

    -

    Defaults to 5000 milliseconds.

    If no_repeat is set as value, the repeated file sync operation is disabled, and it is the operating system settings that determine how quickly or slowly data is written to disk. The user can also call the filesync/1 function to perform a file sync.

    +

    Defaults to 5000 milliseconds.

    Other configuration parameters exist, to be used for customizing -- cgit v1.2.3 From 1832ab7dfb86559af2dfc0518669dcdce4f704ee Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 4 Sep 2018 17:23:08 +0200 Subject: [logger] Fix logger_disk_log_h_SUITE:sync and add som debug info --- lib/kernel/test/logger_disk_log_h_SUITE.erl | 62 +++++++++++++---------------- 1 file changed, 27 insertions(+), 35 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 29624b16c2..ed441f7e65 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -524,27 +524,14 @@ sync(Config) -> formatter=>{?MODULE,nl}}), start_tracer([{disk_log,blog,2}, - {disk_log,sync,1}], + {logger_disk_log_h,disk_log_sync,2}], [{disk_log,blog,<<"first\n">>}, - {disk_log,sync}]), + {logger_disk_log_h,disk_log_sync}]), logger:notice("first", ?domain), %% wait for automatic disk_log_sync check_tracer(?FILESYNC_REPEAT_INTERVAL*2), - start_tracer([{disk_log,blog,2}, - {disk_log,sync,1}], - [{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 - logger:notice("second", ?domain), - logger:notice("third", ?domain), - %% do explicit sync - logger_disk_log_h:filesync(?MODULE), - check_tracer(100), - %% check that if there's no repeated disk_log_sync active, %% a disk_log_sync is still performed when handler goes idle {ok,#{config := HConfig}} = logger:get_handler_config(?MODULE), @@ -553,20 +540,25 @@ sync(Config) -> no_repeat = maps:get(filesync_repeat_interval, logger_disk_log_h:info(?MODULE)), + %% The following timer is to make sure the time from last log + %% ("first") to next ("second") is long enough, so the a flush is + %% triggered by the idle timeout between "fourth" and "fifth". + timer:sleep(?IDLE_DETECT_TIME_MSEC*2), start_tracer([{disk_log,blog,2}, - {disk_log,sync,1}], - [{disk_log,blog,<<"fourth\n">>}, - {disk_log,blog,<<"fifth\n">>}, - {disk_log,sync}]), + {logger_disk_log_h,disk_log_sync,2}], + [{disk_log,blog,<<"second\n">>}, + {logger_disk_log_h,disk_log_sync}, + {disk_log,blog,<<"third\n">>}, + {logger_disk_log_h,disk_log_sync}]), - logger:notice("fourth", ?domain), + logger:notice("second", ?domain), timer:sleep(?IDLE_DETECT_TIME_MSEC*2), - logger:notice("fifth", ?domain), + logger:notice("third", ?domain), %% wait for automatic disk_log_sync check_tracer(?IDLE_DETECT_TIME_MSEC*2), - try_read_file(Log, {ok,<<"first\nsecond\nthird\nfourth\nfifth\n">>}, 1000), + try_read_file(Log, {ok,<<"first\nsecond\nthird\n">>}, 1000), %% switch repeated disk_log_sync on and verify that the looping works SyncInt = 1000, @@ -1494,7 +1486,7 @@ start_tracer(Trace,Expected) -> ok. tpl([{M,F,A}|Trace]) -> - {ok,Match} = dbg:tpl(M,F,A,[]), + {ok,Match} = dbg:tpl(M,F,A,c), case lists:keyfind(matched,1,Match) of {_,_,1} -> ok; @@ -1507,23 +1499,23 @@ tpl([{M,F,A}|Trace]) -> tpl([]) -> ok. -tracer({trace,_,call,{logger_disk_log_h,handle_cast,[Op|_]}}, +tracer({trace,_,call,{logger_disk_log_h,handle_cast,[Op|_]},Caller}, {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]), + maybe_tracer_done(Pid,Expected,{Mod,Func,Op},Caller); +tracer({trace,_,call,{Mod=disk_log,Func=blog,[_,Data]},Caller}, {Pid,[{Mod,Func,Data}|Expected]}) -> + maybe_tracer_done(Pid,Expected,{Mod,Func,Data},Caller); +tracer({trace,_,call,{Mod,Func,_},Caller}, {Pid,[{Mod,Func}|Expected]}) -> + maybe_tracer_done(Pid,Expected,{Mod,Func},Caller); +tracer({trace,_,call,Call,Caller}, {Pid,Expected}) -> + ct:log("Tracer got unexpected: ~p~nCaller: ~p~nExpected: ~p~n",[Call,Caller,Expected]), Pid ! {tracer_got_unexpected,Call,Expected}, {Pid,Expected}. -maybe_tracer_done(Pid,[],Got) -> - ct:log("Tracer got: ~p~n",[Got]), +maybe_tracer_done(Pid,[],Got,Caller) -> + ct:log("Tracer got: ~p~nCaller: ~p~n",[Got,Caller]), Pid ! tracer_done; -maybe_tracer_done(Pid,Expected,Got) -> - ct:log("Tracer got: ~p~n",[Got]), +maybe_tracer_done(Pid,Expected,Got,Caller) -> + ct:log("Tracer got: ~p~nCaller: ~p~n",[Got,Caller]), {Pid,Expected}. check_tracer(T) -> -- cgit v1.2.3 From a9a72da93e9b93a1d7ee9936b8e223545277480e Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 6 Sep 2018 13:51:49 +0200 Subject: [logger] Refactor, and add error detection for configuration --- lib/kernel/doc/src/logger.xml | 2 +- lib/kernel/src/logger.erl | 153 +++++++++++--------- lib/kernel/test/logger_SUITE.erl | 222 +++++++++++++++++++++++++++++- lib/kernel/test/logger_simple_h_SUITE.erl | 3 +- 4 files changed, 310 insertions(+), 70 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index a4d6efa2d8..464c65ba76 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -527,7 +527,7 @@ start(_, []) -> Error -> Error end.

    This reads the logger configuration parameter from - the my_all application and starts the configured + the my_app application and starts the configured handlers. The contents of the configuration use the same rules as the logger handler configuration. diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index df96367d82..0ada8b4924 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -115,6 +115,11 @@ -type config_handler() :: {handler, handler_id(), module(), handler_config()}. +-type config_logger() :: [{handler,default,undefined} | + config_handler() | + {filters,log | stop,[{filter_id(),filter()}]} | + {module_level,level(),[module()]}]. + -export_type([log_event/0, level/0, report/0, @@ -582,19 +587,21 @@ get_config() -> %% tree is started. internal_init_logger() -> try + Env = get_logger_env(kernel), + check_logger_config(kernel,Env), ok = logger:set_primary_config(level, get_logger_level()), - ok = logger:set_primary_config(filter_default, get_primary_filter_default()), + ok = logger:set_primary_config(filter_default, + get_primary_filter_default(Env)), [case logger:add_primary_filter(Id, Filter) of ok -> ok; {error, Reason} -> throw(Reason) - end || {Id, Filter} <- get_primary_filters()], + end || {Id, Filter} <- get_primary_filters(Env)], - _ = [[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_module_level(Modules, Level) of + ok -> ok; + {error, Reason} -> throw(Reason) + end || {module_level, Level, Modules} <- Env], case logger:set_handler_config(simple,filters, get_default_handler_filters()) of @@ -602,24 +609,24 @@ internal_init_logger() -> {error,{not_found,simple}} -> ok end, - init_kernel_handlers() + init_kernel_handlers(Env) catch throw:Reason -> ?LOG_ERROR("Invalid logger config: ~p", [Reason]), {error, {bad_config, {kernel, Reason}}} end. --spec init_kernel_handlers() -> ok | {error,term()}. +-spec init_kernel_handlers(config_logger()) -> 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() -> +init_kernel_handlers(Env) -> try - case get_logger_type() of + case get_logger_type(Env) of {ok,silent} -> ok = logger:remove_handler(simple); {ok,false} -> ok; {ok,Type} -> - init_default_config(Type) + init_default_config(Type,Env) end catch throw:Reason -> ?LOG_ERROR("Invalid default handler config: ~p", [Reason]), @@ -634,10 +641,13 @@ init_kernel_handlers() -> %% 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(App,get_logger_env(App)); add_handlers(HandlerConfig) -> + add_handlers(application:get_application(),HandlerConfig). + +add_handlers(App,HandlerConfig) -> try - check_logger_config(HandlerConfig), + check_logger_config(App,HandlerConfig), DefaultAdded = lists:foldl( fun({handler, default = Id, Module, Config}, _) @@ -651,17 +661,22 @@ add_handlers(HandlerConfig) -> ({handler, Id, Module, Config}, Default) -> setup_handler(Id, Module, Config), Default orelse Id == default; - (_, Default) -> 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(simple) of - ok -> ok; - {error,{not_found,simple}} -> ok - end || DefaultAdded], + [case logger:remove_handler(simple) of + ok -> ok; + {error,{not_found,simple}} -> ok + end || DefaultAdded], ok - catch throw:Reason -> + catch throw:Reason0 -> + Reason = + case App of + undefined -> Reason0; + _ -> {App,Reason0} + end, ?LOG_ERROR("Invalid logger handler config: ~p", [Reason]), {error, {bad_config, {handler, Reason}}} end. @@ -672,26 +687,35 @@ setup_handler(Id, Module, Config) -> {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() -> +check_logger_config(_,[]) -> + ok; +check_logger_config(App,[{handler,_,_,_}|Env]) -> + check_logger_config(App,Env); +check_logger_config(kernel,[{handler,default,undefined}|Env]) -> + check_logger_config(kernel,Env); +check_logger_config(kernel,[{filters,_,_}|Env]) -> + check_logger_config(kernel,Env); +check_logger_config(kernel,[{module_level,_,_}|Env]) -> + check_logger_config(kernel,Env); +check_logger_config(_,Bad) -> + throw(Bad). + +-spec get_logger_type(config_logger()) -> + {ok, standard_io | false | silent | + {file, file:name_all()} | + {file, file:name_all(), [file:mode()]}}. +get_logger_type(Env) -> case application:get_env(kernel, error_logger) of {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, false} -> {ok, false}; {ok, silent} -> {ok, silent}; undefined -> - case lists:member({handler,default,undefined}, get_logger_env()) of + case lists:member({handler,default,undefined}, Env) of true -> {ok, false}; false -> @@ -709,56 +733,55 @@ get_logger_level() -> throw({logger_level, Level}) end. -get_primary_filter_default() -> - case lists:keyfind(filters,1,get_logger_env()) of +get_primary_filter_default(Env) -> + case lists:keyfind(filters,1,Env) of {filters,Default,_} -> Default; false -> log end. -get_primary_filters() -> - lists:foldl( - fun({filters, _, Filters}, _Acc) -> - Filters; - (_, Acc) -> - Acc - end, [], get_logger_env()). +get_primary_filters(Env) -> + case [F || F={filters,_,_} <- Env] of + [{filters,_,Filters}] -> + case lists:all(fun({_,_}) -> true; (_) -> false end,Filters) of + true -> Filters; + false -> throw({invalid_filters,Filters}) + end; + [] -> []; + _ -> throw({multiple_filters,Env}) + end. %% 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(), +init_default_config(Type,Env) when Type==standard_io; + Type==standard_error; + element(1,Type)==file -> DefaultFormatter = #{formatter=>{?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}}, DefaultConfig = DefaultFormatter#{config=>#{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)}; - ({handler, default, logger_disk_log_h, _}) -> - %% Add default formatter. The point of this - %% is to get the expected formatter config - %% for the default handler, since this - %% differs from the default values that - %% logger_formatter itself adds. - {handler, default, logger_disk_log_h, maps:merge(DefaultFormatter,Config)}; - (Other) -> - Other - end, Env); + {handler, default, logger_std_h, Config} -> + %% Only want to add the logger_std_h config + %% if not configured by user AND the default + %% handler is still the logger_std_h. + lists:keyreplace(default, 2, Env, + {handler, default, logger_std_h, + maps:merge(DefaultConfig,Config)}); + {handler, default, Module,Config} -> + %% Add default formatter. The point of this + %% is to get the expected formatter config + %% for the default handler, since this + %% differs from the default values that + %% logger_formatter itself adds. + lists:keyreplace(default, 2, Env, + {handler, default, Module, + maps:merge(DefaultFormatter,Config)}); _ -> %% 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}). + application:set_env(kernel, logger, NewLoggerEnv, [{timeout,infinity}]). get_default_handler_filters() -> case application:get_env(kernel, logger_sasl_compatible, false) of @@ -768,8 +791,8 @@ get_default_handler_filters() -> ?DEFAULT_HANDLER_FILTERS([otp,sasl]) end. -get_logger_env() -> - application:get_env(kernel, logger, []). +get_logger_env(App) -> + application:get_env(App, logger, []). %%%----------------------------------------------------------------- %%% Internal diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index 824c95d6df..49b76f03b2 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -37,7 +37,8 @@ suite() -> - [{timetrap,{seconds,30}}]. + [{timetrap,{seconds,30}}, + {ct_hooks,[logger_test_lib]}]. init_per_suite(Config) -> case logger:get_handler_config(?STANDARD_HANDLER) of @@ -98,7 +99,9 @@ all() -> via_logger_process, other_node, compare_levels, - process_metadata]. + process_metadata, + app_config, + kernel_config]. start_stop(_Config) -> S = whereis(logger), @@ -895,12 +898,227 @@ process_metadata(_Config) -> ok = logger:unset_process_metadata(), undefined = logger:get_process_metadata(), + ok = logger:update_process_metadata(#{custom=>added_again}), + {error,badarg} = ?TRY(logger:update_process_metadata(bad)), + #{custom:=added_again} = logger:get_process_metadata(), + ok. process_metadata(cleanup,_Config) -> logger:remove_handler(h1), ok. +app_config(Config) -> + %% Start a node with default configuration + {ok,_,Node} = logger_test_lib:setup(Config,[]), + + App1Name = app1, + App1 = {application, App1Name, + [{description, "Test of app with logger config"}, + {applications, [kernel]}]}, + ok = rpc:call(Node,application,load,[App1]), + ok = rpc:call(Node,application,set_env, + [App1Name,logger,[{handler,default,logger_std_h,#{}}]]), + + %% Try to add an own default handler + {error,{bad_config,{handler,{app1,{already_exist,default}}}}} = + rpc:call(Node,logger,add_handlers,[App1Name]), + + %% Add a different handler + ok = rpc:call(Node,application,set_env,[App1Name,logger, + [{handler,myh,logger_std_h,#{}}]]), + ok = rpc:call(Node,logger,add_handlers,[App1Name]), + + {ok,#{filters:=DF}} = rpc:call(Node,logger,get_handler_config,[default]), + {ok,#{filters:=[]}} = rpc:call(Node,logger,get_handler_config,[myh]), + + true = test_server:stop_node(Node), + + %% Start a node with no default handler, then add an own default handler + {ok,#{handlers:=[#{id:=simple}]},Node} = + logger_test_lib:setup(Config,[{logger,[{handler,default,undefined}]}]), + + ok = rpc:call(Node,application,load,[App1]), + ok = rpc:call(Node,application,set_env, + [App1Name,logger,[{handler,default,logger_std_h,#{}}]]), + ok = rpc:call(Node,logger,add_handlers,[App1Name]), + + #{handlers:=[#{id:=default,filters:=DF}]} = + rpc:call(Node,logger,get_config,[]), + + true = test_server:stop_node(Node), + + %% Start a silent node, then add an own default handler + {ok,#{handlers:=[]},Node} = + logger_test_lib:setup(Config,[{error_logger,silent}]), + + {error,{bad_config,{handler,[{some,bad,config}]}}} = + rpc:call(Node,logger,add_handlers,[[{some,bad,config}]]), + ok = rpc:call(Node,logger,add_handlers, + [[{handler,default,logger_std_h,#{}}]]), + + #{handlers:=[#{id:=default,filters:=DF}]} = + rpc:call(Node,logger,get_config,[]), + + ok. + +%% This test case is maintly to see code coverage. Note that +%% logger_env_var_SUITE tests a lot of the same, and checks the +%% functionality more thoroughly, but since it all happens at node +%% start, it is not possible to see code coverage in that test. +kernel_config(Config) -> + %% Start a node with simple handler only, then simulate kernel + %% start by calling internally exported + %% internal_init_logger(). This is to test all variants of kernel + %% config, including bad config, and see the code coverage. + {ok,#{handlers:=[#{id:=simple,filters:=DF}]}=LC,Node} = + logger_test_lib:setup(Config,[{error_logger,false}]), + + %% Same once more, to get coverage + ok = rpc:call(Node,logger,internal_init_logger,[]), + ok = rpc:call(Node,logger,add_handlers,[kernel]), + LC = rpc:call(Node,logger,get_config,[]), + + %% This shall mean the same as above, but using 'logger' parameter + %% instead of 'error_logger' + ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), + ok = rpc:call(Node,application,set_env, + [kernel,logger,[{handler,default,undefined}]]), + ok = rpc:call(Node,logger,internal_init_logger,[]), + ok = rpc:call(Node,logger,add_handlers,[kernel]), + LC = rpc:call(Node,logger,get_config,[]), + + %% Silent + ok = rpc:call(Node,application,unset_env,[kernel,logger]), + ok = rpc:call(Node,application,set_env,[kernel,error_logger,silent]), + ok = rpc:call(Node,logger,internal_init_logger,[]), + ok = rpc:call(Node,logger,add_handlers,[kernel]), + #{primary:=#{filter_default:=log,filters:=[]}, + handlers:=[], + module_levels:=[]} = rpc:call(Node,logger,get_config,[]), + + %% Default + ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), + ok = rpc:call(Node,application,unset_env,[kernel,logger]), + ok = rpc:call(Node,logger,internal_init_logger,[]), + ok = rpc:call(Node,logger,add_handlers,[kernel]), + #{primary:=#{filter_default:=log,filters:=[]}, + handlers:=[#{id:=default,filters:=DF,config:=#{type:=standard_io}}], + module_levels:=[]} = rpc:call(Node,logger,get_config,[]), + + %% error_logger=tty (same as default) + ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again + ok = rpc:call(Node,application,set_env,[kernel,error_logger,tty]), + ok = rpc:call(Node,application,unset_env,[kernel,logger]), + ok = rpc:call(Node,logger,internal_init_logger,[]), + ok = rpc:call(Node,logger,add_handlers,[kernel]), + #{primary:=#{filter_default:=log,filters:=[]}, + handlers:=[#{id:=default,filters:=DF,config:=#{type:=standard_io}}], + module_levels:=[]} = rpc:call(Node,logger,get_config,[]), + + %% error_logger={file,File} + ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again + F = filename:join(?config(priv_dir,Config), + atom_to_list(?FUNCTION_NAME)++".log"), + ok = rpc:call(Node,application,set_env,[kernel,error_logger,{file,F}]), + ok = rpc:call(Node,application,unset_env,[kernel,logger]), + ok = rpc:call(Node,logger,internal_init_logger,[]), + ok = rpc:call(Node,logger,add_handlers,[kernel]), + #{primary:=#{filter_default:=log,filters:=[]}, + handlers:=[#{id:=default,filters:=DF,config:=#{type:={file,F}}}], + module_levels:=[]} = rpc:call(Node,logger,get_config,[]), + + %% Same, but using 'logger' parameter instead of 'error_logger' + ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again + ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), + ok = rpc:call(Node,application,set_env,[kernel,logger, + [{handler,default,logger_std_h, + #{config=>#{type=>{file,F}}}}]]), + ok = rpc:call(Node,logger,internal_init_logger,[]), + ok = rpc:call(Node,logger,add_handlers,[kernel]), + #{primary:=#{filter_default:=log,filters:=[]}, + handlers:=[#{id:=default,filters:=DF,config:=#{type:={file,F}}}], + module_levels:=[]} = rpc:call(Node,logger,get_config,[]), + + %% Same, but with type={file,File,Modes} + ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again + ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), + M = [raw,write,delayed_write], + ok = rpc:call(Node,application,set_env,[kernel,logger, + [{handler,default,logger_std_h, + #{config=>#{type=>{file,F,M}}}}]]), + ok = rpc:call(Node,logger,internal_init_logger,[]), + ok = rpc:call(Node,logger,add_handlers,[kernel]), + #{primary:=#{filter_default:=log,filters:=[]}, + handlers:=[#{id:=default,filters:=DF,config:=#{type:={file,F,M}}}], + module_levels:=[]} = rpc:call(Node,logger,get_config,[]), + + %% Same, but with disk_log handler + ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again + ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), + M = [raw,write,delayed_write], + ok = rpc:call(Node,application,set_env,[kernel,logger, + [{handler,default,logger_disk_log_h, + #{config=>#{file=>F}}}]]), + ok = rpc:call(Node,logger,internal_init_logger,[]), + ok = rpc:call(Node,logger,add_handlers,[kernel]), + #{primary:=#{filter_default:=log,filters:=[]}, + handlers:=[#{id:=default,filters:=DF,config:=#{file:=F}}], + module_levels:=[]} = rpc:call(Node,logger,get_config,[]), + + %% Set primary filters and module level. No default handler. + ok = rpc:call(Node,logger,remove_handler,[default]),% so it can be added again + ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), + ok = rpc:call(Node,application,set_env, + [kernel,logger,[{handler,default,undefined}, + {filters,stop,[{f1,{fun(_,_) -> log end,ok}}]}, + {module_level,debug,[?MODULE]}]]), + ok = rpc:call(Node,logger,internal_init_logger,[]), + ok = rpc:call(Node,logger,add_handlers,[kernel]), + #{primary:=#{filter_default:=stop,filters:=[_]}, + handlers:=[], + module_levels:=[{?MODULE,debug}]} = rpc:call(Node,logger,get_config,[]), + + %% Bad config + ok = rpc:call(Node,application,unset_env,[kernel,logger]), + + ok = rpc:call(Node,application,set_env,[kernel,error_logger,bad]), + {error,{bad_config,{kernel,{error_logger,bad}}}} = + rpc:call(Node,logger,internal_init_logger,[]), + + ok = rpc:call(Node,application,unset_env,[kernel,error_logger]), + ok = rpc:call(Node,application,set_env,[kernel,logger_level,bad]), + {error,{bad_config,{kernel,{logger_level,bad}}}} = + rpc:call(Node,logger,internal_init_logger,[]), + + ok = rpc:call(Node,application,unset_env,[kernel,logger_level]), + ok = rpc:call(Node,application,set_env, + [kernel,logger,[{filters,stop,[bad]}]]), + {error,{bad_config,{kernel,{invalid_filters,[bad]}}}} = + rpc:call(Node,logger,internal_init_logger,[]), + + ok = rpc:call(Node,application,set_env, + [kernel,logger,[{filters,stop,[bad]}]]), + {error,{bad_config,{kernel,{invalid_filters,[bad]}}}} = + rpc:call(Node,logger,internal_init_logger,[]), + + ok = rpc:call(Node,application,set_env, + [kernel,logger,[{filters,stop,[{f1,bad}]}]]), + {error,{bad_config,{kernel,{invalid_filter,{f1,bad}}}}} = + rpc:call(Node,logger,internal_init_logger,[]), + + ok = rpc:call(Node,application,set_env, + [kernel,logger,MF=[{filters,stop,[]},{filters,log,[]}]]), + {error,{bad_config,{kernel,{multiple_filters,MF}}}} = + rpc:call(Node,logger,internal_init_logger,[]), + + ok = rpc:call(Node,application,set_env, + [kernel,logger,[{module_level,bad,[?MODULE]}]]), + {error,{bad_config,{kernel,{invalid_level,bad}}}} = + rpc:call(Node,logger,internal_init_logger,[]), + + ok. + %%%----------------------------------------------------------------- %%% Internal check_logged(Level,Format,Args,Meta) -> diff --git a/lib/kernel/test/logger_simple_h_SUITE.erl b/lib/kernel/test/logger_simple_h_SUITE.erl index 79e5c057ad..e0ad792bdb 100644 --- a/lib/kernel/test/logger_simple_h_SUITE.erl +++ b/lib/kernel/test/logger_simple_h_SUITE.erl @@ -117,8 +117,7 @@ replace_default(Config) -> log(Node, critical, [?str,[?keyval_rep]]), log(Node, notice, [["fake",string,"line:",?LINE]]), - Env = rpc:call(Node, application, get_env, [kernel, logger, []]), - ok = rpc:call(Node, logger, add_handlers, [Env]), + ok = rpc:call(Node, logger, add_handlers, [kernel]), ok. -- cgit v1.2.3 From 625616652595249ff0866d4de8a04f3c8fa0eec4 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 6 Sep 2018 14:30:36 +0200 Subject: [logger] Improve code coverage in test --- lib/kernel/src/logger_server.erl | 4 +--- lib/kernel/test/logger_SUITE.erl | 9 +++++++-- lib/kernel/test/logger_formatter_SUITE.erl | 7 +++++++ 3 files changed, 15 insertions(+), 5 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index 01ac1b4b50..e3827c6475 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -372,7 +372,7 @@ sanity_check(Owner,Key,Value) -> sanity_check(HandlerId,Config) when is_map(Config) -> sanity_check_1(HandlerId,maps:to_list(Config)); sanity_check(_,Config) -> - {error,{invalid_handler_config,Config}}. + {error,{invalid_config,Config}}. sanity_check_1(Owner,Config) when is_list(Config) -> try @@ -417,8 +417,6 @@ check_mod(Mod) when is_atom(Mod) -> check_mod(Mod) -> throw({invalid_module,Mod}). -check_level({LevelInt,cached}) when LevelInt>=?EMERGENCY, LevelInt= - ok; check_level(Level) -> case lists:member(Level,?LEVELS) of true -> diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index 49b76f03b2..36c093d7f4 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -214,6 +214,7 @@ add_remove_filter(cleanup,_Config) -> change_config(_Config) -> %% Overwrite handler config - check that defaults are added + {error,{not_found,h1}} = logger:set_handler_config(h1,#{}), ok = logger:add_handler(h1,?MODULE,#{level=>notice,custom=>custom}), {ok,#{module:=?MODULE,level:=notice,filter_default:=log,custom:=custom}} = logger:get_handler_config(h1), @@ -392,6 +393,8 @@ set_module_level(_Config) -> {error,{invalid_level,bad}} = logger:set_module_level(?MODULE,bad), {error,{not_a_list_of_modules,{bad}}} = logger:set_module_level({bad},warning), + {error,{not_a_list_of_modules,[{bad}]}} = + logger:set_module_level([{bad}],warning), ok = logger:set_module_level(?MODULE,warning), [{?MODULE,warning}] = logger:get_module_level([?MODULE,other]), [{?MODULE,warning}] = logger:get_module_level(?MODULE), @@ -408,6 +411,7 @@ set_module_level(_Config) -> ok = check_logged(notice,M2,?MY_LOC(1)), {error,{not_a_list_of_modules,{bad}}} = logger:unset_module_level({bad}), + {error,{not_a_list_of_modules,[{bad}]}} = logger:unset_module_level([{bad}]), ok = logger:unset_module_level(?MODULE), [] = logger:get_module_level([?MODULE,other]), [] = logger:get_module_level(?MODULE), @@ -550,7 +554,7 @@ handler_failed(_Config) -> register(callback_receiver,self()), {error,{invalid_id,1}} = logger:add_handler(1,?MODULE,#{}), {error,{invalid_module,"nomodule"}} = logger:add_handler(h1,"nomodule",#{}), - {error,{invalid_handler_config,bad}} = logger:add_handler(h1,?MODULE,bad), + {error,{invalid_config,bad}} = logger:add_handler(h1,?MODULE,bad), {error,{invalid_filters,false}} = logger:add_handler(h1,?MODULE,#{filters=>false}), {error,{invalid_filter_default,true}} = @@ -625,7 +629,8 @@ handler_failed(cleanup,_Config) -> ok. config_sanity_check(_Config) -> - %% Logger config + %% Primary config + {error,{invalid_config,bad}} = logger:set_primary_config(bad), {error,{invalid_filter_default,bad}} = logger:set_primary_config(filter_default,bad), {error,{invalid_level,bad}} = logger:set_primary_config(level,bad), diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl index 2ec4b243cf..8c13f0f908 100644 --- a/lib/kernel/test/logger_formatter_SUITE.erl +++ b/lib/kernel/test/logger_formatter_SUITE.erl @@ -765,6 +765,8 @@ check_config(_Config) -> %% Test that formatter config can be changed, and that the default %% template is updated accordingly update_config(_Config) -> + {error,{not_found,?MODULE}} = logger:update_formatter_config(?MODULE,#{}), + logger:add_handler_filter(default,silence,{fun(_,_) -> stop end,ok}), ok = logger:add_handler(?MODULE,?MODULE,#{}), D = lists:seq(1,1000), @@ -817,6 +819,11 @@ update_config(_Config) -> ct:log("~p",[C6]), ["=NOTICE REPORT==== "++_,_D6] = Lines6, + {error,{invalid_formatter_config,bad}} = + logger:update_formatter_config(?MODULE,bad), + {error,{invalid_formatter_config,logger_formatter,{depth,bad}}} = + logger:update_formatter_config(?MODULE,depth,bad), + ok. update_config(cleanup,_Config) -> -- cgit v1.2.3 From eb29e06488ded39e657db655e09f04e95568eb76 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 7 Sep 2018 14:38:46 +0200 Subject: [logger] Increase timetrap timer for handler_requests_under_load test --- lib/kernel/test/logger_disk_log_h_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index ed441f7e65..a815db14e9 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -1186,7 +1186,7 @@ restart_after(cleanup, _Config) -> %% during high load to verify that sync, dropping and flushing is %% handled correctly. handler_requests_under_load() -> - [{timetrap,{minutes,3}}]. + [{timetrap,{minutes,5}}]. handler_requests_under_load(Config) -> {Log,HConfig,DLHConfig} = start_handler(?MODULE, ?FUNCTION_NAME, Config), NewHConfig = -- cgit v1.2.3 From a98ffe831d2a1cf44d0b7c53e2d72ade9d5a3dbb Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 7 Sep 2018 16:49:30 +0200 Subject: [logger] Fix spec for handler callbacks to always return ok --- lib/kernel/src/logger_disk_log_h.erl | 2 +- lib/kernel/src/logger_std_h.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index 3c12c9e68b..26375373aa 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -225,7 +225,7 @@ removing_handler(#{id:=Name}) -> %%%----------------------------------------------------------------- %%% Log a string or report --spec log(LogEvent, Config) -> ok | dropped when +-spec log(LogEvent, Config) -> ok when LogEvent :: logger:log_event(), Config :: logger:handler_config(). diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index b60e7cf587..5dc4bc91ad 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -210,7 +210,7 @@ removing_handler(#{id:=Name}) -> %%%----------------------------------------------------------------- %%% Log a string or report --spec log(LogEvent, Config) -> ok | dropped when +-spec log(LogEvent, Config) -> ok when LogEvent :: logger:log_event(), Config :: logger:handler_config(). -- cgit v1.2.3 From 7140fe7679c1ea0862961d2fc34f9d8283c04ca3 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 11 Sep 2018 14:11:29 +0200 Subject: [logger] Check for deadlock when removing filters If a filter fails, it is removed by a call to logger:remove_filter/1,2. If the log event that caused the failure was issued on the logger process itself, this would earlier cause a deadlock due to a gen_server:call to self(). This is now prevented. --- lib/kernel/src/logger_server.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index e3827c6475..a1d40f1123 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -319,6 +319,7 @@ call(Request) -> case get(?LOGGER_SERVER_TAG) of true when Action == add_handler; Action == remove_handler; + Action == add_filter; Action == remove_filter; Action == update_config; Action == set_config -> {error,{attempting_syncronous_call_to_self,Request}}; _ -> -- cgit v1.2.3 From a4ff9f378ae7008e268d1c2facd44d049be6e930 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 12 Sep 2018 14:37:02 +0200 Subject: [logger] Include single_line option to report_cb If the report callback function has two arguments, the second argument is a map with options to limit the size of the log event. To even allow a better formatting when the event shall be printed on a single line, the new option single_line is now included in this argument. --- lib/kernel/doc/src/logger_chapter.xml | 11 +++++++---- lib/kernel/src/logger.erl | 3 ++- lib/kernel/src/logger_formatter.erl | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 26066d0777..4a81cfa34a 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -209,10 +209,13 @@

    fun((logger:report(),logger:report_cb_config()) -> unicode:chardata())
           

    The fun must obey the depth and chars_limit - parameters provided in the second argument, as the formatter - can not do anything useful of these parameters with the - returned string. This variant is used when the formatting of - the report depends on the size parameters.

    + parameters provided in the second argument, as the formatter can + not do anything useful of these parameters with the returned + string. The extra data also contains a field named + single_line, indicating if the printed log message may + contain line breaks or not. This variant is used when the + formatting of the report depends on the size or single line + parameters.

    Example, format string and arguments:

    logger:error("The file does not exist: ~ts",[Filename])

    Example, string:

    diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 0ada8b4924..752dd8d493 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -78,7 +78,8 @@ -type report_cb() :: fun((report()) -> {io:format(),[term()]}) | fun((report(),report_cb_config()) -> unicode:chardata()). -type report_cb_config() :: #{depth := pos_integer() | unlimited, - chars_limit := pos_integer() | unlimited}. + chars_limit := pos_integer() | unlimited, + single_line := boolean()}. -type msg_fun() :: fun((term()) -> {io:format(),[term()]} | report() | unicode:chardata()). diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl index 59aa6ec3af..ded89bac9f 100644 --- a/lib/kernel/src/logger_formatter.erl +++ b/lib/kernel/src/logger_formatter.erl @@ -165,7 +165,7 @@ format_msg({report,Report},#{report_cb:=Fun}=Meta,Config) when is_function(Fun,1 Meta,Config) end; format_msg({report,Report},#{report_cb:=Fun}=Meta,Config) when is_function(Fun,2) -> - try Fun(Report,maps:with([depth,chars_limit],Config)) of + try Fun(Report,maps:with([depth,chars_limit,single_line],Config)) of Chardata when ?IS_STRING(Chardata) -> try chardata_to_list(Chardata) % already size limited by report_cb catch _:_ -> -- cgit v1.2.3 From 48a1c458a46dcd1f031fd3b9689af80b32b4a6a2 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 11 Sep 2018 11:18:52 +0200 Subject: [error_logger] Handle report_cb with arity 2 --- lib/kernel/src/error_logger.erl | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index ad8c937882..e324be5290 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -147,14 +147,27 @@ do_log(Level,{report,Msg},#{?MODULE:=#{tag:=Tag}}=Meta) -> _ -> %% From logger call which added error_logger data to %% obtain backwards compatibility with error_logger:*_msg/1,2 - RCBFun=maps:get(report_cb,Meta,fun logger:format_report/1), - try RCBFun(Msg) of - {F,A} when is_list(F), is_list(A) -> - {F,A}; - Other -> - {"REPORT_CB ERROR: ~tp; Returned: ~tp",[Msg,Other]} - catch C:R -> - {"REPORT_CB CRASH: ~tp; Reason: ~tp",[Msg,{C,R}]} + case maps:get(report_cb,Meta,fun logger:format_report/1) of + RCBFun when is_function(RCBFun,1) -> + try RCBFun(Msg) of + {F,A} when is_list(F), is_list(A) -> + {F,A}; + Other -> + {"REPORT_CB ERROR: ~tp; Returned: ~tp",[Msg,Other]} + catch C:R -> + {"REPORT_CB CRASH: ~tp; Reason: ~tp",[Msg,{C,R}]} + end; + RCBFun when is_function(RCBFun,2) -> + try RCBFun(Msg,#{depth=>get_format_depth(), + chars_limit=>unlimited, + single_line=>false}) of + Chardata when ?IS_STRING(Chardata) -> + {"~ts",[Chardata]}; + Other -> + {"REPORT_CB ERROR: ~tp; Returned: ~tp",[Msg,Other]} + catch C:R -> + {"REPORT_CB CRASH: ~tp; Reason: ~tp",[Msg,{C,R}]} + end end end, notify(Level,Tag,Format,Args,Meta); -- cgit v1.2.3 From 6073d37a5a0691704d0fa1a5ff9289ba9e35c8ed Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 14 Sep 2018 14:12:13 +0200 Subject: [logger] Read config before terminating handler process When a handler process is terminated due to overload, it reads its configuration from the configuration database, so it can be restarted with the same configuration after a small delay. This was earlier done in a different process, which was spawned off from the terminate function. This caused a race condition, where in some cases, the configuration was already removed before it could be read. The reason for spawning off a process, is to avoid a deadlock due to the call to logger:remove_handler/1. This commit moves the call to logger:get_handler_config/1 back to the handler process - to ensure that the data is still there, but keeps the call to logger:remove_handler/1 in the spawned off process - to avoid deadlock. --- lib/kernel/src/logger_disk_log_h.erl | 3 ++- lib/kernel/src/logger_h_common.erl | 3 ++- lib/kernel/src/logger_std_h.erl | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index 26375373aa..a8f141f135 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -426,8 +426,9 @@ terminate(Reason, State = #{id := Name}) -> _ = logger_h_common:cancel_timer(maps:get(rep_sync_tref, State, undefined)), _ = close_disk_log(Name, normal), + ok = logger_h_common:stop_or_restart(Name, Reason, State), unregister(?name_to_reg_name(?MODULE, Name)), - logger_h_common:stop_or_restart(Name, Reason, State). + ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl index 2818a460f1..38ac7d8ffc 100644 --- a/lib/kernel/src/logger_h_common.erl +++ b/lib/kernel/src/logger_h_common.erl @@ -294,6 +294,7 @@ stop_or_restart(Name, {shutdown,Reason={overloaded,_Name,_QLen,_Mem}}, %% and set a restart timer. A separate process must perform this %% in order to avoid deadlock. HandlerPid = self(), + ConfigResult = logger:get_handler_config(Name), RemoveAndRestart = fun() -> MRef = erlang:monitor(process, HandlerPid), @@ -304,7 +305,7 @@ stop_or_restart(Name, {shutdown,Reason={overloaded,_Name,_QLen,_Mem}}, error_notify(Reason), exit(HandlerPid, kill) end, - case logger:get_handler_config(Name) of + case ConfigResult of {ok,#{module:=HMod}=HConfig} when is_integer(RestartAfter) -> _ = logger:remove_handler(Name), _ = timer:apply_after(RestartAfter, logger, add_handler, diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index 5dc4bc91ad..66fa6b6ab6 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -415,8 +415,9 @@ terminate(Reason, State = #{id:=Name, file_ctrl_pid:=FWPid, false -> ok end, + ok = logger_h_common:stop_or_restart(Name, Reason, State), unregister(?name_to_reg_name(?MODULE, Name)), - logger_h_common:stop_or_restart(Name, Reason, State). + ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. -- cgit v1.2.3 From d38c98809dd5a9e44d9d7320ba934523011d7f8e Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 17 Sep 2018 15:29:03 +0200 Subject: Terminate nodes after failed test cases in erl_distribution_SUITE A hanging node from this suite can fail later tests, so terminate all nodes() after each test case. --- lib/kernel/test/erl_distribution_SUITE.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/kernel') diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl index 0e13f0383e..5a8bbd56c4 100644 --- a/lib/kernel/test/erl_distribution_SUITE.erl +++ b/lib/kernel/test/erl_distribution_SUITE.erl @@ -89,6 +89,7 @@ init_per_suite(Config) -> Config. end_per_suite(_Config) -> + [slave:stop(N) || N <- nodes()], ok. init_per_group(_GroupName, Config) -> -- cgit v1.2.3 From 6053725bb54f7302d3698d0df100eb01c4df4f12 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Wed, 5 Sep 2018 16:36:53 +0200 Subject: Write testcases for recvtos and friends Use os:type() and os:version() to predict if the individual options are supposed to be supported. We'll see if this holds. --- lib/kernel/test/gen_tcp_misc_SUITE.erl | 207 ++++++++++++++++++++++++++++++++- lib/kernel/test/gen_udp_SUITE.erl | 163 ++++++++++++++++++++++++++ 2 files changed, 369 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index 91c89e76dd..d532537eb9 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -41,6 +41,7 @@ busy_send/1, busy_disconnect_passive/1, busy_disconnect_active/1, fill_sendq/1, partial_recv_and_close/1, partial_recv_and_close_2/1,partial_recv_and_close_3/1,so_priority/1, + recvtos/1, recvttl/1, recvtosttl/1, recvtclass/1, %% Accept tests primitive_accept/1,multi_accept_close_listen/1,accept_timeout/1, accept_timeouts_in_order/1,accept_timeouts_in_order2/1, @@ -83,7 +84,8 @@ all() -> busy_disconnect_passive, busy_disconnect_active, fill_sendq, partial_recv_and_close, partial_recv_and_close_2, partial_recv_and_close_3, - so_priority, primitive_accept, + so_priority, recvtos, recvttl, recvtosttl, + recvtclass, primitive_accept, multi_accept_close_listen, accept_timeout, accept_timeouts_in_order, accept_timeouts_in_order2, accept_timeouts_in_order3, accept_timeouts_in_order4, @@ -1914,6 +1916,209 @@ so_priority(Config) when is_list(Config) -> end end. + + +%% IP_RECVTOS and IP_RECVTCLASS for IP_PKTOPTIONS +%% does not seem to be implemented in Linux until kernel 3.0 + +recvtos(_Config) -> + test_pktoptions( + inet, [{recvtos,tos,96}], + fun recvtos_ok/2, + false). + +recvtosttl(_Config) -> + test_pktoptions( + inet, [{recvtos,tos,96},{recvttl,ttl,33}], + fun (OSType, OSVer) -> + recvtos_ok(OSType, OSVer) andalso recvttl_ok(OSType, OSVer) + end, + false). + +recvttl(_Config) -> + test_pktoptions( + inet, [{recvttl,ttl,33}], + fun recvttl_ok/2, + false). + +recvtclass(_Config) -> + {ok,IFs} = inet:getifaddrs(), + case + [Name || + {Name,Opts} <- IFs, + lists:member({addr,{0,0,0,0,0,0,0,1}}, Opts)] + of + [_] -> + test_pktoptions( + inet6, [{recvtclass,tclass,224}], + fun recvtclass_ok/2, + true); + [] -> + {skip,{ipv6_not_supported,IFs}} + end. + +%% These version numbers are the highest noted in daily tests +%% where the test fails for a plausible reason, so +%% skip on that platform. +%% +%% On newer versions it might be fixed, but we'll see about that +%% when machines with newer versions gets installed... +%% If the test still fails for a plausible reason these +%% version numbers simply should be increased. + +%% Using the option returns einval, so it is not implemented. +recvtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0}); +%% Does not return any value - not implemented for pktoptions +recvtos_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {3,1,0}); +%% +recvtos_ok({unix,_}, _) -> true; +recvtos_ok(_, _) -> false. + +recvttl_ok({unix,linux}, _) -> true; +recvttl_ok({unix,_}, _) -> true; +recvttl_ok(_, _) -> false. + +%% Using the option returns einval, so it is not implemented. +recvtclass_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); +recvtclass_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); +%% Using the option returns einval up to 0.9.0, so it is not implemented. +%% Does not return any value - not implemented for pktoptions +recvtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0}); +%% Does not return any value - not implemented for pktoptions +recvtclass_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {3,1,0}); +%% +recvtclass_ok({unix,_}, _) -> true; +recvtclass_ok(_, _) -> false. + +semver_lt({X1,Y1,Z1}, {X2,Y2,Z2}) -> + if + X1 > X2 -> false; + X1 < X2 -> true; + Y1 > Y2 -> false; + Y1 < Y2 -> true; + Z1 > Z2 -> false; + Z1 < Z2 -> true; + true -> false + end; +semver_lt(_, {_,_,_}) -> false. + +test_pktoptions(Family, Spec, OSFilter, CheckAccept) -> + OSType = os:type(), + OSVer = os:version(), + case OSFilter(OSType, OSVer) of + true -> + io:format("Os: ~p, ~p~n", [OSType,OSVer]), + test_pktoptions(Family, Spec, CheckAccept, OSType, OSVer); + false -> + {skip,{not_supported_for_os_version,{OSType,OSVer}}} + end. +%% +test_pktoptions(Family, Spec, CheckAccept, OSType, OSVer) -> + Timeout = 5000, + RecvOpts = [RecvOpt || {RecvOpt,_,_} <- Spec], + TrueRecvOpts = [{RecvOpt,true} || {RecvOpt,_,_} <- Spec], + FalseRecvOpts = [{RecvOpt,false} || {RecvOpt,_,_} <- Spec], + Opts = [Opt || {_,Opt,_} <- Spec], + OptsVals = [{Opt,Val} || {_,Opt,Val} <- Spec], + Address = + case Family of + inet -> + {127,0,0,1}; + inet6 -> + {0,0,0,0,0,0,0,1} + end, + %% + %% Set RecvOpts on listen socket + {ok,L} = + gen_tcp:listen( + 0, + [Family,binary,{active,false},{send_timeout,Timeout} + |TrueRecvOpts]), + {ok,P} = inet:port(L), + {ok,TrueRecvOpts} = inet:getopts(L, RecvOpts), + {ok,OptsValsDefault} = inet:getopts(L, Opts), + %% + %% Set RecvOpts and Option values on connect socket + {ok,S2} = + gen_tcp:connect( + Address, P, + [Family,binary,{active,false},{send_timeout,Timeout} + |TrueRecvOpts ++ OptsVals], + Timeout), + {ok,TrueRecvOpts} = inet:getopts(S2, RecvOpts), + {ok,OptsVals} = inet:getopts(S2, Opts), + %% + %% Accept socket inherits the options from listen socket + {ok,S1} = gen_tcp:accept(L, Timeout), + {ok,TrueRecvOpts} = inet:getopts(S1, RecvOpts), + {ok,OptsValsDefault} = inet:getopts(S1, Opts), +%%% %% +%%% %% Handshake +%%% ok = gen_tcp:send(S1, <<"hello">>), +%%% {ok,<<"hello">>} = gen_tcp:recv(S2, 5, Timeout), +%%% ok = gen_tcp:send(S2, <<"hi">>), +%%% {ok,<<"hi">>} = gen_tcp:recv(S1, 2, Timeout), + %% + %% Verify returned remote options + {ok,[{pktoptions,OptsVals1}]} = inet:getopts(S1, [pktoptions]), + {ok,[{pktoptions,OptsVals2}]} = inet:getopts(S2, [pktoptions]), + (Result1 = sets_eq(OptsVals1, OptsVals)) + orelse io:format( + "Accept differs: ~p neq ~p~n", [OptsVals1,OptsVals]), + (Result2 = sets_eq(OptsVals2, OptsValsDefault)) + orelse io:format( + "Connect differs: ~p neq ~p~n", + [OptsVals2,OptsValsDefault]), + %% + ok = gen_tcp:close(S2), + ok = gen_tcp:close(S1), + %% + %% + %% Clear RecvOpts on listen socket and set Option values + ok = inet:setopts(L, FalseRecvOpts ++ OptsVals), + {ok,FalseRecvOpts} = inet:getopts(L, RecvOpts), + {ok,OptsVals} = inet:getopts(L, Opts), + %% + %% Set RecvOpts on connecting socket + %% + {ok,S4} = + gen_tcp:connect( + Address, P, + [Family,binary,{active,false},{send_timeout,Timeout} + |TrueRecvOpts], + Timeout), + {ok,TrueRecvOpts} = inet:getopts(S4, RecvOpts), + {ok,OptsValsDefault} = inet:getopts(S4, Opts), + %% + %% Accept socket inherits the options from listen socket + {ok,S3} = gen_tcp:accept(L, Timeout), + {ok,FalseRecvOpts} = inet:getopts(S3, RecvOpts), + {ok,OptsVals} = inet:getopts(S3, Opts), + %% + %% Verify returned remote options + {ok,[{pktoptions,[]}]} = inet:getopts(S3, [pktoptions]), + {ok,[{pktoptions,OptsVals4}]} = inet:getopts(S4, [pktoptions]), + (Result3 = sets_eq(OptsVals4, OptsVals)) + orelse io:format( + "Accept2 differs: ~p neq ~p~n", [OptsVals4,OptsVals]), + %% + ok = gen_tcp:close(S4), + ok = gen_tcp:close(S3), + ok = gen_tcp:close(L), + (Result1 and ((not CheckAccept) or (Result2 and Result3))) + orelse + exit({failed, + [{OptsVals1,OptsVals4,OptsVals}, + {OptsVals2,OptsValsDefault}], + {OSType,OSVer}}), +%% exit({{OSType,OSVer},success}), % In search for the truth + ok. + +sets_eq(L1, L2) -> + lists:sort(L1) == lists:sort(L2). + + + %% Accept test utilities (suites are below) millis() -> diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index 3acfff929e..878e48786c 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -36,6 +36,7 @@ -export([send_to_closed/1, active_n/1, buffer_size/1, binary_passive_recv/1, max_buffer_size/1, bad_address/1, read_packets/1, open_fd/1, connect/1, implicit_inet6/1, + recvtos/1, recvtosttl/1, recvttl/1, recvtclass/1, local_basic/1, local_unbound/1, local_fdopen/1, local_fdopen_unbound/1, local_abstract/1]). @@ -47,6 +48,7 @@ all() -> [send_to_closed, buffer_size, binary_passive_recv, max_buffer_size, bad_address, read_packets, open_fd, connect, implicit_inet6, active_n, + recvtos, recvtosttl, recvttl, recvtclass, {group, local}]. groups() -> @@ -572,6 +574,167 @@ active_n(Config) when is_list(Config) -> ok. + +recvtos(_Config) -> + test_recv_opts( + inet, [{recvtos,tos,96}], + fun recvtos_ok/2). + +recvtosttl(_Config) -> + test_recv_opts( + inet, [{recvtos,tos,96},{recvttl,ttl,33}], + fun (OSType, OSVer) -> + recvtos_ok(OSType, OSVer) andalso recvttl_ok(OSType, OSVer) + end). + +recvttl(_Config) -> + test_recv_opts( + inet, [{recvttl,ttl,33}], + fun recvttl_ok/2). + +recvtclass(_Config) -> + {ok,IFs} = inet:getifaddrs(), + case + [Name || + {Name,Opts} <- IFs, + lists:member({addr,{0,0,0,0,0,0,0,1}}, Opts)] + of + [_] -> + test_recv_opts( + inet6, [{recvtclass,tclass,224}], + fun recvtclass_ok/2); + [] -> + {skip,ipv6_not_supported,IFs} + end. + +%% These version numbers are just above the highest noted in daily tests +%% where the test fails for a plausible reason, that is the lowest +%% where we can expect that the test mighe succeed, so +%% skip on platforms lower than this. +%% +%% On newer versions it might be fixed, but we'll see about that +%% when machines with newer versions gets installed... +%% If the test still fails for a plausible reason these +%% version numbers simply should be increased. + +%% Using the option returns einval, so it is not implemented. +recvtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0}); +%% Using the option returns einval, so it is not implemented. +recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); +%% Using the option returns einval, so it is not implemented. +recvtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,11,0}); +%% +recvtos_ok({unix,_}, _) -> true; +recvtos_ok(_, _) -> false. + +recvttl_ok({unix,_}, _) -> true; +recvttl_ok(_, _) -> false. + +%% Using the option returns einval, so it is not implemented. +recvtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {9,9,0}); +recvtclass_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {2,6,11}); +%% +recvtclass_ok({unix,_}, _) -> true; +recvtclass_ok(_, _) -> false. + +semver_lt({X1,Y1,Z1}, {X2,Y2,Z2}) -> + if + X1 > X2 -> false; + X1 < X2 -> true; + Y1 > Y2 -> false; + Y1 < Y2 -> true; + Z1 > Z2 -> false; + Z1 < Z2 -> true; + true -> false + end; +semver_lt(_, {_,_,_}) -> false. + +test_recv_opts(Family, Spec, OSFilter) -> + OSType = os:type(), + OSVer = os:version(), + case OSFilter(OSType, OSVer) of + true -> + io:format("Os: ~p, ~p~n", [OSType,OSVer]), + test_recv_opts(Family, Spec, OSType, OSVer); + false -> + {skip,{not_supported_for_os_version,{OSType,OSVer}}} + end. +%% +test_recv_opts(Family, Spec, _OSType, _OSVer) -> + Timeout = 5000, + RecvOpts = [RecvOpt || {RecvOpt,_,_} <- Spec], + TrueRecvOpts = [{RecvOpt,true} || {RecvOpt,_,_} <- Spec], + FalseRecvOpts = [{RecvOpt,false} || {RecvOpt,_,_} <- Spec], + Opts = [Opt || {_,Opt,_} <- Spec], + OptsVals = [{Opt,Val} || {_,Opt,Val} <- Spec], + TrueRecvOpts_OptsVals = TrueRecvOpts ++ OptsVals, + Addr = + case Family of + inet -> + {127,0,0,1}; + inet6 -> + {0,0,0,0,0,0,0,1} + end, + %% + {ok,S1} = + gen_udp:open(0, [Family,binary,{active,false}|TrueRecvOpts]), + {ok,P1} = inet:port(S1), + {ok,TrueRecvOpts} = inet:getopts(S1, RecvOpts), + ok = inet:setopts(S1, FalseRecvOpts), + {ok,FalseRecvOpts} = inet:getopts(S1, RecvOpts), + ok = inet:setopts(S1, TrueRecvOpts_OptsVals), + {ok,TrueRecvOpts_OptsVals} = inet:getopts(S1, RecvOpts ++ Opts), + %% + {ok,S2} = + gen_udp:open(0, [Family,binary,{active,true}|FalseRecvOpts]), + {ok,P2} = inet:port(S2), + {ok,FalseRecvOpts_OptsVals2} = inet:getopts(S2, RecvOpts ++ Opts), + OptsVals2 = FalseRecvOpts_OptsVals2 -- FalseRecvOpts, + %% + ok = gen_udp:send(S2, Addr, P1, <<"abcde">>), + ok = gen_udp:send(S1, Addr, P2, <<"fghij">>), + {ok,{_,P2,OptsVals3,<<"abcde">>}} = gen_udp:recv(S1, 0, Timeout), + verify_sets_eq(OptsVals3, OptsVals2), + receive + {udp,S2,_,P1,<<"fghij">>} -> + ok; + Other1 -> + exit({unexpected,Other1}) + after Timeout -> + exit(timeout) + end, + %% + ok = inet:setopts(S1, FalseRecvOpts), + {ok,FalseRecvOpts} = inet:getopts(S1, RecvOpts), + ok = inet:setopts(S2, TrueRecvOpts), + {ok,TrueRecvOpts} = inet:getopts(S2, RecvOpts), + %% + ok = gen_udp:send(S2, Addr, P1, <<"klmno">>), + ok = gen_udp:send(S1, Addr, P2, <<"pqrst">>), + {ok,{_,P2,<<"klmno">>}} = gen_udp:recv(S1, 0, Timeout), + receive + {udp,S2,_,P1,OptsVals4,<<"pqrst">>} -> + verify_sets_eq(OptsVals4, OptsVals); + Other2 -> + exit({unexpected,Other2}) + after Timeout -> + exit(timeout) + end, + ok = gen_udp:close(S1), + ok = gen_udp:close(S2), +%% exit({{OSType,OSVer},success}), % In search for the truth + ok. + +verify_sets_eq(L1, L2) -> + L = lists:sort(L1), + case lists:sort(L2) of + L -> + ok; + _ -> + exit({sets_neq,L1,L2}) + end. + + local_basic(_Config) -> SFile = local_filename(server), SAddr = {local,bin_filename(SFile)}, -- cgit v1.2.3 From b1216cf39c987a65f5545f8d9034a1c37f1654cf Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 17 Sep 2018 15:42:52 +0200 Subject: [logger] Skip app level test if app cannot be loaded --- lib/kernel/test/logger_SUITE.erl | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index 6bd9b20c35..aef3047856 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -429,27 +429,32 @@ set_application_level(_Config) -> {error,{not_loaded,mnesia}} = logger:set_application_level(mnesia, warning), {error,{not_loaded,mnesia}} = logger:unset_application_level(mnesia), - application:load(mnesia), - {ok, Modules} = application:get_key(mnesia, modules), - [] = logger:get_module_level(Modules), + case application:load(mnesia) of + ok -> + {ok, Modules} = application:get_key(mnesia, modules), + [] = logger:get_module_level(Modules), - {error,{invalid_level,warn}} = logger:set_application_level(mnesia, warn), + {error,{invalid_level,warn}} = + logger:set_application_level(mnesia, warn), - ok = logger:set_application_level(mnesia, debug), - DebugModules = lists:sort([{M,debug} || M <- Modules]), - DebugModules = lists:sort(logger:get_module_level(Modules)), + ok = logger:set_application_level(mnesia, debug), + DebugModules = lists:sort([{M,debug} || M <- Modules]), + DebugModules = lists:sort(logger:get_module_level(Modules)), - ok = logger:set_application_level(mnesia, warning), + ok = logger:set_application_level(mnesia, warning), - WarnModules = lists:sort([{M,warning} || M <- Modules]), - WarnModules = lists:sort(logger:get_module_level(Modules)), + WarnModules = lists:sort([{M,warning} || M <- Modules]), + WarnModules = lists:sort(logger:get_module_level(Modules)), - ok = logger:unset_application_level(mnesia), - [] = logger:get_module_level(Modules). + ok = logger:unset_application_level(mnesia), + [] = logger:get_module_level(Modules); + {error,{"no such file or directory","mnesia.app"}} -> + {skip, "Cannot load mnesia, does not exist"} + end. set_application_level(cleanup,_Config) -> - ok = logger:unset_application_level(mnesia), - ok = application:unload(mnesia), + _ = logger:unset_application_level(mnesia), + _ = application:unload(mnesia), ok. cache_module_level(_Config) -> -- cgit v1.2.3 From a04f3246c1eda7b8d8d83ba2bcc46d502b80d22b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 18 Sep 2018 14:27:27 +0200 Subject: Consolidate distribution entry state transitions * Make connection_id part of the distribution handle as {ConnId, DistEntry} in order for BIFs to verify correct connection. * Make distribution handle opaque to net_kernel. * Remove some unsafe lockless reads of DistEntry.flags * Change state ERTS_DE_STATE_EXITING to be more of an internal state that prevents erts from enqueue, encode or schedule new data to be sent. Otherwise it should behave like ERTS_DE_STATE_CONNECTED. --- lib/kernel/src/kernel.app.src | 2 +- lib/kernel/src/net_kernel.erl | 72 ++++++++++++++++--------------------------- 2 files changed, 28 insertions(+), 46 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 4933eae76f..fe073621c8 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -146,6 +146,6 @@ {logger_sasl_compatible, false} ]}, {mod, {kernel, []}}, - {runtime_dependencies, ["erts-10.0", "stdlib-3.5", "sasl-3.0"]} + {runtime_dependencies, ["erts-10.1", "stdlib-3.5", "sasl-3.0"]} ] }. diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index ef92f9f4d1..3cf11fd7b1 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -369,11 +369,11 @@ do_auto_connect_1(Node, ConnId, From, State) -> end. do_auto_connect_2(Node, passive_cnct, From, State, ConnLookup) -> - case (catch erts_internal:new_connection(Node)) of - {Nr,_DHandle}=ConnId when is_integer(Nr) -> - do_auto_connect_2(Node, ConnId, From, State, ConnLookup); - - _Error -> + try erts_internal:new_connection(Node) of + ConnId -> + do_auto_connect_2(Node, ConnId, From, State, ConnLookup) + catch + _:_ -> error_logger:error_msg("~n** Cannot get connection id for node ~w~n", [Node]), {reply, false, State} @@ -406,7 +406,7 @@ do_auto_connect_2(Node, ConnId, From, State, ConnLookup) -> erts_internal:abort_connection(Node, ConnId), {reply, false, State}; _ -> - case setup(ConnLookup, Node,ConnId,normal,From,State) of + case setup(Node, ConnId, normal, From, State) of {ok, SetupPid} -> Owners = [{SetupPid, Node} | State#state.conn_owners], {noreply,State#state{conn_owners=Owners}}; @@ -430,8 +430,8 @@ do_explicit_connect([#connection{conn_id = ConnId}=Conn], _, _, ConnId, From, St do_explicit_connect([#barred_connection{}], Type, Node, ConnId, From , State) -> %% Barred connection only affects auto_connect, ignore it. do_explicit_connect([], Type, Node, ConnId, From , State); -do_explicit_connect(ConnLookup, Type, Node, ConnId, From , State) -> - case setup(ConnLookup, Node,ConnId,Type,From,State) of +do_explicit_connect(_ConnLookup, Type, Node, ConnId, From , State) -> + case setup(Node,ConnId,Type,From,State) of {ok, SetupPid} -> Owners = [{SetupPid, Node} | State#state.conn_owners], {noreply,State#state{conn_owners=Owners}}; @@ -440,18 +440,6 @@ do_explicit_connect(ConnLookup, Type, Node, ConnId, From , State) -> {reply, false, State} end. --define(ERTS_DIST_CON_ID_MASK, 16#ffffff). % also in external.h - -verify_new_conn_id([], {Nr,_DHandle}) - when (Nr band (bnot ?ERTS_DIST_CON_ID_MASK)) =:= 0 -> - true; -verify_new_conn_id([#connection{conn_id = {Old,_}}], {New,_}) - when New =:= ((Old+1) band ?ERTS_DIST_CON_ID_MASK) -> - true; -verify_new_conn_id(_, _) -> - false. - - %% ------------------------------------------------------------ %% handle_call. @@ -477,8 +465,8 @@ handle_call({connect, _, Node}, From, State) when Node =:= node() -> handle_call({connect, Type, Node}, From, State) -> verbose({connect, Type, Node}, 1, State), ConnLookup = ets:lookup(sys_dist, Node), - R = case (catch erts_internal:new_connection(Node)) of - {Nr,_DHandle}=ConnId when is_integer(Nr) -> + R = try erts_internal:new_connection(Node) of + ConnId -> R1 = do_explicit_connect(ConnLookup, Type, Node, ConnId, From, State), case R1 of {reply, true, _S} -> %% already connected @@ -488,9 +476,10 @@ handle_call({connect, Type, Node}, From, State) -> {reply, false, _S} -> %% connection refused erts_internal:abort_connection(Node, ConnId) end, - R1; + R1 - _Error -> + catch + _:_ -> error_logger:error_msg("~n** Cannot get connection id for node ~w~n", [Node]), {reply, false, State} @@ -708,9 +697,9 @@ terminate(_Reason, State) -> %% %% Asynchronous auto connect request %% -handle_info({auto_connect,Node, Nr, DHandle}, State) -> - verbose({auto_connect, Node, Nr, DHandle}, 1, State), - ConnId = {Nr, DHandle}, +handle_info({auto_connect,Node, DHandle}, State) -> + verbose({auto_connect, Node, DHandle}, 1, State), + ConnId = DHandle, NewState = case do_auto_connect_1(Node, ConnId, noreply, State) of {noreply, S} -> %% Pending connection @@ -804,8 +793,8 @@ handle_info({AcceptPid, {accept_pending,MyNode,Node,Address,Type}}, State) -> AcceptPid ! {self(), {accept_pending, already_pending}}, {noreply, State}; _ -> - case (catch erts_internal:new_connection(Node)) of - {Nr,_DHandle}=ConnId when is_integer(Nr) -> + try erts_internal:new_connection(Node) of + ConnId -> ets:insert(sys_dist, #connection{node = Node, conn_id = ConnId, state = pending, @@ -814,9 +803,9 @@ handle_info({AcceptPid, {accept_pending,MyNode,Node,Address,Type}}, State) -> type = Type}), AcceptPid ! {self(),{accept_pending,ok}}, Owners = [{AcceptPid,Node} | State#state.conn_owners], - {noreply, State#state{conn_owners = Owners}}; - - _ -> + {noreply, State#state{conn_owners = Owners}} + catch + _:_ -> error_logger:error_msg("~n** Cannot get connection id for node ~w~n", [Node]), AcceptPid ! {self(),{accept_pending,nok_pending}} @@ -1283,8 +1272,8 @@ spawn_func(_,{From,Tag},M,F,A,Gleader) -> %% Set up connection to a new node. %% ----------------------------------------------------------- -setup(ConnLookup, Node,ConnId,Type,From,State) -> - case setup_check(ConnLookup, Node, ConnId, State) of +setup(Node, ConnId, Type, From, State) -> + case setup_check(Node, State) of {ok, L} -> Mod = L#listen.module, LAddr = L#listen.address, @@ -1313,7 +1302,7 @@ setup(ConnLookup, Node,ConnId,Type,From,State) -> Error end. -setup_check(ConnLookup, Node, ConnId, State) -> +setup_check(Node, State) -> Allowed = State#state.allowed, case lists:member(Node, Allowed) of false when Allowed =/= [] -> @@ -1321,16 +1310,9 @@ setup_check(ConnLookup, Node, ConnId, State) -> "disallowed node ~w ** ~n", [Node]), {error, bad_node}; _ -> - case verify_new_conn_id(ConnLookup, ConnId) of - false -> - error_msg("** Connection attempt to ~w with " - "bad connection id ~w ** ~n", [Node, ConnId]), - {error, bad_conn_id}; - true -> - case select_mod(Node, State#state.listen) of - {ok, _L}=OK -> OK; - Error -> Error - end + case select_mod(Node, State#state.listen) of + {ok, _L}=OK -> OK; + Error -> Error end end. -- cgit v1.2.3 From 2ad4b8f65e07423dd3cf230cecae635189b6f437 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Tue, 18 Sep 2018 15:06:44 +0200 Subject: Improve platform filter --- lib/kernel/test/gen_tcp_misc_SUITE.erl | 43 ++++++++++++++++++++++++++-------- lib/kernel/test/gen_udp_SUITE.erl | 3 ++- 2 files changed, 35 insertions(+), 11 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index d532537eb9..358ca872f7 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -1919,7 +1919,17 @@ so_priority(Config) when is_list(Config) -> %% IP_RECVTOS and IP_RECVTCLASS for IP_PKTOPTIONS -%% does not seem to be implemented in Linux until kernel 3.0 +%% does not seem to be implemented in Linux until kernel 3.1 +%% +%% It seems pktoptions does not return valid values +%% for IPv4 connect sockets. On the accept socket +%% we get valid values, but on the connect socket we get +%% the default values for TOS and TTL. +%% +%% Therefore the argument CheckConnect that enables +%% checking the returned values for the connect socket. +%% It is only used for recvtclass that is an IPv6 option +%% and there we get valid values from both socket ends. recvtos(_Config) -> test_pktoptions( @@ -1965,25 +1975,38 @@ recvtclass(_Config) -> %% when machines with newer versions gets installed... %% If the test still fails for a plausible reason these %% version numbers simply should be increased. +%% Or maybe we should change to only test on known good +%% platforms - change {unix,_} to false? -%% Using the option returns einval, so it is not implemented. +%% pktoptions is not supported for IPv4 +recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); recvtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0}); +recvtos_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); +%% Using the option returns einval, so it is not implemented. +recvtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% Does not return any value - not implemented for pktoptions recvtos_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {3,1,0}); %% recvtos_ok({unix,_}, _) -> true; recvtos_ok(_, _) -> false. +%% pktoptions is not supported for IPv4 +recvttl_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); +recvttl_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0}); +recvttl_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); +%% Using the option returns einval, so it is not implemented. +recvttl_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); +%% recvttl_ok({unix,linux}, _) -> true; recvttl_ok({unix,_}, _) -> true; recvttl_ok(_, _) -> false. -%% Using the option returns einval, so it is not implemented. +%% pktoptions is not supported for IPv6 recvtclass_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); -recvtclass_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); -%% Using the option returns einval up to 0.9.0, so it is not implemented. -%% Does not return any value - not implemented for pktoptions recvtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0}); +recvtclass_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); +%% Using the option returns einval, so it is not implemented. +recvtclass_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); %% Does not return any value - not implemented for pktoptions recvtclass_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {3,1,0}); %% @@ -2002,18 +2025,18 @@ semver_lt({X1,Y1,Z1}, {X2,Y2,Z2}) -> end; semver_lt(_, {_,_,_}) -> false. -test_pktoptions(Family, Spec, OSFilter, CheckAccept) -> +test_pktoptions(Family, Spec, OSFilter, CheckConnect) -> OSType = os:type(), OSVer = os:version(), case OSFilter(OSType, OSVer) of true -> io:format("Os: ~p, ~p~n", [OSType,OSVer]), - test_pktoptions(Family, Spec, CheckAccept, OSType, OSVer); + test_pktoptions(Family, Spec, CheckConnect, OSType, OSVer); false -> {skip,{not_supported_for_os_version,{OSType,OSVer}}} end. %% -test_pktoptions(Family, Spec, CheckAccept, OSType, OSVer) -> +test_pktoptions(Family, Spec, CheckConnect, OSType, OSVer) -> Timeout = 5000, RecvOpts = [RecvOpt || {RecvOpt,_,_} <- Spec], TrueRecvOpts = [{RecvOpt,true} || {RecvOpt,_,_} <- Spec], @@ -2105,7 +2128,7 @@ test_pktoptions(Family, Spec, CheckAccept, OSType, OSVer) -> ok = gen_tcp:close(S4), ok = gen_tcp:close(S3), ok = gen_tcp:close(L), - (Result1 and ((not CheckAccept) or (Result2 and Result3))) + (Result1 and ((not CheckConnect) or (Result2 and Result3))) orelse exit({failed, [{OptsVals1,OptsVals4,OptsVals}, diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index 878e48786c..af9985de45 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -616,13 +616,14 @@ recvtclass(_Config) -> %% when machines with newer versions gets installed... %% If the test still fails for a plausible reason these %% version numbers simply should be increased. +%% Or maybe we should change to only test on known good platforms? %% Using the option returns einval, so it is not implemented. recvtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0}); %% Using the option returns einval, so it is not implemented. recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); %% Using the option returns einval, so it is not implemented. -recvtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,11,0}); +recvtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% recvtos_ok({unix,_}, _) -> true; recvtos_ok(_, _) -> false. -- cgit v1.2.3 From 9674ece7b2e57e265bd1dc55a623cc7888a1caa0 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Tue, 18 Sep 2018 15:46:31 +0200 Subject: Elaborate the disclaimer for 'pktoptions' --- lib/kernel/doc/src/gen_tcp.xml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index cf649991d0..24d63693fd 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -74,13 +74,25 @@ do_recv(Sock, Bs) ->

    If the platform implements the IPv4 option - IP_PKTOPTIONS (probably Linux specific), or the IPv6 option + IP_PKTOPTIONS, or the IPv6 option IPV6_PKTOPTIONS or IPV6_2292PKTOPTIONS for the socket this value is returned from inet:getopts/2 when called with the option name pktoptions.

    + +

    + This option appears to be VERY Linux specific, + and its existence in future Linux kernel versions + is also worrying since the option is part of RFC 2292 + which is since long (2003) obsoleted by RFC 3542 + that explicitly removes this possibility to get + packet information from a stream socket. + For comparision: it has existed in FreeBSD but is now removed, + at least since FreeBSD 10. +

    +
    -- cgit v1.2.3 From 224a42d658d3c08e3ce080fa8041c161e16f3863 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 20 Sep 2018 16:56:26 +0200 Subject: [logger] Add SetOrUpdate parameter to handler callback changing_config Since logger has now knowledge of the handler specific data ('config' field in the handler configuration map), it can not merge this data structure with default or old values upon configuration change. Instead, the handler callback changing_config must do this. Earlier, this callback did not reflect if the configuration change was initiated by a call to set_handler_config or update_handler_config, so the handler did not know if unspecified fields should get default values or the values from the existing configuration. To overcome this problem, the new parameter SetOrUpdate is added to this callback. If SetOrUpdate equals set, then default values should be used. If SetOrUpdate equals update, then existing configuration values should be used. --- lib/kernel/doc/src/logger.xml | 26 ++++++++++---- lib/kernel/doc/src/logger_chapter.xml | 6 ++-- lib/kernel/src/logger_server.erl | 64 ++++++++++++++++++++++++----------- 3 files changed, 67 insertions(+), 29 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index 464c65ba76..8f54d0e54f 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -1041,10 +1041,11 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)). - HModule:changing_config(Config1, Config2) -> {ok, Config3} | {error, Reason} + HModule:changing_config(SetOrUpdate, OldConfig, NewConfig) -> {ok, Config} | {error, Reason} The configuration for this handler is about to change. - Config1 = Config2 = Config3 = + SetOrUpdate = set | update + OldConfig = NewConfig = Config = handler_config() Reason = term() @@ -1053,12 +1054,25 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)).

    The function is called on a temporary process when the configuration for a handler is about to change. The purpose is to verify and act on the new configuration.

    -

    Config1 is the existing configuration - and Config2 is the new configuration.

    +

    OldConfig is the existing configuration + and NewConfig is the new configuration.

    The handler identity is associated with the id key - in Config1.

    + in OldConfig.

    +

    SetOrUpdate has the value set if the + configuration change originates from a call to + + set_handler_config/2,3, and update + if it originates from + update_handler_config/2. The handler can + use this parameteter to decide how to update the value of + the config field, that is, the handler specific + configuration data. Typically, if SetOrUpdate + equals set, values that are not specified must be + given their default values. If SetOrUpdate + equals update, the values found in OldConfig + must be used instead.

    If everything succeeds, the callback function must return a - possibly adjusted configuration in {ok,Config3}.

    + possibly adjusted configuration in {ok,Config}.

    If the configuration is faulty, the callback function must return {error,Reason}.

    diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 4a81cfa34a..7e88f82dad 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -384,7 +384,7 @@ logger:debug(#{got => connection_request, id => Id, state => State},

    In addition to the mandatory callback function log/2, a handler module can export the optional callback - functions adding_handler/1, changing_config/2 + functions adding_handler/1, changing_config/3 and removing_handler/1. See section Handler Callback Functions in the logger(3) manual page for @@ -1024,7 +1024,7 @@ ok adding_handler(Config) removing_handler(Config) - changing_config(OldConfig, NewConfig) + changing_config(SetOrUpdate, OldConfig, NewConfig)

    When a handler is added, by for example a call to @@ -1045,7 +1045,7 @@ ok or logger:update_handler_config/2 is called, Logger - calls HModule:changing_config(OldConfig, NewConfig). If + calls HModule:changing_config(SetOrUpdate, OldConfig, NewConfig). If this function returns {ok,NewConfig1}, Logger writes NewConfig1 to the configuration database.

    diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index a1d40f1123..a14de2664e 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -105,12 +105,17 @@ cache_module_level(Module) -> gen_server:cast(?SERVER,{cache_module_level,Module}). set_config(Owner,Key,Value) -> - update_config(Owner,#{Key=>Value}). + case sanity_check(Owner,Key,Value) of + ok -> + call({change_config,set,Owner,Key,Value}); + Error -> + Error + end. set_config(Owner,Config) -> case sanity_check(Owner,Config) of ok -> - call({set_config,Owner,Config}); + call({change_config,set,Owner,Config}); Error -> Error end. @@ -118,7 +123,7 @@ set_config(Owner,Config) -> update_config(Owner, Config) -> case sanity_check(Owner,Config) of ok -> - call({update_config,Owner,Config}); + call({change_config,update,Owner,Config}); Error -> Error end. @@ -204,17 +209,35 @@ handle_call({add_filter,Id,Filter}, _From,#state{tid=Tid}=State) -> handle_call({remove_filter,Id,FilterId}, _From, #state{tid=Tid}=State) -> Reply = do_remove_filter(Tid,Id,FilterId), {reply,Reply,State}; -handle_call({update_config,primary,NewConfig}, _From, #state{tid=Tid}=State) -> +handle_call({change_config,SetOrUpd,primary,Config0}, _From, + #state{tid=Tid}=State) -> + {ok,#{handlers:=Handlers}=OldConfig} = logger_config:get(Tid,primary), + Default = + case SetOrUpd of + set -> default_config(primary); + update -> OldConfig + end, + Config = maps:merge(Default,Config0), + Reply = logger_config:set(Tid,primary,Config#{handlers=>Handlers}), + {reply,Reply,State}; +handle_call({change_config,_SetOrUpd,primary,Key,Value}, _From, + #state{tid=Tid}=State) -> {ok,OldConfig} = logger_config:get(Tid,primary), - Config = maps:merge(OldConfig,NewConfig), - {reply,logger_config:set(Tid,primary,Config),State}; -handle_call({update_config,HandlerId,NewConfig}, From, #state{tid=Tid}=State) -> + Reply = logger_config:set(Tid,primary,OldConfig#{Key=>Value}), + {reply,Reply,State}; +handle_call({change_config,SetOrUpd,HandlerId,Config0}, From, + #state{tid=Tid}=State) -> case logger_config:get(Tid,HandlerId) of {ok,#{module:=Module}=OldConfig} -> - Config = maps:merge(OldConfig,NewConfig), + Default = + case SetOrUpd of + set -> default_config(HandlerId,Module); + update -> OldConfig + end, + Config = maps:merge(Default,Config0), call_h_async( fun() -> - call_h(Module,changing_config,[OldConfig,Config], + call_h(Module,changing_config,[SetOrUpd,OldConfig,Config], {ok,Config}) end, fun({ok,Config1}) -> @@ -222,21 +245,17 @@ handle_call({update_config,HandlerId,NewConfig}, From, #state{tid=Tid}=State) -> (Error) -> Error end,From,State); - Error -> - {reply,Error,State} + _ -> + {reply,{error,{not_found,HandlerId}},State} end; -handle_call({set_config,primary,Config0}, _From, #state{tid=Tid}=State) -> - Config = maps:merge(default_config(primary),Config0), - {ok,#{handlers:=Handlers}} = logger_config:get(Tid,primary), - Reply = logger_config:set(Tid,primary,Config#{handlers=>Handlers}), - {reply,Reply,State}; -handle_call({set_config,HandlerId,Config0}, From, #state{tid=Tid}=State) -> +handle_call({change_config,SetOrUpd,HandlerId,Key,Value}, From, + #state{tid=Tid}=State) -> case logger_config:get(Tid,HandlerId) of {ok,#{module:=Module}=OldConfig} -> - Config = maps:merge(default_config(HandlerId,Module),Config0), + Config = OldConfig#{Key=>Value}, call_h_async( fun() -> - call_h(Module,changing_config,[OldConfig,Config], + call_h(Module,changing_config,[SetOrUpd,OldConfig,Config], {ok,Config}) end, fun({ok,Config1}) -> @@ -320,7 +339,7 @@ call(Request) -> true when Action == add_handler; Action == remove_handler; Action == add_filter; Action == remove_filter; - Action == update_config; Action == set_config -> + Action == change_config -> {error,{attempting_syncronous_call_to_self,Request}}; _ -> gen_server:call(?SERVER,Request,?DEFAULT_LOGGER_CALL_TIMEOUT) @@ -466,6 +485,11 @@ call_h(Module, Function, Args, DefRet) -> catch C:R:S -> case {C,R,S} of + {error,undef,[{Module,Function=changing_config,Args,_}|_]} + when length(Args)=:=3 -> + %% Backwards compatible call, if changing_config/3 + %% did not exist. + call_h(Module, Function, tl(Args), DefRet); {error,undef,[{Module,Function,Args,_}|_]} -> DefRet; _ -> -- cgit v1.2.3 From d04b53936d5e9f84a3066ecd466993671b1428bf Mon Sep 17 00:00:00 2001 From: Henrik Nord Date: Fri, 21 Sep 2018 12:23:25 +0200 Subject: Update copyright year --- lib/kernel/doc/src/net_kernel.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml index d3bd7365e2..bfbe7a6470 100644 --- a/lib/kernel/doc/src/net_kernel.xml +++ b/lib/kernel/doc/src/net_kernel.xml @@ -4,7 +4,7 @@
    - 19962017 + 19962018 Ericsson AB. All Rights Reserved. -- cgit v1.2.3 From fb7006280f8d5a45459e1fba066fe6f6131e8e86 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Mon, 24 Sep 2018 11:32:41 +0200 Subject: Prepare release --- lib/kernel/doc/src/notes.xml | 105 +++++++++++++++++++++++++++++++++++++++++++ lib/kernel/vsn.mk | 2 +- 2 files changed, 106 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index c766c18233..8188ede6a2 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,111 @@

    This document describes the changes made to the Kernel application.

    +
    Kernel 6.1 + +
    Fixed Bugs and Malfunctions + + +

    + The values all and none are documented as + valid value for the Kernel configuration parameter + logger_level, but would cause a crash during node + start. This is now corrected.

    +

    + Own Id: OTP-15143

    +
    + +

    + Fix some potential buggy behavior in how ticks are sent + on inter node distribution connections. Tick is now sent + to c-node even if there are unsent buffered data, as + c-nodes need ticks in order to send reply ticks. The + amount of sent data was also calculated wrongly when + ticks were suppressed due to unsent buffered data.

    +

    + Own Id: OTP-15162 Aux Id: ERIERL-191

    +
    + +

    + Non semantic change in dist_util.erl to silence dialyzer + warning.

    +

    + Own Id: OTP-15170

    +
    + +

    + Fixed net_kernel:connect_node(node()) to return + true (and do nothing) as it always has before + OTP-21.0. Also documented this successful "self connect" + as the expected behavior.

    +

    + Own Id: OTP-15182 Aux Id: ERL-643

    +
    + +

    + The single_line option on logger_formatter would in some + cases add an unwanted comma after the association arrows + in a map. This is now corrected.

    +

    + Own Id: OTP-15228

    +
    + +

    + Improved robustness of distribution connection setup. In + OTP-21.0 a truly asynchronous connection setup was + introduced. This is further improvement on that work to + make the emulator more robust and also be able to recover + in cases when involved Erlang processes misbehave.

    +

    + Own Id: OTP-15297 Aux Id: OTP-15279, OTP-15280

    +
    +
    +
    + + +
    Improvements and New Features + + +

    + A new macro, ?LOG(Level,...), is added. This is + equivalent to the existing ?LOG_<LEVEL>(...) + macros.

    +

    + A new variant of Logger report callback is added, which + takes an extra argument containing options for size + limiting and line breaks. Module proc_lib in + STDLIB uses this for crash reports.

    +

    + Logger configuration is now checked a bit more for + errors.

    +

    + Own Id: OTP-15132

    +
    + +

    + The socket options recvtos, recvttl, + recvtclass and pktoptions have been + implemented in the socket modules. See the documentation + for the gen_tcp, gen_udp and inet + modules. Note that support for these in the runtime + system is platform dependent. Especially for + pktoptions which is very Linux specific and + obsoleted by the RFCs that defined it.

    +

    + Own Id: OTP-15145 Aux Id: ERIERL-187

    +
    + +

    + Add logger:set_application_level/2 for setting the + logger level of all modules in one application.

    +

    + Own Id: OTP-15146

    +
    +
    +
    + +
    +
    Kernel 6.0.1
    Fixed Bugs and Malfunctions diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index fe22e2af98..df95174c9f 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 6.0.1 +KERNEL_VSN = 6.1 -- cgit v1.2.3 From 26507e04fdc5c9910a24ddc44010c45d2d0027da Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 27 Sep 2018 12:14:51 +0200 Subject: Update kernel, stdlib and sasl appups for OTP-21.2 --- lib/kernel/src/kernel.appup.src | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src index 305a1c788c..0c0435e051 100644 --- a/lib/kernel/src/kernel.appup.src +++ b/lib/kernel/src/kernel.appup.src @@ -20,9 +20,11 @@ %% Up from - max one major revision back [{<<"5\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-20.0 {<<"5\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-20.1+ - {<<"6\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-21 + {<<"6\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-21.0 + {<<"6\\.1(\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-21.1 %% Down to - max one major revision back [{<<"5\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-20.0 {<<"5\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-20.1+ - {<<"6\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-21 + {<<"6\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-21.0 + {<<"6\\.1(\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-21.1 }. -- cgit v1.2.3 From 10dd2b092875d460c1f1785e247c061093d5f9c5 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Tue, 25 Sep 2018 13:59:48 +0200 Subject: Fix bug for sockopt pktoptions on BSD The macros for the BSD style option names had accidentally wound up outside the option parsing loop, causing unclear behaviour and Valgrind errors. --- lib/kernel/test/gen_tcp_misc_SUITE.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index 358ca872f7..194522c009 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -1981,8 +1981,8 @@ recvtclass(_Config) -> %% pktoptions is not supported for IPv4 recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); recvtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0}); -recvtos_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); %% Using the option returns einval, so it is not implemented. +recvtos_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); recvtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% Does not return any value - not implemented for pktoptions recvtos_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {3,1,0}); @@ -1993,8 +1993,8 @@ recvtos_ok(_, _) -> false. %% pktoptions is not supported for IPv4 recvttl_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); recvttl_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0}); -recvttl_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); %% Using the option returns einval, so it is not implemented. +recvttl_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); recvttl_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% recvttl_ok({unix,linux}, _) -> true; -- cgit v1.2.3 From b32911645a20230864eaee965d6a7f1be248e47e Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Fri, 21 Sep 2018 15:50:39 +0200 Subject: Implement {netns,NS} option for inet:getifaddrs/1 Also implement the same option for the legacy undocumented functions inet:getif/1,getiflist/1,ifget/2,ifset/2. The arity 1 functions had before this change got signatures that took a socket port that was used to do the needed syscall, so now the signature was extended to also take an option list with the only supported option {netns,Namespace}. The Socket argument variant remains unsupported. For inet:getifaddrs/1 the documentation file was changed to old style function name definition so be able to hide the Socket argument variant that is visible in the type spec. The arity 2 functions had got an option list as second argument. This list had to be partitioned into one list for the namespace option(s) and the other for the rest. The namespace option list was then fed to the already existing namespace support for socket opening, which places the socket in a namespace and hence made all these functions that in inet_drv.c used getsockopt() work without change. The functions that used getifaddrs() in inet_drv.c had to be changed in inet_drv.c to swap namespaces around the getifaddrs() syscall. This functionality was separated into a new function call_getifaddrs(). --- lib/kernel/doc/src/inet.xml | 182 +++++++++++++++++++++++++++++++++++--------- lib/kernel/src/inet.erl | 82 ++++++++++++++------ 2 files changed, 202 insertions(+), 62 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index ed775d67eb..127c110df4 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -197,6 +197,79 @@ fe80::204:acff:fe17:bf38

    + + + +

    + Interface address description list returned from + getifaddrs/0,1 + for a named interface, translated from the returned + data of the POSIX API function getaddrinfo(). +

    +

    + Hwaddr is hardware dependent, + for example, on Ethernet interfaces it is + the 6-byte Ethernet address (MAC address (EUI-48 address)). +

    +

    + The tuples {addr,Addr}, + {netmask,Netmask}, and possibly + {broadaddr,Broadaddr} or + {dstaddr,Dstaddr} + are repeated in the list + if the interface has got multiple addresses. + An interface may have multiple {flag,_} tuples + for example if it has different flags + for different address families. + Multiple {hwaddr,Hwaddr} tuples + is hard to say anything definite about, though. + The tuple {flag,Flags} is mandatory, + all others are optional. +

    +

    + Do not rely too much on the order + of Flags atoms + or the Ifopt tuples. + There are however some rules: +

    + +

    + A {flag,_} tuple applies to all other tuples that follow. +

    +

    + Immediately after {addr,_} follows {netmask,_}. +

    +

    + Immediately thereafter may {broadaddr,_} follow + if broadcast is member of Flags, + or {dstaddr,_} if pointtopoint + is member of Flags. + Both {dstaddr,_} and {broadaddr,_} + does not occur for the same {addr,_}. +

    +

    + Any {netmask,_}, {broadaddr,_}, or + {dstaddr,_} tuples that follow an + {addr,Addr} + tuple concerns the address Addr. +

    +
    +

    + The tuple {hwaddr,_} is not returned on Solaris, as the + hardware address historically belongs to the link layer + and it is not returned by the Solaris API function + getaddrinfo(). +

    + +

    + On Windows, the data is fetched from different + OS API functions, so the Netmask + and Broadaddr values may be calculated, + just as some Flags values. +

    +
    +
    +
    @@ -324,38 +397,64 @@ fe80::204:acff:fe17:bf38 Return a list of interfaces and their addresses. -

    Returns a list of 2-tuples containing interface names and the - interface addresses. Ifname is a Unicode string. - Hwaddr is hardware dependent, for example, on - Ethernet interfaces - it is the 6-byte Ethernet address (MAC address (EUI-48 address)).

    -

    The tuples {addr,Addr}, {netmask,_}, and - {broadaddr,_} are repeated in the result list if the interface - has multiple addresses. If you come across an interface with - multiple {flag,_} or {hwaddr,_} tuples, you have - a strange interface or possibly a bug in this function. The tuple - {flag,_} is mandatory, all others are optional.

    -

    Do not rely too much on the order of Flag atoms - or Ifopt tuples. There are however some rules:

    - -

    Immediately after - {addr,_} follows {netmask,_}.

    -

    Immediately thereafter follows {broadaddr,_} if flag - broadcast is not set and flag - pointtopoint is set.

    -

    Any {netmask,_}, {broadaddr,_}, or - {dstaddr,_} tuples that follow an {addr,_} - tuple concerns that address.

    -
    -

    The tuple {hwaddr,_} is not returned on Solaris, as the - hardware address historically belongs to the link layer and only - the superuser can read such addresses.

    - -

    On Windows, the data is fetched from different OS API functions, - so the Netmask and Broadaddr - values can be calculated, just as some Flag - values. Report flagrant bugs.

    -
    +

    + Returns a list of 2-tuples containing interface names and + the interfaces' addresses. Ifname + is a Unicode string and + Ifopts is a list of + interface address description tuples. +

    +

    + The interface address description tuples + are documented under the type of the + + Ifopts + + value. +

    +
    + + + + getifaddrs(Opts) -> + {ok, [{Ifname, Ifopts}]} | {error, Posix} + + Return a list of interfaces and their addresses. + + + Opts = [{netns, Namespace}] + + + Namespace = + + file:filename_all() + + + Ifname = string() + + Ifopts = + + getifaddrs_ifopts() + + + Posix = posix() + + +

    + The same as + getifaddrs/0 + but the Option + {netns, Namespace} sets a network namespace + for the OS call, on platforms that supports that feature. +

    +

    + See the socket option + + {netns, Namespace} + + under + setopts/2. +

    @@ -950,20 +1049,29 @@ get_tcpi_sacked(Sock) -> {mode, Mode :: binary | list} -

    Received Packet is delivered as defined by Mode. +

    + Received Packet is delivered as defined by Mode.

    - {netns, Namespace :: file:filename_all()} + + + {netns, Namespace :: file:filename_all()} + -

    Sets a network namespace for the socket. Parameter +

    + Sets a network namespace for the socket. Parameter Namespace is a filename defining the namespace, for example, "/var/run/netns/example", typically created by command ip netns add example. This option must be used in a function call that creates a socket, that is, gen_tcp:connect/3,4, gen_tcp:listen/2, - gen_udp:open/1,2, or - gen_sctp:open/0,1,2.

    + gen_udp:open/1,2 + or + gen_sctp:open/0,1,2, + and also + getifaddrs/1. +

    This option uses the Linux-specific syscall setns(), such as in Linux kernel 3.0 or later, and therefore only exists when the runtime system diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 5dd68dc285..9f22eb6aaa 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -154,6 +154,15 @@ 'running' | 'multicast' | 'loopback']} | {'hwaddr', ether_address()}. +-type getifaddrs_ifopts() :: + [Ifopt :: {flags, Flags :: [up | broadcast | loopback | + pointtopoint | running | multicast]} | + {addr, Addr :: ip_address()} | + {netmask, Netmask :: ip_address()} | + {broadaddr, Broadaddr :: ip_address()} | + {dstaddr, Dstaddr :: ip_address()} | + {hwaddr, Hwaddr :: [byte()]}]. + -type address_family() :: 'inet' | 'inet6' | 'local'. -type socket_protocol() :: 'tcp' | 'udp' | 'sctp'. -type socket_type() :: 'stream' | 'dgram' | 'seqpacket'. @@ -321,32 +330,32 @@ getopts(Socket, Opts) -> Other end. --spec getifaddrs(Socket :: socket()) -> - {'ok', [string()]} | {'error', posix()}. - +-spec getifaddrs( + [Option :: {netns, Namespace :: file:filename_all()}] + | socket()) -> + {'ok', [{Ifname :: string(), + Ifopts :: getifaddrs_ifopts()}]} + | {'error', posix()}. +getifaddrs(Opts) when is_list(Opts) -> + withsocket(fun(S) -> prim_inet:getifaddrs(S) end, Opts); getifaddrs(Socket) -> prim_inet:getifaddrs(Socket). --spec getifaddrs() -> {ok, Iflist} | {error, posix()} when - Iflist :: [{Ifname,[Ifopt]}], - Ifname :: string(), - Ifopt :: {flags,[Flag]} | {addr,Addr} | {netmask,Netmask} - | {broadaddr,Broadaddr} | {dstaddr,Dstaddr} - | {hwaddr,Hwaddr}, - Flag :: up | broadcast | loopback | pointtopoint - | running | multicast, - Addr :: ip_address(), - Netmask :: ip_address(), - Broadaddr :: ip_address(), - Dstaddr :: ip_address(), - Hwaddr :: [byte()]. - +-spec getifaddrs() -> + {'ok', [{Ifname :: string(), + Ifopts :: getifaddrs_ifopts()}]} + | {'error', posix()}. getifaddrs() -> withsocket(fun(S) -> prim_inet:getifaddrs(S) end). --spec getiflist(Socket :: socket()) -> - {'ok', [string()]} | {'error', posix()}. +-spec getiflist( + [Option :: {netns, Namespace :: file:filename_all()}] + | socket()) -> + {'ok', [string()]} | {'error', posix()}. + +getiflist(Opts) when is_list(Opts) -> + withsocket(fun(S) -> prim_inet:getiflist(S) end, Opts); getiflist(Socket) -> prim_inet:getiflist(Socket). @@ -363,11 +372,19 @@ getiflist() -> ifget(Socket, Name, Opts) -> prim_inet:ifget(Socket, Name, Opts). --spec ifget(Name :: string() | atom(), Opts :: [if_getopt()]) -> +-spec ifget( + Name :: string() | atom(), + Opts :: [if_getopt() | + {netns, Namespace :: file:filename_all()}]) -> {'ok', [if_getopt_result()]} | {'error', posix()}. ifget(Name, Opts) -> - withsocket(fun(S) -> prim_inet:ifget(S, Name, Opts) end). + {NSOpts,IFOpts} = + lists:partition( + fun ({netns,_}) -> true; + (_) -> false + end, Opts), + withsocket(fun(S) -> prim_inet:ifget(S, Name, IFOpts) end, NSOpts). -spec ifset(Socket :: socket(), Name :: string() | atom(), @@ -377,11 +394,19 @@ ifget(Name, Opts) -> ifset(Socket, Name, Opts) -> prim_inet:ifset(Socket, Name, Opts). --spec ifset(Name :: string() | atom(), Opts :: [if_setopt()]) -> +-spec ifset( + Name :: string() | atom(), + Opts :: [if_setopt() | + {netns, Namespace :: file:filename_all()}]) -> 'ok' | {'error', posix()}. ifset(Name, Opts) -> - withsocket(fun(S) -> prim_inet:ifset(S, Name, Opts) end). + {NSOpts,IFOpts} = + lists:partition( + fun ({netns,_}) -> true; + (_) -> false + end, Opts), + withsocket(fun(S) -> prim_inet:ifset(S, Name, IFOpts) end, NSOpts). -spec getif() -> {'ok', [{ip_address(), ip_address() | 'undefined', ip_address()}]} | @@ -391,10 +416,14 @@ getif() -> withsocket(fun(S) -> getif(S) end). %% backwards compatible getif --spec getif(Socket :: socket()) -> +-spec getif( + [Option :: {netns, Namespace :: file:filename_all()}] + | socket()) -> {'ok', [{ip_address(), ip_address() | 'undefined', ip_address()}]} | {'error', posix()}. +getif(Opts) when is_list(Opts) -> + withsocket(fun(S) -> getif(S) end, Opts); getif(Socket) -> case prim_inet:getiflist(Socket) of {ok, IfList} -> @@ -415,7 +444,10 @@ getif(Socket) -> end. withsocket(Fun) -> - case inet_udp:open(0,[]) of + withsocket(Fun, []). +%% +withsocket(Fun, Opts) -> + case inet_udp:open(0, Opts) of {ok,Socket} -> Res = Fun(Socket), inet_udp:close(Socket), -- cgit v1.2.3 From a006915a4f3ebbff84ccc83fb87f0283ebe49e8e Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 3 Oct 2018 12:41:28 +0200 Subject: [logger] Add update_handler_config/3 This function is similar to set_handler_config/3, but calls the handler callback changing_config/3 with the first parameter SetOrUpdate=update. --- lib/kernel/doc/src/logger.xml | 48 +++++++++++++++++++++++++++++++++-- lib/kernel/doc/src/logger_chapter.xml | 14 ++++++---- lib/kernel/src/logger.erl | 26 ++++++++++++++++++- lib/kernel/src/logger_server.erl | 11 +++++++- 4 files changed, 90 insertions(+), 9 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index 8f54d0e54f..aa6e17f27b 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -748,6 +748,14 @@ start(_, []) -> exists, its associated value will be changed to the given value. If it does not exist, it will be added.

    +

    If the value is incomplete, which for example can be the + case for the config key, it is up to the handler + implementation how the unspecified parts are set. For all + handlers in the Kernel application, unspecified data for + the config key is set to default values. To update + only specified data, and keep the existing configuration for + the rest, use + update_handler_config/3.

    See the definition of the handler_config() type for more @@ -932,6 +940,42 @@ logger:set_handler_config(HandlerId, maps:merge(Old, Config)). + + + + + + + Add or update configuration data for the specified + handler. + + + + + + + + +

    Add or update configuration data for the specified + handler. If the given Key already + exists, its associated value will be changed + to the given value. If it does not exist, it will + be added.

    +

    If the value is incomplete, which for example can be the + case for the config key, it is up to the handler + implementation how the unspecified parts are set. For all + handlers in the Kernel application, unspecified data for + the config key is not changed. To reset unspecified + data to default values, + use + set_handler_config/3.

    +

    See the definition of + the + handler_config() type for more + information about the different parameters.

    +
    + + Update primary configuration data for Logger. @@ -1063,7 +1107,7 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)). set_handler_config/2,3, and update if it originates from - update_handler_config/2. The handler can + update_handler_config/2,3. The handler can use this parameteter to decide how to update the value of the config field, that is, the handler specific configuration data. Typically, if SetOrUpdate @@ -1150,7 +1194,7 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)). logger:set_handler_config/2,3 - logger:updata_handler_config/2 + logger:updata_handler_config/2,3 logger:update_formatter_config/2 diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 7e88f82dad..777c37058e 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -555,7 +555,7 @@ logger:debug(#{got => connection_request, id => Id, state => State}, set_handler_config/2,3 - update_handler_config/2 + update_handler_config/2,3 add_handler_filter/3 @@ -704,9 +704,13 @@ logger:debug(#{got => connection_request, id => Id, state => State},

    If HandlerId is default, then this entry modifies the default handler, equivalent to calling

    -
    
    -		logger:set_handler_config(default, Module, HandlerConfig)
    -	      
    +
    
    +		logger:remove_handler(default)
    +	    
    +

    followed by

    +
    
    +		logger:add_handler(default, Module, HandlerConfig)
    +	    

    For all other values of HandlerId, this entry adds a new handler, equivalent to calling

    
    @@ -1043,7 +1047,7 @@ ok

    When logger:set_handler_config/2,3 or - logger:update_handler_config/2 is called, + logger:update_handler_config/2,3 is called, Logger calls HModule:changing_config(SetOrUpdate, OldConfig, NewConfig). If this function returns {ok,NewConfig1}, Logger diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 752dd8d493..3ffa8a0c4e 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -43,7 +43,8 @@ get_module_level/0, get_module_level/1, set_primary_config/1, set_primary_config/2, set_handler_config/2, set_handler_config/3, - update_primary_config/1, update_handler_config/2, + update_primary_config/1, + update_handler_config/2, update_handler_config/3, update_formatter_config/2, update_formatter_config/3, get_primary_config/0, get_handler_config/1, get_handler_config/0, get_handler_ids/0, get_config/0, @@ -423,6 +424,29 @@ set_handler_config(HandlerId,Config) -> update_primary_config(Config) -> logger_server:update_config(primary,Config). +-spec update_handler_config(HandlerId,level,Level) -> Return when + HandlerId :: handler_id(), + Level :: level() | all | none, + Return :: ok | {error,term()}; + (HandlerId,filter_default,FilterDefault) -> Return when + HandlerId :: handler_id(), + FilterDefault :: log | stop, + Return :: ok | {error,term()}; + (HandlerId,filters,Filters) -> Return when + HandlerId :: handler_id(), + Filters :: [{filter_id(),filter()}], + Return :: ok | {error,term()}; + (HandlerId,formatter,Formatter) -> Return when + HandlerId :: handler_id(), + Formatter :: {module(), formatter_config()}, + Return :: ok | {error,term()}; + (HandlerId,config,Config) -> Return when + HandlerId :: handler_id(), + Config :: term(), + Return :: ok | {error,term()}. +update_handler_config(HandlerId,Key,Value) -> + logger_server:update_config(HandlerId,Key,Value). + -spec update_handler_config(HandlerId,Config) -> ok | {error,term()} when HandlerId :: handler_id(), Config :: handler_config(). diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index a14de2664e..9e043f9362 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -27,7 +27,8 @@ add_filter/2, remove_filter/2, set_module_level/2, unset_module_level/0, unset_module_level/1, cache_module_level/1, - set_config/2, set_config/3, update_config/2, + set_config/2, set_config/3, + update_config/2, update_config/3, update_formatter_config/2]). %% gen_server callbacks @@ -120,6 +121,14 @@ set_config(Owner,Config) -> Error end. +update_config(Owner,Key,Value) -> + case sanity_check(Owner,Key,Value) of + ok -> + call({change_config,update,Owner,Key,Value}); + Error -> + Error + end. + update_config(Owner, Config) -> case sanity_check(Owner,Config) of ok -> -- cgit v1.2.3 From 1838418b9193140f6ba5f5716884b62aad7b662d Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 3 Oct 2018 13:00:41 +0200 Subject: [logger] Start using handler callback changing_config/3 in built-in handlers The new parameter to this function, SetOrUpdate, indicates how unspecified configuration data fields shall be set. The rule is that if SetOrUpdate equals set, then default values shall be used, and if SetOrUpdate equals update, then existing configuration values shall be used. Consequently, these examples now apply to logger_std_h and logger_disk_log_h: set_handler_config(default, config, #{sync_mode_qlen => 20}) sets the value of sync_mode_qlen to 20, and resets all other (writable) fields in the config map to their default values. update_handler_config(default, config, #{sync_mode_qlen => 20}) sets the value of sync_mode_qlen to 20, and leaves all other fields in the config map unchanged. --- lib/kernel/doc/src/logger_disk_log_h.xml | 10 ++ lib/kernel/doc/src/logger_std_h.xml | 4 +- lib/kernel/src/logger_disk_log_h.erl | 75 +++++++------ lib/kernel/src/logger_server.erl | 75 +++++++++---- lib/kernel/src/logger_std_h.erl | 61 ++++++----- lib/kernel/test/logger_SUITE.erl | 12 ++ lib/kernel/test/logger_disk_log_h_SUITE.erl | 164 ++++++++++++++++++++++------ lib/kernel/test/logger_std_h_SUITE.erl | 160 +++++++++++++++++++++------ 8 files changed, 414 insertions(+), 147 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger_disk_log_h.xml b/lib/kernel/doc/src/logger_disk_log_h.xml index dfe2ab3275..d9b941a0a9 100644 --- a/lib/kernel/doc/src/logger_disk_log_h.xml +++ b/lib/kernel/doc/src/logger_disk_log_h.xml @@ -66,6 +66,10 @@ corresponds to the name property in the dlog_option() datatype.

    +

    The value is set when the handler is added, and it can not + be changed in runtime.

    +

    Defaults to the same name as the handler identity, in the + current directory.

    type @@ -73,6 +77,8 @@ corresponds to the type property in the dlog_option() datatype.

    +

    The value is set when the handler is added, and it can not + be changed in runtime.

    Defaults to wrap.

    max_no_files @@ -82,6 +88,8 @@ corresponds to the MaxNoFiles element in the size property in the dlog_option() datatype.

    +

    The value is set when the handler is added, and it can not + be changed in runtime.

    Defaults to 10.

    The setting has no effect on a halt log.

    @@ -93,6 +101,8 @@ corresponds to the MaxNoBytes element in the size property in the dlog_option() datatype.

    +

    The value is set when the handler is added, and it can not + be changed in runtime.

    Defaults to 1048576 bytes for a wrap log, and infinity for a halt log.

    diff --git a/lib/kernel/doc/src/logger_std_h.xml b/lib/kernel/doc/src/logger_std_h.xml index fcd8189bae..e156f5719b 100644 --- a/lib/kernel/doc/src/logger_std_h.xml +++ b/lib/kernel/doc/src/logger_std_h.xml @@ -74,7 +74,9 @@ circular logging. Use the disk_log handler, logger_disk_log_h, for this.

    -

    Defaults to standard_io.

    +

    The value is set when the handler is added, and it can not + be changed in runtime.

    +

    Defaults to standard_io.

    filesync_repeat_interval diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index a8f141f135..bb8b9f08a4 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -33,7 +33,7 @@ terminate/2, code_change/3]). %% logger callbacks --export([log/2, adding_handler/1, removing_handler/1, changing_config/2]). +-export([log/2, adding_handler/1, removing_handler/1, changing_config/3]). %% handler internal -export([log_handler_info/4]). @@ -114,9 +114,8 @@ reset(Name) -> %%% Handler being added adding_handler(#{id:=Name}=Config) -> case check_config(adding, Config) of - {ok, Config1} -> + {ok, #{config:=HConfig}=Config1} -> %% create initial handler state by merging defaults with config - HConfig = maps:get(config, Config1, #{}), HState = maps:merge(get_init_state(), HConfig), case logger_h_common:overload_levels_ok(HState) of true -> @@ -133,32 +132,40 @@ adding_handler(#{id:=Name}=Config) -> %%%----------------------------------------------------------------- %%% Updating handler config -changing_config(OldConfig = #{id:=Name, config:=OldHConfig}, - NewConfig = #{id:=Name, config:=NewHConfig}) -> - #{type:=Type, file:=File, max_no_files:=MaxFs, - max_no_bytes:=MaxBytes} = OldHConfig, - case NewHConfig of - #{type:=Type, file:=File, max_no_files:=MaxFs, - max_no_bytes:=MaxBytes} -> - changing_config1(OldConfig, NewConfig); - _ -> - {error,{illegal_config_change,OldConfig,NewConfig}} - end; -changing_config(OldConfig, NewConfig) -> - {error,{illegal_config_change,OldConfig,NewConfig}}. +changing_config(SetOrUpdate,OldConfig=#{config:=OldHConfig},NewConfig) -> + WriteOnce = maps:with([type,file,max_no_files,max_no_bytes],OldHConfig), + ReadOnly = maps:with([handler_pid,mode_tab],OldHConfig), + NewHConfig0 = maps:get(config, NewConfig, #{}), + Default = + case SetOrUpdate of + set -> + %% Do not reset write-once fields to defaults + maps:merge(get_default_config(),WriteOnce); + update -> + OldHConfig + end, + + %% Allow (accidentially) included read-only fields - just overwrite them + NewHConfig = maps:merge(maps:merge(Default,NewHConfig0),ReadOnly), + + %% But fail if write-once fields are changed + case maps:with([type,file,max_no_files,max_no_bytes],NewHConfig) of + WriteOnce -> + changing_config1(maps:get(handler_pid,OldHConfig), + OldConfig, + NewConfig#{config=>NewHConfig}); + Other -> + {Old,New} = logger_server:diff_maps(WriteOnce,Other), + {error,{illegal_config_change,#{config=>Old},#{config=>New}}} + end. -changing_config1(OldConfig=#{config:=OldHConfig}, NewConfig) -> +changing_config1(HPid, OldConfig, NewConfig) -> case check_config(changing, NewConfig) of - {ok,NewConfig1 = #{config:=NewHConfig}} -> - #{handler_pid:=HPid, - mode_tab:=ModeTab} = OldHConfig, - NewHConfig1 = NewHConfig#{handler_pid=>HPid, - mode_tab=>ModeTab}, - NewConfig2 = NewConfig1#{config=>NewHConfig1}, - try gen_server:call(HPid, {change_config,OldConfig,NewConfig2}, + Result = {ok,NewConfig1} -> + try gen_server:call(HPid, {change_config,OldConfig,NewConfig1}, ?DEFAULT_CALL_TIMEOUT) of - ok -> {ok,NewConfig2}; - HError -> HError + ok -> Result; + Error -> Error catch _:{timeout,_} -> {error,handler_busy} end; @@ -168,10 +175,12 @@ changing_config1(OldConfig=#{config:=OldHConfig}, NewConfig) -> check_config(adding, #{id:=Name}=Config) -> %% merge handler specific config data - HConfig = merge_default_logopts(Name, maps:get(config, Config, #{})), - case check_h_config(maps:to_list(HConfig)) of + HConfig1 = maps:get(config, Config, #{}), + HConfig2 = maps:merge(get_default_config(), HConfig1), + HConfig3 = merge_default_logopts(Name, HConfig2), + case check_h_config(maps:to_list(HConfig3)) of ok -> - {ok,Config#{config=>HConfig}}; + {ok,Config#{config=>HConfig3}}; Error -> Error end; @@ -438,7 +447,7 @@ code_change(_OldVsn, State, _Extra) -> %%%----------------------------------------------------------------- %%% -get_init_state() -> +get_default_config() -> #{sync_mode_qlen => ?SYNC_MODE_QLEN, drop_mode_qlen => ?DROP_MODE_QLEN, flush_qlen => ?FLUSH_QLEN, @@ -449,10 +458,12 @@ get_init_state() -> overload_kill_qlen => ?OVERLOAD_KILL_QLEN, overload_kill_mem_size => ?OVERLOAD_KILL_MEM_SIZE, overload_kill_restart_after => ?OVERLOAD_KILL_RESTART_AFTER, - dl_sync_int => ?CONTROLLER_SYNC_INTERVAL, - filesync_ok_qlen => ?FILESYNC_OK_QLEN, filesync_repeat_interval => ?FILESYNC_REPEAT_INTERVAL}. +get_init_state() -> + #{dl_sync_int => ?CONTROLLER_SYNC_INTERVAL, + filesync_ok_qlen => ?FILESYNC_OK_QLEN}. + %%%----------------------------------------------------------------- %%% Add a disk_log handler to the logger. %%% This starts a dedicated handler process which should always diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl index 9e043f9362..b7735dbcf7 100644 --- a/lib/kernel/src/logger_server.erl +++ b/lib/kernel/src/logger_server.erl @@ -31,6 +31,9 @@ update_config/2, update_config/3, update_formatter_config/2]). +%% Helper +-export([diff_maps/2]). + %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]). @@ -244,16 +247,22 @@ handle_call({change_config,SetOrUpd,HandlerId,Config0}, From, update -> OldConfig end, Config = maps:merge(Default,Config0), - call_h_async( - fun() -> - call_h(Module,changing_config,[SetOrUpd,OldConfig,Config], - {ok,Config}) - end, - fun({ok,Config1}) -> - logger_config:set(Tid,HandlerId,Config1); - (Error) -> - Error - end,From,State); + case check_config_change(OldConfig,Config) of + ok -> + call_h_async( + fun() -> + call_h(Module,changing_config, + [SetOrUpd,OldConfig,Config], + {ok,Config}) + end, + fun({ok,Config1}) -> + logger_config:set(Tid,HandlerId,Config1); + (Error) -> + Error + end,From,State); + Error -> + {reply,Error,State} + end; _ -> {reply,{error,{not_found,HandlerId}},State} end; @@ -262,16 +271,22 @@ handle_call({change_config,SetOrUpd,HandlerId,Key,Value}, From, case logger_config:get(Tid,HandlerId) of {ok,#{module:=Module}=OldConfig} -> Config = OldConfig#{Key=>Value}, - call_h_async( - fun() -> - call_h(Module,changing_config,[SetOrUpd,OldConfig,Config], - {ok,Config}) - end, - fun({ok,Config1}) -> - logger_config:set(Tid,HandlerId,Config1); - (Error) -> - Error - end,From,State); + case check_config_change(OldConfig,Config) of + ok -> + call_h_async( + fun() -> + call_h(Module,changing_config, + [SetOrUpd,OldConfig,Config], + {ok,Config}) + end, + fun({ok,Config1}) -> + logger_config:set(Tid,HandlerId,Config1); + (Error) -> + Error + end,From,State); + Error -> + {reply,Error,State} + end; _ -> {reply,{error,{not_found,HandlerId}},State} end; @@ -486,6 +501,15 @@ check_formatter({Mod,Config}) -> check_formatter(Formatter) -> throw({invalid_formatter,Formatter}). +%% When changing configuration for a handler, the id and module fields +%% can not be changed. +check_config_change(#{id:=Id,module:=Module},#{id:=Id,module:=Module}) -> + ok; +check_config_change(OldConfig,NewConfig) -> + {Old,New} = logger_server:diff_maps(maps:with([id,module],OldConfig), + maps:with([id,module],NewConfig)), + {error,{illegal_config_change,Old,New}}. + call_h(Module, Function, Args, DefRet) -> %% Not calling code:ensure_loaded + erlang:function_exported here, %% since in some rare terminal cases, the code_server might not @@ -558,3 +582,14 @@ call_h_reply(Unexpected,State) -> {process,?SERVER}, {message,Unexpected}]), {noreply,State}. + +%% Return two maps containing only the fields that differ. +diff_maps(M1,M2) -> + diffs(lists:sort(maps:to_list(M1)),lists:sort(maps:to_list(M2)),#{},#{}). + +diffs([H|T1],[H|T2],D1,D2) -> + diffs(T1,T2,D1,D2); +diffs([{K,V1}|T1],[{K,V2}|T2],D1,D2) -> + diffs(T1,T2,D1#{K=>V1},D2#{K=>V2}); +diffs([],[],D1,D2) -> + {D1,D2}. diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index 66fa6b6ab6..6bbf75e0ab 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/1, removing_handler/1, changing_config/2]). +-export([log/2, adding_handler/1, removing_handler/1, changing_config/3]). %% handler internal -export([log_handler_info/4]). @@ -116,9 +116,8 @@ reset(Name) -> %%% Handler being added adding_handler(#{id:=Name}=Config) -> case check_config(adding, Config) of - {ok, Config1} -> + {ok, #{config:=HConfig}=Config1} -> %% create initial handler state by merging defaults with config - HConfig = maps:get(config, Config1, #{}), HState = maps:merge(get_init_state(), HConfig), case logger_h_common:overload_levels_ok(HState) of true -> @@ -135,22 +134,31 @@ adding_handler(#{id:=Name}=Config) -> %%%----------------------------------------------------------------- %%% Updating handler config -changing_config(OldConfig=#{id:=Name, config:=OldHConfig}, - NewConfig=#{id:=Name}) -> - #{type:=Type, handler_pid:=HPid, mode_tab:=ModeTab} = OldHConfig, - NewHConfig = maps:get(config, NewConfig, #{}), - case maps:get(type, NewHConfig, Type) of - Type -> - NewHConfig1 = NewHConfig#{type=>Type, - handler_pid=>HPid, - mode_tab=>ModeTab}, - changing_config1(HPid, OldConfig, - NewConfig#{config=>NewHConfig1}); - _ -> - {error,{illegal_config_change,OldConfig,NewConfig}} - end; -changing_config(OldConfig, NewConfig) -> - {error,{illegal_config_change,OldConfig,NewConfig}}. +changing_config(SetOrUpdate,OldConfig=#{config:=OldHConfig},NewConfig) -> + WriteOnce = maps:with([type],OldHConfig), + ReadOnly = maps:with([handler_pid,mode_tab],OldHConfig), + NewHConfig0 = maps:get(config, NewConfig, #{}), + Default = + case SetOrUpdate of + set -> + %% Do not reset write-once fields to defaults + maps:merge(get_default_config(),WriteOnce); + update -> + OldHConfig + end, + + %% Allow (accidentially) included read-only fields - just overwrite them + NewHConfig = maps:merge(maps:merge(Default, NewHConfig0),ReadOnly), + + %% But fail if write-once fields are changed + case maps:with([type],NewHConfig) of + WriteOnce -> + changing_config1(maps:get(handler_pid,OldHConfig), + OldConfig, + NewConfig#{config=>NewHConfig}); + Other -> + {error,{illegal_config_change,#{config=>WriteOnce},#{config=>Other}}} + end. changing_config1(HPid, OldConfig, NewConfig) -> case check_config(changing, NewConfig) of @@ -169,8 +177,7 @@ changing_config1(HPid, OldConfig, NewConfig) -> check_config(adding, Config) -> %% Merge in defaults on handler level HConfig0 = maps:get(config, Config, #{}), - HConfig = maps:merge(#{type => standard_io}, - HConfig0), + HConfig = maps:merge(get_default_config(),HConfig0), case check_h_config(maps:to_list(HConfig)) of ok -> {ok,Config#{config=>HConfig}}; @@ -428,8 +435,9 @@ code_change(_OldVsn, State, _Extra) -> %%%----------------------------------------------------------------- %%% -get_init_state() -> - #{sync_mode_qlen => ?SYNC_MODE_QLEN, +get_default_config() -> + #{type => standard_io, + sync_mode_qlen => ?SYNC_MODE_QLEN, drop_mode_qlen => ?DROP_MODE_QLEN, flush_qlen => ?FLUSH_QLEN, burst_limit_enable => ?BURST_LIMIT_ENABLE, @@ -439,10 +447,12 @@ get_init_state() -> overload_kill_qlen => ?OVERLOAD_KILL_QLEN, overload_kill_mem_size => ?OVERLOAD_KILL_MEM_SIZE, overload_kill_restart_after => ?OVERLOAD_KILL_RESTART_AFTER, - file_ctrl_sync_int => ?CONTROLLER_SYNC_INTERVAL, - filesync_ok_qlen => ?FILESYNC_OK_QLEN, filesync_repeat_interval => ?FILESYNC_REPEAT_INTERVAL}. +get_init_state() -> + #{file_ctrl_sync_int => ?CONTROLLER_SYNC_INTERVAL, + filesync_ok_qlen => ?FILESYNC_OK_QLEN}. + %%%----------------------------------------------------------------- %%% Add a standard handler to the logger. %%% This starts a dedicated handler process which should always @@ -825,4 +835,3 @@ sync_dev(Fd, DevName, PrevSyncResult, HandlerName) -> logger_h_common:error_notify({HandlerName,filesync,DevName,Error}), Error end. - diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl index b7ccba8e70..d831d0d108 100644 --- a/lib/kernel/test/logger_SUITE.erl +++ b/lib/kernel/test/logger_SUITE.erl @@ -246,6 +246,18 @@ change_config(_Config) -> {ok,C4} = logger:get_handler_config(h1), C4 = C3#{custom:=new_custom}, + %% Change handler config: Id and module can not be changed + {error,{illegal_config_change,Old,New}} = + logger:set_handler_config(h1,id,newid), + %% Check that only the faulty field is included in return + [{id,h1}] = maps:to_list(Old), + [{id,newid}] = maps:to_list(New), + %% Check that both fields are included when both are changed + {error,{illegal_config_change, + #{id:=h1,module:=?MODULE}, + #{id:=newid,module:=newmodule}}} = + logger:set_handler_config(h1,#{id=>newid,module=>newmodule}), + %% Change primary config: Single key PConfig0 = logger:get_primary_config(), ok = logger:set_primary_config(level,warning), diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index a815db14e9..34f1969e67 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -316,13 +316,29 @@ errors(Config) -> %%! TODO: %%! Check how bad log_opts are handled! - {error,{illegal_config_change,_,_}} = - logger:set_handler_config(Name1, - config, - #{file=>LogFile1, - type=>halt}), - {error,{illegal_config_change,_,_}} = - logger:set_handler_config(Name1,id,new), + {error,{illegal_config_change, + #{config:=#{type:=wrap}}, + #{config:=#{type:=halt}}}} = + logger:update_handler_config(Name1, + config, + #{type=>halt, + file=>LogFile1}), + + {error,{illegal_config_change, + #{config:=#{file:=LogFile1}}, + #{config:=#{file:="newfilename"}}}} = + logger:update_handler_config(Name1, + config, + #{file=>"newfilename"}), + + %% Read-only fields may (accidentially) be included in the change, + %% but it won't take effect + {ok,C} = logger:get_handler_config(Name1), + ok = logger:set_handler_config(Name1,config, + #{handler_pid=>self(), + mode_tab=>erlang:make_ref()}), + {ok,C} = logger:get_handler_config(Name1), + ok = logger:remove_handler(Name1), {error,{not_found,Name1}} = logger:remove_handler(Name1), @@ -403,21 +419,21 @@ config_fail(_Config) -> formatter=>{?MODULE,self()}}), %% can't change the disk log options for a log already in use {error,{illegal_config_change,_,_}} = - logger:set_handler_config(?MODULE,config, - #{max_no_files=>2}), + logger:update_handler_config(?MODULE,config, + #{max_no_files=>2}), %% can't change name of an existing handler {error,{illegal_config_change,_,_}} = - logger:set_handler_config(?MODULE,id,bad), + logger:update_handler_config(?MODULE,id,bad), %% incorrect values of OP params {ok,#{config := HConfig}} = logger:get_handler_config(?MODULE), {error,{invalid_levels,_}} = - logger:set_handler_config(?MODULE,config, - HConfig#{sync_mode_qlen=>100, - flush_qlen=>99}), + logger:update_handler_config(?MODULE,config, + HConfig#{sync_mode_qlen=>100, + flush_qlen=>99}), %% invalid name of config parameter {error,{invalid_config,logger_disk_log_h,{filesync_rep_int,2000}}} = - logger:set_handler_config(?MODULE, config, - HConfig#{filesync_rep_int => 2000}), + logger:update_handler_config(?MODULE, config, + HConfig#{filesync_rep_int => 2000}), ok. config_fail(cleanup,_Config) -> logger:remove_handler(?MODULE). @@ -459,10 +475,26 @@ reconfig(Config) -> log_opts := #{type := ?DISK_LOG_TYPE, max_no_files := ?DISK_LOG_MAX_NO_FILES, max_no_bytes := ?DISK_LOG_MAX_NO_BYTES, - file := _DiskLogFile}} = + file := DiskLogFile}} = logger_disk_log_h:info(?MODULE), + {ok,#{config := + #{sync_mode_qlen := ?SYNC_MODE_QLEN, + drop_mode_qlen := ?DROP_MODE_QLEN, + flush_qlen := ?FLUSH_QLEN, + burst_limit_enable := ?BURST_LIMIT_ENABLE, + burst_limit_max_count := ?BURST_LIMIT_MAX_COUNT, + burst_limit_window_time := ?BURST_LIMIT_WINDOW_TIME, + overload_kill_enable := ?OVERLOAD_KILL_ENABLE, + overload_kill_qlen := ?OVERLOAD_KILL_QLEN, + overload_kill_mem_size := ?OVERLOAD_KILL_MEM_SIZE, + overload_kill_restart_after := ?OVERLOAD_KILL_RESTART_AFTER, + filesync_repeat_interval := ?FILESYNC_REPEAT_INTERVAL, + file := DiskLogFile, + max_no_files := ?DISK_LOG_MAX_NO_FILES, + max_no_bytes := ?DISK_LOG_MAX_NO_BYTES, + type := wrap} = HConfig0}} = + logger:get_handler_config(?MODULE), - {ok,#{config := HConfig0}} = logger:get_handler_config(?MODULE), HConfig1 = HConfig0#{sync_mode_qlen => 1, drop_mode_qlen => 2, flush_qlen => 3, @@ -488,6 +520,29 @@ reconfig(Config) -> overload_kill_restart_after := infinity, filesync_repeat_interval := no_repeat} = logger_disk_log_h:info(?MODULE), + {ok,#{config:=HConfig1}} = logger:get_handler_config(?MODULE), + + ok = logger:update_handler_config(?MODULE, config, + #{flush_qlen => ?FLUSH_QLEN}), + {ok,#{config:=C1}} = logger:get_handler_config(?MODULE), + ct:log("C1: ~p",[C1]), + C1 = HConfig1#{flush_qlen => ?FLUSH_QLEN}, + + ok = logger:set_handler_config(?MODULE, config, #{sync_mode_qlen => 1}), + {ok,#{config:=C2}} = logger:get_handler_config(?MODULE), + ct:log("C2: ~p",[C2]), + C2 = HConfig0#{sync_mode_qlen => 1}, + + ok = logger:set_handler_config(?MODULE, config, #{drop_mode_qlen => 100}), + {ok,#{config:=C3}} = logger:get_handler_config(?MODULE), + ct:log("C3: ~p",[C3]), + C3 = HConfig0#{drop_mode_qlen => 100}, + + ok = logger:update_handler_config(?MODULE, config, #{sync_mode_qlen => 1}), + {ok,#{config:=C4}} = logger:get_handler_config(?MODULE), + ct:log("C4: ~p",[C4]), + C4 = HConfig0#{sync_mode_qlen => 1, + drop_mode_qlen => 100}, ok = logger:remove_handler(?MODULE), @@ -507,6 +562,43 @@ reconfig(Config) -> max_no_bytes := 1024, file := File}} = logger_disk_log_h:info(?MODULE), + {ok,#{config := + #{type := halt, + max_no_files := 1, + max_no_bytes := 1024, + file := File}=HaltHConfig} = Config2} = + logger:get_handler_config(?MODULE), + + ok = logger:update_handler_config(?MODULE, level, notice), + {ok,C5} = logger:get_handler_config(?MODULE), + ct:log("C5: ~p",[C5]), + C5 = Config2#{level => notice}, + + ok = logger:set_handler_config(?MODULE, level, info), + {ok,C6} = logger:get_handler_config(?MODULE), + ct:log("C6: ~p",[C6]), + C6 = Config2#{level => info}, + + %% You are not allowed to actively set the write once fields + %% (type, max_no_files, max_no_bytes, file) in runtime. + {error, {illegal_config_change,_,_}} = + logger:set_handler_config(?MODULE,config,#{type=>wrap}), + {error, {illegal_config_change,_,_}} = + logger:set_handler_config(?MODULE,config,#{max_no_files=>2}), + {error, {illegal_config_change,_,_}} = + logger:set_handler_config(?MODULE,config,#{max_no_bytes=>2048}), + {error, {illegal_config_change,_,_}} = + logger:set_handler_config(?MODULE,config,#{file=>"otherfile.log"}), + {ok,C7} = logger:get_handler_config(?MODULE), + ct:log("C7: ~p",[C7]), + C7 = C6, + + %% ... but if you don't specify the write once fields, then + %% set_handler_config shall NOT reset them to their default value + ok = logger:set_handler_config(?MODULE,config,#{sync_mode_qlen=>1}), + {ok,#{config:=C8}} = logger:get_handler_config(?MODULE), + ct:log("C8: ~p",[C8]), + C8 = HaltHConfig#{sync_mode_qlen=>1}, ok. reconfig(cleanup, _Config) -> @@ -536,7 +628,7 @@ sync(Config) -> %% a disk_log_sync is still performed when handler goes idle {ok,#{config := HConfig}} = logger:get_handler_config(?MODULE), HConfig1 = HConfig#{filesync_repeat_interval => no_repeat}, - ok = logger:set_handler_config(?MODULE, config, HConfig1), + ok = logger:update_handler_config(?MODULE, config, HConfig1), no_repeat = maps:get(filesync_repeat_interval, logger_disk_log_h:info(?MODULE)), @@ -569,13 +661,13 @@ sync(Config) -> [OneSync || _ <- lists:seq(1, 1 + trunc(WaitT/SyncInt))]), HConfig2 = HConfig#{filesync_repeat_interval => SyncInt}, - ok = logger:set_handler_config(?MODULE, config, HConfig2), + ok = logger:update_handler_config(?MODULE, config, HConfig2), SyncInt = maps:get(filesync_repeat_interval, logger_disk_log_h:info(?MODULE)), timer:sleep(WaitT), HConfig3 = HConfig#{filesync_repeat_interval => no_repeat}, - ok = logger:set_handler_config(?MODULE, config, HConfig3), + ok = logger:update_handler_config(?MODULE, config, HConfig3), check_tracer(100), ok. sync(cleanup,_Config) -> @@ -797,7 +889,7 @@ sync_failure(Config) -> LogOpts = maps:get(log_opts, HState), SyncInt = 500, - ok = rpc:call(Node, logger, set_handler_config, + ok = rpc:call(Node, logger, update_handler_config, [?STANDARD_HANDLER, config, #{filesync_repeat_interval => SyncInt}]), Info = rpc:call(Node, logger_disk_log_h, info, [?STANDARD_HANDLER]), @@ -872,7 +964,7 @@ op_switch_to_sync(Config) -> drop_mode_qlen => NumOfReqs+1, flush_qlen => 2*NumOfReqs, burst_limit_enable => false}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Lines = count_lines(Log), NumOfReqs = Lines, @@ -897,7 +989,7 @@ op_switch_to_drop(Config) -> drop_mode_qlen => 2, flush_qlen => Procs*NumOfReqs*Bursts, burst_limit_enable => false}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), %% It sometimes happens that the handler either gets %% the requests in a slow enough pace so that dropping %% never occurs. Therefore, lets generate a number of @@ -943,7 +1035,7 @@ op_switch_to_flush(Config) -> drop_mode_qlen => 300, flush_qlen => 300, burst_limit_enable => false}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), NumOfReqs = 1500, Procs = 10, Bursts = 10, @@ -985,7 +1077,7 @@ limit_burst_disabled(Config) -> burst_limit_window_time => 2000, drop_mode_qlen => 200, flush_qlen => 300}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Logged = count_lines(Log), @@ -1005,7 +1097,7 @@ limit_burst_enabled_one(Config) -> burst_limit_window_time => 2000, drop_mode_qlen => 200, flush_qlen => 300}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Logged = count_lines(Log), @@ -1026,7 +1118,7 @@ limit_burst_enabled_period(Config) -> burst_limit_window_time => BurstTWin, drop_mode_qlen => 20000, flush_qlen => 20001}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), Windows = 3, Sent = send_burst({t,BurstTWin*Windows}, seq, {chars,79}, notice), @@ -1046,7 +1138,7 @@ kill_disabled(Config) -> HConfig#{config=>DLHConfig#{overload_kill_enable=>false, overload_kill_qlen=>10, overload_kill_mem_size=>100}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Logged = count_lines(Log), @@ -1068,7 +1160,7 @@ qlen_kill_new(Config) -> overload_kill_qlen=>10, overload_kill_mem_size=>Mem0+50000, overload_kill_restart_after=>RestartAfter}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), MRef = erlang:monitor(process, Pid0), NumOfReqs = 100, Procs = 4, @@ -1105,7 +1197,7 @@ mem_kill_new(Config) -> overload_kill_qlen=>50000, overload_kill_mem_size=>Mem0+500, overload_kill_restart_after=>RestartAfter}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), MRef = erlang:monitor(process, Pid0), NumOfReqs = 100, Procs = 4, @@ -1139,7 +1231,7 @@ restart_after(Config) -> HConfig#{config=>DLHConfig#{overload_kill_enable=>true, overload_kill_qlen=>10, overload_kill_restart_after=>infinity}}, - ok = logger:set_handler_config(?MODULE, NewHConfig1), + ok = logger:update_handler_config(?MODULE, NewHConfig1), MRef1 = erlang:monitor(process, whereis(h_proc_name())), %% kill handler send_burst({n,100}, {spawn,4,0}, {chars,79}, notice), @@ -1161,7 +1253,7 @@ restart_after(Config) -> HConfig#{config=>DLHConfig#{overload_kill_enable=>true, overload_kill_qlen=>10, overload_kill_restart_after=>RestartAfter}}, - ok = logger:set_handler_config(?MODULE, NewHConfig2), + ok = logger:update_handler_config(?MODULE, NewHConfig2), Pid0 = whereis(h_proc_name()), MRef2 = erlang:monitor(process, Pid0), %% kill handler @@ -1194,7 +1286,7 @@ handler_requests_under_load(Config) -> drop_mode_qlen => 1000, flush_qlen => 2000, burst_limit_enable => false}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), Pid = spawn_link(fun() -> send_requests(?MODULE, 1, [{filesync,[]}, {info,[]}, {reset,[]}, @@ -1227,9 +1319,9 @@ send_requests(HName, TO, Reqs = [{Req,Res}|Rs]) -> Result = case Req of change_config -> - logger:set_handler_config(HName, logger_disk_log_h, - #{overload_kill_enable => - false}); + logger:update_handler_config(HName, logger_disk_log_h, + #{overload_kill_enable => + false}); Func -> logger_disk_log_h:Func(HName) end, diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index 3426567bbf..c5d742ea7f 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -319,11 +319,10 @@ config_fail(_Config) -> ok = logger:add_handler(?MODULE,logger_std_h, #{filter_default=>log, formatter=>{?MODULE,self()}}), - {error,{illegal_config_change,_,_}} = + {error,{illegal_config_change,#{config:=#{type:=_}},#{config:=#{type:=_}}}} = logger:set_handler_config(?MODULE,config, #{type=>{file,"file"}}), - {error,{illegal_config_change,_,_}} = - logger:set_handler_config(?MODULE,id,bad), + {error,{invalid_levels,_}} = logger:set_handler_config(?MODULE,config, #{sync_mode_qlen=>100, @@ -331,6 +330,15 @@ config_fail(_Config) -> {error,{invalid_config,logger_std_h,{filesync_rep_int,2000}}} = logger:set_handler_config(?MODULE, config, #{filesync_rep_int => 2000}), + + %% Read-only fields may (accidentially) be included in the change, + %% but it won't take effect + {ok,C} = logger:get_handler_config(?MODULE), + ok = logger:set_handler_config(?MODULE,config, + #{handler_pid=>self(), + mode_tab=>erlang:make_ref()}), + {ok,C} = logger:get_handler_config(?MODULE), + ok. config_fail(cleanup,_Config) -> @@ -457,9 +465,26 @@ reconfig(Config) -> overload_kill_qlen := ?OVERLOAD_KILL_QLEN, overload_kill_mem_size := ?OVERLOAD_KILL_MEM_SIZE, overload_kill_restart_after := ?OVERLOAD_KILL_RESTART_AFTER, - filesync_repeat_interval := ?FILESYNC_REPEAT_INTERVAL} = + filesync_repeat_interval := ?FILESYNC_REPEAT_INTERVAL} = DefaultInfo = logger_std_h:info(?MODULE), + {ok, + #{config:= + #{type := standard_io, + sync_mode_qlen := ?SYNC_MODE_QLEN, + drop_mode_qlen := ?DROP_MODE_QLEN, + flush_qlen := ?FLUSH_QLEN, + burst_limit_enable := ?BURST_LIMIT_ENABLE, + burst_limit_max_count := ?BURST_LIMIT_MAX_COUNT, + burst_limit_window_time := ?BURST_LIMIT_WINDOW_TIME, + overload_kill_enable := ?OVERLOAD_KILL_ENABLE, + overload_kill_qlen := ?OVERLOAD_KILL_QLEN, + overload_kill_mem_size := ?OVERLOAD_KILL_MEM_SIZE, + overload_kill_restart_after := ?OVERLOAD_KILL_RESTART_AFTER, + filesync_repeat_interval := ?FILESYNC_REPEAT_INTERVAL} = + DefaultHConf}} + = logger:get_handler_config(?MODULE), + ok = logger:set_handler_config(?MODULE, config, #{sync_mode_qlen => 1, drop_mode_qlen => 2, @@ -485,7 +510,77 @@ reconfig(Config) -> overload_kill_qlen := 100000, overload_kill_mem_size := 10000000, overload_kill_restart_after := infinity, - filesync_repeat_interval := no_repeat} = logger_std_h:info(?MODULE), + filesync_repeat_interval := no_repeat} = Info = logger_std_h:info(?MODULE), + + {ok,#{config := + #{type := standard_io, + sync_mode_qlen := 1, + drop_mode_qlen := 2, + flush_qlen := 3, + burst_limit_enable := false, + burst_limit_max_count := 10, + burst_limit_window_time := 10, + overload_kill_enable := true, + overload_kill_qlen := 100000, + overload_kill_mem_size := 10000000, + overload_kill_restart_after := infinity, + filesync_repeat_interval := no_repeat} = HConf}} = + logger:get_handler_config(?MODULE), + + ok = logger:update_handler_config(?MODULE, config, + #{flush_qlen => ?FLUSH_QLEN}), + {ok,#{config:=C1}} = logger:get_handler_config(?MODULE), + ct:log("C1: ~p",[C1]), + C1 = HConf#{flush_qlen => ?FLUSH_QLEN}, + + ok = logger:set_handler_config(?MODULE, config, #{sync_mode_qlen => 1}), + {ok,#{config:=C2}} = logger:get_handler_config(?MODULE), + ct:log("C2: ~p",[C2]), + C2 = DefaultHConf#{sync_mode_qlen => 1}, + + ok = logger:set_handler_config(?MODULE, config, #{drop_mode_qlen => 100}), + {ok,#{config:=C3}} = logger:get_handler_config(?MODULE), + ct:log("C3: ~p",[C3]), + C3 = DefaultHConf#{drop_mode_qlen => 100}, + + ok = logger:update_handler_config(?MODULE, config, #{sync_mode_qlen => 1}), + {ok,#{config:=C4}} = logger:get_handler_config(?MODULE), + ct:log("C4: ~p",[C4]), + C4 = DefaultHConf#{sync_mode_qlen => 1, + drop_mode_qlen => 100}, + + ok = logger:remove_handler(?MODULE), + + File = filename:join(Dir,lists:concat([?FUNCTION_NAME,".log"])), + ok = logger:add_handler(?MODULE, + logger_std_h, + #{config => #{type => {file,File}}, + filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}), + + {ok,#{config:=#{filesync_repeat_interval:=FSI}=FileHConfig}} = + logger:get_handler_config(?MODULE), + ok = logger:update_handler_config(?MODULE,config, + #{filesync_repeat_interval=>FSI+2000}), + {ok,#{config:=C5}} = logger:get_handler_config(?MODULE), + ct:log("C5: ~p",[C5]), + C5 = FileHConfig#{filesync_repeat_interval=>FSI+2000}, + + %% You are not allowed to actively set 'type' in runtime, since + %% this is a write once field. + {error, {illegal_config_change,_,_}} = + logger:set_handler_config(?MODULE,config,#{type=>standard_io}), + {ok,#{config:=C6}} = logger:get_handler_config(?MODULE), + ct:log("C6: ~p",[C6]), + C6 = C5, + + %% ... but if you don't specify 'type', then set_handler_config shall + %% NOT reset it to its default value + ok = logger:set_handler_config(?MODULE,config,#{sync_mode_qlen=>1}), + {ok,#{config:=C7}} = logger:get_handler_config(?MODULE), + ct:log("C7: ~p",[C7]), + C7 = FileHConfig#{sync_mode_qlen=>1}, ok. reconfig(cleanup, _Config) -> @@ -561,8 +656,8 @@ sync(Config) -> %% check that if there's no repeated filesync active, %% a filesync is still performed when handler goes idle - logger:set_handler_config(?MODULE, config, - #{filesync_repeat_interval => no_repeat}), + ok = logger:update_handler_config(?MODULE, config, + #{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 @@ -592,12 +687,12 @@ sync(Config) -> start_tracer([{logger_std_h,handle_cast,2}], [OneSync || _ <- lists:seq(1, 1 + trunc(WaitT/SyncInt))]), - logger:set_handler_config(?MODULE, config, - #{filesync_repeat_interval => SyncInt}), + ok = logger:update_handler_config(?MODULE, config, + #{filesync_repeat_interval => SyncInt}), SyncInt = maps:get(filesync_repeat_interval, logger_std_h:info(?MODULE)), timer:sleep(WaitT), - logger:set_handler_config(?MODULE, config, - #{filesync_repeat_interval => no_repeat}), + ok = logger:update_handler_config(?MODULE, config, + #{filesync_repeat_interval => no_repeat}), check_tracer(100), ok. sync(cleanup, _Config) -> @@ -652,7 +747,7 @@ sync_failure(Config) -> rpc:call(Node, ?MODULE, set_result, [file_datasync,ok]), SyncInt = 500, - ok = rpc:call(Node, logger, set_handler_config, + ok = rpc:call(Node, logger, update_handler_config, [?STANDARD_HANDLER, config, #{filesync_repeat_interval => SyncInt}]), Info = rpc:call(Node, logger_std_h, info, [?STANDARD_HANDLER]), @@ -718,7 +813,7 @@ op_switch_to_sync_file(Config) -> drop_mode_qlen => NumOfReqs+1, flush_qlen => 2*NumOfReqs, burst_limit_enable => false}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), %% TRecvPid = start_op_trace(), send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Lines = count_lines(Log), @@ -747,7 +842,7 @@ op_switch_to_sync_tty(Config) -> drop_mode_qlen => NumOfReqs+1, flush_qlen => 2*NumOfReqs, burst_limit_enable => false}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), send_burst({n,NumOfReqs}, seq, {chars,79}, notice), ok. op_switch_to_sync_tty(cleanup, _Config) -> @@ -770,7 +865,7 @@ op_switch_to_drop_file(Config) -> flush_qlen => Procs*NumOfReqs*Bursts, burst_limit_enable => false}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), %% It sometimes happens that the handler gets the %% requests in a slow enough pace so that dropping %% never occurs. Therefore, lets generate a number of @@ -807,7 +902,7 @@ op_switch_to_drop_tty(Config) -> flush_qlen => Procs*NumOfReqs+1, burst_limit_enable => false}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice), ok. op_switch_to_drop_tty(cleanup, _Config) -> @@ -832,7 +927,7 @@ op_switch_to_flush_file(Config) -> drop_mode_qlen => 300, flush_qlen => 300, burst_limit_enable => false}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), NumOfReqs = 1500, Procs = 10, Bursts = 10, @@ -879,7 +974,7 @@ op_switch_to_flush_tty(Config) -> drop_mode_qlen => 100, flush_qlen => 100, burst_limit_enable => false}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), NumOfReqs = 1000, Procs = 100, send_burst({n,NumOfReqs}, {spawn,Procs,0}, {chars,79}, notice), @@ -895,7 +990,7 @@ limit_burst_disabled(Config) -> burst_limit_window_time => 2000, drop_mode_qlen => 200, flush_qlen => 300}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Logged = count_lines(Log), @@ -915,7 +1010,7 @@ limit_burst_enabled_one(Config) -> burst_limit_window_time => 2000, drop_mode_qlen => 200, flush_qlen => 300}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Logged = count_lines(Log), @@ -936,7 +1031,7 @@ limit_burst_enabled_period(Config) -> burst_limit_window_time => BurstTWin, drop_mode_qlen => 20000, flush_qlen => 20001}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), Windows = 3, Sent = send_burst({t,BurstTWin*Windows}, seq, {chars,79}, notice), @@ -956,7 +1051,7 @@ kill_disabled(Config) -> HConfig#{config=>StdHConfig#{overload_kill_enable=>false, overload_kill_qlen=>10, overload_kill_mem_size=>100}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), NumOfReqs = 100, send_burst({n,NumOfReqs}, seq, {chars,79}, notice), Logged = count_lines(Log), @@ -977,7 +1072,7 @@ qlen_kill_new(Config) -> overload_kill_qlen=>10, overload_kill_mem_size=>Mem0+50000, overload_kill_restart_after=>RestartAfter}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), MRef = erlang:monitor(process, Pid0), NumOfReqs = 100, Procs = 4, @@ -1011,7 +1106,7 @@ qlen_kill_std(_Config) -> %% File = lists:concat([?MODULE,"_",?FUNCTION_NAME,".log"]), %% Log = filename:join(Dir, File), %% Node = start_std_h_on_new_node(Config, ?FUNCTION_NAME, Log), - %% ok = rpc:call(Node, logger, set_handler_config, + %% ok = rpc:call(Node, logger, update_handler_config, %% [?STANDARD_HANDLER, config, %% #{overload_kill_enable=>true, %% overload_kill_qlen=>10, @@ -1028,7 +1123,7 @@ mem_kill_new(Config) -> overload_kill_qlen=>50000, overload_kill_mem_size=>Mem0+500, overload_kill_restart_after=>RestartAfter}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), MRef = erlang:monitor(process, Pid0), NumOfReqs = 100, Procs = 4, @@ -1067,7 +1162,7 @@ restart_after(Config) -> HConfig#{config=>StdHConfig#{overload_kill_enable=>true, overload_kill_qlen=>10, overload_kill_restart_after=>infinity}}, - ok = logger:set_handler_config(?MODULE, NewHConfig1), + ok = logger:update_handler_config(?MODULE, NewHConfig1), MRef1 = erlang:monitor(process, whereis(h_proc_name())), %% kill handler send_burst({n,100}, {spawn,4,0}, {chars,79}, notice), @@ -1082,14 +1177,15 @@ restart_after(Config) -> ct:pal("Handler state = ~p", [Info1]), ct:fail("Handler not dead! It should not have survived this!") end, - + {Log,_,_} = start_handler(?MODULE, ?FUNCTION_NAME, Config), RestartAfter = ?OVERLOAD_KILL_RESTART_AFTER, + NewHConfig2 = HConfig#{config=>StdHConfig#{overload_kill_enable=>true, overload_kill_qlen=>10, overload_kill_restart_after=>RestartAfter}}, - ok = logger:set_handler_config(?MODULE, NewHConfig2), + ok = logger:update_handler_config(?MODULE, NewHConfig2), Pid0 = whereis(h_proc_name()), MRef2 = erlang:monitor(process, Pid0), %% kill handler @@ -1123,7 +1219,7 @@ handler_requests_under_load(Config) -> drop_mode_qlen => 1000, flush_qlen => 2000, burst_limit_enable => false}}, - ok = logger:set_handler_config(?MODULE, NewHConfig), + ok = logger:update_handler_config(?MODULE, NewHConfig), Pid = spawn_link(fun() -> send_requests(?MODULE, 1, [{filesync,[]}, {info,[]}, {reset,[]}, @@ -1155,9 +1251,9 @@ send_requests(HName, TO, Reqs = [{Req,Res}|Rs]) -> Result = case Req of change_config -> - logger:set_handler_config(HName, config, - #{overload_kill_enable => - false}); + logger:update_handler_config(HName, config, + #{overload_kill_enable => + false}); Func -> logger_std_h:Func(HName) end, -- cgit v1.2.3 From 6fae4fbd3650366730d35988ee588c32848dbecc Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 3 Oct 2018 12:21:23 +0200 Subject: [logger] Add handler callback filter_config/1 This function is called when a logger API function for fetching handler configuration is called. The point is to allow the handler to remove internal data fields that it might have stored in the configuration database, before returning the handler configuration to the caller. An example of such internal data are the 'handler_pid' and 'mode_tab' fields that logger_std_h and logger_disk_log_h store in their configuration maps. --- lib/kernel/doc/src/logger.xml | 20 ++++++++++++++++++++ lib/kernel/doc/src/logger_chapter.xml | 12 ++++++++++-- lib/kernel/src/logger.erl | 9 ++++++++- lib/kernel/src/logger_disk_log_h.erl | 8 +++++++- lib/kernel/src/logger_h_common.erl | 5 ++++- lib/kernel/src/logger_std_h.erl | 8 +++++++- lib/kernel/test/logger_disk_log_h_SUITE.erl | 15 +++++++++++++++ lib/kernel/test/logger_std_h_SUITE.erl | 15 +++++++++++++++ 8 files changed, 86 insertions(+), 6 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index aa6e17f27b..2bcf137299 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -1122,6 +1122,26 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)).
    + + HModule:filter_config(Config) -> FilteredConfig + Remove internal data from configuration. + + Config = FilteredConfig = + handler_config() + + +

    This callback function is optional.

    +

    The function is called when one of the Logger API functions + for fetching the handler configuration is called, for + example + + logger:get_handler_config/1.

    +

    It allows the handler to remove internal data fields from + its configuration data before it is returned to the + caller.

    +
    +
    + HModule:log(LogEvent, Config) -> void() Log the given log event. diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 777c37058e..1870d2ab79 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -384,8 +384,8 @@ logger:debug(#{got => connection_request, id => Id, state => State},

    In addition to the mandatory callback function log/2, a handler module can export the optional callback - functions adding_handler/1, changing_config/3 - and removing_handler/1. See + functions adding_handler/1, changing_config/3, + filter_config/1, and removing_handler/1. See section Handler Callback Functions in the logger(3) manual page for more information about these function.

    @@ -1029,6 +1029,7 @@ ok adding_handler(Config) removing_handler(Config) changing_config(SetOrUpdate, OldConfig, NewConfig) + filter_config(Config)

    When a handler is added, by for example a call to @@ -1052,6 +1053,13 @@ ok calls HModule:changing_config(SetOrUpdate, OldConfig, NewConfig). If this function returns {ok,NewConfig1}, Logger writes NewConfig1 to the configuration database.

    +

    When + logger:get_config/0 or + + logger:get_handler_config/0,1 is called, + Logger calls HModule:filter_config(Config). This function + must return the handler configuration where internal data is + removed.

    A simple handler that prints to the terminal can be implemented as follows:

    diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl index 3ffa8a0c4e..6762998d4f 100644 --- a/lib/kernel/src/logger.erl +++ b/lib/kernel/src/logger.erl @@ -463,7 +463,14 @@ get_primary_config() -> HandlerId :: handler_id(), Config :: handler_config(). get_handler_config(HandlerId) -> - logger_config:get(?LOGGER_TABLE,HandlerId). + case logger_config:get(?LOGGER_TABLE,HandlerId) of + {ok,#{module:=Module}=Config} -> + {ok,try Module:filter_config(Config) + catch _:_ -> Config + end}; + Error -> + Error + end. -spec get_handler_config() -> [Config] when Config :: handler_config(). diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index bb8b9f08a4..2a81458ec8 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -33,7 +33,8 @@ terminate/2, code_change/3]). %% logger callbacks --export([log/2, adding_handler/1, removing_handler/1, changing_config/3]). +-export([log/2, adding_handler/1, removing_handler/1, changing_config/3, + filter_config/1]). %% handler internal -export([log_handler_info/4]). @@ -247,6 +248,11 @@ log(LogEvent, Config = #{id := Name, Bin = logger_h_common:log_to_binary(LogEvent, Config), logger_h_common:call_cast_or_drop(Name, HPid, ModeTab, Bin). +%%%----------------------------------------------------------------- +%%% Remove internal fields from configuration +filter_config(#{config:=HConfig}=Config) -> + Config#{config=>maps:without([handler_pid,mode_tab],HConfig)}. + %%%=================================================================== %%% gen_server callbacks %%%=================================================================== diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl index 38ac7d8ffc..94c640cb92 100644 --- a/lib/kernel/src/logger_h_common.erl +++ b/lib/kernel/src/logger_h_common.erl @@ -306,8 +306,11 @@ stop_or_restart(Name, {shutdown,Reason={overloaded,_Name,_QLen,_Mem}}, exit(HandlerPid, kill) end, case ConfigResult of - {ok,#{module:=HMod}=HConfig} when is_integer(RestartAfter) -> + {ok,#{module:=HMod}=HConfig0} when is_integer(RestartAfter) -> _ = logger:remove_handler(Name), + HConfig = try HMod:filter_config(HConfig0) + catch _:_ -> HConfig0 + end, _ = timer:apply_after(RestartAfter, logger, add_handler, [Name,HMod,HConfig]); {ok,_} -> diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index 6bbf75e0ab..42e0f5caf4 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -35,7 +35,8 @@ terminate/2, code_change/3]). %% logger callbacks --export([log/2, adding_handler/1, removing_handler/1, changing_config/3]). +-export([log/2, adding_handler/1, removing_handler/1, changing_config/3, + filter_config/1]). %% handler internal -export([log_handler_info/4]). @@ -230,6 +231,11 @@ log(LogEvent, Config = #{id := Name, Bin = logger_h_common:log_to_binary(LogEvent, Config), logger_h_common:call_cast_or_drop(Name, HPid, ModeTab, Bin). +%%%----------------------------------------------------------------- +%%% Remove internal fields from configuration +filter_config(#{config:=HConfig}=Config) -> + Config#{config=>maps:without([handler_pid,mode_tab],HConfig)}. + %%%=================================================================== %%% gen_server callbacks %%%=================================================================== diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 34f1969e67..905c2c52c5 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -92,6 +92,7 @@ all() -> disk_log_opts, default_formatter, logging, + filter_config, errors, formatter_fail, config_fail, @@ -302,6 +303,20 @@ logging(cleanup, _Config) -> Name = list_to_atom(lists:concat([?FUNCTION_NAME,"_1"])), remove_and_stop(Name). +filter_config(_Config) -> + ok = logger:add_handler(?MODULE,logger_disk_log_h,#{}), + {ok,#{config:=HConfig}=Config} = logger:get_handler_config(?MODULE), + HConfig = maps:without([handler_pid,mode_tab],HConfig), + + FakeFullHConfig = HConfig#{handler_pid=>self(),mode_tab=>erlang:make_ref()}, + #{config:=HConfig} = + logger_disk_log_h:filter_config(Config#{config=>FakeFullHConfig}), + ok. + +filter_config(cleanup,_Config) -> + logger:remove_handler(?MODULE), + ok. + errors(Config) -> PrivDir = ?config(priv_dir,Config), Name1 = list_to_atom(lists:concat([?FUNCTION_NAME,"_1"])), diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index c5d742ea7f..b6a09f4980 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -108,6 +108,7 @@ all() -> add_remove_instance_file1, add_remove_instance_file2, default_formatter, + filter_config, errors, formatter_fail, config_fail, @@ -204,6 +205,20 @@ default_formatter(_Config) -> match = re:run(Msg,"=NOTICE REPORT====.*\n"++M1,[{capture,none}]), ok. +filter_config(_Config) -> + ok = logger:add_handler(?MODULE,logger_std_h,#{}), + {ok,#{config:=HConfig}=Config} = logger:get_handler_config(?MODULE), + HConfig = maps:without([handler_pid,mode_tab],HConfig), + + FakeFullHConfig = HConfig#{handler_pid=>self(),mode_tab=>erlang:make_ref()}, + #{config:=HConfig} = + logger_std_h:filter_config(Config#{config=>FakeFullHConfig}), + ok. + +filter_config(cleanup,_Config) -> + logger:remove_handler(?MODULE), + ok. + errors(Config) -> Dir = ?config(priv_dir,Config), Log = filename:join(Dir,?FUNCTION_NAME), -- cgit v1.2.3 From 2c0354281b7d47e60d6d44a130b601d5090de4d1 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Fri, 5 Oct 2018 14:48:51 +0200 Subject: Adjust test case to reality --- lib/kernel/test/inet_SUITE.erl | 172 +++++++++++++++++++++-------------------- 1 file changed, 89 insertions(+), 83 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl index 2e5f8c7d2c..f436eafad3 100644 --- a/lib/kernel/test/inet_SUITE.erl +++ b/lib/kernel/test/inet_SUITE.erl @@ -1060,28 +1060,26 @@ getservbyname_overflow(Config) when is_list(Config) -> getifaddrs(Config) when is_list (Config) -> {ok,IfAddrs} = inet:getifaddrs(), io:format("IfAddrs = ~p.~n", [IfAddrs]), - case - {os:type(), - [If || - {If,Opts} <- IfAddrs, - lists:keymember(hwaddr, 1, Opts)]} of - {{unix,sunos},[]} -> ok; - {OT,[]} -> - ct:fail({should_have_hwaddr,OT}); - _ -> ok + case [If || {If,Opts} <- IfAddrs, lists:keymember(hwaddr, 1, Opts)] of + [] -> + case os:type() of + {unix,sunos} -> ok; + OT -> + ct:fail({should_have_hwaddr,OT}) + end; + [_|_] -> ok end, - Addrs = - [element(1, A) || A <- ifaddrs(IfAddrs)], + Addrs = ifaddrs(IfAddrs), io:format("Addrs = ~p.~n", [Addrs]), [check_addr(Addr) || Addr <- Addrs], ok. -check_addr({addr,Addr}) +check_addr(Addr) when tuple_size(Addr) =:= 8, element(1, Addr) band 16#FFC0 =:= 16#FE80 -> io:format("Addr: ~p link local; SKIPPED!~n", [Addr]), ok; -check_addr({addr,Addr}) -> +check_addr(Addr) -> io:format("Addr: ~p.~n", [Addr]), Ping = "ping", Pong = "pong", @@ -1097,78 +1095,86 @@ check_addr({addr,Addr}) -> ok = gen_tcp:close(S2), ok = gen_tcp:close(L). --record(ifopts, {name,flags,addrs=[],hwaddr}). - -ifaddrs([]) -> []; -ifaddrs([{If,Opts}|IOs]) -> - #ifopts{flags=F} = Ifopts = check_ifopts(Opts, #ifopts{name=If}), - case F of - {flags,Flags} -> - case lists:member(running, Flags) of - true -> Ifopts#ifopts.addrs; - false -> [] - end ++ ifaddrs(IOs); - undefined -> - ifaddrs(IOs) +ifaddrs(IfOpts) -> + IfMap = collect_ifopts(IfOpts), + ChkFun = + fun Self({{_,Flags} = Key, Opts}, ok) -> + Broadcast = lists:member(broadcast, Flags), + P2P = lists:member(pointtopoint, Flags), + case Opts of + [{addr,_},{netmask,_},{broadaddr,_}|Os] + when Broadcast -> + Self({Key, Os}, ok); + [{addr,_},{netmask,_},{dstaddr,_}|Os] + when P2P -> + Self({Key, Os}, ok); + [{addr,_},{netmask,_}|Os] -> + Self({Key, Os}, ok); + [{hwaddr,_}|Os] -> + Self({Key, Os}, ok); + [] -> + ok + end + end, + fold_ifopts(ChkFun, ok, IfMap), + AddrsFun = + fun ({{_,Flags}, Opts}, Acc) -> + case + lists:member(running, Flags) + andalso (not lists:member(pointtopoint, Flags)) + of + true -> + lists:reverse( + [Addr || {addr,Addr} <- Opts], + Acc); + false -> + Acc + end + end, + fold_ifopts(AddrsFun, [], IfMap). + +collect_ifopts(IfOpts) -> + collect_ifopts(IfOpts, #{}). +%% +collect_ifopts(IfOpts, IfMap) -> + case IfOpts of + [{If,[{flags,Flags}|Opts]}|IfOs] -> + Key = {If,Flags}, + case maps:is_key(Key, IfMap) of + true -> + ct:fail({unexpected_ifopts,IfOpts,IfMap}); + false -> + collect_ifopts(IfOs, IfMap, Opts, Key, []) + end; + [] -> + IfMap; + _ -> + ct:fail({unexpected_ifopts,IfOpts,IfMap}) + end. +%% +collect_ifopts(IfOpts, IfMap, Opts, Key, R) -> + case Opts of + [{flags,_}|_] -> + {If,_} = Key, + collect_ifopts( + [{If,Opts}|IfOpts], maps:put(Key, lists:reverse(R), IfMap)); + [OptVal|Os] -> + collect_ifopts(IfOpts, IfMap, Os, Key, [OptVal|R]); + [] -> + collect_ifopts(IfOpts, maps:put(Key, lists:reverse(R), IfMap)) end. -check_ifopts([], #ifopts{flags=F,addrs=Raddrs}=Ifopts) -> - Addrs = lists:reverse(Raddrs), - R = Ifopts#ifopts{addrs=Addrs}, - io:format("~p.~n", [R]), - %% See how we did... - {flags,Flags} = F, - case lists:member(broadcast, Flags) of - true -> - [case A of - {{addr,_},{netmask,_},{broadaddr,_}} -> - A; - {{addr,T},{netmask,_}} when tuple_size(T) =:= 8 -> - A - end || A <- Addrs]; - false -> - case lists:member(pointtopoint, Flags) of - true -> - [case A of - {{addr,_},{netmask,_},{dstaddr,_}} -> - A - end || A <- Addrs]; - false -> - [case A of - {{addr,_},{netmask,_}} -> - A - end || A <- Addrs] - end - end, - R; -check_ifopts([{flags,_}=F|Opts], #ifopts{flags=undefined}=Ifopts) -> - check_ifopts(Opts, Ifopts#ifopts{flags=F}); -check_ifopts([{flags,_}=F|Opts], #ifopts{flags=Flags}=Ifopts) -> - case F of - Flags -> - check_ifopts(Opts, Ifopts); - _ -> - ct:fail({multiple_flags,F,Ifopts}) - end; -check_ifopts( - [{addr,_}=A,{netmask,_}=N,{dstaddr,_}=D|Opts], - #ifopts{addrs=Addrs}=Ifopts) -> - check_ifopts(Opts, Ifopts#ifopts{addrs=[{A,N,D}|Addrs]}); -check_ifopts( - [{addr,_}=A,{netmask,_}=N,{broadaddr,_}=B|Opts], - #ifopts{addrs=Addrs}=Ifopts) -> - check_ifopts(Opts, Ifopts#ifopts{addrs=[{A,N,B}|Addrs]}); -check_ifopts( - [{addr,_}=A,{netmask,_}=N|Opts], - #ifopts{addrs=Addrs}=Ifopts) -> - check_ifopts(Opts, Ifopts#ifopts{addrs=[{A,N}|Addrs]}); -check_ifopts([{addr,_}=A|Opts], #ifopts{addrs=Addrs}=Ifopts) -> - check_ifopts(Opts, Ifopts#ifopts{addrs=[{A}|Addrs]}); -check_ifopts([{hwaddr,Hwaddr}=H|Opts], #ifopts{hwaddr=undefined}=Ifopts) - when is_list(Hwaddr) -> - check_ifopts(Opts, Ifopts#ifopts{hwaddr=H}); -check_ifopts([{hwaddr,_}=H|_], #ifopts{}=Ifopts) -> - ct:fail({multiple_hwaddrs,H,Ifopts}). +fold_ifopts(Fun, Acc, IfMap) -> + fold_ifopts(Fun, Acc, IfMap, maps:keys(IfMap)). +%% +fold_ifopts(Fun, Acc, IfMap, Keys) -> + case Keys of + [Key|Ks] -> + Opts = maps:get(Key, IfMap), + fold_ifopts(Fun, Fun({Key,Opts}, Acc), IfMap, Ks); + [] -> + Acc + end. %% Works just like lists:member/2, except that any {127,_,_,_} tuple %% matches any other {127,_,_,_}. We do this to handle Linux systems -- cgit v1.2.3 From 210665e8a252faecf28ccebade999e734ab92582 Mon Sep 17 00:00:00 2001 From: Igor Slepchin Date: Mon, 24 Sep 2018 22:44:03 -0400 Subject: Add nopush TCP socket option This translates to TCP_CORK on Linux and TCP_NOPUSH on BSD. In effect, this acts as super-Nagle: no partial TCP segments are sent out until this option is turned off. Once turned off, all accumulated unsent data is sent out immediately. The latter is *not* the case on OSX, hence the implementation ignores "nopush" on OSX to reduce confusion. --- lib/kernel/doc/src/inet.xml | 12 ++++++++++++ lib/kernel/src/inet_int.hrl | 1 + lib/kernel/test/inet_sockopt_SUITE.erl | 9 +++++++-- 3 files changed, 20 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index ed775d67eb..9fc361a234 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -1039,6 +1039,18 @@ setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp is turned on for the socket, which means that also small amounts of data are sent immediately.

    + {nopush, Boolean}(TCP/IP sockets) + +

    This translates to TCP_NOPUSH on BSD and + to TCP_CORK on Linux.

    +

    If Boolean == true, the corresponding option + is turned on for the socket, which means that small + amounts of data are accumulated until a full MSS-worth + of data is available or this option is turned off.

    +

    Note that while TCP_NOPUSH socket option is available on OSX, its semantics + is very different (e.g., unsetting it does not cause immediate send + of accumulated data). Hence, nopush option is intentionally ignored on OSX.

    +
    {packet, PacketType}(TCP/IP sockets)

    Defines the type of packets to use for a socket. diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl index c8e09d18ad..f6525d7261 100644 --- a/lib/kernel/src/inet_int.hrl +++ b/lib/kernel/src/inet_int.hrl @@ -162,6 +162,7 @@ -define(INET_OPT_PKTOPTIONS, 45). -define(INET_OPT_TTL, 46). -define(INET_OPT_RECVTTL, 47). +-define(TCP_OPT_NOPUSH, 48). % Specific SCTP options: separate range: -define(SCTP_OPT_RTOINFO, 100). -define(SCTP_OPT_ASSOCINFO, 101). diff --git a/lib/kernel/test/inet_sockopt_SUITE.erl b/lib/kernel/test/inet_sockopt_SUITE.erl index ada9c2689c..27ff74e309 100644 --- a/lib/kernel/test/inet_sockopt_SUITE.erl +++ b/lib/kernel/test/inet_sockopt_SUITE.erl @@ -110,9 +110,14 @@ simple(Config) when is_list(Config) -> {S1,S2} = create_socketpair(Opt, Opt), {ok,Opt} = inet:getopts(S1,OptTags), {ok,Opt} = inet:getopts(S2,OptTags), - COpt = [{X,case X of nodelay -> false;_ -> Y end} || {X,Y} <- Opt], + NoPushOpt = case os:type() of + {unix, Osname} when Osname =:= linux; Osname =:= freebsd -> {nopush, true}; + {_,_} -> {nopush, false} + end, + COpt = [{X,case X of nodelay -> false;_ -> Y end} || {X,Y} <- [NoPushOpt|Opt]], + COptTags = [X || {X,_} <- COpt], inet:setopts(S1,COpt), - {ok,COpt} = inet:getopts(S1,OptTags), + {ok,COpt} = inet:getopts(S1,COptTags), {ok,Opt} = inet:getopts(S2,OptTags), gen_tcp:close(S1), gen_tcp:close(S2), -- cgit v1.2.3 From 1997b6b57d755e25d4050043450bb3cc7b229189 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Thu, 27 Sep 2018 16:38:00 +0200 Subject: erts: Fix UNC path handling on Windows This is unlikely to be the last of the path problems seen after OTP 21, and I'm starting to regret my decision to unconditionally use long paths. The idea to hit all long-path problems all the time was good in theory as it makes such bugs far more visible, but there just aren't enough people who test pre-release versions on Windows, making this the world's slowest game of whack-a-mole. --- lib/kernel/test/file_SUITE.erl | 31 ++++++++++++++++++++++++++++--- lib/kernel/test/prim_file_SUITE.erl | 3 ++- 2 files changed, 30 insertions(+), 4 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index e784c06865..a51025cba6 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -100,7 +100,7 @@ -export([unicode_mode/1]). --export([volume_relative_paths/1]). +-export([volume_relative_paths/1,unc_paths/1]). -export([tiny_writes/1, tiny_writes_delayed/1, large_writes/1, large_writes_delayed/1, @@ -129,7 +129,7 @@ suite() -> all() -> [unicode, altname, read_write_file, {group, dirs}, - {group, files}, delete, rename, names, volume_relative_paths, + {group, files}, delete, rename, names, volume_relative_paths, unc_paths, {group, errors}, {group, compression}, {group, links}, copy, delayed_write, read_ahead, segment_read, segment_write, ipread, pid2name, interleaved_read_write, otp_5814, otp_10852, @@ -2182,6 +2182,30 @@ volume_relative_paths(Config) when is_list(Config) -> {skip, "This test is Windows-specific."} end. +unc_paths(Config) when is_list(Config) -> + case os:type() of + {win32, _} -> + %% We assume administrative shares are set up and reachable, and we + %% settle for testing presence as some of the returned data is + %% different. + {ok, _} = file:read_file_info("C:\\Windows\\explorer.exe"), + {ok, _} = file:read_file_info("\\\\localhost\\c$\\Windows\\explorer.exe"), + + {ok, Cwd} = file:get_cwd(), + + try + ok = file:set_cwd("\\\\localhost\\c$\\Windows\\"), + {ok, _} = file:read_file_info("explorer.exe") + after + file:set_cwd(Cwd) + end, + + [] = flush(), + ok; + _ -> + {skip, "This test is Windows-specific."} + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -2210,7 +2234,8 @@ e_delete(Config) when is_list(Config) -> case os:type() of {win32, _} -> %% Remove a character device. - {error, eacces} = ?FILE_MODULE:delete("nul"); + expect({error, eacces}, {error, einval}, + ?FILE_MODULE:delete("nul")); _ -> ?FILE_MODULE:write_file_info( Base, #file_info {mode=0}), diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl index a02b5f87d1..2f465a15bc 100644 --- a/lib/kernel/test/prim_file_SUITE.erl +++ b/lib/kernel/test/prim_file_SUITE.erl @@ -1300,7 +1300,8 @@ e_delete(Config) when is_list(Config) -> case os:type() of {win32, _} -> %% Remove a character device. - {error, eacces} = ?PRIM_FILE:delete("nul"); + expect({error, eacces}, {error, einval}, + ?PRIM_FILE:delete("nul")); _ -> ?PRIM_FILE:write_file_info( Base, #file_info {mode=0}), -- cgit v1.2.3 From acf610d99e209b1304f9368d70fe66e46fa3cdc2 Mon Sep 17 00:00:00 2001 From: Vince Foley Date: Sat, 13 Oct 2018 20:15:25 -0700 Subject: Fix fallback address_please --- lib/kernel/src/inet_tcp_dist.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl index d1701afdaa..c37212b0f9 100644 --- a/lib/kernel/src/inet_tcp_dist.erl +++ b/lib/kernel/src/inet_tcp_dist.erl @@ -450,7 +450,7 @@ get_tcp_address(Driver, Socket) -> get_address_resolver(EpmdModule) -> case erlang:function_exported(EpmdModule, address_please, 3) of true -> {EpmdModule, address_please}; - _ -> {inet, getaddr} + _ -> {erl_epmd, address_please} end. %% ------------------------------------------------------------ -- cgit v1.2.3 From bc693e588be932663b75a7261ac19655ab597ae2 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 8 Oct 2018 14:25:18 +0200 Subject: [logger] Move common handler code to logger_h_common There was a lot of duplicated code in logger_std_h and logger_disk_log_h. Most of this is now moved to logger_h_common, which now also serves as the gen_server callback. --- lib/kernel/src/logger_disk_log_h.erl | 642 +++++--------------------- lib/kernel/src/logger_h_common.erl | 646 +++++++++++++++++++++++--- lib/kernel/src/logger_std_h.erl | 672 +++++----------------------- lib/kernel/test/logger_disk_log_h_SUITE.erl | 62 +-- lib/kernel/test/logger_std_h_SUITE.erl | 42 +- 5 files changed, 850 insertions(+), 1214 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index 2a81458ec8..8c09dd071f 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -19,59 +19,35 @@ %% -module(logger_disk_log_h). --behaviour(gen_server). - -include("logger.hrl"). -include("logger_internal.hrl"). -include("logger_h_common.hrl"). %%% API --export([start_link/3, info/1, filesync/1, reset/1]). +-export([info/1, filesync/1, reset/1]). -%% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). +%% logger_h_common callbacks +-export([init/2, check_config/4, reset_state/1, + async_filesync/2, sync_filesync/2, + async_write/3, sync_write/3, + handle_info/2, terminate/3]). %% logger callbacks -export([log/2, adding_handler/1, removing_handler/1, changing_config/3, filter_config/1]). -%% handler internal --export([log_handler_info/4]). - %%%=================================================================== %%% API %%%=================================================================== -%%%----------------------------------------------------------------- -%%% Start a disk_log handler process and link to caller. -%%% This function is called by the kernel supervisor when this -%%% handler process gets added (as a result of calling add/3). --spec start_link(Name, Config, HandlerState) -> {ok,Pid} | {error,Reason} when - Name :: atom(), - Config :: logger:handler_config(), - HandlerState :: map(), - Pid :: pid(), - Reason :: term(). - -start_link(Name, Config, HandlerState) -> - proc_lib:start_link(?MODULE,init,[[Name,Config,HandlerState]]). - %%%----------------------------------------------------------------- %%% -spec filesync(Name) -> ok | {error,Reason} when Name :: atom(), Reason :: handler_busy | {badarg,term()}. -filesync(Name) when is_atom(Name) -> - try - gen_server:call(?name_to_reg_name(?MODULE,Name), - disk_log_sync, ?DEFAULT_CALL_TIMEOUT) - catch - _:{timeout,_} -> {error,handler_busy} - end; filesync(Name) -> - {error,{badarg,{filesync,[Name]}}}. + logger_h_common:filesync(?MODULE,Name). %%%----------------------------------------------------------------- %%% @@ -80,15 +56,8 @@ filesync(Name) -> Info :: term(), Reason :: handler_busy | {badarg,term()}. -info(Name) when is_atom(Name) -> - try - gen_server:call(?name_to_reg_name(?MODULE,Name), - info, ?DEFAULT_CALL_TIMEOUT) - catch - _:{timeout,_} -> {error,handler_busy} - end; info(Name) -> - {error,{badarg,{info,[Name]}}}. + logger_h_common:info(?MODULE,Name). %%%----------------------------------------------------------------- %%% @@ -96,16 +65,8 @@ info(Name) -> Name :: atom(), Reason :: handler_busy | {badarg,term()}. -reset(Name) when is_atom(Name) -> - try - gen_server:call(?name_to_reg_name(?MODULE,Name), - reset, ?DEFAULT_CALL_TIMEOUT) - catch - _:{timeout,_} -> {error,handler_busy} - end; reset(Name) -> - {error,{badarg,{reset,[Name]}}}. - + logger_h_common:reset(?MODULE,Name). %%%=================================================================== %%% logger callbacks @@ -113,30 +74,55 @@ reset(Name) -> %%%----------------------------------------------------------------- %%% Handler being added -adding_handler(#{id:=Name}=Config) -> - case check_config(adding, Config) of - {ok, #{config:=HConfig}=Config1} -> - %% create initial handler state by merging defaults with config - HState = maps:merge(get_init_state(), HConfig), - case logger_h_common:overload_levels_ok(HState) of - true -> - start(Name, Config1, HState); - false -> - #{sync_mode_qlen := SMQL, - drop_mode_qlen := DMQL, - flush_qlen := FQL} = HState, - {error,{invalid_levels,{SMQL,DMQL,FQL}}} - end; +adding_handler(Config) -> + logger_h_common:adding_handler(Config). + +%%%----------------------------------------------------------------- +%%% Updating handler config +changing_config(SetOrUpdate, OldConfig, NewConfig) -> + logger_h_common:changing_config(SetOrUpdate, OldConfig, NewConfig). + +%%%----------------------------------------------------------------- +%%% Handler being removed +removing_handler(Config) -> + logger_h_common:removing_handler(Config). + +%%%----------------------------------------------------------------- +%%% Log a string or report +-spec log(LogEvent, Config) -> ok when + LogEvent :: logger:log_event(), + Config :: logger:handler_config(). + +log(LogEvent, Config) -> + logger_h_common:log(LogEvent, Config). + +%%%----------------------------------------------------------------- +%%% Remove internal fields from configuration +filter_config(Config) -> + logger_h_common:filter_config(Config). + +%%%=================================================================== +%%% logger_h_common callbacks +%%%=================================================================== +init(Name, #{file:=File,type:=Type,max_no_bytes:=MNB,max_no_files:=MNF}) -> + case open_disk_log(Name, File, Type, MNB, MNF) of + ok -> + {ok,#{log_opts => #{file => File, + type => Type, + max_no_bytes => MNB, + max_no_files => MNF}, + prev_log_result => ok, + prev_sync_result => ok, + prev_disk_log_info => undefined}}; Error -> Error end. -%%%----------------------------------------------------------------- -%%% Updating handler config -changing_config(SetOrUpdate,OldConfig=#{config:=OldHConfig},NewConfig) -> +check_config(Name,set,undefined,HConfig0) -> + HConfig=merge_default_logopts(Name,maps:merge(get_default_config(),HConfig0)), + check_config(HConfig); +check_config(_Name,SetOrUpdate,OldHConfig,NewHConfig0) -> WriteOnce = maps:with([type,file,max_no_files,max_no_bytes],OldHConfig), - ReadOnly = maps:with([handler_pid,mode_tab],OldHConfig), - NewHConfig0 = maps:get(config, NewConfig, #{}), Default = case SetOrUpdate of set -> @@ -146,66 +132,25 @@ changing_config(SetOrUpdate,OldConfig=#{config:=OldHConfig},NewConfig) -> OldHConfig end, - %% Allow (accidentially) included read-only fields - just overwrite them - NewHConfig = maps:merge(maps:merge(Default,NewHConfig0),ReadOnly), + NewHConfig = maps:merge(Default,NewHConfig0), - %% But fail if write-once fields are changed + %% Fail if write-once fields are changed case maps:with([type,file,max_no_files,max_no_bytes],NewHConfig) of WriteOnce -> - changing_config1(maps:get(handler_pid,OldHConfig), - OldConfig, - NewConfig#{config=>NewHConfig}); + check_config(NewHConfig); Other -> {Old,New} = logger_server:diff_maps(WriteOnce,Other), - {error,{illegal_config_change,#{config=>Old},#{config=>New}}} + {error,{illegal_config_change,?MODULE,Old,New}} end. -changing_config1(HPid, OldConfig, NewConfig) -> - case check_config(changing, NewConfig) of - Result = {ok,NewConfig1} -> - try gen_server:call(HPid, {change_config,OldConfig,NewConfig1}, - ?DEFAULT_CALL_TIMEOUT) of - ok -> Result; - Error -> Error - catch - _:{timeout,_} -> {error,handler_busy} - end; - Error -> - Error - end. - -check_config(adding, #{id:=Name}=Config) -> - %% merge handler specific config data - HConfig1 = maps:get(config, Config, #{}), - HConfig2 = maps:merge(get_default_config(), HConfig1), - HConfig3 = merge_default_logopts(Name, HConfig2), - case check_h_config(maps:to_list(HConfig3)) of - ok -> - {ok,Config#{config=>HConfig3}}; - Error -> - Error - end; -check_config(changing, Config) -> - HConfig = maps:get(config, Config, #{}), +check_config(HConfig) -> case check_h_config(maps:to_list(HConfig)) of - ok -> {ok,Config}; - Error -> Error + ok -> + {ok,HConfig}; + {error,{Key,Value}} -> + {error,{invalid_config,?MODULE,#{Key=>Value}}} end. -merge_default_logopts(Name, HConfig) -> - Type = maps:get(type, HConfig, wrap), - {DefaultNoFiles,DefaultNoBytes} = - case Type of - halt -> {undefined,infinity}; - _wrap -> {10,1048576} - end, - {ok,Dir} = file:get_cwd(), - Defaults = #{file => filename:join(Dir,Name), - max_no_files => DefaultNoFiles, - max_no_bytes => DefaultNoBytes, - type => Type}, - maps:merge(Defaults, HConfig). - check_h_config([{file,File}|Config]) when is_list(File) -> check_h_config(Config); check_h_config([{max_no_files,undefined}|Config]) -> @@ -218,454 +163,75 @@ check_h_config([{max_no_bytes,N}|Config]) when is_integer(N), N>0 -> check_h_config(Config); check_h_config([{type,Type}|Config]) when Type==wrap; Type==halt -> check_h_config(Config); -check_h_config([Other | Config]) -> - case logger_h_common:check_common_config(Other) of - valid -> - check_h_config(Config); - invalid -> - {error,{invalid_config,?MODULE,Other}} - end; +check_h_config([Other | _]) -> + {error,Other}; check_h_config([]) -> ok. -%%%----------------------------------------------------------------- -%%% Handler being removed -removing_handler(#{id:=Name}) -> - stop(Name). +get_default_config() -> + #{}. -%%%----------------------------------------------------------------- -%%% Log a string or report --spec log(LogEvent, Config) -> ok when - LogEvent :: logger:log_event(), - Config :: logger:handler_config(). +merge_default_logopts(Name, HConfig) -> + Type = maps:get(type, HConfig, wrap), + {DefaultNoFiles,DefaultNoBytes} = + case Type of + halt -> {undefined,infinity}; + _wrap -> {10,1048576} + end, + {ok,Dir} = file:get_cwd(), + Defaults = #{file => filename:join(Dir,Name), + max_no_files => DefaultNoFiles, + max_no_bytes => DefaultNoBytes, + type => Type}, + maps:merge(Defaults, HConfig). -log(LogEvent, Config = #{id := Name, - config := #{handler_pid := HPid, - mode_tab := ModeTab}}) -> - %% if the handler has crashed, we must drop this event - %% and hope the handler restarts so we can try again - true = is_process_alive(HPid), - Bin = logger_h_common:log_to_binary(LogEvent, Config), - logger_h_common:call_cast_or_drop(Name, HPid, ModeTab, Bin). +async_filesync(Name,State) -> + {_,State1} = disk_log_sync(Name,State), + State1. -%%%----------------------------------------------------------------- -%%% Remove internal fields from configuration -filter_config(#{config:=HConfig}=Config) -> - Config#{config=>maps:without([handler_pid,mode_tab],HConfig)}. +sync_filesync(Name,State) -> + disk_log_sync(Name,State). -%%%=================================================================== -%%% gen_server callbacks -%%%=================================================================== +async_write(Name, Bin, State) -> + {_,State1} = disk_log_write(Name, Bin, State), + State1. -init([Name, - Config = #{config := HConfig = #{file:=File, - type:=Type, - max_no_bytes:=MNB, - max_no_files:=MNF}}, - State = #{dl_sync_int := DLSyncInt}]) -> - - RegName = ?name_to_reg_name(?MODULE,Name), - register(RegName, self()), - process_flag(trap_exit, true), - process_flag(message_queue_data, off_heap), - - ?init_test_hooks(), - ?start_observation(Name), - - LogOpts = #{file=>File, type=>Type, max_no_bytes=>MNB, max_no_files=>MNF}, - case open_disk_log(Name, File, Type, MNB, MNF) of - ok -> - try ets:new(Name, [public]) of - ModeTab -> - ?set_mode(ModeTab, async), - T0 = ?timestamp(), - State1 = - ?merge_with_stats(State#{ - id => Name, - mode_tab => ModeTab, - mode => async, - dl_sync => DLSyncInt, - log_opts => LogOpts, - last_qlen => 0, - last_log_ts => T0, - burst_win_ts => T0, - burst_msg_count => 0, - last_op => sync, - prev_log_result => ok, - prev_sync_result => ok, - prev_disk_log_info => undefined}), - Config1 = - Config#{config => HConfig#{handler_pid => self(), - mode_tab => ModeTab}}, - proc_lib:init_ack({ok,self(),Config1}), - gen_server:cast(self(), repeated_disk_log_sync), - case logger_h_common:unset_restart_flag(Name, ?MODULE) of - true -> - %% inform about restart - gen_server:cast(self(), {log_handler_info, - "Handler ~p restarted", - [Name]}); - false -> - %% initial start - ok - end, - gen_server:enter_loop(?MODULE, [], State1) - catch - _:Error -> - unregister(RegName), - logger_h_common:error_notify({open_disk_log,Name,Error}), - proc_lib:init_ack(Error) - end; - Error -> - unregister(RegName), - logger_h_common:error_notify({open_disk_log,Name,Error}), - proc_lib:init_ack(Error) - end. +sync_write(Name, Bin, State) -> + disk_log_write(Name, Bin, State). -%% This is the synchronous log event. -handle_call({log, Bin}, _From, State) -> - {Result,State1} = do_log(Bin, call, State), - %% Result == ok | dropped - {reply, Result, State1}; - -handle_call(disk_log_sync, _From, State = #{id := Name}) -> - State1 = #{prev_sync_result := Result} = disk_log_sync(Name, State), - {reply, Result, State1}; - -handle_call({change_config,_OldConfig,NewConfig}, _From, - State = #{filesync_repeat_interval := FSyncInt0}) -> - HConfig = maps:get(config, NewConfig, #{}), - State1 = #{sync_mode_qlen := SMQL, - drop_mode_qlen := DMQL, - flush_qlen := FQL} = maps:merge(State, HConfig), - case logger_h_common:overload_levels_ok(State1) of - true -> - _ = - case maps:get(filesync_repeat_interval, HConfig, undefined) of - undefined -> - ok; - no_repeat -> - _ = logger_h_common:cancel_timer(maps:get(rep_sync_tref, - State, - undefined)); - FSyncInt0 -> - ok; - _FSyncInt1 -> - _ = logger_h_common:cancel_timer(maps:get(rep_sync_tref, - State, - undefined)), - _ = gen_server:cast(self(), repeated_disk_log_sync) - end, - {reply, ok, State1}; - false -> - {reply, {error,{invalid_levels,{SMQL,DMQL,FQL}}}, State} - end; - -handle_call(info, _From, State) -> - {reply, State, State}; - -handle_call(reset, _From, State) -> - State1 = ?merge_with_stats(State), - {reply, ok, State1#{last_qlen => 0, - last_log_ts => ?timestamp(), - prev_log_result => ok, - prev_sync_result => ok, - prev_disk_log_info => undefined}}; - -handle_call(stop, _From, State) -> - {stop, {shutdown,stopped}, ok, State}. - - -%% This is the asynchronous log event. -handle_cast({log, Bin}, State) -> - {_,State1} = do_log(Bin, cast, State), - {noreply, State1}; - -handle_cast({log_handler_info, Format, Args}, State = #{id:=Name}) -> - log_handler_info(Name, Format, Args, State), - {noreply, State}; - -%% If FILESYNC_REPEAT_INTERVAL is set to a millisec value, this -%% clause gets called repeatedly by the handler. In order to -%% guarantee that a filesync *always* happens after the last log -%% event, the repeat operation must be active! -handle_cast(repeated_disk_log_sync, - State = #{id := Name, - filesync_repeat_interval := FSyncInt, - last_op := LastOp}) -> - State1 = - if is_integer(FSyncInt) -> - %% only do filesync if something has been - %% written since last time we checked - NewState = if LastOp == sync -> - State; - true -> - disk_log_sync(Name, State) - end, - {ok,TRef} = - timer:apply_after(FSyncInt, gen_server,cast, - [self(),repeated_disk_log_sync]), - NewState#{rep_sync_tref => TRef, last_op => sync}; - true -> - State - end, - {noreply,State1}. +reset_state(State) -> + State#{prev_log_result => ok, + prev_sync_result => ok, + prev_disk_log_info => undefined}. %% The disk log owner must handle status messages from disk_log. handle_info({disk_log, _Node, _Log, {wrap,_NoLostItems}}, State) -> - {noreply, State}; + State; handle_info({disk_log, _Node, Log, Info = {truncated,_NoLostItems}}, State = #{id := Name, prev_disk_log_info := PrevInfo}) -> error_notify_new(Info, PrevInfo, {disk_log,Name,Log,Info}), - {noreply, State#{prev_disk_log_info => Info}}; + State#{prev_disk_log_info => Info}; handle_info({disk_log, _Node, Log, Info = {blocked_log,_Items}}, State = #{id := Name, prev_disk_log_info := PrevInfo}) -> error_notify_new(Info, PrevInfo, {disk_log,Name,Log,Info}), - {noreply, State#{prev_disk_log_info => Info}}; + State#{prev_disk_log_info => Info}; handle_info({disk_log, _Node, Log, full}, State = #{id := Name, prev_disk_log_info := PrevInfo}) -> error_notify_new(full, PrevInfo, {disk_log,Name,Log,full}), - {noreply, State#{prev_disk_log_info => full}}; + State#{prev_disk_log_info => full}; handle_info({disk_log, _Node, Log, Info = {error_status,_Status}}, State = #{id := Name, prev_disk_log_info := PrevInfo}) -> error_notify_new(Info, PrevInfo, {disk_log,Name,Log,Info}), - {noreply, State#{prev_disk_log_info => Info}}; - -handle_info({'EXIT',_Pid,_Why}, State = #{id := _Name}) -> - {noreply, State}; - + State#{prev_disk_log_info => Info}; handle_info(_, State) -> - {noreply, State}. + State. -terminate(Reason, State = #{id := Name}) -> - _ = logger_h_common:cancel_timer(maps:get(rep_sync_tref, State, - undefined)), +terminate(Name, _Reason, _State) -> _ = close_disk_log(Name, normal), - ok = logger_h_common:stop_or_restart(Name, Reason, State), - unregister(?name_to_reg_name(?MODULE, Name)), ok. -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - %%%----------------------------------------------------------------- %%% Internal functions - -%%%----------------------------------------------------------------- -%%% -get_default_config() -> - #{sync_mode_qlen => ?SYNC_MODE_QLEN, - drop_mode_qlen => ?DROP_MODE_QLEN, - flush_qlen => ?FLUSH_QLEN, - burst_limit_enable => ?BURST_LIMIT_ENABLE, - burst_limit_max_count => ?BURST_LIMIT_MAX_COUNT, - burst_limit_window_time => ?BURST_LIMIT_WINDOW_TIME, - overload_kill_enable => ?OVERLOAD_KILL_ENABLE, - overload_kill_qlen => ?OVERLOAD_KILL_QLEN, - overload_kill_mem_size => ?OVERLOAD_KILL_MEM_SIZE, - overload_kill_restart_after => ?OVERLOAD_KILL_RESTART_AFTER, - filesync_repeat_interval => ?FILESYNC_REPEAT_INTERVAL}. - -get_init_state() -> - #{dl_sync_int => ?CONTROLLER_SYNC_INTERVAL, - filesync_ok_qlen => ?FILESYNC_OK_QLEN}. - -%%%----------------------------------------------------------------- -%%% Add a disk_log handler to the logger. -%%% This starts a dedicated handler process which should always -%%% exist if the handler is registered with logger (and should not -%%% exist if the handler is not registered). -%%% -%%% Config is the logger:handler_config() map. Handler specific parameters -%%% should be provided with a sub map associated with a key named -%%% 'config', e.g: -%%% -%%% Config = #{config => #{sync_mode_qlen => 50} -%%% -%%% The 'config' sub map will also contain parameters for configuring -%%% the disk_log: -%%% -%%% Config = #{config => #{file => file:filename(), -%%% max_no_bytes => integer(), -%%% max_no_files => integer(), -%%% type => wrap | halt}}. -%%% -%%% If type == halt, then max_no_files is ignored. -%%% -%%% The disk_log handler process is linked to logger_sup, which is -%%% part of the kernel application's supervision tree. -start(Name, Config, HandlerState) -> - LoggerDLH = - #{id => Name, - start => {?MODULE, start_link, [Name,Config,HandlerState]}, - restart => temporary, - shutdown => 2000, - type => worker, - modules => [?MODULE]}, - case supervisor:start_child(logger_sup, LoggerDLH) of - {ok,Pid,Config1} -> - ok = logger_handler_watcher:register_handler(Name,Pid), - {ok,Config1}; - Error -> - Error - end. - -%%%----------------------------------------------------------------- -%%% Stop and remove the handler. -stop(Name) -> - case whereis(?name_to_reg_name(?MODULE,Name)) of - undefined -> - ok; - Pid -> - %% We don't want to do supervisor:terminate_child here - %% since we need to distinguish this explicit stop from a - %% system termination in order to avoid circular attempts - %% at removing the handler (implying deadlocks and - %% timeouts). - %% And we don't need to do supervisor:delete_child, since - %% the restart type is temporary, which means that the - %% child specification is automatically removed from the - %% supervisor when the process dies. - _ = gen_server:call(Pid, stop), - ok - end. - -%%%----------------------------------------------------------------- -%%% Logging and overload control. --define(update_dl_sync(C, Interval), - if C == 0 -> Interval; - true -> C-1 end). - -%% check for overload between every event (and set Mode to async, -%% sync or drop accordingly), but never flush the whole mailbox -%% before LogWindowSize events have been handled -do_log(Bin, CallOrCast, State = #{id:=Name, mode := Mode0}) -> - T1 = ?timestamp(), - - %% check if the handler is getting overloaded, or if it's - %% recovering from overload (the check must be done for each - %% event to react quickly to large bursts of events and - %% to ensure that the handler can never end up in drop mode - %% with an empty mailbox, which would stop operation) - {Mode1,QLen,Mem,State1} = logger_h_common:check_load(State), - - if (Mode1 == drop) andalso (Mode0 =/= drop) -> - log_handler_info(Name, "Handler ~p switched to drop mode", - [Name], State); - (Mode0 == drop) andalso ((Mode1 == async) orelse (Mode1 == sync)) -> - log_handler_info(Name, "Handler ~p switched to ~w mode", - [Name,Mode1], State); - true -> - ok - end, - - %% kill the handler if it can't keep up with the load - logger_h_common:kill_if_choked(Name, QLen, Mem, ?MODULE, State), - - if Mode1 == flush -> - flush(Name, QLen, T1, State1); - true -> - write(Name, Mode1, T1, Bin, CallOrCast, State1) - end. - -%% this function is called by do_log/3 after an overload check -%% has been performed, where QLen > FlushQLen -flush(Name, _QLen0, T1, State=#{last_log_ts := _T0, mode_tab := ModeTab}) -> - %% flush messages in the mailbox (a limited number in - %% order to not cause long delays) - NewFlushed = logger_h_common:flush_log_events(?FLUSH_MAX_N), - - %% write info in log about flushed messages - log_handler_info(Name, "Handler ~p flushed ~w log events", - [Name,NewFlushed], State), - - %% because of the receive loop when flushing messages, the - %% handler will be scheduled out often and the mailbox could - %% grow very large, so we'd better check the queue again here - {_,_QLen1} = process_info(self(), message_queue_len), - ?observe(Name,{max_qlen,_QLen1}), - - %% Add 1 for the current log event - ?observe(Name,{flushed,NewFlushed+1}), - - State1 = ?update_max_time(?diff_time(T1,_T0),State), - {dropped,?update_other(flushed,FLUSHED,NewFlushed, - State1#{mode => ?set_mode(ModeTab,async), - last_qlen => 0, - last_log_ts => T1})}. - -%% this function is called to write to disk_log -write(Name, Mode, T1, Bin, _CallOrCast, - State = #{mode_tab := ModeTab, - dl_sync := DLSync, - dl_sync_int := DLSyncInt, - last_qlen := LastQLen, - last_log_ts := T0}) -> - %% check if we need to limit the number of writes - %% during a burst of log events - {DoWrite,BurstWinT,BurstMsgCount} = logger_h_common:limit_burst(State), - - %% only send a synhrounous event to the disk_log process - %% every DLSyncInt time, to give the handler time between - %% writes so it can keep up with incoming messages - {Status,LastQLen1,State1} = - if DoWrite, DLSync == 0 -> - ?observe(Name,{_CallOrCast,1}), - NewState = disk_log_write(Name, Bin, State), - {ok, element(2,process_info(self(),message_queue_len)), - NewState}; - DoWrite -> - ?observe(Name,{_CallOrCast,1}), - NewState = disk_log_write(Name, Bin, State), - {ok, LastQLen, NewState}; - not DoWrite -> - ?observe(Name,{flushed,1}), - {dropped, LastQLen, State} - end, - - %% Check if the time since the previous log event is long enough - - %% and the queue length small enough - to assume the mailbox has - %% been emptied, and if so, do filesync operation and reset mode to - %% async. Note that this is the best we can do to detect an idle - %% handler without setting a timer after each log call/cast. If the - %% time between two consecutive log events is fast and no new - %% event comes in after the last one, idle state won't be detected! - Time = ?diff_time(T1,T0), - {Mode1,BurstMsgCount1,State2} = - if (LastQLen1 < ?FILESYNC_OK_QLEN) andalso - (Time > ?IDLE_DETECT_TIME_USEC) -> - {?change_mode(ModeTab,Mode,async), 0, disk_log_sync(Name,State1)}; - true -> - {Mode, BurstMsgCount,State1} - end, - - State3 = - ?update_calls_or_casts(_CallOrCast,1,State2), - State4 = - ?update_max_time(Time, - State3#{mode => Mode1, - last_qlen := LastQLen1, - last_log_ts => T1, - burst_win_ts => BurstWinT, - burst_msg_count => BurstMsgCount1, - dl_sync => ?update_dl_sync(DLSync,DLSyncInt)}), - {Status,State4}. - - -log_handler_info(Name, Format, Args, State) -> - Config = - case logger:get_handler_config(Name) of - {ok,Conf} -> Conf; - _ -> #{formatter=>{?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}} - end, - Meta = #{time=>erlang:system_time(microsecond)}, - Bin = logger_h_common:log_to_binary(#{level => notice, - msg => {Format,Args}, - meta => Meta}, Config), - _ = disk_log_write(Name, Bin, State), - ok. - - open_disk_log(Name, File, Type, MaxNoBytes, MaxNoFiles) -> case filelib:ensure_dir(File) of ok -> @@ -701,7 +267,7 @@ close_disk_log(Name, _) -> disk_log_write(Name, Bin, State) -> case ?disk_log_blog(Name, Bin) of ok -> - State#{prev_log_result => ok, last_op => write}; + {ok,State#{prev_log_result => ok, last_op => write}}; LogError -> _ = case maps:get(prev_log_result, State) of LogError -> @@ -713,13 +279,13 @@ disk_log_write(Name, Bin, State) -> LogOpts, LogError}) end, - State#{prev_log_result => LogError} + {LogError,State#{prev_log_result => LogError}} end. disk_log_sync(Name, State) -> case ?disk_log_sync(Name) of ok -> - State#{prev_sync_result => ok, last_op => sync}; + {ok,State#{prev_sync_result => ok, last_op => sync}}; SyncError -> _ = case maps:get(prev_sync_result, State) of SyncError -> @@ -731,7 +297,7 @@ disk_log_sync(Name, State) -> LogOpts, SyncError}) end, - State#{prev_sync_result => SyncError} + {SyncError,State#{prev_sync_result => SyncError}} end. error_notify_new(Info,Info, _Term) -> diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl index 94c640cb92..d01851b2b1 100644 --- a/lib/kernel/src/logger_h_common.erl +++ b/lib/kernel/src/logger_h_common.erl @@ -18,26 +18,541 @@ %% %CopyrightEnd% %% -module(logger_h_common). +-behaviour(gen_server). -include("logger_h_common.hrl"). -include("logger_internal.hrl"). --export([log_to_binary/2, - check_common_config/1, - call_cast_or_drop/4, - check_load/1, - limit_burst/1, - kill_if_choked/5, - flush_log_events/0, - flush_log_events/1, - handler_exit/2, - set_restart_flag/2, - unset_restart_flag/2, - cancel_timer/1, - stop_or_restart/3, - overload_levels_ok/1, - error_notify/1, - info_notify/1]). +%% API +-export([start_link/3, info/2, filesync/2, reset/2]). + +%% gen_server and proc_lib callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +%% logger callbacks +-export([log/2, adding_handler/1, removing_handler/1, changing_config/3, + filter_config/1]). + +%% Library functions for handlers +-export([error_notify/1]). + +%%%----------------------------------------------------------------- +-define(CONFIG_KEYS,[sync_mode_qlen, + drop_mode_qlen, + flush_qlen, + burst_limit_enable, + burst_limit_max_count, + burst_limit_window_time, + overload_kill_enable, + overload_kill_qlen, + overload_kill_mem_size, + overload_kill_restart_after, + filesync_repeat_interval]). +-define(READ_ONLY_KEYS,[handler_pid,mode_tab]). + +%%%----------------------------------------------------------------- +%%% +filesync(Module, Name) when is_atom(Name) -> + try + gen_server:call(?name_to_reg_name(Module,Name), + filesync, ?DEFAULT_CALL_TIMEOUT) + catch + _:{timeout,_} -> {error,handler_busy} + end; +filesync(_, Name) -> + {error,{badarg,{filesync,[Name]}}}. + +info(Module, Name) when is_atom(Name) -> + try + gen_server:call(?name_to_reg_name(Module,Name), + info, ?DEFAULT_CALL_TIMEOUT) + catch + _:{timeout,_} -> {error,handler_busy} + end; +info(_, Name) -> + {error,{badarg,{info,[Name]}}}. + +reset(Module, Name) when is_atom(Name) -> + try + gen_server:call(?name_to_reg_name(Module,Name), + reset, ?DEFAULT_CALL_TIMEOUT) + catch + _:{timeout,_} -> {error,handler_busy} + end; +reset(_, Name) -> + {error,{badarg,{reset,[Name]}}}. + + + +%%%----------------------------------------------------------------- +%%% Handler being added +adding_handler(#{id:=Name,module:=Module}=Config) -> + HConfig = maps:get(config, Config, #{}), + HandlerConfig0 = maps:without(?CONFIG_KEYS,HConfig), + case Module:check_config(Name,set,undefined,HandlerConfig0) of + {ok,HandlerConfig} -> + ModifiedCommon = maps:with(?CONFIG_KEYS,HandlerConfig), + CommonConfig0 = maps:with(?CONFIG_KEYS,HConfig), + CommonConfig = maps:merge( + maps:merge(get_default_config(), CommonConfig0), + ModifiedCommon), + case check_config(CommonConfig) of + ok -> + State = maps:merge(get_init_state(), CommonConfig), + HConfig1 = maps:merge(CommonConfig,HandlerConfig), + Config1 = Config#{config=>HConfig1}, + case overload_levels_ok(State) of + true -> + start(Name, Config1, State); + false -> + #{sync_mode_qlen := SMQL, + drop_mode_qlen := DMQL, + flush_qlen := FQL} = State, + {error,{invalid_levels,{SMQL,DMQL,FQL}}} + end; + {error,Faulty} -> + {error,{invalid_config,Module,Faulty}} + end; + Error -> + Error + end. + +%%%----------------------------------------------------------------- +%%% Handler being removed +removing_handler(#{id:=Name, module:=Module}) -> + case whereis(?name_to_reg_name(Module,Name)) of + undefined -> + ok; + Pid -> + %% We don't want to do supervisor:terminate_child here + %% since we need to distinguish this explicit stop from a + %% system termination in order to avoid circular attempts + %% at removing the handler (implying deadlocks and + %% timeouts). + %% And we don't need to do supervisor:delete_child, since + %% the restart type is temporary, which means that the + %% child specification is automatically removed from the + %% supervisor when the process dies. + _ = gen_server:call(Pid, stop), + ok + end. + +%%%----------------------------------------------------------------- +%%% Updating handler config +changing_config(SetOrUpdate, + OldConfig=#{id:=Name,config:=OldHConfig,module:=Module}, + NewConfig0) -> + NewHConfig0 = maps:get(config, NewConfig0, #{}), + OldHandlerConfig = maps:without(?CONFIG_KEYS++?READ_ONLY_KEYS,OldHConfig), + NewHandlerConfig0 = maps:without(?CONFIG_KEYS++?READ_ONLY_KEYS,NewHConfig0), + case Module:check_config(Name, SetOrUpdate, + OldHandlerConfig,NewHandlerConfig0) of + {ok, NewHandlerConfig} -> + ModifiedCommon = maps:with(?CONFIG_KEYS,NewHandlerConfig), + NewCommonConfig0 = maps:with(?CONFIG_KEYS,NewHConfig0), + CommonDefault = + case SetOrUpdate of + set -> + get_default_config(); + update -> + maps:with(?CONFIG_KEYS,OldHConfig) + end, + NewCommonConfig = maps:merge( + maps:merge(CommonDefault,NewCommonConfig0), + ModifiedCommon), + case check_config(NewCommonConfig) of + ok -> + ReadOnly = maps:with(?READ_ONLY_KEYS,OldHConfig), + NewHConfig = maps:merge( + maps:merge(NewCommonConfig,NewHandlerConfig), + ReadOnly), + NewConfig = NewConfig0#{config=>NewHConfig}, + HPid = maps:get(handler_pid,OldHConfig), + try gen_server:call(HPid, {change_config,OldConfig,NewConfig}, + ?DEFAULT_CALL_TIMEOUT) of + ok -> {ok,NewConfig}; + Error -> Error + catch + _:{timeout,_} -> {error,handler_busy} + end; + {error,Faulty} -> + {error,{invalid_config,Module,Faulty}} + end; + Error -> + Error + end. + +%%%----------------------------------------------------------------- +%%% Log a string or report +-spec log(LogEvent, Config) -> ok when + LogEvent :: logger:log_event(), + Config :: logger:handler_config(). + +log(LogEvent, Config = #{id := Name, + config := #{handler_pid := HPid, + mode_tab := ModeTab}}) -> + %% if the handler has crashed, we must drop this event + %% and hope the handler restarts so we can try again + true = is_process_alive(HPid), + Bin = log_to_binary(LogEvent, Config), + call_cast_or_drop(Name, HPid, ModeTab, Bin). + +%%%----------------------------------------------------------------- +%%% Remove internal fields from configuration +filter_config(#{config:=HConfig}=Config) -> + Config#{config=>maps:without(?READ_ONLY_KEYS,HConfig)}. + +%%%----------------------------------------------------------------- +%%% Add a standard handler to the logger. +%%% This starts a dedicated handler process which should always +%%% exist if the handler is registered with logger (and should not +%%% exist if the handler is not registered). +%%% +%%% Handler specific config should be provided with a sub map associated +%%% with a key named 'config', e.g: +%%% +%%% Config = #{config => #{sync_mode_qlen => 50} +%%% +%%% The standard handler process is linked to logger_sup, which is +%%% part of the kernel application's supervision tree. +start(Name, Config, HandlerState) -> + ChildSpec = + #{id => Name, + start => {?MODULE, start_link, [Name,Config,HandlerState]}, + restart => temporary, + shutdown => 2000, + type => worker, + modules => [?MODULE]}, + case supervisor:start_child(logger_sup, ChildSpec) of + {ok,Pid,Config1} -> + ok = logger_handler_watcher:register_handler(Name,Pid), + {ok,Config1}; + Error -> + Error + end. + +%%%----------------------------------------------------------------- +%%% Start a standard handler process and link to caller. +%%% This function is called by the kernel supervisor when this +%%% handler process gets added +-spec start_link(Name, Config, HandlerState) -> {ok,Pid} | {error,Reason} when + Name :: atom(), + Config :: logger:handler_config(), + HandlerState :: map(), + Pid :: pid(), + Reason :: term(). + +start_link(Name, Config, HandlerState) -> + proc_lib:start_link(?MODULE,init,[[Name,Config,HandlerState]]). + +%%%----------------------------------------------------------------- +%%% +get_init_state() -> + #{ctrl_sync_int => ?CONTROLLER_SYNC_INTERVAL, + filesync_ok_qlen => ?FILESYNC_OK_QLEN}. + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== + +init([Name, Config = #{config := HConfig, module := Module}, + State = #{filesync_repeat_interval := FSyncInt, + ctrl_sync_int := CtrlSyncInt}]) -> + RegName = ?name_to_reg_name(Module,Name), + register(RegName, self()), + process_flag(trap_exit, true), + process_flag(message_queue_data, off_heap), + + ?init_test_hooks(), + ?start_observation(Name), + + case Module:init(Name, HConfig) of + {ok,HState} -> + try ets:new(Name, [public]) of + ModeTab -> + ?set_mode(ModeTab, async), + T0 = ?timestamp(), + State1 = + ?merge_with_stats(State#{id => Name, + module => Module, + mode_tab => ModeTab, + mode => async, + ctrl_sync_count => CtrlSyncInt, + last_qlen => 0, + last_log_ts => T0, + last_op => sync, + burst_win_ts => T0, + burst_msg_count => 0, + handler_state => HState}), + Config1 = + Config#{config => HConfig#{handler_pid => self(), + mode_tab => ModeTab}}, + proc_lib:init_ack({ok,self(),Config1}), + if is_integer(FSyncInt) -> + gen_server:cast(self(), repeated_filesync); + true -> + ok + end, + case unset_restart_flag(Name, Module) of + true -> + %% inform about restart + gen_server:cast(self(), {log_handler_info, + "Handler ~p restarted", + [Name]}); + false -> + %% initial start + ok + end, + gen_server:enter_loop(?MODULE, [], State1) + catch + _:Error -> + unregister(RegName), + error_notify({init_handler,Name,Error}), + proc_lib:init_ack(Error) + end; + Error -> + unregister(RegName), + error_notify({init_handler,Name,Error}), + proc_lib:init_ack(Error) + end. + +%% This is the synchronous log event. +handle_call({log, Bin}, _From, State) -> + {Result,State1} = do_log(Bin, call, State), + %% Result == ok | dropped + {reply,Result, State1}; + +handle_call(filesync, _From, State = #{id := Name, + module := Module, + handler_state := HandlerState}) -> + {Result,HandlerState1} = Module:sync_filesync(Name,HandlerState), + {reply, Result, State#{handler_state=>HandlerState1, last_op=>sync}}; + +handle_call({change_config,_OldConfig,NewConfig}, _From, + State = #{filesync_repeat_interval := FSyncInt0}) -> + HConfig = maps:get(config, NewConfig, #{}), + State1 = maps:merge(State, HConfig), + case overload_levels_ok(State1) of + true -> + _ = + case maps:get(filesync_repeat_interval, HConfig, undefined) of + undefined -> + ok; + no_repeat -> + _ = logger_h_common:cancel_timer(maps:get(rep_sync_tref, + State, + undefined)); + FSyncInt0 -> + ok; + _FSyncInt1 -> + _ = logger_h_common:cancel_timer(maps:get(rep_sync_tref, + State, + undefined)), + gen_server:cast(self(), repeated_filesync) + end, + {reply, ok, State1}; + false -> + #{sync_mode_qlen := SMQL, + drop_mode_qlen := DMQL, + flush_qlen := FQL} = State1, + {reply, {error,{invalid_levels,{SMQL,DMQL,FQL}}}, State} + end; + +handle_call(info, _From, State) -> + {reply, State, State}; + +handle_call(reset, _From, #{module:=Module,handler_state:=HandlerState}=State) -> + State1 = ?merge_with_stats(State), + {reply, ok, State1#{last_qlen => 0, + last_log_ts => ?timestamp(), + handler_state => Module:reset_state(HandlerState)}}; + +handle_call(stop, _From, State) -> + {stop, {shutdown,stopped}, ok, State}. + +%% This is the asynchronous log event. +handle_cast({log, Bin}, State) -> + {_,State1} = do_log(Bin, cast, State), + {noreply, State1}; + +handle_cast({log_handler_info, Format, Args}, State = #{id:=Name}) -> + log_handler_info(Name, Format, Args, State), + {noreply, State}; + +%% If FILESYNC_REPEAT_INTERVAL is set to a millisec value, this +%% clause gets called repeatedly by the handler. In order to +%% guarantee that a filesync *always* happens after the last log +%% event, the repeat operation must be active! +handle_cast(repeated_filesync,State = #{filesync_repeat_interval := no_repeat}) -> + {noreply,State}; +handle_cast(repeated_filesync, + State = #{id := Name, + module := Module, + handler_state := HandlerState, + filesync_repeat_interval := FSyncInt, + last_op := LastOp}) -> + HandlerState1 = + if LastOp == sync -> + HandlerState; + true -> + Module:async_filesync(Name,HandlerState) + end, + {ok,TRef} = timer:apply_after(FSyncInt, gen_server,cast, + [self(),repeated_filesync]), + {noreply,State#{handler_state=>HandlerState1, + rep_sync_tref => TRef, + last_op => sync}}. + +handle_info(Info, #{module := Module, handler_state := HandlerState} = State) -> + {noreply,State#{handler_state => Module:handle_info(Info,HandlerState)}}. + +terminate(Reason, State = #{id := Name, + module := Module, + handler_state := HandlerState}) -> + _ = cancel_timer(maps:get(rep_sync_tref, State, undefined)), + _ = Module:terminate(Name, Reason, HandlerState), + ok = stop_or_restart(Name, Reason, State), + unregister(?name_to_reg_name(Module, Name)), + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + + +%% check for overload between every event (and set Mode to async, +%% sync or drop accordingly), but never flush the whole mailbox +%% before LogWindowSize events have been handled +do_log(Bin, CallOrCast, State = #{id:=Name, module:=Module, mode:=Mode0}) -> + T1 = ?timestamp(), + + %% check if the handler is getting overloaded, or if it's + %% recovering from overload (the check must be done for each + %% event to react quickly to large bursts of events and + %% to ensure that the handler can never end up in drop mode + %% with an empty mailbox, which would stop operation) + {Mode1,QLen,Mem,State1} = check_load(State), + + if (Mode1 == drop) andalso (Mode0 =/= drop) -> + log_handler_info(Name, "Handler ~p switched to drop mode", + [Name], State); + (Mode0 == drop) andalso ((Mode1 == async) orelse (Mode1 == sync)) -> + log_handler_info(Name, "Handler ~p switched to ~w mode", + [Name,Mode1], State); + true -> + ok + end, + + %% kill the handler if it can't keep up with the load + kill_if_choked(Name, Module, QLen, Mem, State), + + if Mode1 == flush -> + flush(Name, QLen, T1, State1); + true -> + write(Name, Mode1, T1, Bin, CallOrCast, State1) + end. + +%% this clause is called by do_log/3 after an overload check +%% has been performed, where QLen > FlushQLen +flush(Name, _QLen0, T1, State=#{last_log_ts := _T0, mode_tab := ModeTab}) -> + %% flush messages in the mailbox (a limited number in + %% order to not cause long delays) + NewFlushed = flush_log_events(?FLUSH_MAX_N), + + %% write info in log about flushed messages + log_handler_info(Name, "Handler ~p flushed ~w log events", + [Name,NewFlushed], State), + + %% because of the receive loop when flushing messages, the + %% handler will be scheduled out often and the mailbox could + %% grow very large, so we'd better check the queue again here + {_,_QLen1} = process_info(self(), message_queue_len), + ?observe(Name,{max_qlen,_QLen1}), + + %% Add 1 for the current log event + ?observe(Name,{flushed,NewFlushed+1}), + + State1 = ?update_max_time(?diff_time(T1,_T0),State), + {dropped,?update_other(flushed,FLUSHED,NewFlushed, + State1#{mode => ?set_mode(ModeTab,async), + last_qlen => 0, + last_log_ts => T1})}. + +%% this clause is called to write to file +write(Name, Mode, T1, Bin, _CallOrCast, + State = #{module := Module, + handler_state := HandlerState, + mode_tab := ModeTab, + ctrl_sync_count := CtrlSync, + ctrl_sync_int := CtrlSyncInt, + last_qlen := LastQLen, + last_log_ts := T0}) -> + %% check if we need to limit the number of writes + %% during a burst of log events + {DoWrite,BurstWinT,BurstMsgCount} = limit_burst(State), + + %% only log synhrounously every CtrlSyncInt time, to give the + %% handler time between writes so it can keep up with incoming + %% messages + {Result,LastQLen1,HandlerState1} = + if DoWrite, CtrlSync == 0 -> + ?observe(Name,{_CallOrCast,1}), + {_,HS1} = Module:sync_write(Name, Bin, HandlerState), + {ok,element(2, process_info(self(), message_queue_len)),HS1}; + DoWrite -> + ?observe(Name,{_CallOrCast,1}), + HS1 = Module:async_write(Name, Bin, HandlerState), + {ok,LastQLen,HS1}; + not DoWrite -> + ?observe(Name,{flushed,1}), + {dropped,LastQLen,HandlerState} + end, + + %% Check if the time since the previous log event is long enough - + %% and the queue length small enough - to assume the mailbox has + %% been emptied, and if so, do filesync operation and reset mode to + %% async. Note that this is the best we can do to detect an idle + %% handler without setting a timer after each log call/cast. If the + %% time between two consecutive log events is fast and no new + %% event comes in after the last one, idle state won't be detected! + Time = ?diff_time(T1,T0), + {Mode1,BurstMsgCount1,HandlerState2} = + if (LastQLen1 < ?FILESYNC_OK_QLEN) andalso + (Time > ?IDLE_DETECT_TIME_USEC) -> + HS2 = Module:async_filesync(Name,HandlerState), + {?change_mode(ModeTab, Mode, async),0,HS2}; + true -> + {Mode,BurstMsgCount,HandlerState1} + end, + State1 = + ?update_calls_or_casts(_CallOrCast,1,State), + State2 = + ?update_max_time(Time, + State1#{mode => Mode1, + last_qlen := LastQLen1, + last_log_ts => T1, + last_op => write, + burst_win_ts => BurstWinT, + burst_msg_count => BurstMsgCount1, + ctrl_sync_count => if CtrlSync==0 -> CtrlSyncInt; + true -> CtrlSync-1 + end, + handler_state => HandlerState2}), + {Result,State2}. + +log_handler_info(Name, Format, Args, #{module:=Module, + handler_state:=HandlerState}) -> + Config = + case logger:get_handler_config(Name) of + {ok,Conf} -> Conf; + _ -> #{formatter=>{?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}} + end, + Meta = #{time=>erlang:system_time(microsecond)}, + Bin = log_to_binary(#{level => notice, + msg => {Format,Args}, + meta => Meta}, Config), + _ = Module:async_write(Name, Bin, HandlerState), + ok. %%%----------------------------------------------------------------- %%% Convert log data on any form to binary @@ -94,46 +609,52 @@ string_to_binary(String) -> throw(Error) end. - %%%----------------------------------------------------------------- %%% Check that the configuration term is valid -check_common_config({mode_tab,_Tid}) -> - valid; -check_common_config({handler_pid,Pid}) when is_pid(Pid) -> - valid; - -check_common_config({sync_mode_qlen,N}) when is_integer(N) -> - valid; -check_common_config({drop_mode_qlen,N}) when is_integer(N) -> - valid; -check_common_config({flush_qlen,N}) when is_integer(N) -> - valid; - -check_common_config({burst_limit_enable,Bool}) when Bool == true; - Bool == false -> - valid; -check_common_config({burst_limit_max_count,N}) when is_integer(N) -> - valid; -check_common_config({burst_limit_window_time,N}) when is_integer(N) -> - valid; - -check_common_config({overload_kill_enable,Bool}) when Bool == true; - Bool == false -> - valid; -check_common_config({overload_kill_qlen,N}) when is_integer(N) -> - valid; -check_common_config({overload_kill_mem_size,N}) when is_integer(N) -> - valid; -check_common_config({overload_kill_restart_after,NorA}) when is_integer(NorA); - NorA == infinity -> - valid; - -check_common_config({filesync_repeat_interval,NorA}) when is_integer(NorA); - NorA == no_repeat -> - valid; -check_common_config(_) -> - invalid. +check_config(Config) when is_map(Config) -> + check_common_config(maps:to_list(Config)). +check_common_config([{sync_mode_qlen,N}|Config]) when is_integer(N) -> + check_common_config(Config); +check_common_config([{drop_mode_qlen,N}|Config]) when is_integer(N) -> + check_common_config(Config); +check_common_config([{flush_qlen,N}|Config]) when is_integer(N) -> + check_common_config(Config); +check_common_config([{burst_limit_enable,Bool}|Config]) when is_boolean(Bool) -> + check_common_config(Config); +check_common_config([{burst_limit_max_count,N}|Config]) when is_integer(N) -> + check_common_config(Config); +check_common_config([{burst_limit_window_time,N}|Config]) when is_integer(N) -> + check_common_config(Config); +check_common_config([{overload_kill_enable,Bool}|Config]) when is_boolean(Bool) -> + check_common_config(Config); +check_common_config([{overload_kill_qlen,N}|Config]) when is_integer(N) -> + check_common_config(Config); +check_common_config([{overload_kill_mem_size,N}|Config]) when is_integer(N) -> + check_common_config(Config); +check_common_config([{overload_kill_restart_after,NorA}|Config]) + when is_integer(NorA); NorA == infinity -> + check_common_config(Config); +check_common_config([{filesync_repeat_interval,NorA}|Config]) + when is_integer(NorA); NorA == no_repeat -> + check_common_config(Config); +check_common_config([{Key,Value}|_]) -> + {error,#{Key=>Value}}; +check_common_config([]) -> + ok. + +get_default_config() -> + #{sync_mode_qlen => ?SYNC_MODE_QLEN, + drop_mode_qlen => ?DROP_MODE_QLEN, + flush_qlen => ?FLUSH_QLEN, + burst_limit_enable => ?BURST_LIMIT_ENABLE, + burst_limit_max_count => ?BURST_LIMIT_MAX_COUNT, + burst_limit_window_time => ?BURST_LIMIT_WINDOW_TIME, + overload_kill_enable => ?OVERLOAD_KILL_ENABLE, + overload_kill_qlen => ?OVERLOAD_KILL_QLEN, + overload_kill_mem_size => ?OVERLOAD_KILL_MEM_SIZE, + overload_kill_restart_after => ?OVERLOAD_KILL_RESTART_AFTER, + filesync_repeat_interval => ?FILESYNC_REPEAT_INTERVAL}. %%%----------------------------------------------------------------- %%% Overload Protection @@ -243,16 +764,16 @@ limit_burst(#{burst_win_ts := BurstWinT0, {true,BurstWinT0,BurstMsgCount+1} end. -kill_if_choked(Name, QLen, Mem, HandlerMod, +kill_if_choked(Name, Module, QLen, Mem, State = #{overload_kill_enable := KillIfOL, overload_kill_qlen := OLKillQLen, overload_kill_mem_size := OLKillMem}) -> if KillIfOL andalso ((QLen > OLKillQLen) orelse (Mem > OLKillMem)) -> - HandlerMod:log_handler_info(Name, - "Handler ~p overloaded and stopping", - [Name], State), - set_restart_flag(Name, HandlerMod), + log_handler_info(Name, + "Handler ~p overloaded and stopping", + [Name], State), + set_restart_flag(Name, Module), handler_exit(Name, {shutdown,{overloaded,Name,QLen,Mem}}); true -> ok @@ -290,9 +811,9 @@ cancel_timer(TRef) -> timer:cancel(TRef). stop_or_restart(Name, {shutdown,Reason={overloaded,_Name,_QLen,_Mem}}, #{overload_kill_restart_after := RestartAfter}) -> %% If we're terminating because of an overload situation (see - %% logger_h_common:kill_if_choked/4), we need to remove the handler - %% and set a restart timer. A separate process must perform this - %% in order to avoid deadlock. + %% kill_if_choked/5), we need to remove the handler and set a + %% restart timer. A separate process must perform this in order to + %% avoid deadlock. HandlerPid = self(), ConfigResult = logger:get_handler_config(Name), RemoveAndRestart = @@ -334,6 +855,3 @@ overload_levels_ok(HandlerConfig) -> error_notify(Term) -> ?internal_log(error, Term). - -info_notify(Term) -> - ?internal_log(info, Term). diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index 42e0f5caf4..95e7d17c0d 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -19,8 +19,6 @@ %% -module(logger_std_h). --behaviour(gen_server). - -include("logger.hrl"). -include("logger_internal.hrl"). -include("logger_h_common.hrl"). @@ -28,52 +26,30 @@ -include_lib("kernel/include/file.hrl"). %% API --export([start_link/3, info/1, filesync/1, reset/1]). +-export([info/1, filesync/1, reset/1]). -%% gen_server and proc_lib callbacks --export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). +%% logger_h_common callbacks +-export([init/2, check_config/4, reset_state/1, + async_filesync/2, sync_filesync/2, + async_write/3, sync_write/3, + handle_info/2, terminate/3]). %% logger callbacks -export([log/2, adding_handler/1, removing_handler/1, changing_config/3, filter_config/1]). -%% handler internal --export([log_handler_info/4]). - %%%=================================================================== %%% API %%%=================================================================== -%%%----------------------------------------------------------------- -%%% Start a standard handler process and link to caller. -%%% This function is called by the kernel supervisor when this -%%% handler process gets added --spec start_link(Name, Config, HandlerState) -> {ok,Pid} | {error,Reason} when - Name :: atom(), - Config :: logger:handler_config(), - HandlerState :: map(), - Pid :: pid(), - Reason :: term(). - -start_link(Name, Config, HandlerState) -> - proc_lib:start_link(?MODULE,init,[[Name,Config,HandlerState]]). - %%%----------------------------------------------------------------- %%% -spec filesync(Name) -> ok | {error,Reason} when Name :: atom(), Reason :: handler_busy | {badarg,term()}. -filesync(Name) when is_atom(Name) -> - try - gen_server:call(?name_to_reg_name(?MODULE,Name), - filesync, ?DEFAULT_CALL_TIMEOUT) - catch - _:{timeout,_} -> {error,handler_busy} - end; filesync(Name) -> - {error,{badarg,{filesync,[Name]}}}. + logger_h_common:filesync(?MODULE,Name). %%%----------------------------------------------------------------- %%% @@ -82,15 +58,8 @@ filesync(Name) -> Info :: term(), Reason :: handler_busy | {badarg,term()}. -info(Name) when is_atom(Name) -> - try - gen_server:call(?name_to_reg_name(?MODULE,Name), - info, ?DEFAULT_CALL_TIMEOUT) - catch - _:{timeout,_} -> {error,handler_busy} - end; info(Name) -> - {error,{badarg,{info,[Name]}}}. + logger_h_common:info(?MODULE,Name). %%%----------------------------------------------------------------- %%% @@ -98,16 +67,8 @@ info(Name) -> Name :: atom(), Reason :: handler_busy | {badarg,term()}. -reset(Name) when is_atom(Name) -> - try - gen_server:call(?name_to_reg_name(?MODULE,Name), - reset, ?DEFAULT_CALL_TIMEOUT) - catch - _:{timeout,_} -> {error,handler_busy} - end; reset(Name) -> - {error,{badarg,{reset,[Name]}}}. - + logger_h_common:reset(?MODULE,Name). %%%=================================================================== %%% logger callbacks @@ -115,30 +76,48 @@ reset(Name) -> %%%----------------------------------------------------------------- %%% Handler being added -adding_handler(#{id:=Name}=Config) -> - case check_config(adding, Config) of - {ok, #{config:=HConfig}=Config1} -> - %% create initial handler state by merging defaults with config - HState = maps:merge(get_init_state(), HConfig), - case logger_h_common:overload_levels_ok(HState) of - true -> - start(Name, Config1, HState); - false -> - #{sync_mode_qlen := SMQL, - drop_mode_qlen := DMQL, - flush_qlen := FQL} = HState, - {error,{invalid_levels,{SMQL,DMQL,FQL}}} - end; +adding_handler(Config) -> + logger_h_common:adding_handler(Config). + +%%%----------------------------------------------------------------- +%%% Updating handler config +changing_config(SetOrUpdate, OldConfig, NewConfig) -> + logger_h_common:changing_config(SetOrUpdate, OldConfig, NewConfig). + +%%%----------------------------------------------------------------- +%%% Handler being removed +removing_handler(Config) -> + logger_h_common:removing_handler(Config). + +%%%----------------------------------------------------------------- +%%% Log a string or report +-spec log(LogEvent, Config) -> ok when + LogEvent :: logger:log_event(), + Config :: logger:handler_config(). + +log(LogEvent, Config) -> + logger_h_common:log(LogEvent, Config). + +%%%----------------------------------------------------------------- +%%% Remove internal fields from configuration +filter_config(Config) -> + logger_h_common:filter_config(Config). + +%%%=================================================================== +%%% logger_h_common callbacks +%%%=================================================================== +init(Name, #{type := Type}) -> + case open_log_file(Name, Type) of + {ok,FileCtrlPid} -> + {ok,#{type=>Type,file_ctrl_pid=>FileCtrlPid}}; Error -> Error end. -%%%----------------------------------------------------------------- -%%% Updating handler config -changing_config(SetOrUpdate,OldConfig=#{config:=OldHConfig},NewConfig) -> +check_config(_Name,set,undefined,NewHConfig) -> + check_config(maps:merge(get_default_config(),NewHConfig)); +check_config(_Name,SetOrUpdate,OldHConfig,NewHConfig0) -> WriteOnce = maps:with([type],OldHConfig), - ReadOnly = maps:with([handler_pid,mode_tab],OldHConfig), - NewHConfig0 = maps:get(config, NewConfig, #{}), Default = case SetOrUpdate of set -> @@ -148,48 +127,24 @@ changing_config(SetOrUpdate,OldConfig=#{config:=OldHConfig},NewConfig) -> OldHConfig end, - %% Allow (accidentially) included read-only fields - just overwrite them - NewHConfig = maps:merge(maps:merge(Default, NewHConfig0),ReadOnly), + NewHConfig = maps:merge(Default, NewHConfig0), - %% But fail if write-once fields are changed + %% Fail if write-once fields are changed case maps:with([type],NewHConfig) of WriteOnce -> - changing_config1(maps:get(handler_pid,OldHConfig), - OldConfig, - NewConfig#{config=>NewHConfig}); + check_config(NewHConfig); Other -> - {error,{illegal_config_change,#{config=>WriteOnce},#{config=>Other}}} + {error,{illegal_config_change,?MODULE,WriteOnce,Other}} end. -changing_config1(HPid, OldConfig, NewConfig) -> - case check_config(changing, NewConfig) of - Result = {ok,NewConfig1} -> - try gen_server:call(HPid, {change_config,OldConfig,NewConfig1}, - ?DEFAULT_CALL_TIMEOUT) of - ok -> Result; - HError -> HError - catch - _:{timeout,_} -> {error,handler_busy} - end; - Error -> - Error - end. - -check_config(adding, Config) -> - %% Merge in defaults on handler level - HConfig0 = maps:get(config, Config, #{}), - HConfig = maps:merge(get_default_config(),HConfig0), +check_config(#{type:=Type}=HConfig) -> case check_h_config(maps:to_list(HConfig)) of + ok when is_atom(Type) -> + {ok,HConfig#{filesync_repeat_interval=>no_repeat}}; ok -> - {ok,Config#{config=>HConfig}}; - Error -> - Error - end; -check_config(changing, Config) -> - HConfig = maps:get(config, Config, #{}), - case check_h_config(maps:to_list(HConfig)) of - ok -> {ok,Config}; - Error -> Error + {ok,HConfig}; + {error,{Key,Value}} -> + {error,{invalid_config,?MODULE,#{Key=>Value}}} end. check_h_config([{type,Type} | Config]) when Type == standard_io; @@ -200,219 +155,44 @@ check_h_config([{type,{file,File}} | Config]) when is_list(File) -> check_h_config([{type,{file,File,Modes}} | Config]) when is_list(File), is_list(Modes) -> check_h_config(Config); -check_h_config([Other | Config]) -> - case logger_h_common:check_common_config(Other) of - valid -> - check_h_config(Config); - invalid -> - {error,{invalid_config,?MODULE,Other}} - end; +check_h_config([Other | _]) -> + {error,Other}; check_h_config([]) -> ok. - -%%%----------------------------------------------------------------- -%%% Handler being removed -removing_handler(#{id:=Name}) -> - stop(Name). - -%%%----------------------------------------------------------------- -%%% Log a string or report --spec log(LogEvent, Config) -> ok when - LogEvent :: logger:log_event(), - Config :: logger:handler_config(). - -log(LogEvent, Config = #{id := Name, - config := #{handler_pid := HPid, - mode_tab := ModeTab}}) -> - %% if the handler has crashed, we must drop this event - %% and hope the handler restarts so we can try again - true = is_process_alive(HPid), - Bin = logger_h_common:log_to_binary(LogEvent, Config), - logger_h_common:call_cast_or_drop(Name, HPid, ModeTab, Bin). - -%%%----------------------------------------------------------------- -%%% Remove internal fields from configuration -filter_config(#{config:=HConfig}=Config) -> - Config#{config=>maps:without([handler_pid,mode_tab],HConfig)}. - -%%%=================================================================== -%%% gen_server callbacks -%%%=================================================================== - -init([Name, Config = #{config := HConfig}, - State0 = #{type := Type, file_ctrl_sync_int := FileCtrlSyncInt}]) -> - RegName = ?name_to_reg_name(?MODULE,Name), - register(RegName, self()), - process_flag(trap_exit, true), - process_flag(message_queue_data, off_heap), - - ?init_test_hooks(), - ?start_observation(Name), - - case do_init(Name, Type) of - {ok,InitState} -> - try ets:new(Name, [public]) of - ModeTab -> - ?set_mode(ModeTab, async), - State = maps:merge(State0, InitState), - T0 = ?timestamp(), - State1 = - ?merge_with_stats(State#{ - mode_tab => ModeTab, - mode => async, - file_ctrl_sync => FileCtrlSyncInt, - last_qlen => 0, - last_log_ts => T0, - last_op => sync, - burst_win_ts => T0, - burst_msg_count => 0}), - Config1 = - Config#{config => HConfig#{handler_pid => self(), - mode_tab => ModeTab}}, - proc_lib:init_ack({ok,self(),Config1}), - gen_server:cast(self(), repeated_filesync), - gen_server:enter_loop(?MODULE, [], State1) - catch - _:Error -> - unregister(RegName), - logger_h_common:error_notify({init_handler,Name,Error}), - proc_lib:init_ack(Error) - end; - Error -> - unregister(RegName), - logger_h_common:error_notify({init_handler,Name,Error}), - proc_lib:init_ack(Error) - end. - -do_init(Name, Type) -> - case open_log_file(Name, Type) of - {ok,FileCtrlPid} -> - case logger_h_common:unset_restart_flag(Name, ?MODULE) of - true -> - %% inform about restart - gen_server:cast(self(), {log_handler_info, - "Handler ~p restarted", - [Name]}); - false -> - %% initial start - ok - end, - {ok,#{id=>Name,type=>Type,file_ctrl_pid=>FileCtrlPid}}; - Error -> - Error - end. - -%% This is the synchronous log event. -handle_call({log, Bin}, _From, State) -> - {Result,State1} = do_log(Bin, call, State), - %% Result == ok | dropped - {reply,Result, State1}; - -handle_call(filesync, _From, State = #{type := Type, - file_ctrl_pid := FileCtrlPid}) -> - if is_atom(Type) -> - {reply, ok, State}; - true -> - {reply, file_ctrl_filesync_sync(FileCtrlPid), State#{last_op=>sync}} - end; - -handle_call({change_config,_OldConfig,NewConfig}, _From, - State = #{filesync_repeat_interval := FSyncInt0}) -> - HConfig = maps:get(config, NewConfig, #{}), - State1 = maps:merge(State, HConfig), - case logger_h_common:overload_levels_ok(State1) of - true -> - _ = - case maps:get(filesync_repeat_interval, HConfig, undefined) of - undefined -> - ok; - no_repeat -> - _ = logger_h_common:cancel_timer(maps:get(rep_sync_tref, - State, - undefined)); - FSyncInt0 -> - ok; - _FSyncInt1 -> - _ = logger_h_common:cancel_timer(maps:get(rep_sync_tref, - State, - undefined)), - gen_server:cast(self(), repeated_filesync) - end, - {reply, ok, State1}; - false -> - #{sync_mode_qlen := SMQL, - drop_mode_qlen := DMQL, - flush_qlen := FQL} = State1, - {reply, {error,{invalid_levels,{SMQL,DMQL,FQL}}}, State} - end; - -handle_call(info, _From, State) -> - {reply, State, State}; - -handle_call(reset, _From, State) -> - State1 = ?merge_with_stats(State), - {reply, ok, State1#{last_qlen => 0, - last_log_ts => ?timestamp()}}; - -handle_call(stop, _From, State) -> - {stop, {shutdown,stopped}, ok, State}. - -%% This is the asynchronous log event. -handle_cast({log, Bin}, State) -> - {_,State1} = do_log(Bin, cast, State), - {noreply, State1}; - -handle_cast({log_handler_info, Format, Args}, State = #{id:=Name}) -> - log_handler_info(Name, Format, Args, State), - {noreply, State}; - -%% If FILESYNC_REPEAT_INTERVAL is set to a millisec value, this -%% clause gets called repeatedly by the handler. In order to -%% guarantee that a filesync *always* happens after the last log -%% event, the repeat operation must be active! -handle_cast(repeated_filesync, - State = #{type := Type, - file_ctrl_pid := FileCtrlPid, - filesync_repeat_interval := FSyncInt, - last_op := LastOp}) -> - State1 = - if not is_atom(Type), is_integer(FSyncInt) -> - %% only do filesync if something has been - %% written since last time we checked - if LastOp == sync -> - ok; - true -> - file_ctrl_filesync_async(FileCtrlPid) - end, - {ok,TRef} = - timer:apply_after(FSyncInt, gen_server,cast, - [self(),repeated_filesync]), - State#{rep_sync_tref => TRef, last_op => sync}; - true -> - State - end, - {noreply,State1}. - -handle_info({'EXIT',Pid,Why}, State = #{id := Name, type := FileInfo}) -> - case maps:get(file_ctrl_pid, State, undefined) of - Pid -> - %% file error, terminate handler - logger_h_common:handler_exit(Name, - {error,{write_failed,FileInfo,Why}}); - _Other -> - %% ignore EXIT - ok - end, - {noreply, State}; - -handle_info(_Info, State) -> - {noreply, State}. - -terminate(Reason, State = #{id:=Name, file_ctrl_pid:=FWPid, - type:=_FileInfo}) -> - _ = logger_h_common:cancel_timer(maps:get(rep_sync_tref, State, - undefined)), +get_default_config() -> + #{type => standard_io}. + +async_filesync(_Name,#{type := Type}=State) when is_atom(Type) -> + State; +async_filesync(_Name,#{file_ctrl_pid := FileCtrlPid}=State) -> + ok = file_ctrl_filesync_async(FileCtrlPid), + State. + +sync_filesync(_Name,#{type := Type}=State) when is_atom(Type) -> + {ok,State}; +sync_filesync(_Name,#{file_ctrl_pid := FileCtrlPid}=State) -> + Result = file_ctrl_filesync_sync(FileCtrlPid), + {Result,State}. + +async_write(_Name, Bin, #{file_ctrl_pid:=FileCtrlPid} = State) -> + ok = file_write_async(FileCtrlPid, Bin), + State. + +sync_write(_Name, Bin, #{file_ctrl_pid:=FileCtrlPid} = State) -> + Result = file_write_sync(FileCtrlPid, Bin), + {Result,State}. + +reset_state(State) -> + State. + +handle_info({'EXIT',Pid,Why}, #{type := FileInfo, file_ctrl_pid := Pid}) -> + %% file_ctrl_pid died, file error, terminate handler + exit({error,{write_failed,FileInfo,Why}}); +handle_info(_, State) -> + State. + +terminate(_Name, _Reason, #{file_ctrl_pid:=FWPid}) -> case is_process_alive(FWPid) of true -> unlink(FWPid), @@ -427,13 +207,7 @@ terminate(Reason, State = #{id:=Name, file_ctrl_pid:=FWPid, end; false -> ok - end, - ok = logger_h_common:stop_or_restart(Name, Reason, State), - unregister(?name_to_reg_name(?MODULE, Name)), - ok. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. + end. %%%=================================================================== %%% Internal functions @@ -441,203 +215,6 @@ code_change(_OldVsn, State, _Extra) -> %%%----------------------------------------------------------------- %%% -get_default_config() -> - #{type => standard_io, - sync_mode_qlen => ?SYNC_MODE_QLEN, - drop_mode_qlen => ?DROP_MODE_QLEN, - flush_qlen => ?FLUSH_QLEN, - burst_limit_enable => ?BURST_LIMIT_ENABLE, - burst_limit_max_count => ?BURST_LIMIT_MAX_COUNT, - burst_limit_window_time => ?BURST_LIMIT_WINDOW_TIME, - overload_kill_enable => ?OVERLOAD_KILL_ENABLE, - overload_kill_qlen => ?OVERLOAD_KILL_QLEN, - overload_kill_mem_size => ?OVERLOAD_KILL_MEM_SIZE, - overload_kill_restart_after => ?OVERLOAD_KILL_RESTART_AFTER, - filesync_repeat_interval => ?FILESYNC_REPEAT_INTERVAL}. - -get_init_state() -> - #{file_ctrl_sync_int => ?CONTROLLER_SYNC_INTERVAL, - filesync_ok_qlen => ?FILESYNC_OK_QLEN}. - -%%%----------------------------------------------------------------- -%%% Add a standard handler to the logger. -%%% This starts a dedicated handler process which should always -%%% exist if the handler is registered with logger (and should not -%%% exist if the handler is not registered). -%%% -%%% Handler specific config should be provided with a sub map associated -%%% with a key named 'config', e.g: -%%% -%%% Config = #{config => #{sync_mode_qlen => 50} -%%% -%%% The standard handler process is linked to logger_sup, which is -%%% part of the kernel application's supervision tree. -start(Name, Config, HandlerState) -> - LoggerStdH = - #{id => Name, - start => {?MODULE, start_link, [Name,Config,HandlerState]}, - restart => temporary, - shutdown => 2000, - type => worker, - modules => [?MODULE]}, - case supervisor:start_child(logger_sup, LoggerStdH) of - {ok,Pid,Config1} -> - ok = logger_handler_watcher:register_handler(Name,Pid), - {ok,Config1}; - Error -> - Error - end. - -%%%----------------------------------------------------------------- -%%% Stop and remove the handler. -stop(Name) -> - case whereis(?name_to_reg_name(?MODULE,Name)) of - undefined -> - ok; - Pid -> - %% We don't want to do supervisor:terminate_child here - %% since we need to distinguish this explicit stop from a - %% system termination in order to avoid circular attempts - %% at removing the handler (implying deadlocks and - %% timeouts). - %% And we don't need to do supervisor:delete_child, since - %% the restart type is temporary, which means that the - %% child specification is automatically removed from the - %% supervisor when the process dies. - _ = gen_server:call(Pid, stop), - ok - end. - -%%%----------------------------------------------------------------- -%%% Logging and overload control. --define(update_file_ctrl_sync(C, Interval), - if C == 0 -> Interval; - true -> C-1 end). - -%% check for overload between every event (and set Mode to async, -%% sync or drop accordingly), but never flush the whole mailbox -%% before LogWindowSize events have been handled -do_log(Bin, CallOrCast, State = #{id:=Name, mode:=Mode0}) -> - T1 = ?timestamp(), - - %% check if the handler is getting overloaded, or if it's - %% recovering from overload (the check must be done for each - %% event to react quickly to large bursts of events and - %% to ensure that the handler can never end up in drop mode - %% with an empty mailbox, which would stop operation) - {Mode1,QLen,Mem,State1} = logger_h_common:check_load(State), - - if (Mode1 == drop) andalso (Mode0 =/= drop) -> - log_handler_info(Name, "Handler ~p switched to drop mode", - [Name], State); - (Mode0 == drop) andalso ((Mode1 == async) orelse (Mode1 == sync)) -> - log_handler_info(Name, "Handler ~p switched to ~w mode", - [Name,Mode1], State); - true -> - ok - end, - - %% kill the handler if it can't keep up with the load - logger_h_common:kill_if_choked(Name, QLen, Mem, ?MODULE, State), - - if Mode1 == flush -> - flush(Name, QLen, T1, State1); - true -> - write(Name, Mode1, T1, Bin, CallOrCast, State1) - end. - -%% this clause is called by do_log/3 after an overload check -%% has been performed, where QLen > FlushQLen -flush(Name, _QLen0, T1, State=#{last_log_ts := _T0, mode_tab := ModeTab}) -> - %% flush messages in the mailbox (a limited number in - %% order to not cause long delays) - NewFlushed = logger_h_common:flush_log_events(?FLUSH_MAX_N), - - %% write info in log about flushed messages - log_handler_info(Name, "Handler ~p flushed ~w log events", - [Name,NewFlushed], State), - - %% because of the receive loop when flushing messages, the - %% handler will be scheduled out often and the mailbox could - %% grow very large, so we'd better check the queue again here - {_,_QLen1} = process_info(self(), message_queue_len), - ?observe(Name,{max_qlen,_QLen1}), - - %% Add 1 for the current log event - ?observe(Name,{flushed,NewFlushed+1}), - - State1 = ?update_max_time(?diff_time(T1,_T0),State), - {dropped,?update_other(flushed,FLUSHED,NewFlushed, - State1#{mode => ?set_mode(ModeTab,async), - last_qlen => 0, - last_log_ts => T1})}. - -%% this clause is called to write to file -write(_Name, Mode, T1, Bin, _CallOrCast, - State = #{mode_tab := ModeTab, - file_ctrl_pid := FileCtrlPid, - file_ctrl_sync := FileCtrlSync, - last_qlen := LastQLen, - last_log_ts := T0, - file_ctrl_sync_int := FileCtrlSyncInt}) -> - %% check if we need to limit the number of writes - %% during a burst of log events - {DoWrite,BurstWinT,BurstMsgCount} = logger_h_common:limit_burst(State), - - %% only send a synhrounous event to the file controller process - %% every FileCtrlSyncInt time, to give the handler time between - %% file writes so it can keep up with incoming messages - {Result,LastQLen1} = - if DoWrite, FileCtrlSync == 0 -> - ?observe(_Name,{_CallOrCast,1}), - file_write_sync(FileCtrlPid, Bin, false), - {ok,element(2, process_info(self(), message_queue_len))}; - DoWrite -> - ?observe(_Name,{_CallOrCast,1}), - file_write_async(FileCtrlPid, Bin), - {ok,LastQLen}; - not DoWrite -> - ?observe(_Name,{flushed,1}), - {dropped,LastQLen} - end, - - %% Check if the time since the previous log event is long enough - - %% and the queue length small enough - to assume the mailbox has - %% been emptied, and if so, do filesync operation and reset mode to - %% async. Note that this is the best we can do to detect an idle - %% handler without setting a timer after each log call/cast. If the - %% time between two consecutive log events is fast and no new - %% event comes in after the last one, idle state won't be detected! - Time = ?diff_time(T1,T0), - {Mode1,BurstMsgCount1} = - if (LastQLen1 < ?FILESYNC_OK_QLEN) andalso - (Time > ?IDLE_DETECT_TIME_USEC) -> - %% do filesync if necessary - case maps:get(type, State) of - Std when is_atom(Std) -> - ok; - _File -> - file_ctrl_filesync_async(FileCtrlPid) - end, - {?change_mode(ModeTab, Mode, async),0}; - true -> - {Mode,BurstMsgCount} - end, - State1 = - ?update_calls_or_casts(_CallOrCast,1,State), - State2 = - ?update_max_time(Time, - State1#{mode => Mode1, - last_qlen := LastQLen1, - last_log_ts => T1, - last_op => write, - burst_win_ts => BurstWinT, - burst_msg_count => BurstMsgCount1, - file_ctrl_sync => - ?update_file_ctrl_sync(FileCtrlSync, - FileCtrlSyncInt)}), - {Result,State2}. - open_log_file(HandlerName, FileInfo) -> case file_ctrl_start(HandlerName, FileInfo) of OK = {ok,_FileCtrlPid} -> OK; @@ -669,19 +246,6 @@ close_log_file(Fd) -> _ = file:close(Fd). -log_handler_info(Name, Format, Args, #{file_ctrl_pid := FileCtrlPid}) -> - Config = - case logger:get_handler_config(Name) of - {ok,Conf} -> Conf; - _ -> #{formatter=>{?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}} - end, - Meta = #{time=>erlang:system_time(microsecond)}, - Bin = logger_h_common:log_to_binary(#{level => notice, - msg => {Format,Args}, - meta => Meta}, Config), - _ = file_write_async(FileCtrlPid, Bin), - ok. - %%%----------------------------------------------------------------- %%% File control process @@ -708,13 +272,8 @@ file_write_async(Pid, Bin) -> Pid ! {log,Bin}, ok. -file_write_sync(Pid, Bin, FileSync) -> - case file_ctrl_call(Pid, {log,self(),Bin,FileSync}) of - {error,Reason} -> - {error,{write_failed,Bin,Reason}}; - Result -> - Result - end. +file_write_sync(Pid, Bin) -> + file_ctrl_call(Pid, {log,self(),Bin}). file_ctrl_filesync_async(Pid) -> Pid ! filesync, @@ -766,33 +325,22 @@ file_ctrl_loop(Fd, Type, DevName, Synced, Result, PrevSyncResult, HandlerName); %% synchronous event - {{log,From,Bin,FileSync},MRef} -> - if Type == file -> - %% check that file hasn't been deleted - CheckFile = - fun() -> {ok,_} = file:read_file_info(DevName) end, - spawn_link(CheckFile), - WResult = write_to_dev(Fd, Bin, DevName, - PrevWriteResult, HandlerName), - {Synced1,SResult} = - if not FileSync -> - {false,PrevSyncResult}; - true -> - case sync_dev(Fd, DevName, - PrevSyncResult, HandlerName) of - ok -> {true,ok}; - Error -> {false,Error} - end - end, - From ! {MRef,ok}, - file_ctrl_loop(Fd, Type, DevName, Synced1, - WResult, SResult, HandlerName); - true -> - _ = io:put_chars(Fd, Bin), - From ! {MRef,ok}, - file_ctrl_loop(Fd, Type, DevName, false, - ok, PrevSyncResult, HandlerName) - end; + {{log,From,Bin},MRef} -> + WResult = + if Type == file -> + %% check that file hasn't been deleted + CheckFile = + fun() -> {ok,_} = file:read_file_info(DevName) end, + spawn_link(CheckFile), + write_to_dev(Fd, Bin, DevName, + PrevWriteResult, HandlerName); + true -> + _ = io:put_chars(Fd, Bin), + ok + end, + From ! {MRef,ok}, + file_ctrl_loop(Fd, Type, DevName, false, + WResult, PrevSyncResult, HandlerName); filesync when not Synced -> Result = sync_dev(Fd, DevName, PrevSyncResult, HandlerName), diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 905c2c52c5..7bac4c2cb2 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -332,16 +332,18 @@ errors(Config) -> %%! Check how bad log_opts are handled! {error,{illegal_config_change, - #{config:=#{type:=wrap}}, - #{config:=#{type:=halt}}}} = + logger_disk_log_h, + #{type:=wrap}, + #{type:=halt}}} = logger:update_handler_config(Name1, config, #{type=>halt, file=>LogFile1}), {error,{illegal_config_change, - #{config:=#{file:=LogFile1}}, - #{config:=#{file:="newfilename"}}}} = + logger_disk_log_h, + #{file:=LogFile1}, + #{file:="newfilename"}}} = logger:update_handler_config(Name1, config, #{file=>"newfilename"}), @@ -411,7 +413,7 @@ formatter_fail(cleanup,_Config) -> ok. config_fail(_Config) -> - {error,{handler_not_added,{invalid_config,logger_disk_log_h,{bad,bad}}}} = + {error,{handler_not_added,{invalid_config,logger_disk_log_h,#{bad:=bad}}}} = logger:add_handler(?MODULE,logger_disk_log_h, #{config => #{bad => bad}, filter_default=>log, @@ -433,12 +435,9 @@ config_fail(_Config) -> #{filter_default=>log, formatter=>{?MODULE,self()}}), %% can't change the disk log options for a log already in use - {error,{illegal_config_change,_,_}} = + {error,{illegal_config_change,logger_disk_log_h,_,_}} = logger:update_handler_config(?MODULE,config, #{max_no_files=>2}), - %% can't change name of an existing handler - {error,{illegal_config_change,_,_}} = - logger:update_handler_config(?MODULE,id,bad), %% incorrect values of OP params {ok,#{config := HConfig}} = logger:get_handler_config(?MODULE), {error,{invalid_levels,_}} = @@ -446,7 +445,7 @@ config_fail(_Config) -> HConfig#{sync_mode_qlen=>100, flush_qlen=>99}), %% invalid name of config parameter - {error,{invalid_config,logger_disk_log_h,{filesync_rep_int,2000}}} = + {error,{invalid_config,logger_disk_log_h,#{filesync_rep_int:=2000}}} = logger:update_handler_config(?MODULE, config, HConfig#{filesync_rep_int => 2000}), ok. @@ -487,10 +486,11 @@ reconfig(Config) -> overload_kill_mem_size := ?OVERLOAD_KILL_MEM_SIZE, overload_kill_restart_after := ?OVERLOAD_KILL_RESTART_AFTER, filesync_repeat_interval := ?FILESYNC_REPEAT_INTERVAL, - log_opts := #{type := ?DISK_LOG_TYPE, - max_no_files := ?DISK_LOG_MAX_NO_FILES, - max_no_bytes := ?DISK_LOG_MAX_NO_BYTES, - file := DiskLogFile}} = + handler_state := + #{log_opts := #{type := ?DISK_LOG_TYPE, + max_no_files := ?DISK_LOG_MAX_NO_FILES, + max_no_bytes := ?DISK_LOG_MAX_NO_BYTES, + file := DiskLogFile}}} = logger_disk_log_h:info(?MODULE), {ok,#{config := #{sync_mode_qlen := ?SYNC_MODE_QLEN, @@ -572,10 +572,11 @@ reconfig(Config) -> max_no_files => 1, max_no_bytes => 1024, file => File}}), - #{log_opts := #{type := halt, - max_no_files := 1, - max_no_bytes := 1024, - file := File}} = + #{handler_state := + #{log_opts := #{type := halt, + max_no_files := 1, + max_no_bytes := 1024, + file := File}}} = logger_disk_log_h:info(?MODULE), {ok,#{config := #{type := halt, @@ -596,13 +597,13 @@ reconfig(Config) -> %% You are not allowed to actively set the write once fields %% (type, max_no_files, max_no_bytes, file) in runtime. - {error, {illegal_config_change,_,_}} = + {error, {illegal_config_change,_,_,_}} = logger:set_handler_config(?MODULE,config,#{type=>wrap}), - {error, {illegal_config_change,_,_}} = + {error, {illegal_config_change,_,_,_}} = logger:set_handler_config(?MODULE,config,#{max_no_files=>2}), - {error, {illegal_config_change,_,_}} = + {error, {illegal_config_change,_,_,_}} = logger:set_handler_config(?MODULE,config,#{max_no_bytes=>2048}), - {error, {illegal_config_change,_,_}} = + {error, {illegal_config_change,_,_,_}} = logger:set_handler_config(?MODULE,config,#{file=>"otherfile.log"}), {ok,C7} = logger:get_handler_config(?MODULE), ct:log("C7: ~p",[C7]), @@ -639,7 +640,7 @@ sync(Config) -> %% wait for automatic disk_log_sync check_tracer(?FILESYNC_REPEAT_INTERVAL*2), - %% check that if there's no repeated disk_log_sync active, + %% check that if there's no repeated filesync active, %% a disk_log_sync is still performed when handler goes idle {ok,#{config := HConfig}} = logger:get_handler_config(?MODULE), HConfig1 = HConfig#{filesync_repeat_interval => no_repeat}, @@ -667,12 +668,12 @@ sync(Config) -> try_read_file(Log, {ok,<<"first\nsecond\nthird\n">>}, 1000), - %% switch repeated disk_log_sync on and verify that the looping works + %% switch repeated filesync on and verify that the looping works SyncInt = 1000, WaitT = 4500, - OneSync = {logger_disk_log_h,handle_cast,repeated_disk_log_sync}, - %% receive 1 initial repeated_disk_log_sync, then 1 per sec - start_tracer([{logger_disk_log_h,handle_cast,2}], + OneSync = {logger_h_common,handle_cast,repeated_filesync}, + %% receive 1 initial repeated_filesync, then 1 per sec + start_tracer([{logger_h_common,handle_cast,2}], [OneSync || _ <- lists:seq(1, 1 + trunc(WaitT/SyncInt))]), HConfig2 = HConfig#{filesync_repeat_interval => SyncInt}, @@ -851,7 +852,8 @@ write_failure(Config) -> rpc:call(Node, ?MODULE, set_internal_log, [?MODULE,internal_log]), rpc:call(Node, ?MODULE, set_result, [disk_log_blog,ok]), HState = rpc:call(Node, logger_disk_log_h, info, [?STANDARD_HANDLER]), - ct:pal("LogOpts = ~p", [LogOpts = maps:get(log_opts, HState)]), + ct:pal("LogOpts = ~p", [LogOpts = maps:get(log_opts, + maps:get(handler_state,HState))]), ok = log_on_remote_node(Node, "Logged1"), rpc:call(Node, logger_disk_log_h, filesync, [?STANDARD_HANDLER]), @@ -901,7 +903,7 @@ sync_failure(Config) -> rpc:call(Node, ?MODULE, set_internal_log, [?MODULE,internal_log]), rpc:call(Node, ?MODULE, set_result, [disk_log_sync,ok]), HState = rpc:call(Node, logger_disk_log_h, info, [?STANDARD_HANDLER]), - LogOpts = maps:get(log_opts, HState), + LogOpts = maps:get(log_opts, maps:get(handler_state,HState)), SyncInt = 500, ok = rpc:call(Node, logger, update_handler_config, @@ -1606,7 +1608,7 @@ tpl([{M,F,A}|Trace]) -> tpl([]) -> ok. -tracer({trace,_,call,{logger_disk_log_h,handle_cast,[Op|_]},Caller}, +tracer({trace,_,call,{logger_h_common,handle_cast,[Op|_]},Caller}, {Pid,[{Mod,Func,Op}|Expected]}) -> maybe_tracer_done(Pid,Expected,{Mod,Func,Op},Caller); tracer({trace,_,call,{Mod=disk_log,Func=blog,[_,Data]},Caller}, {Pid,[{Mod,Func,Data}|Expected]}) -> diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index b6a09f4980..affa04d410 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -140,7 +140,7 @@ all() -> ]. add_remove_instance_tty(_Config) -> - {error,{handler_not_added,{invalid_config,logger_std_h,{type,tty}}}} = + {error,{handler_not_added,{invalid_config,logger_std_h,#{type:=tty}}}} = logger:add_handler(?MODULE,logger_std_h, #{config => #{type => tty}, filter_default=>log, @@ -234,7 +234,7 @@ errors(Config) -> {error, {handler_not_added, - {invalid_config,logger_std_h,{type,faulty_type}}}} = + {invalid_config,logger_std_h,#{type:=faulty_type}}}} = logger:add_handler(?MODULE,logger_std_h, #{config => #{type => faulty_type}}), @@ -308,13 +308,13 @@ formatter_fail(cleanup,_Config) -> logger:remove_handler(?MODULE). config_fail(_Config) -> - {error,{handler_not_added,{invalid_config,logger_std_h,{bad,bad}}}} = + {error,{handler_not_added,{invalid_config,logger_std_h,#{bad:=bad}}}} = logger:add_handler(?MODULE,logger_std_h, #{config => #{bad => bad}, filter_default=>log, formatter=>{?MODULE,self()}}), {error,{handler_not_added,{invalid_config,logger_std_h, - {restart_type,bad}}}} = + #{restart_type:=bad}}}} = logger:add_handler(?MODULE,logger_std_h, #{config => #{restart_type => bad}, filter_default=>log, @@ -334,7 +334,7 @@ config_fail(_Config) -> ok = logger:add_handler(?MODULE,logger_std_h, #{filter_default=>log, formatter=>{?MODULE,self()}}), - {error,{illegal_config_change,#{config:=#{type:=_}},#{config:=#{type:=_}}}} = + {error,{illegal_config_change,logger_std_h,#{type:=_},#{type:=_}}} = logger:set_handler_config(?MODULE,config, #{type=>{file,"file"}}), @@ -342,7 +342,7 @@ config_fail(_Config) -> logger:set_handler_config(?MODULE,config, #{sync_mode_qlen=>100, flush_qlen=>99}), - {error,{invalid_config,logger_std_h,{filesync_rep_int,2000}}} = + {error,{invalid_config,logger_std_h,#{filesync_rep_int:=2000}}} = logger:set_handler_config(?MODULE, config, #{filesync_rep_int => 2000}), @@ -468,8 +468,8 @@ reconfig(Config) -> filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,self()}}), #{id := ?MODULE, - type := standard_io, - file_ctrl_pid := FileCtrlPid, + handler_state := #{type := standard_io, + file_ctrl_pid := FileCtrlPid}, sync_mode_qlen := ?SYNC_MODE_QLEN, drop_mode_qlen := ?DROP_MODE_QLEN, flush_qlen := ?FLUSH_QLEN, @@ -480,7 +480,7 @@ reconfig(Config) -> overload_kill_qlen := ?OVERLOAD_KILL_QLEN, overload_kill_mem_size := ?OVERLOAD_KILL_MEM_SIZE, overload_kill_restart_after := ?OVERLOAD_KILL_RESTART_AFTER, - filesync_repeat_interval := ?FILESYNC_REPEAT_INTERVAL} = DefaultInfo = + filesync_repeat_interval := no_repeat} = DefaultInfo = logger_std_h:info(?MODULE), {ok, @@ -496,7 +496,7 @@ reconfig(Config) -> overload_kill_qlen := ?OVERLOAD_KILL_QLEN, overload_kill_mem_size := ?OVERLOAD_KILL_MEM_SIZE, overload_kill_restart_after := ?OVERLOAD_KILL_RESTART_AFTER, - filesync_repeat_interval := ?FILESYNC_REPEAT_INTERVAL} = + filesync_repeat_interval := no_repeat} = DefaultHConf}} = logger:get_handler_config(?MODULE), @@ -511,10 +511,10 @@ reconfig(Config) -> overload_kill_qlen => 100000, overload_kill_mem_size => 10000000, overload_kill_restart_after => infinity, - filesync_repeat_interval => no_repeat}), + filesync_repeat_interval => 5000}), #{id := ?MODULE, - type := standard_io, - file_ctrl_pid := FileCtrlPid, + handler_state := #{type := standard_io, + file_ctrl_pid := FileCtrlPid}, sync_mode_qlen := 1, drop_mode_qlen := 2, flush_qlen := 3, @@ -584,7 +584,7 @@ reconfig(Config) -> %% You are not allowed to actively set 'type' in runtime, since %% this is a write once field. - {error, {illegal_config_change,_,_}} = + {error, {illegal_config_change,logger_std_h,_,_}} = logger:set_handler_config(?MODULE,config,#{type=>standard_io}), {ok,#{config:=C6}} = logger:get_handler_config(?MODULE), ct:log("C6: ~p",[C6]), @@ -620,7 +620,7 @@ file_opts(Config) -> filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,self()}}), - #{type := OkType} = logger_std_h:info(?MODULE), + #{handler_state := #{type := OkType}} = logger_std_h:info(?MODULE), logger:notice(M1=?msg,?domain), ?check(M1), B1 = ?bin(M1), @@ -697,9 +697,9 @@ sync(Config) -> %% switch repeated filesync on and verify that the looping works SyncInt = 1000, WaitT = 4500, - OneSync = {logger_std_h,handle_cast,repeated_filesync}, + OneSync = {logger_h_common,handle_cast,repeated_filesync}, %% receive 1 initial repeated_filesync, then 1 per sec - start_tracer([{logger_std_h,handle_cast,2}], + start_tracer([{logger_h_common,handle_cast,2}], [OneSync || _ <- lists:seq(1, 1 + trunc(WaitT/SyncInt))]), ok = logger:update_handler_config(?MODULE, config, @@ -1533,7 +1533,7 @@ start_op_trace() -> {ok,_} = dbg:p(self(), [c]), MS1 = dbg:fun2ms(fun([_]) -> return_trace() end), - {ok,_} = dbg:tp(logger_h_common, check_load, 1, MS1), + {ok,_} = dbg:tpl(logger_h_common, check_load, 1, MS1), {ok,_} = dbg:tpl(logger_h_common, flush_log_requests, 2, []), @@ -1607,7 +1607,9 @@ analyse(Msgs) -> start_tracer(Trace,Expected) -> Pid = self(), - FileCtrlPid = maps:get(file_ctrl_pid, logger_std_h:info(?MODULE)), + FileCtrlPid = maps:get(file_ctrl_pid, + maps:get(handler_state, + logger_std_h:info(?MODULE))), dbg:tracer(process,{fun tracer/2,{Pid,Expected}}), dbg:p(whereis(h_proc_name()),[c]), dbg:p(FileCtrlPid,[c]), @@ -1628,7 +1630,7 @@ tpl([{M,F,A}|Trace]) -> tpl([]) -> ok. -tracer({trace,_,call,{logger_std_h,handle_cast,[Op|_]}}, +tracer({trace,_,call,{logger_h_common,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,_,_,_]}}, -- cgit v1.2.3 From 35d0c2085cd07376157ac4bbe95c79db005e7a5c Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 9 Oct 2018 13:18:19 +0200 Subject: [logger] Further refactor built-in handlers --- lib/kernel/src/logger_disk_log_h.erl | 102 +++----- lib/kernel/src/logger_h_common.erl | 389 ++++++++++++---------------- lib/kernel/src/logger_std_h.erl | 160 ++++++------ lib/kernel/test/logger_disk_log_h_SUITE.erl | 29 ++- lib/kernel/test/logger_std_h_SUITE.erl | 24 +- 5 files changed, 306 insertions(+), 398 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index 8c09dd071f..1e48e5b0a8 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -27,10 +27,8 @@ -export([info/1, filesync/1, reset/1]). %% logger_h_common callbacks --export([init/2, check_config/4, reset_state/1, - async_filesync/2, sync_filesync/2, - async_write/3, sync_write/3, - handle_info/2, terminate/3]). +-export([init/2, check_config/4, reset_state/2, + filesync/3, write/4, handle_info/3, terminate/3]). %% logger callbacks -export([log/2, adding_handler/1, removing_handler/1, changing_config/3, @@ -185,45 +183,27 @@ merge_default_logopts(Name, HConfig) -> type => Type}, maps:merge(Defaults, HConfig). -async_filesync(Name,State) -> - {_,State1} = disk_log_sync(Name,State), - State1. - -sync_filesync(Name,State) -> +filesync(Name,_Mode,State) -> disk_log_sync(Name,State). -async_write(Name, Bin, State) -> - {_,State1} = disk_log_write(Name, Bin, State), - State1. - -sync_write(Name, Bin, State) -> +write(Name, _Mode, Bin, State) -> disk_log_write(Name, Bin, State). -reset_state(State) -> +reset_state(_Name, State) -> State#{prev_log_result => ok, prev_sync_result => ok, prev_disk_log_info => undefined}. %% The disk log owner must handle status messages from disk_log. -handle_info({disk_log, _Node, _Log, {wrap,_NoLostItems}}, State) -> - State; -handle_info({disk_log, _Node, Log, Info = {truncated,_NoLostItems}}, - State = #{id := Name, prev_disk_log_info := PrevInfo}) -> - error_notify_new(Info, PrevInfo, {disk_log,Name,Log,Info}), - State#{prev_disk_log_info => Info}; -handle_info({disk_log, _Node, Log, Info = {blocked_log,_Items}}, - State = #{id := Name, prev_disk_log_info := PrevInfo}) -> - error_notify_new(Info, PrevInfo, {disk_log,Name,Log,Info}), - State#{prev_disk_log_info => Info}; -handle_info({disk_log, _Node, Log, full}, - State = #{id := Name, prev_disk_log_info := PrevInfo}) -> - error_notify_new(full, PrevInfo, {disk_log,Name,Log,full}), - State#{prev_disk_log_info => full}; -handle_info({disk_log, _Node, Log, Info = {error_status,_Status}}, - State = #{id := Name, prev_disk_log_info := PrevInfo}) -> - error_notify_new(Info, PrevInfo, {disk_log,Name,Log,Info}), - State#{prev_disk_log_info => Info}; -handle_info(_, State) -> +handle_info(Name, {disk_log, _Node, Log, Info={truncated,_NoLostItems}}, State) -> + maybe_notify_status(Name, Log, Info, prev_disk_log_info, State); +handle_info(Name, {disk_log, _Node, Log, Info = {blocked_log,_Items}}, State) -> + maybe_notify_status(Name, Log, Info, prev_disk_log_info, State); +handle_info(Name, {disk_log, _Node, Log, Info = full}, State) -> + maybe_notify_status(Name, Log, Info, prev_disk_log_info, State); +handle_info(Name, {disk_log, _Node, Log, Info = {error_status,_Status}}, State) -> + maybe_notify_status(Name, Log, Info, prev_disk_log_info, State); +handle_info(_, _, State) -> State. terminate(Name, _Reason, _State) -> @@ -265,42 +245,28 @@ close_disk_log(Name, _) -> ok. disk_log_write(Name, Bin, State) -> - case ?disk_log_blog(Name, Bin) of - ok -> - {ok,State#{prev_log_result => ok, last_op => write}}; - LogError -> - _ = case maps:get(prev_log_result, State) of - LogError -> - %% don't report same error twice - ok; - _ -> - LogOpts = maps:get(log_opts, State), - logger_h_common:error_notify({Name,log, - LogOpts, - LogError}) - end, - {LogError,State#{prev_log_result => LogError}} - end. + Result = ?disk_log_blog(Name, Bin), + maybe_notify_error(Name, log, Result, prev_log_result, State). disk_log_sync(Name, State) -> - case ?disk_log_sync(Name) of - ok -> - {ok,State#{prev_sync_result => ok, last_op => sync}}; - SyncError -> - _ = case maps:get(prev_sync_result, State) of - SyncError -> - %% don't report same error twice - ok; - _ -> - LogOpts = maps:get(log_opts, State), - logger_h_common:error_notify({Name,filesync, - LogOpts, - SyncError}) - end, - {SyncError,State#{prev_sync_result => SyncError}} - end. + Result = ?disk_log_sync(Name), + maybe_notify_error(Name, filesync, Result, prev_sync_result, State). -error_notify_new(Info,Info, _Term) -> +%%%----------------------------------------------------------------- +%%% Print error messages, but don't repeat the same message +maybe_notify_error(Name, Op, Result, Key, #{log_opts:=LogOpts}=State) -> + {Result,error_notify_new({Name, Op, LogOpts, Result}, Result, Key, State)}. + +maybe_notify_status(Name, Log, Info, Key, State) -> + error_notify_new({disk_log, Name, Log, Info}, Info, Key, State). + +error_notify_new(Term, What, Key, State) -> + error_notify_new(What, maps:get(Key,State), Term), + State#{Key => What}. + +error_notify_new(ok,_Prev,_Term) -> + ok; +error_notify_new(Same,Same,_Term) -> ok; -error_notify_new(_Info0,_Info1, Term) -> +error_notify_new(_New,_Prev,Term) -> logger_h_common:error_notify(Term). diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl index d01851b2b1..d290f51e34 100644 --- a/lib/kernel/src/logger_h_common.erl +++ b/lib/kernel/src/logger_h_common.erl @@ -24,7 +24,7 @@ -include("logger_internal.hrl"). %% API --export([start_link/3, info/2, filesync/2, reset/2]). +-export([start_link/1, info/2, filesync/2, reset/2]). %% gen_server and proc_lib callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, @@ -52,65 +52,37 @@ -define(READ_ONLY_KEYS,[handler_pid,mode_tab]). %%%----------------------------------------------------------------- -%%% -filesync(Module, Name) when is_atom(Name) -> - try - gen_server:call(?name_to_reg_name(Module,Name), - filesync, ?DEFAULT_CALL_TIMEOUT) - catch - _:{timeout,_} -> {error,handler_busy} - end; -filesync(_, Name) -> - {error,{badarg,{filesync,[Name]}}}. +%%% API -info(Module, Name) when is_atom(Name) -> - try - gen_server:call(?name_to_reg_name(Module,Name), - info, ?DEFAULT_CALL_TIMEOUT) - catch - _:{timeout,_} -> {error,handler_busy} - end; -info(_, Name) -> - {error,{badarg,{info,[Name]}}}. +%% This function is called by the logger_sup supervisor +start_link(Args) -> + proc_lib:start_link(?MODULE,init,[Args]). -reset(Module, Name) when is_atom(Name) -> - try - gen_server:call(?name_to_reg_name(Module,Name), - reset, ?DEFAULT_CALL_TIMEOUT) - catch - _:{timeout,_} -> {error,handler_busy} - end; -reset(_, Name) -> - {error,{badarg,{reset,[Name]}}}. +filesync(Module, Name) -> + call(Module, Name, filesync). +info(Module, Name) -> + call(Module, Name, info). +reset(Module, Name) -> + call(Module, Name, reset). %%%----------------------------------------------------------------- %%% Handler being added adding_handler(#{id:=Name,module:=Module}=Config) -> - HConfig = maps:get(config, Config, #{}), - HandlerConfig0 = maps:without(?CONFIG_KEYS,HConfig), + HConfig0 = maps:get(config, Config, #{}), + HandlerConfig0 = maps:without(?CONFIG_KEYS,HConfig0), case Module:check_config(Name,set,undefined,HandlerConfig0) of {ok,HandlerConfig} -> ModifiedCommon = maps:with(?CONFIG_KEYS,HandlerConfig), - CommonConfig0 = maps:with(?CONFIG_KEYS,HConfig), + CommonConfig0 = maps:with(?CONFIG_KEYS,HConfig0), CommonConfig = maps:merge( maps:merge(get_default_config(), CommonConfig0), ModifiedCommon), case check_config(CommonConfig) of ok -> - State = maps:merge(get_init_state(), CommonConfig), - HConfig1 = maps:merge(CommonConfig,HandlerConfig), - Config1 = Config#{config=>HConfig1}, - case overload_levels_ok(State) of - true -> - start(Name, Config1, State); - false -> - #{sync_mode_qlen := SMQL, - drop_mode_qlen := DMQL, - flush_qlen := FQL} = State, - {error,{invalid_levels,{SMQL,DMQL,FQL}}} - end; + HConfig = maps:merge(CommonConfig,HandlerConfig), + start(Config#{config => HConfig}); {error,Faulty} -> {error,{invalid_config,Module,Faulty}} end; @@ -141,7 +113,7 @@ removing_handler(#{id:=Name, module:=Module}) -> %%%----------------------------------------------------------------- %%% Updating handler config changing_config(SetOrUpdate, - OldConfig=#{id:=Name,config:=OldHConfig,module:=Module}, + #{id:=Name,config:=OldHConfig,module:=Module}, NewConfig0) -> NewHConfig0 = maps:get(config, NewConfig0, #{}), OldHandlerConfig = maps:without(?CONFIG_KEYS++?READ_ONLY_KEYS,OldHConfig), @@ -169,12 +141,9 @@ changing_config(SetOrUpdate, ReadOnly), NewConfig = NewConfig0#{config=>NewHConfig}, HPid = maps:get(handler_pid,OldHConfig), - try gen_server:call(HPid, {change_config,OldConfig,NewConfig}, - ?DEFAULT_CALL_TIMEOUT) of + case call(HPid, {change_config,NewConfig}) of ok -> {ok,NewConfig}; Error -> Error - catch - _:{timeout,_} -> {error,handler_busy} end; {error,Faulty} -> {error,{invalid_config,Module,Faulty}} @@ -204,61 +173,35 @@ filter_config(#{config:=HConfig}=Config) -> Config#{config=>maps:without(?READ_ONLY_KEYS,HConfig)}. %%%----------------------------------------------------------------- -%%% Add a standard handler to the logger. -%%% This starts a dedicated handler process which should always -%%% exist if the handler is registered with logger (and should not -%%% exist if the handler is not registered). -%%% -%%% Handler specific config should be provided with a sub map associated -%%% with a key named 'config', e.g: +%%% Start the handler process %%% -%%% Config = #{config => #{sync_mode_qlen => 50} +%%% The process must always exist if the handler is registered with +%%% logger (and must not exist if the handler is not registered). %%% -%%% The standard handler process is linked to logger_sup, which is -%%% part of the kernel application's supervision tree. -start(Name, Config, HandlerState) -> +%%% The handler process is linked to logger_sup, which is part of the +%%% kernel application's supervision tree. +start(#{id := Name} = Config0) -> ChildSpec = #{id => Name, - start => {?MODULE, start_link, [Name,Config,HandlerState]}, + start => {?MODULE, start_link, [Config0]}, restart => temporary, shutdown => 2000, type => worker, modules => [?MODULE]}, case supervisor:start_child(logger_sup, ChildSpec) of - {ok,Pid,Config1} -> + {ok,Pid,Config} -> ok = logger_handler_watcher:register_handler(Name,Pid), - {ok,Config1}; + {ok,Config}; Error -> Error end. -%%%----------------------------------------------------------------- -%%% Start a standard handler process and link to caller. -%%% This function is called by the kernel supervisor when this -%%% handler process gets added --spec start_link(Name, Config, HandlerState) -> {ok,Pid} | {error,Reason} when - Name :: atom(), - Config :: logger:handler_config(), - HandlerState :: map(), - Pid :: pid(), - Reason :: term(). - -start_link(Name, Config, HandlerState) -> - proc_lib:start_link(?MODULE,init,[[Name,Config,HandlerState]]). - -%%%----------------------------------------------------------------- -%%% -get_init_state() -> - #{ctrl_sync_int => ?CONTROLLER_SYNC_INTERVAL, - filesync_ok_qlen => ?FILESYNC_OK_QLEN}. - %%%=================================================================== %%% gen_server callbacks %%%=================================================================== -init([Name, Config = #{config := HConfig, module := Module}, - State = #{filesync_repeat_interval := FSyncInt, - ctrl_sync_int := CtrlSyncInt}]) -> +init(#{id := Name, module := Module, + formatter := Formatter, config := HConfig0} = Config0) -> RegName = ?name_to_reg_name(Module,Name), register(RegName, self()), process_flag(trap_exit, true), @@ -267,43 +210,36 @@ init([Name, Config = #{config := HConfig, module := Module}, ?init_test_hooks(), ?start_observation(Name), - case Module:init(Name, HConfig) of + case Module:init(Name, HConfig0) of {ok,HState} -> try ets:new(Name, [public]) of ModeTab -> ?set_mode(ModeTab, async), T0 = ?timestamp(), - State1 = - ?merge_with_stats(State#{id => Name, - module => Module, - mode_tab => ModeTab, - mode => async, - ctrl_sync_count => CtrlSyncInt, - last_qlen => 0, - last_log_ts => T0, - last_op => sync, - burst_win_ts => T0, - burst_msg_count => 0, - handler_state => HState}), - Config1 = - Config#{config => HConfig#{handler_pid => self(), - mode_tab => ModeTab}}, - proc_lib:init_ack({ok,self(),Config1}), - if is_integer(FSyncInt) -> - gen_server:cast(self(), repeated_filesync); - true -> - ok - end, - case unset_restart_flag(Name, Module) of - true -> - %% inform about restart - gen_server:cast(self(), {log_handler_info, - "Handler ~p restarted", - [Name]}); - false -> - %% initial start - ok - end, + HConfig = HConfig0#{handler_pid => self(), + mode_tab => ModeTab}, + Config = Config0#{config => HConfig}, + proc_lib:init_ack({ok,self(),Config}), + %% Storing common config in state to avoid copying + %% (sending) the config data for each log message + CommonConfig = maps:with(?CONFIG_KEYS,HConfig), + State = + ?merge_with_stats( + CommonConfig#{id => Name, + module => Module, + mode_tab => ModeTab, + mode => async, + ctrl_sync_count => + ?CONTROLLER_SYNC_INTERVAL, + last_qlen => 0, + last_log_ts => T0, + last_op => sync, + burst_win_ts => T0, + burst_msg_count => 0, + formatter => Formatter, + handler_state => HState}), + State1 = set_repeated_filesync(State), + unset_restart_flag(State1), gen_server:enter_loop(?MODULE, [], State1) catch _:Error -> @@ -326,47 +262,33 @@ handle_call({log, Bin}, _From, State) -> handle_call(filesync, _From, State = #{id := Name, module := Module, handler_state := HandlerState}) -> - {Result,HandlerState1} = Module:sync_filesync(Name,HandlerState), + {Result,HandlerState1} = Module:filesync(Name,sync,HandlerState), {reply, Result, State#{handler_state=>HandlerState1, last_op=>sync}}; -handle_call({change_config,_OldConfig,NewConfig}, _From, +handle_call({change_config, #{formatter:=Formatter, config:=NewHConfig}}, _From, State = #{filesync_repeat_interval := FSyncInt0}) -> - HConfig = maps:get(config, NewConfig, #{}), - State1 = maps:merge(State, HConfig), - case overload_levels_ok(State1) of - true -> - _ = - case maps:get(filesync_repeat_interval, HConfig, undefined) of - undefined -> - ok; - no_repeat -> - _ = logger_h_common:cancel_timer(maps:get(rep_sync_tref, - State, - undefined)); - FSyncInt0 -> - ok; - _FSyncInt1 -> - _ = logger_h_common:cancel_timer(maps:get(rep_sync_tref, - State, - undefined)), - gen_server:cast(self(), repeated_filesync) - end, - {reply, ok, State1}; - false -> - #{sync_mode_qlen := SMQL, - drop_mode_qlen := DMQL, - flush_qlen := FQL} = State1, - {reply, {error,{invalid_levels,{SMQL,DMQL,FQL}}}, State} - end; + %% In the future, if handler_state must be updated due to config + %% change, then we need to add a callback to Module here. + CommonConfig = maps:with(?CONFIG_KEYS,NewHConfig), + State1 = maps:merge(State, CommonConfig), + State2 = + case maps:get(filesync_repeat_interval, NewHConfig) of + FSyncInt0 -> + State1; + _FSyncInt1 -> + set_repeated_filesync(cancel_repeated_filesync(State1)) + end, + {reply, ok, State2#{formatter:=Formatter}}; handle_call(info, _From, State) -> {reply, State, State}; -handle_call(reset, _From, #{module:=Module,handler_state:=HandlerState}=State) -> +handle_call(reset, _From, + #{id:=Name,module:=Module,handler_state:=HandlerState}=State) -> State1 = ?merge_with_stats(State), {reply, ok, State1#{last_qlen => 0, last_log_ts => ?timestamp(), - handler_state => Module:reset_state(HandlerState)}}; + handler_state => Module:reset_state(Name,HandlerState)}}; handle_call(stop, _From, State) -> {stop, {shutdown,stopped}, ok, State}. @@ -376,41 +298,37 @@ handle_cast({log, Bin}, State) -> {_,State1} = do_log(Bin, cast, State), {noreply, State1}; -handle_cast({log_handler_info, Format, Args}, State = #{id:=Name}) -> - log_handler_info(Name, Format, Args, State), - {noreply, State}; - %% If FILESYNC_REPEAT_INTERVAL is set to a millisec value, this %% clause gets called repeatedly by the handler. In order to %% guarantee that a filesync *always* happens after the last log %% event, the repeat operation must be active! handle_cast(repeated_filesync,State = #{filesync_repeat_interval := no_repeat}) -> + %% This clause handles a race condition which may occur when + %% config changes filesync_repeat_interval from an integer value + %% to no_repeat. {noreply,State}; handle_cast(repeated_filesync, State = #{id := Name, module := Module, handler_state := HandlerState, - filesync_repeat_interval := FSyncInt, last_op := LastOp}) -> - HandlerState1 = + State1 = if LastOp == sync -> - HandlerState; + State; true -> - Module:async_filesync(Name,HandlerState) + {_,HS} = Module:filesync(Name, async, HandlerState), + State#{handler_state => HS, last_op => sync} end, - {ok,TRef} = timer:apply_after(FSyncInt, gen_server,cast, - [self(),repeated_filesync]), - {noreply,State#{handler_state=>HandlerState1, - rep_sync_tref => TRef, - last_op => sync}}. + {noreply,set_repeated_filesync(State1)}. -handle_info(Info, #{module := Module, handler_state := HandlerState} = State) -> - {noreply,State#{handler_state => Module:handle_info(Info,HandlerState)}}. +handle_info(Info, #{id := Name, module := Module, + handler_state := HandlerState} = State) -> + {noreply,State#{handler_state => Module:handle_info(Name,Info,HandlerState)}}. terminate(Reason, State = #{id := Name, module := Module, handler_state := HandlerState}) -> - _ = cancel_timer(maps:get(rep_sync_tref, State, undefined)), + _ = cancel_repeated_filesync(State), _ = Module:terminate(Name, Reason, HandlerState), ok = stop_or_restart(Name, Reason, State), unregister(?name_to_reg_name(Module, Name)), @@ -420,10 +338,24 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}. +%%%----------------------------------------------------------------- +%%% Internal functions +call(Module, Name, Op) when is_atom(Name) -> + call(?name_to_reg_name(Module,Name), Op); +call(_, Name, Op) -> + {error,{badarg,{Op,[Name]}}}. + +call(Server, Msg) -> + try + gen_server:call(Server, Msg, ?DEFAULT_CALL_TIMEOUT) + catch + _:{timeout,_} -> {error,handler_busy} + end. + %% check for overload between every event (and set Mode to async, %% sync or drop accordingly), but never flush the whole mailbox %% before LogWindowSize events have been handled -do_log(Bin, CallOrCast, State = #{id:=Name, module:=Module, mode:=Mode0}) -> +do_log(Bin, CallOrCast, State = #{id:=Name, mode:=Mode0}) -> T1 = ?timestamp(), %% check if the handler is getting overloaded, or if it's @@ -444,7 +376,7 @@ do_log(Bin, CallOrCast, State = #{id:=Name, module:=Module, mode:=Mode0}) -> end, %% kill the handler if it can't keep up with the load - kill_if_choked(Name, Module, QLen, Mem, State), + kill_if_choked(Name, QLen, Mem, State), if Mode1 == flush -> flush(Name, QLen, T1, State1); @@ -473,8 +405,9 @@ flush(Name, _QLen0, T1, State=#{last_log_ts := _T0, mode_tab := ModeTab}) -> ?observe(Name,{flushed,NewFlushed+1}), State1 = ?update_max_time(?diff_time(T1,_T0),State), + State2 = ?update_max_qlen(_QLen1,State1), {dropped,?update_other(flushed,FLUSHED,NewFlushed, - State1#{mode => ?set_mode(ModeTab,async), + State2#{mode => ?set_mode(ModeTab,async), last_qlen => 0, last_log_ts => T1})}. @@ -484,24 +417,23 @@ write(Name, Mode, T1, Bin, _CallOrCast, handler_state := HandlerState, mode_tab := ModeTab, ctrl_sync_count := CtrlSync, - ctrl_sync_int := CtrlSyncInt, last_qlen := LastQLen, last_log_ts := T0}) -> %% check if we need to limit the number of writes %% during a burst of log events {DoWrite,BurstWinT,BurstMsgCount} = limit_burst(State), - %% only log synhrounously every CtrlSyncInt time, to give the - %% handler time between writes so it can keep up with incoming - %% messages + %% only log synhrounously every ?CONTROLLER_SYNC_INTERVAL time, to + %% give the handler time between writes so it can keep up with + %% incoming messages {Result,LastQLen1,HandlerState1} = if DoWrite, CtrlSync == 0 -> ?observe(Name,{_CallOrCast,1}), - {_,HS1} = Module:sync_write(Name, Bin, HandlerState), + {_,HS1} = Module:write(Name, sync, Bin, HandlerState), {ok,element(2, process_info(self(), message_queue_len)),HS1}; DoWrite -> ?observe(Name,{_CallOrCast,1}), - HS1 = Module:async_write(Name, Bin, HandlerState), + {_,HS1} = Module:write(Name, async, Bin, HandlerState), {ok,LastQLen,HS1}; not DoWrite -> ?observe(Name,{flushed,1}), @@ -519,39 +451,37 @@ write(Name, Mode, T1, Bin, _CallOrCast, {Mode1,BurstMsgCount1,HandlerState2} = if (LastQLen1 < ?FILESYNC_OK_QLEN) andalso (Time > ?IDLE_DETECT_TIME_USEC) -> - HS2 = Module:async_filesync(Name,HandlerState), + {_,HS2} = Module:filesync(Name,async,HandlerState), {?change_mode(ModeTab, Mode, async),0,HS2}; true -> {Mode,BurstMsgCount,HandlerState1} end, - State1 = - ?update_calls_or_casts(_CallOrCast,1,State), - State2 = + State1 = ?update_calls_or_casts(_CallOrCast,1,State), + State2 = ?update_max_qlen(LastQLen1,State1), + State3 = ?update_max_time(Time, - State1#{mode => Mode1, + State2#{mode => Mode1, last_qlen := LastQLen1, last_log_ts => T1, last_op => write, burst_win_ts => BurstWinT, burst_msg_count => BurstMsgCount1, - ctrl_sync_count => if CtrlSync==0 -> CtrlSyncInt; - true -> CtrlSync-1 - end, + ctrl_sync_count => + if CtrlSync==0 -> ?CONTROLLER_SYNC_INTERVAL; + true -> CtrlSync-1 + end, handler_state => HandlerState2}), - {Result,State2}. + {Result,State3}. log_handler_info(Name, Format, Args, #{module:=Module, + formatter:=Formatter, handler_state:=HandlerState}) -> - Config = - case logger:get_handler_config(Name) of - {ok,Conf} -> Conf; - _ -> #{formatter=>{?DEFAULT_FORMATTER,?DEFAULT_FORMAT_CONFIG}} - end, + Config = #{formatter=>Formatter}, Meta = #{time=>erlang:system_time(microsecond)}, Bin = log_to_binary(#{level => notice, msg => {Format,Args}, meta => Meta}, Config), - _ = Module:async_write(Name, Bin, HandlerState), + _ = Module:write(Name, async, Bin, HandlerState), ok. %%%----------------------------------------------------------------- @@ -612,7 +542,20 @@ string_to_binary(String) -> %%%----------------------------------------------------------------- %%% Check that the configuration term is valid check_config(Config) when is_map(Config) -> - check_common_config(maps:to_list(Config)). + case check_common_config(maps:to_list(Config)) of + ok -> + case overload_levels_ok(Config) of + true -> + ok; + false -> + Faulty = maps:with([sync_mode_qlen, + drop_mode_qlen, + flush_qlen],Config), + {error,{invalid_levels,Faulty}} + end; + Error -> + Error + end. check_common_config([{sync_mode_qlen,N}|Config]) when is_integer(N) -> check_common_config(Config); @@ -667,15 +610,13 @@ call_cast_or_drop(_Name, HandlerPid, ModeTab, Bin) -> async -> gen_server:cast(HandlerPid, {log,Bin}); sync -> - try gen_server:call(HandlerPid, {log,Bin}, ?DEFAULT_CALL_TIMEOUT) of - %% if return value from call == dropped, the - %% message has been flushed by handler and should - %% therefore not be counted as dropped in stats - ok -> ok; - dropped -> ok - catch - _:{timeout,_} -> - ?observe(_Name,{dropped,1}) + case call(HandlerPid, {log,Bin}) of + ok -> + ok; + _Other -> + %% dropped or {error,handler_busy} + ?observe(_Name,{dropped,1}), + ok end; drop -> ?observe(_Name,{dropped,1}) @@ -686,10 +627,8 @@ call_cast_or_drop(_Name, HandlerPid, ModeTab, Bin) -> end, ok. -handler_exit(_Name, Reason) -> - exit(Reason). - -set_restart_flag(Name, Module) -> +set_restart_flag(#{id := Name, module := Module} = State) -> + log_handler_info(Name, "Handler ~p overloaded and stopping", [Name], State), Flag = list_to_atom(lists:concat([Module,"_",Name,"_restarting"])), spawn(fun() -> register(Flag, self()), @@ -697,14 +636,14 @@ set_restart_flag(Name, Module) -> end), ok. -unset_restart_flag(Name, Module) -> +unset_restart_flag(#{id := Name, module := Module} = State) -> Flag = list_to_atom(lists:concat([Module,"_",Name,"_restarting"])), case whereis(Flag) of undefined -> - false; + ok; Pid -> exit(Pid, kill), - true + log_handler_info(Name, "Handler ~p restarted", [Name], State) end. check_load(State = #{id:=_Name, mode_tab := ModeTab, mode := Mode, @@ -764,24 +703,17 @@ limit_burst(#{burst_win_ts := BurstWinT0, {true,BurstWinT0,BurstMsgCount+1} end. -kill_if_choked(Name, Module, QLen, Mem, - State = #{overload_kill_enable := KillIfOL, - overload_kill_qlen := OLKillQLen, - overload_kill_mem_size := OLKillMem}) -> +kill_if_choked(Name, QLen, Mem, State = #{overload_kill_enable := KillIfOL, + overload_kill_qlen := OLKillQLen, + overload_kill_mem_size := OLKillMem}) -> if KillIfOL andalso ((QLen > OLKillQLen) orelse (Mem > OLKillMem)) -> - log_handler_info(Name, - "Handler ~p overloaded and stopping", - [Name], State), - set_restart_flag(Name, Module), - handler_exit(Name, {shutdown,{overloaded,Name,QLen,Mem}}); + set_restart_flag(State), + exit({shutdown,{overloaded,Name,QLen,Mem}}); true -> ok end. -flush_log_events() -> - flush_log_events(-1). - flush_log_events(Limit) -> process_flag(priority, high), Flushed = flush_log_events(0, Limit), @@ -804,14 +736,27 @@ flush_log_events(N, Limit) -> 0 -> N end. -cancel_timer(TRef) when is_atom(TRef) -> ok; -cancel_timer(TRef) -> timer:cancel(TRef). - +set_repeated_filesync(#{filesync_repeat_interval:=FSyncInt} = State) + when is_integer(FSyncInt) -> + {ok,TRef} = timer:apply_after(FSyncInt, gen_server, cast, + [self(),repeated_filesync]), + State#{rep_sync_tref=>TRef}; +set_repeated_filesync(State) -> + State. + +cancel_repeated_filesync(State) -> + case maps:take(rep_sync_tref,State) of + {TRef,State1} -> + _ = timer:cancel(TRef), + State1; + error -> + State + end. stop_or_restart(Name, {shutdown,Reason={overloaded,_Name,_QLen,_Mem}}, #{overload_kill_restart_after := RestartAfter}) -> %% If we're terminating because of an overload situation (see - %% kill_if_choked/5), we need to remove the handler and set a + %% kill_if_choked/4), we need to remove the handler and set a %% restart timer. A separate process must perform this in order to %% avoid deadlock. HandlerPid = self(), diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index 95e7d17c0d..ebe741e331 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -29,10 +29,8 @@ -export([info/1, filesync/1, reset/1]). %% logger_h_common callbacks --export([init/2, check_config/4, reset_state/1, - async_filesync/2, sync_filesync/2, - async_write/3, sync_write/3, - handle_info/2, terminate/3]). +-export([init/2, check_config/4, reset_state/2, + filesync/3, write/4, handle_info/3, terminate/3]). %% logger callbacks -export([log/2, adding_handler/1, removing_handler/1, changing_config/3, @@ -71,21 +69,36 @@ reset(Name) -> logger_h_common:reset(?MODULE,Name). %%%=================================================================== -%%% logger callbacks +%%% logger callbacks - just forward to logger_h_common %%%=================================================================== %%%----------------------------------------------------------------- %%% Handler being added +-spec adding_handler(Config) -> {ok,Config} | {error,Reason} when + Config :: logger:handler_config(), + Reason :: term(). + adding_handler(Config) -> logger_h_common:adding_handler(Config). %%%----------------------------------------------------------------- %%% Updating handler config +-spec changing_config(SetOrUpdate, OldConfig, NewConfig) -> + {ok,Config} | {error,Reason} when + SetOrUpdate :: set | update, + OldConfig :: logger:handler_config(), + NewConfig :: logger:handler_config(), + Config :: logger:handler_config(), + Reason :: term(). + changing_config(SetOrUpdate, OldConfig, NewConfig) -> logger_h_common:changing_config(SetOrUpdate, OldConfig, NewConfig). %%%----------------------------------------------------------------- %%% Handler being removed +-spec removing_handler(Config) -> ok when + Config :: logger:handler_config(). + removing_handler(Config) -> logger_h_common:removing_handler(Config). @@ -100,6 +113,9 @@ log(LogEvent, Config) -> %%%----------------------------------------------------------------- %%% Remove internal fields from configuration +-spec filter_config(Config) -> Config when + Config :: logger:handler_config(). + filter_config(Config) -> logger_h_common:filter_config(Config). @@ -163,33 +179,29 @@ check_h_config([]) -> get_default_config() -> #{type => standard_io}. -async_filesync(_Name,#{type := Type}=State) when is_atom(Type) -> - State; -async_filesync(_Name,#{file_ctrl_pid := FileCtrlPid}=State) -> +filesync(_Name, _Mode, #{type := Type}=State) when is_atom(Type) -> + {ok,State}; +filesync(_Name, async, #{file_ctrl_pid := FileCtrlPid} = State) -> ok = file_ctrl_filesync_async(FileCtrlPid), - State. - -sync_filesync(_Name,#{type := Type}=State) when is_atom(Type) -> {ok,State}; -sync_filesync(_Name,#{file_ctrl_pid := FileCtrlPid}=State) -> +filesync(_Name, sync, #{file_ctrl_pid := FileCtrlPid} = State) -> Result = file_ctrl_filesync_sync(FileCtrlPid), {Result,State}. -async_write(_Name, Bin, #{file_ctrl_pid:=FileCtrlPid} = State) -> +write(_Name, async, Bin, #{file_ctrl_pid:=FileCtrlPid} = State) -> ok = file_write_async(FileCtrlPid, Bin), - State. - -sync_write(_Name, Bin, #{file_ctrl_pid:=FileCtrlPid} = State) -> + {ok,State}; +write(_Name, sync, Bin, #{file_ctrl_pid:=FileCtrlPid} = State) -> Result = file_write_sync(FileCtrlPid, Bin), {Result,State}. -reset_state(State) -> +reset_state(_Name, State) -> State. -handle_info({'EXIT',Pid,Why}, #{type := FileInfo, file_ctrl_pid := Pid}) -> +handle_info(_Name, {'EXIT',Pid,Why}, #{type := FileInfo, file_ctrl_pid := Pid}) -> %% file_ctrl_pid died, file error, terminate handler exit({error,{write_failed,FileInfo,Why}}); -handle_info(_, State) -> +handle_info(_, _, State) -> State. terminate(_Name, _Reason, #{file_ctrl_pid:=FWPid}) -> @@ -203,7 +215,8 @@ terminate(_Name, _Reason, #{file_ctrl_pid:=FWPid}) -> ok after ?DEFAULT_CALL_TIMEOUT -> - exit(FWPid, kill) + exit(FWPid, kill), + ok end; false -> ok @@ -273,18 +286,18 @@ file_write_async(Pid, Bin) -> ok. file_write_sync(Pid, Bin) -> - file_ctrl_call(Pid, {log,self(),Bin}). + file_ctrl_call(Pid, {log,Bin}). file_ctrl_filesync_async(Pid) -> Pid ! filesync, ok. file_ctrl_filesync_sync(Pid) -> - file_ctrl_call(Pid, {filesync,self()}). + file_ctrl_call(Pid, filesync). file_ctrl_call(Pid, Msg) -> MRef = monitor(process, Pid), - Pid ! {Msg,MRef}, + Pid ! {Msg,{self(),MRef}}, receive {MRef,Result} -> demonitor(MRef, [flush]), @@ -302,63 +315,40 @@ file_ctrl_init(HandlerName, FileInfo, Starter) when is_tuple(FileInfo) -> case do_open_log_file(FileInfo) of {ok,Fd} -> Starter ! {self(),ok}, - file_ctrl_loop(Fd, file, FileName, false, ok, ok, HandlerName); + file_ctrl_loop(Fd, FileName, false, ok, ok, HandlerName); {error,Reason} -> Starter ! {self(),{error,{open_failed,FileName,Reason}}} end; file_ctrl_init(HandlerName, StdDev, Starter) -> Starter ! {self(),ok}, - file_ctrl_loop(StdDev, standard_io, StdDev, false, ok, ok, HandlerName). + file_ctrl_loop(StdDev, StdDev, false, ok, ok, HandlerName). -file_ctrl_loop(Fd, Type, DevName, Synced, +file_ctrl_loop(Fd, DevName, Synced, PrevWriteResult, PrevSyncResult, HandlerName) -> receive %% asynchronous event {log,Bin} -> - Result = if Type == file -> - write_to_dev(Fd, Bin, DevName, - PrevWriteResult, HandlerName); - true -> - io:put_chars(Fd, Bin) - end, - file_ctrl_loop(Fd, Type, DevName, false, + Result = write_to_dev(Fd, Bin, DevName, PrevWriteResult, HandlerName), + file_ctrl_loop(Fd, DevName, false, Result, PrevSyncResult, HandlerName); %% synchronous event - {{log,From,Bin},MRef} -> - WResult = - if Type == file -> - %% check that file hasn't been deleted - CheckFile = - fun() -> {ok,_} = file:read_file_info(DevName) end, - spawn_link(CheckFile), - write_to_dev(Fd, Bin, DevName, - PrevWriteResult, HandlerName); - true -> - _ = io:put_chars(Fd, Bin), - ok - end, + {{log,Bin},{From,MRef}} -> + check_exist(Fd, DevName), + Result = write_to_dev(Fd, Bin, DevName, PrevWriteResult, HandlerName), From ! {MRef,ok}, - file_ctrl_loop(Fd, Type, DevName, false, - WResult, PrevSyncResult, HandlerName); + file_ctrl_loop(Fd, DevName, false, + Result, PrevSyncResult, HandlerName); - filesync when not Synced -> - Result = sync_dev(Fd, DevName, PrevSyncResult, HandlerName), - file_ctrl_loop(Fd, Type, DevName, true, + filesync -> + Result = sync_dev(Fd, DevName, Synced, PrevSyncResult, HandlerName), + file_ctrl_loop(Fd, DevName, true, PrevWriteResult, Result, HandlerName); - filesync -> - file_ctrl_loop(Fd, Type, DevName, true, - PrevWriteResult, PrevSyncResult, HandlerName); - - {{filesync,From},MRef} -> - Result = if not Synced -> - sync_dev(Fd, DevName, PrevSyncResult, HandlerName); - true -> - ok - end, + {filesync,{From,MRef}} -> + Result = sync_dev(Fd, DevName, Synced, PrevSyncResult, HandlerName), From ! {MRef,ok}, - file_ctrl_loop(Fd, Type, DevName, true, + file_ctrl_loop(Fd, DevName, true, PrevWriteResult, Result, HandlerName); stop -> @@ -366,26 +356,30 @@ file_ctrl_loop(Fd, Type, DevName, Synced, stopped end. +check_exist(DevName, DevName) when is_atom(DevName) -> + ok; +check_exist(_Fd, FileName) -> + _ = spawn_link(fun() -> {ok,_} = file:read_file_info(FileName) end), + ok. + +write_to_dev(DevName, Bin, _DevName, _PrevWriteResult, _HandlerName) + when is_atom(DevName) -> + io:put_chars(DevName, Bin); write_to_dev(Fd, Bin, FileName, PrevWriteResult, HandlerName) -> - case ?file_write(Fd, Bin) of - ok -> - ok; - PrevWriteResult -> - %% don't report same error twice - PrevWriteResult; - Error -> - logger_h_common:error_notify({HandlerName,write,FileName,Error}), - Error - end. + Result = ?file_write(Fd, Bin), + maybe_notify_error(write,Result,PrevWriteResult,FileName,HandlerName). -sync_dev(Fd, DevName, PrevSyncResult, HandlerName) -> - case ?file_datasync(Fd) of - ok -> - ok; - PrevSyncResult -> - %% don't report same error twice - PrevSyncResult; - Error -> - logger_h_common:error_notify({HandlerName,filesync,DevName,Error}), - Error - end. +sync_dev(_Fd, _FileName, true, PrevSyncResult, _HandlerName) -> + PrevSyncResult; +sync_dev(Fd, FileName, false, PrevSyncResult, HandlerName) -> + Result = ?file_datasync(Fd), + maybe_notify_error(filesync,Result,PrevSyncResult,FileName,HandlerName). + +maybe_notify_error(_Op, ok, _PrevResult, _FileName, _HandlerName) -> + ok; +maybe_notify_error(_Op, PrevResult, PrevResult, _FileName, _HandlerName) -> + %% don't report same error twice + PrevResult; +maybe_notify_error(Op, Error, _PrevResult, FileName, HandlerName) -> + logger_h_common:error_notify({HandlerName,Op,FileName,Error}), + Error. diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 7bac4c2cb2..2bd49432b2 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -419,14 +419,19 @@ config_fail(_Config) -> filter_default=>log, formatter=>{?MODULE,self()}}), - {error,{handler_not_added,{invalid_levels,{_,1,_}}}} = + {error,{handler_not_added,{invalid_config,logger_disk_log_h, + {invalid_levels,#{drop_mode_qlen:=1}}}}} = logger:add_handler(?MODULE,logger_disk_log_h, #{config => #{drop_mode_qlen=>1}}), - {error,{handler_not_added,{invalid_levels,{43,42,_}}}} = + {error,{handler_not_added,{invalid_config,logger_disk_log_h, + {invalid_levels,#{sync_mode_qlen:=43, + drop_mode_qlen:=42}}}}} = logger:add_handler(?MODULE,logger_disk_log_h, #{config => #{sync_mode_qlen=>43, drop_mode_qlen=>42}}), - {error,{handler_not_added,{invalid_levels,{_,43,42}}}} = + {error,{handler_not_added,{invalid_config,logger_disk_log_h, + {invalid_levels,#{drop_mode_qlen:=43, + flush_qlen:=42}}}}} = logger:add_handler(?MODULE,logger_disk_log_h, #{config => #{drop_mode_qlen=>43, flush_qlen=>42}}), @@ -440,7 +445,7 @@ config_fail(_Config) -> #{max_no_files=>2}), %% incorrect values of OP params {ok,#{config := HConfig}} = logger:get_handler_config(?MODULE), - {error,{invalid_levels,_}} = + {error,{invalid_config,logger_disk_log_h,{invalid_levels,_}}} = logger:update_handler_config(?MODULE,config, HConfig#{sync_mode_qlen=>100, flush_qlen=>99}), @@ -672,9 +677,9 @@ sync(Config) -> SyncInt = 1000, WaitT = 4500, OneSync = {logger_h_common,handle_cast,repeated_filesync}, - %% receive 1 initial repeated_filesync, then 1 per sec + %% receive 1 repeated_filesync per sec start_tracer([{logger_h_common,handle_cast,2}], - [OneSync || _ <- lists:seq(1, 1 + trunc(WaitT/SyncInt))]), + [OneSync || _ <- lists:seq(1, trunc(WaitT/SyncInt))]), HConfig2 = HConfig#{filesync_repeat_interval => SyncInt}, ok = logger:update_handler_config(?MODULE, config, HConfig2), @@ -717,7 +722,7 @@ disk_log_wrap(Config) -> end, {ok,_} = dbg:tracer(process, {TraceFun, Tester}), {ok,_} = dbg:p(whereis(h_proc_name()), [c]), - {ok,_} = dbg:tp(logger_disk_log_h, handle_info, 2, []), + {ok,_} = dbg:tp(logger_disk_log_h, handle_info, 3, []), Text = [34 + rand:uniform(126-34) || _ <- lists:seq(1,MaxBytes)], ct:pal("String = ~p (~w)", [Text, erts_debug:size(Text)]), @@ -735,7 +740,7 @@ disk_log_wrap(Config) -> timer:sleep(1000), dbg:stop_clear(), Received = lists:flatmap(fun({trace,_M,handle_info, - [{disk_log,_Node,_Name,What},_]}) -> + [_,{disk_log,_Node,_Name,What},_]}) -> [{trace,What}]; ({log,_}) -> [] @@ -771,7 +776,7 @@ disk_log_full(Config) -> end, {ok,_} = dbg:tracer(process, {TraceFun, Tester}), {ok,_} = dbg:p(whereis(h_proc_name()), [c]), - {ok,_} = dbg:tp(logger_disk_log_h, handle_info, 2, []), + {ok,_} = dbg:tp(logger_disk_log_h, handle_info, 3, []), NoOfChars = 5, Text = [34 + rand:uniform(126-34) || _ <- lists:seq(1,NoOfChars)], @@ -781,7 +786,7 @@ disk_log_full(Config) -> timer:sleep(2000), dbg:stop_clear(), Received = lists:flatmap(fun({trace,_M,handle_info, - [{disk_log,_Node,_Name,What},_]}) -> + [_,{disk_log,_Node,_Name,What},_]}) -> [{trace,What}]; ({log,_}) -> [] @@ -820,14 +825,14 @@ disk_log_events(Config) -> end, {ok,_} = dbg:tracer(process, {TraceFun, Tester}), {ok,_} = dbg:p(whereis(h_proc_name()), [c]), - {ok,_} = dbg:tp(logger_disk_log_h, handle_info, 2, []), + {ok,_} = dbg:tp(logger_disk_log_h, handle_info, 3, []), [whereis(h_proc_name()) ! E || E <- Events], %% wait for trace messages timer:sleep(2000), dbg:stop_clear(), Received = lists:map(fun({trace,_M,handle_info, - [Got,_]}) -> Got + [_,Got,_]}) -> Got end, test_server:messages_get()), ct:pal("Trace =~n~p", [Received]), NoOfEvents = length(Events), diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index affa04d410..a1159f280c 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -319,14 +319,19 @@ config_fail(_Config) -> #{config => #{restart_type => bad}, filter_default=>log, formatter=>{?MODULE,self()}}), - {error,{handler_not_added,{invalid_levels,{_,1,_}}}} = + {error,{handler_not_added,{invalid_config,logger_std_h, + {invalid_levels,#{drop_mode_qlen:=1}}}}} = logger:add_handler(?MODULE,logger_std_h, #{config => #{drop_mode_qlen=>1}}), - {error,{handler_not_added,{invalid_levels,{43,42,_}}}} = + {error,{handler_not_added,{invalid_config,logger_std_h, + {invalid_levels,#{sync_mode_qlen:=43, + drop_mode_qlen:=42}}}}} = logger:add_handler(?MODULE,logger_std_h, #{config => #{sync_mode_qlen=>43, drop_mode_qlen=>42}}), - {error,{handler_not_added,{invalid_levels,{_,43,42}}}} = + {error,{handler_not_added,{invalid_config,logger_std_h, + {invalid_levels,#{drop_mode_qlen:=43, + flush_qlen:=42}}}}} = logger:add_handler(?MODULE,logger_std_h, #{config => #{drop_mode_qlen=>43, flush_qlen=>42}}), @@ -338,7 +343,7 @@ config_fail(_Config) -> logger:set_handler_config(?MODULE,config, #{type=>{file,"file"}}), - {error,{invalid_levels,_}} = + {error,{invalid_config,logger_std_h,{invalid_levels,_}}} = logger:set_handler_config(?MODULE,config, #{sync_mode_qlen=>100, flush_qlen=>99}), @@ -643,10 +648,8 @@ sync(Config) -> %% 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:notice("first", ?domain), @@ -655,10 +658,8 @@ sync(Config) -> %% 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} ]), @@ -679,13 +680,10 @@ sync(Config) -> %% 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:notice("third", ?domain), %% wait for automatic filesync @@ -698,9 +696,9 @@ sync(Config) -> SyncInt = 1000, WaitT = 4500, OneSync = {logger_h_common,handle_cast,repeated_filesync}, - %% receive 1 initial repeated_filesync, then 1 per sec + %% receive 1 repeated_filesync per sec start_tracer([{logger_h_common,handle_cast,2}], - [OneSync || _ <- lists:seq(1, 1 + trunc(WaitT/SyncInt))]), + [OneSync || _ <- lists:seq(1, trunc(WaitT/SyncInt))]), ok = logger:update_handler_config(?MODULE, config, #{filesync_repeat_interval => SyncInt}), -- cgit v1.2.3 From b68f7bfaa70a8f2f17f3167abbe3e8de6b8441ce Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 16 Oct 2018 18:59:08 +0200 Subject: [logger] Add statistics counter for drops due to message bursts --- lib/kernel/src/logger_h_common.erl | 40 +++++++++++++++++++------------------- lib/kernel/src/logger_h_common.hrl | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_h_common.erl b/lib/kernel/src/logger_h_common.erl index d290f51e34..74a2d158fc 100644 --- a/lib/kernel/src/logger_h_common.erl +++ b/lib/kernel/src/logger_h_common.erl @@ -421,7 +421,7 @@ write(Name, Mode, T1, Bin, _CallOrCast, last_log_ts := T0}) -> %% check if we need to limit the number of writes %% during a burst of log events - {DoWrite,BurstWinT,BurstMsgCount} = limit_burst(State), + {DoWrite,State1} = limit_burst(State), %% only log synhrounously every ?CONTROLLER_SYNC_INTERVAL time, to %% give the handler time between writes so it can keep up with @@ -448,30 +448,28 @@ write(Name, Mode, T1, Bin, _CallOrCast, %% time between two consecutive log events is fast and no new %% event comes in after the last one, idle state won't be detected! Time = ?diff_time(T1,T0), - {Mode1,BurstMsgCount1,HandlerState2} = + State2 = if (LastQLen1 < ?FILESYNC_OK_QLEN) andalso (Time > ?IDLE_DETECT_TIME_USEC) -> {_,HS2} = Module:filesync(Name,async,HandlerState), - {?change_mode(ModeTab, Mode, async),0,HS2}; + State1#{mode => ?change_mode(ModeTab, Mode, async), + burst_msg_count => 0, + handler_state => HS2}; true -> - {Mode,BurstMsgCount,HandlerState1} + State1#{mode => Mode, handler_state => HandlerState1} end, - State1 = ?update_calls_or_casts(_CallOrCast,1,State), - State2 = ?update_max_qlen(LastQLen1,State1), - State3 = + State3 = ?update_calls_or_casts(_CallOrCast,1,State2), + State4 = ?update_max_qlen(LastQLen1,State3), + State5 = ?update_max_time(Time, - State2#{mode => Mode1, - last_qlen := LastQLen1, + State4#{last_qlen := LastQLen1, last_log_ts => T1, last_op => write, - burst_win_ts => BurstWinT, - burst_msg_count => BurstMsgCount1, ctrl_sync_count => if CtrlSync==0 -> ?CONTROLLER_SYNC_INTERVAL; true -> CtrlSync-1 - end, - handler_state => HandlerState2}), - {Result,State3}. + end}), + {Result,State5}. log_handler_info(Name, Format, Args, #{module:=Module, formatter:=Formatter, @@ -681,26 +679,28 @@ check_load(State = #{id:=_Name, mode_tab := ModeTab, mode := Mode, ?update_other(flushes,FLUSHES,_NewFlushes, State1#{last_qlen => QLen})}. -limit_burst(#{burst_limit_enable := false}) -> - {true,0,0}; +limit_burst(#{burst_limit_enable := false}=State) -> + {true,State}; limit_burst(#{burst_win_ts := BurstWinT0, burst_msg_count := BurstMsgCount, burst_limit_window_time := BurstLimitWinTime, - burst_limit_max_count := BurstLimitMaxCnt}) -> + burst_limit_max_count := BurstLimitMaxCnt} = State) -> if (BurstMsgCount >= BurstLimitMaxCnt) -> %% the limit for allowed messages has been reached BurstWinT1 = ?timestamp(), case ?diff_time(BurstWinT1,BurstWinT0) of BurstCheckTime when BurstCheckTime < (BurstLimitWinTime*1000) -> %% we're still within the burst time frame - {false,BurstWinT0,BurstMsgCount}; + {false,?update_other(burst_drops,BURSTS,1,State)}; _BurstCheckTime -> %% burst time frame passed, reset counters - {true,BurstWinT1,0} + {true,State#{burst_win_ts => BurstWinT1, + burst_msg_count => 0}} end; true -> %% the limit for allowed messages not yet reached - {true,BurstWinT0,BurstMsgCount+1} + {true,State#{burst_win_ts => BurstWinT0, + burst_msg_count => BurstMsgCount+1}} end. kill_if_choked(Name, QLen, Mem, State = #{overload_kill_enable := KillIfOL, diff --git a/lib/kernel/src/logger_h_common.hrl b/lib/kernel/src/logger_h_common.hrl index e0a7b6e3ca..1a0b2c700d 100644 --- a/lib/kernel/src/logger_h_common.hrl +++ b/lib/kernel/src/logger_h_common.hrl @@ -210,7 +210,7 @@ -ifdef(SAVE_STATS). -define(merge_with_stats(STATE), STATE#{flushes => 0, flushed => 0, drops => 0, - casts => 0, calls => 0, + burst_drops => 0, casts => 0, calls => 0, max_qlen => 0, max_time => 0}). -define(update_max_qlen(QLEN, STATE), -- cgit v1.2.3 From de968677491c21af9695ba910f4ae4682221ffea Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 17 Oct 2018 19:26:08 +0200 Subject: [logger] Write asynchronously to disk log This commit updates logger_disk_log_h to write most log events asynchronlously (disk_log:balog/2), but synchronously (disk_log:blog/2) on every 20th event in the same way as logger_std_h does. This is for efficiency reasons. --- lib/kernel/src/logger_disk_log_h.erl | 19 ++++++----- lib/kernel/src/logger_h_common.hrl | 14 ++++----- lib/kernel/test/logger_disk_log_h_SUITE.erl | 49 ++++++++++++++++------------- 3 files changed, 44 insertions(+), 38 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_disk_log_h.erl b/lib/kernel/src/logger_disk_log_h.erl index 1e48e5b0a8..41e0d51a9d 100644 --- a/lib/kernel/src/logger_disk_log_h.erl +++ b/lib/kernel/src/logger_disk_log_h.erl @@ -184,10 +184,12 @@ merge_default_logopts(Name, HConfig) -> maps:merge(Defaults, HConfig). filesync(Name,_Mode,State) -> - disk_log_sync(Name,State). + Result = ?disk_log_sync(Name), + maybe_notify_error(Name, filesync, Result, prev_sync_result, State). -write(Name, _Mode, Bin, State) -> - disk_log_write(Name, Bin, State). +write(Name, Mode, Bin, State) -> + Result = ?disk_log_write(Name, Mode, Bin), + maybe_notify_error(Name, log, Result, prev_log_result, State). reset_state(_Name, State) -> State#{prev_log_result => ok, @@ -244,13 +246,10 @@ close_disk_log(Name, _) -> _ = disk_log:lclose(Name), ok. -disk_log_write(Name, Bin, State) -> - Result = ?disk_log_blog(Name, Bin), - maybe_notify_error(Name, log, Result, prev_log_result, State). - -disk_log_sync(Name, State) -> - Result = ?disk_log_sync(Name), - maybe_notify_error(Name, filesync, Result, prev_sync_result, State). +disk_log_write(Name, sync, Bin) -> + disk_log:blog(Name, Bin); +disk_log_write(Name, async, Bin) -> + disk_log:balog(Name, Bin). %%%----------------------------------------------------------------- %%% Print error messages, but don't repeat the same message diff --git a/lib/kernel/src/logger_h_common.hrl b/lib/kernel/src/logger_h_common.hrl index 1a0b2c700d..261b0a6246 100644 --- a/lib/kernel/src/logger_h_common.hrl +++ b/lib/kernel/src/logger_h_common.hrl @@ -137,7 +137,7 @@ ets:insert(?TEST_HOOKS_TAB, {internal_log,{logger,internal_log}}), ets:insert(?TEST_HOOKS_TAB, {file_write,ok}), ets:insert(?TEST_HOOKS_TAB, {file_datasync,ok}), - ets:insert(?TEST_HOOKS_TAB, {disk_log_blog,ok}), + ets:insert(?TEST_HOOKS_TAB, {disk_log_write,ok}), ets:insert(?TEST_HOOKS_TAB, {disk_log_sync,ok})). -define(set_internal_log(MOD_FUNC), @@ -150,7 +150,7 @@ ets:insert(?TEST_HOOKS_TAB, {internal_log,{logger,internal_log}}), ets:insert(?TEST_HOOKS_TAB, {file_write,ok}), ets:insert(?TEST_HOOKS_TAB, {file_datasync,ok}), - ets:insert(?TEST_HOOKS_TAB, {disk_log_blog,ok}), + ets:insert(?TEST_HOOKS_TAB, {disk_log_write,ok}), ets:insert(?TEST_HOOKS_TAB, {disk_log_sync,ok})). -define(internal_log(TYPE, TERM), @@ -171,11 +171,11 @@ [{_,ERROR}] -> ERROR catch _:_ -> file:datasync(DEVICE) end). - -define(disk_log_blog(LOG, DATA), - try ets:lookup(?TEST_HOOKS_TAB, disk_log_blog) of - [{_,ok}] -> disk_log:blog(LOG, DATA); + -define(disk_log_write(LOG, MODE, DATA), + try ets:lookup(?TEST_HOOKS_TAB, disk_log_write) of + [{_,ok}] -> disk_log_write(LOG, MODE, DATA); [{_,ERROR}] -> ERROR - catch _:_ -> disk_log:blog(LOG, DATA) end). + catch _:_ -> disk_log_write(LOG, MODE, DATA) end). -define(disk_log_sync(LOG), try ets:lookup(?TEST_HOOKS_TAB, disk_log_sync) of @@ -194,7 +194,7 @@ -define(internal_log(TYPE, TERM), logger:internal_log(TYPE, TERM)). -define(file_write(DEVICE, DATA), file:write(DEVICE, DATA)). -define(file_datasync(DEVICE), file:datasync(DEVICE)). - -define(disk_log_blog(LOG, DATA), disk_log:blog(LOG, DATA)). + -define(disk_log_write(LOG, MODE, DATA), disk_log_write(LOG, MODE, DATA)). -define(disk_log_sync(LOG), disk_log:sync(LOG)). -define(DEFAULT_CALL_TIMEOUT, 10000). -endif. diff --git a/lib/kernel/test/logger_disk_log_h_SUITE.erl b/lib/kernel/test/logger_disk_log_h_SUITE.erl index 2bd49432b2..87b8250781 100644 --- a/lib/kernel/test/logger_disk_log_h_SUITE.erl +++ b/lib/kernel/test/logger_disk_log_h_SUITE.erl @@ -636,10 +636,10 @@ sync(Config) -> filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), formatter=>{?MODULE,nl}}), - start_tracer([{disk_log,blog,2}, - {logger_disk_log_h,disk_log_sync,2}], - [{disk_log,blog,<<"first\n">>}, - {logger_disk_log_h,disk_log_sync}]), + start_tracer([{logger_disk_log_h,disk_log_write,3}, + {disk_log,sync,1}], + [{logger_disk_log_h,disk_log_write,<<"first\n">>}, + {disk_log,sync}]), logger:notice("first", ?domain), %% wait for automatic disk_log_sync @@ -658,12 +658,12 @@ sync(Config) -> %% triggered by the idle timeout between "fourth" and "fifth". timer:sleep(?IDLE_DETECT_TIME_MSEC*2), - start_tracer([{disk_log,blog,2}, - {logger_disk_log_h,disk_log_sync,2}], - [{disk_log,blog,<<"second\n">>}, - {logger_disk_log_h,disk_log_sync}, - {disk_log,blog,<<"third\n">>}, - {logger_disk_log_h,disk_log_sync}]), + start_tracer([{logger_disk_log_h,disk_log_write,3}, + {disk_log,sync,1}], + [{logger_disk_log_h,disk_log_write,<<"second\n">>}, + {disk_log,sync}, + {logger_disk_log_h,disk_log_write,<<"third\n">>}, + {disk_log,sync}]), logger:notice("second", ?domain), timer:sleep(?IDLE_DETECT_TIME_MSEC*2), @@ -792,8 +792,12 @@ disk_log_full(Config) -> [] end, test_server:messages_get()), ct:pal("Trace =~n~p", [Received]), - [{trace,full}, - {trace,{error_status,{error,{full,_}}}}] = Received, + + %% The tail here could be an error_status notification, if the + %% last write was synchronous, but in most cases it will not be + [{trace,full}|_] = Received, + %% [{trace,full}, + %% {trace,{error_status,{error,{full,_}}}}] = Received, ok. disk_log_full(cleanup, _Config) -> dbg:stop_clear(), @@ -855,14 +859,15 @@ write_failure(Config) -> 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]), - rpc:call(Node, ?MODULE, set_result, [disk_log_blog,ok]), + rpc:call(Node, ?MODULE, set_result, [disk_log_write,ok]), HState = rpc:call(Node, logger_disk_log_h, info, [?STANDARD_HANDLER]), ct:pal("LogOpts = ~p", [LogOpts = maps:get(log_opts, maps:get(handler_state,HState))]), + %% ?check and ?check_no_log in this test only check for internal log events ok = log_on_remote_node(Node, "Logged1"), rpc:call(Node, logger_disk_log_h, filesync, [?STANDARD_HANDLER]), - ?check_no_log, + ?check_no_log, % no internal log when write ok SyncRepInt = case (fun() -> is_atom(?FILESYNC_REPEAT_INTERVAL) end)() of true -> 5500; @@ -871,24 +876,26 @@ write_failure(Config) -> try_read_file(Log, {ok,<<"Logged1\n">>}, SyncRepInt), - rpc:call(Node, ?MODULE, set_result, [disk_log_blog,{error,no_such_log}]), + rpc:call(Node, ?MODULE, set_result, [disk_log_write,{error,no_such_log}]), ok = log_on_remote_node(Node, "Cause simple error printout"), + %% this should have caused an internal log ?check({error,{?STANDARD_HANDLER,log,LogOpts,{error,no_such_log}}}), - + ok = log_on_remote_node(Node, "No second error printout"), - ?check_no_log, + ?check_no_log, % but don't log same error twice - rpc:call(Node, ?MODULE, set_result, [disk_log_blog, + rpc:call(Node, ?MODULE, set_result, [disk_log_write, {error,{full,?STANDARD_HANDLER}}]), ok = log_on_remote_node(Node, "Cause simple error printout"), + %% this was a different error, so it should be logged ?check({error,{?STANDARD_HANDLER,log,LogOpts, {error,{full,?STANDARD_HANDLER}}}}), - rpc:call(Node, ?MODULE, set_result, [disk_log_blog,ok]), + rpc:call(Node, ?MODULE, set_result, [disk_log_write,ok]), ok = log_on_remote_node(Node, "Logged2"), rpc:call(Node, logger_disk_log_h, filesync, [?STANDARD_HANDLER]), - ?check_no_log, + ?check_no_log, % no internal log when write ok try_read_file(Log, {ok,<<"Logged1\nLogged2\n">>}, SyncRepInt), ok. write_failure(cleanup, _Config) -> @@ -1616,7 +1623,7 @@ tpl([]) -> tracer({trace,_,call,{logger_h_common,handle_cast,[Op|_]},Caller}, {Pid,[{Mod,Func,Op}|Expected]}) -> maybe_tracer_done(Pid,Expected,{Mod,Func,Op},Caller); -tracer({trace,_,call,{Mod=disk_log,Func=blog,[_,Data]},Caller}, {Pid,[{Mod,Func,Data}|Expected]}) -> +tracer({trace,_,call,{Mod=logger_disk_log_h,Func=disk_log_write,[_,_,Data]},Caller}, {Pid,[{Mod,Func,Data}|Expected]}) -> maybe_tracer_done(Pid,Expected,{Mod,Func,Data},Caller); tracer({trace,_,call,{Mod,Func,_},Caller}, {Pid,[{Mod,Func}|Expected]}) -> maybe_tracer_done(Pid,Expected,{Mod,Func},Caller); -- cgit v1.2.3 From 6cf477d189ba993a5cb66aad421c9d7505da250c Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Fri, 12 Oct 2018 11:01:05 +0200 Subject: Write test case --- lib/kernel/test/gen_tcp_misc_SUITE.erl | 146 ++++++++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index 358ca872f7..30e201d2de 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -52,7 +52,8 @@ several_accepts_in_one_go/1, accept_system_limit/1, active_once_closed/1, send_timeout/1, send_timeout_active/1, otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1, - wrapping_oct/0, wrapping_oct/1, otp_9389/1, otp_13939/1]). + wrapping_oct/0, wrapping_oct/1, otp_9389/1, otp_13939/1, + otp_12242/1]). %% Internal exports. -export([sender/3, not_owner/1, passive_sockets_server/2, priority_server/1, @@ -95,7 +96,8 @@ all() -> killing_multi_acceptors2, several_accepts_in_one_go, accept_system_limit, active_once_closed, send_timeout, send_timeout_active, otp_7731, wrapping_oct, - zombie_sockets, otp_7816, otp_8102, otp_9389]. + zombie_sockets, otp_7816, otp_8102, otp_9389, + otp_12242]. groups() -> []. @@ -3284,3 +3286,143 @@ otp_13939(Config) when is_list(Config) -> exit(Pid, normal), ct:fail("Server process blocked on send.") end. + +otp_12242(Config) when is_list(Config) -> + case os:type() of + {win32,_} -> + %% Even if we set sndbuf and recbuf to small sizes + %% Windows either happily accepts to send GBytes of data + %% in no time, so the second send below that is supposed + %% to time out just succedes, or the first send that + %% is supposed to fill the inet_drv I/O queue and + %% start waiting for when more data can be sent + %% instead sends all data but suffers a send + %% failure that closes the socket + {skipped,backpressure_broken_on_win32}; + _ -> + %% Find the IPv4 address of an up and running interface + %% that is not loopback nor pointtopoint + {ok,IFList} = inet:getifaddrs(), + ct:pal("IFList ~p~n", [IFList]), + case + lists:flatten( + [lists:filtermap( + fun ({addr,Addr}) when tuple_size(Addr) =:= 4 -> + {true,Addr}; + (_) -> + false + end, Opts) + || {_,Opts} <- IFList, + case lists:keyfind(flags, 1, Opts) of + {_,Flags} -> + lists:member(up, Flags) + andalso + lists:member(running, Flags) + andalso + not lists:member(loopback, Flags) + andalso + not lists:member(pointtopoint, Flags); + false -> + false + end]) + of + [Addr|_] -> + otp_12242(Addr); + Other -> + {skipped,{no_external_address,Other}} + end + end; +%% +otp_12242(Addr) when tuple_size(Addr) =:= 4 -> + ct:timetrap(30000), + ct:pal("Using address ~p~n", [Addr]), + Bufsize = 16 * 1024, + Datasize = 128 * 1024 * 1024, % At least 1 s on GBit interface + Blob = binary:copy(<<$x>>, Datasize), + LOpts = + [{backlog,4},{reuseaddr,true},{ip,Addr}, + binary,{active,false}, + {recbuf,Bufsize},{sndbuf,Bufsize},{buffer,Bufsize}], + COpts = + [binary,{active,false},{ip,Addr}, + {linger,{true,1}}, % 1 s + {send_timeout,500}, + {recbuf,Bufsize},{sndbuf,Bufsize},{buffer,Bufsize}], + Dir = filename:dirname(code:which(?MODULE)), + {ok,ListenerNode} = + test_server:start_node( + ?UNIQ_NODE_NAME, slave, [{args,"-pa " ++ Dir}]), + Tester = self(), + Listener = + spawn( + ListenerNode, + fun () -> + {ok,L} = gen_tcp:listen(0, LOpts), + {ok,LPort} = inet:port(L), + Tester ! {self(),port,LPort}, + {ok,A} = gen_tcp:accept(L), + ok = gen_tcp:close(L), + receive + {Tester,stop} -> + ok = gen_tcp:close(A) + end + end), + ListenerMref = monitor(process, Listener), + LPort = receive {Listener,port,P} -> P end, + {ok,C} = gen_tcp:connect(Addr, LPort, COpts, infinity), + {ok,ReadCOpts} = inet:getopts(C, [recbuf,sndbuf,buffer]), + ct:pal("ReadCOpts ~p~n", [ReadCOpts]), + %% + %% Fill the buffers + ct:pal("Sending ~p bytes~n", [Datasize]), + ok = gen_tcp:send(C, Blob), + ct:pal("Sent ~p bytes~n", [Datasize]), + %% Spawn the Closer, + %% try to ensure that the close call is in progress + %% before the owner proceeds with sending + Owner = self(), + {_Closer,CloserMref} = + spawn_opt( + fun () -> + Owner ! {tref, erlang:start_timer(50, Owner, closing)}, + ct:pal("Calling gen_tcp:close(C)~n"), + try gen_tcp:close(C) of + Result -> + ct:pal("gen_tcp:close(C) -> ~p~n", [Result]), + ok = Result + catch + Class:Reason:Stacktrace -> + ct:pal( + "gen_tcp:close(C) >< ~p:~p~n ~p~n", + [Class,Reason,Stacktrace]), + erlang:raise(Class, Reason, Stacktrace) + end + end, [link,monitor]), + receive + {tref,Tref} -> + receive {timeout,Tref,_} -> ok end, + ct:pal("Sending ~p bytes again~n", [Datasize]), + %% Now should the close be in progress... + %% All buffers are full, remote end is not reading, + %% and the send timeout is 1 s so this will timeout: + {error,timeout} = gen_tcp:send(C, Blob), + ct:pal("Sending ~p bytes again timed out~n", [Datasize]), + ok = inet:setopts(C, [{send_timeout,10000}]), + %% There is a hidden timeout here. Port close is sampled + %% every 5 s by prim_inet:send_recv_reply. + %% Linger is 3 s so the Closer will finish this send: + ct:pal("Sending ~p bytes with 10 s timeout~n", [Datasize]), + {error,closed} = gen_tcp:send(C, Blob), + ct:pal("Sending ~p bytes with 10 s timeout was closed~n", + [Datasize]), + normal = wait(CloserMref), + ct:pal("The Closer has exited~n"), + Listener ! {Tester,stop}, + receive {'DOWN',ListenerMref,_,_,_} -> ok end, + ct:pal("The Listener has exited~n"), + test_server:stop_node(ListenerNode), + ok + end. + +wait(Mref) -> + receive {'DOWN',Mref,_,_,Reason} -> Reason end. -- cgit v1.2.3 From a4ab1f1026bdefa2f7434b288e2894f2efe81acb Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Tue, 6 Nov 2018 16:36:10 +0100 Subject: Update OS version numbers --- lib/kernel/test/gen_tcp_misc_SUITE.erl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index 04c0c48e3a..c47de58fae 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -1969,20 +1969,20 @@ recvtclass(_Config) -> {skip,{ipv6_not_supported,IFs}} end. -%% These version numbers are the highest noted in daily tests -%% where the test fails for a plausible reason, so -%% skip on that platform. +%% These version numbers are above the highest noted +%% in daily tests where the test fails for a plausible reason, +%% so skip on platforms of lower version, i.e they are future +%% versions where it is possible that it might not fail. %% -%% On newer versions it might be fixed, but we'll see about that -%% when machines with newer versions gets installed... -%% If the test still fails for a plausible reason these +%% When machines with newer versions gets installed, +%% if the test still fails for a plausible reason these %% version numbers simply should be increased. %% Or maybe we should change to only test on known good %% platforms - change {unix,_} to false? %% pktoptions is not supported for IPv4 recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); -recvtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0}); +recvtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); %% Using the option returns einval, so it is not implemented. recvtos_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); recvtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); @@ -1994,7 +1994,7 @@ recvtos_ok(_, _) -> false. %% pktoptions is not supported for IPv4 recvttl_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); -recvttl_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0}); +recvttl_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); %% Using the option returns einval, so it is not implemented. recvttl_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); recvttl_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); @@ -2005,7 +2005,7 @@ recvttl_ok(_, _) -> false. %% pktoptions is not supported for IPv6 recvtclass_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); -recvtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0}); +recvtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); recvtclass_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% Using the option returns einval, so it is not implemented. recvtclass_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); -- cgit v1.2.3 From 044d974adfe7f244374bf534b5dae1a90c58239c Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 23 Nov 2018 18:49:22 +0100 Subject: kernel: Fix bug in net_kernel for connection attempt from self net_kernel crashed due to invalid return from handle_info. Could only happen if erts_internal:new_connection fails due to non-atom Node or if Node is the own node name. --- lib/kernel/src/net_kernel.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index 3cf11fd7b1..a9dc77837e 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -808,7 +808,8 @@ handle_info({AcceptPid, {accept_pending,MyNode,Node,Address,Type}}, State) -> _:_ -> error_logger:error_msg("~n** Cannot get connection id for node ~w~n", [Node]), - AcceptPid ! {self(),{accept_pending,nok_pending}} + AcceptPid ! {self(),{accept_pending,nok_pending}}, + {noreply, State} end end; -- cgit v1.2.3 From 01fad41be5fb558b90a1381f8c64c5d49af529b3 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 27 Nov 2018 16:20:03 +0100 Subject: [logger] Add read_concurrency to options for logger ets table --- lib/kernel/src/logger_config.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_config.erl b/lib/kernel/src/logger_config.erl index 6bfe658552..5e9faf332c 100644 --- a/lib/kernel/src/logger_config.erl +++ b/lib/kernel/src/logger_config.erl @@ -31,7 +31,9 @@ -include("logger_internal.hrl"). new(Name) -> - _ = ets:new(Name,[set,protected,named_table,{write_concurrency,true}]), + _ = ets:new(Name,[set,protected,named_table, + {read_concurrency,true}, + {write_concurrency,true}]), ets:whereis(Name). delete(Tid,Id) -> -- cgit v1.2.3 From a99db2cf37ce08c07724645cc2d290c0fc574c81 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 27 Nov 2018 16:36:17 +0100 Subject: [logger] Document that macros are defined in logger.hrl --- lib/kernel/doc/src/logger.xml | 5 ++++- lib/kernel/doc/src/logger_chapter.xml | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index 2bcf137299..e0b95fa4ad 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -290,7 +290,10 @@ logger:error("error happened because: ~p", [Reason]). % Without macro

    Macros -

    The following macros are defined:

    +

    The following macros are defined in logger.hrl, which + is included in a module with the directive

    + + -include_lib("kernel/include/logger.hrl"). ?LOG_EMERGENCY(StringOrReport[,Metadata]) diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 1870d2ab79..458e61cef5 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -113,7 +113,10 @@ of functions on the form logger:Level/1,2,3, which are all shortcuts for - logger:log(Level,Arg1[,Arg2[,Arg3]]).

    + logger:log(Level,Arg1[,Arg2[,Arg3]]).

    +

    The macros are defined in logger.hrl, which is included + in a module with the directive

    + -include_lib("kernel/include/logger.hrl").

    The difference between using the macros and the exported functions is that macros add location (originator) information to the metadata, and performs lazy evaluation by wrapping the -- cgit v1.2.3 From 8c6c5bd7bea3f2787eceec1767325facd5236b17 Mon Sep 17 00:00:00 2001 From: Louis Pilfold Date: Tue, 27 Nov 2018 21:30:03 +0000 Subject: Correct typo in logger formatter docs --- lib/kernel/doc/src/logger.xml | 2 +- lib/kernel/doc/src/logger_formatter.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/logger.xml b/lib/kernel/doc/src/logger.xml index 2bcf137299..4f8fa17b51 100644 --- a/lib/kernel/doc/src/logger.xml +++ b/lib/kernel/doc/src/logger.xml @@ -1214,7 +1214,7 @@ logger:set_process_metadata(maps:merge(logger:get_process_metadata(), Meta)). logger:set_handler_config/2,3 - logger:updata_handler_config/2,3 + logger:update_handler_config/2,3 logger:update_formatter_config/2 diff --git a/lib/kernel/doc/src/logger_formatter.xml b/lib/kernel/doc/src/logger_formatter.xml index 24772fd6c4..d066e263df 100644 --- a/lib/kernel/doc/src/logger_formatter.xml +++ b/lib/kernel/doc/src/logger_formatter.xml @@ -303,7 +303,7 @@ exit_reason: "It crashed" logger:set_handler_config/2,3 - logger:updata_handler_config/2 + logger:update_handler_config/2 logger:update_formatter_config/2 -- cgit v1.2.3 From 0ad63bcc5523f0b560c843b3ed02c08e1369e522 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 27 Nov 2018 21:16:47 +0100 Subject: erts: Fix bug in sendfile for active socket driver_select() was called after port had been killed by tcp_inet_sendfile() calling tcp_send_error(). --- lib/kernel/test/sendfile_SUITE.erl | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/sendfile_SUITE.erl b/lib/kernel/test/sendfile_SUITE.erl index 0c0b1cbcb6..062209fa29 100644 --- a/lib/kernel/test/sendfile_SUITE.erl +++ b/lib/kernel/test/sendfile_SUITE.erl @@ -341,7 +341,9 @@ t_sendfile_closeduring(Config) -> -1 end, - ok = sendfile_send({127,0,0,1}, Send, 0). + ok = sendfile_send({127,0,0,1}, Send, 0, [{active,false}]), + ok = sendfile_send({127,0,0,1}, Send, 0, [{active,true}]). + t_sendfile_crashduring(Config) -> Filename = proplists:get_value(big_file, Config), @@ -409,12 +411,16 @@ sendfile_send(Send) -> sendfile_send(Host, Send) -> sendfile_send(Host, Send, []). sendfile_send(Host, Send, Orig) -> + sendfile_send(Host, Send, Orig, [{active,false}]). + +sendfile_send(Host, Send, Orig, SockOpts) -> + SFServer = spawn_link(?MODULE, sendfile_server, [self(), Orig]), receive {server, Port} -> - {ok, Sock} = gen_tcp:connect(Host, Port, - [binary,{packet,0}, - {active,false}]), + Opts = [binary,{packet,0}|SockOpts], + io:format("connect with opts = ~p\n", [Opts]), + {ok, Sock} = gen_tcp:connect(Host, Port, Opts), Data = case proplists:get_value(arity,erlang:fun_info(Send)) of 1 -> Send(Sock); -- cgit v1.2.3 From 06c46662022efb6892c036eeaf8c957e51fc17f7 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 28 Nov 2018 20:31:37 +0100 Subject: erts: Fix unexpected inet_reply message from failing file:sendfile A failing file:sendfile call would often send a message {inet_reply, Port, {error, Reason}} that would pollute the mailbox of the calling process. TCP_REQ_SENDFILE has its own reply messages format {sendfile, _, _} and does not expect an inet_reply message. Solution: Suppress inet_reply error message if TCP_ADDF_SENDFILE is set. --- lib/kernel/test/sendfile_SUITE.erl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/sendfile_SUITE.erl b/lib/kernel/test/sendfile_SUITE.erl index 062209fa29..ad060aa05c 100644 --- a/lib/kernel/test/sendfile_SUITE.erl +++ b/lib/kernel/test/sendfile_SUITE.erl @@ -342,8 +342,20 @@ t_sendfile_closeduring(Config) -> end, ok = sendfile_send({127,0,0,1}, Send, 0, [{active,false}]), - ok = sendfile_send({127,0,0,1}, Send, 0, [{active,true}]). + [] = flush(), + ok = sendfile_send({127,0,0,1}, Send, 0, [{active,true}]), + [] = flush(), + ok. + +flush() -> + lists:reverse(flush([])). +flush(Acc) -> + receive M -> + flush([M | Acc]) + after 0 -> + Acc + end. t_sendfile_crashduring(Config) -> Filename = proplists:get_value(big_file, Config), -- cgit v1.2.3 From 38c038b16835726c1273c57ed806d304498b3aa7 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 29 Nov 2018 13:53:43 +0100 Subject: [logger] Unlink simple handler before terminating This avoids an unexpected EXIT message with reason normal beeing printed from logger_server when the simple handler is replaced. It will still be printed if it exists unexpectedly. --- lib/kernel/src/logger_simple_h.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_simple_h.erl b/lib/kernel/src/logger_simple_h.erl index 8b51dd8569..fe181722f3 100644 --- a/lib/kernel/src/logger_simple_h.erl +++ b/lib/kernel/src/logger_simple_h.erl @@ -50,7 +50,6 @@ removing_handler(#{id:=simple}) -> ok; Pid -> Ref = erlang:monitor(process,Pid), - unlink(Pid), Pid ! stop, receive {'DOWN',Ref,process,Pid,_} -> ok @@ -99,7 +98,11 @@ loop(Buffer) -> replay_buffer(Buffer); _ -> ok - end; + end, + %% Before stopping, we unlink the logger process to avoid + %% an unexpected EXIT message + unlink(whereis(logger)), + ok; {log,#{msg:=_,meta:=#{time:=_}}=Log} -> do_log(Log), loop(update_buffer(Buffer,Log)); -- cgit v1.2.3 From 132a7d957dc85182c17647a857bb3655e1e7016e Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Thu, 29 Nov 2018 16:00:23 +0100 Subject: Prepare release --- lib/kernel/doc/src/notes.xml | 16 +++++++++++++++ lib/kernel/src/kernel.appup.src | 45 ++++++++++++++++++++++++++++++++--------- lib/kernel/vsn.mk | 2 +- 3 files changed, 53 insertions(+), 10 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 8188ede6a2..bfbaf6ef3e 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,22 @@

    This document describes the changes made to the Kernel application.

    +
    Kernel 6.1.1 + +
    Fixed Bugs and Malfunctions + + +

    + Fix bug causing net_kernel process crash on connection + attempt from node with name identical to local node.

    +

    + Own Id: OTP-15438 Aux Id: ERL-781

    +
    +
    +
    + +
    +
    Kernel 6.1
    Fixed Bugs and Malfunctions diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src index 305a1c788c..e73cea8351 100644 --- a/lib/kernel/src/kernel.appup.src +++ b/lib/kernel/src/kernel.appup.src @@ -16,13 +16,40 @@ %% limitations under the License. %% %% %CopyrightEnd% +%% +%% We allow upgrade from, and downgrade to all previous +%% versions from the following OTP releases: +%% - OTP 20 +%% - OTP 21 +%% +%% We also allow upgrade from, and downgrade to all +%% versions that have branched off from the above +%% stated previous versions. +%% {"%VSN%", - %% Up from - max one major revision back - [{<<"5\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-20.0 - {<<"5\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-20.1+ - {<<"6\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-21 - %% Down to - max one major revision back - [{<<"5\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-20.0 - {<<"5\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-20.1+ - {<<"6\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-21 -}. + [{<<"^5\\.3$">>,[restart_new_emulator]}, + {<<"^5\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^5\\.3\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^5\\.4$">>,[restart_new_emulator]}, + {<<"^5\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^5\\.4\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^5\\.4\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^5\\.4\\.3(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^6\\.0$">>,[restart_new_emulator]}, + {<<"^6\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^6\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^6\\.1$">>,[restart_new_emulator]}, + {<<"^6\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}], + [{<<"^5\\.3$">>,[restart_new_emulator]}, + {<<"^5\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^5\\.3\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^5\\.4$">>,[restart_new_emulator]}, + {<<"^5\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^5\\.4\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^5\\.4\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^5\\.4\\.3(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^6\\.0$">>,[restart_new_emulator]}, + {<<"^6\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^6\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^6\\.1$">>,[restart_new_emulator]}, + {<<"^6\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}]}. diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index df95174c9f..35e6a16a49 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 6.1 +KERNEL_VSN = 6.1.1 -- cgit v1.2.3 From c64420ee0c50958c61958fcec4947fbbe721a661 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 30 Nov 2018 17:45:18 +0100 Subject: [logger] Re-create log file if deleted This is an update to logger_std_h, which makes it play well with tools like logrotate. --- lib/kernel/src/logger_std_h.erl | 45 +++++++++++++++++++++++----------- lib/kernel/test/logger_std_h_SUITE.erl | 23 ++++++++++++++--- 2 files changed, 51 insertions(+), 17 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index ebe741e331..63d1dbaba2 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -328,27 +328,30 @@ file_ctrl_loop(Fd, DevName, Synced, receive %% asynchronous event {log,Bin} -> - Result = write_to_dev(Fd, Bin, DevName, PrevWriteResult, HandlerName), - file_ctrl_loop(Fd, DevName, false, + Fd1 = ensure(Fd, DevName), + Result = write_to_dev(Fd1, Bin, DevName, PrevWriteResult, HandlerName), + file_ctrl_loop(Fd1, DevName, false, Result, PrevSyncResult, HandlerName); %% synchronous event {{log,Bin},{From,MRef}} -> - check_exist(Fd, DevName), - Result = write_to_dev(Fd, Bin, DevName, PrevWriteResult, HandlerName), + Fd1 = ensure(Fd, DevName), + Result = write_to_dev(Fd1, Bin, DevName, PrevWriteResult, HandlerName), From ! {MRef,ok}, - file_ctrl_loop(Fd, DevName, false, + file_ctrl_loop(Fd1, DevName, false, Result, PrevSyncResult, HandlerName); filesync -> - Result = sync_dev(Fd, DevName, Synced, PrevSyncResult, HandlerName), - file_ctrl_loop(Fd, DevName, true, + Fd1 = ensure(Fd, DevName), + Result = sync_dev(Fd1, DevName, Synced, PrevSyncResult, HandlerName), + file_ctrl_loop(Fd1, DevName, true, PrevWriteResult, Result, HandlerName); {filesync,{From,MRef}} -> - Result = sync_dev(Fd, DevName, Synced, PrevSyncResult, HandlerName), + Fd1 = ensure(Fd, DevName), + Result = sync_dev(Fd1, DevName, Synced, PrevSyncResult, HandlerName), From ! {MRef,ok}, - file_ctrl_loop(Fd, DevName, true, + file_ctrl_loop(Fd1, DevName, true, PrevWriteResult, Result, HandlerName); stop -> @@ -356,11 +359,25 @@ file_ctrl_loop(Fd, DevName, Synced, stopped end. -check_exist(DevName, DevName) when is_atom(DevName) -> - ok; -check_exist(_Fd, FileName) -> - _ = spawn_link(fun() -> {ok,_} = file:read_file_info(FileName) end), - ok. +%% In order to play well with tools like logrotate, we need to be able +%% to re-create the file if it has disappeared (e.g. if rotated by +%% logrotate) +ensure(Fd,DevName) when is_atom(DevName) -> + Fd; +ensure(Fd,FileName) -> + case file:read_file_info(FileName) of + {ok,_} -> + Fd; + _ -> + _ = file:close(Fd), + _ = file:close(Fd), % delayed_write cause close not to close + case do_open_log_file({file,FileName}) of + {ok,Fd1} -> + Fd1; + Error -> + exit({could_not_reopen_file,Error}) + end + end. write_to_dev(DevName, Bin, _DevName, _PrevWriteResult, _HandlerName) when is_atom(DevName) -> diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index a1159f280c..eb17a6d857 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -136,7 +136,8 @@ all() -> mem_kill_new, mem_kill_std, restart_after, - handler_requests_under_load + handler_requests_under_load, + recreate_deleted_log ]. add_remove_instance_tty(_Config) -> @@ -1255,6 +1256,22 @@ handler_requests_under_load(Config) -> handler_requests_under_load(cleanup, _Config) -> ok = stop_handler(?MODULE). +recreate_deleted_log(Config) -> + {Log,_HConfig,_StdHConfig} = + start_handler(?MODULE, ?FUNCTION_NAME, Config), + logger:notice("first",?domain), + logger_std_h:filesync(?MODULE), + ok = file:rename(Log,Log++".old"), + logger:notice("second",?domain), + logger_std_h:filesync(?MODULE), + {ok,<<"first\n">>} = file:read_file(Log++".old"), + {ok,<<"second\n">>} = file:read_file(Log), + ok. +recreate_deleted_log(cleanup, _Config) -> + ok = stop_handler(?MODULE). + +%%%----------------------------------------------------------------- +%%% send_requests(HName, TO, Reqs = [{Req,Res}|Rs]) -> receive {From,finish} -> @@ -1276,8 +1293,8 @@ send_requests(HName, TO, Reqs = [{Req,Res}|Rs]) -> %%%----------------------------------------------------------------- %%% -start_handler(Name, TTY, Config) when TTY == standard_io; - TTY == standard_error-> +start_handler(Name, TTY, _Config) when TTY == standard_io; + TTY == standard_error-> ok = logger:add_handler(Name, logger_std_h, #{config => #{type => TTY}, -- cgit v1.2.3 From 754e2022059864f2c4025ab67da759bde922a066 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 4 Dec 2018 10:10:37 +0100 Subject: erts: Fix copy of literal msg during gc A copy has to be made of the message as there is a trace token. There was a bug where the actual message was incorrectly modified even if it was a literal. --- lib/kernel/test/seq_trace_SUITE.erl | 39 ++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/seq_trace_SUITE.erl b/lib/kernel/test/seq_trace_SUITE.erl index cf4bf11328..ee8f4e94f8 100644 --- a/lib/kernel/test/seq_trace_SUITE.erl +++ b/lib/kernel/test/seq_trace_SUITE.erl @@ -25,7 +25,8 @@ -export([token_set_get/1, tracer_set_get/1, print/1, send/1, distributed_send/1, recv/1, distributed_recv/1, trace_exit/1, distributed_exit/1, call/1, port/1, - match_set_seq_token/1, gc_seq_token/1, label_capability_mismatch/1]). + match_set_seq_token/1, gc_seq_token/1, label_capability_mismatch/1, + send_literal/1]). %% internal exports -export([simple_tracer/2, one_time_receiver/0, one_time_receiver/1, @@ -44,7 +45,7 @@ suite() -> {timetrap,{minutes,1}}]. all() -> - [token_set_get, tracer_set_get, print, send, + [token_set_get, tracer_set_get, print, send, send_literal, distributed_send, recv, distributed_recv, trace_exit, distributed_exit, call, port, match_set_seq_token, gc_seq_token, label_capability_mismatch]. @@ -158,23 +159,51 @@ do_print(TsType) -> {0,{print,_,_,[],print3}, Ts1}] = stop_tracer(2), check_ts(TsType, Ts0), check_ts(TsType, Ts1). - + send(Config) when is_list(Config) -> lists:foreach(fun do_send/1, ?TIMESTAMP_MODES). do_send(TsType) -> + do_send(TsType, send). + +do_send(TsType, Msg) -> seq_trace:reset_trace(), start_tracer(), Receiver = spawn(?MODULE,one_time_receiver,[]), Label = make_ref(), seq_trace:set_token(label,Label), set_token_flags([send, TsType]), - Receiver ! send, + Receiver ! Msg, Self = self(), seq_trace:reset_trace(), - [{Label,{send,_,Self,Receiver,send}, Ts}] = stop_tracer(1), + [{Label,{send,_,Self,Receiver,Msg}, Ts}] = stop_tracer(1), check_ts(TsType, Ts). +%% This testcase tests that we do not segfault when we have a +%% literal as the message and the message is copied onto the +%% heap during a GC. +send_literal(Config) when is_list(Config) -> + lists:foreach(fun do_send_literal/1, + [atom, make_ref(), ets:new(hej,[]), 1 bsl 64, + "gurka", {tuple,test,with,#{}}, #{}]). + +do_send_literal(Msg) -> + N = 10000, + seq_trace:reset_trace(), + start_tracer(), + Label = make_ref(), + seq_trace:set_token(label,Label), + set_token_flags([send, 'receive', no_timestamp]), + Receiver = spawn_link(fun() -> receive ok -> ok end end), + [Receiver ! Msg || _ <- lists:seq(1, N)], + erlang:garbage_collect(Receiver), + [Receiver ! Msg || _ <- lists:seq(1, N)], + erlang:garbage_collect(Receiver), + Self = self(), + seq_trace:reset_trace(), + [{Label,{send,_,Self,Receiver,Msg}, Ts} | _] = stop_tracer(N), + check_ts(no_timestamp, Ts). + distributed_send(Config) when is_list(Config) -> lists:foreach(fun do_distributed_send/1, ?TIMESTAMP_MODES). -- cgit v1.2.3 From 0b3ad939020020988e96ff8087aa770b99d1ebb7 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Wed, 5 Dec 2018 15:35:53 +0100 Subject: Update Linux version for pktoptions TTL --- lib/kernel/test/gen_tcp_misc_SUITE.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index c47de58fae..244bd7e2a0 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -1998,8 +1998,9 @@ recvttl_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); %% Using the option returns einval, so it is not implemented. recvttl_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); recvttl_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); +%% Does not return any value - not implemented for pktoptions +recvttl_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {2,7,0}); %% -recvttl_ok({unix,linux}, _) -> true; recvttl_ok({unix,_}, _) -> true; recvttl_ok(_, _) -> false. -- cgit v1.2.3 From a2ca3bd78fc002dd1e6533c191e44092cc3aa949 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Mon, 10 Dec 2018 11:07:05 +0100 Subject: Prepare release --- lib/kernel/doc/src/notes.xml | 101 ++++++++++++++++++++++++++++++++++++++++ lib/kernel/src/kernel.appup.src | 6 ++- lib/kernel/vsn.mk | 2 +- 3 files changed, 106 insertions(+), 3 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index bfbaf6ef3e..021ecfa40d 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,107 @@

    This document describes the changes made to the Kernel application.

    +
    Kernel 6.2 + +
    Fixed Bugs and Malfunctions + + +

    + A new function, logger:update_handler_config/3 is + added, and the handler callback changing_config + now has a new argument, SetOrUpdate, which + indicates if the configuration change comes from + set_handler_config/2,3 or + update_handler_config/2,3.

    +

    + This allows the handler to consistently merge the new + configuration with the old (if the change comes from + update_handler_config/2,3) or with the default (if + the change comes from set_handler_config/2,3).

    +

    + The built-in handlers logger_std_h and + logger_disk_log_h are updated accordingly. A bug + which could cause inconsistency between the handlers' + internal state and the stored configuration is also + corrected.

    +

    + *** POTENTIAL INCOMPATIBILITY ***

    +

    + Own Id: OTP-15364

    +
    + +

    + Fix fallback when custom erl_epmd client does not + implement address_please.

    +

    + Own Id: OTP-15388 Aux Id: PR-1983

    +
    + +

    + The logger ets table did not have the + read_concurrency option. This is now added.

    +

    + Own Id: OTP-15453 Aux Id: ERL-782

    +
    + +

    + During system start, logger has a simple handler which + prints to stdout. After the kernel supervision is + started, this handler is removed and replaced by the + default handler. Due to a bug, logger earlier issued a + debug printout saying it received an unexpected message, + which was the EXIT message from the simple handler's + process. This is now corrected. The simple handler's + process now unlinks from the logger process before + terminating.

    +

    + Own Id: OTP-15466 Aux Id: ERL-788

    +
    + +

    + The logger handler logger_std_h would not + re-create it's log file if it was removed. Due to this it + could not be used with tools like 'logrotate'. This is + now corrected.

    +

    + Own Id: OTP-15469

    +
    +
    +
    + + +
    Improvements and New Features + + +

    + A function inet:getifaddrs/1 that takes a list + with a namespace option has been added, for platforms + that support that feature, for example Linux (only?).

    +

    + Own Id: OTP-15121 Aux Id: ERIERL-189, PR-1974

    +
    + +

    Added the nopush option for TCP sockets, which + corresponds to TCP_NOPUSH on *BSD and + TCP_CORK on Linux.

    +

    This is also used internally in file:sendfile + to reduce latency on subsequent send operations.

    +

    + Own Id: OTP-15357 Aux Id: ERL-698

    +
    + +

    + Optimize handling of send_delay for tcp sockes to better + work with the new pollthread implementation introduced in + OTP-21.

    +

    + Own Id: OTP-15471 Aux Id: ERIERL-229

    +
    +
    +
    + +
    +
    Kernel 6.1.1
    Fixed Bugs and Malfunctions diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src index e73cea8351..ccf0a82ced 100644 --- a/lib/kernel/src/kernel.appup.src +++ b/lib/kernel/src/kernel.appup.src @@ -39,7 +39,8 @@ {<<"^6\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, {<<"^6\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, {<<"^6\\.1$">>,[restart_new_emulator]}, - {<<"^6\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}], + {<<"^6\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^6\\.1\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}], [{<<"^5\\.3$">>,[restart_new_emulator]}, {<<"^5\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, {<<"^5\\.3\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, @@ -52,4 +53,5 @@ {<<"^6\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, {<<"^6\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, {<<"^6\\.1$">>,[restart_new_emulator]}, - {<<"^6\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}]}. + {<<"^6\\.1\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^6\\.1\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}]}. diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 35e6a16a49..4b43c6ae9d 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 6.1.1 +KERNEL_VSN = 6.2 -- cgit v1.2.3