class Console::Filter

A log filter which can be used to filter log messages based on severity, subject, and other criteria.

def self.[] **levels

@parameter levels [Hash(Symbol, Integer)] A hash of log levels.

```
class MyLogger < Console::Filter[debug: 0, okay: 1, bad: 2, terrible: 3]
```ruby

Create a new log filter with specific log levels.
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)
		
		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 self.define_immutable_method(name, &block)

Define a method which can be shared between ractors.
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.
def self.define_immutable_method(name, &block)
	define_method(name, &block)
end

def all!

Enable all logging.
def all!
	@level = self.class::MINIMUM_LEVEL - 1
end

def call(subject, *arguments, **options, &block)

@returns [Nil] Always returns nil.
@parameter block [Proc] A block passed to the output.
@parameter options [Hash] Additional options to pass to the output.
@parameter arguments [Array] The arguments to log.
@parameter subject [Object] The subject of the log message.

Log a message with the given severity.
def call(subject, *arguments, **options, &block)
	severity = options[:severity] || UNKNOWN
	level = self.class::LEVELS[severity]
	
	if self.enabled?(subject, level)
		@output.call(subject, *arguments, **options, &block)
	end
	
	return nil
end

def clear(subject)

@parameter subject [Module] The class to disable.

Clear any specific filters for the given class.
def clear(subject)
	unless subject.is_a?(Module)
		raise ArgumentError, "Expected a class, got #{subject.inspect}"
	end
	
	@subjects.delete(subject)
end

def disable(subject)

@parameter name [Module] The class to disable.

Disable logging for the given class.
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 enable(subject, level = self.class::MINIMUM_LEVEL)

@parameter name [Module] The class to enable.

Enable specific log level for the given class.
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 enabled?(subject, level = self.class::MINIMUM_LEVEL)

@returns [Boolean] Whether logging is enabled.
@parameter level [Integer] The log level.
@parameter subject [Module | Object] The subject to check.

You can enable and disable logging for classes. This function checks if logging for a given subject is enabled.

Whether logging is enabled for the given subject and log level.
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 filter(subject, level)

@parameter level [Integer] The log level.
@parameter subject [Module] The subject to filter.

You must provide the subject's class, not an instance of the class.

Filter log messages based on the subject and log level.
def filter(subject, level)
	unless subject.is_a?(Module)
		raise ArgumentError, "Expected a class, got #{subject.inspect}"
	end
	
	@subjects[subject] = level
end

def initialize(output, verbose: true, level: nil, **options)

@parameter options [Hash] Additional options.
@parameter level [Integer] The log level.
@parameter verbose [Boolean] Enable verbose output.
@parameter output [Console::Output] The output destination.

Create a new log filter.
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 level= level

@parameter level [Integer | Symbol] The log level.

Set the log level.
def level= level
	if level.is_a? Symbol
		@level = self.class::LEVELS[level]
	else
		@level = level
	end
end

def off!

Disable all logging.
def off!
	@level = self.class::MAXIMUM_LEVEL + 1
end

def verbose!(value = true)

@parameter value [Boolean] Enable or disable verbose output.

Set verbose output (enable by default with no arguments).
def verbose!(value = true)
	@verbose = value
	@output.verbose!(value)
end

def with(level: @level, verbose: @verbose, **options)

@returns [Console::Filter] The new log filter.
@parameter options [Hash] Additional options.
@parameter verbose [Boolean] Enable verbose output.
@parameter level [Integer] The log level.

Create a new log filter with the given options, from an existing log filter.
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