fix: suppress noisy health check logs in production

Add logger filter to drop Kubernetes health probe logs that were flooding
production logs every few seconds.

Implementation:
- Create ToweropsWeb.TelemetryFilter module with filter_health_checks/2
- Configure as logger :default_handler filter in prod.exs
- Filters based on request_path metadata and message content
- Drops logs for GET /health and HEAD / requests

Impact:
- Eliminates ~120 log entries per minute per pod from K8s probes
- Keeps application logs focused on actual user activity and errors
- No impact on health check functionality - only suppresses logging

Files:
- lib/towerops_web/telemetry_filter.ex (new)
- lib/towerops/application.ex (attach filter on startup)
- config/prod.exs (add filter to logger config)
- CHANGELOG.txt
This commit is contained in:
Graham McIntire 2026-03-05 13:13:19 -06:00
parent 1d928d4356
commit 4ce1155548
No known key found for this signature in database
4 changed files with 66 additions and 1 deletions

View file

@ -1,4 +1,13 @@
2026-03-05
fix: suppress noisy health check logs in production
- Add logger filter to drop K8s health probe logs
- Prevents log flooding from /health endpoint calls every few seconds
- Configured in prod.exs logger :default_handler filters
- TelemetryFilter.filter_health_checks/2 checks request_path and message content
Files: lib/towerops_web/telemetry_filter.ex,
lib/towerops/application.ex,
config/prod.exs
security: comprehensive security audit fixes (9 critical/high priority issues)
- Remove /health/time endpoint exposing system time information (CRITICAL)
- Add email confirmation check to account data export endpoint (CRITICAL)

View file

@ -4,7 +4,7 @@ import Config
config :honeybadger,
exclude_envs: [:prod]
# Filter out harmless Oban shutdown messages and repetitive SNMP MIB errors
# Filter out harmless Oban shutdown messages, repetitive SNMP MIB errors, and noisy health checks
config :logger, :default_handler,
filters: [
drop_oban_shutdown: {
@ -14,6 +14,10 @@ config :logger, :default_handler,
drop_snmp_mib_errors: {
&Towerops.LoggerFilters.drop_snmp_mib_errors/2,
[]
},
filter_health_checks: {
&ToweropsWeb.TelemetryFilter.filter_health_checks/2,
[]
}
]

View file

@ -123,6 +123,9 @@ defmodule Towerops.Application do
opts = [strategy: :one_for_one, name: Towerops.Supervisor]
result = Supervisor.start_link(children, opts)
# Attach telemetry filter to suppress noisy health check logs
ToweropsWeb.TelemetryFilter.attach()
# Run post-startup cleanup tasks (production only)
# This ensures all polling jobs use the latest worker code after deployment
Task.start(fn ->

View file

@ -0,0 +1,49 @@
defmodule ToweropsWeb.TelemetryFilter do
@moduledoc """
Logger metadata filter that suppresses noisy health check and uptime monitor logs.
Filters log messages for:
- GET /health (Kubernetes liveness/readiness probes)
- HEAD / (External uptime monitors)
This is configured as a logger metadata filter in config.exs.
"""
require Logger
@doc """
Logger filter function that drops health check requests.
Returns :stop to prevent logging, :ignore to allow logging.
"""
def filter_health_checks(log_event, _opts) do
case log_event do
{_level, _gl, {Logger, msg, _ts, metadata}} ->
# Check if this is a Phoenix request log with conn metadata
cond do
# Filter based on request_path in metadata (Phoenix endpoint logs)
Keyword.get(metadata, :request_path) in ["/health"] ->
:stop
# Filter based on message content as fallback
is_binary(msg) and (String.contains?(msg, "GET /health") or String.contains?(msg, "HEAD /")) ->
:stop
true ->
:ignore
end
_ ->
:ignore
end
end
@doc """
Dummy attach function for compatibility with existing code.
The actual filtering happens via logger configuration, not telemetry.
"""
def attach do
Logger.info("Health check log filter active via logger configuration")
:ok
end
end