Class: Arrow::Logger

Inherits:
Object
  • Object
show all
Extended by:
DebugLogger, Configurability
Includes:
DebugLogger
Defined in:
lib/arrow/logger.rb

Overview

A hierarchical logging class for the Arrow framework. It provides a generalized means of logging from inside Arrow classes, and then selectively outputting/formatting log messages from points within the hierarchy.

A lot of concepts in this class were stolen from Log4r, though it’s all original code, and works a bit differently.

Synopsis

require 'arrow/object'
require 'arrow/logger'

logger = Arrow::Logger.global
logfile = File.open( "global.log", "a" )
logger.outputters << Arrow::Logger::Outputter.new(logfile)
logger.level = :debug

class MyClass < Arrow::Object

    def self::fooMethod
        Arrow::Logger.debug( "In server start routine" )
        Arrow::Logger.info( "Server is not yet configured." )
        Arrow::Logger.notice( "Server is starting up." )
    end

    def initialize
        self.log.info( "Initializing another MyClass object." )
    end
end

VCS Id

$Id: logger.rb,v ba725438313d 2010/08/09 02:08:40 ged $

Authors

Please see the file LICENSE in the top-level directory for licensing details.

Defined Under Namespace

Modules: DebugLogger Classes: ApacheOutputter, ArrayOutputter, ColorOutputter, FileOutputter, HtmlOutputter, Outputter

Constant Summary collapse

LEVELS =

Construct a log levels Hash on the fly

[
	:debug,
	:info,
	:notice,
	:warning,
	:error,
	:crit,
	:alert,
	:emerg,
].inject({}) {|hsh, sym| hsh[ sym ] = hsh.length; hsh}
LEVEL_NAMES =
LEVELS.invert

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from DebugLogger

debug_msg

Constructor Details

#initialize(mod, level = :info, *outputters) ⇒ Logger

Create and return a new Arrow::Logger object for the given mod (a Module object). If It will be configured at the given level. Any outputters that are specified will be added.



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/arrow/logger.rb', line 208

def initialize( mod, level=:info, *outputters )
	@module     = mod
	@outputters = outputters
	@trace      = false
	@level      = nil

	# Cached Array of modules and classes between 
	# this logger's module and Object
	@supermods  = nil

	# Level to force messages written to this logger to
	@forced_level = nil

	self.level  = level
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(sym, *args) ⇒ Object (protected)

