logging 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,44 +1,55 @@
1
-
2
1
  module Logging::Appenders
3
2
 
4
- # Accessor / Factory for the Stdout appender.
5
- #
6
- def self.stdout( *args )
7
- if args.empty?
8
- return self['stdout'] || ::Logging::Appenders::Stdout.new
9
- end
10
- ::Logging::Appenders::Stdout.new(*args)
11
- end
12
-
13
- # This class provides an Appender that can write to STDOUT.
14
- #
15
- class Stdout < ::Logging::Appenders::IO
3
+ # This class is provides an Appender base class for writing to the standard IO
4
+ # stream - STDOUT and STDERR. This class should not be instantiated directly.
5
+ # The `Stdout` and `Stderr` subclasses should be used.
6
+ class Console < ::Logging::Appenders::IO
16
7
 
17
8
  # call-seq:
18
9
  # Stdout.new( name = 'stdout' )
19
- # Stdout.new( :layout => layout )
10
+ # Stderr.new( :layout => layout )
20
11
  # Stdout.new( name = 'stdout', :level => 'info' )
21
12
  #
22
- # Creates a new Stdout Appender. The name 'stdout' will be used unless
23
- # another is given. Optionally, a layout can be given for the appender
24
- # to use (otherwise a basic appender will be created) and a log level
25
- # can be specified.
13
+ # Creates a new Stdout/Stderr Appender. The name 'stdout'/'stderr' will be
14
+ # used unless another is given. Optionally, a layout can be given for the
15
+ # appender to use (otherwise a basic appender will be created) and a log
16
+ # level can be specified.
26
17
  #
27
18
  # Options:
28
19
  #
29
- # :layout => the layout to use when formatting log events
30
- # :level => the level at which to log
20
+ # :layout => the layout to use when formatting log events
21
+ # :level => the level at which to log
31
22
  #
32
23
  def initialize( *args )
24
+ name = self.class.name.split("::").last.downcase
25
+ io = Object.const_get(name.upcase)
26
+
33
27
  opts = Hash === args.last ? args.pop : {}
34
- name = args.empty? ? 'stdout' : args.shift
28
+ name = args.shift unless args.empty?
35
29
 
36
- opts[:encoding] = STDOUT.external_encoding if STDOUT.respond_to? :external_encoding
30
+ opts[:encoding] = io.external_encoding if io.respond_to? :external_encoding
37
31
 
38
- super(name, STDOUT, opts)
32
+ super(name, io, opts)
33
+ rescue NameError
34
+ raise RuntimeError, "Please do not use the `Logging::Appenders::Console` class directly - " +
35
+ "use `Logging::Appenders::Stdout` and `Logging::Appenders::Stderr` instead"
39
36
  end
40
- end # Stdout
37
+ end
38
+
39
+ # This class provides an Appender that can write to STDOUT.
40
+ Stdout = Class.new(Console)
41
41
 
42
+ # This class provides an Appender that can write to STDERR.
43
+ Stderr = Class.new(Console)
44
+
45
+ # Accessor / Factory for the Stdout appender.
46
+ #
47
+ def self.stdout( *args )
48
+ if args.empty?
49
+ return self['stdout'] || ::Logging::Appenders::Stdout.new
50
+ end
51
+ ::Logging::Appenders::Stdout.new(*args)
52
+ end
42
53
 
43
54
  # Accessor / Factory for the Stderr appender.
44
55
  #
@@ -48,34 +59,4 @@ module Logging::Appenders
48
59
  end
49
60
  ::Logging::Appenders::Stderr.new(*args)
50
61
  end
51
-
52
- # This class provides an Appender that can write to STDERR.
53
- #
54
- class Stderr < ::Logging::Appenders::IO
55
-
56
- # call-seq:
57
- # Stderr.new( name = 'stderr' )
58
- # Stderr.new( :layout => layout )
59
- # Stderr.new( name = 'stderr', :level => 'warn' )
60
- #
61
- # Creates a new Stderr Appender. The name 'stderr' will be used unless
62
- # another is given. Optionally, a layout can be given for the appender
63
- # to use (otherwise a basic appender will be created) and a log level
64
- # can be specified.
65
- #
66
- # Options:
67
- #
68
- # :layout => the layout to use when formatting log events
69
- # :level => the level at which to log
70
- #
71
- def initialize( *args )
72
- opts = Hash === args.last ? args.pop : {}
73
- name = args.empty? ? 'stderr' : args.shift
74
-
75
- opts[:encoding] = STDERR.external_encoding if STDERR.respond_to? :external_encoding
76
-
77
- super(name, STDERR, opts)
78
- end
79
- end # Stderr
80
- end # Logging::Appenders
81
-
62
+ end
@@ -4,7 +4,7 @@ module Logging::Appenders
4
4
  # Accessor / Factory for the File appender.
