threadedlogger 1.1.0 → 1.2.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.
data/History.txt CHANGED
@@ -1,5 +1,10 @@
1
1
  # History for ThreadedLogger
2
2
 
3
+ ## v1.2.0
4
+
5
+ * Allow multiple loggers to be instantiated as subclasses
6
+ * add ::clear and ::clear_all class methods
7
+
3
8
  ## v1.1.0
4
9
 
5
10
  * Dynamically generate logging methods
data/Manifest.txt CHANGED
@@ -4,8 +4,13 @@ README.txt
4
4
  LICENSE.txt
5
5
  Rakefile
6
6
  lib/threadedlogger.rb
7
+ lib/threadedlogger/core.rb
8
+ lib/threadedlogger/version.rb
9
+ lib/threadedlogger/logger.rb
7
10
  test/minitest_helper.rb
8
11
  test/test_logging.rb
9
12
  test/test_methods.rb
10
13
  test/test_threadedlogger.rb
14
+ test/test_shutdown.rb
15
+ test/test_clear.rb
11
16
  threadedlogger.gemspec
data/README.txt CHANGED
@@ -13,24 +13,46 @@ to ensure that multiple threads don't step on each other's toes.
13
13
 
14
14
  ## SYNOPSIS:
15
15
 
16
- ```ruby
17
- require 'threadedlogger'
18
-
19
- log = ThreadedLogger.instance(logfname, 'daily', 'debug')
20
- log.info('START')
21
- ...
22
- log.debug('super important stuff')
23
- ...
24
- log.info("STOP")
25
- log.shutdown
26
-
27
- log2 = ThreadedLogger.instance(logfname, 'daily', 'debug', proc { |l| "prefix: #{l}" })
28
- ```
29
-
30
- ThreadedLogger has one and only one instance, accessed via the ::instance
16
+ require 'threadedlogger'
17
+
18
+ log = ThreadedLogger.instance(logfname, 'daily', 'debug')
19
+ log.info('START')
20
+ ...
21
+ log.debug('super important stuff')
22
+ ...
23
+ log.info("STOP")
24
+ log.shutdown
25
+
26
+ or if you have multiple loggers
27
+
28
+ require 'threadedlogger'
29
+
30
+ class Log1 < ThreadedLogger
31
+ end
32
+
33
+ class Log2 < ThreadedLogger
34
+ end
35
+
36
+ log1 = Log1.instance(logfname1, 'daily', 'debug')
37
+ log2 = Log2.instance(logfname2, 'daily', 'debug')
38
+ log1.info('START')
39
+ ...
40
+ log1.debug('super important stuff')
41
+ log2.info('something that only goes to the second log')
42
+ ...
43
+ log1.info("STOP")
44
+ log2.shutdown
45
+ log1.shutdown
46
+
47
+ ThreadedLogger can be subclassed if you need to have multiple logs in a
48
+ program. Each subclass has one logger instance, accessed via the ::instance
31
49
  class method. You can only provide arguments the first time - trying to
32
50
  're-construct' the object will throw an ArgumentError.
33
51
 
52
+ If you only need one logger, you can just use ThreadedLogger without
53
+ subclassing it. Instances are stored keyed on class name, and the base
54
+ class is a valid key.
55
+
34
56
  Under the covers, the dedicated thread uses the standard Ruby Logger
35
57
  library. Refer to [Logger](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/logger/rdoc/Logger.html)
36
58
  for further detail.
@@ -46,9 +68,21 @@ instance method before your program exits. On a clean shutdown this should
46
68
  happen automatically, but if you exit in a funky way it might not capture
47
69
  the last message.
48
70
 
71
+ The catalog of instances can be cleared using the ::clear and ::clear_all
72
+ class methods. Each takes an optional boolean argument indicating whether
73
+ ::shutdown should be called on any active loggers before clearing them.
74
+
49
75
  This library also overrides Logger.LogDevice.add_log_header to prevent it
50
76
  from putting a header line at the top of a logfile when it is first opened.
51
77
 
78
+ ## CONSTRUCTION THREAD SAFETY
79
+
80
+ ThreadedLogger does not mutex construction, as the typical use case is for
81
+ the logger to be initialized for the first time outside of threaded code.
82
+ If you call the constructor for the first time from threaded code, you will
83
+ need to protect ::instance with some kind of synchronization to avoid a race
84
+ condition.
85
+
52
86
  ## LICENSE:
