honeybadger-elixir icon indicating copy to clipboard operation
honeybadger-elixir copied to clipboard

Honeybadger.Logger process starts in wrong environments and terminates/times out for no clear reason

Open kwayebopp opened this issue 5 years ago • 4 comments

Hi! I have an Elixir/Phoenix app using Honeybadger for error reporting and logging. During development (and I suspect occasionally in production) the server throws a DBConnection.ConnectionError. This happens from time to time, and I'm working on optimizing queries. What is strange is that I see the Honeybadger application is running as well, as I've tried to disable it in the development environment.

Here's an example of one such error:

[info] Postgrex.Protocol (#PID<0.435.0>) disconnected: ** (DBConnection.ConnectionError) client #PID<0.1575.0> exited
[error] #PID<0.1563.0> running CoolAppWeb.Endpoint (connection #PID<0.1548.0>, stream id 3) terminated
Server: subdomain.localhost:4000 (http)
Request: POST /graphql
** (exit) exited in: Task.await(%Task{owner: #PID<0.1563.0>, pid: #PID<0.1575.0>, ref: #Reference<0.3695852442.3919314945.95728>}, 5000)
** (EXIT) time out
[error] :gen_event handler Honeybadger.Logger installed in Logger terminating
** (FunctionClauseError) no function clause matching in Honeybadger.Notice.new/3
(honeybadger) lib/honeybadger/notice.ex:36: Honeybadger.Notice.new(:timeout, %{context: %{}}, {Task, :await, [%Task{owner: #PID<0.1563.0>, pid: #PID<0.1575.0>, ref: #Reference<0.3695852442.3919314945.95728>}, 5000]})
(honeybadger) lib/honeybadger.ex:207: Honeybadger.notify/3
(honeybadger) lib/honeybadger/logger.ex:35: Honeybadger.Logger.handle_event/2
(stdlib) gen_event.erl:577: :gen_event.server_update/4
(stdlib) gen_event.erl:559: :gen_event.server_notify/4
(stdlib) gen_event.erl:300: :gen_event.handle_msg/6
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Last message: {:error, #PID<0.427.0>, {Logger, ["#PID<0.1563.0>", " running ", "CoolAppWeb.Endpoint", [" (connection ", "#PID<0.1548.0>", ", stream id ", "3", 41], " terminated\n", [["Server: ", "subdomain.localhost", ":", "4000", 32, 40, "http", 41, 10], ["Request: ", "POST", 32, "/graphql", 10]] | "** (exit) exited in: Task.await(%Task{owner: #PID<0.1563.0>, pid: #PID<0.1575.0>, ref: #Reference<0.3695852442.3919314945.95728>}, 5000)\n ** (EXIT) time out"], {{2019, 12, 6}, {12, 39, 18, 576}}, [crash_reason: {:timeout, {Task, :await, [%Task{owner: #PID<0.1563.0>, pid: #PID<0.1575.0>, ref: #Reference<0.3695852442.3919314945.95728>}, 5000]}}, pid: #PID<0.1548.0>]}}
State: %{level: nil}
[error] GenServer #PID<0.409.0> terminating
** (stop) {:EXIT, {:function_clause, [{Honeybadger.Notice, :new, [:timeout, %{context: %{}}, {Task, :await, [%Task{owner: #PID<0.1563.0>, pid: #PID<0.1575.0>, ref: #Reference<0.3695852442.3919314945.95728>}, 5000]}], [file: 'lib/honeybadger/notice.ex', line: 36]}, {Honeybadger, :notify, 3, [file: 'lib/honeybadger.ex', line: 207]}, {Honeybadger.Logger, :handle_event, 2, [file: 'lib/honeybadger/logger.ex', line: 35]}, {:gen_event, :server_update, 4, [file: 'gen_event.erl', line: 577]}, {:gen_event, :server_notify, 4, [file: 'gen_event.erl', line: 559]}, {:gen_event, :handle_msg, 6, [file: 'gen_event.erl', line: 300]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 249]}]}}
Last message: {:gen_event_EXIT, Honeybadger.Logger, {:EXIT, {:function_clause, [{Honeybadger.Notice, :new, [:timeout, %{context: %{}}, {Task, :await, [%Task{owner: #PID<0.1563.0>, pid: #PID<0.1575.0>, ref: #Reference<0.3695852442.3919314945.95728>}, 5000]}], [file: 'lib/honeybadger/notice.ex', line: 36]}, {Honeybadger, :notify, 3, [file: 'lib/honeybadger.ex', line: 207]}, {Honeybadger.Logger, :handle_event, 2, [file: 'lib/honeybadger/logger.ex', line: 35]}, {:gen_event, :server_update, 4, [file: 'gen_event.erl', line: 577]}, {:gen_event, :server_notify, 4, [file: 'gen_event.erl', line: 559]}, {:gen_event, :handle_msg, 6, [file: 'gen_event.erl', line: 300]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 249]}]}}}
State: {Logger, Honeybadger.Logger}

As stated above, I tried to take measures to ensure that Honeybadger doesn't run during development. In mix.exs a function is used to collect the appropriate extra_applications:

mix.exs:

  defp get_extra_applications do
    apps = [
      :logger,
      :runtime_tools,
      #some other applications...
    ]

    case Mix.env() do
      :prod -> apps ++ [:honeybadger] # we only want honeybadger running in production
      :test -> apps ++ [:ex_machina] 
      :dev -> apps
    end
  end

  def application do
    [
      mod: {CoolApp.Application, []},
      extra_applications: get_extra_applications()
    ]
  end

In addition to that, I tried to make sure that the Honeybadger.Plug is only conditionally used in router.ex:

defmodule CoolAppWeb.Router do
  use CoolAppWeb, :router

  if Mix.env() === :prod do # only use the plug in production
    use Honeybadger.Plug
  end

...

This behavior is probably not correct, so I think I maybe have something wrong in my setup somewhere. In the interest of putting everything up for examination I've included the dev and prod configs.

config.exs:

config :honeybadger,
app: :cool_app,
exclude_envs: [:dev, :test],
filter: Honeybadger.Filter.Default,
filter_keys: [:password],
api_key: System.get_env("HONEYBADGER_API_KEY")

dev.exs:

config :cool_app, CoolApp.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: ...,
  password: ...,
  database: ...,
  hostname: "localhost",
  pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
  queue_target: 5_000

config :honeybadger, environment_name: :dev

prod.exs:

config :cool_app, CoolApp.Repo,
  adapter: Ecto.Adapters.Postgres,
  url: System.get_env("DATABASE_URL"),
  pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
  ssl: true,
  queue_target: 5_000

config :honeybadger, environment_name: :prod

This problem is a real head-scratcher for me, so any and all help or suggestions are greatly appreciated!

kwayebopp avatar Dec 09 '19 13:12 kwayebopp

You can disable the logger in dev:

config :honeybadger, environment_name: :dev, use_logger: false

However, that shouldn't be necessary. The logger shouldn't ever crash when attempting to send a notice. I'm curious how the call to Notice.new/3 got a single stack line and not a list of stacklines:

(honeybadger) lib/honeybadger/notice.ex:36: Honeybadger.Notice.new(:timeout, %{context: %{}}, {Task, :await, [%Task{owner: #PID<0.1563.0>, pid: #PID<0.1575.0>, ref: #Reference<0.3695852442.3919314945.95728>}, 5000]})

I have a potential fix for that issue ready, but I can't recreate it with any tests.

sorentwo avatar Dec 09 '19 15:12 sorentwo

Looking at the line, the error is a timeout, and the integer at the end is the same value as my :queue_target config. Maybe that has something to do with it?

kwayebopp avatar Dec 09 '19 21:12 kwayebopp

That is the format of every error tuple in a stacktrace. The strange part is that it isn't part of a list.

sorentwo avatar Dec 10 '19 23:12 sorentwo

Here to chime in that we just experienced a similar issue where the Honeybadger.Notice.new call fails because the stacktrace is a tuple and not a list.

** (FunctionClauseError) no function clause matching in Honeybadger.Notice.new/4
    (honeybadger 0.15.0) lib/honeybadger/notice.ex:38: Honeybadger.Notice.new(:timeout, %{breadcrumbs: %{enabled: true, trail: [%Honeybadger.Breadcrumbs.Breadcrumb{category: "error", message: "ErlangError", metadata: %{message: "Erlang error: :timeout"}, timestamp: ~U[2021-03-12 16:26:05.940350Z]}, %Honeybadger.Breadcrumbs.Breadcrumb{category: "notice", message: "Honeybadger Notice: timeout", metadata: %{}, timestamp: ~U[2021-03-12 16:26:05.940373Z]}]}, context: %{domain: [:cowboy], erl_level: :error, error_logger: %{tag: :error}, gl: #PID<0.444.0>, report_cb: #Function<0.118689316/1 in :error_logger."-fun.report_to_format/1-">, time: 1615566365940243}}, {GenServer, :call, [:XXX, {:valid_account, "XXX:XXX"}, 5000]}, "")
    (honeybadger 0.15.0) lib/honeybadger.ex:297: Honeybadger.notify/2
    (honeybadger 0.15.0) lib/honeybadger/logger.ex:37: Honeybadger.Logger.handle_event/2
    (stdlib 3.13.2) gen_event.erl:620: :gen_event.server_update/4
    (stdlib 3.13.2) gen_event.erl:602: :gen_event.server_notify/4
    (stdlib 3.13.2) gen_event.erl:343: :gen_event.handle_msg/6
    (stdlib 3.13.2) proc_lib.erl:226: :proc_lib.init_p_do_apply/3

jimmybot avatar Mar 12 '21 20:03 jimmybot