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.
- checksums.yaml +4 -4
- data/.travis.yml +2 -1
- data/History.txt +19 -0
- data/Rakefile +1 -1
- data/examples/rails4.rb +21 -0
- data/lib/logging/appender.rb +6 -7
- data/lib/logging/appenders/buffering.rb +127 -56
- data/lib/logging/appenders/console.rb +36 -55
- data/lib/logging/appenders/file.rb +1 -1
- data/lib/logging/appenders/io.rb +10 -7
- data/lib/logging/appenders/rolling_file.rb +2 -2
- data/lib/logging/appenders/syslog.rb +1 -1
- data/lib/logging/diagnostic_context.rb +5 -3
- data/lib/logging/layout.rb +21 -8
- data/lib/logging/layouts/pattern.rb +3 -6
- data/lib/logging/log_event.rb +1 -1
- data/lib/logging/logger.rb +109 -89
- data/lib/logging/version.rb +1 -1
- data/logging.gemspec +1 -0
- 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 +6 -3
- data/test/appenders/test_console.rb +8 -0
- data/test/appenders/test_file.rb +11 -6
- data/test/appenders/test_rolling_file.rb +6 -0
- data/test/appenders/test_syslog.rb +6 -0
- data/test/benchmark.rb +42 -18
- data/test/performance.rb +66 -0
- data/test/setup.rb +20 -28
- data/test/test_appender.rb +2 -4
- data/test/test_layout.rb +9 -0
- data/test/test_logger.rb +20 -3
- metadata +11 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e350554eb562c0d216fa262c015afb4c2a68b82e
|
4
|
+
data.tar.gz: 22820d111096e8c44e7d0908e1cb7cca27d49009
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d8167665ad0871c41f053154f7d36686afd08d15d151a3d8c759a20ab9d845060494ed580fe6c95eeddccea69c57a6d94d464f6f3c463ed126b61c8021d05ac9
|
7
|
+
data.tar.gz: e741f1bea9ca11f52a06ca5b2873007430f26b02d92262222b2e64a86ae0de9635c2482b57e40c0b512d6a180486d665b9e36196a39d418311437a855e0d5a7c
|
data/.travis.yml
CHANGED
data/History.txt
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
== 2.1.0 / 2016-03-13
|
2
|
+
|
3
|
+
Enhancements
|
4
|
+
- appender factories now fail explicitly on bad arguments [PR #117]
|
5
|
+
- console loggers (stdout, stderr) use `write` for compatibility [PR #127]
|
6
|
+
- allow whitespace in traced method names [PR #128]
|
7
|
+
- accessor methods for `backtrace` settings [PR #134]
|
8
|
+
- asynchronous writing from buffered appenders [PR #135]
|
9
|
+
- improve date format performance when outputting microseconds [PR #136]
|
10
|
+
- added some thread safety to log level setters [PR #137]
|
11
|
+
- use `write` everywhere instead of `syswrite` [PR #138]
|
12
|
+
|
13
|
+
Bug Fixes
|
14
|
+
- fixing encodings in tests [PR #116]
|
15
|
+
- fix Rails4 logger compatibility [PR #121]
|
16
|
+
- explicitly adding the MIT license [PR #123]
|
17
|
+
- update `object_id` format in inspect methods [PR #133]
|
18
|
+
- fixed Travis-CI tests
|
19
|
+
|
1
20
|
== 2.0.0 / 2015-03-28
|
2
21
|
|
3
22
|
Enhancements
|
data/Rakefile
CHANGED
@@ -28,7 +28,7 @@ Bones {
|
|
28
28
|
depend_on 'little-plugger', '~> 1.1'
|
29
29
|
depend_on 'multi_json', '~> 1.10'
|
30
30
|
|
31
|
-
depend_on '
|
31
|
+
depend_on 'test-unit', '~> 3.1', :development => true
|
32
32
|
depend_on 'bones-git', '~> 1.3', :development => true
|
33
33
|
#depend_on 'bones-rcov', :development => true
|
34
34
|
}
|
data/examples/rails4.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# :stopdoc:
|
2
|
+
#
|
3
|
+
# Rails 4 allows you to hook up multiple loggers (even those external to this gem)
|
4
|
+
# so you can use a single Rails.logger statement. For Rails developers, this is
|
5
|
+
# easier because if you ever change logging frameworks, you don't have to change
|
6
|
+
# all of your app code.
|
7
|
+
#
|
8
|
+
# See http://railsware.com/blog/2014/08/07/rails-logging-into-several-backends/
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'logging'
|
12
|
+
|
13
|
+
log = Logging.logger(STDOUT)
|
14
|
+
log.level = :warn
|
15
|
+
|
16
|
+
Rails.logger.extend(ActiveSupport::Logger.broadcast(log))
|
17
|
+
|
18
|
+
Rails.logger.debug "this debug message will not be output by the logger"
|
19
|
+
Rails.logger.warn "this is your last warning"
|
20
|
+
|
21
|
+
# :startdoc:
|
data/lib/logging/appender.rb
CHANGED
@@ -251,17 +251,16 @@ class Appender
|
|
251
251
|
self
|
252
252
|
end
|
253
253
|
|
254
|
+
# Save off the original `to_s` for use in tests
|
255
|
+
alias_method :_to_s, :to_s
|
256
|
+
|
254
257
|
# call-seq:
|
255
|
-
#
|
258
|
+
# to_s => string
|
256
259
|
#
|
257
260
|
# Returns a string representation of the appender.
|
258
261
|
#
|
259
|
-
def
|
260
|
-
"<%s
|
261
|
-
self.class.name.sub(%r/^Logging::/, ''),
|
262
|
-
self.object_id,
|
263
|
-
self.name
|
264
|
-
]
|
262
|
+
def to_s
|
263
|
+
"<%s name=\"%s\">" % [self.class.name.sub(%r/^Logging::/, ''), self.name]
|
265
264
|
end
|
266
265
|
|
267
266
|
# Returns the current Encoding for the appender or nil if an encoding has
|
@@ -27,6 +27,10 @@ module Logging::Appenders
|
|
27
27
|
# flush_period.
|
28
28
|
attr_reader :flush_period
|
29
29
|
|
30
|
+
# When set, the buffer will be flushed using an asynchronous Thread. That
|
31
|
+
# is, the main program thread will not be blocked during writes.
|
32
|
+
attr_reader :async
|
33
|
+
|
30
34
|
# Messages will be written in chunks. This controls the number of messages
|
31
35
|
# to pull from the buffer for each write operation. The default is to pull
|
32
36
|
# all messages from the buffer at once.
|
@@ -39,20 +43,21 @@ module Logging::Appenders
|
|
39
43
|
@buffer = []
|
40
44
|
@immediate = []
|
41
45
|
@auto_flushing = 1
|
42
|
-
@
|
46
|
+
@async = false
|
47
|
+
@flush_period = @async_flusher = nil
|
43
48
|
|
44
49
|
super(*args, &block)
|
45
50
|
end
|
46
51
|
|
47
|
-
# Close the message buffer by flushing all log events to the appender. If
|
48
|
-
#
|
52
|
+
# Close the message buffer by flushing all log events to the appender. If an
|
53
|
+
# async flusher thread is running, shut it down and allow it to exit.
|
49
54
|
#
|
50
55
|
def close( *args )
|
51
56
|
flush
|
52
57
|
|
53
|
-
if @
|
54
|
-
@
|
55
|
-
@
|
58
|
+
if @async_flusher
|
59
|
+
@async_flusher.stop
|
60
|
+
@async_flusher = nil
|
56
61
|
Thread.pass
|
57
62
|
end
|
58
63
|
|
@@ -60,11 +65,11 @@ module Logging::Appenders
|
|
60
65
|
end
|
61
66
|
|
62
67
|
# Reopen the connection to the underlying logging destination. In addition
|
63
|
-
# if the appender is configured for
|
68
|
+
# if the appender is configured for asynchronous flushing, then the flushing
|
64
69
|
# thread will be stopped and restarted.
|
65
70
|
#
|
66
71
|
def reopen
|
67
|
-
|
72
|
+
_setup_async_flusher
|
68
73
|
super
|
69
74
|
end
|
70
75
|
|
@@ -194,21 +199,43 @@ module Logging::Appenders
|
|
194
199
|
# manually if so desired.
|
195
200
|
#
|
196
201
|
def flush_period=( period )
|
197
|
-
|
202
|
+
@flush_period =
|
198
203
|
case period
|
199
204
|
when Integer, Float, nil; period
|
200
|
-
when String
|
205
|
+
when String
|
201
206
|
num = _parse_hours_minutes_seconds(period) || _parse_numeric(period)
|
202
|
-
|
207
|
+
raise ArgumentError.new("unrecognized flush period: #{period.inspect}") if num.nil?
|
203
208
|
num
|
204
|
-
else
|
209
|
+
else
|
210
|
+
raise ArgumentError.new("unrecognized flush period: #{period.inspect}")
|
211
|
+
end
|
205
212
|
|
206
|
-
|
207
|
-
|
213
|
+
if !@flush_period.nil? && @flush_period <= 0
|
214
|
+
raise ArgumentError,
|
215
|
+
"flush_period must be greater than zero: #{period.inspect}"
|
216
|
+
end
|
208
217
|
|
209
|
-
|
218
|
+
_setup_async_flusher
|
210
219
|
end
|
211
220
|
|
221
|
+
# Returns `true` if an asynchronous flush period has been defined for the
|
222
|
+
# appender.
|
223
|
+
def flush_period?
|
224
|
+
!@flush_period.nil?
|
225
|
+
end
|
226
|
+
|
227
|
+
# Enable or disable asynchronous logging via a dedicated logging Thread.
|
228
|
+
# Pass in `true` to enable and `false` to disable.
|
229
|
+
#
|
230
|
+
# bool - A boolean value
|
231
|
+
#
|
232
|
+
def async=( bool )
|
233
|
+
@async = bool ? true : false
|
234
|
+
_setup_async_flusher
|
235
|
+
end
|
236
|
+
|
237
|
+
alias_method :async?, :async
|
238
|
+
|
212
239
|
protected
|
213
240
|
|
214
241
|
# Configure the buffering using the arguments found in the give options
|
@@ -223,12 +250,12 @@ module Logging::Appenders
|
|
223
250
|
self.immediate_at = opts.fetch(:immediate_at, '')
|
224
251
|
self.auto_flushing = opts.fetch(:auto_flushing, true)
|
225
252
|
self.flush_period = opts.fetch(:flush_period, nil)
|
253
|
+
self.async = opts.fetch(:async, false)
|
226
254
|
self.write_size = opts.fetch(:write_size, DEFAULT_BUFFER_SIZE)
|
227
255
|
end
|
228
256
|
|
229
|
-
# Returns true if the
|
230
|
-
# immediate logging levels. Otherwise returns false
|
231
|
-
#
|
257
|
+
# Returns `true` if the `event` level matches one of the configured
|
258
|
+
# immediate logging levels. Otherwise returns `false`.
|
232
259
|
def immediate?( event )
|
233
260
|
return false unless event.respond_to? :level
|
234
261
|
@immediate[event.level]
|
@@ -240,14 +267,15 @@ module Logging::Appenders
|
|
240
267
|
# call-seq:
|
241
268
|
# write( event )
|
242
269
|
#
|
243
|
-
# Writes the given
|
270
|
+
# Writes the given `event` to the logging destination. The `event` can
|
244
271
|
# be either a LogEvent or a String. If a LogEvent, then it will be
|
245
272
|
# formatted using the layout given to the appender when it was created.
|
246
273
|
#
|
247
|
-
# The
|
274
|
+
# The `event` will be formatted and then buffered until the
|
248
275
|
# "auto_flushing" level has been reached. At this time the canonical_write
|
249
276
|
# method will be used to log all events stored in the buffer.
|
250
277
|
#
|
278
|
+
# Returns this appender instance
|
251
279
|
def write( event )
|
252
280
|
str = event.instance_of?(::Logging::LogEvent) ?
|
253
281
|
layout.format(event) : event.to_s
|
@@ -256,12 +284,21 @@ module Logging::Appenders
|
|
256
284
|
if @auto_flushing == 1
|
257
285
|
canonical_write(str)
|
258
286
|
else
|
287
|
+
str = str.force_encoding(encoding) if encoding && str.encoding != encoding
|
259
288
|
sync {
|
260
|
-
str = str.force_encoding(encoding) if encoding && str.encoding != encoding
|
261
289
|
@buffer << str
|
262
290
|
}
|
263
|
-
@
|
264
|
-
|
291
|
+
flush_now = @buffer.length >= @auto_flushing || immediate?(event)
|
292
|
+
|
293
|
+
if flush_now
|
294
|
+
if async?
|
295
|
+
@async_flusher.signal(flush_now)
|
296
|
+
else
|
297
|
+
self.flush
|
298
|
+
end
|
299
|
+
elsif @async_flusher && flush_period?
|
300
|
+
@async_flusher.signal
|
301
|
+
end
|
265
302
|
end
|
266
303
|
|
267
304
|
self
|
@@ -270,9 +307,14 @@ module Logging::Appenders
|
|
270
307
|
# Attempt to parse an hours/minutes/seconds value from the string and return
|
271
308
|
# an integer number of seconds.
|
272
309
|
#
|
310
|
+
# str - The input String to parse for time values.
|
311
|
+
#
|
312
|
+
# Examples
|
313
|
+
#
|
273
314
|
# _parse_hours_minutes_seconds("14:12:42") #=> 51162
|
274
315
|
# _parse_hours_minutes_seconds("foo") #=> nil
|
275
316
|
#
|
317
|
+
# Returns a Numeric or `nil`
|
276
318
|
def _parse_hours_minutes_seconds( str )
|
277
319
|
m = %r/^\s*(\d{2,}):(\d{2}):(\d{2}(?:\.\d+)?)\s*$/.match(str)
|
278
320
|
return if m.nil?
|
@@ -281,49 +323,60 @@ module Logging::Appenders
|
|
281
323
|
end
|
282
324
|
|
283
325
|
# Convert the string into a numeric value. If the string does not
|
284
|
-
# represent a valid Integer or Float then
|
326
|
+
# represent a valid Integer or Float then `nil` is returned.
|
327
|
+
#
|
328
|
+
# str - The input String to parse for Numeric values.
|
329
|
+
#
|
330
|
+
# Examples
|
285
331
|
#
|
286
332
|
# _parse_numeric("10") #=> 10
|
287
333
|
# _parse_numeric("1.0") #=> 1.0
|
288
334
|
# _parse_numeric("foo") #=> nil
|
289
335
|
#
|
336
|
+
# Returns a Numeric or `nil`
|
290
337
|
def _parse_numeric( str )
|
291
338
|
Integer(str) rescue (Float(str) rescue nil)
|
292
339
|
end
|
293
340
|
|
294
|
-
# Using the flush_period, create a new
|
341
|
+
# Using the flush_period, create a new AsyncFlusher attached to this
|
295
342
|
# appender. If the flush_period is nil, then no action will be taken. If a
|
296
|
-
#
|
343
|
+
# AsyncFlusher already exists, it will be stopped and a new one will be
|
297
344
|
# created.
|
298
345
|
#
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
@
|
346
|
+
# Returns `nil`
|
347
|
+
def _setup_async_flusher
|
348
|
+
# stop and remove any existing async flusher instance
|
349
|
+
if @async_flusher
|
350
|
+
@async_flusher.stop
|
351
|
+
@async_flusher = nil
|
304
352
|
Thread.pass
|
305
353
|
end
|
306
354
|
|
307
|
-
# create a new
|
308
|
-
if @flush_period
|
355
|
+
# create a new async flusher if we have a valid flush period
|
356
|
+
if @flush_period || async?
|
309
357
|
@auto_flushing = DEFAULT_BUFFER_SIZE unless @auto_flushing > 1
|
310
|
-
@
|
311
|
-
@
|
358
|
+
@async_flusher = AsyncFlusher.new(self, @flush_period)
|
359
|
+
@async_flusher.start
|
360
|
+
Thread.pass
|
312
361
|
end
|
362
|
+
|
363
|
+
nil
|
313
364
|
end
|
314
365
|
|
315
366
|
# :stopdoc:
|
316
367
|
|
317
|
-
# The
|
368
|
+
# The AsyncFlusher contains an internal run loop that will periodically
|
318
369
|
# wake up and flush any log events contained in the message buffer of the
|
319
|
-
# owning appender instance. The
|
370
|
+
# owning appender instance. The AsyncFlusher relies on a `signal` from
|
320
371
|
# the appender in order to wakeup and perform the flush on the appender.
|
321
|
-
|
322
|
-
class PeriodicFlusher
|
372
|
+
class AsyncFlusher
|
323
373
|
|
324
|
-
# Create a new
|
325
|
-
# method on the given
|
326
|
-
# every
|
374
|
+
# Create a new AsyncFlusher instance that will call the `flush`
|
375
|
+
# method on the given `appender`. The `flush` method will be called
|
376
|
+
# every `period` seconds, but only when the message buffer is non-empty.
|
377
|
+
#
|
378
|
+
# appender - The Appender instance to periodically `flush`
|
379
|
+
# period - The Numeric sleep period or `nil`
|
327
380
|
#
|
328
381
|
def initialize( appender, period )
|
329
382
|
@appender = appender
|
@@ -334,10 +387,12 @@ module Logging::Appenders
|
|
334
387
|
@thread = nil
|
335
388
|
@waiting = nil
|
336
389
|
@signaled = false
|
390
|
+
@immediate = 0
|
337
391
|
end
|
338
392
|
|
339
393
|
# Start the periodic flusher's internal run loop.
|
340
394
|
#
|
395
|
+
# Returns this flusher instance
|
341
396
|
def start
|
342
397
|
return if @thread
|
343
398
|
|
@@ -345,57 +400,75 @@ module Logging::Appenders
|
|
345
400
|
begin
|
346
401
|
break if Thread.current[:stop]
|
347
402
|
_wait_for_signal
|
348
|
-
|
403
|
+
_try_to_sleep
|
349
404
|
@appender.flush
|
350
405
|
rescue => err
|
351
|
-
::Logging.log_internal {"
|
406
|
+
::Logging.log_internal {"AsyncFlusher for appender #{@appender.inspect} encountered an error"}
|
352
407
|
::Logging.log_internal_error(err)
|
353
408
|
end
|
354
|
-
}
|
409
|
+
}}
|
355
410
|
|
356
411
|
self
|
357
412
|
end
|
358
413
|
|
359
|
-
# Stop the
|
414
|
+
# Stop the async flusher's internal run loop.
|
360
415
|
#
|
416
|
+
# Returns this flusher instance
|
361
417
|
def stop
|
362
418
|
return if @thread.nil?
|
363
419
|
@thread[:stop] = true
|
364
420
|
signal if waiting?
|
421
|
+
@thread = nil
|
365
422
|
self
|
366
423
|
end
|
367
424
|
|
368
|
-
# Signal the
|
425
|
+
# Signal the async flusher. This will wake up the run loop if it is
|
369
426
|
# currently waiting for something to do. If the signal method is never
|
370
|
-
# called, the
|
427
|
+
# called, the async flusher will never perform the flush action on
|
371
428
|
# the appender.
|
372
429
|
#
|
373
|
-
|
430
|
+
# immediate - Set to `true` if the sleep period should be skipped
|
431
|
+
#
|
432
|
+
# Returns this flusher instance
|
433
|
+
def signal( immediate = nil )
|
374
434
|
return if Thread.current == @thread # don't signal ourselves
|
375
435
|
return if @signaled # don't need to signal again
|
376
436
|
|
377
437
|
@mutex.synchronize {
|
378
438
|
@signaled = true
|
439
|
+
@immediate += 1 if immediate
|
379
440
|
@cv.signal
|
380
441
|
}
|
381
442
|
self
|
382
443
|
end
|
383
444
|
|
384
|
-
# Returns
|
445
|
+
# Returns `true` if the flusher is waiting for a signal. Returns `false`
|
385
446
|
# if the flusher is somewhere in the processing loop.
|
386
|
-
#
|
387
447
|
def waiting?
|
388
448
|
@waiting
|
389
449
|
end
|
390
450
|
|
451
|
+
# Returns `true` if the flusher should immeidately write the buffer to the
|
452
|
+
# IO destination.
|
453
|
+
def immediate?
|
454
|
+
@immediate > 0
|
455
|
+
end
|
456
|
+
|
391
457
|
private
|
392
458
|
|
459
|
+
def _try_to_sleep
|
460
|
+
return if Thread.current[:stop]
|
461
|
+
return if immediate?
|
462
|
+
sleep @period unless @period.nil?
|
463
|
+
end
|
464
|
+
|
393
465
|
def _wait_for_signal
|
394
466
|
@mutex.synchronize {
|
395
467
|
begin
|
396
468
|
# wait on the condition variable only if we have NOT been signaled
|
397
469
|
unless @signaled
|
398
470
|
@waiting = true
|
471
|
+
@immediate -= 1 if immediate?
|
399
472
|
@cv.wait(@mutex)
|
400
473
|
@waiting = false
|
401
474
|
end
|
@@ -406,9 +479,7 @@ module Logging::Appenders
|
|
406
479
|
ensure
|
407
480
|
@waiting = false
|
408
481
|
end
|
409
|
-
end
|
482
|
+
end
|
410
483
|
# :startdoc:
|
411
|
-
|
412
|
-
|
413
|
-
end # Logging::Appenders
|
414
|
-
|
484
|
+
end
|
485
|
+
end
|