logging 2.2.2 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: bb433e2db44c13bc4956095561ad2ad34cd4fabf
4
- data.tar.gz: 6d6a401267834a2b1dad3c5992bb4c4c28ea05a0
2
+ SHA256:
3
+ metadata.gz: 1ec479cf977c621a6b42426cfe7c478a97e106c23762f3608ee67fcbaa49b324
4
+ data.tar.gz: 6a0c0107cf3c1a1f7504e19e85a9505ad94c4a84991d01eb6c65191257a01163
5
5
  SHA512:
6
- metadata.gz: 44ad1070f56d8618f9e6c118876364f70442723424d055716928e422a6699a40e744c68a86619227ed8fd5eea26e6c1fd6850f50877c74769a5160d6133b5140
7
- data.tar.gz: bf24fb750a15937f18d75b2acc738e323a8ea718661bb9877ea64b0d23eea12def864c3175e71cd1f9b7ccff69c44d661455ff3526e5c192cc0f67c026a77a7f
6
+ metadata.gz: 2cd72dabcd8b878b73f69ec8a445678b14635ca1c115576ef148a7160673f83939d7706e5d673c32ed11ef2c48046cc7a50bd2c2c0795b2fa79c35e9d4bccf87
7
+ data.tar.gz: 23422872b58d1bd0e0a146f64e4bb792ef5208da309b16e7650d5c74ca9969b12e6e65dd5ef79a90ce39e0d6c51dbc7514f7a2e913ec74b2e8404a06aefa8975
data/.gitignore CHANGED
@@ -12,3 +12,4 @@ tmp/
12
12
  vendor/
13
13
  .rbx
14
14
  .rvmrc
15
+ .tool-versions
@@ -9,6 +9,8 @@ rvm:
9
9
  - 2.0.0-p648
10
10
  - 2.1.10
11
11
  - 2.2.7