53
87
 
54
88
  The MIT License (MIT)
data/Rakefile CHANGED
@@ -14,5 +14,5 @@ Rake::TestTask.new("unit_tests") { |t|
14
14
  t.libs.push 'test'
15
15
  t.pattern = 'test/test_*.rb'
16
16
  t.verbose = true
17
- t.warning = true
17
+ t.warning = false
18
18
  }
@@ -1,133 +1,3 @@
1
- require 'thread'
2
- require 'logger'
3
-
4
- class ThreadedLogger
5
-
6
- VERSION = '1.1.0'
7
-
8
- @@instance = nil
9
-
10
- LOGLEVELS = {
11
- 'debug' => Logger::DEBUG,
12
- 'info' => Logger::INFO,
13
- 'warn' => Logger::WARN,
14
- 'error' => Logger::ERROR,
15
- 'fatal' => Logger::FATAL,
16
- }
17
-
18
- private_class_method :new
19
-
20
- # create the logging methods
21
- LOGLEVELS.each do |k,v|
22
- log_method = k.to_sym
23
- test_method = "#{k}?".to_sym
24
- define_method(log_method) { |msg=nil|
25
- enqueue(v, msg)
26
- }
27
- define_method(test_method) {
28
- @log.send(test_method)
29
- }
30
- end
31
-
32
- def ThreadedLogger.instance(*args)
33
-
34
- if @@instance.nil?
35
- @@instance = new(*args)
36
- else
37
- if ! args.empty?
38
- raise ArgumentError, "instance already constructed"
39
- end
40
- end
41
-
42
- return @@instance
43
-
44
- end
45
-
46
- def initialize(file, rotation = 'daily', level = 'info', formatter = nil)
47
-
48
- if file.nil?
49
- raise ArgumentError, "log file name is required"
50
- end
51
-
52
- # create a logger
53
- @log = Logger.new(file, rotation)
54
-
55
- # set the min threshold
56
- send(:level=, level)
57
-
58
- # apply a formatter if one was given
59
- if ! formatter.nil?
60
- @log.formatter = formatter
61
- end
62
-
63
- # set up a queue and spawn a thread to do the logging
64
- @queue = Queue.new
65
- @shutdown = nil
66
- @t = Thread.new { runlogger }
67
-
68
- end
69
-
70
- def level=(level)
71
- if LOGLEVELS.has_key?(level)
72
- @log.level = LOGLEVELS[level]
73
- else
74
- raise ArgumentError, "invalid log level #{level}"
75
- end
76
- end
77
-
78
- def shutdown
79
-
80
- # stops new messages from being enqueued and tells thread
81
- # to drain what's in the queue
82
- @shutdown = true
83
- @t.join
84
-
85
- end
86
-
87
- def enqueue(severity, msg=nil)
88
-
89
- # don't enqueue if we're in shutdown
90
- if( @shutdown )
91
- return
92
- end
93
-
94
- # put the message on the queue
95
- @queue.push( [severity, msg] )
96
-
97
- end
98
-
99
- private
100
-
101
- def runlogger
102
-
103
- # do blocking pops off the queue until the shutdown flag is set
104
- while @shutdown.nil?
105
- msg = @queue.pop(false)
106
- @log.add(msg[0], msg[1])
107
- end
108
-
109
- # pop anything left on the queue off
110
- while ! @queue.empty?
111
- msg = @queue.pop(true)
112
- @log.add(msg[0], msg[1])
113
- end
114
-
115
- end
116
-
117
- end
118
-
119
- # supress the "Logfile created at" header line that logger.rb provides
120
- class Logger
121
-
122
- private
123
-
124
- class LogDevice
125
-
126
- private
127
-
128
- def add_log_header(file)
129
- end
130
-
131
- end
132
-
133
- end
1
+ require 'threadedlogger/version'
2
+ require 'threadedlogger/core'
3
+ require 'threadedlogger/logger'
@@ -0,0 +1,138 @@
1
+ require 'thread'
2
+ require 'logger'
3
+
4
+ class ThreadedLogger
5
+
6
+ LOGLEVELS = {
7
+ 'debug' => Logger::DEBUG,
8
+ 'info' => Logger::INFO,
9
+ 'warn' => Logger::WARN,
10
+ 'error' => Logger::ERROR,
11
+ 'fatal' => Logger::FATAL,
12
+ }
13
+
14
+ @@instances = nil
15
+
16
+ private_class_method :new
17
+
18
+ # create the logging methods
19
+ LOGLEVELS.each do |k,v|
20
+ log_method = k.to_sym
21
+ test_method = "#{k}?".to_sym
22
+ define_method(log_method) { |msg=nil|
23
+ enqueue(v, msg)
24
+ }
25
+ define_method(test_method) {
26
+ @log.send(test_method)
27
+ }
28
+ end
29
+
30
+ def self.instance(*args)
31
+ if @@instances[self].nil?
32
+ @@instances[self] = new(*args)
33
+ else
34
+ if ! args.empty?
35
+ raise ArgumentError, "instance for #{self} already constructed"
36
+ end
37
+ end
38
+ return @@instances[self]
39
+ end
40
+
41
+ def self.clear(shutdown = false)
42
+ if shutdown and ! @@instances[self].nil?
43
+ @@instances[self].shutdown
44
+ end
45
+ @@instances[self] = nil
46
+ end
47
+
48
+ def self.clear_all(shutdown = false)
49
+ if shutdown and ! @@instances.nil?
50
+ @@instances.each_value do |obj|
51
+ if ! obj.nil?
52
+ obj.shutdown
53
+ end
54
+ end
55
+ end
56
+ @@instances = Hash.new
57
+ end
58
+
59
+ def initialize(file, rotation = 'daily', level = 'info', formatter = nil)
60
+ if file.nil?
61
+ raise ArgumentError, "log file name is required"
62
+ end
63
+
64
+ # create a logger
65
+ @log = Logger.new(file, rotation)
66
+
67
+ # set the min threshold
68
+ send(:level=, level)
69
+
70
+ # apply a formatter if one was given
71
+ if ! formatter.nil?
72
+ @log.formatter = formatter
73
+ end
74
+
75
+ # set up a queue and spawn a thread to do the logging
76
+ @queue = Queue.new
77
+ @shutdown = false
78
+ @t = Thread.new {
79
+ runlogger
80
+ }
81
+ end
82
+
83
+ def level=(level)
84
+ if LOGLEVELS.has_key?(level)
85
+ @log.level = LOGLEVELS[level]
86
+ else
87
+ raise ArgumentError, "invalid log level #{level}"
88
+ end
89
+ end
90
+
91
+ def shutdown
92
+
93
+ # stops new messages from being enqueued and tells thread
94
+ # to drain what's in the queue
95
+ if ! @shutdown
96
+ @shutdown = true
97
+ @queue.push(nil)
98
+ @t.join
99
+ @t = nil
100
+ end
101
+
102
+ end
103
+
104
+ def enqueue(severity, msg=nil)
105
+ # don't enqueue if we're in shutdown
106
+ if( @shutdown )
107
+ return
108
+ end
109
+
110
+ # put the message on the queue
111
+ @queue.push( [severity, msg] )
112
+ end
113
+
114
+ private
115
+
116
+ def runlogger
117
+ # do blocking pops off the queue until the shutdown flag is set
118
+ while ! @shutdown
119
+ msg = @queue.pop(false)
120
+ if ! msg.nil?
121
+ @log.add(msg[0], msg[1])
122
+ end
123
+ end
124
+
125
+ # pop anything left on the queue off
126
+ while ! @queue.empty?
127
+ msg = @queue.pop(true)
128
+ if ! msg.nil?
129
+ @log.add(msg[0], msg[1])
130
+ end
131
+ end
132
+ end
133
+
134
+ # create catalog of per-subclass instances
135
+ self.clear_all
136
+
137
+ end
138
+
@@ -0,0 +1,15 @@
1
+ # Monkey patch Logger to supress the "Logfile created at" header line
2
+ class Logger
3
+
4
+ private
5
+
6
+ class LogDevice
7
+
8
+ private
9
+
10
+ def add_log_header(file)
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,5 @@
1
+ class ThreadedLogger
2
+
3
+ VERSION = '1.2.0'
4
+
5
+ end
@@ -1,5 +1,21 @@
1
1
  require 'simplecov'
