logging 2.2.2 → 2.3.1

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: f7bb89dcc5956ebabbf591d8f8d348e0e3cfdc7cb5fa356e9995168278b45a95
4
+ data.tar.gz: be4ad44cf88417be5abe80fbaf0b1d28a2f4899df2c00d1d4818af78cc440443
5
5
  SHA512:
6
- metadata.gz: 44ad1070f56d8618f9e6c118876364f70442723424d055716928e422a6699a40e744c68a86619227ed8fd5eea26e6c1fd6850f50877c74769a5160d6133b5140
7
- data.tar.gz: bf24fb750a15937f18d75b2acc738e323a8ea718661bb9877ea64b0d23eea12def864c3175e71cd1f9b7ccff69c44d661455ff3526e5c192cc0f67c026a77a7f
6
+ metadata.gz: b61a6a3bcda67484ec4334a89adfd6149d2aa91be51ba0ca476514c7c4532d0b4040cc202ce58d30e9d93ba5821e6eedbde77d8bdeaac7a82224731796d79eda
7
+ data.tar.gz: 55c888af479f8c01eb89f96eda24b2cc9cd92f52cc0b1f21d5fde7fcb7e91e573b6007998c67b00781cf8c5ea3eadcb9462b9df2da26dbcfe0464deb13340d18
data/.gitignore CHANGED
@@ -12,3 +12,4 @@ tmp/
12
12
  vendor/
13
13
  .rbx
14
14
  .rvmrc
