feat(log): configurable time format
now logs can be configured to use 'epoch' or 'rfc3339' time format
This commit is contained in:
parent
ed5a4aa921
commit
c42e980442
|
@ -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
|
||||||
|
|
|
@ -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) ->
|
||||||
|
|
|
@ -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}) ->
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
|
@ -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."""
|
||||||
|
|
Loading…
Reference in New Issue