2
+ require 'simplecov-console'
3
+
4
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
5
+ SimpleCov::Formatter::HTMLFormatter,
6
+ SimpleCov::Formatter::Console,
7
+ ]
2
8
  SimpleCov.start do
3
9
  add_filter '/vendor/'
4
10
  add_filter '/test/'
5
11
  end
12
+
13
+ require 'minitest/autorun'
14
+ require 'minitest/debugger' if ENV['DEBUG']
15
+ require 'threadedlogger'
16
+
17
+ class ThreadedLoggerTest < Minitest::Test
18
+ def teardown
19
+ ThreadedLogger.clear_all(true)
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ require 'minitest_helper'
2
+ require 'threadedlogger'
3
+
4
+ class TestClear < ThreadedLoggerTest
5
+ def test_clear_without_shutdown
6
+ logger = ThreadedLogger.instance('test/foo.log', 'daily')
7
+ ThreadedLogger.clear
8
+ logger = ThreadedLogger.instance('test/foo.log', 'daily')
9
+ ThreadedLogger.clear(true)
10
+ end
11
+ end
@@ -0,0 +1,67 @@
1
+ require 'minitest_helper'
2
+ require 'threadedlogger'
3
+
4
+ class OurLog1 < ThreadedLogger
5
+ end
6
+
7
+ class OurLog2 < ThreadedLogger
8
+ end
9
+
10
+ class TestInheritable < ThreadedLoggerTest
11
+ def test_construct_subclass
12
+ logger = OurLog1.instance('test/foo.log', 'daily')
13
+ assert_instance_of(OurLog1, logger)
14
+ logger.info 'foo'
15
+ File.exists?('test/foo.log')
16
+ logger.shutdown
17
+ end
18
+ def test_construct_two_subclasses
19
+ logger1 = OurLog1.instance('test/foo.log', 'daily')
20
+ logger2 = OurLog2.instance('test/bar.log', 'daily')
21
+ assert_instance_of(OurLog1, logger1)
22
+ assert_instance_of(OurLog2, logger2)
23
+ logger1.info 'foo'
24
+ logger2.info 'bar'
25
+ File.exists?('test/foo.log')
26
+ File.exists?('test/bar.log')
27
+ logger1.shutdown
28
+ logger2.shutdown
29
+ end
30
+ def test_reconstruct_subclass
31
+ logger1 = OurLog1.instance('test/foo.log', 'daily')
32
+ logger2 = OurLog2.instance('test/bar.log', 'daily')
33
+ assert_instance_of(OurLog1, logger1)
34
+ assert_instance_of(OurLog2, logger2)
35
+ assert_raises(ArgumentError) {
36
+ OurLog1.instance('test/foo.log', 'daily')
37
+ }
38
+ assert_raises(ArgumentError) {
39
+ OurLog2.instance('test/bar.log', 'daily')
40
+ }
41
+ end
42
+ def test_clear
43
+ logger1 = OurLog1.instance('test/foo.log', 'daily')
44
+ logger2 = OurLog2.instance('test/bar.log', 'daily')
45
+ assert_instance_of(OurLog1, logger1)
46
+ assert_instance_of(OurLog2, logger2)
47
+ assert_raises(ArgumentError) {
48
+ OurLog1.instance('test/foo.log', 'daily')
49
+ }
50
+ OurLog1.clear
51
+ logger2b = OurLog2.instance
52
+ assert_instance_of(OurLog2, logger2b)
53
+ assert_equal logger2b, logger2
54
+ logger1 = OurLog1.instance('test/foo.log', 'daily')
55
+ assert_instance_of(OurLog1, logger1)
56
+ end
57
+ def test_clear_all
58
+ logger1 = OurLog1.instance('test/foo.log', 'daily')
59
+ assert_instance_of(OurLog1, logger1)
60
+ assert_raises(ArgumentError) {
61
+ OurLog1.instance('test/foo.log', 'daily')
62
+ }
63
+ ThreadedLogger.clear_all
64
+ logger1 = OurLog1.instance('test/foo.log', 'daily')
65
+ assert_instance_of(OurLog1, logger1)
66
+ end
67
+ end
data/test/test_logging.rb CHANGED
@@ -1,25 +1,11 @@
1
1
  require 'minitest_helper'
