logging 2.0.0 → 2.3.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.
Files changed (55) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.travis.yml +8 -5
  4. data/History.txt +59 -0
  5. data/LICENSE +22 -0
  6. data/README.md +20 -41
  7. data/Rakefile +2 -2
  8. data/examples/appenders.rb +1 -1
  9. data/examples/layouts.rb +1 -1
  10. data/examples/lazy.rb +1 -1
  11. data/examples/mdc.rb +2 -2
  12. data/examples/rails4.rb +21 -0
  13. data/examples/reusing_layouts.rb +51 -0
  14. data/lib/logging.rb +99 -9
  15. data/lib/logging/appender.rb +13 -34
  16. data/lib/logging/appenders/buffering.rb +130 -59
  17. data/lib/logging/appenders/console.rb +68 -57
  18. data/lib/logging/appenders/file.rb +43 -22
  19. data/lib/logging/appenders/io.rb +22 -16
  20. data/lib/logging/appenders/rolling_file.rb +60 -26
  21. data/lib/logging/appenders/string_io.rb +1 -1
  22. data/lib/logging/appenders/syslog.rb +3 -4
  23. data/lib/logging/color_scheme.rb +1 -1
  24. data/lib/logging/diagnostic_context.rb +100 -73
  25. data/lib/logging/layout.rb +144 -16
  26. data/lib/logging/layouts/parseable.rb +50 -12
  27. data/lib/logging/layouts/pattern.rb +8 -9
  28. data/lib/logging/log_event.rb +19 -12
  29. data/lib/logging/logger.rb +117 -95
  30. data/lib/logging/proxy.rb +1 -1
  31. data/lib/logging/rails_compat.rb +4 -13
  32. data/lib/logging/version.rb +1 -1
  33. data/logging.gemspec +31 -32
  34. data/script/console +8 -0
  35. data/test/appenders/{test_periodic_flushing.rb → test_async_flushing.rb} +67 -14
  36. data/test/appenders/test_buffered_io.rb +19 -18
  37. data/test/appenders/test_console.rb +55 -12
  38. data/test/appenders/test_file.rb +48 -28
  39. data/test/appenders/test_rolling_file.rb +18 -12
  40. data/test/appenders/test_syslog.rb +6 -0
  41. data/test/benchmark.rb +42 -18
  42. data/test/layouts/test_json.rb +14 -1
  43. data/test/layouts/test_nested_exceptions.rb +124 -0
  44. data/test/layouts/test_pattern.rb +16 -3
  45. data/test/layouts/test_yaml.rb +15 -1
  46. data/test/performance.rb +66 -0
  47. data/test/setup.rb +26 -30
  48. data/test/test_appender.rb +2 -4
  49. data/test/test_layout.rb +49 -0
  50. data/test/test_log_event.rb +10 -2
  51. data/test/test_logger.rb +20 -3
  52. data/test/test_logging.rb +75 -4
  53. data/test/test_mapped_diagnostic_context.rb +15 -6
  54. data/test/test_nested_diagnostic_context.rb +6 -1
  55. metadata +23 -17
@@ -103,7 +103,7 @@ module Logging::Layouts
103
103
  'message' => 'format_obj(event.data)'.freeze,
104
104
  'file' => 'event.file'.freeze,
105
105
  'line' => 'event.line'.freeze,
106
- 'method' => 'event.method'.freeze,
106
+ 'method' => 'event.method_name'.freeze,
107
107
  'hostname' => "'#{Socket.gethostname}'".freeze,
108
108
  'pid' => 'Process.pid'.freeze,
109
109
  'millis' => 'Integer((event.time-@created_at)*1000)'.freeze,
@@ -177,8 +177,9 @@ module Logging::Layouts
177
177
  #
178
178
  # Creates a new Parseable layout using the following options:
179
179
  #
180
- # :style => :json or :yaml
181
- # :items => %w[timestamp level logger message]
180
+ # :style => :json or :yaml
181
+ # :items => %w[timestamp level logger message]
182
+ # :utc_offset => "-06:00" or -21600 or "UTC"
182
183
  #
