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


154
155
156
157
158
159
160
161
162
# File 'lib/logging/logger.rb', line 154

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

Returns the value of attribute additive.



130
131
132
# File 'lib/logging/logger.rb', line 130

def additive
  @additive
end

#caller_tracingObject

Returns the value of attribute caller_tracing.



130
131
132
# File 'lib/logging/logger.rb', line 130

def caller_tracing
  @caller_tracing
end

#nameObject (readonly)

Returns the value of attribute name.



130
131
132
# File 'lib/logging/logger.rb', line 130

def name
  @name
end

#parentObject

Returns the value of attribute parent.



130
131
132
# File 'lib/logging/logger.rb', line 130

def parent
  @parent
end

Class Method Details

.[](name) ⇒ Object

Returns a logger instance for the given name.



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/logging/logger.rb', line 43

def self.[]( name )
  repo = ::Logging::Repository.instance
  name = repo.to_key(name)
  logger = repo[name]
  return logger unless logger.nil?

  # Share the same mutex that's used by 'define_log_methods' because
  # it iterates over the hash of loggers, and adding a new key to a hash
  # while iterating over it produces an error.
  ::Logging::Logger.mutex.synchronize do
    logger = repo[name]
    return logger unless logger.nil? # thread-safe double checking

    logger = instantiate(name)
    repo[name] = logger
    repo.children(name).each { |child| child.__send__(:parent=, logger) }
    logger
  end
end

.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


85
86
87
88
89
# File 'lib/logging/logger.rb', line 85

def self.define_log_methods( logger )
  code = log_methods_for_level(logger.level)
  logger._meta_eval(code, __FILE__, __LINE__)
  logger
end

.instantiateObject

‘instantiate` becomes the “real” `new`



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

alias_method :instantiate, :new

.log_methods_for_level(level) ⇒ Object

This generator is used to define the log methods for the given ‘level`. This code is evaluated in the context of a Logger instance.

Returns log methods as a String



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

def self.log_methods_for_level( level )
  code = []
  ::Logging::LEVELS.each do |name,num|
    code << <<-CODE
        undef :#{name}  if method_defined? :#{name}
        undef :#{name}? if method_defined? :#{name}?
    CODE

    if 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, @caller_tracing))
          true
        end
      CODE
    end
  end
  code.join("\n")
end

.mutexObject

Returns a global ReentrantMutex for use when creating Logger instances and/or updating log levels.



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

def self.mutex
  @mutex
end

.new(*args) ⇒ Object

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



38
39
40
# File 'lib/logging/logger.rb', line 38

def self.new( *args )
  args.empty? ? super : self[args.shift]
end

.rootObject

Returns the root logger.



28
29
30
# File 'lib/logging/logger.rb', line 28

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

Instance Method Details

#<<(msg) ⇒ Object Also known as: write

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.



185
186
187
188
# File 'lib/logging/logger.rb', line 185

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.



170
171
172
173
174
175
176
# File 'lib/logging/logger.rb', line 170

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(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 caller_tracing settings. The configured appenders are also printed to the io stream.



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
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
# File 'lib/logging/logger.rb', line 472

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

  str << indent_str
  str << self.name.shrink(base - indent)
  if (str.length + spacer.length) < base
    str << spacer
    str << '.' * (base - str.length)
  end
  str = str.ljust(base)
  str << 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

  str << sprintf("%#{level_len}s" % level_str)
  str << spacer

  if self.respond_to?(:additive)
    str << (additive ? '+A' : '-A')
  else
    str << '  '
  end

  str << spacer
  str << (caller_tracing ? '+T' : '-T')
  str << "\n"

  @appenders.each do |appender|
    str << indent_str
    str << '- '
    str << appender.to_s
    str << "\n"
  end

  return str
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.



441
442
443
444
# File 'lib/logging/logger.rb', line 441

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.



452
453
454
455
456
457
458
459
460
461
462
# File 'lib/logging/logger.rb', line 452

def _setup( name, opts = {} )
  @name      = name
  @parent    = opts.fetch(:parent, nil)
  @appenders = opts.fetch(:appenders, [])
  @additive  = opts.fetch(:additive, true)
  @level     = opts.fetch(:level, nil)

  @caller_tracing = opts.fetch(:caller_tracing, false)

  ::Logging::Logger.define_log_methods(self)
end

#add(lvl, data = nil, progname = 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.



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/logging/logger.rb', line 215

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

  if data.nil?
    if block_given?
      data = yield
    else
      data = progname
    end
  end

  log_event(::Logging::LogEvent.new(@name, lvl, data, @caller_tracing))
  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.



349
350
351
352
353
354
355
356
# File 'lib/logging/logger.rb', line 349

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

#appendersObject

Returns the list of appenders.



328
329
330
# File 'lib/logging/logger.rb', line 328

def appenders
  @appenders.dup
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.



338
339
340
341
# File 'lib/logging/logger.rb', line 338

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

#clear_appendersObject

call-seq:

clear_appenders

Remove all appenders from this logger.



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

def clear_appenders( ) @appenders.clear end

#has_own_level?Boolean

Returns ‘true` if the logger has its own level defined.

Returns:

  • (Boolean)


322
323
324
# File 'lib/logging/logger.rb', line 322

def has_own_level?
  !@level.nil?
end

#levelObject

call-seq:

level    => integer

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



267
268
269
270
# File 'lib/logging/logger.rb', line 267

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 an Integer. 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 produce an ArgumentError

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


300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/logging/logger.rb', line 300

def level=( level )
  @level =
    if level.nil? then level
    else
      lvl = case level
            when String, Symbol; ::Logging::level_num(level)
            when Integer; 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.



366
367
368
369
370
371
372
373
374
375
376
377
378
# File 'lib/logging/logger.rb', line 366

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