2
- require 'minitest/autorun'
3
2
  require 'threadedlogger'
4
- require 'fakefs'
5
3
 
6
- class ThreadedLogger
7
- def ThreadedLogger.clear
8
- if ! @@instance.nil?
9
- @@instance.shutdown
10
- @@instance = nil
11
- end
12
- end
13
- end
14
-
15
- class TestLogging < Minitest::Test
16
- def setup
17
- ThreadedLogger.clear
18
- end
4
+ class TestLogging < ThreadedLoggerTest
19
5
  def test_logging
20
- logger = ThreadedLogger.instance('foo.log', 'daily')
6
+ logger = ThreadedLogger.instance('test/foo.log', 'daily')
21
7
  logger.info 'foo'
22
- File.exists?('foo.log')
8
+ File.exists?('test/foo.log')
23
9
  logger.shutdown
24
10
  end
25
11
  end
data/test/test_methods.rb CHANGED
@@ -1,23 +1,8 @@
1
1
  require 'minitest_helper'
2
- require 'minitest/autorun'
3
- require 'threadedlogger'
4
- require 'fakefs'
5
2
 
6
- class ThreadedLogger
7
- def ThreadedLogger.clear
8
- if ! @@instance.nil?
9
- @@instance.shutdown
10
- @@instance = nil
11
- end
12
- end
13
- end
14
-
15
- class TestMethods < Minitest::Test
16
- def setup
17
- ThreadedLogger.clear
18
- end
3
+ class TestMethods < ThreadedLoggerTest
19
4
  def test_levels
