Current section
Files
Jump to
Current section
Files
src/logstasher_h.erl
-module(logstasher_h).
%% logger callbacks
-export([log/2]).
%% Xref ignores
-ignore_xref([log/2]).
%%==============================================================================
%% API
%%==============================================================================
-spec log(logger:log_event(), logger:handler_config()) -> ok.
log(#{level := info, meta := #{error_logger := #{type := progress}}}, _Config) ->
% Ignore supervisor progress reports
ok;
log(#{level := Level, msg := EventData, meta := Meta}, _Config) ->
{Msg, MsgFields} = format_msg(EventData),
Fields = [{severity, Level}] ++ safe_meta(Meta) ++ MsgFields,
Data = #{fields => Fields, '@timestamp' => format_timestamp(Meta), message => Msg},
_ = logstasher:send(jsx:encode(Data)),
ok.
%%==============================================================================
%% Internal functions
%%==============================================================================
-spec format_msg(Data) -> {binary(), [{binary() | atom(), jsx:json_term()}]} when
Data :: {io:format(), [term()]}
| {report, logger:report()}
| {string, unicode:chardata()}.
format_msg({string, Message}) ->
{unicode:characters_to_binary(Message), []};
format_msg({report, Report}) when is_map(Report) ->
format_msg({report, maps:to_list(Report)});
format_msg({report, Report}) when is_list(Report) ->
{proplists:get_value(msg, Report, null), safe_fields(Report)};
format_msg({Format, Params}) ->
{unicode:characters_to_binary(io_lib:format(Format, Params)), []}.
-spec format_timestamp(logger:metadata()) -> binary().
format_timestamp(#{time := Ts}) ->
list_to_binary(calendar:system_time_to_rfc3339(Ts, [{unit, microsecond}, {offset, "Z"}])).
-spec safe_meta(logger:metadata()) -> [{binary() | atom(), jsx:json_term()}].
safe_meta(Meta) ->
safe_fields(maps:to_list(Meta)).
-spec safe_fields([{term(), term()}]) -> [{binary() | atom(), jsx:json_term()}].
safe_fields(Terms) ->
lists:map(fun safe_field/1, Terms).
-spec safe_field({atom() | binary() | atom(), term()}) -> {atom() | binary(), jsx:json_term()}.
safe_field({Key, Value}) when is_atom(Key); is_binary(Key) ->
{Key, safe_value(Value)};
safe_field({Key, Value}) when is_list(Key) ->
safe_field({list_to_binary(Key), Value}).
-spec safe_value(term()) -> jsx:json_term().
safe_value(Pid) when is_pid(Pid) ->
list_to_binary(pid_to_list(Pid));
safe_value(List) when is_list(List) ->
case io_lib:char_list(List) of
true ->
list_to_binary(List);
false ->
lists:map(fun safe_value/1, List)
end;
safe_value(undefined) ->
null;
safe_value(Val) when is_binary(Val); is_atom(Val); is_integer(Val) ->
Val;
safe_value(Val) ->
unicode:characters_to_binary(io_lib:format("~p", [Val])).