Class: Logging::Logger

Inherits:
Object
  • Object
show all
Defined in:
lib/logging/logger.rb

Overview

The Logger class is the primary interface to the Logging framework. It provides the logging methods that will be called from user methods, and it generates logging events that are sent to the appenders (the appenders take care of sending the log events to the logging destinations – files, sockets, etc).

Logger instances are obtained from the Repository and should not be directly created by users.

Example:

log = Logging.logger['my logger']
log.add_appenders( Logging.appenders.stdout )   # append to STDOUT
log.level = :info                               # log 'info' and above

log.info 'starting foo operation'
...
log.info 'finishing foo operation'
...
log.fatal 'unknown exception', exception

Direct Known Subclasses

RootLogger

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name) ⇒ Logger

call-seq:

Logger.new( name )
Logger[name]

Returns the logger identified by name.

When name is a String or a Symbol it will be used “as is” to retrieve the logger. When name is a Class the class name will be used to retrieve the logger. When name is an object the name of the object’s class will be used to retrieve the logger.

Example:

obj = MyClass.new

log1 = Logger.new(obj)
log2 = Logger.new(MyClass)
log3 = Logger['MyClass']

log1.object_id == log2.object_id         # => true
log2.object_id == log3.object_id         # => true


151
152
153
154
155
156
157
158
159
# File 'lib/logging/logger.rb', line 151

def initialize( name )
  case name
  when String
    raise(ArgumentError, "logger must have a name") if name.empty?
  else raise(ArgumentError, "logger name must be a String") end

  repo = ::Logging::Repository.instance
  _setup(name, :parent => repo.parent(name))
end

Instance Attribute Details

#additiveObject

class << self



127
128
129
# File 'lib/logging/logger.rb', line 127

def additive
  @additive
end

#nameObject (readonly)

class << self



127
128
129
# File 'lib/logging/logger.rb', line 127

def name
  @name
end

#parentObject

class << self



127
128
129
# File 'lib/logging/logger.rb', line 127

def parent
  @parent
end

#traceObject

class << self



127
128
129
# File 'lib/logging/logger.rb', line 127

def trace
  @trace
end

Class Method Details

.define_log_methods(logger) ⇒ Object

This is where the actual logging methods are defined. Two methods are created for each log level. The first is a query method used to determine if that perticular logging level is enabled. The second is the actual logging method that accepts a list of objects to be logged or a block. If a block is given, then the object returned from the block will be logged.

Example

log = Logging::Logger['my logger']
log.level = :warn

log.info?                               # => false
log.warn?                               # => true
log.warn 'this is your last warning'
log.fatal 'I die!', exception

log.debug do
  # expensive method to construct log message
  msg
end


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
# File 'lib/logging/logger.rb', line 98