20
- logger = ThreadedLogger.instance('test/example.log', 'daily')
5
+ logger = ThreadedLogger.instance('test/foo.log', 'daily')
21
6
  assert_instance_of ThreadedLogger, logger, "constructor works with 2 args"
22
7
  %w(debug info warn error fatal).each do |level|
23
8
  assert_respond_to(logger, level.to_sym, "logger responds to level #{level}")
@@ -27,14 +12,14 @@ class TestMethods < Minitest::Test
27
12
  }
28
13
  end
29
14
  def test_level_predicates
30
- logger = ThreadedLogger.instance('test/example.log', 'daily', 'info')
15
+ logger = ThreadedLogger.instance('test/foo.log', 'daily', 'info')
31
16
  assert_instance_of ThreadedLogger, logger, "constructor works with 3 args"
32
17
  %w(debug info warn error fatal).each do |level|
33
18
  assert_respond_to(logger, "#{level}?".to_sym, "logger responds to predicate #{level}?")
34
19
  end
35
20
  end
36
21
  def test_setlevel
37
- logger = ThreadedLogger.instance('test/example.log', 'daily', 'info')
22
+ logger = ThreadedLogger.instance('test/foo.log', 'daily', 'info')
38
23
  assert_instance_of ThreadedLogger, logger, "constructor works with 3 args"
39
24
  assert_equal(false, logger.debug?, 'debug is not on when contstructed at level info')
40
25
  logger.level = 'debug'
@@ -0,0 +1,10 @@
1
+ require 'minitest_helper'
2
+ require 'threadedlogger'
3
+
4
+ class TestShutdown < ThreadedLoggerTest
5
+ def test_enqueue_after_shutdown
6
+ logger = ThreadedLogger.instance('test/foo.log', 'daily')
7
+ logger.shutdown
8
+ logger.info('foo')
9
+ end
10
+ end
@@ -1,31 +1,16 @@
1
1
  require 'minitest_helper'
2
- require 'minitest/autorun'
3
- require 'threadedlogger'
4
- require 'fakefs'
5
2
 
6
- class ThreadedLogger
7
- def ThreadedLogger.clear
8
- if ! @@instance.nil?
9
- @@instance.shutdown
10
- @@instance = nil
11
- end
12
- end
13
- end
14
-
15
- class TestThreadedLogger < Minitest::Test
16
- def setup
17
- ThreadedLogger.clear
18
- end
3
+ class TestThreadedLogger < ThreadedLoggerTest
19
4
  def test_constructor