Auto-install logging methods (ie., methods whose names match one of Arrow::Logger::LEVELS.



451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
# File 'lib/arrow/logger.rb', line 451

def method_missing( sym, *args )
	name = sym.to_s
	level = name[/\w+/].to_sym
	return super unless Arrow::Logger::LEVELS.member?( level )
	code = nil

	case name
	when /^\w+\?/
		code = self.make_level_predicate_method( level )

	when /^\w+$/
		code = self.make_writer_method( level )

	else
		return super
	end

	self.class.send( :define_method, sym, &code )
	return self.method( sym ).call( *args )
end

Class Attribute Details

.logger_mapObject (readonly)

Returns the value of attribute logger_map.



87
88
89
# File 'lib/arrow/logger.rb', line 87

def logger_map
  @logger_map
end

Instance Attribute Details

#forced_levelObject

The level to force messages written to this logger to



242
243
244
# File 'lib/arrow/logger.rb', line 242

def forced_level
  @forced_level
end

#levelObject

The integer level of the logger.



239
240
241
# File 'lib/arrow/logger.rb', line 239

def level
  @level
end

#moduleObject (readonly)

The module this logger is associated with



230
231
232
# File 'lib/arrow/logger.rb', line 230

def module
  @module
end

#outputtersObject

The outputters attached to this branch of the logger tree.



233
234
235
# File 'lib/arrow/logger.rb', line 233

def outputters
  @outputters
end

#traceObject

Set to a true value to turn tracing on



236
237
238
# File 'lib/arrow/logger.rb', line 236

def trace
  @trace
end

Class Method Details

.[](mod = nil) ⇒ Object

Return the Arrow::Logger for the given module mod, which can be a Module object, a Symbol, or a String.



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/arrow/logger.rb', line 151

def self::[]( mod=nil )
	return self.global if mod.nil?

	case mod
	when Module
		return self.logger_map[ mod ]

	# If it's a String, try to map it to a class name, falling back on the global
	# logger if that fails
	when String
		mod = mod.split('::').
			inject( Object ) {|k, modname| k.const_get(modname) } rescue Object
		return self.logger_map[ mod ]
	else

		return self.logger_map[ mod.class ]
	end

end

.configure(config) ⇒ Object

Configure logging from the ‘logging’ section of the config.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/arrow/logger.rb', line 91

def self::configure( config )

		self.reset
		apacheoutputter = Arrow::Logger::Outputter.create( 'apache' )

		config.each do |klass, setting|
			level, uri = self.parse_log_setting( setting )

			# Use the Apache log as the outputter if none is configured
			if uri.nil?
outputter = apacheoutputter
			else
outputter = Arrow::Logger::Outputter.create( uri )
			end

			# The 'global' entry configures the global logger
			if klass == :global
self.global.level = level
self.global.outputters << outputter
next
			end

			# If the class bit is something like 'applet', then transform
			# it into 'Arrow::Applet'
			if klass.to_s.match( /^[a-z][a-zA-Z]+$/ )
realclass = "Arrow::%s" % klass.to_s.sub(/^([a-z])/){ $1.upcase }
			else
realclass = klass.to_s
			end

			Arrow::Logger[ realclass ].level = level
			Arrow::Logger[ realclass ].outputters << outputter
		end

end

.globalObject

Return the global Arrow logger, setting it up if it hasn’t been already.



174
175
176
# File 'lib/arrow/logger.rb', line 174

def self::global
	self.logger_map[ Object ]
end

.method_missing(sym, *args) ⇒ Object

Autoload global logging methods for the log levels



187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/arrow/logger.rb', line 187

def self::method_missing( sym, *args )
	return super unless LEVELS.key?( sym )

	self.global.debug( "Autoloading class log method '#{sym}'." )
	(class << self; self; end).class_eval do
		define_method( sym ) do |*args|
			self.global.send( sym, *args )
		end
	end

	self.global.send( sym, *args )
end

.parse_log_setting(setting) ⇒ Object

Parse the configuration for a given class’s logger. The configuration is in the form:

<level> [<outputter_uri>]

where level is one of the logging levels defined by this class (see the LEVELS constant), and the optional outputter_uri indicates which outputter to use, and how it should be configured. See Arrow::Logger::Outputter for more info.

Examples:

notice
debug file:///tmp/broker-debug.log
error dbi://www:password@localhost/www.errorlog?driver=postgresql


141
142
143
144
145
146
# File 'lib/arrow/logger.rb', line 141

def self::parse_log_setting( setting )
	level, rawuri = setting.split( ' ', 2 )
	uri = rawuri.nil? ? nil : URI.parse( rawuri )

	return level.to_sym, uri
end

.resetObject

Reset the logging subsystem. Clears out any registered loggers and their associated outputters.



181
182
183
# File 'lib/arrow/logger.rb', line 181

def self::reset
	self.logger_map.clear
end

Instance Method Details

#<<(obj) ⇒ Object

Append the given obj to the logger at :debug level. This is for compatibility with objects that append to $stderr for their logging (e.g., net/protocols-based libraries).



426
427
428
429
# File 'lib/arrow/logger.rb', line 426

def <<( obj )
	self.write( :debug, obj )
	return self
end

#hierloggers(level = :emerg) ⇒ Object

Return a uniquified Array of the loggers which are more-generally related hierarchically to the receiver, inclusive, and whose level is level or lower.



353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/arrow/logger.rb', line 353

def hierloggers( level=:emerg )
	level = LEVELS[ level ] if level.is_a?( Symbol )

	loggers = []
	self.supermods.each do |mod|
		logger = self.class.logger_map[ mod ]
		next unless logger.level <= level

		loggers << logger
		yield( logger ) if block_given?
	end

	return loggers
end

#hieroutputters(level = ) ⇒ Object

Return a uniquified Array of all outputters for this logger and all of the loggers above it in the logging hierarchy that are set to level or lower. If called with a block, it will be called once for each outputter and the first logger to which it is attached.



373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/arrow/logger.rb', line 373

def hieroutputters( level=LEVELS[:emerg] )
	outputters = []

	# Look for loggers which are higher in the hierarchy
	self.hierloggers( level ) do |logger|
		outpary = logger.outputters || []
		newoutpary = outpary - (outpary & outputters)

		# If there are any outputters which haven't already been seen,
		# output to them.
		unless newoutpary.empty?
			# debug_msg "hieroutputters: adding: %s" %
				# newoutpary.collect {|outp| outp.description}.join(", ")
			if block_given?
				newoutpary.each {|outputter| yield(outputter, logger)}
			end
			outputters += newoutpary
		end
	end

	return outputters
end

#inspectObject

Return a human-readable string representation of the object.



246
247
248
249
250
251
252
253
254
255
# File 'lib/arrow/logger.rb', line 246

def inspect
	"#<%s:0x%0x %s [level: %s, outputters: %d, trace: %s]>" % [
		self.class.name,
		self.object_id * 2,
		self.readable_name,
		self.readable_level,
		self.outputters.length,
		self.trace ? "on" : "off",
	]
end

#inspect_details(level = 0) ⇒ Object

Return a (more-detailed) human-readable string representation of the object.



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/arrow/logger.rb', line 259

def inspect_details( level=0 )
	indent = '  ' * (level + 1)

	prelude = "<< %s [level: %s, trace: %s] >>" % [
		self.readable_name,
		self.readable_level,
		self.trace ? "on" : "off",
	  ]

	details = []
	unless self.outputters.empty?
		details << "Outputters:" << self.outputters.map {|op| op.inspect }
	end
	details = details.flatten.compact.map {|line| indent + line }

	if level.zero?
		return [ prelude, *details ].join( "\n" )
	else
		return [ prelude, *details ]
	end
end

#readable_levelObject

Return the logger’s level as a Symbol.



291
292
293
# File 'lib/arrow/logger.rb', line 291

def readable_level
	return LEVEL_NAMES[ @level ]
end

#readable_nameObject

Return the name of the logger formatted to be suitable for reading.



283
284
285
286
287
# File 'lib/arrow/logger.rb', line 283

def readable_name
	return '(global)' if self.module == Object
	return self.module.inspect if self.module.name == ''
	return self.module.name
end

#superloggerObject

Return the Arrow::Logger for this instance’s module’s parent class if it’s a Class, and the global logger otherwise.



325
326
327
328
329
330
331
332
333
# File 'lib/arrow/logger.rb', line 325

def superlogger
	if @module == Object
		return nil
	elsif @module.respond_to?( :superclass )
		Arrow::Logger[ @module.superclass ]
	else
		Arrow::Logger.global
	end
end

#supermodsObject

Return the Array of modules and classes the receiver’s module includes or inherits, inclusive of the receiver’s module itself.



338
339
340
341
342
343
344
345
346
# File 'lib/arrow/logger.rb', line 338

def supermods
	unless @supermods
		objflag = false
		@supermods = self.module.ancestors.partition {|mod| objflag ||= (mod == Object) }.last
		@supermods << Object
	end

	return @supermods
end

#write(level, *args) ⇒ Object

Write the given args to any connected outputters if level is less than or equal to this logger’s level.



399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/arrow/logger.rb', line 399

def write( level, *args )
	# debug_msg "Writing message at %p from %s: %p" % [ level, caller(2).first, args ]

	msg, frame = nil, nil
	time = Time.now

	# If tracing is turned on, pick the first frame in the stack that
	# isn't in this file, or the last one if that fails to yield one.
	if @trace
		frame = caller(1).find {|fr| fr !~ %r{arrow/logger\.rb} } ||
		 	caller(1).last
	end

	level = @forced_level if @forced_level

	# Find the outputters that need to be written to, then write to them.
	self.hieroutputters( level ) do |outp, logger|
		# debug_msg "Got outputter %p" % outp
		msg ||= args.collect {|obj| self.stringify_object(obj) }.join
		outp.write( time, level, self.readable_name, frame, msg )
	end
end