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