logging 2.0.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +8 -5
- data/History.txt +59 -0
- data/LICENSE +22 -0
- data/README.md +20 -41
- data/Rakefile +2 -2
- data/examples/appenders.rb +1 -1
- data/examples/layouts.rb +1 -1
- data/examples/lazy.rb +1 -1
- data/examples/mdc.rb +2 -2
- data/examples/rails4.rb +21 -0
- data/examples/reusing_layouts.rb +51 -0
- data/lib/logging.rb +99 -9
- data/lib/logging/appender.rb +13 -34
- data/lib/logging/appenders/buffering.rb +130 -59
- data/lib/logging/appenders/console.rb +68 -57
- data/lib/logging/appenders/file.rb +43 -22
- data/lib/logging/appenders/io.rb +22 -16
- data/lib/logging/appenders/rolling_file.rb +60 -26
- data/lib/logging/appenders/string_io.rb +1 -1
- data/lib/logging/appenders/syslog.rb +3 -4
- data/lib/logging/color_scheme.rb +1 -1
- data/lib/logging/diagnostic_context.rb +100 -73
- data/lib/logging/layout.rb +144 -16
- data/lib/logging/layouts/parseable.rb +50 -12
- data/lib/logging/layouts/pattern.rb +8 -9
- data/lib/logging/log_event.rb +19 -12
- data/lib/logging/logger.rb +117 -95
- data/lib/logging/proxy.rb +1 -1
- data/lib/logging/rails_compat.rb +4 -13
- data/lib/logging/version.rb +1 -1
- data/logging.gemspec +31 -32
- data/script/console +8 -0
- data/test/appenders/{test_periodic_flushing.rb → test_async_flushing.rb} +67 -14
- data/test/appenders/test_buffered_io.rb +19 -18
- data/test/appenders/test_console.rb +55 -12
- data/test/appenders/test_file.rb +48 -28
- data/test/appenders/test_rolling_file.rb +18 -12
- data/test/appenders/test_syslog.rb +6 -0
- data/test/benchmark.rb +42 -18
- data/test/layouts/test_json.rb +14 -1
- data/test/layouts/test_nested_exceptions.rb +124 -0
- data/test/layouts/test_pattern.rb +16 -3
- data/test/layouts/test_yaml.rb +15 -1
- data/test/performance.rb +66 -0
- data/test/setup.rb +26 -30
- data/test/test_appender.rb +2 -4
- data/test/test_layout.rb +49 -0
- data/test/test_log_event.rb +10 -2
- data/test/test_logger.rb +20 -3
- data/test/test_logging.rb +75 -4
- data/test/test_mapped_diagnostic_context.rb +15 -6
- data/test/test_nested_diagnostic_context.rb +6 -1
- metadata +23 -17
@@ -9,7 +9,7 @@ module Logging::Appenders
|
|
9
9
|
# Accessor / Factory for the Syslog appender.
|
10
10
|
#
|
11
11
|
def self.syslog( *args )
|
12
|
-
|
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
|
|
@@ -188,7 +188,7 @@ module Logging::Appenders
|
|
188
188
|
end
|
189
189
|
return if message.empty?
|
190
190
|
|
191
|
-
@syslog.log(pri, '%s', message)
|
191
|
+
@mutex.synchronize { @syslog.log(pri, '%s', message) }
|
192
192
|
self
|
193
193
|
end
|
194
194
|
|
@@ -205,11 +205,10 @@ module Logging::Appenders
|
|
205
205
|
level = level.to_s.upcase
|
206
206
|
self.class.const_get level
|
207
207
|
else
|
208
|
-
raise ArgumentError, "
|
208
|
+
raise ArgumentError, "unknown level '#{level}'"
|
209
209
|
end
|
210
210
|
end
|
211
211
|
|
212
212
|
end # Syslog
|
213
213
|
end # Logging::Appenders
|
214
214
|
end # HAVE_SYSLOG
|
215
|
-
|
data/lib/logging/color_scheme.rb
CHANGED
@@ -40,7 +40,7 @@ module Logging
|
|
40
40
|
# Store a color scheme by name.
|
41
41
|
#
|
42
42
|
def []=( name, value )
|
43
|
-
raise ArgumentError, "Silly! That's not a ColorSchmeme!" unless ColorScheme
|
43
|
+
raise ArgumentError, "Silly! That's not a ColorSchmeme!" unless value.is_a?(ColorScheme)
|
44
44
|
@color_schemes[name.to_s] = value
|
45
45
|
end
|
46
46
|
|
@@ -110,7 +110,7 @@ module Logging
|
|
110
110
|
# Returns nil or the Hash removed from the stack.
|
111
111
|
#
|
112
112
|
def pop
|
113
|
-
return unless Thread.current
|
113
|
+
return unless Thread.current.thread_variable_get(STACK_NAME)
|
114
114
|
return unless stack.length > 1
|
115
115
|
clear_context
|
116
116
|
stack.pop
|
@@ -125,7 +125,7 @@ module Logging
|
|
125
125
|
#
|
126
126
|
def clear
|
127
127
|
clear_context
|
128
|
-
Thread.current
|
128
|
+
Thread.current.thread_variable_set(STACK_NAME, nil)
|
129
129
|
self
|
130
130
|
end
|
131
131
|
|
@@ -140,15 +140,14 @@ module Logging
|
|
140
140
|
def inherit( obj )
|
141
141
|
case obj
|
142
142
|
when Hash
|
143
|
-
Thread.current
|
143
|
+
Thread.current.thread_variable_set(STACK_NAME, [obj.dup])
|
144
144
|
when Thread
|
145
145
|
return if Thread.current == obj
|
146
|
-
|
147
|
-
if obj
|
148
|
-
|
149
|
-
Thread.current[STACK_NAME] = [hash]
|
146
|
+
DIAGNOSTIC_MUTEX.synchronize do
|
147
|
+
if hash = obj.thread_variable_get(STACK_NAME)
|
148
|
+
Thread.current.thread_variable_set(STACK_NAME, [flatten(hash)])
|
150
149
|
end
|
151
|
-
|
150
|
+
end
|
152
151
|
end
|
153
152
|
|
154
153
|
self
|
@@ -159,12 +158,18 @@ module Logging
|
|
159
158
|
# application.
|
160
159
|
#
|
161
160
|
def context
|
162
|
-
c = Thread.current
|
163
|
-
return c unless c.nil?
|
161
|
+
c = Thread.current.thread_variable_get(NAME)
|
164
162
|
|
165
|
-
|
163
|
+
if c.nil?
|
164
|
+
c = if Thread.current.thread_variable_get(STACK_NAME)
|
165
|
+
flatten(stack)
|
166
|
+
else
|
167
|
+
Hash.new
|
168
|
+
end
|
169
|
+
Thread.current.thread_variable_set(NAME, c)
|
170
|
+
end
|
166
171
|
|
167
|
-
|
172
|
+
return c
|
168
173
|
end
|
169
174
|
|
170
175
|
# Returns the stack of Hash objects that are storing the diagnostic
|
@@ -172,7 +177,12 @@ module Logging
|
|
172
177
|
# one Hash.
|
173
178
|
#
|
174
179
|
def stack
|
175
|
-
Thread.current
|
180
|
+
s = Thread.current.thread_variable_get(STACK_NAME)
|
181
|
+
if s.nil?
|
182
|
+
s = [{}]
|
183
|
+
Thread.current.thread_variable_set(STACK_NAME, s)
|
184
|
+
end
|
185
|
+
return s
|
176
186
|
end
|
177
187
|
|
178
188
|
# Returns the most current Hash from the stack of contexts.
|
@@ -184,7 +194,7 @@ module Logging
|
|
184
194
|
# Remove the flattened context.
|
185
195
|
#
|
186
196
|
def clear_context
|
187
|
-
Thread.current
|
197
|
+
Thread.current.thread_variable_set(NAME, nil)
|
188
198
|
self
|
189
199
|
end
|
190
200
|
|
@@ -199,7 +209,7 @@ module Logging
|
|
199
209
|
# Raises an ArgumentError if hash is not a Hash.
|
200
210
|
#
|
201
211
|
def sanitize( hash, target = {} )
|
202
|
-
unless Hash
|
212
|
+
unless hash.is_a?(Hash)
|
203
213
|
raise ArgumentError, "Expecting a Hash but received a #{hash.class.name}"
|
204
214
|
end
|
205
215
|
|
@@ -320,7 +330,7 @@ module Logging
|
|
320
330
|
# Returns the NestedDiagnosticContext.
|
321
331
|
#
|
322
332
|
def clear
|
323
|
-
Thread.current
|
333
|
+
Thread.current.thread_variable_set(NAME, nil)
|
324
334
|
self
|
325
335
|
end
|
326
336
|
|
@@ -335,12 +345,12 @@ module Logging
|
|
335
345
|
def inherit( obj )
|
336
346
|
case obj
|
337
347
|
when Array
|
338
|
-
Thread.current
|
348
|
+
Thread.current.thread_variable_set(NAME, obj.dup)
|
339
349
|
when Thread
|
340
350
|
return if Thread.current == obj
|
341
|
-
|
342
|
-
Thread.current
|
343
|
-
|
351
|
+
DIAGNOSTIC_MUTEX.synchronize do
|
352
|
+
Thread.current.thread_variable_set(NAME, obj.thread_variable_get(NAME).dup) if obj.thread_variable_get(NAME)
|
353
|
+
end
|
344
354
|
end
|
345
355
|
|
346
356
|
self
|
@@ -351,7 +361,12 @@ module Logging
|
|
351
361
|
# running in the application.
|
352
362
|
#
|
353
363
|
def context
|
354
|
-
Thread.current
|
364
|
+
c = Thread.current.thread_variable_get(NAME)
|
365
|
+
if c.nil?
|
366
|
+
c = Array.new
|
367
|
+
Thread.current.thread_variable_set(NAME, c)
|
368
|
+
end
|
369
|
+
return c
|
355
370
|
end
|
356
371
|
end # NestedDiagnosticContext
|
357
372
|
|
@@ -381,13 +396,13 @@ module Logging
|
|
381
396
|
#
|
382
397
|
def self.clear_diagnostic_contexts( all = false )
|
383
398
|
if all
|
384
|
-
|
385
|
-
Thread.list.each
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
399
|
+
DIAGNOSTIC_MUTEX.synchronize do
|
400
|
+
Thread.list.each do |t|
|
401
|
+
t.thread_variable_set(MappedDiagnosticContext::NAME, nil) if t.thread_variable?(MappedDiagnosticContext::NAME)
|
402
|
+
t.thread_variable_set(NestedDiagnosticContext::NAME, nil) if t.thread_variable?(NestedDiagnosticContext::NAME)
|
403
|
+
t.thread_variable_set(MappedDiagnosticContext::STACK_NAME, nil) if t.thread_variable?(MappedDiagnosticContext::STACK_NAME)
|
404
|
+
end
|
405
|
+
end
|
391
406
|
else
|
392
407
|
MappedDiagnosticContext.clear
|
393
408
|
NestedDiagnosticContext.clear
|
@@ -396,60 +411,72 @@ module Logging
|
|
396
411
|
self
|
397
412
|
end
|
398
413
|
|
399
|
-
|
400
|
-
|
414
|
+
DIAGNOSTIC_MUTEX = Mutex.new
|
415
|
+
end
|
401
416
|
|
402
417
|
# :stopdoc:
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
end
|
413
|
-
__
|
414
|
-
end
|
415
|
-
|
416
|
-
private
|
418
|
+
Logging::INHERIT_CONTEXT =
|
419
|
+
if ENV.key?("LOGGING_INHERIT_CONTEXT")
|
420
|
+
case ENV["LOGGING_INHERIT_CONTEXT"].downcase
|
421
|
+
when 'false', 'no', '0'; false
|
422
|
+
when false, nil; false
|
423
|
+
else true end
|
424
|
+
else
|
425
|
+
true
|
426
|
+
end
|
417
427
|
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
# parent Thread at the time the child Thread is created. The code below does
|
422
|
-
# just this. If there is a more idiomatic way of accomplishing this in Ruby,
|
423
|
-
# please let me know!
|
424
|
-
#
|
425
|
-
# Also, great care is taken in this code to ensure that a reference to the
|
426
|
-
# parent thread does not exist in the binding associated with the block
|
427
|
-
# being executed in the child thread. The same is true for the parent
|
428
|
-
# thread's mdc and ndc. If any of those references end up in the binding,
|
429
|
-
# then they cannot be garbage collected until the child thread exits.
|
430
|
-
#
|
431
|
-
def create_with_logging_context( m, *a, &b )
|
432
|
-
mdc, ndc = nil
|
428
|
+
if Logging::INHERIT_CONTEXT
|
429
|
+
class Thread
|
430
|
+
class << self
|
433
431
|
|
434
|
-
|
435
|
-
|
432
|
+
%w[new start fork].each do |m|
|
433
|
+
class_eval <<-__, __FILE__, __LINE__
|
434
|
+
alias_method :_orig_#{m}, :#{m}
|
435
|
+
private :_orig_#{m}
|
436
|
+
def #{m}( *a, &b )
|
437
|
+
create_with_logging_context(:_orig_#{m}, *a ,&b)
|
438
|
+
end
|
439
|
+
__
|
436
440
|
end
|
437
441
|
|
438
|
-
|
439
|
-
|
442
|
+
private
|
443
|
+
|
444
|
+
# In order for the diagnostic contexts to behave properly we need to
|
445
|
+
# inherit state from the parent thread. The only way I have found to do
|
446
|
+
# this in Ruby is to override `new` and capture the contexts from the
|
447
|
+
# parent Thread at the time the child Thread is created. The code below does
|
448
|
+
# just this. If there is a more idiomatic way of accomplishing this in Ruby,
|
449
|
+
# please let me know!
|
450
|
+
#
|
451
|
+
# Also, great care is taken in this code to ensure that a reference to the
|
452
|
+
# parent thread does not exist in the binding associated with the block
|
453
|
+
# being executed in the child thread. The same is true for the parent
|
454
|
+
# thread's mdc and ndc. If any of those references end up in the binding,
|
455
|
+
# then they cannot be garbage collected until the child thread exits.
|
456
|
+
#
|
457
|
+
def create_with_logging_context( m, *a, &b )
|
458
|
+
mdc, ndc = nil
|
459
|
+
|
460
|
+
if Thread.current.thread_variable_get(Logging::MappedDiagnosticContext::STACK_NAME)
|
461
|
+
mdc = Logging::MappedDiagnosticContext.context.dup
|
462
|
+
end
|
463
|
+
|
464
|
+
if Thread.current.thread_variable_get(Logging::NestedDiagnosticContext::NAME)
|
465
|
+
ndc = Logging::NestedDiagnosticContext.context.dup
|
466
|
+
end
|
467
|
+
|
468
|
+
# This calls the actual `Thread#new` method to create the Thread instance.
|
469
|
+
# If your memory profiling tool says this method is leaking memory, then
|
470
|
+
# you are leaking Thread instances somewhere.
|
471
|
+
self.send(m, *a) { |*args|
|
472
|
+
Logging::MappedDiagnosticContext.inherit(mdc)
|
473
|
+
Logging::NestedDiagnosticContext.inherit(ndc)
|
474
|
+
b.call(*args)
|
475
|
+
}
|
440
476
|
end
|
441
477
|
|
442
|
-
# This calls the actual `Thread#new` method to create the Thread instance.
|
443
|
-
# If your memory profiling tool says this method is leaking memory, then
|
444
|
-
# you are leaking Thread instances somewhere.
|
445
|
-
self.send(m, *a) { |*args|
|
446
|
-
Logging::MappedDiagnosticContext.inherit(mdc)
|
447
|
-
Logging::NestedDiagnosticContext.inherit(ndc)
|
448
|
-
b.call(*args)
|
449
|
-
}
|
450
478
|
end
|
451
|
-
|
452
479
|
end
|
453
|
-
end
|
480
|
+
end
|
454
481
|
# :startdoc:
|
455
482
|
|
data/lib/logging/layout.rb
CHANGED
@@ -41,13 +41,86 @@ class Layout
|
|
41
41
|
when :inspect, :yaml, :json; f
|
42
42
|
else :string end
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
44
|
+
self.backtrace = opts.fetch(:backtrace, ::Logging.backtrace)
|
45
|
+
self.utc_offset = opts.fetch(:utc_offset, ::Logging.utc_offset)
|
46
|
+
self.cause_depth = opts.fetch(:cause_depth, ::Logging.cause_depth)
|
47
|
+
end
|
48
|
+
|
49
|
+
# call-seq:
|
50
|
+
# layout.backtrace = true
|
51
|
+
#
|
52
|
+
# Set the backtrace flag to the given value. This can be set to `true` or
|
53
|
+
# `false`.
|
54
|
+
#
|
55
|
+
def backtrace=( value )
|
56
|
+
@backtrace = case value
|
57
|
+
when :on, 'on', true; true
|
58
|
+
when :off, 'off', false; false
|
59
|
+
else
|
60
|
+
raise ArgumentError, "backtrace must be `true` or `false`"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the backtrace setting.
|
65
|
+
attr_reader :backtrace
|
66
|
+
alias :backtrace? :backtrace
|
67
|
+
|
68
|
+
# Set the UTC offset used when formatting time values. If left unset, the
|
69
|
+
# default local time zone will be used for time values. This method accepts
|
70
|
+
# the `utc_offset` format supported by the `Time#localtime` method in Ruby.
|
71
|
+
#
|
72
|
+
# Passing "UTC" or `0` as the UTC offset will cause all times to be reported
|
73
|
+
# in the UTC timezone.
|
74
|
+
#
|
75
|
+
# layout.utc_offset = "-07:00" # Mountain Standard Time in North America
|
76
|
+
# layout.utc_offset = "+01:00" # Central European Time
|
77
|
+
# layout.utc_offset = "UTC" # UTC
|
78
|
+
# layout.utc_offset = 0 # UTC
|
79
|
+
#
|
80
|
+
def utc_offset=( value )
|
81
|
+
@utc_offset = case value
|
82
|
+
when nil; nil
|
83
|
+
when "UTC", "GMT", 0; 0
|
84
|
+
else
|
85
|
+
Time.now.localtime(value)
|
86
|
+
value
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns the UTC offset.
|
91
|
+
attr_reader :utc_offset
|
92
|
+
|
93
|
+
#
|
94
|
+
#
|
95
|
+
def cause_depth=( value )
|
96
|
+
if value.nil?
|
97
|
+
@cause_depth = ::Logging::DEFAULT_CAUSE_DEPTH
|
98
|
+
else
|
99
|
+
value = Integer(value)
|
100
|
+
@cause_depth = value < 0 ? ::Logging::DEFAULT_CAUSE_DEPTH : value
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns the exception cause depth formatting limit.
|
105
|
+
attr_reader :cause_depth
|
106
|
+
|
107
|
+
# Internal: Helper method that applies the UTC offset to the given `time`
|
108
|
+
# instance. A new Time is returned that is equivalent to the original `time`
|
109
|
+
# but pinned to the timezone given by the UTC offset.
|
110
|
+
#
|
111
|
+
# If a UTC offset has not been set, then the original `time` instance is
|
112
|
+
# returned unchanged.
|
113
|
+
#
|
114
|
+
def apply_utc_offset( time )
|
115
|
+
return time if utc_offset.nil?
|
116
|
+
|
117
|
+
time = time.dup
|
118
|
+
if utc_offset == 0
|
119
|
+
time.utc
|
120
|
+
else
|
121
|
+
time.localtime(utc_offset)
|
122
|
+
end
|
123
|
+
time
|
51
124
|
end
|
52
125
|
|
53
126
|
# call-seq:
|
@@ -84,11 +157,10 @@ class Layout
|
|
84
157
|
case obj
|
85
158
|
when String; obj
|
86
159
|
when Exception
|
87
|
-
|
88
|
-
if
|
89
|
-
|
90
|
-
|
91
|
-
str
|
160
|
+
lines = ["<#{obj.class.name}> #{obj.message}"]
|
161
|
+
lines.concat(obj.backtrace) if backtrace? && obj.backtrace
|
162
|
+
format_cause(obj, lines)
|
163
|
+
lines.join("\n\t")
|
92
164
|
when nil; "<#{obj.class.name}> nil"
|
93
165
|
else
|
94
166
|
str = "<#{obj.class.name}> "
|
@@ -101,6 +173,64 @@ class Layout
|
|
101
173
|
end
|
102
174
|
end
|
103
175
|
|
176
|
+
# Internal: Format any nested exceptions found in the given exception `e`
|
177
|
+
# while respecting the maximum `cause_depth`. The lines array is used to
|
178
|
+
# capture all the output lines form the nested exceptions; the array is later
|
179
|
+
# joined by the `format_obj` method.
|
180
|
+
#
|
181
|
+
# e - Exception to format
|
182
|
+
# lines - Array of output lines
|
183
|
+
#
|
184
|
+
# Returns the input `lines` Array
|
185
|
+
def format_cause(e, lines)
|
186
|
+
return lines if cause_depth == 0
|
187
|
+
|
188
|
+
cause_depth.times do
|
189
|
+
break unless e.respond_to?(:cause) && e.cause
|
190
|
+
|
191
|
+
cause = e.cause
|
192
|
+
lines << "--- Caused by ---"
|
193
|
+
lines << "<#{cause.class.name}> #{cause.message}"
|
194
|
+
lines.concat(format_cause_backtrace(e, cause)) if backtrace? && cause.backtrace
|
195
|
+
|
196
|
+
e = cause
|
197
|
+
end
|
198
|
+
|
199
|
+
if e.respond_to?(:cause) && e.cause
|
200
|
+
lines << "--- Further #cause backtraces were omitted ---"
|
201
|
+
end
|
202
|
+
|
203
|
+
lines
|
204
|
+
end
|
205
|
+
|
206
|
+
# Internal: Format the backtrace of the nested `cause` but remove the common
|
207
|
+
# exception lines from the parent exception. This helps keep the backtraces a
|
208
|
+
# wee bit shorter and more comprehensible.
|
209
|
+
#
|
210
|
+
# e - parent exception
|
211
|
+
# cause - the nested exception generating the returned backtrace
|
212
|
+
#
|
213
|
+
# Returns an Array of backtracke lines.
|
214
|
+
def format_cause_backtrace(e, cause)
|
215
|
+
# Find where the cause's backtrace differs from the parent exception's.
|
216
|
+
backtrace = Array(e.backtrace)
|
217
|
+
cause_backtrace = Array(cause.backtrace)
|
218
|
+
index = -1
|
219
|
+
min_index = [backtrace.size, cause_backtrace.size].min * -1
|
220
|
+
just_in_case = -5000
|
221
|
+
|
222
|
+
while index > min_index && backtrace[index] == cause_backtrace[index] && index >= just_in_case
|
223
|
+
index -= 1
|
224
|
+
end
|
225
|
+
|
226
|
+
# Add on a few common frames to make it clear where the backtraces line up.
|
227
|
+
index += 3
|
228
|
+
index = -1 if index >= 0
|
229
|
+
|
230
|
+
cause_backtrace[0..index]
|
231
|
+
end
|
232
|
+
|
233
|
+
|
104
234
|
# Attempt to format the _obj_ using yaml, but fall back to inspect style
|
105
235
|
# formatting if yaml fails.
|
106
236
|
#
|
@@ -126,7 +256,5 @@ class Layout
|
|
126
256
|
rescue StandardError
|
127
257
|
obj.inspect
|
128
258
|
end
|
129
|
-
|
130
|
-
end
|
131
|
-
end # module Logging
|
132
|
-
|
259
|
+
end
|
260
|
+
end
|