aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/kernel/doc/src/logger_formatter.xml14
-rw-r--r--lib/kernel/src/logger_formatter.erl102
-rw-r--r--lib/kernel/test/logger_formatter_SUITE.erl63
3 files changed, 142 insertions, 37 deletions
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
- <seealso marker="stdlib:io#format-2">io:format/2</seealso>
+ <seealso marker="stdlib:io#format-2">io:format/2,3</seealso>
in STDLIB.</p>
+ <p><c>chars_limit</c> is a positive integer representing the
+ value of the option with the same name to be used when calling
+ <seealso marker="stdlib:io#format-3">io:format/3</seealso>. 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 <c>max_size</c>.</p>
+
<p><c>max_size</c> 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.</p>
+ have. If the formatted string is longer, after possibly
+ being limited by <c>depth</c> and/or <c>chars_limit</c>, it
+ will be truncated.</p>
<p><c>utc</c> 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()}},