diff --git a/config/config.exs b/config/config.exs index a6e8a40e..642c5b0c 100644 --- a/config/config.exs +++ b/config/config.exs @@ -22,6 +22,14 @@ config :logger, :default_formatter, format: "$time $metadata[$level] $message\n", metadata: [:request_id] +# Filter out noisy errors from port scanners and bots +config :logger, :default_handler, + filters: [ + # Suppress HTTP/0.9 and other invalid protocol errors from Bandit + # These are typically from port scanners and automated bots + bandit_invalid_http: {&Towerops.LogFilter.filter_bandit_errors/2, []} + ] + # Register protobuf MIME type for agent API config :mime, :types, %{ "application/x-protobuf" => ["protobuf"] diff --git a/lib/towerops/log_filter.ex b/lib/towerops/log_filter.ex new file mode 100644 index 00000000..0dfe238b --- /dev/null +++ b/lib/towerops/log_filter.ex @@ -0,0 +1,48 @@ +defmodule Towerops.LogFilter do + @moduledoc """ + Filters out noisy log messages from external sources like port scanners and bots. + """ + + require Logger + + @doc """ + Filters out Bandit HTTP errors from invalid requests. + + This suppresses errors from: + - HTTP/0.9 requests (very old protocol, typically port scanners) + - Malformed HTTP requests from bots + - Invalid protocol versions + + These errors are expected when running a public-facing server and don't + indicate application problems. + """ + def filter_bandit_errors(log_event, _opts) do + case log_event do + # Filter Bandit.HTTPError with Invalid HTTP version + %{level: :error, msg: {:string, msg}} when is_binary(msg) -> + filter_string_message(msg) + + # Filter other Bandit connection errors + %{level: :error, msg: {:report, %{label: {:error_logger, :error_report}, report: report}}} -> + filter_error_report(report) + + # Don't filter anything else + _ -> + :ignore + end + end + + defp filter_string_message(msg) do + if bandit_invalid_http_error?(msg), do: :stop, else: :ignore + end + + defp filter_error_report(message: msg) when is_binary(msg) do + if String.contains?(msg, "Bandit.HTTPError"), do: :stop, else: :ignore + end + + defp filter_error_report(_), do: :ignore + + defp bandit_invalid_http_error?(msg) do + String.contains?(msg, "Bandit.HTTPError") and String.contains?(msg, "Invalid HTTP version") + end +end