feat: add hidden option to record MFA and line in the log is very useful for debug.
This commit is contained in:
parent
fe303231cf
commit
c72b455a83
|
@ -30,7 +30,10 @@
|
||||||
logger:log(
|
logger:log(
|
||||||
Level,
|
Level,
|
||||||
(Data),
|
(Data),
|
||||||
Meta
|
maps:merge(Meta, #{
|
||||||
|
mfa => {?MODULE, ?FUNCTION_NAME, ?FUNCTION_ARITY},
|
||||||
|
line => ?LINE
|
||||||
|
})
|
||||||
);
|
);
|
||||||
false ->
|
false ->
|
||||||
ok
|
ok
|
||||||
|
|
|
@ -237,32 +237,35 @@ log_formatter(HandlerName, Conf) ->
|
||||||
_ ->
|
_ ->
|
||||||
conf_get("formatter", Conf)
|
conf_get("formatter", Conf)
|
||||||
end,
|
end,
|
||||||
TsFormat = timstamp_format(Conf),
|
TsFormat = timestamp_format(Conf),
|
||||||
|
WithMfa = conf_get("with_mfa", Conf),
|
||||||
do_formatter(
|
do_formatter(
|
||||||
Format, CharsLimit, SingleLine, TimeOffSet, Depth, TsFormat
|
Format, CharsLimit, SingleLine, TimeOffSet, Depth, TsFormat, WithMfa
|
||||||
).
|
).
|
||||||
|
|
||||||
%% auto | epoch | rfc3339
|
%% auto | epoch | rfc3339
|
||||||
timstamp_format(Conf) ->
|
timestamp_format(Conf) ->
|
||||||
conf_get("timestamp_format", Conf).
|
conf_get("timestamp_format", Conf).
|
||||||
|
|
||||||
%% helpers
|
%% helpers
|
||||||
do_formatter(json, CharsLimit, SingleLine, TimeOffSet, Depth, TsFormat) ->
|
do_formatter(json, CharsLimit, SingleLine, TimeOffSet, Depth, TsFormat, WithMfa) ->
|
||||||
{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
|
timestamp_format => TsFormat,
|
||||||
|
with_mfa => WithMfa
|
||||||
}};
|
}};
|
||||||
do_formatter(text, CharsLimit, SingleLine, TimeOffSet, Depth, TsFormat) ->
|
do_formatter(text, CharsLimit, SingleLine, TimeOffSet, Depth, TsFormat, WithMfa) ->
|
||||||
{emqx_logger_textfmt, #{
|
{emqx_logger_textfmt, #{
|
||||||
template => ["[", 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
|
timestamp_format => TsFormat,
|
||||||
|
with_mfa => WithMfa
|
||||||
}}.
|
}}.
|
||||||
|
|
||||||
%% Don't record all logger message
|
%% Don't record all logger message
|
||||||
|
|
|
@ -292,6 +292,7 @@ json_obj_root(Data0, Config) ->
|
||||||
_ ->
|
_ ->
|
||||||
json(Msg1, Config)
|
json(Msg1, Config)
|
||||||
end,
|
end,
|
||||||
|
MFA = emqx_utils:format_mfal(Data0, Config),
|
||||||
Data =
|
Data =
|
||||||
maps:fold(
|
maps:fold(
|
||||||
fun(K, V, D) ->
|
fun(K, V, D) ->
|
||||||
|
@ -300,12 +301,12 @@ json_obj_root(Data0, Config) ->
|
||||||
end,
|
end,
|
||||||
[],
|
[],
|
||||||
maps:without(
|
maps:without(
|
||||||
[time, gl, file, report_cb, msg, '$kind', level, is_trace], Data0
|
[time, gl, file, report_cb, msg, '$kind', level, mfa, is_trace], Data0
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
lists:filter(
|
lists:filter(
|
||||||
fun({_, V}) -> V =/= undefined end,
|
fun({_, V}) -> V =/= undefined end,
|
||||||
[{time, format_ts(Time, Config)}, {level, Level}, {msg, Msg}]
|
[{time, format_ts(Time, Config)}, {level, Level}, {msg, Msg}, {mfa, MFA}]
|
||||||
) ++ Data.
|
) ++ Data.
|
||||||
|
|
||||||
format_ts(Ts, #{timestamp_format := rfc3339, time_offset := Offset}) when is_integer(Ts) ->
|
format_ts(Ts, #{timestamp_format := rfc3339, time_offset := Offset}) when is_integer(Ts) ->
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
%% Used in the other log formatters
|
%% Used in the other log formatters
|
||||||
-export([evaluate_lazy_values_if_dbg_level/1, evaluate_lazy_values/1]).
|
-export([evaluate_lazy_values_if_dbg_level/1, evaluate_lazy_values/1]).
|
||||||
|
|
||||||
check_config(X) -> logger_formatter:check_config(maps:without([timestamp_format], X)).
|
check_config(X) -> logger_formatter:check_config(maps:without([timestamp_format, with_mfa], 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
|
||||||
|
@ -32,7 +32,7 @@ format(#{msg := {report, ReportMap0}, meta := _Meta} = Event0, Config) when is_m
|
||||||
#{msg := {report, ReportMap}, meta := Meta} = Event = evaluate_lazy_values_if_dbg_level(Event0),
|
#{msg := {report, ReportMap}, meta := Meta} = Event = evaluate_lazy_values_if_dbg_level(Event0),
|
||||||
%% The most common case, when entering from SLOG macro
|
%% The most common case, when entering from SLOG macro
|
||||||
%% i.e. logger:log(Level, #{msg => "my_msg", foo => bar})
|
%% i.e. logger:log(Level, #{msg => "my_msg", foo => bar})
|
||||||
ReportList = enrich_report(ReportMap, Meta),
|
ReportList = enrich_report(ReportMap, Meta, Config),
|
||||||
Report =
|
Report =
|
||||||
case is_list_report_acceptable(Meta) of
|
case is_list_report_acceptable(Meta) of
|
||||||
true ->
|
true ->
|
||||||
|
@ -40,18 +40,24 @@ format(#{msg := {report, ReportMap0}, meta := _Meta} = Event0, Config) when is_m
|
||||||
false ->
|
false ->
|
||||||
maps:from_list(ReportList)
|
maps:from_list(ReportList)
|
||||||
end,
|
end,
|
||||||
fmt(Event#{msg := {report, Report}}, Config);
|
fmt(Event#{msg := {report, Report}}, maps:remove(with_mfa, 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
|
||||||
format(Event#{msg => {"~ts ", [String]}}, Config);
|
format(Event#{msg => {"~ts ", [String]}}, maps:remove(with_mfa, Config));
|
||||||
format(#{msg := _Msg, meta := _Meta} = Event0, Config) ->
|
format(#{msg := _Msg, meta := _Meta} = Event0, Config) ->
|
||||||
#{msg := Msg0, meta := Meta} = Event1 = evaluate_lazy_values_if_dbg_level(Event0),
|
#{msg := Msg0, meta := Meta} = Event1 = evaluate_lazy_values_if_dbg_level(Event0),
|
||||||
%% For format strings like logger:log(Level, "~p", [Var])
|
%% For format strings like logger:log(Level, "~p", [Var])
|
||||||
%% 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_mfa(Msg1, Meta, Config),
|
||||||
fmt(Event1#{msg := Msg2}, Config).
|
Msg3 = enrich_topic(Msg2, Meta),
|
||||||
|
fmt(Event1#{msg := Msg3}, maps:remove(with_mfa, Config)).
|
||||||
|
|
||||||
|
enrich_mfa({Fmt, Args}, Data, #{with_mfa := true} = Config) when is_list(Fmt) ->
|
||||||
|
{Fmt ++ " mfa: ~ts", Args ++ [emqx_utils:format_mfal(Data, Config)]};
|
||||||
|
enrich_mfa(Msg, _, _) ->
|
||||||
|
Msg.
|
||||||
|
|
||||||
%% Most log entries with lazy values are trace events with level debug. So to
|
%% Most log entries with lazy values are trace events with level debug. So to
|
||||||
%% be more efficient we only search for lazy values to evaluate in the entries
|
%% be more efficient we only search for lazy values to evaluate in the entries
|
||||||
|
@ -101,7 +107,7 @@ is_list_report_acceptable(#{report_cb := Cb}) ->
|
||||||
is_list_report_acceptable(_) ->
|
is_list_report_acceptable(_) ->
|
||||||
false.
|
false.
|
||||||
|
|
||||||
enrich_report(ReportRaw, Meta) ->
|
enrich_report(ReportRaw, Meta, Config) ->
|
||||||
%% clientid and peername always in emqx_conn's process metadata.
|
%% clientid and peername always in emqx_conn's process metadata.
|
||||||
%% topic and username can be put in meta using ?SLOG/3, or put in msg's report by ?SLOG/2
|
%% topic and username can be put in meta using ?SLOG/3, or put in msg's report by ?SLOG/2
|
||||||
Topic =
|
Topic =
|
||||||
|
@ -117,6 +123,7 @@ enrich_report(ReportRaw, Meta) ->
|
||||||
ClientId = maps:get(clientid, Meta, undefined),
|
ClientId = maps:get(clientid, Meta, undefined),
|
||||||
Peer = maps:get(peername, Meta, undefined),
|
Peer = maps:get(peername, Meta, undefined),
|
||||||
Msg = maps:get(msg, ReportRaw, undefined),
|
Msg = maps:get(msg, ReportRaw, undefined),
|
||||||
|
MFA = emqx_utils:format_mfal(Meta, Config),
|
||||||
%% TODO: move all tags to Meta so we can filter traces
|
%% TODO: move all tags to Meta so we can filter traces
|
||||||
%% based on tags (currently not supported)
|
%% based on tags (currently not supported)
|
||||||
Tag = maps:get(tag, ReportRaw, maps:get(tag, Meta, undefined)),
|
Tag = maps:get(tag, ReportRaw, maps:get(tag, Meta, undefined)),
|
||||||
|
@ -131,6 +138,7 @@ enrich_report(ReportRaw, Meta) ->
|
||||||
{topic, try_format_unicode(Topic)},
|
{topic, try_format_unicode(Topic)},
|
||||||
{username, try_format_unicode(Username)},
|
{username, try_format_unicode(Username)},
|
||||||
{peername, Peer},
|
{peername, Peer},
|
||||||
|
{mfa, try_format_unicode(MFA)},
|
||||||
{msg, Msg},
|
{msg, Msg},
|
||||||
{clientid, try_format_unicode(ClientId)},
|
{clientid, try_format_unicode(ClientId)},
|
||||||
{tag, Tag}
|
{tag, Tag}
|
||||||
|
|
|
@ -1369,6 +1369,15 @@ log_handler_common_confs(Handler, Default) ->
|
||||||
desc => ?DESC("common_handler_max_depth"),
|
desc => ?DESC("common_handler_max_depth"),
|
||||||
importance => ?IMPORTANCE_HIDDEN
|
importance => ?IMPORTANCE_HIDDEN
|
||||||
}
|
}
|
||||||
|
)},
|
||||||
|
{"with_mfa",
|
||||||
|
sc(
|
||||||
|
boolean(),
|
||||||
|
#{
|
||||||
|
default => false,
|
||||||
|
desc => <<"Recording MFA and line in the log(usefully).">>,
|
||||||
|
importance => ?IMPORTANCE_HIDDEN
|
||||||
|
}
|
||||||
)}
|
)}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,7 @@
|
||||||
flattermap/2,
|
flattermap/2,
|
||||||
tcp_keepalive_opts/4,
|
tcp_keepalive_opts/4,
|
||||||
format/1,
|
format/1,
|
||||||
|
format_mfal/2,
|
||||||
call_first_defined/1,
|
call_first_defined/1,
|
||||||
ntoa/1,
|
ntoa/1,
|
||||||
foldl_while/3,
|
foldl_while/3,
|
||||||
|
@ -565,6 +566,32 @@ tcp_keepalive_opts(OS, _Idle, _Interval, _Probes) ->
|
||||||
format(Term) ->
|
format(Term) ->
|
||||||
iolist_to_binary(io_lib:format("~0p", [Term])).
|
iolist_to_binary(io_lib:format("~0p", [Term])).
|
||||||
|
|
||||||
|
%% @doc Helper function for log formatters.
|
||||||
|
-spec format_mfal(map(), map()) -> undefined | binary().
|
||||||
|
format_mfal(Data, #{with_mfa := true}) ->
|
||||||
|
Line =
|
||||||
|
case maps:get(line, Data, undefined) of
|
||||||
|
undefined ->
|
||||||
|
<<"">>;
|
||||||
|
Num ->
|
||||||
|
["(", integer_to_list(Num), ")"]
|
||||||
|
end,
|
||||||
|
case maps:get(mfa, Data, undefined) of
|
||||||
|
{M, F, A} ->
|
||||||
|
iolist_to_binary([
|
||||||
|
atom_to_binary(M, utf8),
|
||||||
|
$:,
|
||||||
|
atom_to_binary(F, utf8),
|
||||||
|
$/,
|
||||||
|
integer_to_binary(A),
|
||||||
|
Line
|
||||||
|
]);
|
||||||
|
_ ->
|
||||||
|
undefined
|
||||||
|
end;
|
||||||
|
format_mfal(_, _) ->
|
||||||
|
undefined.
|
||||||
|
|
||||||
-spec call_first_defined(list({module(), atom(), list()})) -> term() | no_return().
|
-spec call_first_defined(list({module(), atom(), list()})) -> term() | no_return().
|
||||||
call_first_defined([{Module, Function, Args} | Rest]) ->
|
call_first_defined([{Module, Function, Args} | Rest]) ->
|
||||||
try
|
try
|
||||||
|
|
Loading…
Reference in New Issue