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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2d4abf77abb948de12d0e860a18be03b9b2314bd
4
- data.tar.gz: f3bb7dc5daf79b192d56de6d2a96d217bebbe24b
3
+ metadata.gz: e350554eb562c0d216fa262c015afb4c2a68b82e
4
+ data.tar.gz: 22820d111096e8c44e7d0908e1cb7cca27d49009
5
5
  SHA512:
6
- metadata.gz: 31e6820636ea493830b4b880c112aa57a53fc4e283da0682b5dfbdb373d3040017889153e50f625525eb6e0e7e4d7e2752fd6fd60c8acbc909d249762b2fd879
7
- data.tar.gz: c55a5532bcc8e6c8543f231484a11cb9f65a747afe231f58709fc13274962e6318a05a3e76d09b12c1793bc346cd87eca759e0eefa2185af1ea38d2caf554ab9
6
+ metadata.gz: d8167665ad0871c41f053154f7d36686afd08d15d151a3d8c759a20ab9d845060494ed580fe6c95eeddccea69c57a6d94d464f6f3c463ed126b61c8021d05ac9
7
+ data.tar.gz: e741f1bea9ca11f52a06ca5b2873007430f26b02d92262222b2e64a86ae0de9635c2482b57e40c0b512d6a180486d665b9e36196a39d418311437a855e0d5a7c
@@ -10,4 +10,5 @@ rvm:
10
10
  - 2.0.0
11
11
  - 2.1.5
12
12
  - 2.2.1
13
-
13
+ - 2.3.0
14
+ - jruby-9.0.4.0
@@ -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 'flexmock', '~> 1.0', :development => true
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
  }
@@ -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:
@@ -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
- # inspect => string
258
+ # to_s => string
256
259
  #
257
260
  # Returns a string representation of the appender.
258
261
  #
259
- def inspect
260
- "<%s:0x%x name=\"%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
- @flush_period = @periodic_flusher = nil
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 a
48
- # periodic flusher thread is running, shut it down and allow it to exit.
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 @periodic_flusher
54
- @periodic_flusher.stop
55
- @periodic_flusher = nil
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 periodic flushing, then the flushing
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
- _setup_periodic_flusher
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
- period =
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
- num = ArgumentError.new("unrecognized flush period: #{period.inspect}") if num.nil?
207
+ raise ArgumentError.new("unrecognized flush period: #{period.inspect}") if num.nil?
203
208
  num
204
- else ArgumentError.new("unrecognized flush period: #{period.inspect}") end
209
+ else
210
+ raise ArgumentError.new("unrecognized flush period: #{period.inspect}")
211
+ end
205
212
 
206
- raise period if Exception === period
207
- @flush_period = period
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
- _setup_periodic_flusher
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 _event_ level matches one of the configured
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 _event_ to the logging destination. The _event_ can
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 _event_ will be formatted and then buffered until 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
- @periodic_flusher.signal if @periodic_flusher
264
- flush if @buffer.length >= @auto_flushing || immediate?(event)
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 +nil+ is returned.
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 PeriodicFlusher attached to this
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
- # PeriodicFlusher already exists, it will be stopped and a new one will be
343
+ # AsyncFlusher already exists, it will be stopped and a new one will be
297
344
  # created.
298
345
  #
299
- def _setup_periodic_flusher
300
- # stop and remove any existing periodic flusher instance
301
- if @periodic_flusher
302
- @periodic_flusher.stop
303
- @periodic_flusher = nil
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 periodic flusher if we have a valid flush period
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
- @periodic_flusher = PeriodicFlusher.new(self, @flush_period)
311
- @periodic_flusher.start
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 PeriodicFlusher contains an internal run loop that will periodically
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 PeriodicFlusher relies on a _signal_ from
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 PeriodicFlusher instance that will call the +flush+
325
- # method on the given _appender_. The +flush+ method will be called
326
- # every _period_ seconds, but only when the message buffer is non-empty.
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
- sleep @period unless Thread.current[:stop]
403
+ _try_to_sleep
349
404
  @appender.flush
350
405
  rescue => err
351
- ::Logging.log_internal {"PeriodicFlusher for appender #{@appender.inspect} encountered an error"}
406
+ ::Logging.log_internal {"AsyncFlusher for appender #{@appender.inspect} encountered an error"}
352
407
  ::Logging.log_internal_error(err)
353
408
  end
354
- }; @thread = nil }
409
+ }}
355
410
 
356
411
  self
357
412
  end
358
413
 
359
- # Stop the periodic flusher's internal run loop.
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 periodic flusher. This will wake up the run loop if it is
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 periodic flusher will never perform the flush action on
427
+ # called, the async flusher will never perform the flush action on
371
428
  # the appender.
372
429
  #
373
- def signal
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 +true+ if the flusher is waiting for a signal. Returns +false+
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 # class PeriodicFlusher
482
+ end
410
483
  # :startdoc:
411
-
412
- end # Buffering
413
- end # Logging::Appenders
414
-
484
+ end
485
+ end