15
+ .tool-versions
data/.travis.yml CHANGED
@@ -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
data/History.txt CHANGED
@@ -1,3 +1,24 @@
1
+ == 2.3.1 / 2022-05-24
2
+
3
+ Bug Fixes
4
+ - logging hangs on JRuby when the stdout appender is closed [PR #237]
5
+ - initialize the Logging framework when a Filter is created [PR #238]
6
+
7
+ == 2.3.0 / 2020-07-04
8
+
9
+ Enhancements
10
+ - all appender output is now synchronized [PR #219]
11
+ - renamed the `LogEvent#method` to no longer conflict with `Kernel#method` [PR #218]
12
+ - @bhuga (not the Fortnite star) added a `raise_errors` method for debugging [PR #203]
13
+ - thanks to @olleolleolle for keeping on top of Travis and Ruby versions
14
+
15
+ Bug Fixes
16
+ - conosle appenders can be reopened [PR #220]
17
+ - fixed a race condition in the rolling file appender [PR #216]
18
+ - fixed a race condition when opening log file destinations [PR #208 #217]
19
+ - @MikaelSmith fixed a race condition in Logger creation [PR #201]
20
+ - documentation bug fixes [PR #184 #185 #188 #194 #209]
21
+
1
22
  == 2.2.2 / 2017-04-11
2
23
 
3
24
  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
data/examples/mdc.rb CHANGED
@@ -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.},
@@ -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,39 @@ 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
+ flush if defined? @io && @io
41
+ @io = open_fd
42
+ }
43
+ super
44
+ self
45
+ end
46
+
47
+ private
48
+
49
+ def open_fd
50
+ case self.class.name
51
+ when "Logging::Appenders::Stdout"; STDOUT
52
+ when "Logging::Appenders::Stderr"; STDERR
53
+ else
54
+ raise RuntimeError, "Please do not use the `Logging::Appenders::Console` class directly - " +
55
+ "use `Logging::Appenders::Stdout` and `Logging::Appenders::Stderr` instead" +
56
+ " [class #{self.class.name}]"
57
+ end
36
58
  end
37
59
  end
38
60
 
@@ -43,7 +65,6 @@ module Logging::Appenders
43
65
  Stderr = Class.new(Console)
44
66
 
45
67
  # Accessor / Factory for the Stdout appender.
46
- #
47
68
  def self.stdout( *args )
48
69
  if args.empty?
49
70
  return self['stdout'] || ::Logging::Appenders::Stdout.new
@@ -52,7 +73,6 @@ module Logging::Appenders
52
73
  end
53
74
 
54
75
  # Accessor / Factory for the Stderr appender.
55
- #
56
76
  def self.stderr( *args )
57
77
  if args.empty?
58
78
  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,13 +43,12 @@ 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
54
49
 
55
50
  io, @io = @io, nil
56
- unless [STDIN, STDERR, STDOUT].include?(io)
51
+ if ![STDIN, STDERR, STDOUT].include?(io)
57
52
  io.send(@close_method) if @close_method && io.respond_to?(@close_method)
58
53
  end
59
54
  rescue IOError
@@ -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
@@ -84,24 +84,32 @@ module Logging::Appenders
84
84
  # 'date'.
85
85
  #
86
86
  def initialize( name, opts = {} )
87
- @roller = Roller.new name, opts
87
+ @roller = Roller.new(
88
+ opts.fetch(:filename, name),
89
+ age: opts.fetch(:age, nil),
90
+ size: opts.fetch(:size, nil),
91
+ roll_by: opts.fetch(:roll_by, nil),
92
+ keep: opts.fetch(:keep, nil)
93
+ )
88
94
 
89
95
  # grab our options
90
96
  @size = opts.fetch(:size, nil)
91
97
  @size = Integer(@size) unless @size.nil?
92
98
 
93
- @age_fn = filename + '.age'
99
+ @age_fn = self.filename + '.age'
94
100
  @age_fn_mtime = nil
95
101
  @age = opts.fetch(:age, nil)
96
102
 
97
103
  # create our `sufficiently_aged?` method
98
104
  build_singleton_methods
99
- FileUtils.touch(@age_fn) if @age && !test(?f, @age_fn)
105
+ FileUtils.touch(@age_fn) if @age && !::File.file?(@age_fn)
100
106
 
101
107
  # we are opening the file in read/write mode so that a shared lock can
102
108
  # be used on the file descriptor => http://pubs.opengroup.org/onlinepubs/009695399/functions/fcntl.html
103
- @mode = encoding ? "a+:#{encoding}" : 'a+'
104
- super(name, ::File.new(filename, @mode), opts)
109
+ self.encoding = opts.fetch(:encoding, self.encoding)
110
+
111
+ io = open_file
112
+ super(name, io, opts)
105
113
 
106
114
  # if the truncate flag was set to true, then roll
107
115
  roll_now = opts.fetch(:truncate, false)
@@ -121,11 +129,11 @@ module Logging::Appenders
121
129
  # is currently open then it will be closed and immediately opened.
122
130
  def reopen
123
131
  @mutex.synchronize {
124
- if defined?(@io) && @io
132
+ if defined? @io && @io
125
133
  flush
126
134
  @io.close rescue nil
127
135
  end
128
- @io = ::File.new(filename, @mode)
136
+ @io = open_file
129
137
  }
130
138
  super
131
139
  self
@@ -134,6 +142,20 @@ module Logging::Appenders
134
142
 
135
143
  private
136
144
 
145
+ def open_file
146
+ mode = ::File::RDWR | ::File::APPEND
147
+ ::File.open(filename, mode: mode, external_encoding: encoding)
148
+ rescue Errno::ENOENT
149
+ create_file
150
+ end
151
+
152
+ def create_file
153
+ mode = ::File::RDWR | ::File::APPEND | ::File::CREAT | ::File::EXCL
154
+ ::File.open(filename, mode: mode, external_encoding: encoding)
155
+ rescue Errno::EEXIST
156
+ open_file
157
+ end
158
+
137
159
  # Returns the file name to use as the temporary copy location. We are
138
160
  # using copy-and-truncate semantics for rolling files so that the IO
139
161
  # file descriptor remains valid during rolling.
@@ -157,14 +179,18 @@ module Logging::Appenders
157
179
  return self if @io.nil?
158
180
 
159
181
  str = str.force_encoding(encoding) if encoding && str.encoding != encoding
160
- @io.flock_sh { @io.write str }
182
+ @mutex.synchronize {
183
+ @io.flock_sh { @io.write str }
184
+ }
161
185
 
162
186
  if roll_required?
163
- @io.flock? {
164
- @age_fn_mtime = nil
165
- copy_truncate if roll_required?
187
+ @mutex.synchronize {
188
+ @io.flock? {
189
+ @age_fn_mtime = nil
190
+ copy_truncate if roll_required?
191
+ }
192
+ @roller.roll_files
166
193
  }
167
- @roller.roll_files
168
194
  end
169
195
  self
170
196
  rescue StandardError => err
@@ -193,7 +219,7 @@ module Logging::Appenders
193
219
  def copy_truncate
194
220
  return unless ::File.exist?(filename)
195
221
  FileUtils.concat filename, copy_file
196
- @io.truncate 0
222
+ @io.truncate(0)
197
223
 
198
224
  # touch the age file if needed
199
225
  if @age
@@ -255,22 +281,22 @@ module Logging::Appenders
255
281
  # Create a new roller. See the RollingFile#initialize documentation for
256
282
  # the list of options.
257
283
  #
258
- # name - The appender name as a String
259
- # opts - The options Hash
284
+ # filename - the name of the file to roll
285
+ # age - the age of the file at which it should be rolled
286
+ # size - the size of the file in bytes at which it should be rolled
287
+ # roll_by - roll either by 'number' or 'date'
288
+ # keep - the number of log files to keep when rolling
260
289
  #
261
- def initialize( name, opts )
290
+ def initialize( filename, age: nil, size: nil, roll_by: nil, keep: nil )
262
291
  # raise an error if a filename was not given
263
- @fn = opts.fetch(:filename, name)
292
+ @fn = filename
264
293
  raise ArgumentError, 'no filename was given' if @fn.nil?
265
294
 
266
295
  if (m = RGXP.match @fn)
267
296
  @roll_by = ("#{m[2]}%d" == m[1]) ? :number : :date
268
297
  else
269
- age = opts.fetch(:age, nil)
270
- size = opts.fetch(:size, nil)
271
-
272
298
  @roll_by =
273
- case opts.fetch(:roll_by, nil)
299
+ case roll_by
274
300
  when 'number'; :number
275
301
  when 'date'; :date
276
302
  else
@@ -293,8 +319,7 @@ module Logging::Appenders
293
319
  ::Logging::Appenders::File.assert_valid_logfile(filename)
294
320
 
295
321
  @roll = false
296
- @keep = opts.fetch(:keep, nil)
297
- @keep = Integer(keep) unless keep.nil?
322
+ @keep = keep.nil? ? nil : Integer(keep)
298
323
  end
299
324
 
300
325
  attr_reader :keep, :roll_by
@@ -347,7 +372,6 @@ module Logging::Appenders
347
372
  files.delete copy_file
348
373
 
349
374
  self.send "roll_by_#{roll_by}", files
350
-
351
375
  nil
352
376
  ensure
353
377
  self.roll = false
@@ -62,7 +62,7 @@ module Logging::Appenders
62
62
  %w[read readline readlines].each do|m|
63
63
  class_eval <<-CODE, __FILE__, __LINE__+1
64
64
  def #{m}( *args )
65
- sync {
65
+ @mutex.synchronize {
66
66
  begin
67
67
  @sio.seek @pos
68
68
  rv = @sio.#{m}(*args)
@@ -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, "unkonwn level '#{level}'"
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
-
@@ -8,10 +8,17 @@ module Logging
8
8
  # Otherwise the `allow` method should return `nil`.
9
9
  class Filter
10
10
 
11
- # Returns the event if it should be allowed into the log. Returns `nil` if
12
- # the event should _not_ be allowed into the log. Subclasses should override
13
- # this method and provide their own filtering semantics.
14
- def allow( event )
11
+ # Creates a new level filter that will pass all log events. Create a
12
+ # subclass and override the `allow` method to filter log events.
13
+ def initialize
14
+ ::Logging.init unless ::Logging.initialized?
15
+ end
16
+
17
+ # Returns the event if it should be forwarded to the logging appender.
18
+ # Returns `nil` if the event should _not_ be forwarded to the logging
19
+ # appender. Subclasses should override this method and provide their own
20
+ # filtering semantics.
21
+ def allow(event)
15
22
  event
16
23
  end
17
24
  end
@@ -4,8 +4,7 @@ module Logging
4
4
  module Filters
5
5
 
6
6
  # The `Level` filter class provides a simple level-based filtering mechanism
7
- # that filters messages to only include those from an enumerated list of
8
- # levels to log.
7
+ # that allows events whose log level matches a preconfigured list of values.
9
8
  class Level < ::Logging::Filter
10
9
 
11
10
  # Creates a new level filter that will only allow the given _levels_ to
@@ -15,15 +14,19 @@ module Logging
15
14
  # Examples
16
15
  # Logging::Filters::Level.new(:debug, :info)
17
16
  #
18
- def initialize( *levels )
19
- levels = levels.map { |level| ::Logging::level_num(level) }
20
- @levels = Set.new levels
17
+ def initialize(*levels)
18
+ super()
19
+ levels = levels.flatten.map {|level| ::Logging::level_num(level)}
20
+ @levels = Set.new(levels)
21
21
  end
22
22
 
23
- def allow( event )
23
+ # Returns the event if it should be forwarded to the logging appender.
24
+ # Otherwise, `nil` is returned. The log event is allowed if the
25
+ # `event.level` matches one of the levels provided to the filter when it
26
+ # was constructred.
27
+ def allow(event)
24
28
  @levels.include?(event.level) ? event : nil
25
29
  end
26
-
27
30
  end
28
31
  end
29
32
  end