defmodule BCUtils.LoggerFilters do @moduledoc """ Custom logger filters for reducing noise from commonly verbose libraries. This module provides pre-configured filters that can be used to reduce log noise from libraries like Swarm, LibCluster, and others while still allowing important error messages through. """ @doc """ Retrieves the list of default filters used to reduce logging noise from common libraries such as Swarm and LibCluster. These filters focus on allowing error and critical messages to pass while filtering out less severe log messages. ## Examples iex> filters = BCUtils.LoggerFilters.default_filters() [{:swarm_filter, {BCUtils.LoggerFilters, :filter_swarm}}, {:libcluster_filter, {BCUtils.LoggerFilters, :filter_libcluster}}] """ def default_filters do [ swarm_filter: {__MODULE__, :filter_swarm}, libcluster_filter: {__MODULE__, :filter_libcluster} ] end @doc """ Filter function to reduce Swarm logging noise. This filter allows only :error level messages from Swarm modules to pass through, filtering out :info, :debug, and :warning messages that create noise. ## Usage Add to your logger configuration: config :logger, :console, format: "$time [$level] $message\n", metadata: :all, filters: BCUtils.LoggerFilters.default_filters() """ def filter_swarm(%{meta: %{mfa: {module, _function, _arity}}, level: level} = log_event) do case {is_swarm_module?(module), level} do {true, :error} -> log_event # Allow Swarm errors through {true, _} -> :stop # Filter out non-error Swarm messages {false, _} -> log_event # Allow all non-Swarm messages end end # Handle log events without MFA metadata def filter_swarm(%{level: _level} = log_event), do: log_event @doc """ Filter function to reduce LibCluster logging noise. Similar to filter_swarm/1 but for LibCluster modules. """ def filter_libcluster(%{meta: %{mfa: {module, _function, _arity}}, level: level} = log_event) do case {is_libcluster_module?(module), level} do {true, :error} -> log_event # Allow LibCluster errors through {true, _} -> :stop # Filter out non-error LibCluster messages {false, _} -> log_event # Allow all non-LibCluster messages end end def filter_libcluster(%{level: _level} = log_event), do: log_event @doc """ Combined filter for both Swarm and LibCluster noise. ## Usage config :logger, :console, filters: [verbose_libs: {BCUtils.LoggerFilters, :filter_verbose_libs}] """ def filter_verbose_libs(%{meta: %{mfa: {module, _function, _arity}}, level: level} = log_event) do cond do is_swarm_module?(module) and level != :error -> :stop is_libcluster_module?(module) and level != :error -> :stop true -> log_event end end def filter_verbose_libs(%{level: _level} = log_event), do: log_event @doc """ Filter that only allows error and warning messages from specified modules. ## Usage config :logger, :console, filters: [ errors_only: {BCUtils.LoggerFilters, :errors_and_warnings_only} ] """ def errors_and_warnings_only(%{meta: %{mfa: {module, _function, _arity}}, level: level} = log_event) do case {is_noisy_module?(module), level} do {true, level} when level in [:error, :warning] -> log_event {true, _} -> :stop {false, _} -> log_event end end def errors_and_warnings_only(%{level: _level} = log_event), do: log_event ## Private Functions # Check if a module is part of Swarm defp is_swarm_module?(module) when is_atom(module) do module_string = Atom.to_string(module) String.starts_with?(module_string, "Elixir.Swarm") or module == Swarm.Logger # Handle Swarm.Logger specifically end defp is_swarm_module?(_), do: false # Check if a module is part of LibCluster defp is_libcluster_module?(module) when is_atom(module) do module_string = Atom.to_string(module) String.starts_with?(module_string, "Elixir.Cluster") or String.starts_with?(module_string, "Elixir.LibCluster") end defp is_libcluster_module?(_), do: false # Check if a module is generally known to be noisy defp is_noisy_module?(module) when is_atom(module) do is_swarm_module?(module) or is_libcluster_module?(module) or is_other_noisy_module?(module) end defp is_noisy_module?(_), do: false # Add other commonly noisy modules here defp is_other_noisy_module?(module) when is_atom(module) do module_string = Atom.to_string(module) # Add patterns for other noisy libraries String.starts_with?(module_string, "Elixir.Phoenix.PubSub") or String.starts_with?(module_string, "Elixir.Ranch") or String.starts_with?(module_string, "Elixir.Cowboy") end defp is_other_noisy_module?(_), do: false end