Nebo15 / logger_json

JSON logger formatter with support for Google Cloud, DataDog and other for Elixir.

Home Page:https://nebo15.github.io/logger_json/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

protocol Jason.Encoder not implemented for tuple

dvic opened this issue · comments

I'm getting the following error (the value {1, 1, 1, 1} is dummy here)

Protocol.UndefinedError: protocol Jason.Encoder not implemented for {1, 1, 1, 1} of type Tuple, Jason.Encoder protocol must always be explicitly implemented. This protocol is implemented for the following type(s): ....
  File "lib/jason.ex", line 199, in Jason.encode_to_iodata!/2
  File "lib/logger_json.ex", line 290, in LoggerJSON.format_event/5
  File "lib/logger_json.ex", line 225, in LoggerJSON.log_event/5
  File "lib/logger_json.ex", line 127, in LoggerJSON.handle_event/2
  File "gen_event.erl", line 620, in :gen_event.server_update/4
  File "gen_event.erl", line 602, in :gen_event.server_notify/4
  File "gen_event.erl", line 343, in :gen_event.handle_msg/6
  File "proc_lib.erl", line 226, in :proc_lib.init_p_do_apply/3

It seems like the event that the formatter (GoogleCloudLogger in my case) cannot be encoded to JSON. I have no idea where this tuple is coming from (looks like an ip address). The GoogleCloudLogger should never crash right? The only thing I can think of is that one of the @processed_metadata_keys (pid, file, line, function, module or application) contains this tuple as value, because these are not "safe" converted but taken as-is. Do you have any idea on how I can debug this?

@dvic which version of LoggerJSON is used? In one of recent changes we have added SafeEncoder which inspects everything Jason does not support

Version 4.1 (76037738cd0b71bfb6230c8e3dc505c4f5789bb47e7a2e3d4a0421c6778d48e4), so it should include the SafeEncoder.

I should add that I'm using :all and also Sentry (which is probably the one adding the IP).

The problem could then be in take_metadata:

def take_metadata(metadata, :all, ignored_keys) do
    metadata
    |> Keyword.drop(ignored_keys ++ @ignored_metadata_keys)
    |> Enum.into(%{})
  end

which is called in

  defp format_metadata(md, md_keys) do
    LoggerJSON.take_metadata(md, md_keys, @processed_metadata_keys)
    |> JasonSafeFormatter.format()
    |> FormatterUtils.maybe_put(:error, FormatterUtils.format_process_crash(md))
    |> FormatterUtils.maybe_put(:"logging.googleapis.com/sourceLocation", format_source_location(md))
    |> FormatterUtils.maybe_put(:"logging.googleapis.com/operation", format_operation(md))
  end

Shouldn't the take_metadata function also apply SafeEncoder?

never mind, I missed the JasonSafeFormatter.format() call above

@dvic I think then we need to find a place where JasonSafeFormatter fails to encode that tuple. If you can submit a PR with a failing test case I can jump on it and fix it ASAP.

I'll try to see if I can replicate it in an test.

I'm not sure that PR fixes my example (which was plain {1, 1, 1, 1}) but I'll try to see if I can create a test case.

@dvic I had the exact same error in Sentry, just a plain tuple. Jason only complains about the innermost value that it cannot encode:

iex(3)> Jason.encode!([{1, 2}, 3])
** (Protocol.UndefinedError) protocol Jason.Encoder not implemented for {1, 2} of type Tuple, Jason.Encoder protocol must always be explicitly implemented. This protocol is implemented for the following type(s): ...
    (jason 1.2.2) lib/jason.ex:150: Jason.encode!/2

The only place that didn't call the format function recursively on each value was the function clause for formatting tuples.

Found another place: #75

@woylie wow, I see! Will try the master version