notarius 0.1.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/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: