aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/src/logger_formatter.erl
diff options
context:
space:
mode:
authorSiri Hansen <[email protected]>2018-06-28 14:52:11 +0200
committerSiri Hansen <[email protected]>2018-07-13 12:20:26 +0200
commit270d909696a753af022df72a404c73f2895b4a02 (patch)
tree83a8d9635dc7dc3320724c24dbfbaa106e0be806 /lib/kernel/src/logger_formatter.erl
parentc606b5659908618a4e54a1ac7af90f8f4e78c2c8 (diff)
downloadotp-270d909696a753af022df72a404c73f2895b4a02.tar.gz
otp-270d909696a753af022df72a404c73f2895b4a02.tar.bz2
otp-270d909696a753af022df72a404c73f2895b4a02.zip
[logger] Allow report callback with two arguments returning a string
If the report callback is a fun of arity 2, then the second argument is a map with the keys 'encoding', 'depth' and 'chars_limit', and the fun must return a string which is size limited according to the given depth and chars_limit. If the report callback is a fun of arity 1, then it must return a tuple containing a format string and a list of arguments. The formatter will produce the string, and limit it's size.
Diffstat (limited to 'lib/kernel/src/logger_formatter.erl')
-rw-r--r--lib/kernel/src/logger_formatter.erl151
1 files changed, 100 insertions, 51 deletions
diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl
index 456b0c9e8d..937e7f859b 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()].
%%%-----------------------------------------------------------------
@@ -119,65 +120,96 @@ 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) ->
+format_msg(Msg,_Meta,#{depth:=Depth,chars_limit:=CharsLimit,encoding:=Enc}) ->
+ limit_size(Msg, Depth, CharsLimit, Enc).
+
+limit_size(Msg,Depth,unlimited,Enc) ->
+ limit_size(Msg,Depth,[],Enc);
+limit_size(Msg,Depth,CharsLimit,Enc) when is_integer(CharsLimit) ->
+ limit_size(Msg,Depth,[{chars_limit,CharsLimit}],Enc);
+limit_size({Format,Args},unlimited,Opts,Enc) when is_list(Opts) ->
try io_lib:format(Format,Args,Opts)
catch _:_ ->
- io_lib:format("FORMAT ERROR: ~tp - ~tp",[Format,Args],Opts)
+ P = p(Enc),
+ io_lib:format("FORMAT ERROR: "++P++" - "++P,[Format,Args],Opts)
end;
-limit_size({Format0,Args},Depth,Opts) when is_integer(Depth) ->
+limit_size({Format0,Args},Depth,Opts,Enc) when is_integer(Depth) ->
try
Format1 = io_lib:scan_format(Format0, Args),
Format = limit_format(Format1, Depth),
io_lib:build_text(Format,Opts)
catch _:_ ->
- limit_size({"FORMAT ERROR: ~tp - ~tp",[Format0,Args]},Depth,Opts)
+ P = p(Enc),
+ limit_size({"FORMAT ERROR: "++P++" - "++P,[Format0,Args]},
+ Depth,Opts,Enc)
end.
limit_format([#{control_char:=C0}=M0|T], Depth) when C0 =:= $p;
@@ -225,22 +257,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 +313,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 +403,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 +491,17 @@ check_timezone(Tz) ->
catch _:_ ->
error
end.
+
+p(#{encoding:=Enc}) ->
+ p(Enc);
+p(latin1) ->
+ "~p";
+p(_) ->
+ "~tp".
+
+s(#{encoding:=Enc}) ->
+ s(Enc);
+s(latin1) ->
+ "~s";
+s(_) ->
+ "~ts".