12
- - 2.3.4
13
- - 2.4.1
14
- - jruby-9.1.8.0
12
+ - 2.3.8
13
+ - 2.4.9
14
+ - 2.5.7
15
+ - 2.6.5
16
+ - jruby-9.2.9.0
@@ -1,3 +1,18 @@
1
+ == 2.3.0 / 2020-07-04
2
+
3
+ Enhancements
4
+ - all appender output is now synchronized [PR #219]
5
+ - renamed the `LogEvent#method` to no longer conflict with `Kernel#method` [PR #218]
6
+ - @bhuga (not the Fortnite star) added a `raise_errors` method for debugging [PR #203]
7
+ - thanks to @olleolleolle for keeping on top of Travis and Ruby versions
8
+
9
+ Bug Fixes
10
+ - conosle appenders can be reopened [PR #220]
11
+ - fixed a race condition in the rolling file appender [PR #216]
12
+ - fixed a race condition when opening log file destinations [PR #208 #217]
13
+ - @MikaelSmith fixed a race condition in Logger creation [PR #201]
14
+ - documentation bug fixes [PR #184 #185 #188 #194 #209]
15
+
1
16
  == 2.2.2 / 2017-04-11
2
17
 
3
18
  Enhancements
data/Rakefile CHANGED
@@ -26,9 +26,9 @@ Bones {
26
26
  use_gmail
27
27
 
28
28
  depend_on 'little-plugger', '~> 1.1'
29
- depend_on 'multi_json', '~> 1.10'
29
+ depend_on 'multi_json', '~> 1.14'
30
30
 
31
- depend_on 'test-unit', '~> 3.1', :development => true
31
+ depend_on 'test-unit', '~> 3.3', :development => true
32
32
  depend_on 'bones-git', '~> 1.3', :development => true
33
33
  #depend_on 'bones-rcov', :development => true
34
34
  }
@@ -1,7 +1,7 @@
1
1
  # :stopdoc:
2
2
  #
3
3
  # Appenders are used to output log events to some logging destination. The
4
- # same log event can be sent to multiple desitnations by associating
4
+ # same log event can be sent to multiple destinations by associating
5
5
  # multiple appenders with the logger.
6
6
  #
7
7
  # The following is a list of all the available appenders and a brief
@@ -29,9 +29,9 @@
29
29
  Logging.mdc['first'] = 'John'
30
30
  Logging.mdc['last'] = 'Doe'
31
31
 
32
- # in this first thread we will log some quotes by Allan Rickman
32
+ # in this first thread we will log some quotes by Alan Rickman
33
33
  t1 = Thread.new {
34
- Logging.mdc['first'] = 'Allan'
34
+ Logging.mdc['first'] = 'Alan'
35
35
  Logging.mdc['last'] = 'Rickman'
36
36
 
37
37
  [ %q{I've never been able to plan my life. I just lurch from indecision to indecision.},
@@ -258,6 +258,7 @@ module Logging
258
258
  module_eval "MAX_LEVEL_LENGTH = #{longest.length}", __FILE__, __LINE__
259
259
 
260
260
  self.cause_depth = nil unless defined? @cause_depth
261
+ self.raise_errors = false unless defined? @raise_errors
261
262
 
262
263
  initialize_plugins
263
264
  levels.keys
@@ -268,8 +269,8 @@ module Logging
268
269
  #
269
270
  # Defines the default _obj_format_ method to use when converting objects
270
271
  # into string representations for logging. _obj_format_ can be one of
271
- # <tt>:string</tt>, <tt>:inspect</tt>, or <tt>:yaml</tt>. These
272
- # formatting commands map to the following object methods
272
+ # <tt>:string</tt>, <tt>:inspect</tt>, <tt>:json</tt> or <tt>:yaml</tt>.
273
+ # These formatting commands map to the following object methods
273
274
  #
274
275
  # * :string => to_s
275
276
  # * :inspect => inspect
@@ -277,7 +278,7 @@ module Logging
277
278
  # * :json => MultiJson.encode(obj)
278
279
  #
279
280
  # An +ArgumentError+ is raised if anything other than +:string+,
280
- # +:inspect+, +:yaml+ is passed to this method.
281
+ # +:inspect+, +:json+ or +:yaml+ is passed to this method.
281
282
  #
282
283
  def format_as( f )
283
284
  f = f.intern if f.instance_of? String
@@ -490,6 +491,21 @@ module Logging
490
491
  io
491
492
  end
492
493
 
494
+ # Raise an exception when an error is encountered while logging, be it with
495
+ # a backing store, formatter, or anything else. You probably wouldn't want
496
+ # to enable this outside of test.
497
+ #
498
+ # Not that only one error will ever be raised per logging backend, as
499
+ # backends that raise errors on write will be set to :off.
500
+ def raise_errors=(boolean)
501
+ @raise_errors = boolean
502
+ end
503
+
504
+ # Whether or not we should raise errors when writing logs.
505
+ def raise_errors?
506
+ @raise_errors
507
+ end
508
+
493
509
  # :stopdoc:
494
510
  # Convert the given level into a canonical form - a lowercase string.
495
511
  def levelify( level )
@@ -518,7 +534,7 @@ module Logging
518
534
  # exception will be raised again.
519
535
  def log_internal_error( err )
520
536
  log_internal(-2) { err }
521
- raise err if Thread.abort_on_exception
537
+ raise err if ::Logging.raise_errors?
522
538
  end
523
539
 
524
540
  # Close all appenders
@@ -569,8 +585,7 @@ module Logging
569
585
  require libpath('logging/diagnostic_context')
570
586
 
571
587
  require libpath('logging/rails_compat')
572
- end # module Logging
573
-
588
+ end
574
589
 
575
590
  # This finalizer will close all the appenders that exist in the system.
576
591
  # This is needed for closing IO streams and connections to the syslog server
@@ -9,11 +9,6 @@ module Logging
9
9
  # Each subclass should provide a +write+ method that will write log
10
10
  # messages to the logging destination.
11
11
  #
12
- # A private +sync+ method is provided for use by subclasses. It is used to
13
- # synchronize writes to the logging destination, and can be used by
14
- # subclasses to synchronize the closing or flushing of the logging
15
- # destination.
16
- #
17
12
  class Appender
18
13
 
19
14
  attr_reader :name, :layout, :level, :filters
@@ -263,13 +258,9 @@ class Appender
263
258
  "<%s name=\"%s\">" % [self.class.name.sub(%r/^Logging::/, ''), self.name]
264
259
  end
265
260
 
266
- # Returns the current Encoding for the appender or nil if an encoding has
267
- # not been set.
268
- #
269
- def encoding
270
- return @encoding if defined? @encoding
271
- @encoding = Object.const_defined?(:Encoding) ? Encoding.default_external : nil
272
- end
261
+ # Returns the current Encoding for the appender. The default external econding
262
+ # will be used if none is explicitly set.
263
+ attr_reader :encoding
273
264
 
274
265
  # Set the appender encoding to the given value. The value can either be an
275
266
  # Encoding instance or a String or Symbol referring to a valid encoding.
@@ -282,9 +273,9 @@ class Appender
282
273
  # Raises ArgumentError if the value is not a valid encoding.
283
274
  def encoding=( value )
284
275
  if value.nil?
285
- @encoding = nil
276
+ @encoding = Encoding.default_external
286
277
  else
287
- @encoding = Object.const_defined?(:Encoding) ? Encoding.find(value.to_s) : nil
278
+ @encoding = Encoding.find(value.to_s)
288
279
  end
289
280
  end
290
281
 
@@ -328,17 +319,6 @@ private
328
319
  nil
329
320
  end
330
321
 
331
- # call-seq:
332
- # sync { block }
333
- #
334
- # Obtains an exclusive lock, runs the block, and releases the lock when
335
- # the block completes. This method is re-entrant so that a single thread
336
- # can call +sync+ multiple times without hanging the thread.
337
- #
338
- def sync( &block )
339
- @mutex.synchronize(&block)
340
- end
341
-
342
322
  end # class Appender
343
323
  end # module Logging
344
324
 
@@ -79,7 +79,7 @@ module Logging::Appenders
79
79
  return self if @buffer.empty?
80
80
 
81
81
  ary = nil
82
- sync {
82
+ @mutex.synchronize {
83
83
  ary = @buffer.dup
84
84
  @buffer.clear
85
85
  }
@@ -100,7 +100,7 @@ module Logging::Appenders
100
100
  # Clear the underlying buffer of all log events. These events will not be
101
101
  # appended to the logging destination; they will be lost.
102
102
  def clear!
103
- sync { @buffer.clear }
103
+ @mutex.synchronize { @buffer.clear }
104
104
  end
105
105
 
106
106
  # Configure the levels that will trigger an immediate flush of the
@@ -285,7 +285,7 @@ module Logging::Appenders
285
285
  canonical_write(str)
286
286
  else
287
287
  str = str.force_encoding(encoding) if encoding && str.encoding != encoding
288
- sync {
288
+ @mutex.synchronize {
289
289
  @buffer << str
290
290
  }
291
291
  flush_now = @buffer.length >= @auto_flushing || immediate?(event)
@@ -22,17 +22,49 @@ module Logging::Appenders
22
22
  #
23
23
  def initialize( *args )
24
24
  name = self.class.name.split("::").last.downcase
25
- io = Object.const_get(name.upcase)
26
25
 
27
26
  opts = args.last.is_a?(Hash) ? args.pop : {}
28
27
  name = args.shift unless args.empty?
29
28
 
30
- opts[:encoding] = io.external_encoding if io.respond_to? :external_encoding
29
+ io = open_fd
30
+ opts[:encoding] = io.external_encoding
31
31
 
32
32
  super(name, io, opts)
33
- rescue NameError
34
- raise RuntimeError, "Please do not use the `Logging::Appenders::Console` class directly - " +
35
- "use `Logging::Appenders::Stdout` and `Logging::Appenders::Stderr` instead"
33
+ end
34
+
35
+ # Reopen the connection to the underlying logging destination. If the
36
+ # connection is currently closed then it will be opened. If the connection
37
+ # is currently open then it will be closed and immediately reopened.
38
+ def reopen
39
+ @mutex.synchronize {
40
+ if defined? @io && @io
41
+ flush
42
+ @io.close rescue nil
43
+ end
44
+ @io = open_fd
45
+ }
46
+ super
47
+ self
48
+ end
49
+
50
+ private
51
+
52
+ def open_fd
53
+ case self.class.name
54
+ when "Logging::Appenders::Stdout"
55
+ fd = STDOUT.fileno
56
+ encoding = STDOUT.external_encoding
57
+ when "Logging::Appenders::Stderr"
58
+ fd = STDERR.fileno
59
+ encoding = STDERR.external_encoding
60
+ else
61
+ raise RuntimeError, "Please do not use the `Logging::Appenders::Console` class directly - " +
62
+ "use `Logging::Appenders::Stdout` and `Logging::Appenders::Stderr` instead" +
63
+ " [class #{self.class.name}]"
64
+ end
65
+
66
+ mode = ::File::WRONLY | ::File::APPEND
67
+ ::IO.for_fd(fd, mode: mode, encoding: encoding)
36
68
  end
37
69
  end
38
70
 
@@ -43,7 +75,6 @@ module Logging::Appenders
43
75
  Stderr = Class.new(Console)
44
76
 
45
77
  # Accessor / Factory for the Stdout appender.
46
- #
47
78
  def self.stdout( *args )
48
79
  if args.empty?
49
80
  return self['stdout'] || ::Logging::Appenders::Stdout.new
@@ -52,7 +83,6 @@ module Logging::Appenders
52
83
  end
53
84
 
54
85
  # Accessor / Factory for the Stderr appender.
55
- #
56
86
  def self.stderr( *args )
57
87
  if args.empty?
58
88
  return self['stderr'] || ::Logging::Appenders::Stderr.new
@@ -2,14 +2,12 @@
2
2
  module Logging::Appenders
3
3
 
4
4
  # Accessor / Factory for the File appender.
5
- #
6
5
  def self.file( *args )
7
6
  fail ArgumentError, '::Logging::Appenders::File needs a name as first argument.' if args.empty?
8
7
  ::Logging::Appenders::File.new(*args)
9
8
  end
10
9
 
11
10
  # This class provides an Appender that can write to a File.
12
- #
13
11
  class File < ::Logging::Appenders::IO
14
12
 
15
13
  # call-seq:
@@ -21,15 +19,14 @@ module Logging::Appenders
21
19
  # writable.
22
20
  #
23
21
  # An +ArgumentError+ is raised if any of these assertions fail.
24
- #
25
22
  def self.assert_valid_logfile( fn )
26
23
  if ::File.exist?(fn)
27
- if not ::File.file?(fn)
24
+ if !::File.file?(fn)
28
25
  raise ArgumentError, "#{fn} is not a regular file"
29
- elsif not ::File.writable?(fn)
26
+ elsif !::File.writable?(fn)
30
27
  raise ArgumentError, "#{fn} is not writeable"
31
28
  end
32
- elsif not ::File.writable?(::File.dirname(fn))
29
+ elsif !::File.writable?(::File.dirname(fn))
33
30
  raise ArgumentError, "#{::File.dirname(fn)} is not writable"
34
31
  end
35
32
  true
@@ -45,41 +42,65 @@ module Logging::Appenders
45
42
  # created. If the :truncate option is set to +true+ then the file will
46
43
  # be truncated before writing begins; otherwise, log messages will be
47
44
  # appended to the file.
48
- #
49
45
  def initialize( name, opts = {} )
50
- @fn = opts.fetch(:filename, name)
51
- raise ArgumentError, 'no filename was given' if @fn.nil?
46
+ @filename = opts.fetch(:filename, name)
47
+ raise ArgumentError, 'no filename was given' if @filename.nil?
52
48
 
53
- @fn = ::File.expand_path(@fn)
54
- self.class.assert_valid_logfile(@fn)
55
- @mode = opts.fetch(:truncate, false) ? 'w' : 'a'
49
+ @filename = ::File.expand_path(@filename).freeze
50
+ self.class.assert_valid_logfile(@filename)
56
51
 
57
52
  self.encoding = opts.fetch(:encoding, self.encoding)
58
- @mode = "#{@mode}:#{self.encoding}" if self.encoding
59
53
 
60
- super(name, ::File.new(@fn, @mode), opts)
54
+ io = open_file
55
+ super(name, io, opts)
56
+
57
+ truncate if opts.fetch(:truncate, false)
61
58
  end
62
59
 
63
60
  # Returns the path to the logfile.
64
- #
65
- def filename() @fn.dup end
61
+ attr_reader :filename
66
62
 
67
63
  # Reopen the connection to the underlying logging destination. If the
68
64
  # connection is currently closed then it will be opened. If the connection
69
65
  # is currently open then it will be closed and immediately opened.
70
- #
71
66
  def reopen
72
67
  @mutex.synchronize {
73
- if defined? @io and @io
68
+ if defined? @io && @io
74
69
  flush
75
70
  @io.close rescue nil
76
71
  end
77
- @io = ::File.new(@fn, @mode)
72
+ @io = open_file
78
73
  }
79
74
  super
80
75
  self
81
76
  end
82
77
 
83
- end # FileAppender
84
- end # Logging::Appenders
85
78
 
79
+ protected
80
+
81
+ def truncate
82
+ @mutex.synchronize {
83
+ begin
84
+ @io.flock(::File::LOCK_EX)
85
+ @io.truncate(0)
86
+ ensure
87
+ @io.flock(::File::LOCK_UN)
88
+ end
89
+ }
90
+ end
91
+
92
+ def open_file
93
+ mode = ::File::WRONLY | ::File::APPEND
94
+ ::File.open(filename, mode: mode, external_encoding: encoding)
95
+ rescue Errno::ENOENT
96
+ create_file
97
+ end
98
+
99
+ def create_file
100
+ mode = ::File::WRONLY | ::File::APPEND | ::File::CREAT | ::File::EXCL
101
+ ::File.open(filename, mode: mode, external_encoding: encoding)
102
+ rescue Errno::EEXIST
103
+ open_file
104
+ end
105
+ end
106
+ end
@@ -2,7 +2,6 @@
2
2
  module Logging::Appenders
3
3
 
4
4
  # Accessor / Factory for the IO appender.
5
- #
6
5
  def self.io( *args )
7
6
  return ::Logging::Appenders::IO if args.empty?
8
7
  ::Logging::Appenders::IO.new(*args)
@@ -10,14 +9,12 @@ module Logging::Appenders
10
9
 
11
10
  # This class provides an Appender that can write to any IO stream
12
11
  # configured for writing.
13
- #
14
12
  class IO < ::Logging::Appender
15
13
  include Buffering
16
14
 
17
15
  # The method that will be used to close the IO stream. Defaults to :close
18
16
  # but can be :close_read, :close_write or nil. When nil, the IO stream
19
17
  # will not be closed when the appender's close method is called.
20
- #
21
18
  attr_accessor :close_method
22
19
 
23
20
  # call-seq:
@@ -26,7 +23,6 @@ module Logging::Appenders
26
23
  #
27
24
  # Creates a new IO Appender using the given name that will use the _io_
28
25
  # stream as the logging destination.
29
- #
30
26
  def initialize( name, io, opts = {} )
31
27
  unless io.respond_to? :write
32
28
  raise TypeError, "expecting an IO object but got '#{io.class.name}'"
@@ -47,7 +43,6 @@ module Logging::Appenders
47
43
  # destination if the _footer_ flag is set to +true+. Log events will
48
44
  # no longer be written to the logging destination after the appender
49
45
  # is closed.
50
- #
51
46
  def close( *args )
52
47
  return self if @io.nil?
53
48
  super
@@ -61,16 +56,25 @@ module Logging::Appenders
61
56
  return self
62
57
  end
63
58
 
59
+ # Reopen the connection to the underlying logging destination. If the
60
+ # connection is currently closed then it will be opened. If the connection
61
+ # is currently open then it will be closed and immediately opened. If
62
+ # supported, the IO will have its sync mode set to `true` so that all writes
63
+ # are immediately flushed to the underlying operating system.
64
+ def reopen
65
+ super
66
+ @io.sync = true if @io.respond_to? :sync=
67
+ self
68
+ end
64
69
 
65
70
  private
66
71
 
67
72
  # This method is called by the buffering code when messages need to be
68
73
  # written to the logging destination.
69
- #
70
74
  def canonical_write( str )
71
75
  return self if @io.nil?
72
76
  str = str.force_encoding(encoding) if encoding && str.encoding != encoding
73
- @io.write str
77
+ @mutex.synchronize { @io.write str }
74
78
  self
75
79
  rescue StandardError => err
76
80
  handle_internal_error(err)
@@ -82,6 +86,5 @@ module Logging::Appenders
82
86
  ::Logging.log_internal {"appender #{name.inspect} has been disabled"}
83
87
  ::Logging.log_internal_error(err)
84
88
  end
85
-
86
- end # IO
87
- end # Logging::Appenders
89
+ end
90
+ end