fuelen/ecto_dev_logger

Crashing on a struct that gets persisted as JSON

bhuntpenn opened this issue · 4 comments

Hi there, I add Ecto.DevLogger.install(Brybags.Repo) to my Application.start/2 callback, and start the application.

The following crash is logged to the terminal.

10:46:32.388 [error] Handler [:ecto_dev_logger, :brybags, :repo] has failed and has been detached. Class=:error
Reason=%Protocol.UndefinedError{
  protocol: Ecto.DevLogger.PrintableParameter,
  value: %Cheese.Assignments{
    members: [%Cheese.Member{node: :"brybags@catah", can_provide: []}],
    assignments: [
      %Cheese.Assignment{
        service: Ingest.Providers.GoatsSupervisor,
        shard_id: 0,
        owning_node: nil
      },
      %Cheese.Assignment{
        service: Cheese.Registration.ButterSupervisor,
        shard_id: 1,
        owning_node: nil
      }
    ]
  },
  description: ""
}
Stacktrace=[
  {Ecto.DevLogger.PrintableParameter, :impl_for!, 1,
   [file: 'lib/ecto/dev_logger/printable_parameter.ex', line: 1]},
  {Ecto.DevLogger.PrintableParameter, :to_expression, 1,
   [file: 'lib/ecto/dev_logger/printable_parameter.ex', line: 31]},
  {Ecto.DevLogger, :"-inline_params/4-fun-1-", 3,
   [file: 'lib/ecto/dev_logger.ex', line: 109]},
  {Regex, :apply_list, 5, [file: 'lib/regex.ex', line: 756]},
  {Regex, :apply_list, 5, [file: 'lib/regex.ex', line: 751]},
  {Regex, :replace, 4, [file: 'lib/regex.ex', line: 686]},
  {Ecto.DevLogger, :"-telemetry_handler/4-fun-0-", 6,
   [file: 'lib/ecto/dev_logger.ex', line: 82]},
  {Logger, :__do_log__, 4, [file: 'lib/logger.ex', line: 884]},
  {:telemetry, :"-execute/3-fun-0-", 4,
   [
     file: '/Users/Bryan.Hunt/repos/brybags/devlogger_experiment/deps/telemetry/src/telemetry.erl',
     line: 135
   ]},
  {:lists, :foreach, 2, [file: 'lists.erl', line: 1342]},
  {Ecto.Adapters.SQL, :log, 4, [file: 'lib/ecto/adapters/sql.ex', line: 1078]},
  {DBConnection, :log, 5, [file: 'lib/db_connection.ex', line: 1559]},
  {Postgrex, :query_prepare_execute, 4, [file: 'lib/postgrex.ex', line: 361]},
  {Ecto.Adapters.SQL, :query!, 4, [file: 'lib/ecto/adapters/sql.ex', line: 438]},
  {Cheese.Adapters.Postgres.Server, :handle_call, 3,
   [file: 'lib/cheese/adapters/postgres/server.ex', line: 107]},
  {:gen_server, :try_handle_call, 4, [file: 'gen_server.erl', line: 721]},
  {:gen_server, :handle_msg, 6, [file: 'gen_server.erl', line: 750]},
  {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 226]}
]

I then install the handler manually, Ecto.DevLogger.install(Brybags.Repo) and everything works fine.

I was thinking, perhaps I should implement a PrintableParameter like such, but doesn't seem to have taken effect (I did edit the dep source code directly, but in any event, I'm not sure what approach I should take).

  defimpl Ecto.DevLogger.PrintableParameter, for: Any do # or maybe 
    # test
    def to_expression(x) when is_map(x) , do: to_string_literal( Map.from_struct(x))
    def to_string_literal(x), do: Jason.encode!(Map.from_struct(x))
  end

Or would I have to do something like this?

  defimpl Ecto.DevLogger.PrintableParameter, for: Cheese.Assignments.. 
  defimpl Ecto.DevLogger.PrintableParameter, for: Cheese.Assignment..
  defimpl Ecto.DevLogger.PrintableParameter, for: Cheese.Member.. 

I'm asking because I'm wondering if I can add something to improve first-time user experience, it works fine by manually running Ecto.DevLogger.install(Brybags.Repo) - but I'm thinking of the next person.

  • Some names have been changed to protect the guilty.

Hey @bhuntpenn

I then install the handler manually, Ecto.DevLogger.install(Brybags.Repo) and everything works fine.

It will work to the next invocation of the query that causes the error :)

I'd go with this:

defimpl Ecto.DevLogger.PrintableParameter, for: [Cheese.Assignments, Cheese.Assignment, Cheese.Member] do
  def to_expression(x) , do: to_string_literal(x)
  def to_string_literal(x), do: Jason.encode!(Map.from_struct(x))
end

Thanks for the help @fuelen !

@bhuntpenn I just want to add, that there was one small bug with map representation, the end result has to be wrapped in quotes 486c2bb
also, the changes you're doing are a result of updating from Ecto 3.9.0 to 3.9.1.
Ideally, you wouldn't have to manually convert your structs to JSON. Here is a corresponding issue I raised today elixir-ecto/ecto_sql#463

Hey @bhuntpenn
The fix is already in the main ecto_sql branch elixir-ecto/ecto_sql#464