File 2794-logger-Use-persistent_term-for-module-level-config.patch of Package erlang
From acfc294ad0ae9bbeb4787f70db49842d957821c6 Mon Sep 17 00:00:00 2001
From: Lukas Larsson <lukas@erlang.org>
Date: Wed, 21 Aug 2019 11:18:25 +0200
Subject: [PATCH 4/7] logger: Use persistent_term for module level config
Move the module level config into persistent_term for
faster and more scalable lookup.
---
lib/kernel/src/logger_config.erl | 94 ++++++++++++++++++++++------------------
lib/kernel/src/logger_server.erl | 10 +----
lib/kernel/test/logger_SUITE.erl | 15 ++++---
3 files changed, 63 insertions(+), 56 deletions(-)
diff --git a/lib/kernel/src/logger_config.erl b/lib/kernel/src/logger_config.erl
index 4b67325b69..31b5480cde 100644
--- a/lib/kernel/src/logger_config.erl
+++ b/lib/kernel/src/logger_config.erl
@@ -25,39 +25,51 @@
get/2, get/3,
create/3, set/3,
set_module_level/3,unset_module_level/2,
- get_module_level/1,cache_module_level/2,
+ get_module_level/1,
level_to_int/1]).
-include("logger_internal.hrl").
+-compile({inline,[get_primary_level/0,level_to_int/1]}).
+
+-define(LEVEL_TO_CACHE(Level),Level + 16#10).
+-define(PRIMARY_TO_CACHE(Level),Level).
+-define(IS_CACHED(Level),(Level =< ?LOG_ALL)).
+-define(CACHE_TO_LEVEL(Level),if ?IS_CACHED(Level) -> Level; true -> Level - 16#10 end).
+
+-define(IS_MODULE(Module),is_atom(Module) andalso Module =/= ?PRIMARY_KEY).
+
new(Name) ->
_ = ets:new(Name,[set,protected,named_table,
{read_concurrency,true},
{write_concurrency,true}]),
ets:whereis(Name).
-delete(Tid,Id) ->
- ets:delete(Tid,table_key(Id)).
+delete(Tid,What) ->
+ persistent_term:put({?MODULE,table_key(What)},undefined),
+ ets:delete(Tid,table_key(What)).
%% Optimized for speed.
-allow(Tid,Level,Module) ->
- LevelInt = level_to_int(Level),
- case ets:lookup(Tid,Module) of
- [{Module,{ModLevel,cached}}] when is_integer(ModLevel),
- LevelInt =< ModLevel ->
- less_or_equal_level(Level,ModLevel);
- [{Module,ModLevel}] when is_integer(ModLevel),
- LevelInt =< ModLevel ->
- less_or_equal_level(Level,ModLevel);
- [] ->
- logger_server:cache_module_level(Module),
- allow(Tid,Level);
- _ ->
- false
- end.
+allow(_Tid,Level,Module) ->
+ ModLevel =
+ case persistent_term:get({?MODULE,Module},undefined) of
+ undefined ->
+ %% This is where the module cache takes place. We insert the module level
+ %% plus 16 into the pt and then when looking it up we need to do a check
+ %% and subtraction.
+ %% The reason why we do this dance and not just wrap it all in a tuple
+ %% is because updates of immediates (i.e. small ints in this case)
+ %% is cheap even in pt, so we cannot put any complex terms in there.
+ IntLevel = get_primary_level(),
+ persistent_term:put({?MODULE,Module},?PRIMARY_TO_CACHE(IntLevel)),
+ IntLevel;
+ IntLevel ->
+ ?CACHE_TO_LEVEL(IntLevel)
+ end,
+ less_or_equal_level(Level,ModLevel).
allow(_Tid,Level) ->
- PrimaryLevelInt = persistent_term:get({?MODULE,?PRIMARY_KEY}, ?NOTICE),
+ PrimaryLevelInt = get_primary_level(),
less_or_equal_level(Level,PrimaryLevelInt).
less_or_equal_level(emergency,ModLevel) -> ?EMERGENCY =< ModLevel;
@@ -72,6 +84,9 @@ less_or_equal_level(debug,ModLevel) -> ?DEBUG =< ModLevel.
exist(Tid,What) ->
ets:member(Tid,table_key(What)).
+get_primary_level() ->
+ persistent_term:get({?MODULE,?PRIMARY_KEY},?NOTICE).
+
get(Tid,What) ->
case ets:lookup(Tid,table_key(What)) of
[{_,_,Config}] ->
@@ -108,14 +123,12 @@ set(Tid,proxy,Config) ->
ok;
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
primary ->
- _ = ets:select_delete(Tid,[{{'_',{'$1',cached}},
- [{'=/=','$1',LevelInt}],
- [true]}]),
ok = persistent_term:put({?MODULE,?PRIMARY_KEY}, LevelInt),
+ [persistent_term:put(Key, ?PRIMARY_TO_CACHE(LevelInt))
+ || {{?MODULE, Module} = Key,L} <- persistent_term:get(),
+ Module =/= ?PRIMARY_KEY, L > ?LOG_ALL],
ok;
_ ->
ok
@@ -123,29 +136,28 @@ set(Tid,What,Config) ->
ets:update_element(Tid,table_key(What),[{2,LevelInt},{3,Config}]),
ok.
-set_module_level(Tid,Modules,Level) ->
+set_module_level(_Tid,Modules,Level) ->
LevelInt = level_to_int(Level),
- [ets:insert(Tid,{Module,LevelInt}) || Module <- Modules],
+ [persistent_term:put({?MODULE,Module},?LEVEL_TO_CACHE(LevelInt)) || Module <- Modules],
ok.
-%% 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),
+%% We overwrite instead of delete because that is more efficient
+%% when using persistent_term
+unset_module_level(_Tid,all) ->
+ PrimaryLevel = get_primary_level(),
+ [persistent_term:put(Key, ?PRIMARY_TO_CACHE(PrimaryLevel))
+ || {{?MODULE, Module} = Key,_} <- persistent_term:get(), ?IS_MODULE(Module)],
ok;
-unset_module_level(Tid,Modules) ->
- [ets:delete(Tid,Module) || Module <- Modules],
+unset_module_level(_Tid,Modules) ->
+ PrimaryLevel = get_primary_level(),
+ [persistent_term:put({?MODULE,Module}, ?PRIMARY_TO_CACHE(PrimaryLevel)) || 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,?PRIMARY_KEY,2),
- ets:insert_new(Tid,{Module,{GlobalLevelInt,cached}}),
- ok.
+get_module_level(_Tid) ->
+ lists:sort(
+ [{Module,int_to_level(?CACHE_TO_LEVEL(Level))}
+ || {{?MODULE, Module},Level} <- persistent_term:get(),
+ ?IS_MODULE(Module), not ?IS_CACHED(Level)]).
level_to_int(none) -> ?LOG_NONE;
level_to_int(emergency) -> ?EMERGENCY;
diff --git a/lib/kernel/src/logger_server.erl b/lib/kernel/src/logger_server.erl
index 722246e82c..1ca9adbf19 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, unset_module_level/0,
- unset_module_level/1, cache_module_level/1,
+ unset_module_level/1,
set_config/2, set_config/3,
update_config/2, update_config/3,
update_formatter_config/2]).
@@ -104,9 +104,6 @@ unset_module_level(Modules) when is_list(Modules) ->
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) ->
case sanity_check(Owner,Key,Value) of
ok ->
@@ -333,10 +330,7 @@ handle_call({unset_module_level,Modules}, _From, #state{tid=Tid}=State) ->
{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}.
+ call_h_reply(Reply,State).
%% Interface for those who can't call the API - e.g. the emulator, or
%% places related to code loading.
diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl
index f8f3d27778..c6b563e4a9 100644
--- a/lib/kernel/test/logger_SUITE.erl
+++ b/lib/kernel/test/logger_SUITE.erl
@@ -478,14 +478,15 @@ set_application_level(cleanup,_Config) ->
ok.
cache_module_level(_Config) ->
- ok = logger:unset_module_level(?MODULE),
- [] = ets:lookup(?LOGGER_TABLE,?MODULE), %dirty - add API in logger_config?
+
+ %% This test does a lot of whitebox tests so be prepared for that
+ persistent_term:erase({logger_config,?MODULE}),
+
+ primary = persistent_term:get({logger_config,?MODULE}, primary),
?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?
- ok = logger:unset_module_level(?MODULE),
- [] = ets:lookup(?LOGGER_TABLE,?MODULE), %dirty - add API in logger_config?
+ 5 = persistent_term:get({logger_config,?MODULE}, primary),
+ logger:set_primary_config(level, info),
+ 6 = persistent_term:get({logger_config,?MODULE}, primary),
ok.
cache_module_level(cleanup,_Config) ->
--
2.16.4