183
184
  def initialize( opts = {} )
184
185
  super
@@ -218,10 +219,15 @@ module Logging::Layouts
218
219
  def format_obj( obj )
219
220
  case obj
220
221
  when Exception
221
- h = { :class => obj.class.name,
222
- :message => obj.message }
223
- h[:backtrace] = obj.backtrace if @backtrace && !obj.backtrace.nil?
224
- h
222
+ hash = {
223
+ :class => obj.class.name,
224
+ :message => obj.message
225
+ }
226
+ hash[:backtrace] = obj.backtrace if backtrace? && obj.backtrace
227
+
228
+ cause = format_cause(obj)
229
+ hash[:cause] = cause unless cause.empty?
230
+ hash
225
231
  when Time
226
232
  iso8601_format(obj)
227
233
  else
@@ -229,6 +235,37 @@ module Logging::Layouts
229
235
  end
230
236
  end
231
237
 
238
+ # Internal: Format any nested exceptions found in the given exception `e`
239
+ # while respecting the maximum `cause_depth`.
240
+ #
241
+ # e - Exception to format
242
+ #
243
+ # Returns the cause formatted as a Hash
244
+ def format_cause(e)
245
+ rv = curr = {}
246
+ prev = nil
247
+
248
+ cause_depth.times do
249
+ break unless e.respond_to?(:cause) && e.cause
250
+
251
+ cause = e.cause
252
+ curr[:class] = cause.class.name
253
+ curr[:message] = cause.message
254
+ curr[:backtrace] = format_cause_backtrace(e, cause) if backtrace? && cause.backtrace
255
+
256
+ prev[:cause] = curr unless prev.nil?
257
+ prev, curr = curr, {}
258
+
259
+ e = cause
260
+ end
261
+
262
+ if e.respond_to?(:cause) && e.cause
263
+ prev[:cause] = {message: "Further #cause backtraces were omitted"}
264
+ end
265
+
266
+ rv
267
+ end
268
+
232
269
  private
233
270
 
234
271
  # Call the appropriate class level create format method based on the
@@ -241,9 +278,11 @@ module Logging::Layouts
241
278
  else raise ArgumentError, "unknown format style '#@style'" end
242
279
  end
243
280
 
244
- # Convert the given time _value_ into an ISO8601 formatted time string.
281
+ # Convert the given `time` into an ISO8601 formatted time string.
245
282
  #
246
- def iso8601_format( value )
283
+ def iso8601_format( time )
284
+ value = apply_utc_offset(time)
285
+
247
286
  str = value.strftime('%Y-%m-%dT%H:%M:%S')
248
287
  str << ('.%06d' % value.usec)
249
288
 
@@ -254,6 +293,5 @@ module Logging::Layouts
254
293
  return str << (value.gmt_offset < 0 ? '-' : '+') << offset
255
294
  end
256
295
 
257
- end # Parseable
258
- end # Logging::Layouts
259
-
296
+ end
297
+ end
@@ -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
@@ -164,14 +164,12 @@ module Logging::Layouts
164
164
  def self.create_date_format_methods( pl )
165
165
  code = "undef :format_date if method_defined? :format_date\n"
166
166
  code << "def format_date( time )\n"
167
+ code << "time = apply_utc_offset(time)\n"
167
168
  if pl.date_method.nil?
168
169
  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
170
+ code << "time.strftime('#{pl.date_pattern.gsub('%s','%6N')}')\n"
173
171
  else
174
- code << "time.strftime '#{pl.date_pattern}'\n"
172
+ code << "time.strftime('#{pl.date_pattern}')\n"
175
173
  end
176
174
  else
177
175
  code << "time.#{pl.date_method}\n"
@@ -206,7 +204,8 @@ module Logging::Layouts
206
204
  #
207
205
  # :pattern => "[%d] %-5l -- %c : %m\n"
