fix: log date format by emqx_calendar
This commit is contained in:
parent
34ec0cc04b
commit
664ebcc192
|
@ -427,7 +427,7 @@ log.to = file
|
|||
## this level will be logged.
|
||||
##
|
||||
## Default: warning
|
||||
log.level = warning
|
||||
log.level = debug
|
||||
|
||||
## The dir for log files.
|
||||
##
|
||||
|
@ -471,9 +471,10 @@ log.formatter = text
|
|||
## %M: minute
|
||||
## %S: second
|
||||
## %6N: microseconds
|
||||
## %z: timezone
|
||||
## %3N: milliseconds
|
||||
## %z: timezone, [+|-]HH:MM or [+|-]HH:MM:SS. Depends on `log.formatter.text.date.timezone`
|
||||
## For example:
|
||||
## log.formatter.text.date.format = emqx-server-date: %Y-%m-%dT%H:%M:%S:%6N %z
|
||||
## log.formatter.text.date.format = emqx-server-date: %Y-%m-%dT%H:%M:%S.%6N %z
|
||||
## Default: rfc3339
|
||||
# log.formatter.text.date.format = rfc3339
|
||||
|
||||
|
@ -481,9 +482,9 @@ log.formatter = text
|
|||
## Only takes effect when log.formatter.text.date.format not rfc3339.
|
||||
##
|
||||
## Value: local | z | UTC TimeOffset
|
||||
## TimeOffset: [+|-]HH:MM:SS, for example: +08:00
|
||||
## z: UTC zulu timezone.
|
||||
## TimeOffset: [+|-]HH:MM or [+|-]HH:MM:SS, for example: +08:00.
|
||||
## local is the system local time timezone.
|
||||
## z: UTC zulu timezone.
|
||||
## Default: local
|
||||
# log.formatter.text.date.timezone = local
|
||||
|
||||
|
|
|
@ -530,13 +530,13 @@ end}.
|
|||
|
||||
%% @doc format logs as text, date format part
|
||||
{mapping, "log.formatter.text.date.format", "kernel.logger", [
|
||||
{default, rfc3339},
|
||||
{default, "rfc3339"},
|
||||
{datatype, string}
|
||||
]}.
|
||||
|
||||
%% @doc format logs as text, date format part. local or any UTC offset
|
||||
{mapping, "log.formatter.text.date.timezone", "kernel.logger", [
|
||||
{default, local},
|
||||
{default, "local"},
|
||||
{datatype, string}
|
||||
]}.
|
||||
|
||||
|
@ -642,7 +642,7 @@ end}.
|
|||
}};
|
||||
text ->
|
||||
DateFormat =
|
||||
case cuttlefish:conf_get("log.formatter.text.date.format", Conf) of
|
||||
case cuttlefish:conf_get("log.formatter.text.date.format", Conf, "rfc3339") of
|
||||
"rfc3339" ->
|
||||
rfc3339;
|
||||
DateStr ->
|
||||
|
@ -655,7 +655,9 @@ end}.
|
|||
DST(<<"%H", Tail/binary>>, Formatter) -> DST(Tail, [hour | Formatter]);
|
||||
DST(<<"%M", Tail/binary>>, Formatter) -> DST(Tail, [minute | Formatter]);
|
||||
DST(<<"%S", Tail/binary>>, Formatter) -> DST(Tail, [second | Formatter]);
|
||||
DST(<<"%6N", Tail/binary>>, Formatter) -> DST(Tail, [nano_second | Formatter]);
|
||||
DST(<<"%N", Tail/binary>>, Formatter) -> DST(Tail, [nanosecond | Formatter]);
|
||||
DST(<<"%3N", Tail/binary>>, Formatter) -> DST(Tail, [millisecond | Formatter]);
|
||||
DST(<<"%6N", Tail/binary>>, Formatter) -> DST(Tail, [microsecond | Formatter]);
|
||||
DST(<<"%z", Tail/binary>>, Formatter) -> DST(Tail, [timezone | Formatter]);
|
||||
DST(<<Char:8, Tail/binary>>, [Str | Formatter]) when is_list(Str) ->
|
||||
DST(Tail, [lists:append(Str, [Char]) | Formatter]);
|
||||
|
@ -665,7 +667,7 @@ end}.
|
|||
DateStrTrans(list_to_binary(DateStr), [])
|
||||
end,
|
||||
{DateOffsetStr, DateOffset} =
|
||||
case cuttlefish:conf_get("log.formatter.text.date.timezone", Conf) of
|
||||
case cuttlefish:conf_get("log.formatter.text.date.timezone", Conf, "local") of
|
||||
"z" ->
|
||||
{"z", 0};
|
||||
"Z" ->
|
||||
|
@ -688,9 +690,9 @@ end}.
|
|||
Str =
|
||||
case S of
|
||||
0 ->
|
||||
io_lib:format("~c~p:~p", [Sign, H, M]);
|
||||
io_lib:format("~c~2.10.0B:~2.10.0B", [Sign, H, M]);
|
||||
S ->
|
||||
io_lib:format("~c~p:~p:~p", [Sign, H, M, S])
|
||||
io_lib:format("~c~2.10.0B:~2.10.0B:~2.10.0B", [Sign, H, M, S])
|
||||
end,
|
||||
{Str, OffsetSecond};
|
||||
DateOStr ->
|
||||
|
|
|
@ -0,0 +1,512 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2019-2022 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-module(emqx_calendar).
|
||||
|
||||
-define(SECONDS_PER_MINUTE, 60).
|
||||
-define(SECONDS_PER_HOUR, 3600).
|
||||
-define(SECONDS_PER_DAY, 86400).
|
||||
-define(DAYS_PER_YEAR, 365).
|
||||
-define(DAYS_PER_LEAP_YEAR, 366).
|
||||
-define(DAYS_FROM_0_TO_1970, 719528).
|
||||
-define(SECONDS_FROM_0_TO_1970, ?DAYS_FROM_0_TO_1970 * ?SECONDS_PER_DAY).
|
||||
|
||||
-export([ formatter/1
|
||||
, format/3
|
||||
, format/4
|
||||
, format/5
|
||||
, parse/3
|
||||
, parse/4
|
||||
, offset_second/1
|
||||
]).
|
||||
|
||||
-export([test/0]).
|
||||
|
||||
-define(DATE_PART,
|
||||
[ year
|
||||
, month
|
||||
, day
|
||||
, hour
|
||||
, minute
|
||||
, second
|
||||
, nanosecond
|
||||
, millisecond
|
||||
, microsecond
|
||||
]).
|
||||
|
||||
-define(DATE_ZONE_NAME,
|
||||
[ timezone
|
||||
, timezone1
|
||||
, timezone2
|
||||
]).
|
||||
|
||||
formatter(FormatterStr) when is_list(FormatterStr) ->
|
||||
formatter(list_to_binary(FormatterStr));
|
||||
formatter(FormatterBin) when is_binary(FormatterBin) ->
|
||||
do_formatter(FormatterBin, []).
|
||||
|
||||
offset_second(Offset) ->
|
||||
do_offset_second(Offset).
|
||||
|
||||
format(Time, Unit, Formatter) ->
|
||||
format(Time, Unit, undefined, Formatter).
|
||||
|
||||
format(Time, Unit, Offset, FormatterBin) when is_binary(FormatterBin) ->
|
||||
format(Time, Unit, Offset, formatter(FormatterBin));
|
||||
format(Time, Unit, Offset, Formatter) ->
|
||||
do_format(Time, time_unit(Unit), offset_second(Offset), Formatter).
|
||||
|
||||
%% for logger format
|
||||
format(Time, Unit, Offset, Zones, Formatter) ->
|
||||
do_format(Time, time_unit(Unit), offset_second(Offset), Zones, Formatter).
|
||||
|
||||
test() ->
|
||||
Units = [second, millisecond, microsecond, nanosecond],
|
||||
Formatters = [
|
||||
<<"%Y-%m-%d %H:%M:%S:%N%z">>,
|
||||
<<"%Y-%m-%d %H:%M:%S:%N%:z">>,
|
||||
<<"%Y-%m-%d %H:%M:%S:%N%::z">>,
|
||||
<<"%Y-%m-%d %H:%M:%S:%N">>,
|
||||
<<"%Y-%m-%d %H:%M:%S:%3N">>,
|
||||
<<"%Y-%m-%d %H:%M:%S:%6N">>
|
||||
],
|
||||
Offsets = [
|
||||
0,
|
||||
8 * 3600,
|
||||
-8 * 3600,
|
||||
<<"+08:00">>,
|
||||
<<"+08:00:01">>,
|
||||
<<"+0802">>,
|
||||
<<"-08:00">>,
|
||||
<<"local">>
|
||||
],
|
||||
[
|
||||
[
|
||||
[begin
|
||||
Time = erlang:system_time(U),
|
||||
FormatDate = erlang:list_to_binary(format(Time, U, O, F)),
|
||||
PTime = emqx_calendar:parse(FormatDate, U, O, F),
|
||||
io:format("~p ~p ~p ~p ~n~p~n~p~n", [U, O, F, FormatDate, Time, PTime]),
|
||||
% Equal = (PTime == Time),
|
||||
% F2 =
|
||||
% fun() ->
|
||||
% Delta = Time - PTime,
|
||||
% DeltaFmt = erlang:list_to_binary(format(Delta, U, 0, <<"%Y-%m-%d %H:%M:%S:%N">>)),
|
||||
% io:format("delta ~p~n ~p~n-----~n", [Delta, DeltaFmt])
|
||||
% end,
|
||||
% Equal orelse F2()
|
||||
ok
|
||||
end||O <- Offsets]
|
||||
|| F <-Formatters]
|
||||
|| U <-Units],
|
||||
ok.
|
||||
|
||||
parse(DateStr, Unit, FormatterBin) ->
|
||||
parse(DateStr, Unit, 0, FormatterBin).
|
||||
|
||||
parse(DateStr, Unit, Offset, FormatterBin) when is_binary(FormatterBin) ->
|
||||
parse(DateStr, Unit, Offset, formatter(FormatterBin));
|
||||
parse(DateStr, Unit, Offset, Formatter) ->
|
||||
do_parse(DateStr, Unit, offset_second(Offset), Formatter).
|
||||
|
||||
do_parse(DateStr, Unit, Offset, Formatter) ->
|
||||
DateInfo = do_parse_date_str(DateStr, Formatter, #{}),
|
||||
{Precise, PrecisionUnit} = precise(DateInfo),
|
||||
Counter =
|
||||
fun
|
||||
(year, V, Res) ->
|
||||
Res + dy(V) * ?SECONDS_PER_DAY * Precise - (?SECONDS_FROM_0_TO_1970 * Precise);
|
||||
(month, V, Res) ->
|
||||
Res + nano_sm(V);
|
||||
(day, V, Res) ->
|
||||
Res + (V * ?SECONDS_PER_DAY * Precise);
|
||||
(hour, V, Res) ->
|
||||
Res + (V * ?SECONDS_PER_HOUR * Precise);
|
||||
(minute, V, Res) ->
|
||||
Res + (V * ?SECONDS_PER_MINUTE * Precise);
|
||||
(second, V, Res) ->
|
||||
Res + V * Precise;
|
||||
(millisecond, V, Res) ->
|
||||
case PrecisionUnit of
|
||||
millisecond ->
|
||||
Res + V;
|
||||
microsecond ->
|
||||
Res + (V * 1000);
|
||||
nanosecond ->
|
||||
Res + (V * 1000000)
|
||||
end;
|
||||
(microsecond, V, Res) ->
|
||||
case PrecisionUnit of
|
||||
microsecond ->
|
||||
Res + V;
|
||||
nanosecond ->
|
||||
Res + (V * 1000)
|
||||
end;
|
||||
(nanosecond, V, Res) ->
|
||||
Res + V;
|
||||
(parsed_offset, V, Res) ->
|
||||
Res - V
|
||||
end,
|
||||
Counter1 =
|
||||
fun(K,V,R) ->
|
||||
Res = Counter(K,V,R),
|
||||
% io:format("K, V, R = ~p, ~p, ~p, Res = ~p~n", [K, V, R, Res - R]),
|
||||
Res
|
||||
end,
|
||||
Count0 = maps:fold(Counter1, 0, DateInfo) - (?SECONDS_PER_DAY * Precise),
|
||||
Count =
|
||||
case maps:is_key(parsed_offset, DateInfo) of
|
||||
true ->
|
||||
Count0;
|
||||
false ->
|
||||
(Offset * Precise) + Count0
|
||||
end,
|
||||
erlang:convert_time_unit(Count, PrecisionUnit, Unit).
|
||||
|
||||
precise(#{nanosecond := _}) -> {1000_000_000, nanosecond};
|
||||
precise(#{microsecond := _}) -> {1000_000, microsecond};
|
||||
precise(#{millisecond := _}) -> {1000, millisecond};
|
||||
precise(#{second := _}) -> {1, second};
|
||||
precise(_) -> {1, second}.
|
||||
|
||||
do_parse_date_str(<<>>, _, Result) -> Result;
|
||||
do_parse_date_str(_, [], Result) -> Result;
|
||||
do_parse_date_str(Date, [Key | Formatter], Result) ->
|
||||
Size = date_size(Key),
|
||||
<<DatePart:Size/binary-unit:8, Tail/binary>> = Date,
|
||||
case lists:member(Key, ?DATE_PART) of
|
||||
true ->
|
||||
do_parse_date_str(Tail, Formatter, Result#{Key => erlang:binary_to_integer(DatePart)});
|
||||
false ->
|
||||
case lists:member(Key, ?DATE_ZONE_NAME) of
|
||||
true ->
|
||||
io:format("DatePart -------------- ~p ~p~n", [DatePart, offset_second(DatePart)]),
|
||||
do_parse_date_str(Tail, Formatter, Result#{parsed_offset => offset_second(DatePart)});
|
||||
false ->
|
||||
do_parse_date_str(Tail, Formatter, Result)
|
||||
end
|
||||
end.
|
||||
|
||||
date_size(Str) when is_list(Str) -> erlang:length(Str);
|
||||
date_size(DateName) ->
|
||||
Map = #{
|
||||
year => 4,
|
||||
month => 2,
|
||||
day => 2,
|
||||
hour => 2,
|
||||
minute => 2,
|
||||
second => 2,
|
||||
millisecond => 3,
|
||||
microsecond => 6,
|
||||
nanosecond => 9,
|
||||
timezone => 5,
|
||||
timezone1 => 6,
|
||||
timezone2 => 9
|
||||
},
|
||||
maps:get(DateName, Map).
|
||||
|
||||
nano_sm(Month) -> dm(Month) * ?SECONDS_PER_DAY * 1000_000.
|
||||
|
||||
dm(Month) ->
|
||||
MonthDays = #{
|
||||
1 => 0,
|
||||
2 => 31,
|
||||
3 => 59,
|
||||
4 => 90,
|
||||
5 => 120,
|
||||
6 => 151,
|
||||
7 => 181,
|
||||
8 => 212,
|
||||
9 => 243,
|
||||
10 => 273,
|
||||
11 => 304,
|
||||
12 => 334
|
||||
},
|
||||
maps:get(Month, MonthDays).
|
||||
|
||||
%% -------------------------------------------------------------------------------------------------
|
||||
%% internal
|
||||
%% emqx_calendar:format(erlang:system_time(second), <<"second">>, <<"+8:00">> ,<<"%Y-%m-%d-%H:%M:%S%Z">>).
|
||||
|
||||
time_unit(second) -> second;
|
||||
time_unit(millisecond) -> millisecond;
|
||||
time_unit(microsecond) -> microsecond;
|
||||
time_unit(nanosecond) -> nanosecond;
|
||||
time_unit("second") -> second;
|
||||
time_unit("millisecond") -> millisecond;
|
||||
time_unit("microsecond") -> microsecond;
|
||||
time_unit("nanosecond") -> nanosecond;
|
||||
time_unit(<<"second">>) -> second;
|
||||
time_unit(<<"millisecond">>) -> millisecond;
|
||||
time_unit(<<"microsecond">>) -> microsecond;
|
||||
time_unit(<<"nanosecond">>) -> nanosecond.
|
||||
|
||||
%% -------------------------------------------------------------------------------------------------
|
||||
%% internal: format part
|
||||
|
||||
do_formatter(<<>>, Formatter) -> lists:reverse(Formatter);
|
||||
do_formatter(<<"%Y", Tail/binary>>, Formatter) -> do_formatter(Tail, [year | Formatter]);
|
||||
do_formatter(<<"%m", Tail/binary>>, Formatter) -> do_formatter(Tail, [month | Formatter]);
|
||||
do_formatter(<<"%d", Tail/binary>>, Formatter) -> do_formatter(Tail, [day | Formatter]);
|
||||
do_formatter(<<"%H", Tail/binary>>, Formatter) -> do_formatter(Tail, [hour | Formatter]);
|
||||
do_formatter(<<"%M", Tail/binary>>, Formatter) -> do_formatter(Tail, [minute | Formatter]);
|
||||
do_formatter(<<"%S", Tail/binary>>, Formatter) -> do_formatter(Tail, [second | Formatter]);
|
||||
do_formatter(<<"%N", Tail/binary>>, Formatter) -> do_formatter(Tail, [nanosecond | Formatter]);
|
||||
do_formatter(<<"%3N", Tail/binary>>, Formatter) -> do_formatter(Tail, [millisecond | Formatter]);
|
||||
do_formatter(<<"%6N", Tail/binary>>, Formatter) -> do_formatter(Tail, [microsecond | Formatter]);
|
||||
do_formatter(<<"%z", Tail/binary>>, Formatter) -> do_formatter(Tail, [timezone | Formatter]);
|
||||
do_formatter(<<"%:z", Tail/binary>>, Formatter) -> do_formatter(Tail, [timezone1 | Formatter]);
|
||||
do_formatter(<<"%::z", Tail/binary>>, Formatter) -> do_formatter(Tail, [timezone2 | Formatter]);
|
||||
do_formatter(<<Char:8, Tail/binary>>, [Str | Formatter]) when is_list(Str) ->
|
||||
do_formatter(Tail, [lists:append(Str, [Char]) | Formatter]);
|
||||
do_formatter(<<Char:8, Tail/binary>>, Formatter) -> do_formatter(Tail, [[Char] | Formatter]).
|
||||
|
||||
do_offset_second(OffsetSecond) when is_integer(OffsetSecond) -> OffsetSecond;
|
||||
do_offset_second(undefined) -> 0;
|
||||
do_offset_second("local") -> do_offset_second(local);
|
||||
do_offset_second(<<"local">>) -> do_offset_second(local);
|
||||
do_offset_second(local) ->
|
||||
UniversalTime = calendar:system_time_to_universal_time(erlang:system_time(second), second),
|
||||
LocalTime = erlang:universaltime_to_localtime(UniversalTime),
|
||||
LocalSecs = calendar:datetime_to_gregorian_seconds(LocalTime),
|
||||
UniversalSecs = calendar:datetime_to_gregorian_seconds(UniversalTime),
|
||||
LocalSecs - UniversalSecs;
|
||||
do_offset_second(Offset) when is_binary(Offset) ->
|
||||
do_offset_second(erlang:binary_to_list(Offset));
|
||||
do_offset_second("Z") -> 0;
|
||||
do_offset_second("z") -> 0;
|
||||
do_offset_second(Offset) when is_list(Offset) ->
|
||||
Sign = hd(Offset),
|
||||
((Sign == $+) orelse (Sign == $-))
|
||||
orelse error({bad_zone, Offset}),
|
||||
Signs = #{$+ => 1, $- => -1},
|
||||
PosNeg = maps:get(Sign, Signs),
|
||||
[Sign | HM] = Offset,
|
||||
{HourStr, MinuteStr, SecondStr} =
|
||||
case string:tokens(HM, ":") of
|
||||
[H, M] ->
|
||||
{H, M, "0"};
|
||||
[H, M, S] ->
|
||||
{H, M, S};
|
||||
[HHMM] when erlang:length(HHMM) == 4 ->
|
||||
{string:sub_string(HHMM, 1,2), string:sub_string(HHMM, 3,4), "0"};
|
||||
_ ->
|
||||
error({bad_zone, Offset})
|
||||
end,
|
||||
Hour = erlang:list_to_integer(HourStr),
|
||||
Minute = erlang:list_to_integer(MinuteStr),
|
||||
Second = erlang:list_to_integer(SecondStr),
|
||||
(Hour =< 23) orelse error({bad_hour, Hour}),
|
||||
(Minute =< 59) orelse error({bad_minute, Minute}),
|
||||
(Second =< 59) orelse error({bad_second, Second}),
|
||||
PosNeg * (Hour * 3600 + Minute * 60 + Second).
|
||||
|
||||
do_format(Time, Unit, Offset, Formatter) ->
|
||||
Timezones = formatter_timezones(Offset, Formatter, #{}),
|
||||
do_format(Time, Unit, Offset, Timezones, Formatter).
|
||||
|
||||
do_format(Time, Unit, Offset, Timezones, Formatter) ->
|
||||
Adjustment = erlang:convert_time_unit(Offset, second, Unit),
|
||||
AdjustedTime = Time + Adjustment,
|
||||
Factor = factor(Unit),
|
||||
Secs = AdjustedTime div Factor,
|
||||
DateTime = system_time_to_datetime(Secs),
|
||||
{{Year, Month, Day}, {Hour, Min, Sec}} = DateTime,
|
||||
Date = #{
|
||||
year => padding(Year, 4),
|
||||
month => padding(Month, 2),
|
||||
day => padding(Day, 2),
|
||||
hour => padding(Hour, 2),
|
||||
minute => padding(Min, 2),
|
||||
second => padding(Sec, 2),
|
||||
millisecond => trans_x_second(Unit, millisecond, Time),
|
||||
microsecond => trans_x_second(Unit, microsecond, Time),
|
||||
nanosecond => trans_x_second(Unit, nanosecond, Time)
|
||||
},
|
||||
DateWithZone = maps:merge(Date, Timezones),
|
||||
[maps:get(Key, DateWithZone, Key) || Key <- Formatter].
|
||||
|
||||
formatter_timezones(_Offset, [], Zones) -> Zones;
|
||||
formatter_timezones(Offset, [Timezone | Formatter], Zones) ->
|
||||
case lists:member(Timezone, [timezone, timezone1, timezone2]) of
|
||||
true ->
|
||||
NZones = Zones#{Timezone => offset_to_timezone(Offset, Timezone)},
|
||||
formatter_timezones(Offset, Formatter, NZones);
|
||||
false ->
|
||||
formatter_timezones(Offset, Formatter, Zones)
|
||||
end.
|
||||
|
||||
offset_to_timezone(Offset, Timezone) ->
|
||||
Sign =
|
||||
case Offset >= 0 of
|
||||
true ->
|
||||
$+;
|
||||
false ->
|
||||
$-
|
||||
end,
|
||||
{H, M, S} = seconds_to_time(abs(Offset)),
|
||||
%% TODO: Support zone define %:::z
|
||||
%% Numeric time zone with ":" to necessary precision (e.g., -04, +05:30).
|
||||
case Timezone of
|
||||
timezone ->
|
||||
%% +0800
|
||||
io_lib:format("~c~2.10.0B~2.10.0B", [Sign, H, M]);
|
||||
timezone1 ->
|
||||
%% +08:00
|
||||
io_lib:format("~c~2.10.0B:~2.10.0B", [Sign, H, M]);
|
||||
timezone2 ->
|
||||
%% +08:00:00
|
||||
io_lib:format("~c~2.10.0B:~2.10.0B:~2.10.0B", [Sign, H, M, S])
|
||||
end.
|
||||
|
||||
factor(second) -> 1;
|
||||
factor(millisecond) -> 1000;
|
||||
factor(microsecond) -> 1000000;
|
||||
factor(nanosecond) -> 1000000000.
|
||||
|
||||
system_time_to_datetime(Seconds) ->
|
||||
gregorian_seconds_to_datetime(Seconds + ?SECONDS_FROM_0_TO_1970).
|
||||
|
||||
gregorian_seconds_to_datetime(Secs) when Secs >= 0 ->
|
||||
Days = Secs div ?SECONDS_PER_DAY,
|
||||
Rest = Secs rem ?SECONDS_PER_DAY,
|
||||
{gregorian_days_to_date(Days), seconds_to_time(Rest)}.
|
||||
|
||||
seconds_to_time(Secs) when Secs >= 0, Secs < ?SECONDS_PER_DAY ->
|
||||
Secs0 = Secs rem ?SECONDS_PER_DAY,
|
||||
Hour = Secs0 div ?SECONDS_PER_HOUR,
|
||||
Secs1 = Secs0 rem ?SECONDS_PER_HOUR,
|
||||
Minute = Secs1 div ?SECONDS_PER_MINUTE,
|
||||
Second = Secs1 rem ?SECONDS_PER_MINUTE,
|
||||
{Hour, Minute, Second}.
|
||||
|
||||
gregorian_days_to_date(Days) ->
|
||||
{Year, DayOfYear} = day_to_year(Days),
|
||||
{Month, DayOfMonth} = year_day_to_date(Year, DayOfYear),
|
||||
{Year, Month, DayOfMonth}.
|
||||
|
||||
day_to_year(DayOfEpoch) when DayOfEpoch >= 0 ->
|
||||
YMax = DayOfEpoch div ?DAYS_PER_YEAR,
|
||||
YMin = DayOfEpoch div ?DAYS_PER_LEAP_YEAR,
|
||||
{Y1, D1} = dty(YMin, YMax, DayOfEpoch, dy(YMin), dy(YMax)),
|
||||
{Y1, DayOfEpoch - D1}.
|
||||
|
||||
year_day_to_date(Year, DayOfYear) ->
|
||||
ExtraDay =
|
||||
case is_leap_year(Year) of
|
||||
true ->
|
||||
1;
|
||||
false ->
|
||||
0
|
||||
end,
|
||||
{Month, Day} = year_day_to_date2(ExtraDay, DayOfYear),
|
||||
{Month, Day + 1}.
|
||||
|
||||
dty(Min, Max, _D1, DMin, _DMax) when Min == Max ->
|
||||
{Min, DMin};
|
||||
dty(Min, Max, D1, DMin, DMax) ->
|
||||
Diff = Max - Min,
|
||||
Mid = Min + Diff * (D1 - DMin) div (DMax - DMin),
|
||||
MidLength =
|
||||
case is_leap_year(Mid) of
|
||||
true ->
|
||||
?DAYS_PER_LEAP_YEAR;
|
||||
false ->
|
||||
?DAYS_PER_YEAR
|
||||
end,
|
||||
case dy(Mid) of
|
||||
D2 when D1 < D2 ->
|
||||
NewMax = Mid - 1,
|
||||
dty(Min, NewMax, D1, DMin, dy(NewMax));
|
||||
D2 when D1 - D2 >= MidLength ->
|
||||
NewMin = Mid + 1,
|
||||
dty(NewMin, Max, D1, dy(NewMin), DMax);
|
||||
D2 ->
|
||||
{Mid, D2}
|
||||
end.
|
||||
|
||||
dy(Y) when Y =< 0 ->
|
||||
0;
|
||||
dy(Y) ->
|
||||
X = Y - 1,
|
||||
X div 4 - X div 100 + X div 400 + X * ?DAYS_PER_YEAR + ?DAYS_PER_LEAP_YEAR.
|
||||
|
||||
is_leap_year(Y) when is_integer(Y), Y >= 0 ->
|
||||
is_leap_year1(Y).
|
||||
|
||||
is_leap_year1(Year) when Year rem 4 =:= 0, Year rem 100 > 0 ->
|
||||
true;
|
||||
is_leap_year1(Year) when Year rem 400 =:= 0 ->
|
||||
true;
|
||||
is_leap_year1(_) ->
|
||||
false.
|
||||
|
||||
year_day_to_date2(_, Day) when Day < 31 ->
|
||||
{1, Day};
|
||||
year_day_to_date2(E, Day) when 31 =< Day, Day < 59 + E ->
|
||||
{2, Day - 31};
|
||||
year_day_to_date2(E, Day) when 59 + E =< Day, Day < 90 + E ->
|
||||
{3, Day - (59 + E)};
|
||||
year_day_to_date2(E, Day) when 90 + E =< Day, Day < 120 + E ->
|
||||
{4, Day - (90 + E)};
|
||||
year_day_to_date2(E, Day) when 120 + E =< Day, Day < 151 + E ->
|
||||
{5, Day - (120 + E)};
|
||||
year_day_to_date2(E, Day) when 151 + E =< Day, Day < 181 + E ->
|
||||
{6, Day - (151 + E)};
|
||||
year_day_to_date2(E, Day) when 181 + E =< Day, Day < 212 + E ->
|
||||
{7, Day - (181 + E)};
|
||||
year_day_to_date2(E, Day) when 212 + E =< Day, Day < 243 + E ->
|
||||
{8, Day - (212 + E)};
|
||||
year_day_to_date2(E, Day) when 243 + E =< Day, Day < 273 + E ->
|
||||
{9, Day - (243 + E)};
|
||||
year_day_to_date2(E, Day) when 273 + E =< Day, Day < 304 + E ->
|
||||
{10, Day - (273 + E)};
|
||||
year_day_to_date2(E, Day) when 304 + E =< Day, Day < 334 + E ->
|
||||
{11, Day - (304 + E)};
|
||||
year_day_to_date2(E, Day) when 334 + E =< Day ->
|
||||
{12, Day - (334 + E)}.
|
||||
|
||||
trans_x_second(FromUnit, ToUnit, Time) ->
|
||||
XSecond = do_trans_x_second(FromUnit, ToUnit, Time),
|
||||
Len =
|
||||
case ToUnit of
|
||||
millisecond -> 3;
|
||||
microsecond -> 6;
|
||||
nanosecond -> 9
|
||||
end,
|
||||
padding(XSecond, Len).
|
||||
|
||||
do_trans_x_second(second, second, Time) -> Time div 60;
|
||||
do_trans_x_second(second, _, _Time) -> 0;
|
||||
|
||||
do_trans_x_second(millisecond, millisecond, Time) -> Time rem 1000;
|
||||
do_trans_x_second(millisecond, microsecond, _Time) -> 0;
|
||||
do_trans_x_second(millisecond, nanosecond, _Time) -> 0;
|
||||
|
||||
do_trans_x_second(microsecond, millisecond, Time) -> Time div 1000 rem 1000;
|
||||
do_trans_x_second(microsecond, microsecond, Time) -> Time rem 1000000;
|
||||
do_trans_x_second(microsecond, nanosecond, _Time) -> 0;
|
||||
|
||||
do_trans_x_second(nanosecond, millisecond, Time) -> Time div 1000000 rem 1000;
|
||||
do_trans_x_second(nanosecond, microsecond, Time) -> Time div 1000 rem 1000000;
|
||||
do_trans_x_second(nanosecond, nanosecond, Time) -> Time rem 1000000000.
|
||||
|
||||
padding(Data, Len) when is_integer(Data) ->
|
||||
padding(integer_to_list(Data), Len);
|
||||
padding(Data, Len) when Len > 0 andalso erlang:length(Data) < Len ->
|
||||
[$0 | padding(Data, Len - 1)];
|
||||
padding(Data, _Len) ->
|
||||
Data.
|
|
@ -16,16 +16,6 @@
|
|||
|
||||
-module(emqx_logger_textfmt).
|
||||
|
||||
-define(SECONDS_PER_MINUTE, 60).
|
||||
-define(SECONDS_PER_HOUR, 3600).
|
||||
-define(SECONDS_PER_DAY, 86400).
|
||||
-define(DAYS_PER_YEAR, 365).
|
||||
-define(DAYS_PER_LEAP_YEAR, 366).
|
||||
-define(DAYS_FROM_0_TO_1970, 719528).
|
||||
-define(DAYS_FROM_0_TO_10000, 2932897).
|
||||
-define(SECONDS_FROM_0_TO_1970, ?DAYS_FROM_0_TO_1970 * ?SECONDS_PER_DAY).
|
||||
-define(SECONDS_FROM_0_TO_10000, ?DAYS_FROM_0_TO_10000 * ?SECONDS_PER_DAY).
|
||||
|
||||
-export([format/2]).
|
||||
-export([check_config/1]).
|
||||
|
||||
|
@ -39,7 +29,7 @@
|
|||
]).
|
||||
|
||||
check_config(Config0) ->
|
||||
Config = maps:without([date_format, timezone_offset, timezone], Config0),
|
||||
Config = maps:without([timezone_offset, timezone, date_format], Config0),
|
||||
logger_formatter:check_config(Config).
|
||||
|
||||
format(#{msg := Msg0, meta := Meta} = Event,
|
||||
|
@ -51,8 +41,15 @@ format(#{msg := Msg0, meta := Meta} = Event,
|
|||
format(#{msg := Msg0, meta := Meta} = Event,
|
||||
#{timezone_offset := TZO, timezone := TZ, date_format := DFS} = Config) ->
|
||||
Msg = maybe_merge(Msg0, Meta),
|
||||
Time = maps:get(time, Event, undefined),
|
||||
[date_format(Time, TZO, TZ, DFS) | logger_formatter:format(Event#{msg := Msg}, Config)].
|
||||
Time =
|
||||
case maps:get(time, Event, undefined) of
|
||||
undefined ->
|
||||
erlang:system_time(microsecond);
|
||||
T ->
|
||||
T
|
||||
end,
|
||||
Date = emqx_calendar:format(Time, microsecond, TZO, TZ, DFS),
|
||||
[Date | logger_formatter:format(Event#{msg := Msg}, Config)].
|
||||
|
||||
maybe_merge({report, Report}, Meta) when is_map(Report) ->
|
||||
{report, maps:merge(Report, filter(Meta))};
|
||||
|
@ -61,150 +58,3 @@ maybe_merge(Report, _Meta) ->
|
|||
|
||||
filter(Meta) ->
|
||||
maps:without(?WITHOUT_MERGE, Meta).
|
||||
|
||||
date_format(undefined, Offset, OffsetStr, Formatter) ->
|
||||
Time = erlang:system_time(microsecond),
|
||||
date_format(Time, Offset, OffsetStr, Formatter);
|
||||
date_format(Time, Offset, OffsetStr, Formatter) ->
|
||||
Adjustment = erlang:convert_time_unit(Offset, second, microsecond),
|
||||
AdjustedTime = Time + Adjustment,
|
||||
%% Factor 1000000 for microsecond.
|
||||
Factor = 1000000,
|
||||
Secs = AdjustedTime div Factor,
|
||||
DateTime = system_time_to_datetime(Secs),
|
||||
{{Year, Month, Day}, {Hour, Min, Sec}} = DateTime,
|
||||
FractionStr = fraction_str(Factor, AdjustedTime),
|
||||
Date = #{
|
||||
year => padding(Year, 4),
|
||||
month => padding(Month, 2),
|
||||
day => padding(Day, 2),
|
||||
hour => padding(Hour, 2),
|
||||
minute => padding(Min, 2),
|
||||
second => padding(Sec, 2),
|
||||
nano_second => FractionStr,
|
||||
timezone => OffsetStr
|
||||
},
|
||||
[maps:get(Key, Date, Key) || Key <- Formatter].
|
||||
|
||||
system_time_to_datetime(Seconds) ->
|
||||
gregorian_seconds_to_datetime(Seconds + ?SECONDS_FROM_0_TO_1970).
|
||||
|
||||
gregorian_seconds_to_datetime(Secs) when Secs >= 0 ->
|
||||
Days = Secs div ?SECONDS_PER_DAY,
|
||||
Rest = Secs rem ?SECONDS_PER_DAY,
|
||||
{gregorian_days_to_date(Days), seconds_to_time(Rest)}.
|
||||
|
||||
seconds_to_time(Secs) when Secs >= 0, Secs < ?SECONDS_PER_DAY ->
|
||||
Secs0 = Secs rem ?SECONDS_PER_DAY,
|
||||
Hour = Secs0 div ?SECONDS_PER_HOUR,
|
||||
Secs1 = Secs0 rem ?SECONDS_PER_HOUR,
|
||||
Minute = Secs1 div ?SECONDS_PER_MINUTE,
|
||||
Second = Secs1 rem ?SECONDS_PER_MINUTE,
|
||||
{Hour, Minute, Second}.
|
||||
|
||||
gregorian_days_to_date(Days) ->
|
||||
{Year, DayOfYear} = day_to_year(Days),
|
||||
{Month, DayOfMonth} = year_day_to_date(Year, DayOfYear),
|
||||
{Year, Month, DayOfMonth}.
|
||||
|
||||
day_to_year(DayOfEpoch) when DayOfEpoch >= 0 ->
|
||||
YMax = DayOfEpoch div ?DAYS_PER_YEAR,
|
||||
YMin = DayOfEpoch div ?DAYS_PER_LEAP_YEAR,
|
||||
{Y1, D1} = dty(YMin, YMax, DayOfEpoch, dy(YMin), dy(YMax)),
|
||||
{Y1, DayOfEpoch - D1}.
|
||||
|
||||
year_day_to_date(Year, DayOfYear) ->
|
||||
ExtraDay =
|
||||
case is_leap_year(Year) of
|
||||
true ->
|
||||
1;
|
||||
false ->
|
||||
0
|
||||
end,
|
||||
{Month, Day} = year_day_to_date2(ExtraDay, DayOfYear),
|
||||
{Month, Day + 1}.
|
||||
|
||||
dty(Min, Max, _D1, DMin, _DMax) when Min == Max ->
|
||||
{Min, DMin};
|
||||
dty(Min, Max, D1, DMin, DMax) ->
|
||||
Diff = Max - Min,
|
||||
Mid = Min + Diff * (D1 - DMin) div (DMax - DMin),
|
||||
MidLength =
|
||||
case is_leap_year(Mid) of
|
||||
true ->
|
||||
?DAYS_PER_LEAP_YEAR;
|
||||
false ->
|
||||
?DAYS_PER_YEAR
|
||||
end,
|
||||
case dy(Mid) of
|
||||
D2 when D1 < D2 ->
|
||||
NewMax = Mid - 1,
|
||||
dty(Min, NewMax, D1, DMin, dy(NewMax));
|
||||
D2 when D1 - D2 >= MidLength ->
|
||||
NewMin = Mid + 1,
|
||||
dty(NewMin, Max, D1, dy(NewMin), DMax);
|
||||
D2 ->
|
||||
{Mid, D2}
|
||||
end.
|
||||
|
||||
dy(Y) when Y =< 0 ->
|
||||
0;
|
||||
dy(Y) ->
|
||||
X = Y - 1,
|
||||
X div 4 - X div 100 + X div 400 + X * ?DAYS_PER_YEAR + ?DAYS_PER_LEAP_YEAR.
|
||||
|
||||
is_leap_year(Y) when is_integer(Y), Y >= 0 ->
|
||||
is_leap_year1(Y).
|
||||
|
||||
is_leap_year1(Year) when Year rem 4 =:= 0, Year rem 100 > 0 ->
|
||||
true;
|
||||
is_leap_year1(Year) when Year rem 400 =:= 0 ->
|
||||
true;
|
||||
is_leap_year1(_) ->
|
||||
false.
|
||||
|
||||
year_day_to_date2(_, Day) when Day < 31 ->
|
||||
{1, Day};
|
||||
year_day_to_date2(E, Day) when 31 =< Day, Day < 59 + E ->
|
||||
{2, Day - 31};
|
||||
year_day_to_date2(E, Day) when 59 + E =< Day, Day < 90 + E ->
|
||||
{3, Day - (59 + E)};
|
||||
year_day_to_date2(E, Day) when 90 + E =< Day, Day < 120 + E ->
|
||||
{4, Day - (90 + E)};
|
||||
year_day_to_date2(E, Day) when 120 + E =< Day, Day < 151 + E ->
|
||||
{5, Day - (120 + E)};
|
||||
year_day_to_date2(E, Day) when 151 + E =< Day, Day < 181 + E ->
|
||||
{6, Day - (151 + E)};
|
||||
year_day_to_date2(E, Day) when 181 + E =< Day, Day < 212 + E ->
|
||||
{7, Day - (181 + E)};
|
||||
year_day_to_date2(E, Day) when 212 + E =< Day, Day < 243 + E ->
|
||||
{8, Day - (212 + E)};
|
||||
year_day_to_date2(E, Day) when 243 + E =< Day, Day < 273 + E ->
|
||||
{9, Day - (243 + E)};
|
||||
year_day_to_date2(E, Day) when 273 + E =< Day, Day < 304 + E ->
|
||||
{10, Day - (273 + E)};
|
||||
year_day_to_date2(E, Day) when 304 + E =< Day, Day < 334 + E ->
|
||||
{11, Day - (304 + E)};
|
||||
year_day_to_date2(E, Day) when 334 + E =< Day ->
|
||||
{12, Day - (334 + E)}.
|
||||
|
||||
fraction_str(1, _Time) ->
|
||||
"";
|
||||
fraction_str(Factor, Time) ->
|
||||
Fraction = Time rem Factor,
|
||||
S = integer_to_list(abs(Fraction)),
|
||||
[padding(S, log10(Factor) - length(S))].
|
||||
|
||||
log10(1000) ->
|
||||
3;
|
||||
log10(1000000) ->
|
||||
6;
|
||||
log10(1000000000) ->
|
||||
9.
|
||||
|
||||
padding(Data, Len) when is_integer(Data) ->
|
||||
padding(integer_to_list(Data), Len);
|
||||
padding(Data, Len) when Len > 0 andalso erlang:length(Data) < Len ->
|
||||
[$0 | padding(Data, Len - 1)];
|
||||
padding(Data, _Len) ->
|
||||
Data.
|
||||
|
|
Loading…
Reference in New Issue