20
- logger = ThreadedLogger.instance('test/example.log', 'daily')
5
+ logger = ThreadedLogger.instance('test/foo.log', 'daily')
21
6
  assert_instance_of ThreadedLogger, logger, "constructor works with 2 args"
22
7
  end
23
8
  def test_constructor_with_level
24
- logger = ThreadedLogger.instance('test/example.log', 'daily', 'info')
9
+ logger = ThreadedLogger.instance('test/foo.log', 'daily', 'info')
25
10
  assert_instance_of ThreadedLogger, logger, "constructor works with 3 args"
26
11
  end
27
12
  def test_constructor_with_level_and_formatter
28
- logger = ThreadedLogger.instance('test/example.log', 'daily', 'info', proc { |l| l } )
13
+ logger = ThreadedLogger.instance('test/foo.log', 'daily', 'info', proc { |l| l } )
29
14
  assert_instance_of ThreadedLogger, logger, "constructor works with 4 args"
30
15
  end
31
16
  def test_constructor_noargs
@@ -34,14 +19,14 @@ class TestThreadedLogger < Minitest::Test
34
19
  end
35
20
  end
36
21
  def test_singleton
37
- loggera = ThreadedLogger.instance('test/example.log')
22
+ loggera = ThreadedLogger.instance('test/foo.log')
38
23
  loggerb = ThreadedLogger.instance
39
24
  assert_same loggera, loggerb, "two instances are the same object"
40
25
  end
41
26
  def test_construct_twice
42
27
  assert_raises ArgumentError, "cannot call instance a second time with args" do
43
- ThreadedLogger.instance('test/example.log')
44
- ThreadedLogger.instance('test/example2.log')
28
+ ThreadedLogger.instance('test/foo.log')
29
+ ThreadedLogger.instance('test/bar.log')
45
30
  end
46
31
  end
47
32
  end
@@ -4,7 +4,6 @@ require 'threadedlogger'
4
4
  Gem::Specification.new do |s|
5
5
  s.name = 'threadedlogger'
6
6
  s.version = ThreadedLogger::VERSION
7
- s.date = '2013-03-23'
8
7
  s.summary = 'Simple logging library with a dedicated logging thread'
9
8
  s.homepage = 'https://github.com/jf647/ThreadedLogger'
10
9
  s.authors = ['James FitzGibbon']
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: threadedlogger
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -63,11 +63,17 @@ files:
63
63
  - LICENSE.txt
64
64
  - Rakefile
65
65
  - lib/threadedlogger.rb
66
+ - lib/threadedlogger/core.rb
67
+ - lib/threadedlogger/version.rb
68
+ - lib/threadedlogger/logger.rb
66
69
  - test/minitest_helper.rb
67
70
  - test/test_logging.rb
68
71
  - test/test_methods.rb
69
72
  - test/test_threadedlogger.rb
73
+ - test/test_shutdown.rb
74
+ - test/test_clear.rb
70
75
  - threadedlogger.gemspec
76
+ - test/test_inheritable.rb
71
77
  - .gemtest
72
78
  homepage: https://github.com/jf647/ThreadedLogger
73
79
  licenses:
@@ -86,7 +92,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
86
92
  version: '0'
87
93
  segments:
88
94
  - 0
89
- hash: -1670757235924190297
95
+ hash: -484491560591714406
90
96
  required_rubygems_version: !ruby/object:Gem::Requirement
91
97
  none: false
92
98
  requirements:
@@ -95,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
95
101
  version: '0'
96
102
  segments:
97
103
  - 0
98
- hash: -1670757235924190297
104
+ hash: -484491560591714406
99
105
  requirements: []
100
106
  rubyforge_project: threadedlogger
101
107
  rubygems_version: 1.8.23
@@ -104,6 +110,9 @@ specification_version: 3
104
110
  summary: ThreadedLogger runs a dedicated logging thread around Ruby's Logger library
105
111
  to ensure that multiple threads don't step on each other's toes.
106
112
  test_files:
113
+ - test/test_clear.rb
114
+ - test/test_inheritable.rb
107
115
  - test/test_logging.rb
108
116
  - test/test_methods.rb
117
+ - test/test_shutdown.rb
109
118
  - test/test_threadedlogger.rb