threadedlogger 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
File without changes
@@ -0,0 +1,11 @@
1
+ # History for ThreadedLogger
2
+
3
+ ## v1.1.0
4
+
5
+ * Dynamically generate logging methods
6
+ * Enhance test suite
7
+
8
+ ## v1.0.0
9
+
10
+ * Initial Release
11
+
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2012, 2013 James FitzGibbon <james@nadt.net>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to
7
+ deal in the Software without restriction, including without limitation the
8
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9
+ sell copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
+ IN THE SOFTWARE.
@@ -0,0 +1,11 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ LICENSE.txt
5
+ Rakefile
6
+ lib/threadedlogger.rb
7
+ test/minitest_helper.rb
8
+ test/test_logging.rb
9
+ test/test_methods.rb
10
+ test/test_threadedlogger.rb
11
+ threadedlogger.gemspec
@@ -0,0 +1,84 @@
1
+ # ThreadedLogger
2
+
3
+ home :: https://github.com/jf647/ThreadedLogger
4
+
5
+ ## SUMMARY:
6
+
7
+ Simple ruby logging library with a dedicated logging thread.
8
+
9
+ ## DESCRIPTION:
10
+
11
+ ThreadedLogger runs a dedicated logging thread around Ruby's Logger library
12
+ to ensure that multiple threads don't step on each other's toes.
13
+
14
+ ## SYNOPSIS:
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
31
+ class method. You can only provide arguments the first time - trying to
32
+ 're-construct' the object will throw an ArgumentError.
33
+
34
+ Under the covers, the dedicated thread uses the standard Ruby Logger
35
+ library. Refer to [Logger](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/logger/rdoc/Logger.html)
36
+ for further detail.
37
+
38
+ The arguments are: the filename, the rotation period (defaults to 'daily'),
39
+ the loglevel (defaults to 'info') and an optional formatter proc that will
40
+ be used instead of the one that comes with Logger.
41
+
42
+ When you invoke one of the logging methods, the text is enqueued. The
43
+ background thread constantly pops messages off this queue and logs them. To
44
+ ensure that all queued messages have been written out, call the .shutdown
45
+ instance method before your program exits. On a clean shutdown this should
46
+ happen automatically, but if you exit in a funky way it might not capture
47
+ the last message.
48
+
49
+ This library also overrides Logger.LogDevice.add_log_header to prevent it
50
+ from putting a header line at the top of a logfile when it is first opened.
51
+
52
+ ## LICENSE:
53
+
54
+ The MIT License (MIT)
55
+
56
+ Copyright (c) 2012, 2013 James FitzGibbon <james@nadt.net>
57
+
58
+ Permission is hereby granted, free of charge, to any person obtaining a copy
59
+ of this software and associated documentation files (the "Software"), to
60
+ deal in the Software without restriction, including without limitation the
61
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
62
+ sell copies of the Software, and to permit persons to whom the Software is
63
+ furnished to do so, subject to the following conditions:
64
+
65
+ The above copyright notice and this permission notice shall be included in
66
+ all copies or substantial portions of the Software.
67
+
68
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
69
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
70
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
71
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
72
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
73
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
74
+ IN THE SOFTWARE.
75
+
76
+ ## Contributing to ThreadedLogger
77
+
78
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
79
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
80
+ * Fork the project
81
+ * Start a feature/bugfix branch
82
+ * Commit and push until you are happy with your contribution
83
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
84
+ * Please try not to mess with the version or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
@@ -0,0 +1,18 @@
1
+ require 'rake/testtask'
2
+ require 'hoe'
3
+
4
+ Hoe.spec 'threadedlogger' do
5
+ developer("James FitzGibbon", "james@nadt.net")
6
+ license "MIT"
7
+ end
8
+
9
+ task :default => [:unit_tests]
10
+
11
+ desc "Run basic tests"
12
+ Rake::TestTask.new("unit_tests") { |t|
13
+ t.libs.push 'lib'
14
+ t.libs.push 'test'
15
+ t.pattern = 'test/test_*.rb'
16
+ t.verbose = true
17
+ t.warning = true
18
+ }
@@ -0,0 +1,133 @@
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
@@ -0,0 +1,5 @@
1
+ require 'simplecov'
2
+ SimpleCov.start do
3
+ add_filter '/vendor/'
4
+ add_filter '/test/'
5
+ end
@@ -0,0 +1,25 @@
1
+ require 'minitest_helper'
2
+ require 'minitest/autorun'
3
+ require 'threadedlogger'
4
+ require 'fakefs'
5
+
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
19
+ def test_logging
20
+ logger = ThreadedLogger.instance('foo.log', 'daily')
21
+ logger.info 'foo'
22
+ File.exists?('foo.log')
23
+ logger.shutdown
24
+ end
25
+ end
@@ -0,0 +1,46 @@
1
+ require 'minitest_helper'
2
+ require 'minitest/autorun'
3
+ require 'threadedlogger'
4
+ require 'fakefs'
5
+
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
19
+ def test_levels
20
+ logger = ThreadedLogger.instance('test/example.log', 'daily')
21
+ assert_instance_of ThreadedLogger, logger, "constructor works with 2 args"
22
+ %w(debug info warn error fatal).each do |level|
23
+ assert_respond_to(logger, level.to_sym, "logger responds to level #{level}")
24
+ end
25
+ assert_raises(ArgumentError) {
26
+ logger.level = 'foo'
27
+ }
28
+ end
29
+ def test_level_predicates
30
+ logger = ThreadedLogger.instance('test/example.log', 'daily', 'info')
31
+ assert_instance_of ThreadedLogger, logger, "constructor works with 3 args"
32
+ %w(debug info warn error fatal).each do |level|
33
+ assert_respond_to(logger, "#{level}?".to_sym, "logger responds to predicate #{level}?")
34
+ end
35
+ end
36
+ def test_setlevel
37
+ logger = ThreadedLogger.instance('test/example.log', 'daily', 'info')
38
+ assert_instance_of ThreadedLogger, logger, "constructor works with 3 args"
39
+ assert_equal(false, logger.debug?, 'debug is not on when contstructed at level info')
40
+ logger.level = 'debug'
41
+ assert_equal(true, logger.debug?, 'debug is on after explicitly setting level')
42
+ end
43
+ def test_nil_filename
44
+ assert_raises(ArgumentError) { ThreadedLogger.instance(nil) }
45
+ end
46
+ end
@@ -0,0 +1,47 @@
1
+ require 'minitest_helper'
2
+ require 'minitest/autorun'
3
+ require 'threadedlogger'
4
+ require 'fakefs'
5
+
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
19
+ def test_constructor
20
+ logger = ThreadedLogger.instance('test/example.log', 'daily')
21
+ assert_instance_of ThreadedLogger, logger, "constructor works with 2 args"
22
+ end
23
+ def test_constructor_with_level
24
+ logger = ThreadedLogger.instance('test/example.log', 'daily', 'info')
25
+ assert_instance_of ThreadedLogger, logger, "constructor works with 3 args"
26
+ end
27
+ def test_constructor_with_level_and_formatter
28
+ logger = ThreadedLogger.instance('test/example.log', 'daily', 'info', proc { |l| l } )
29
+ assert_instance_of ThreadedLogger, logger, "constructor works with 4 args"
30
+ end
31
+ def test_constructor_noargs
32
+ assert_raises ArgumentError, "cannot call with no args" do
33
+ ThreadedLogger.instance
34
+ end
35
+ end
36
+ def test_singleton
37
+ loggera = ThreadedLogger.instance('test/example.log')
38
+ loggerb = ThreadedLogger.instance
39
+ assert_same loggera, loggerb, "two instances are the same object"
40
+ end
41
+ def test_construct_twice
42
+ assert_raises ArgumentError, "cannot call instance a second time with args" do
43
+ ThreadedLogger.instance('test/example.log')
44
+ ThreadedLogger.instance('test/example2.log')
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,15 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require 'threadedlogger'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'threadedlogger'
6
+ s.version = ThreadedLogger::VERSION
7
+ s.date = '2013-03-23'
8
+ s.summary = 'Simple logging library with a dedicated logging thread'
9
+ s.homepage = 'https://github.com/jf647/ThreadedLogger'
10
+ s.authors = ['James FitzGibbon']
11
+ s.email = ['james@nadt.net']
12
+ s.files = `git ls-files`.split("\n")
13
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f)
15
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: threadedlogger
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - James FitzGibbon
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-09-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rdoc
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '4.0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '4.0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: hoe
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '3.7'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '3.7'
46
+ description: ! 'ThreadedLogger runs a dedicated logging thread around Ruby''s Logger
47
+ library
48
+
49
+ to ensure that multiple threads don''t step on each other''s toes.'
50
+ email:
51
+ - james@nadt.net
52
+ executables: []
53
+ extensions: []
54
+ extra_rdoc_files:
55
+ - History.txt
56
+ - Manifest.txt
57
+ - README.txt
58
+ - LICENSE.txt
59
+ files:
60
+ - History.txt
61
+ - Manifest.txt
62
+ - README.txt
63
+ - LICENSE.txt
64
+ - Rakefile
65
+ - lib/threadedlogger.rb
66
+ - test/minitest_helper.rb
67
+ - test/test_logging.rb
68
+ - test/test_methods.rb
69
+ - test/test_threadedlogger.rb
70
+ - threadedlogger.gemspec
71
+ - .gemtest
72
+ homepage: https://github.com/jf647/ThreadedLogger
73
+ licenses:
74
+ - MIT
75
+ post_install_message:
76
+ rdoc_options:
77
+ - --main
78
+ - README.txt
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ segments:
88
+ - 0
89
+ hash: -1670757235924190297
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ segments:
97
+ - 0
98
+ hash: -1670757235924190297
99
+ requirements: []
100
+ rubyforge_project: threadedlogger
101
+ rubygems_version: 1.8.23
102
+ signing_key:
103
+ specification_version: 3
104
+ summary: ThreadedLogger runs a dedicated logging thread around Ruby's Logger library
105
+ to ensure that multiple threads don't step on each other's toes.
106
+ test_files:
107
+ - test/test_logging.rb
108
+ - test/test_methods.rb
109
+ - test/test_threadedlogger.rb