def define_log_methods( logger )
  ::Logging::LEVELS.each do |name,num|
    code =  "undef :#{name}  if method_defined? :#{name}\n"
    code << "undef :#{name}? if method_defined? :#{name}?\n"

    if logger.level > num
      code << <<-CODE
        def #{name}?( ) false end
        def #{name}( data = nil ) false end
      CODE
    else
      code << <<-CODE
        def #{name}?( ) true end
        def #{name}( data = nil )
          data = yield if block_given?
          log_event(::Logging::LogEvent.new(@name, #{num}, data, @trace))
          true
        end
      CODE
    end

    logger._meta_eval(code, __FILE__, __LINE__)
  end
  logger
end

.new(*args) ⇒ Object Also known as: []

Overrides the new method such that only one Logger will be created for any given logger name.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/logging/logger.rb', line 45

def new( *args )
  return super if args.empty?

  repo = ::Logging::Repository.instance
  name = repo.to_key(args.shift)

  @mutex.synchronize do
    logger = repo[name]
    if logger.nil?

      master = repo.master_for(name)
      if master
        if repo.has_logger?(master)
          logger = repo[master]
        else
          logger = super(master)
          repo[master] = logger
          repo.children(master).each {|c| c.__send__(:parent=, logger)}
        end
        repo[name] = logger
      else
        logger = super(name)
        repo[name] = logger
        repo.children(name).each {|c| c.__send__(:parent=, logger)}
      end
    end
    logger
  end
end

.rootObject

call-seq:

Logger.root

Returns the root logger.



36
37
38
# File 'lib/logging/logger.rb', line 36

def root
  ::Logging::Repository.instance[:root]
end

Instance Method Details

#<<(msg) ⇒ Object

call-seq:

log << "message"

Log the given message without any formatting and without performing any level checks. The message is logged to all appenders. The message is passed up the logger tree if this logger’s additivity is true.



182
183
184
185
# File 'lib/logging/logger.rb', line 182

def <<( msg )
  @appenders.each {|a| a << msg}
  @parent << msg if @additive
end

#<=>(other) ⇒ Object

call-seq:

log <=> other

Compares this logger by name to another logger. The normal return codes for String objects apply.



167
168
169
170
171
172
173
# File 'lib/logging/logger.rb', line 167

def <=>( other )
  case other
  when self; 0
  when ::Logging::RootLogger; 1
  when ::Logging::Logger; @name <=> other.name
  else raise ArgumentError, 'expecting a Logger instance' end
end

#_dump_configuration(io = STDOUT, indent = 0) ⇒ Object

call-seq:

_dump_configuration( io = STDOUT, indent = 0 )

An internal method that is used to dump this logger’s configuration to the given io stream. The configuration includes the logger’s name, level, additivity, and trace settings. The configured appenders are also printed to the io stream.



455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
# File 'lib/logging/logger.rb', line 455

def _dump_configuration( io = STDOUT, indent = 0 )
  str, spacer, base = '', '  ', 50
  indent_str = indent == 0 ? '' : ' ' * indent

  str << indent_str
  str << self.name.reduce(base - indent)
  if (str.length + spacer.length) < base
    str << spacer
    str << '.' * (base - str.length)
  end
  io.write(str.ljust(base))
  io.write(spacer)

  level_str  = @level.nil? ? '' : '*'
  level_str << if level < ::Logging::LEVELS.length
    ::Logging.levelify(::Logging::LNAMES[level])
  else
    'off'
  end
  level_len = ::Logging::MAX_LEVEL_LENGTH + 1

  io.write("%#{level_len}s" % level_str)
  io.write(spacer)

  if self.respond_to?(:additive)
    io.write(additive ? '+A' : '-A')
  else
    io.write('  ')
  end

  io.write(spacer)
  io.write(trace ? '+T' : '-T')
  io.write("\n")

  @appenders.each do |appender|
    io.write(indent_str)
    io.write('- ')
    io.write(appender.inspect)
    io.write("\n")
  end

  return io
end

#_meta_eval(code, file = nil, line = nil) ⇒ Object

call-seq:

_meta_eval( code )

Evaluates the given string of code if the singleton class of this Logger object.



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

def _meta_eval( code, file = nil, line = nil )
  meta = class << self; self end
  meta.class_eval code, file, line
end

#_setup(name, opts = {}) ⇒ Object

call-seq:

_setup( name, opts = {} )

Configures internal variables for the logger. This method can be used to avoid storing the logger in the repository.



437
438
439
440
441
442
443
444
445
# File 'lib/logging/logger.rb', line 437

def _setup( name, opts = {} )
  @name      = name
  @parent    = opts.getopt(:parent)
  @appenders = opts.getopt(:appenders, [])
  @additive  = opts.getopt(:additive, true)
  @trace     = opts.getopt(:trace, false)
  @level     = opts.getopt(:level)
  ::Logging::Logger.define_log_methods(self)
end

#add(lvl, data = nil) ⇒ Object

call-seq:

add( severity, message = nil ) {block}

Log a message if the given severity is high enough. This is the generic logging method. Users will be more inclined to use #debug, #info, #warn, #error, and #fatal.

Message format: message can be any object, but it has to be converted to a String in order to log it. The Logging::format_as method is used to determine how objects chould be converted to strings. Generally, inspect is used.

A special case is an Exception object, which will be printed in detail, including message, class, and backtrace.

If a message is not given, then the return value from the block is used as the message to log. This is useful when creating the actual message is an expensive operation. This allows the logger to check the severity against the configured level before actually constructing the message.

This method returns true if the message was logged, and false is returned if the message was not logged.



211
212
213
214
215
216
217
218
# File 'lib/logging/logger.rb', line 211

def add( lvl, data = nil )
  lvl = Integer(lvl)
  return false if lvl < level

  data = yield if block_given?
  log_event(::Logging::LogEvent.new(@name, lvl, data, @trace))
  true
end

#add_appenders(*args) ⇒ Object

call-seq:

add_appenders( appenders )

Add the given appenders to the list of appenders, where appenders can be either a single appender or an array of appenders.



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

def add_appenders( *args )
  args.flatten.each do |arg|
    o = arg.kind_of?(::Logging::Appender) ? arg : ::Logging::Appenders[arg.to_s]
    raise ArgumentError, "unknown appender #{arg.inspect}" if o.nil?
    @appenders << o unless @appenders.include?(o)
  end
  self
end

#appenders=(args) ⇒ Object

call-seq:

appenders = app

Clears the current list of appenders and replaces them with app, where app can be either a single appender or an array of appenders.



315
316
317
318
# File 'lib/logging/logger.rb', line 315

def appenders=( args )
  @appenders.clear
  add_appenders(*args) unless args.nil?
end

#clear_appendersObject

call-seq:

clear_appenders

Remove all appenders from this logger.



362
# File 'lib/logging/logger.rb', line 362

def clear_appenders( ) @appenders.clear end

#inspectObject

call-seq:

inspect    => string

Returns a string representation of the logger.



369
370
371
# File 'lib/logging/logger.rb', line 369

def inspect
  "<%s:0x%x name=\"%s\">" % [self.class.name, self.object_id, self.name]
end

#levelObject

call-seq:

level    => integer

Returns an integer which is the defined log level for this logger.



255
256
257
258
# File 'lib/logging/logger.rb', line 255

def level
  return @level unless @level.nil?
  @parent.level
end

#level=(level) ⇒ Object

call-seq:

level = :all

Set the level for this logger. The level can be either a String, a Symbol, or a Fixnum. An ArgumentError is raised if this is not the case.

There are two special levels – “all” and “off”. The former will enable log messages from this logger. The latter will disable all log messages from this logger.

Setting the logger level to nil will cause the parent’s logger level to be used.

Example:

log.level = :debug
log.level = "INFO"
log.level = 4
log.level = 'off'
log.level = :all

These prodcue an ArgumentError

log.level = Object
log.level = -1
log.level = 1_000_000_000_000


288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/logging/logger.rb', line 288

def level=( level )
  @level =
    if level.nil? then level
    else
      lvl = case level
            when String, Symbol; ::Logging::level_num(level)
            when Fixnum; level
            else
              raise ArgumentError,
                    "level must be a String, Symbol, or Integer"
            end
      if lvl.nil? or lvl < 0 or lvl > ::Logging::LEVELS.length
        raise ArgumentError, "unknown level was given '#{level}'"
      end
      lvl
    end

  define_log_methods(true)
  self.level
end

#remove_appenders(*args) ⇒ Object

call-seq:

remove_appenders( appenders )

Remove the given appenders from the list of appenders. The appenders to remove can be identified either by name using a String or by passing the appender instance. appenders can be a single appender or an array of appenders.



343
344
345
346
347
348
349
350
351
352
353
354
355
# File 'lib/logging/logger.rb', line 343

def remove_appenders( *args )
  args.flatten.each do |arg|
    @appenders.delete_if do |a|
      case arg
      when String; arg == a.name
      when ::Logging::Appender; arg.object_id == a.object_id
      else
        raise ArgumentError, "#{arg.inspect} is not a 'Logging::Appender'"
      end
    end
  end
  self
end