notarius 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE.md ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Frank Mitchell
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a
4
+ copy of this software and associated documentation files
5
+ (the "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included
12
+ in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,149 @@
1
+ Notarius
2
+ ========
3
+
4
+ Notarius is a logging library with opinions. The word "notarius" is
5
+ Latin for "shorthand writer". To this end, Notarius does everything
6
+ possible to encourage you to write short useful log messages.
7
+
8
+ * Whitespace is converted to spaces.
9
+ * Lines are truncated to 140 characters.
10
+ * Timestamps are formatted as [ISO 8601][].
11
+ * Call stacks are easy to grep.
12
+ * Duplicate messages are discarded.
13
+
14
+ Notarius does not want you to be happy now, while you're writing code.
15
+ It wants the person reading your logs at 3am trying to figure out why
16
+ your code doesn't work to be happy. If those happen to be the same
17
+ person, Notarius would rather future you be happier.
18
+
19
+ There is infinitely more future than there is now.
20
+
21
+ Installation
22
+ ------------
23
+
24
+ Notarius is packaged as a Ruby gem. You do `gem install notarius` from
25
+ the command line to get it.
26
+
27
+ Configuration
28
+ -------------
29
+
30
+ Notarius is namespaced. This lets multiple libraries use Notarius
31
+ without clobbering each others logs.
32
+
33
+ ```ruby
34
+ require 'notarius'
35
+
36
+ Notarius.configure 'BIG' do |log|
37
+ log.file = '/var/log/notarius/notebook.log'
38
+ end
39
+ ```
40
+
41
+ If you try to log to the same file as another library, Notarius will
42
+ yell at you and throw an exception. Notarius does not want you to
43
+ obliterate someone else's carefully crafted logs. However, it will tell
44
+ you what they've namespaced their logs so you can redirect them
45
+ elsewhere.
46
+
47
+ By default, Notarius doesn't log to anything, not even the console. If
48
+ you want console logging, you have to enable it.
49
+
50
+ ```ruby
51
+ Notarius.configure 'BIG' do |log|
52
+ log.console = true
53
+ end
54
+ ```
55
+
56
+ Notarius is fine with you calling the `configure` method multiple times.
57
+ This means you can set up defaults when your program starts and change
58
+ them later.
59
+
60
+ Usage
61
+ -----
62
+
63
+ Using Notarius is simple. Include the `Notarius::NAME` module (where
64
+ NAME is the string you passed to the `configure` method) and call
65
+ `log.info`, `log.warn`, or `log.error`. You can log anything that
66
+ responds to `:message`, `:backtrace`, or `:inspect`.
67
+
68
+ ```ruby
69
+ class Player
70
+ include Notarius::BIG
71
+
72
+ def move direction
73
+ log.info "You head off down the #{direction} path."
74
+ end
75
+
76
+ def poke object
77
+ log.warn "Gingerly, you nudge the #{object}."
78
+ end
79
+
80
+ def attack monster
81
+ if monster.nil?
82
+ log.error "You attack the darkness."
83
+ else
84
+ log.info "You whack the #{monster} with a stick."
85
+ end
86
+ end
87
+ end
88
+ ```
89
+
90
+ Opinions
91
+ --------
92
+
93
+ Notarius only has three logging levels: info, warning, and error. You
94
+ don't need more than that. If think you do, your logs are too verbose.
95
+
96
+ There is one line of output per message. If you insert carriage returns
97
+ and newlines in a message, they will be converted into spaces. The same
98
+ goes for tabs. Log statements shouldn't read like poetry. Exceptions are
99
+ the exceptional exception to this rule.
100
+
101
+ Messages are truncated so the total length of a line is less than 140
102
+ characters. This makes them easy to [tweet][]. Tweetablility is
103
+ important, because it's what the people reading logs at 3am do when they
104
+ find something hilarious. If you're writing non-tweetable messages, your
105
+ logs are too verbose.
106
+
107
+ To make it easy to grep for warnings and errors, messages are output as
108
+ "level [timestamp] message". You can do `cat notarius.log | grep ^WARN`
109
+ and find all the warnings in a log.
110
+
111
+ If the object you pass to the `log.info`, `log.warn`, and `log.error`
112
+ methods responds to `:message` and `:backtrace`, you'll get output like
113
+ this:
114
+
115
+ ```bash
116
+ level [timestamp] message
117
+ ! backtrace
118
+ ! backtrace
119
+ ! backtrace
120
+ ```
121
+
122
+ This makes it easy to log exceptions. More importantly, it makes it easy
123
+ to find exceptions: `cat notarius.log | grep -B 1 "^\!"`
124
+
125
+ If you try to write the same message multiple times, only the first one
126
+ will show up. This is true even if you change the logging level, or the
127
+ timestamps of the messages are different. Multiple identical lines in
128
+ the log are a sign that your code is broken.
129
+
130
+ Inspiration
131
+ -----------
132
+
133
+ At lot of the Notarius philosophy was copied from [Logula][]. The
134
+ rest of it comes from several years watching sysadmins work, and
135
+ realizing that programmers often make their lives more difficult than we
136
+ intend.
137
+
138
+ Notarius tries to make things easier.
139
+
140
+ License
141
+ -------
142
+
143
+ Notarius is available under an [MIT-style][] license. See the
144
+ {file:LICENSE.md} document for more information.
145
+
146
+ [Logula]: https://github.com/codahale/logula "Logula is a Scala library which provides a sane log output format and easy-to-use mixin for adding logging to your code."
147
+ [MIT-style]: http://opensource.org/license/MIT "Open Source Initiative OSI - The MIT License"
148
+ [ISO 8601]: http://en.wikipedia.org/wiki/ISO_8601 "An international standard covering the exchange of date and time related data."
149
+ [tweet]: https://twitter.com/ "Twitter"
data/lib/notarius.rb ADDED
@@ -0,0 +1,82 @@
1
+ require 'notarius/secretary'
2
+ require 'notarius/config'
3
+ require 'notarius/exception'
4
+ require 'notarius/version'
5
+
6
+ module Notarius
7
+ @configs = {}
8
+
9
+ ##
10
+ # Configure logging for the named module.
11
+ #
12
+ # @yieldparam log [Config]
13
+ #
14
+ # @example
15
+ # Notarius.configure 'BIG' do |log|
16
+ # log.console = true
17
+ # log.file = '/var/log/notarius/big.log'
18
+ # end
19
+
20
+ def self.configure name, &block
21
+ name = namespace name
22
+ @configs[name] = Config.new if @configs[name].nil?
23
+ @configs[name].instance_eval(&block) if block_given?
24
+ return if self.const_defined? name
25
+
26
+ mod = Module.new do
27
+ define_method :log do
28
+ @secretary = Secretary.new if @secretary.nil?
29
+ @secretary.configure Notarius.config(name)
30
+ @secretary
31
+ end
32
+ end
33
+ self.const_set name, mod
34
+ end
35
+
36
+ ##
37
+ # Return a config with the given name. Validates the config first.
38
+ # @return [Config, nil] matching config or +nil+ if none found
39
+ # @see Notarius.validate
40
+
41
+ def self.config name
42
+ validate name
43
+ @configs[name]
44
+ end
45
+
46
+ ##
47
+ # Validate a config with the given name.
48
+ # @param [String] name name of config to validate
49
+ # @raise [Exception] when file is already being logged to
50
+
51
+ def self.validate name
52
+ config = @configs[name]
53
+ if config
54
+ @configs.each do |n, c|
55
+ if n != name && c.file && c.file == config.file
56
+ message = <<EOF
57
+ Notarius::#{name} logs to the same file as Notarius::#{n}.
58
+ EOF
59
+ raise Notarius::Exception.new message
60
+ end
61
+ end
62
+ end
63
+ end
64
+ private_class_method :validate
65
+
66
+ ##
67
+ # Convert an +Object+ to a +String+ that can be used as a namespace.
68
+ # This has to generate something that matches Ruby's idea of a
69
+ # constant.
70
+ # @raise [Exception] when +name+ is empty
71
+ # @param [#to_s] name name of the namespace
72
+ # @return [String] converted namespace
73
+
74
+ def self.namespace name
75
+ name = name.to_s
76
+ if name.empty?
77
+ raise Notarius::Exception.new "namespaces can't be empty"
78
+ end
79
+ name[0, 1].upcase + name[1, name.size]
80
+ end
81
+ private_class_method :namespace
82
+ end
@@ -0,0 +1,6 @@
1
+ module Notarius
2
+ class Config
3
+ attr_accessor :console
4
+ attr_accessor :file
5
+ end
6
+ end
@@ -0,0 +1,4 @@
1
+ module Notarius
2
+ class Exception < ::Exception
3
+ end
4
+ end
@@ -0,0 +1,72 @@
1
+ require 'time'
2
+
3
+ module Notarius
4
+ ##
5
+ # Handles formatting of log messages. It's compatable with Ruby's
6
+ # +Logger::Formatter+ class, but has its own opinions:
7
+ #
8
+ # * Whitespace in the message is converted to spaces.
9
+ # * Output is truncated to 140 characters.
10
+ # * Timestamps are formatted as ISO 8601 in UTC.
11
+ # * Lines in call stacks are prefixed with !'s.
12
+ # * Any of the arguments to #call can be nil.
13
+
14
+ class Formatter
15
+ ##
16
+ # This is the interface Ruby's +Logger+ class expects.
17
+ # @param [String] severity the severity level of the message
18
+ # @param [Date] timestamp the timestamp for the message
19
+ # @param [Object] application unused
20
+ # @param [String, #message, #backtrace, #inspect] message
21
+ # the message to log
22
+ # @return [String] formatted as "SEVERITY [timestamp] message\\n"
23
+
24
+ def call severity, timestamp, application, message
25
+ result = []
26
+ result << format_severity(severity) if severity
27
+ result << '[' + format_timestamp(timestamp) + ']' if timestamp
28
+ result << format_message(message) if message
29
+ make_tweetable(result.join(' ')) + "\n"
30
+ end
31
+
32
+ def format_severity severity
33
+ severity.strip.upcase
34
+ end
35
+ private :format_severity
36
+
37
+ def format_message message
38
+ result = []
39
+ if message.respond_to?(:message)
40
+ result << message.message
41
+ else
42
+ result << message
43
+ end
44
+ if message.respond_to?(:backtrace)
45
+ backtrace = [message.backtrace]
46
+ backtrace.flatten!
47
+ backtrace.compact!
48
+ result << backtrace.map { |line| "! %s" % clean_message(line) }
49
+ end
50
+ result.flatten!
51
+ result.map! { |line| clean_message(line) }
52
+ result.join("\n")
53
+ end
54
+ private :format_message
55
+
56
+ def clean_message message
57
+ message = message.inspect unless message.kind_of?(String)
58
+ message.gsub(/[\t\r\n\s]+/, ' ').strip
59
+ end
60
+ private :clean_message
61
+
62
+ def format_timestamp timestamp
63
+ timestamp.utc.iso8601
64
+ end
65
+ private :format_timestamp
66
+
67
+ def make_tweetable message
68
+ message.length > 140 ? message[0, 137] + '...' : message
69
+ end
70
+ private :make_tweetable
71
+ end
72
+ end
@@ -0,0 +1,88 @@
1
+ require 'notarius/formatter'
2
+ require 'logger'
3
+
4
+ module Notarius
5
+ class Secretary
6
+ ##
7
+ # Create a new instance of Secretary.
8
+ # @return [Secretary]
9
+
10
+ def initialize
11
+ @loggers = {}
12
+ @messages = {}
13
+ end
14
+
15
+ ##
16
+ # Configure a Secretary.
17
+ # @param [Config] config the configuration for this Secretary
18
+
19
+ def configure config
20
+ if config.console
21
+ add :console, logger(config.console, $stdout)
22
+ else
23
+ delete :console
24
+ end
25
+ if config.file
26
+ add :file, config.file
27
+ else
28
+ delete :file
29
+ end
30
+ end
31
+
32
+ ##
33
+ # Log an informative message. Informative messages show up the log
34
+ # with "INFO" at the start of the line.
35
+
36
+ def info message
37
+ log Logger::INFO, message
38
+ end
39
+
40
+ ##
41
+ # Log a warning message. Warning messages show up in the log with
42
+ # "WARN" at the start of the line.
43
+
44
+ def warn message
45
+ log Logger::WARN, message
46
+ end
47
+
48
+ ##
49
+ # Log an error message. Error messages show up in the log with
50
+ # "ERROR" at the start of the line.
51
+
52
+ def error message
53
+ log Logger::ERROR, message
54
+ end
55
+
56
+ def log severity, message
57
+ @loggers.each do |key, logger|
58
+ if message != @messages[key]
59
+ @messages[key] = message
60
+ logger.add(severity) { message }
61
+ end
62
+ end
63
+ end
64
+ private :log
65
+
66
+ def add key, stream
67
+ @loggers[key] = Logger.new stream
68
+ @loggers[key].formatter = Formatter.new
69
+ end
70
+ private :add
71
+
72
+ def delete key
73
+ logger = @loggers.delete(key)
74
+ logger.close rescue nil
75
+ end
76
+ private :delete
77
+
78
+ def logger *args
79
+ args.find { |arg| loggable?(arg) }
80
+ end
81
+ private :logger
82
+
83
+ def loggable? stream
84
+ stream.respond_to?(:write) && stream.respond_to?(:close)
85
+ end
86
+ private :loggable?
87
+ end
88
+ end
@@ -0,0 +1,6 @@
1
+ module Notarius
2
+ ##
3
+ # Semantic versioning makes life easier.
4
+
5
+ VERSION = '0.1.0'
6
+ end
data/notarius.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ $LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
2
+
3
+ require 'notarius/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'notarius'
7
+ s.version = Notarius::VERSION
8
+ s.required_ruby_version = '~> 1.9'
9
+
10
+ s.summary = 'Notarius is a logging library with opinions.'
11
+ s.description = <<EOF
12
+ Notarius is a logging library with opinions. The word "notarius" is
13
+ Latin for "shorthand writer". To this end, Notarius does everything
14
+ possible to encourage you to write short useful log messages.
15
+ EOF
16
+
17
+ s.author = 'Frank Mitchell'
18
+ s.email = 'me@frankmitchell.org'
19
+ s.homepage = 'https://github.com/elimossinary/notarius'
20
+ s.license = 'MIT'
21
+
22
+ s.add_development_dependency 'rake', '~> 0.9.2'
23
+ s.add_development_dependency 'rspec', '~> 2.10'
24
+ s.add_development_dependency 'simplecov', '~> 0.6'
25
+
26
+ s.files = `git ls-files`.split("\n")
27
+ s.files.reject! { |file| file =~ /^\./ }
28
+
29
+ s.test_files = `git ls-files -- spec/*_spec.rb`.split("\n")
30
+ end
data/rakefile.rb ADDED
@@ -0,0 +1,43 @@
1
+ require 'rake/clean'
2
+ require 'rspec/core/rake_task'
3
+
4
+ CLEAN.include 'doc'
5
+ CLEAN.include 'coverage'
6
+ CLEAN.include '*.gem'
7
+ CLEAN.include '.yardoc'
8
+
9
+ desc 'Run specs.'
10
+ task :default => :spec
11
+
12
+ desc 'Run specs.'
13
+ RSpec::Core::RakeTask.new :spec
14
+
15
+ desc 'Generate SimpleCov spec coverage.'
16
+ RSpec::Core::RakeTask.new :coverage do |t|
17
+ t.rspec_opts = ['--require simplecov_helper']
18
+ end
19
+
20
+ desc 'Build the gem.'
21
+ task :build do
22
+ sh "gem build #{name}.gemspec"
23
+ end
24
+
25
+ desc 'Install the gem'
26
+ task :install do
27
+ sh "gem install ./#{name}-#{version}.gem"
28
+ end
29
+
30
+ desc 'Uninstall the gem'
31
+ task :uninstall do
32
+ sh "gem uninstall #{name}"
33
+ end
34
+
35
+ def name
36
+ @name ||= Dir['*.gemspec'].first.split('.').first
37
+ end
38
+
39
+ def version
40
+ text = File.read("lib/#{name}/version.rb")
41
+ text = text[/^\s*VERSION\s*=\s*.*/]
42
+ @version ||= text.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
43
+ end
@@ -0,0 +1,21 @@
1
+ require 'notarius/config'
2
+
3
+ describe Notarius::Config do
4
+ let(:config) { Notarius::Config.new }
5
+
6
+ it 'defaults to no console' do
7
+ config.console.should be_false
8
+ end
9
+
10
+ it 'defaults to no file' do
11
+ config.file.should be_false
12
+ end
13
+
14
+ it 'lets you configure a console' do
15
+ config.should respond_to(:console=)
16
+ end
17
+
18
+ it 'lets you configure a file' do
19
+ config.should respond_to(:file=)
20
+ end
21
+ end
@@ -0,0 +1,106 @@
1
+ require 'notarius/formatter'
2
+
3
+ describe Notarius::Formatter do
4
+ describe 'call' do
5
+ let(:formatter) { Notarius::Formatter.new }
6
+
7
+ it 'converts whitespace to spaces' do
8
+ message = "\sMessage\r\nwith\twhitespace "
9
+ message = formatter.call(nil, nil, nil, message)
10
+ message.should == "Message with whitespace\n"
11
+ end
12
+
13
+ it 'formats timestamps as ISO 8601' do
14
+ timestamp = Time.parse('2012-06-25 20:41:30 -0400')
15
+ message = formatter.call(nil, timestamp, nil, nil)
16
+ message.should == "[2012-06-26T00:41:30Z]\n"
17
+ end
18
+
19
+ it 'makes severity all upper case' do
20
+ message = formatter.call('info', nil, nil, nil)
21
+ message.should == "INFO\n"
22
+ end
23
+
24
+ it 'removes extra whitespace from severity' do
25
+ message = formatter.call("\swarn\s", nil, nil, nil)
26
+ message.should == "WARN\n"
27
+ end
28
+
29
+ it 'ignores program name field' do
30
+ message = formatter.call(nil, nil, 'noodles', nil)
31
+ message.should == "\n"
32
+ end
33
+
34
+ it 'makes messages tweetable' do
35
+ message = <<-EOF
36
+ This is a really, really long message that needs to
37
+ be more than 140 characters so that Notarius can trim it
38
+ down to something more reasonable in length.
39
+ EOF
40
+ message.length.should > 140
41
+ message = formatter.call(nil, nil, nil, message)
42
+ message.strip.length.should == 140
43
+ end
44
+
45
+ it 'formats messages as "LEVEL [timestamp] message\n"' do
46
+ timestamp = Time.parse('2012-06-25 21:17:44 -0400')
47
+ message = formatter.call('level', timestamp, nil, 'message')
48
+ message.should == "LEVEL [2012-06-26T01:17:44Z] message\n"
49
+ end
50
+
51
+ it 'appends newlines to messages' do
52
+ message = formatter.call(nil, nil, nil, 'message')
53
+ message.should == "message\n"
54
+ end
55
+
56
+ it 'formats exceptions nicely' do
57
+ exception = Exception.new('message')
58
+ exception.set_backtrace ['trace this line', 'back to here']
59
+ lines = formatter.call(nil, nil, nil, exception).split("\n")
60
+ lines[0].should == 'message'
61
+ lines[1].should == '! trace this line'
62
+ lines[2].should == '! back to here'
63
+ end
64
+
65
+ it 'formats objects nicely' do
66
+ object = Class.new do
67
+ def inspect
68
+ 'details'
69
+ end
70
+ end
71
+ message = formatter.call(nil, nil, nil, object.new)
72
+ message.should == "details\n"
73
+ end
74
+
75
+ it 'formats exceptions without backtraces nicely' do
76
+ exception = Exception.new('message')
77
+ message = formatter.call(nil, nil, nil, exception)
78
+ message.should == "message\n"
79
+ end
80
+
81
+ it 'formats objects that look like exceptions nicely' do
82
+ exception = Class.new do
83
+ def message
84
+ message = Class.new do
85
+ def inspect
86
+ 'message'
87
+ end
88
+ end
89
+ message.new
90
+ end
91
+
92
+ def backtrace
93
+ backtrace = Class.new do
94
+ def inspect
95
+ 'backtrace'
96
+ end
97
+ end
98
+ backtrace.new
99
+ end
100
+ end
101
+ lines = formatter.call(nil, nil, nil, exception.new).split("\n")
102
+ lines[0].should == 'message'
103
+ lines[1].should == '! backtrace'
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,116 @@
1
+ require 'notarius'
2
+ require 'tempfile'
3
+
4
+ describe Notarius do
5
+ def tempfiles *prefixes
6
+ files = prefixes.map { |prefix| Tempfile.new prefix }
7
+ begin
8
+ paths = files.map { |file| file.path }
9
+ paths = paths.first if paths.size == 1
10
+ yield paths if block_given?
11
+ ensure
12
+ files.each { |file| file.close! }
13
+ end
14
+ end
15
+
16
+ it 'has a semantic version number' do
17
+ Notarius::VERSION.should match(/^\d+\.\d+\.\d+$/)
18
+ end
19
+
20
+ it 'can log to a file' do
21
+ tempfiles 'player' do |player_log|
22
+ Notarius.configure('BIG') { |l| l.file = player_log }
23
+ player = Class.new do
24
+ include Notarius::BIG
25
+ def initialize
26
+ log.info 'New player created!'
27
+ end
28
+ end
29
+ player.new
30
+ lines = File.read(player_log).split("\n")
31
+ lines[0].should end_with('New player created!')
32
+ end
33
+ end
34
+
35
+ it 'allows namespaces to be overwritten' do
36
+ tempfiles('player', 'monster') do |player_log, monster_log|
37
+ Notarius.configure('BIG') { |l| l.file = player_log }
38
+
39
+ player = Class.new do
40
+ include Notarius::BIG
41
+ def run message
42
+ log.info message
43
+ end
44
+ end
45
+ p = player.new
46
+
47
+ p.run 'Player is running.'
48
+ lines = File.read(player_log).split("\n")
49
+ lines[0].should end_with('Player is running.')
50
+
51
+ Notarius.configure('BIG') { |l| l.file = monster_log }
52
+ p.run 'Player is still running.'
53
+ lines = File.read(monster_log).split("\n")
54
+ lines[0].should end_with('Player is still running.')
55
+ end
56
+ end
57
+
58
+ it 'allows for unique namespaces' do
59
+ tempfiles('player', 'monster') do |player_log, monster_log|
60
+ Notarius.configure('Player') { |l| l.file = player_log }
61
+ player = Class.new do
62
+ include Notarius::Player
63
+ def initialize
64
+ log.info 'New player created!'
65
+ end
66
+ end
67
+
68
+ Notarius.configure('Monster') { |l| l.file = monster_log }
69
+ monster = Class.new do
70
+ include Notarius::Monster
71
+ def initialize
72
+ log.info 'New monster created!'
73
+ end
74
+ end
75
+
76
+ monster.new
77
+ player.new
78
+ File.read(monster_log).should_not include('New player created!')
79
+ end
80
+ end
81
+
82
+ it 'throws an error if separate namespaces log to the same file' do
83
+ tempfiles 'log' do |log|
84
+ Notarius.configure('Player') { |l| l.file = log }
85
+ Notarius.configure('Monster') { |l| l.file = log }
86
+
87
+ monster = Class.new do
88
+ include Notarius::Monster
89
+ def initialize
90
+ log.info 'New monster created!'
91
+ end
92
+ end
93
+
94
+ expect { monster.new }.to raise_error(Notarius::Exception)
95
+ end
96
+ end
97
+
98
+ describe '#configure' do
99
+ it 'handles lowercase namespaces gracefully' do
100
+ Notarius.configure 'little'
101
+ Notarius.const_defined?(:Little).should be_true
102
+ end
103
+
104
+ it 'throws an error if a namespace is empty' do
105
+ expect do
106
+ Notarius.configure ''
107
+ end.to raise_error(Notarius::Exception)
108
+ end
109
+
110
+ it 'throws an error if a namespace is nil' do
111
+ expect do
112
+ Notarius.configure nil
113
+ end.to raise_error(Notarius::Exception)
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,128 @@
1
+ require 'notarius/secretary'
2
+ require 'stringio'
3
+
4
+ describe Notarius::Secretary do
5
+ describe 'logging' do
6
+ let(:logger) { StringIO.new }
7
+ let(:secretary) do
8
+ c = Notarius::Config.new
9
+ c.file = logger
10
+ s = Notarius::Secretary.new
11
+ s.configure c
12
+ s
13
+ end
14
+
15
+ before :each do
16
+ logger.truncate 0
17
+ logger.rewind
18
+ end
19
+
20
+ it 'can log info messages' do
21
+ secretary.info 'info message'
22
+ logger.string.should match(/^INFO \[[^\]]+\] info message\n$/)
23
+ end
24
+
25
+ it 'can log warning messages' do
26
+ secretary.warn 'warning message'
27
+ logger.string.should match(/^WARN \[[^\]]+\] warning message\n$/)
28
+ end
29
+
30
+ it 'can log error messages' do
31
+ secretary.error 'error message'
32
+ logger.string.should match(/^ERROR \[[^\]]+\] error message\n$/)
33
+ end
34
+ end
35
+
36
+ describe '#configure' do
37
+ it 'logs to multiple outputs' do
38
+ io1 = StringIO.new
39
+ io2 = StringIO.new
40
+
41
+ secretary = Notarius::Secretary.new
42
+
43
+ config = Notarius::Config.new
44
+ config.console = io1
45
+ config.file = io2
46
+ secretary.configure config
47
+
48
+ secretary.info 'info message'
49
+ io1.string.should match(/^INFO \[[^\]]+\] info message\n$/)
50
+ io2.string.should match(/^INFO \[[^\]]+\] info message\n$/)
51
+ end
52
+
53
+ it 'defaults console to stdout' do
54
+ config = Notarius::Config.new
55
+ config.console = true
56
+ secretary = Notarius::Secretary.new
57
+
58
+ output = StringIO.new
59
+ stdout = $stdout
60
+ begin
61
+ $stdout = output
62
+ secretary.configure config
63
+ secretary.info 'message'
64
+ ensure
65
+ $stdout = stdout
66
+ end
67
+
68
+ output.string.should match(/^INFO \[[^\]]+\] message\n$/)
69
+ end
70
+
71
+ it 'removes a logger when no longer referenced' do
72
+ io1 = StringIO.new
73
+ io2 = StringIO.new
74
+
75
+ config1 = Notarius::Config.new
76
+ config1.console = io1
77
+ config1.file = io2
78
+
79
+ secretary = Notarius::Secretary.new
80
+ secretary.configure config1
81
+ secretary.info 'noodles'
82
+ io1.string.should match(/^INFO \[[^\]]+\] noodles\n$/)
83
+ io2.string.should match(/^INFO \[[^\]]+\] noodles\n$/)
84
+
85
+ secretary.configure Notarius::Config.new
86
+ secretary.info 'pasta'
87
+ io1.string.should_not match(/^INFO \[[^\]]+\] pasta\n$/)
88
+ io2.string.should_not match(/^INFO \[[^\]]+\] pasta\n$/)
89
+ end
90
+
91
+ it 'closes a logger when it removes it' do
92
+ config = Notarius::Config.new
93
+ config.console = StringIO.new
94
+
95
+ secretary = Notarius::Secretary.new
96
+ secretary.configure config
97
+ secretary.info 'noodles'
98
+ config.console.string.should match(/^INFO \[[^\]]+\] noodles\n$/)
99
+
100
+ secretary.configure Notarius::Config.new
101
+ secretary.info 'pasta'
102
+ config.console.should be_closed
103
+ end
104
+ end
105
+
106
+ it 'skips duplicate messages per logger' do
107
+ io1 = StringIO.new
108
+ io2 = StringIO.new
109
+
110
+ secretary = Notarius::Secretary.new
111
+
112
+ config1 = Notarius::Config.new
113
+ config1.console = io1
114
+ secretary.configure config1
115
+
116
+ secretary.error 'same message'
117
+
118
+ config2 = Notarius::Config.new
119
+ config2.console = io1
120
+ config2.file = io2
121
+ secretary.configure config2
122
+
123
+ secretary.info 'same message'
124
+
125
+ io1.string.should_not match(/^INFO \[[^\]]+\] same message\n$/)
126
+ io2.string.should match(/^INFO \[[^\]]+\] same message\n$/)
127
+ end
128
+ end
@@ -0,0 +1,2 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: notarius
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Frank Mitchell
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.9.2
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: 0.9.2
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '2.10'
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: '2.10'
46
+ - !ruby/object:Gem::Dependency
47
+ name: simplecov
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '0.6'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '0.6'
62
+ description: ! 'Notarius is a logging library with opinions. The word "notarius" is
63
+
64
+ Latin for "shorthand writer". To this end, Notarius does everything
65
+
66
+ possible to encourage you to write short useful log messages.
67
+
68
+ '
69
+ email: me@frankmitchell.org
70
+ executables: []
71
+ extensions: []
72
+ extra_rdoc_files: []
73
+ files:
74
+ - LICENSE.md
75
+ - README.md
76
+ - lib/notarius.rb
77
+ - lib/notarius/config.rb
78
+ - lib/notarius/exception.rb
79
+ - lib/notarius/formatter.rb
80
+ - lib/notarius/secretary.rb
81
+ - lib/notarius/version.rb
82
+ - notarius.gemspec
83
+ - rakefile.rb
84
+ - spec/config_spec.rb
85
+ - spec/formatter_spec.rb
86
+ - spec/notarius_spec.rb
87
+ - spec/secretary_spec.rb
88
+ - spec/simplecov_helper.rb
89
+ homepage: https://github.com/elimossinary/notarius
90
+ licenses:
91
+ - MIT
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: '1.9'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ! '>='
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubyforge_project:
110
+ rubygems_version: 1.8.24
111
+ signing_key:
112
+ specification_version: 3
113
+ summary: Notarius is a logging library with opinions.
114
+ test_files:
115
+ - spec/config_spec.rb
116
+ - spec/formatter_spec.rb
117
+ - spec/notarius_spec.rb
118
+ - spec/secretary_spec.rb
119
+ has_rdoc: