Current section

Files

Jump to
bc_utils lib logger_filters.ex
Raw

lib/logger_filters.ex

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