aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/src/logger_formatter.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel/src/logger_formatter.erl')
-rw-r--r--lib/kernel/src/logger_formatter.erl175
1 files changed, 142 insertions, 33 deletions
diff --git a/lib/kernel/src/logger_formatter.erl b/lib/kernel/src/logger_formatter.erl
index 8e954f8d98..602c666cc7 100644
--- a/lib/kernel/src/logger_formatter.erl
+++ b/lib/kernel/src/logger_formatter.erl
@@ -20,6 +20,7 @@
-module(logger_formatter).
-export([format/2]).
+-export([check_config/1]).
-include("logger_internal.hrl").
@@ -38,7 +39,8 @@
max_size=>pos_integer() | unlimited,
depth=>pos_integer() | unlimited,
template=>template(),
- utc=>boolean()}.
+ time_designator=>byte(),
+ time_offset=>integer()|[byte()]}.
format(#{level:=Level,msg:=Msg0,meta:=Meta},Config0)
when is_map(Config0) ->
Config = add_default_config(Config0),
@@ -195,16 +197,12 @@ truncate(String,Size) ->
String
end.
-format_time(Timestamp,Config) when is_integer(Timestamp) ->
- {Date,Time,Micro} = timestamp_to_datetimemicro(Timestamp,Config),
- format_time(Date,Time,Micro);
-format_time(Other,_Config) ->
- %% E.g. a string
- to_string(Other).
-
-format_time({Y,M,D},{H,Min,S},Micro) ->
- io_lib:format("~4w-~2..0w-~2..0w ~2w:~2..0w:~2..0w.~6..0w",
- [Y,M,D,H,Min,S,Micro]).
+format_time(Timestamp,#{time_offset:=Offset,time_designator:=Des})
+ when is_integer(Timestamp) ->
+ SysTime = Timestamp + erlang:time_offset(microsecond),
+ calendar:system_time_to_rfc3339(SysTime,[{unit,microsecond},
+ {offset,Offset},
+ {time_designator,Des}]).
%% Assuming this is monotonic time in microseconds
timestamp_to_datetimemicro(Timestamp,Config) when is_integer(Timestamp) ->
@@ -212,12 +210,12 @@ timestamp_to_datetimemicro(Timestamp,Config) when is_integer(Timestamp) ->
Micro = SysTime rem 1000000,
Sec = SysTime div 1000000,
UniversalTime = erlang:posixtime_to_universaltime(Sec),
- {Date,Time} =
- case Config of
- #{utc:=true} -> UniversalTime;
- _ -> erlang:universaltime_to_localtime(UniversalTime)
+ {{Date,Time},UtcStr} =
+ case offset_to_utc(maps:get(time_offset,Config)) of
+ true -> {UniversalTime,"UTC "};
+ _ -> {erlang:universaltime_to_localtime(UniversalTime),""}
end,
- {Date,Time,Micro}.
+ {Date,Time,Micro,UtcStr}.
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);
@@ -230,9 +228,11 @@ maybe_add_legacy_header(Level,
#{time:=Timestamp}=Meta,
#{legacy_header:=true}=Config) ->
#{title:=Title}=MyMeta = add_legacy_title(Level,maps:get(?MODULE,Meta,#{})),
- {{Y,Mo,D},{H,Mi,S},Micro} = timestamp_to_datetimemicro(Timestamp,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(Config)]),
+ {{Y,Mo,D},{H,Mi,S},Micro,UtcStr} =
+ timestamp_to_datetimemicro(Timestamp,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]),
Meta#{?MODULE=>MyMeta#{header=>Header}};
maybe_add_legacy_header(_,Meta,_) ->
Meta.
@@ -256,20 +256,20 @@ month(10) -> "Oct";
month(11) -> "Nov";
month(12) -> "Dec".
-utcstr(#{utc:=true}) -> "UTC ";
-utcstr(_) -> "".
-
-add_default_config(#{utc:=_}=Config0) ->
+%% Ensure that all valid configuration parameters exist in the final
+%% configuration map
+add_default_config(Config0) ->
Default =
#{legacy_header=>false,
single_line=>true,
- chars_limit=>unlimited},
- MaxSize = get_max_size(maps:get(max_size,Config0,false)),
- Depth = get_depth(maps:get(depth,Config0,false)),
+ chars_limit=>unlimited,
+ time_designator=>$T},
+ MaxSize = get_max_size(maps:get(max_size,Config0,undefined)),
+ Depth = get_depth(maps:get(depth,Config0,undefined)),
+ Offset = get_offset(maps:get(time_offset,Config0,undefined)),
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()}).
+ depth=>Depth,
+ time_offset=>Offset})).
add_default_template(#{template:=_}=Config) ->
Config;
@@ -283,12 +283,121 @@ default_template(#{single_line:=true}) ->
default_template(_) ->
?DEFAULT_FORMAT_TEMPLATE.
-get_max_size(false) ->
- logger:get_max_size();
+get_max_size(undefined) ->
+ unlimited;
get_max_size(S) ->
max(10,S).
-get_depth(false) ->
- logger:get_format_depth();
+get_depth(undefined) ->
+ error_logger:get_format_depth();
get_depth(S) ->
max(5,S).
+
+get_offset(undefined) ->
+ utc_to_offset(get_utc_config());
+get_offset(Offset) ->
+ Offset.
+
+utc_to_offset(true) ->
+ "Z";
+utc_to_offset(false) ->
+ "".
+
+get_utc_config() ->
+ %% SASL utc_log overrides stdlib config - in order to have uniform
+ %% timestamps in log messages
+ case application:get_env(sasl, utc_log) of
+ {ok, Val} when is_boolean(Val) -> Val;
+ _ ->
+ case application:get_env(stdlib, utc_log) of
+ {ok, Val} when is_boolean(Val) -> Val;
+ _ -> false
+ end
+ end.
+
+offset_to_utc(Z) when Z=:=0; Z=:="z"; Z=:="Z" ->
+ true;
+offset_to_utc([$+|Tz]) ->
+ case io_lib:fread("~d:~d", Tz) of
+ {ok, [0, 0], []} ->
+ true;
+ _ ->
+ false
+ end;
+offset_to_utc(_) ->
+ false.
+
+check_config(Config) when is_map(Config) ->
+ do_check_config(maps:to_list(Config));
+check_config(Config) ->
+ {error,{invalid_formatter_config,?MODULE,Config}}.
+
+do_check_config([{Type,L}|Config]) when Type == chars_limit;
+ Type == depth;
+ Type == max_size ->
+ case check_limit(L) of
+ ok -> do_check_config(Config);
+ error -> {error,{invalid_formatter_config,?MODULE,{Type,L}}}
+ end;
+do_check_config([{single_line,SL}|Config]) when is_boolean(SL) ->
+ do_check_config(Config);
+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}}
+ end;
+do_check_config([{time_offset,Offset}|Config]) ->
+ case check_offset(Offset) of
+ ok ->
+ do_check_config(Config);
+ error ->
+ {error,{invalid_formatter_config,?MODULE,{time_offset,Offset}}}
+ end;
+do_check_config([{time_designator,Char}|Config]) when Char>=0, Char=<255 ->
+ case io_lib:printable_latin1_list([Char]) of
+ true ->
+ do_check_config(Config);
+ false ->
+ {error,{invalid_formatter_config,?MODULE,{time_designator,Char}}}
+ end;
+do_check_config([C|_]) ->
+ {error,{invalid_formatter_config,?MODULE,C}};
+do_check_config([]) ->
+ ok.
+
+check_limit(L) when is_integer(L), L>0 ->
+ ok;
+check_limit(unlimited) ->
+ ok;
+check_limit(_) ->
+ error.
+
+check_offset(I) when is_integer(I) ->
+ ok;
+check_offset(Tz) when Tz=:=""; Tz=:="Z"; Tz=:="z" ->
+ ok;
+check_offset([Sign|Tz]) when Sign=:=$+; Sign=:=$- ->
+ check_timezone(Tz);
+check_offset(_) ->
+ error.
+
+check_timezone(Tz) ->
+ try io_lib:fread("~d:~d", Tz) of
+ {ok, [_, _], []} ->
+ ok;
+ _ ->
+ error
+ catch _:_ ->
+ error
+ end.