aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorSiri Hansen <[email protected]>2018-06-05 12:35:37 +0200
committerSiri Hansen <[email protected]>2018-06-08 14:41:51 +0200
commit437d46f34b141af8e6361bfc5b2fe8dab9d88efd (patch)
tree83e3cc4d4df0d4a8d6ed56c7b0bcecf290d0f34d /lib
parent983a945b84e15f58c3205c37b7bfcad18537325c (diff)
downloadotp-437d46f34b141af8e6361bfc5b2fe8dab9d88efd.tar.gz
otp-437d46f34b141af8e6361bfc5b2fe8dab9d88efd.tar.bz2
otp-437d46f34b141af8e6361bfc5b2fe8dab9d88efd.zip
[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()
Diffstat (limited to 'lib')
-rw-r--r--lib/kernel/src/logger_formatter.erl100
-rw-r--r--lib/kernel/src/logger_internal.hrl2
-rw-r--r--lib/kernel/test/logger_SUITE.erl2
-rw-r--r--lib/kernel/test/logger_formatter_SUITE.erl139
4 files changed, 200 insertions, 43 deletions
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) ->