5
5
  #
6
6
  def self.file( *args )
7
- return ::Logging::Appenders::File if args.empty?
7
+ fail ArgumentError, '::Logging::Appenders::File needs a name as first argument.' if args.empty?
8
8
  ::Logging::Appenders::File.new(*args)
9
9
  end
10
10
 
@@ -28,13 +28,12 @@ module Logging::Appenders
28
28
  # stream as the logging destination.
29
29
  #
30
30
  def initialize( name, io, opts = {} )
31
- unless io.respond_to? :syswrite
31
+ unless io.respond_to? :write
32
32
  raise TypeError, "expecting an IO object but got '#{io.class.name}'"
33
33
  end
34
34
 
35
35
  @io = io
36
- @io.sync = true if io.respond_to? :sync= # syswrite complains if the IO stream is buffered
37
- @io.flush rescue nil # syswrite also complains if in unbuffered mode and buffer isn't empty
36
+ @io.sync = true if io.respond_to? :sync=
38
37
  @close_method = :close
39
38
 
40
39
  super(name, opts)
@@ -55,7 +54,7 @@ module Logging::Appenders
55
54
 
56
55
  io, @io = @io, nil
57
56
  unless [STDIN, STDERR, STDOUT].include?(io)
58
- io.send(@close_method) if @close_method and io.respond_to? @close_method
57
+ io.send(@close_method) if @close_method && io.respond_to?(@close_method)
59
58
  end
60
59
  rescue IOError
61
60
  ensure
@@ -70,10 +69,15 @@ module Logging::Appenders
70
69
  #
71
70
  def canonical_write( str )
72
71
  return self if @io.nil?
73
- str = str.force_encoding(encoding) if encoding and str.encoding != encoding
74
- @io.syswrite str
72
+ str = str.force_encoding(encoding) if encoding && str.encoding != encoding
73
+ @io.write str
75
74
  self
76
75
  rescue StandardError => err
76
+ handle_internal_error(err)
77
+ end
78
+
79
+ def handle_internal_error( err )
80
+ return err if off?
77
81
  self.level = :off
78
82
  ::Logging.log_internal {"appender #{name.inspect} has been disabled"}
79
83
  ::Logging.log_internal_error(err)
@@ -81,4 +85,3 @@ module Logging::Appenders
81
85
 
82
86
  end # IO
83
87
  end # Logging::Appenders
84
-
@@ -2,7 +2,7 @@ module Logging::Appenders
2
2
 
3
3
  # Accessor / Factory for the RollingFile appender.
4
4
  def self.rolling_file( *args )
5
- return ::Logging::Appenders::RollingFile if args.empty?
5
+ fail ArgumentError, '::Logging::Appenders::RollingFile needs a name as first argument.' if args.empty?
6
6
  ::Logging::Appenders::RollingFile.new(*args)
7
7
  end
8
8
 
@@ -148,7 +148,7 @@ module Logging::Appenders
148
148
  return self if @io.nil?
149
149
 
150
150
  str = str.force_encoding(encoding) if encoding && str.encoding != encoding
151
- @io.flock_sh { @io.syswrite str }
151
+ @io.flock_sh { @io.write str }
152
152
 
153
153
  if roll_required?
