ConsoleSourceConsoleResolver

class Resolver

Represents a class resolver that can be used to set log levels for different classes as they become available.

Definitions

def self.default_resolver(logger, env = ENV)

You can change the log level for different classes using CONSOLE_$LEVEL env vars.

e.g. CONSOLE_WARN=Acorn,Banana CONSOLE_DEBUG=Cat will set the log level for the classes Acorn and Banana to warn and Cat to debug. This overrides the default log level.

You can enable all log levels for a given class by using CONSOLE_ON=MyClass. Similarly you can disable all logging using CONSOLE_OFF=MyClass.

Signature

parameter logger Logger

A logger instance to set the logging levels on.

parameter env Hash

The environment to read levels from.

returns Nil

If there were no custom logging levels specified in the environment.

returns Resolver

If there were custom logging levels, then the created resolver is returned.

Implementation

def self.default_resolver(logger, env = ENV)
	# Find all CONSOLE_$LEVEL variables from environment:
	levels = logger.class::LEVELS
		.map{|label, level| [level, env["CONSOLE_#{label.upcase}"]&.split(",")]}
		.to_h
		.compact
	
	off_klasses = env["CONSOLE_OFF"]&.split(",")
	on_klasses = env["CONSOLE_ON"]&.split(",")
	
	resolver = nil
	
	# If we have any levels, then create a class resolver, and each time a class is resolved, set the log level for that class to the specified level:
	if on_klasses&.any?
		resolver ||= Resolver.new
		
		resolver.bind(on_klasses) do |klass|
			logger.enable(klass, logger.class::MINIMUM_LEVEL - 1)
		end
	end
	
	if off_klasses&.any?
		resolver ||= Resolver.new
		
		resolver.bind(off_klasses) do |klass|
			logger.disable(klass)
		end
	end
	
	levels.each do |level, names|
		resolver ||= Resolver.new
		
		resolver.bind(names) do |klass|
			logger.enable(klass, level)
		end
	end
	
	return resolver
end

def initialize

Create a new class resolver.

Implementation

def initialize
	@names = {}
	
	@trace_point = TracePoint.new(:class, &self.method(:resolve))
end

def bind(names, &block)

Bind the given class names to the given block. When the class name is resolved into an actual class, the block will be called with the class as an argument.

If the class is already defined, the block will be called immediately.

If the class is not defined, the block will be called when the class is defined, using a trace point.

Signature

parameter names Array(String)

The class names to bind.

parameter block Proc

The block to call when the class is resolved.

Implementation

def bind(names, &block)
	names.each do |name|
		if klass = Object.const_get(name) rescue nil
			yield klass
		else
			@names[name] = block
		end
	end
	
	if @names.any?
		@trace_point.enable
	else
		@trace_point.disable
	end
end

def waiting?

Signature

returns Boolean

True if the resolver is waiting for classes to be defined.

Implementation

def waiting?
	@trace_point.enabled?
end

def resolve(trace_point)

Invoked by the trace point when a class is defined.

This will call the block associated with the class name, if any, and remove it from the list of names to resolve.

If the list of names is empty, the trace point will be disabled.

Signature

parameter trace_point TracePoint

The trace point that triggered the event.

Implementation

def resolve(trace_point)
	if block = @names.delete(trace_point.self.to_s)
		block.call(trace_point.self)
	end
	
	if @names.empty?
		@trace_point.disable
	end
end