Ignore pod shutdown errors in ErrorTracker

Add ErrorTrackerIgnorer to drop port_died, write_failed, epipe, and
broken pipe errors that occur during K8s pod shutdown. Same patterns
already filtered by HoneybadgerFilter and LoggerFilters.
This commit is contained in:
Graham McIntire 2026-02-17 10:59:43 -06:00
parent 90c24cce49
commit 0f32086933
No known key found for this signature in database
3 changed files with 71 additions and 1 deletions

View file

@ -12,7 +12,8 @@ config :elixir, :time_zone_database, Tzdata.TimeZoneDatabase
config :error_tracker,
repo: Towerops.Repo,
otp_app: :towerops
otp_app: :towerops,
ignorer: Towerops.ErrorTrackerIgnorer
# Configure esbuild (the version is required)
config :esbuild,

View file

@ -0,0 +1,24 @@
defmodule Towerops.ErrorTrackerIgnorer do
@moduledoc """
Drops benign errors from ErrorTracker that occur during K8s pod shutdown.
Matches the same patterns as `Towerops.HoneybadgerFilter` and
`Towerops.LoggerFilters.drop_shutdown_errors/2`.
"""
@behaviour ErrorTracker.Ignorer
@impl true
def ignore?(%ErrorTracker.Error{reason: reason}, _context) do
shutdown_error?(reason)
end
defp shutdown_error?(reason) when is_binary(reason) do
String.contains?(reason, "port_died") or
String.contains?(reason, "write_failed") or
String.contains?(reason, "epipe") or
String.contains?(reason, "broken pipe")
end
defp shutdown_error?(_), do: false
end

View file

@ -0,0 +1,45 @@
defmodule Towerops.ErrorTrackerIgnorerTest do
use ExUnit.Case, async: true
alias Towerops.ErrorTrackerIgnorer
defp build_error(reason) do
%ErrorTracker.Error{
kind: "ErlangError",
reason: reason,
source_line: "-",
source_function: "-",
status: :unresolved,
fingerprint: "abc",
last_occurrence_at: DateTime.utc_now(),
muted: false
}
end
describe "ignore?/2" do
test "ignores port_died errors" do
error = build_error("Erlang error: {:port_died, :normal}")
assert ErrorTrackerIgnorer.ignore?(error, %{})
end
test "ignores write_failed errors" do
error = build_error("{:write_failed, :standard_io, :enoent}")
assert ErrorTrackerIgnorer.ignore?(error, %{})
end
test "ignores epipe errors" do
error = build_error("{:error, {:write_failed, %{type: :standard_io}, {:badarg, :epipe}}}")
assert ErrorTrackerIgnorer.ignore?(error, %{})
end
test "ignores broken pipe errors" do
error = build_error("broken pipe (epipe)")
assert ErrorTrackerIgnorer.ignore?(error, %{})
end
test "does not ignore regular errors" do
error = build_error("something went wrong")
refute ErrorTrackerIgnorer.ignore?(error, %{})
end
end
end