Disabling logs for a route

Sometimes it's useful to customize or suppress logging for a specific route in a Phoenix application. One example I've encountered several times is dealing with heartbeat logs, either from dev tooling or from external services or hosts.

In this example, Vite (a front-end build tool I use) is repeatedly pinging the route /__vite_ping, due to a tab in local dev where the connection was lost when I restarted the server but didn't refresh the page. In order to suppress logging on this route, we look at endpoint.ex where logging is happening:

  # plug(Plug.Logger) # (for an older app)
  plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint])

In older apps, AppWeb.Endpoint uses Plug.Logger, but in more recent versions of Phoenix, the generated Endpoint module uses Plug.Telemetry. Either can be customized to have route-dependent behavior.

To customize the behavior of Telemetry, create a new Plug module, AppWeb.Plugs.Telemetry. It doesn't have to be named this, but I generally put plug modules into a plugs directory under the main <app name>_web directory. In this case, it's lib/app_web/plugs/telemetry.ex.

defmodule AppWeb.Plugs.Telemetry do
  @behaviour Plug
  # ...
end

To implement the Plug behaviour (note the spelling), we need to define two functions, init/1 and call/2. The simplest version of this module would just use the Plug.Telemetry library's functions for each, like this:

defmodule AppWeb.Plugs.Telemetry do
  @behaviour Plug

  @impl true
  def init(opts), do: Plug.Telemetry.init(opts)

  @impl true
  def call(conn, opts) do
    Plug.Telemetry.call(conn, opts)
  end
end

Just dishing off calls to use the Telemetry library's built-in functions won't actually change the behavior of our app, though, so we also need to add a function head for call that matches the "__vite_ping" path were we don't want logging and handle it without invoking Plug.Telemetry.call/2).

defmodule AppWeb.Plugs.Telemetry do
  @behaviour Plug

  @impl true
  def init(opts), do: Plug.Telemetry.init(opts)

  @impl true
  def call(%{path_info: ["__vite_ping"]} = conn, opts) do
    conn
    |> Plug.Conn.send_resp(200, "")
    |> Plug.Conn.halt()
  end

  def call(conn, opts) do
    Plug.Telemetry.call(conn, opts)
  end
end

The final step is to replace the call to Plug.Telemetry with our customized version in endpoint.ex.

  plug(AppWeb.Plugs.Telemetry, event_prefix: [:phoenix, :endpoint])
Back to index