logging 2.0.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
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