208
206
  # :date_pattern => "%Y-%m-%d %H:%M:%S"
209
- # :date_method => 'usec' or 'to_s'
207
+ # :date_method => "usec" or "to_s"
208
+ # :utc_offset => "-06:00" or -21600 or "UTC"
210
209
  # :color_scheme => :default
211
210
  #
212
211
  # If used, :date_method will supersede :date_pattern.
@@ -219,7 +218,7 @@ module Logging::Layouts
219
218
  #
220
219
  def initialize( opts = {} )
221
220
  super
222
- @created_at = Time.now
221
+ @created_at = Time.now.freeze
223
222
 
224
223
  @date_pattern = opts.fetch(:date_pattern, nil)
225
224
  @date_method = opts.fetch(:date_method, nil)
@@ -308,7 +307,7 @@ module Logging::Layouts
308
307
  'l' => '::Logging::LNAMES[event.level]'.freeze,
309
308
  'L' => 'event.line'.freeze,
310
309
  'm' => 'format_obj(event.data)'.freeze,
311
- 'M' => 'event.method'.freeze,
310
+ 'M' => 'event.method_name'.freeze,
312
311
  'h' => "'#{Socket.gethostname}'".freeze,
313
312
  'p' => 'Process.pid'.freeze,
314
313
  'r' => 'Integer((event.time-@created_at)*1000).to_s'.freeze,
@@ -2,8 +2,7 @@
2
2
  module Logging
3
3
 
4
4
  # This class defines a logging event.