154
154
  @io.flock? {
@@ -9,7 +9,7 @@ module Logging::Appenders
9
9
  # Accessor / Factory for the Syslog appender.
10
10
  #
11
11
  def self.syslog( *args )
12
- return ::Logging::Appenders::Syslog if args.empty?
12
+ fail ArgumentError, '::Logging::Appenders::Syslog needs a name as first argument.' if args.empty?
13
13
  ::Logging::Appenders::Syslog.new(*args)
14
14
  end
15
15
 
@@ -143,7 +143,7 @@ module Logging
143
143
  Thread.current[STACK_NAME] = [obj.dup]
144
144
  when Thread
145
145
  return if Thread.current == obj
146
- Thread.exclusive {
146
+ DIAGNOSTIC_MUTEX.synchronize {
147
147
  if obj[STACK_NAME]
148
148
  hash = flatten(obj[STACK_NAME])
149
149
  Thread.current[STACK_NAME] = [hash]
@@ -338,7 +338,7 @@ module Logging
338
338
  Thread.current[NAME] = obj.dup
339
339
  when Thread
340
340
  return if Thread.current == obj
341
- Thread.exclusive {
341
+ DIAGNOSTIC_MUTEX.synchronize {
342
342
  Thread.current[NAME] = obj[NAME].dup if obj[NAME]
343
343
  }
344
344
  end
@@ -381,7 +381,7 @@ module Logging
381
381
  #
382
382
  def self.clear_diagnostic_contexts( all = false )
383
383
  if all
384
- Thread.exclusive {
384
+ DIAGNOSTIC_MUTEX.synchronize {
385
385
  Thread.list.each { |thread|
386
386
  thread[MappedDiagnosticContext::NAME] = nil if thread[MappedDiagnosticContext::NAME]
387
387
  thread[NestedDiagnosticContext::NAME] = nil if thread[NestedDiagnosticContext::NAME]
@@ -396,6 +396,8 @@ module Logging
396
396
  self
397
397
  end
398
398
 
399
+ DIAGNOSTIC_MUTEX = Mutex.new
400
+
399
401
  end # module Logging
400
402
 
401
403
 
@@ -41,15 +41,28 @@ class Layout
41
41
  when :inspect, :yaml, :json; f
42
42
  else :string end
43
43
 
44
- b = opts.fetch(:backtrace, ::Logging.backtrace)
45
- @backtrace = case b
46
- when :on, 'on', true; true
47
- when :off, 'off', false; false
48
- else
49
- raise ArgumentError, "backtrace must be true or false"
50
- end
44
+ self.backtrace = opts.fetch(:backtrace, ::Logging.backtrace)
51
45
  end
52
46
 
47
+ # call-seq:
48
+ # layout.backtrace = true
49
+ #
50
+ # Set the backtrace flag to the given value. This can be set to `true` or
51
+ # `false`.
52
+ #
53
+ def backtrace=( value )
54
+ @backtrace = case value
55
+ when :on, 'on', true; true
56
+ when :off, 'off', false; false
57
+ else
58
+ raise ArgumentError, "backtrace must be `true` or `false`"
59
+ end
60
+ end
61
+
62
+ # Returns the backtrace setting.
63
+ attr_reader :backtrace
64
+ alias :backtrace? :backtrace
65
+
53
66
  # call-seq:
54
67
  # format( event )
55
68
  #
@@ -85,7 +98,7 @@ class Layout
85
98
  when String; obj
86
99
  when Exception
87
100
  str = "<#{obj.class.name}> #{obj.message}"
88
- if @backtrace && !obj.backtrace.nil?
101
+ if backtrace? && !obj.backtrace.nil?
89
102
  str << "\n\t" << obj.backtrace.join("\n\t")
90
103
  end
91
104
  str
@@ -83,7 +83,7 @@ module Logging::Layouts
83
83
  # events is configured to generate tracing information. If this is not
84
84
  # the case these fields will always be empty.
85
85
  #
86
- # The directives for include diagnostic context information in the log
86
+ # The directives for including diagnostic context information in the log
87
87
  # messages are X and x. For the Mapped Diagnostic Context the directive must
88
88
  # be accompanied by the key identifying the value to insert into the log
89
89
  # message. The X directive can appear multiple times to include multiple
@@ -166,12 +166,9 @@ module Logging::Layouts
166
166
  code << "def format_date( time )\n"
167
167
  if pl.date_method.nil?
168
168
  if pl.date_pattern =~ %r/%s/
169
- code << <<-CODE
170
- dp = '#{pl.date_pattern}'.gsub('%s','%06d' % time.usec)
171
- time.strftime dp
172
- CODE
169
+ code << "time.strftime('#{pl.date_pattern.gsub('%s','%6N')}')\n"
173
170
  else
174
- code << "time.strftime '#{pl.date_pattern}'\n"
171
+ code << "time.strftime('#{pl.date_pattern}')\n"
175
172
  end
176
173
  else
177
174
  code << "time.#{pl.date_method}\n"
@@ -11,7 +11,7 @@ module Logging
11
11
  # * $1 == filename
12
12
  # * $2 == line number
13
13
  # * $3 == method name (might be nil)
14
- CALLER_RGXP = %r/([-\.\/\(\)\w]+):(\d+)(?::in `(\w+)')?/o
14
+ CALLER_RGXP = %r/([-\.\/\(\)\w]+):(\d+)(?::in `([^']+)')?/o
15
15
  #CALLER_INDEX = 2
16
16
  CALLER_INDEX = ((defined? JRUBY_VERSION and JRUBY_VERSION > '1.6') or (defined? RUBY_ENGINE and RUBY_ENGINE[%r/^rbx/i])) ? 1 : 2
17
17
  # :startdoc:
@@ -26,87 +26,97 @@ module Logging
26
26
 
27
27
  @mutex = Mutex.new # :nodoc:
28
28
 
29
- class << self
29
+ # Returns the root logger.
30
+ def self.root
31
+ ::Logging::Repository.instance[:root]
32
+ end
30
33
 
31
- # Returns the root logger.
32
- def root
33
- ::Logging::Repository.instance[:root]
34
- end
34
+ class << self
35
+ alias_method :instantiate, :new # `instantiate` becomes the "real" `new`
36
+ end
35
37
 
36
- alias_method :instantiate, :new # the "real" new
38
+ # Overrides the new method such that only one Logger will be created
39
+ # for any given logger name.
40
+ def self.new( *args )
41
+ args.empty? ? super : self[args.shift]
42
+ end
37
43
 
38
- # Overrides the new method such that only one Logger will be created
39
- # for any given logger name.
40
- def new( *args )
41
- args.empty? ? super : self[args.shift]
42
- end
44
+ # Returns a logger instance for the given name.
45
+ def self.[]( name )
46
+ repo = ::Logging::Repository.instance
47
+ name = repo.to_key(name)
48
+ logger = repo[name]
49
+ return logger unless logger.nil?
43
50
 
44
- # Returns a logger instance for the given name.
45
- def []( name )
46
- repo = ::Logging::Repository.instance
47
- name = repo.to_key(name)
51
+ @mutex.synchronize do
48
52
  logger = repo[name]
49
- return logger unless logger.nil?
53
+ return logger unless logger.nil? # thread-safe double checking
50
54
 
51
- @mutex.synchronize do
52
- logger = repo[name]
53
- return logger unless logger.nil? # thread-safe double checking
54
-
55
- logger = instantiate(name)
56
- repo[name] = logger
57
- repo.children(name).each { |c| c.__send__(:parent=, logger) }
58
- logger
59
- end
55
+ logger = instantiate(name)
56
+ repo[name] = logger
57
+ repo.children(name).each { |child| child.__send__(:parent=, logger) }
58
+ logger
60
59
  end
60
+ end
61
61
 
62
- # This is where the actual logging methods are defined. Two methods
63
- # are created for each log level. The first is a query method used to
64
- # determine if that perticular logging level is enabled. The second is
65
- # the actual logging method that accepts a list of objects to be
66
- # logged or a block. If a block is given, then the object returned
67
- # from the block will be logged.
68
- #
69
- # Example
70
- #
71
- # log = Logging::Logger['my logger']
72
- # log.level = :warn
73
- #
74
- # log.info? # => false
75
- # log.warn? # => true
76
- # log.warn 'this is your last warning'
77
- # log.fatal 'I die!', exception
78
- #
79
- # log.debug do
80
- # # expensive method to construct log message
81
- # msg
82
- # end
83
- #
84
- def define_log_methods( logger )
85
- ::Logging::LEVELS.each do |name,num|
86
- code = "undef :#{name} if method_defined? :#{name}\n"
87
- code << "undef :#{name}? if method_defined? :#{name}?\n"
88
-
89
- if logger.level > num
90
- code << <<-CODE
91
- def #{name}?( ) false end
92
- def #{name}( data = nil ) false end
93
- CODE
94
- else
95
- code << <<-CODE
96
- def #{name}?( ) true end
97
- def #{name}( data = nil )
98
- data = yield if block_given?
99
- log_event(::Logging::LogEvent.new(@name, #{num}, data, @caller_tracing))
100
- true
101
- end
102
- CODE
103
- end
62
+ # This is where the actual logging methods are defined. Two methods
63
+ # are created for each log level. The first is a query method used to
64
+ # determine if that perticular logging level is enabled. The second is
65
+ # the actual logging method that accepts a list of objects to be
66
+ # logged or a block. If a block is given, then the object returned
67
+ # from the block will be logged.
68
+ #
69
+ # Example
70
+ #
71
+ # log = Logging::Logger['my logger']
72
+ # log.level = :warn
73
+ #
74
+ # log.info? # => false
75
+ # log.warn? # => true
76
+ # log.warn 'this is your last warning'
77
+ # log.fatal 'I die!', exception
78
+ #
79
+ # log.debug do
80
+ # # expensive method to construct log message
81
+ # msg
82
+ # end
83
+ #
84
+ def self.define_log_methods( logger )
85
+ code = log_methods_for_level(logger.level)
86
+ logger._meta_eval(code, __FILE__, __LINE__)
87
+ logger
88
+ end
104
89
 
105
- logger._meta_eval(code, __FILE__, __LINE__)
90
+ # This generator is used to define the log methods for the given `level`.
91
+ # This code is evaluated in the context of a Logger instance.
92
+ #
93
+ # Returns log methods as a String
94
+ def self.log_methods_for_level( level )
95
+ code = []
96
+ ::Logging::LEVELS.each do |name,num|
97
+ code << <<-CODE
98
+ undef :#{name} if method_defined? :#{name}
99
+ undef :#{name}? if method_defined? :#{name}?
100
+ CODE
101
+
102
+ if level > num
103
+ code << <<-CODE
104
+ def #{name}?( ) false end
105
+ def #{name}( data = nil ) false end
106
+ CODE
107
+ else
108
+ code << <<-CODE
109
+ def #{name}?( ) true end
110
+ def #{name}( data = nil )
111
+ data = yield if block_given?
112
+ log_event(::Logging::LogEvent.new(@name, #{num}, data, @caller_tracing))
113
+ true
114
+ end
115
+ CODE
106
116
  end
107
- logger
108
117
  end
109
- end # class << self
118
+ code.join("\n")
119
+ end
110
120
 
111
121
  attr_reader :name, :parent, :additive, :caller_tracing
112
122
 
@@ -197,7 +207,14 @@ module Logging
197
207
  lvl = Integer(lvl)
198
208
  return false if lvl < level
199
209
 
200
- data = yield if block_given?
210
+ if data.nil?
211
+ if block_given?
212
+ data = yield
213
+ else
214
+ data = progname
215
+ end
216
+ end
217
+
201
218
  log_event(::Logging::LogEvent.new(@name, lvl, data, @caller_tracing))
202
219
  true
203
220
  end
@@ -292,6 +309,11 @@ module Logging
292
309
  self.level
293
310
  end
294
311
 
312
+ # Returns `true` if the logger has its own level defined.
313
+ def has_own_level?
314
+ !@level.nil?
315
+ end
316
+
295
317
  # Returns the list of appenders.
296
318
  #
297
319
  def appenders
@@ -353,17 +375,7 @@ module Logging
353
375
  #
354
376
  def clear_appenders( ) @appenders.clear end
355
377
 
356
- # call-seq:
357
- # inspect => string
358
- #
359
- # Returns a string representation of the logger.
360
- #
361
- def inspect
362
- "<%s:0x%x name=\"%s\">" % [self.class.name, self.object_id, self.name]
363
- end
364
-
365
-
366
- protected
378
+ protected
367
379
 
368
380
  # call-seq:
369
381
  # parent = ParentLogger
@@ -396,18 +408,26 @@ module Logging
396
408
  #
397
409
  # Recursively call this method on all our children loggers.
398
410
  #
399
- def define_log_methods( force = false )
400
- return if @level and !force
411
+ def define_log_methods( force = false, code = nil )
412
+ return if has_own_level? and !force
401
413
 
402
- ::Logging::Logger.define_log_methods(self)
403
- ::Logging::Repository.instance.children(name).each do |c|
404
- c.define_log_methods
414
+ ::Logging::Logger._reentrant_mutex.synchronize do
415
+ ::Logging::Logger.define_log_methods(self)
416
+ ::Logging::Repository.instance.children(name).each do |child|
417
+ child.define_log_methods
418
+ end
405
419
  end
406
420
  self
407
421
  end
408
422
 
409
423
  # :stopdoc:
410
- public
424
+ public
425
+
426
+ @reentrant_mutex = ReentrantMutex.new
427
+
428
+ def self._reentrant_mutex
429
+ @reentrant_mutex
430
+ end
411
431
 
412
432
  # call-seq:
413
433
  # _meta_eval( code )
@@ -483,7 +503,7 @@ module Logging
483
503
  @appenders.each do |appender|
484
504
  str << indent_str
485
505
  str << '- '
486
- str << appender.inspect
506
+ str << appender.to_s
487
507
  str << "\n"
488
508
  end
489
509