aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel/src')
-rw-r--r--lib/kernel/src/file.erl2
-rw-r--r--lib/kernel/src/kernel.app.src5
-rw-r--r--lib/kernel/src/kernel.erl14
-rw-r--r--lib/kernel/src/logger.erl271
-rw-r--r--lib/kernel/src/logger_config.erl2
-rw-r--r--lib/kernel/src/logger_internal.hrl2
-rw-r--r--lib/kernel/src/logger_server.erl24
-rw-r--r--lib/kernel/src/logger_simple.erl69
8 files changed, 217 insertions, 172 deletions
diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl
index 57d8fc7a15..1d4e37196c 100644
--- a/lib/kernel/src/file.erl
+++ b/lib/kernel/src/file.erl
@@ -69,7 +69,7 @@
%% Types that can be used from other modules -- alphabetically ordered.
-export_type([date_time/0, fd/0, file_info/0, filename/0, filename_all/0,
- io_device/0, name/0, name_all/0, posix/0]).
+ io_device/0, mode/0, name/0, name_all/0, posix/0]).
%%% Includes and defines
-include("file_int.hrl").
diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src
index afffcd156e..23ac5b3444 100644
--- a/lib/kernel/src/kernel.app.src
+++ b/lib/kernel/src/kernel.app.src
@@ -140,7 +140,10 @@
inet_db,
pg2]},
{applications, []},
- {env, []},
+ {env, [{logger_level, info},
+ {logger_sasl_compatible, false},
+ {logger_log_progress, false}
+ ]},
{mod, {kernel, []}},
{runtime_dependencies, ["erts-10.0", "stdlib-3.5", "sasl-3.0"]}
]
diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl
index ae982c1741..b0e8c00bbf 100644
--- a/lib/kernel/src/kernel.erl
+++ b/lib/kernel/src/kernel.erl
@@ -30,17 +30,13 @@
%%% Callback functions for the kernel application.
%%%-----------------------------------------------------------------
start(_, []) ->
+ %% Setup the logger and configure the kernel logger environment
+ ok = logger:internal_init_logger(),
case supervisor:start_link({local, kernel_sup}, kernel, []) of
{ok, Pid} ->
ok = erl_signal_handler:start(),
- %% add error handler
- case logger:setup_standard_handler() of
- ok -> {ok, Pid, []};
- Error ->
- %% Not necessary since the node will crash anyway:
- exit(Pid, shutdown),
- Error
- end;
+ ok = logger:add_handlers(kernel),
+ {ok, Pid, []};
Error -> Error
end.
@@ -147,7 +143,7 @@ init([]) ->
case init:get_argument(mode) of
{ok, [["minimal"]]} ->
{ok, {SupFlags,
- [Code, File, StdError, User, Config, RefC, SafeSup, LoggerSup]}};
+ [Code, File, StdError, User, LoggerSup, Config, RefC, SafeSup]}};
_ ->
Rpc = #{id => rex,
start => {rpc, start_link, []},
diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl
index 98a9937111..9a6719923e 100644
--- a/lib/kernel/src/logger.erl
+++ b/lib/kernel/src/logger.erl
@@ -40,14 +40,17 @@
set_module_level/2, reset_module_level/1,
set_logger_config/1, set_logger_config/2,
set_handler_config/2, set_handler_config/3,
- get_logger_config/0, get_handler_config/1]).
+ get_logger_config/0, get_handler_config/1,
+ add_handlers/1]).
+
+%% Private configuration
+-export([internal_init_logger/0]).
%% Misc
-export([compare_levels/2]).
-export([set_process_metadata/1, update_process_metadata/1,
unset_process_metadata/0, get_process_metadata/0]).
-export([i/0, i/1]).
--export([setup_standard_handler/0, replace_simple_handler/3]).
-export([limit_term/1, get_format_depth/0, get_max_size/0, get_utc_config/0]).
%% Basic report formatting
@@ -93,8 +96,10 @@
term() => term()}.
-type timestamp() :: integer().
+-type config_handler() :: {handler, handler_id(), module(), config()}.
+
-export_type([log/0,level/0,report/0,msg_fun/0,metadata/0,config/0,handler_id/0,
- filter_id/0,filter/0,filter_arg/0,filter_return/0]).
+ filter_id/0,filter/0,filter_arg/0,filter_return/0, config_handler/0]).
%%%-----------------------------------------------------------------
%%% API
@@ -504,118 +509,184 @@ print_module_levels({Module,Level}) ->
print_module_levels(ModuleLevels) ->
lists:map(fun print_module_levels/1, ModuleLevels).
--spec setup_standard_handler() -> ok | {error,term()}.
-setup_standard_handler() ->
- case get_logger_type() of
- {ok,silent} ->
- Level = get_logger_level(),
- ok = set_logger_config(level,Level),
- remove_handler(logger_simple);
- {ok,Type} ->
- Level = get_logger_level(),
- ok = set_logger_config(level,Level),
- Filters = get_logger_filters(),
- setup_standard_handler(Type,#{level=>Level,
- filter_default=>stop,
- filters=>Filters});
- Error ->
- Error
+-spec internal_init_logger() -> ok | {error,term()}.
+%% This function is responsible for config of the logger
+%% This is done before add_handlers because we want the
+%% logger settings to take effect before the kernel supervisor
+%% tree is started.
+internal_init_logger() ->
+ try
+ ok = logger:set_logger_config(level, get_logger_level()),
+ ok = logger:set_logger_config(filter_default, get_logger_filter_default()),
+
+ [case logger:add_logger_filter(Id, Filter) of
+ ok -> ok;
+ {error, Reason} -> throw(Reason)
+ end || {Id, Filter} <- get_logger_filters()],
+
+ _ = [[case logger:set_module_level(Module, Level) of
+ ok -> ok;
+ {error, Reason} -> throw(Reason)
+ end || Module <- Modules]
+ || {module_level, Level, Modules} <- get_logger_env()],
+
+ case logger:set_handler_config(logger_simple,filters,
+ get_default_handler_filters()) of
+ ok -> ok;
+ {error,{not_found,logger_simple}} -> ok
+ end,
+
+ init_kernel_handlers()
+ catch throw:Reason ->
+ ?LOG_ERROR("Invalid logger config: ~p", [Reason]),
+ {error, {bad_config, {kernel, Reason}}}
end.
--spec setup_standard_handler(Type,Config) -> ok | {error,term()} when
- Type :: tty | standard_io | standard_error | {file,File} |
- {file,File,Modes} | {disk_log,LogOpts} | false,
- File :: file:filename(),
- Modes :: [term()], % [file:mode()], or more specific?
- Config :: config(),
- LogOpts :: map().
-setup_standard_handler(false,#{level:=Level,filters:=Filters}) ->
- case set_handler_config(logger_simple,level,Level) of
- ok ->
- set_handler_config(logger_simple,filters,Filters);
- Error ->
- Error
- end;
-setup_standard_handler(Type,Config) ->
- {Module,TypeConfig} = get_type_config(Type),
- replace_simple_handler(?STANDARD_HANDLER,
- Module,
- maps:merge(Config,TypeConfig)).
-
--spec replace_simple_handler(Id,Module,Config) -> ok | {error,term()} when
- Id :: handler_id(),
- Module :: module(),
- Config :: config().
-replace_simple_handler(Id,Module,Config) ->
- _ = code:ensure_loaded(Module),
- DoBuffer = erlang:function_exported(Module,swap_buffer,2),
- case add_handler(Id,Module,Config#{wait_for_buffer=>DoBuffer}) of
- ok ->
- if DoBuffer ->
- {ok,Buffered} = logger_simple:get_buffer(),
- _ = remove_handler(logger_simple),
- Module:swap_buffer(?STANDARD_HANDLER,Buffered);
- true ->
- _ = remove_handler(logger_simple),
- ok
- end,
- ok;
- Error ->
- Error
+-spec init_kernel_handlers() -> ok | {error,term()}.
+%% Setup the kernel environment variables to be correct
+%% The actual handlers are started by a call to add_handlers.
+init_kernel_handlers() ->
+ try
+ case get_logger_type() of
+ {ok,silent} ->
+ ok = logger:remove_handler(logger_simple);
+ {ok,false} ->
+ ok;
+ {ok,Type} ->
+ init_default_config(Type)
+ end
+ catch throw:Reason ->
+ ?LOG_ERROR("Invalid default handler config: ~p", [Reason]),
+ {error, {bad_config, {kernel, Reason}}}
end.
+-spec add_handlers(Application) -> ok | {error,term()} when
+ Application :: atom();
+ (HandlerConfig) -> ok | {error,term()} when
+ HandlerConfig :: [config_handler()].
+%% This function is responsible for resolving the handler config
+%% and then starting the correct handlers. This is done after the
+%% kernel supervisor tree has been started as it needs the logger_sup.
+add_handlers(App) when is_atom(App) ->
+ add_handlers(application:get_env(App, logger, []));
+add_handlers(HandlerConfig) ->
+ try
+ check_logger_config(HandlerConfig),
+ DefaultAdded =
+ lists:foldl(
+ fun({handler, default = Id, Module, Config}, _)
+ when not is_map_key(filters, Config) ->
+ %% The default handler should have a couple of extra filters
+ %% set on it by default.
+ DefConfig = #{ filter_default => stop,
+ filters => get_default_handler_filters()},
+ setup_handler(Id, Module, maps:merge(DefConfig,Config)),
+ true;
+ ({handler, Id, Module, Config}, Default) ->
+ setup_handler(Id, Module, Config),
+ Default orelse Id == default;
+ (_, Default) -> Default
+ end, false, HandlerConfig),
+ %% If a default handler was added we try to remove the simple_logger
+ %% If the simple logger exists it will replay its log events
+ %% to the handler(s) added in the fold above.
+ _ = [case logger:remove_handler(logger_simple) of
+ ok -> ok;
+ {error,{not_found,logger_simple}} -> ok
+ end || DefaultAdded],
+ ok
+ catch throw:Reason ->
+ ?LOG_ERROR("Invalid logger handler config: ~p", [Reason]),
+ {error, {bad_config, {handler, Reason}}}
+ end.
+
+setup_handler(Id, Module, Config) ->
+ case logger:add_handler(Id, Module, Config) of
+ ok -> ok;
+ {error, Reason} -> throw(Reason)
+ end.
+
+check_logger_config(_) ->
+ ok.
+
+-spec get_logger_type() -> {ok, standard_io | false | silent |
+ {file, file:name_all()} |
+ {file, file:name_all(), [file:mode()]}}.
get_logger_type() ->
- Type0 =
- case application:get_env(kernel, logger_dest) of
- undefined ->
- application:get_env(kernel, error_logger);
- T ->
- T
- end,
- case Type0 of
+ case application:get_env(kernel, error_logger) of
{ok, tty} ->
- {ok, tty};
+ {ok, standard_io};
{ok, {file, File}} when is_list(File) ->
{ok, {file, File}};
{ok, {file, File, Modes}} when is_list(File), is_list(Modes) ->
{ok, {file, File, Modes}};
- {ok, {disk_log, File}} when is_list(File) ->
- {ok, {disk_log, get_disk_log_config(File)}};
{ok, false} ->
{ok, false};
{ok, silent} ->
{ok, silent};
undefined ->
- {ok, tty}; % default value
+ case lists:member({handler,default,undefined}, get_logger_env()) of
+ true ->
+ {ok, false};
+ false ->
+ {ok, standard_io} % default value
+ end;
{ok, Bad} ->
- {error,{bad_config, {kernel, {logger_dest, Bad}}}}
+ throw({error_logger, Bad})
end.
-get_disk_log_config(File) ->
- Config1 =
- case application:get_env(kernel,logger_disk_log_maxfiles) of
- undefined -> #{};
- {ok,MF} -> #{max_no_files=>MF}
- end,
- Config2 =
- case application:get_env(kernel,logger_disk_log_maxbytes) of
- undefined -> Config1;
- {ok,MB} -> Config1#{max_no_bytes=>MB}
- end,
- Config3 =
- case application:get_env(kernel,logger_disk_log_type) of
- undefined -> Config2;
- {ok,T} -> Config1#{type=>T}
- end,
- Config3#{file=>File}.
-
get_logger_level() ->
- case application:get_env(kernel,logger_level) of
- undefined -> info;
- {ok,Level} when ?IS_LEVEL(Level) -> Level
+ case application:get_env(kernel,logger_level,info) of
+ Level when ?IS_LEVEL(Level) ->
+ Level;
+ Level ->
+ throw({logger_level, Level})
+ end.
+
+get_logger_filter_default() ->
+ case lists:keyfind(filters,1,get_logger_env()) of
+ {filters,Default,_} ->
+ Default;
+ false ->
+ log
end.
get_logger_filters() ->
+ lists:foldl(
+ fun({filters, _, Filters}, _Acc) ->
+ Filters;
+ (_, Acc) ->
+ Acc
+ end, [], get_logger_env()).
+
+%% This function looks at the kernel logger environment
+%% and updates it so that the correct logger is configured
+init_default_config(Type) when Type==standard_io;
+ Type==standard_error;
+ element(1,Type)==file ->
+ Env = get_logger_env(),
+ DefaultConfig = #{logger_std_h=>#{type=>Type}},
+ NewLoggerEnv =
+ case lists:keyfind(default, 2, Env) of
+ {handler, default, Module, Config} ->
+ lists:map(
+ fun({handler, default, logger_std_h, _}) ->
+ %% Only want to add the logger_std_h config
+ %% if not configured by user AND the default
+ %% handler is still the logger_std_h.
+ {handler, default, Module, maps:merge(DefaultConfig,Config)};
+ (Other) ->
+ Other
+ end, Env);
+ _ ->
+ %% Nothing has been configured, use default
+ [{handler, default, logger_std_h, DefaultConfig} | Env]
+ end,
+ application:set_env(kernel, logger, NewLoggerEnv, [{timeout,infinity}]);
+init_default_config(Type) ->
+ throw({illegal_logger_type,Type}).
+
+get_default_handler_filters() ->
case application:get_env(kernel, logger_sasl_compatible, false) of
true ->
?DEFAULT_HANDLER_FILTERS([beam,erlang,otp]);
@@ -631,18 +702,8 @@ get_logger_filters() ->
Extra ++ ?DEFAULT_HANDLER_FILTERS([beam,erlang,otp,sasl])
end.
-get_type_config({disk_log,LogOpts}) ->
- {logger_disk_log_h,#{disk_log_opts=>LogOpts}};
-get_type_config(tty) ->
- %% This is only for backwards compatibility with error_logger and
- %% old kernel and sasl environment variables
- get_type_config(standard_io);
-get_type_config(Type) when Type==standard_io;
- Type==standard_error;
- element(1,Type)==file ->
- {logger_std_h,#{logger_std_h=>#{type=>Type}}};
-get_type_config(Type) ->
- {error,{illegal_logger_type,Type}}.
+get_logger_env() ->
+ application:get_env(kernel, logger, []).
%%%-----------------------------------------------------------------
-spec limit_term(term()) -> term().
diff --git a/lib/kernel/src/logger_config.erl b/lib/kernel/src/logger_config.erl
index 799aea9617..40dc1b1e1b 100644
--- a/lib/kernel/src/logger_config.erl
+++ b/lib/kernel/src/logger_config.erl
@@ -31,7 +31,7 @@
-include("logger_internal.hrl").
new(Name) ->
- _ = ets:new(Name,[set,protected,named_table]),
+ _ = ets:new(Name,[set,protected,named_table,{write_concurrency,true}]),
ets:whereis(Name).
delete(Tid,Id) ->
diff --git a/lib/kernel/src/logger_internal.hrl b/lib/kernel/src/logger_internal.hrl
index 8c0fc2725d..f9377259f3 100644
--- a/lib/kernel/src/logger_internal.hrl
+++ b/lib/kernel/src/logger_internal.hrl
@@ -22,7 +22,7 @@
-define(LOGGER_KEY,'$logger_config$').
-define(HANDLER_KEY,'$handler_config$').
-define(LOGGER_META_KEY,'$logger_metadata$').
--define(STANDARD_HANDLER, logger_std_h).
+-define(STANDARD_HANDLER, default).
-define(DEFAULT_HANDLER_FILTERS,
?DEFAULT_HANDLER_FILTERS([beam,erlang,otp])).
-define(DEFAULT_HANDLER_FILTERS(Domain),
diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl
index a7f302ac8f..268fd4f2f3 100644
--- a/lib/kernel/src/logger_server.erl
+++ b/lib/kernel/src/logger_server.erl
@@ -122,8 +122,7 @@ init([]) ->
logger_config:create(Tid,logger,LoggerConfig),
SimpleConfig0 = maps:merge(default_config(logger_simple),
#{filter_default=>stop,
- filters=>?DEFAULT_HANDLER_FILTERS,
- logger_simple=>#{buffer=>true}}),
+ filters=>?DEFAULT_HANDLER_FILTERS}),
%% If this fails, then the node should crash
{ok,SimpleConfig} =
logger_simple:adding_handler(logger_simple,SimpleConfig0),
@@ -139,12 +138,21 @@ handle_call({add_handler,Id,Module,HConfig}, _From, #state{tid=Tid}=State) ->
%% inform the handler
case call_h(Module,adding_handler,[Id,HConfig],{ok,HConfig}) of
{ok,HConfig1} ->
- logger_config:create(Tid,Id,Module,HConfig1),
- {ok,Config} = do_get_config(Tid,logger),
- Handlers = maps:get(handlers,Config,[]),
- do_set_config(Tid,logger,
- Config#{handlers=>[Id|Handlers]}),
- ok;
+ %% We know that the call_h would have loaded the module
+ %% if it existed, so it is safe here to call function_exported
+ %% to find out if this is a valid handler
+ case erlang:function_exported(Module, log, 2) of
+ true ->
+ logger_config:create(Tid,Id,Module,HConfig1),
+ {ok,Config} = do_get_config(Tid,logger),
+ Handlers = maps:get(handlers,Config,[]),
+ do_set_config(Tid,logger,
+ Config#{handlers=>[Id|Handlers]});
+ false ->
+ {error,{invalid_handler,
+ {function_not_exported,
+ {Module,log,2}}}}
+ end;
{error,HReason} ->
{error,{handler_not_added,HReason}}
end
diff --git a/lib/kernel/src/logger_simple.erl b/lib/kernel/src/logger_simple.erl
index a1b427b96c..5272455a2d 100644
--- a/lib/kernel/src/logger_simple.erl
+++ b/lib/kernel/src/logger_simple.erl
@@ -20,37 +20,18 @@
-module(logger_simple).
-export([adding_handler/2, removing_handler/2, log/2]).
--export([get_buffer/0]).
%% This module implements a simple handler for logger. It is the
%% default used during system start.
%%%-----------------------------------------------------------------
-%%% API
-get_buffer() ->
- case whereis(?MODULE) of
- undefined ->
- {error,noproc};
- Pid ->
- Ref = erlang:monitor(process,Pid),
- Pid ! {get_buffer,self()},
- receive
- {buffer,Buffer} ->
- erlang:demonitor(Ref,[flush]),
- {ok,Buffer};
- {'DOWN',Ref,process,Pid,Reason} ->
- {error,Reason}
- end
- end.
-
-%%%-----------------------------------------------------------------
%%% Logger callback
adding_handler(?MODULE,Config) ->
Me = self(),
case whereis(?MODULE) of
undefined ->
- {Pid,Ref} = spawn_opt(fun() -> init(Me,Config) end,
+ {Pid,Ref} = spawn_opt(fun() -> init(Me) end,
[link,monitor,{message_queue_data,off_heap}]),
receive
{'DOWN',Ref,process,Pid,Reason} ->
@@ -102,48 +83,44 @@ log(_,_) ->
%%%-----------------------------------------------------------------
%%% Process
-init(Starter,Config) ->
+init(Starter) ->
register(?MODULE,self()),
Starter ! {self(),started},
- BufferSize =
- case Config of
- #{?MODULE:=#{buffer:=true}} ->
- 10;
- _ ->
- infinity
- end,
- loop(#{buffer_size=>BufferSize,dropped=>0,buffer=>[]},infinity).
+ loop(#{buffer_size=>10,dropped=>0,buffer=>[]}).
-loop(Buffer,Timeout) ->
+loop(Buffer) ->
receive
stop ->
- ok;
- {get_buffer,From} ->
- loop(Buffer#{send_to=>From},0);
+ %% We replay the logger messages of there is
+ %% a default handler when the simple handler
+ %% is removed.
+ case logger:get_handler_config(default) of
+ {ok, _} ->
+ replay_buffer(Buffer);
+ _ ->
+ ok
+ end;
{log,#{msg:=_,meta:=#{time:=_}}=Log} ->
do_log(Log),
- loop(update_buffer(Buffer,Log),Timeout);
+ loop(update_buffer(Buffer,Log));
_ ->
%% Unexpected message - flush it!
- loop(Buffer,Timeout)
- after Timeout ->
- #{dropped:=D,buffer:=B,send_to:=Pid} = Buffer,
- LogList = lists:reverse(B) ++ drop_msg(D),
- Pid ! {buffer,LogList},
- loop(Buffer#{buffer_size=>infinity,
- dropped=>0,
- buffer=>[],
- send_to=>false},
- infinity)
+ loop(Buffer)
end.
-update_buffer(#{buffer_size:=infinity}=Buffer,_Log) ->
- Buffer;
update_buffer(#{buffer_size:=0,dropped:=D}=Buffer,_Log) ->
Buffer#{dropped=>D+1};
update_buffer(#{buffer_size:=S,buffer:=B}=Buffer,Log) ->
Buffer#{buffer_size=>S-1,buffer=>[Log|B]}.
+replay_buffer(#{ dropped := D, buffer := Buffer }) ->
+ lists:foreach(
+ fun F(#{msg := {Tag, Msg}} = L) when Tag =:= string; Tag =:= report ->
+ F(L#{ msg := Msg });
+ F(#{ level := Level, msg := Msg, meta := MD}) ->
+ logger:log(Level, Msg, MD)
+ end, lists:reverse(Buffer, drop_msg(D))).
+
drop_msg(0) ->
[];
drop_msg(N) ->