5
- #
6
- LogEvent = Struct.new( :logger, :level, :data, :time, :file, :line, :method ) {
5
+ class LogEvent
7
6
  # :stopdoc:
8
7
 
9
8
  # Regular expression used to parse out caller information
@@ -11,11 +10,13 @@ module Logging
11
10
  # * $1 == filename
12
11
  # * $2 == line number
13
12
  # * $3 == method name (might be nil)
14
- CALLER_RGXP = %r/([-\.\/\(\)\w]+):(\d+)(?::in `(\w+)')?/o
13
+ CALLER_RGXP = %r/([-\.\/\(\)\w]+):(\d+)(?::in `([^']+)')?/o
15
14
  #CALLER_INDEX = 2
16
15
  CALLER_INDEX = ((defined? JRUBY_VERSION and JRUBY_VERSION > '1.6') or (defined? RUBY_ENGINE and RUBY_ENGINE[%r/^rbx/i])) ? 1 : 2
17
16
  # :startdoc:
18
17
 
18
+ attr_accessor :logger, :level, :data, :time, :file, :line, :method_name
19
+
19
20
  # call-seq:
20
21
  # LogEvent.new( logger, level, [data], caller_tracing )
21
22
  #
@@ -25,20 +26,26 @@ module Logging
25
26
  # invoked to get the execution trace of the logging method.
26
27
  #
27
28
  def initialize( logger, level, data, caller_tracing )
28
- f = l = m = ''
29
+ self.logger = logger
30
+ self.level = level
31
+ self.data = data
32
+ self.time = Time.now.freeze
29
33
 
30
34
  if caller_tracing
31
35
  stack = Kernel.caller[CALLER_INDEX]
32
36
  return if stack.nil?
33
37
 
34
38
  match = CALLER_RGXP.match(stack)
35
- f = match[1]
36
- l = Integer(match[2])
37
- m = match[3] unless match[3].nil?
39
+ self.file = match[1]
40
+ self.line = Integer(match[2])
41
+ self.method_name = match[3] unless match[3].nil?
42
+
43
+ if (bp = ::Logging.basepath) && !bp.empty? && file.index(bp) == 0
44
+ self.file = file.slice(bp.length + 1, file.length - bp.length)
45
+ end
46
+ else
47
+ self.file = self.line = self.method_name = ''
38
48
  end
39
-
40
- super(logger, level, data, Time.now, f, l, m)
41
49
  end
42
- }
43
- end # module Logging
44
-
50
+ end
51
+ end
@@ -24,89 +24,108 @@ module Logging
24
24
  #
25
25
  class Logger
26
26
 
27
- @mutex = Mutex.new # :nodoc:
27
+ # Returns the root logger.
28
+ def self.root
29
+ ::Logging::Repository.instance[:root]
30
+ end
28
31
 
29
32
  class << self
33
+ alias_method :instantiate, :new # `instantiate` becomes the "real" `new`
34
+ end
30
35
 
31
- # Returns the root logger.
32
- def root
33
- ::Logging::Repository.instance[:root]
34
- end
36
+ # Overrides the new method such that only one Logger will be created
37
+ # for any given logger name.
38
+ def self.new( *args )
39
+ args.empty? ? super : self[args.shift]
40
+ end
35
41
 
36
- alias_method :instantiate, :new # the "real" new
42
+ # Returns a logger instance for the given name.
43
+ def self.[]( name )
44
+ repo = ::Logging::Repository.instance
45
+ name = repo.to_key(name)
46
+ logger = repo[name]
47
+ return logger unless logger.nil?
48
+
49
+ # Share the same mutex that's used by 'define_log_methods' because
50
+ # it iterates over the hash of loggers, and adding a new key to a hash
51
+ # while iterating over it produces an error.
52
+ ::Logging::Logger.mutex.synchronize do
53
+ logger = repo[name]
54
+ return logger unless logger.nil? # thread-safe double checking
37
55
 
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]
56
+ logger = instantiate(name)
57
+ repo[name] = logger
58
+ repo.children(name).each { |child| child.__send__(:parent=, logger) }
59
+ logger
42
60
  end
61
+ end
43
62
 
44
- # Returns a logger instance for the given name.
45
- def []( name )
46
- repo = ::Logging::Repository.instance
47
- name = repo.to_key(name)
48
- logger = repo[name]
49
- return logger unless logger.nil?
50
-
51
- @mutex.synchronize do
52
- logger = repo[name]
53
- return logger unless logger.nil? # thread-safe double checking
63
+ # This is where the actual logging methods are defined. Two methods
64
+ # are created for each log level. The first is a query method used to
65
+ # determine if that perticular logging level is enabled. The second is
66
+ # the actual logging method that accepts a list of objects to be
67
+ # logged or a block. If a block is given, then the object returned
68
+ # from the block will be logged.
69
+ #
70
+ # Example
71
+ #
72
+ # log = Logging::Logger['my logger']
73
+ # log.level = :warn
74
+ #
75
+ # log.info? # => false
76
+ # log.warn? # => true
77
+ # log.warn 'this is your last warning'
78
+ # log.fatal 'I die!', exception
79
+ #
80
+ # log.debug do
81
+ # # expensive method to construct log message
82
+ # msg
83
+ # end
84
+ #
85
+ def self.define_log_methods( logger )
86
+ code = log_methods_for_level(logger.level)
87
+ logger._meta_eval(code, __FILE__, __LINE__)
88
+ logger
89
+ end
54
90
 
55
- logger = instantiate(name)
56
- repo[name] = logger
57
- repo.children(name).each { |c| c.__send__(:parent=, logger) }
58
- logger
91
+ # This generator is used to define the log methods for the given `level`.
92
+ # This code is evaluated in the context of a Logger instance.
93
+ #
94
+ # Returns log methods as a String
95
+ def self.log_methods_for_level( level )
96
+ code = []
97
+ ::Logging::LEVELS.each do |name,num|
98
+ code << <<-CODE
99
+ undef :#{name} if method_defined? :#{name}
100
+ undef :#{name}? if method_defined? :#{name}?
101
+ CODE
102
+
103
+ if level > num
104
+ code << <<-CODE
105
+ def #{name}?( ) false end
106
+ def #{name}( data = nil ) false end
107
+ CODE
108
+ else
109
+ code << <<-CODE
110
+ def #{name}?( ) true end
111
+ def #{name}( data = nil )
112
+ data = yield if block_given?
113
+ log_event(::Logging::LogEvent.new(@name, #{num}, data, @caller_tracing))
114
+ true
115
+ end
116
+ CODE
59
117
  end
60
118
  end
119
+ code.join("\n")
120
+ end
61
121
 
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
122
+ @mutex = ReentrantMutex.new
104
123
 
105
- logger._meta_eval(code, __FILE__, __LINE__)
106
- end
107
- logger
108
- end
109
- end # class << self
124
+ # Returns a global ReentrantMutex for use when creating Logger instances
125
+ # and/or updating log levels.
126
+ def self.mutex
127
+ @mutex
128
+ end
110
129
 
111
130
  attr_reader :name, :parent, :additive, :caller_tracing
112
131
 
@@ -197,7 +216,14 @@ module Logging
197
216
  lvl = Integer(lvl)
198
217
  return false if lvl < level
199
218
 
200
- data = yield if block_given?
219
+ if data.nil?
220
+ if block_given?
221
+ data = yield
222
+ else
223
+ data = progname
224
+ end
225
+ end
226
+
201
227
  log_event(::Logging::LogEvent.new(@name, lvl, data, @caller_tracing))
202
228
  true
203
229
  end
@@ -247,7 +273,7 @@ module Logging
247
273
  # level = :all
248
274
  #
249
275
  # Set the level for this logger. The level can be either a +String+, a
250
- # +Symbol+, or a +Fixnum+. An +ArgumentError+ is raised if this is not
276
+ # +Symbol+, or an +Integer+. An +ArgumentError+ is raised if this is not
251
277
  # the case.
252
278
  #
253
279
  # There are two special levels -- "all" and "off". The former will
@@ -277,7 +303,7 @@ module Logging
277
303
  else
278
304
  lvl = case level
279
305
  when String, Symbol; ::Logging::level_num(level)
280
- when Fixnum; level
306
+ when Integer; level
281
307
  else
282
308
  raise ArgumentError,
283
309
  "level must be a String, Symbol, or Integer"
@@ -292,6 +318,11 @@ module Logging
292
318
  self.level
293
319
  end
294
320
 
321
+ # Returns `true` if the logger has its own level defined.
322
+ def has_own_level?
323
+ !@level.nil?
324
+ end
325
+
295
326
  # Returns the list of appenders.
296
327
  #
297
328
  def appenders
@@ -353,17 +384,7 @@ module Logging
353
384
  #
354
385
  def clear_appenders( ) @appenders.clear end
355
386
 
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
387
+ protected
367
388
 
368
389
  # call-seq:
369
390
  # parent = ParentLogger
@@ -396,18 +417,20 @@ module Logging
396
417
  #
397
418
  # Recursively call this method on all our children loggers.
398
419
  #
399
- def define_log_methods( force = false )
400
- return if @level and !force
420
+ def define_log_methods( force = false, code = nil )
421
+ return if has_own_level? and !force
401
422
 
402
- ::Logging::Logger.define_log_methods(self)
403
- ::Logging::Repository.instance.children(name).each do |c|
404
- c.define_log_methods
423
+ ::Logging::Logger.mutex.synchronize do
424
+ ::Logging::Logger.define_log_methods(self)
425
+ ::Logging::Repository.instance.children(name).each do |child|
426
+ child.define_log_methods
427
+ end
405
428
  end
406
429
  self
407
430
  end
408
431
 
409
432
  # :stopdoc:
410
- public
433
+ public
411
434
 
412
435
  # call-seq:
413
436
  # _meta_eval( code )
@@ -483,7 +506,7 @@ module Logging
483
506
  @appenders.each do |appender|
484
507
  str << indent_str
485
508
  str << '- '
486
- str << appender.inspect
509
+ str << appender.to_s
487
510
  str << "\n"
488
511
  end
489
512
 
@@ -491,6 +514,5 @@ module Logging
491
514
  end
492
515
  # :startdoc:
493
516
 
494
- end # Logger
495
- end # Logging
496
-
517
+ end
518
+ end