logging 2.2.2 → 2.3.1

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
- 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