# -*- ruby -*- # vim: set nosta noet ts=4 sw=4: # encoding: utf-8 require 'logger' require 'date' # A mixin that provides a top-level logging subsystem based on Logger. module Loggability # Package version constant VERSION = '0.0.2' # VCS revision REVISION = %q$Revision: 1099204b229f $ # The key for the global logger (Loggability's own logger) GLOBAL_KEY = :__global__ # The methods that are delegated across all loggers AGGREGATE_METHODS = [ :level=, :output_to, :write_to, :format_with, :format_as, :formatter= ] require 'loggability/constants' include Loggability::Constants require 'loggability/logger' ## # The Hash of modules that have a Logger, keyed by the name they register with class << self; attr_reader :log_hosts; end @log_hosts = {} ### Return the library's version string def self::version_string( include_buildnum=false ) vstring = "%s %s" % [ self.name, VERSION ] vstring << " (build %s)" % [ REVISION[/: ([[:xdigit:]]+)/, 1] || '0' ] if include_buildnum return vstring end ### Register the specified +host+ as a log host. It should already have been extended ### with LogHostMethods. def self::register_loghost( host ) key = host.log_host_key if self.log_hosts.key?( key ) self.logger.warn "Replacing existing log host for %p (%p) with %p" % [ key, self.log_hosts[key], host ] end self.logger.debug "Registering %p log host: %p" % [ key, host ] if self.logger self.log_hosts[ key ] = host end ### Return the Loggability::Logger for the loghost associated with +logclient+. def self::[]( logclient ) key = logclient.log_host_key if logclient.respond_to?( :log_host_key ) key ||= GLOBAL_KEY return self.log_hosts[ key ].logger end ### Clear out all log hosts except for ones which start with '_'. This is intended ### to be used for testing. def self::clear_loghosts self.log_hosts.delete_if {|key,_| !key.to_s.start_with?('_') } end # # :section: Aggregate Methods # ### Call the method with the given +methodname+ across the loggers of all loghosts with ### the given +arg+ and/or +block+. def self::aggregate( methodname, arg, &block ) Loggability.log_hosts.values.each do |loghost| loghost.logger.send( methodname, arg, &block ) end end ## # :method: level= # :call-seq: # level = newlevel # # Aggregate method: set the log level on all loggers to +newlevel+. See # Loggability::Logger#level= for more info. ## # :method: output_to # :call-seq: # output_to( destination ) # write_to( destination ) # # Aggregate method: set all loggers to log to +destination+. See Loggability::Logger#output_to # for more info. ## # :method: format_with # :call-seq: # format_with( formatter ) # format_as( formatter ) # formatter = formatter # # Aggregate method: set all loggers to log with the given +formatter+. See # Loggability::Logger#format_with for more info. AGGREGATE_METHODS.each do |meth| block = self.method( :aggregate ).to_proc.curry[ meth ] Loggability.singleton_class.send( :define_method, meth, &block ) end # Extension for 'log hosts'. A log host is an object that hosts a Loggability::Logger # object, and is typically the top of some kind of hierarchy, like a namespace # module for a project: # # module MyProject # # end # # This module isn't mean to be used directly -- it's installed via the Loggability#log_as # declaration, which also does some other initialization that you'll likely want. # # module LogHost # The logger that will be used when the logging subsystem is reset attr_accessor :default_logger # The logger that's currently in effect attr_accessor :logger alias_method :log, :logger alias_method :log=, :logger= # The key associated with the logger for this host attr_accessor :log_host_key end # module LogHost # # :section: LogHost API # ### Register as a log host associated with the given +key+, add the methods from ### LogHost, and install a Loggability::Logger. def log_as( key ) self.extend( Loggability::LogHost ) self.log_host_key = key.to_sym self.logger = self.default_logger = Loggability::Logger.new Loggability.register_loghost( self ) end # Install a global logger in Loggability itself extend( Loggability::LogHost ) self.log_host_key = GLOBAL_KEY self.logger = self.default_logger = Loggability::Logger.new Loggability.register_loghost( self ) # Methods to install for objects which call +log_to+. module LogClient ## # The key of the log host this client targets attr_accessor :log_host_key ### Return the Loggability::Logger object associated with the log host the ### client is logging to. ### :TODO: Use delegation for efficiency. def log @__log ||= Loggability[ self ].proxy_for( self ) end # Stuff that gets added to instances of Classes that are log hosts. module InstanceMethods ### Fetch the key of the log host the instance of this client targets def log_host_key return self.class.log_host_key end ### Delegate to the class's logger. def log @__log ||= Loggability[ self.class ].proxy_for( self ) end end # module InstanceMethods end # module LogClient # # :section: LogClient API # ### Register as a log client that will log to to the given +loghost+, which can be ### either the +key+ the host registered with, or the log host object itself. Log messages ### can be written to the loghost via the LogClient API, which is automatically included. def log_to( loghost ) self.extend( Loggability::LogClient ) self.log_host_key = if loghost.respond_to?( :log_host_key ) loghost.log_host_key else loghost.to_sym end # For objects that also can be instantiated include( Loggability::LogClient::InstanceMethods ) if self.is_a?( Class ) end end # module Strelka