feat(log): configurable time format

now logs can be configured to use 'epoch' or 'rfc3339' time format
This commit is contained in:
zmstone 2024-03-26 12:44:41 +01:00
parent ed5a4aa921
commit c42e980442
6 changed files with 73 additions and 10 deletions

View File

@ -237,25 +237,32 @@ log_formatter(HandlerName, Conf) ->
_ -> _ ->
conf_get("formatter", Conf) conf_get("formatter", Conf)
end, end,
TsFormat = timstamp_format(Conf),
do_formatter( do_formatter(
Format, CharsLimit, SingleLine, TimeOffSet, Depth Format, CharsLimit, SingleLine, TimeOffSet, Depth, TsFormat
). ).
%% auto | epoch | rfc3339
timstamp_format(Conf) ->
conf_get("timestamp_format", Conf).
%% helpers %% helpers
do_formatter(json, CharsLimit, SingleLine, TimeOffSet, Depth) -> do_formatter(json, CharsLimit, SingleLine, TimeOffSet, Depth, TsFormat) ->
{emqx_logger_jsonfmt, #{ {emqx_logger_jsonfmt, #{
chars_limit => CharsLimit, chars_limit => CharsLimit,
single_line => SingleLine, single_line => SingleLine,
time_offset => TimeOffSet, time_offset => TimeOffSet,
depth => Depth depth => Depth,
timestamp_format => TsFormat
}}; }};
do_formatter(text, CharsLimit, SingleLine, TimeOffSet, Depth) -> do_formatter(text, CharsLimit, SingleLine, TimeOffSet, Depth, TsFormat) ->
{emqx_logger_textfmt, #{ {emqx_logger_textfmt, #{
template => [time, " [", level, "] ", msg, "\n"], template => ["[", level, "] ", msg, "\n"],
chars_limit => CharsLimit, chars_limit => CharsLimit,
single_line => SingleLine, single_line => SingleLine,
time_offset => TimeOffSet, time_offset => TimeOffSet,
depth => Depth depth => Depth,
timestamp_format => TsFormat
}}. }}.
%% Don't record all logger message %% Don't record all logger message

View File

@ -285,9 +285,21 @@ json_obj_root(Data0, Config) ->
), ),
lists:filter( lists:filter(
fun({_, V}) -> V =/= undefined end, fun({_, V}) -> V =/= undefined end,
[{time, Time}, {level, Level}, {msg, Msg}] [{time, format_ts(Time, Config)}, {level, Level}, {msg, Msg}]
) ++ Data. ) ++ Data.
format_ts(Ts, #{timestamp_format := rfc3339, time_offset := Offset}) when is_integer(Ts) ->
iolist_to_binary(
calendar:system_time_to_rfc3339(Ts, [
{unit, microsecond},
{offset, Offset},
{time_designator, $T}
])
);
format_ts(Ts, _Config) ->
% auto | epoch
Ts.
json_obj(Data, Config) -> json_obj(Data, Config) ->
maps:fold( maps:fold(
fun(K, V, D) -> fun(K, V, D) ->

View File

@ -20,7 +20,7 @@
-export([check_config/1]). -export([check_config/1]).
-export([try_format_unicode/1]). -export([try_format_unicode/1]).
check_config(X) -> logger_formatter:check_config(X). check_config(X) -> logger_formatter:check_config(maps:without([timestamp_format], X)).
%% Principle here is to delegate the formatting to logger_formatter:format/2 %% Principle here is to delegate the formatting to logger_formatter:format/2
%% as much as possible, and only enrich the report with clientid, peername, topic, username %% as much as possible, and only enrich the report with clientid, peername, topic, username
@ -35,7 +35,7 @@ format(#{msg := {report, ReportMap}, meta := Meta} = Event, Config) when is_map(
false -> false ->
maps:from_list(ReportList) maps:from_list(ReportList)
end, end,
logger_formatter:format(Event#{msg := {report, Report}}, Config); fmt(Event#{msg := {report, Report}}, Config);
format(#{msg := {string, String}} = Event, Config) -> format(#{msg := {string, String}} = Event, Config) ->
%% copied from logger_formatter:format/2 %% copied from logger_formatter:format/2
%% unsure how this case is triggered %% unsure how this case is triggered
@ -45,7 +45,23 @@ format(#{msg := Msg0, meta := Meta} = Event, Config) ->
%% and logger:log(Level, "message", #{key => value}) %% and logger:log(Level, "message", #{key => value})
Msg1 = enrich_client_info(Msg0, Meta), Msg1 = enrich_client_info(Msg0, Meta),
Msg2 = enrich_topic(Msg1, Meta), Msg2 = enrich_topic(Msg1, Meta),
logger_formatter:format(Event#{msg := Msg2}, Config). fmt(Event#{msg := Msg2}, Config).
fmt(#{meta := #{time := Ts}} = Data, Config) ->
Timestamp =
case Config of
#{timestamp_format := epoch} ->
integer_to_list(Ts);
_ ->
% auto | rfc3339
TimeOffset = maps:get(time_offset, Config, ""),
calendar:system_time_to_rfc3339(Ts, [
{unit, microsecond},
{offset, TimeOffset},
{time_designator, $T}
])
end,
[Timestamp, " ", logger_formatter:format(Data, Config)].
%% Other report callbacks may only accept map() reports such as gen_server formatter %% Other report callbacks may only accept map() reports such as gen_server formatter
is_list_report_acceptable(#{report_cb := Cb}) -> is_list_report_acceptable(#{report_cb := Cb}) ->

View File

@ -1277,6 +1277,15 @@ log_handler_common_confs(Handler, Default) ->
importance => ?IMPORTANCE_MEDIUM importance => ?IMPORTANCE_MEDIUM
} }
)}, )},
{"timestamp_format",
sc(
hoconsc:enum([auto, epoch, rfc3339]),
#{
default => auto,
desc => ?DESC("common_handler_timestamp_format"),
importance => ?IMPORTANCE_MEDIUM
}
)},
{"time_offset", {"time_offset",
sc( sc(
string(), string(),

View File

@ -0,0 +1,10 @@
Add `timestamp_format` config to log handers.
We've added a new configuration option `timestamp_format` to the log handlers.
This new config supports the following values:
- `auto`: Automatically determines the timestamp format based on the log formatter being used.
Utilizes `rfc3339` format for text formatters, and `epoch` format for JSON formatters.
- `epoch`: Represents timestamps in milliseconds precision Unix epoch format.
- `rfc3339`: Uses RFC3339 compliant format for date-time strings. For example: `2024-03-26T11:52:19.777087+00:00`.

View File

@ -761,6 +761,15 @@ common_handler_formatter.desc:
common_handler_formatter.label: common_handler_formatter.label:
"""Log Formatter""" """Log Formatter"""
common_handler_timestamp_format.label:
"""Timestamp Format"""
common_handler_timestamp_format.desc: """~
Pick a timestamp format:
- `auto`: automatically choose the best format based on log formatter. `epoch` for JSON and `rfc3339` for text.
- `epoch`: Unix epoch time in milliseconds.
- `rfc3339`: RFC3339 format."""
rpc_async_batch_size.desc: rpc_async_batch_size.desc:
"""The maximum number of batch messages sent in asynchronous mode. """The maximum number of batch messages sent in asynchronous mode.
Note that this configuration does not work in synchronous mode.""" Note that this configuration does not work in synchronous mode."""