aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel/src')
-rw-r--r--lib/kernel/src/Makefile3
-rw-r--r--lib/kernel/src/error_logger.erl15
-rw-r--r--lib/kernel/src/kernel.app.src2
-rw-r--r--lib/kernel/src/logger.erl82
-rw-r--r--lib/kernel/src/logger_disk_log_h.erl8
-rw-r--r--lib/kernel/src/logger_formatter.erl200
-rw-r--r--lib/kernel/src/logger_h_common.erl13
-rw-r--r--lib/kernel/src/logger_handler_watcher.erl113
-rw-r--r--lib/kernel/src/logger_server.erl4
-rw-r--r--lib/kernel/src/logger_std_h.erl8
-rw-r--r--lib/kernel/src/logger_sup.erl6
-rw-r--r--lib/kernel/src/net_kernel.erl76
12 files changed, 392 insertions, 138 deletions
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.erl b/lib/kernel/src/logger.erl
index 0a9b1672ec..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,
@@ -74,6 +75,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 +90,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 +116,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
@@ -352,9 +370,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 +384,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).
@@ -452,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(),
@@ -651,7 +709,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/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_formatter.erl b/lib/kernel/src/logger_formatter.erl
index 456b0c9e8d..b0d4adc14d 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()].
%%%-----------------------------------------------------------------
@@ -76,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;
@@ -119,78 +120,119 @@ 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) ->
- try io_lib:format(Format,Args,Opts)
- catch _:_ ->
- io_lib:format("FORMAT ERROR: ~tp - ~tp",[Format,Args],Opts)
- end;
-limit_size({Format0,Args},Depth,Opts) 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 _:_ ->
- limit_size({"FORMAT ERROR: ~tp - ~tp",[Format0,Args]},Depth,Opts)
+ 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) ->
@@ -225,22 +267,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 +323,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 +413,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 +501,26 @@ check_timezone(Tz) ->
catch _:_ ->
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".
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_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/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
diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl
index c4e1a0ce1e..ef92f9f4d1 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,32 +465,30 @@ 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);
%%
%% 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),
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};