class Filter
A log filter which can be used to filter log messages based on severity, subject, and other criteria.
Definitions
def self.define_immutable_method(name, &block)
Define a method which can be shared between ractors.
Implementation
def self.define_immutable_method(name, &block)
block = Ractor.make_shareable(block)
self.define_method(name, &block)
end
def self.define_immutable_method(name, &block)
Define a method.
Implementation
def self.define_immutable_method(name, &block)
define_method(name, &block)
end
def self.[](**levels)
Create a new log filter with specific log levels.
class MyLogger < Console::Filter[debug: 0, okay: 1, bad: 2, terrible: 3]
Signature
-
parameter
levelsHash(Symbol, Integer) A hash of log levels.
Implementation
def self.[] **levels
klass = Class.new(self)
minimum_level, maximum_level = levels.values.minmax
klass.instance_exec do
const_set(:LEVELS, levels.freeze)
const_set(:MINIMUM_LEVEL, minimum_level)
const_set(:MAXIMUM_LEVEL, maximum_level)
# The default log level for instances of this filter class.
# Set to MINIMUM_LEVEL to allow all messages by default.
const_set(:DEFAULT_LEVEL, minimum_level)
levels.each do |name, level|
const_set(name.to_s.upcase, level)
define_immutable_method(name) do |subject = nil, *arguments, **options, &block|
if self.enabled?(subject, level)
@output.call(subject, *arguments, severity: name, **@options, **options, &block)
end
return nil
end
define_immutable_method("#{name}!") do
@level = level
end
define_immutable_method("#{name}?") do
@level <= level
end
end
end
return klass
end
def initialize(output, verbose: true, level: nil, **options)
Create a new log filter.
Signature
-
parameter
outputConsole::Output The output destination.
-
parameter
verboseBoolean Enable verbose output.
-
parameter
levelInteger The log level.
-
parameter
optionsHash Additional options.
Implementation
def initialize(output, verbose: true, level: nil, **options)
@output = output
@verbose = verbose
# Set the log level using the behaviour implemented in `level=`:
if level
self.level = level
else
@level = self.class::DEFAULT_LEVEL
end
@subjects = {}
@options = options
end
def with(level: @level, verbose: @verbose, **options)
Create a new log filter with the given options, from an existing log filter.
Signature
-
parameter
levelInteger The log level.
-
parameter
verboseBoolean Enable verbose output.
-
parameter
optionsHash Additional options.
-
returns
Console::Filter The new log filter.
Implementation
def with(level: @level, verbose: @verbose, **options)
dup.tap do |logger|
logger.level = level
logger.verbose! if verbose
logger.options = @options.merge(options)
end
end
attr_accessor :output
Signature
-
attribute
Console::Output The output destination.
attr :verbose
Signature
-
attribute
Boolean Whether to enable verbose output.
attr :level
Signature
-
attribute
Integer The current log level.
attr :subjects
Signature
-
attribute
Hash(Module, Integer) The log levels for specific subject (classes).
attr_accessor :options
Signature
-
attribute
Hash Additional options.
def level=(level)
Set the log level.
Signature
-
parameter
levelInteger | Symbol The log level.
Implementation
def level= level
if level.is_a? Symbol
@level = self.class::LEVELS[level]
else
@level = level
end
end
def verbose!(value = true)
Set verbose output (enable by default with no arguments).
Signature
-
parameter
valueBoolean Enable or disable verbose output.
Implementation
def verbose!(value = true)
@verbose = value
@output.verbose!(value)
end
def off!
Disable all logging.
Implementation
def off!
@level = self.class::MAXIMUM_LEVEL + 1
end
def all!
Enable all logging.
Implementation
def all!
@level = self.class::MINIMUM_LEVEL - 1
end
def filter(subject, level)
Filter log messages based on the subject and log level.
You must provide the subject's class, not an instance of the class.
Signature
-
parameter
subjectModule The subject to filter.
-
parameter
levelInteger The log level.
Implementation
def filter(subject, level)
unless subject.is_a?(Module)
raise ArgumentError, "Expected a class, got #{subject.inspect}"
end
@subjects[subject] = level
end
def enabled?(subject, level = self.class::MINIMUM_LEVEL)
Whether logging is enabled for the given subject and log level.
You can enable and disable logging for classes. This function checks if logging for a given subject is enabled.
Signature
-
parameter
subjectModule | Object The subject to check.
-
parameter
levelInteger The log level.
-
returns
Boolean Whether logging is enabled.
Implementation
def enabled?(subject, level = self.class::MINIMUM_LEVEL)
subject = subject.class unless subject.is_a?(Module)
if specific_level = @subjects[subject]
return level >= specific_level
end
if level >= @level
return true
end
end
def enable(subject, level = self.class::MINIMUM_LEVEL)
Enable specific log level for the given class.
Signature
-
parameter
nameModule The class to enable.
Implementation
def enable(subject, level = self.class::MINIMUM_LEVEL)
# Set the filter level of logging for a given subject which passes all log messages:
filter(subject, level)
end
def disable(subject)
Disable logging for the given class.
Signature
-
parameter
nameModule The class to disable.
Implementation
def disable(subject)
# Set the filter level of the logging for a given subject which filters all log messages:
filter(subject, self.class::MAXIMUM_LEVEL + 1)
end
def clear(subject)
Clear any specific filters for the given class.
Signature
-
parameter
subjectModule The class to disable.
Implementation
def clear(subject)
unless subject.is_a?(Module)
raise ArgumentError, "Expected a class, got #{subject.inspect}"
end
@subjects.delete(subject)
end
def call(subject, *arguments, **options, &block)
Log a message with the given severity.
If the severity is not defined in this filter's LEVELS (e.g., when chaining filters with different severity levels), the message is passed through to the output without filtering. This allows custom filters to be composed together.
Signature
-
parameter
subjectObject The subject of the log message.
-
parameter
argumentsArray The arguments to log.
-
parameter
optionsHash Additional options to pass to the output.
-
parameter
blockProc A block passed to the output.
-
returns
Nil Always returns nil.
Implementation
def call(subject, *arguments, **options, &block)
severity = options[:severity] || UNKNOWN
level = self.class::LEVELS[severity]
# If the severity is unknown (level is nil), pass through to output without filtering:
if level.nil? || self.enabled?(subject, level)
@output.call(subject, *arguments, **options, &block)
end
return nil
end