hatchet 0.0.2 → 0.0.3

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.
@@ -11,9 +11,20 @@ module Hatchet
11
11
  #
12
12
  LEVELS = [:debug, :info, :warn, :error, :fatal, :off]
13
13
 
14
- # Public: The Hash containing the log level configuration.
14
+ # Public: Returns the Hash containing the log level configuration.
15
15
  #
16
- attr_accessor :levels
16
+ def levels
17
+ @levels ||= {}
18
+ end
19
+
20
+ # Public: Sets the Hash containing the log level configuration.
21
+ #
22
+ # levels - The Hash to use as the log level configuration.
23
+ #
24
+ # Returns nothing.
25
+ def levels=(levels)
26
+ @levels = levels
27
+ end
17
28
 
18
29
  # Public: Set the lowest level of message to log for the given context.
19
30
  #
@@ -5,14 +5,13 @@ module Hatchet
5
5
  # Internal: Class that handles logging calls and distributes them to all its
6
6
  # appenders.
7
7
  #
8
- # Each logger has 6 methods. Those are, in decreasing order of severity:
8
+ # Each logger has 5 methods. Those are, in decreasing order of severity:
9
9
  #
10
10
  # * fatal
11
11
  # * error
12
12
  # * warn
13
13
  # * info
14
14
  # * debug
15
- # * trace
16
15
  #
17
16
  # All the methods have the same signature. You can either provide a message as
18
17
  # a direct string, or as a block to the method is lazily evaluated (this is
@@ -38,21 +37,30 @@ module Hatchet
38
37
  @appenders = appenders
39
38
  end
40
39
 
41
- # Public: Logs a message at the given level.
42
- #
43
- # message - An already evaluated message, usually a String (default: nil).
44
- # block - An optional block which will provide a message when invoked.
45
- #
46
- # One of message or block must be provided. If both are provided then the
47
- # block is preferred as it is assumed to provide more detail.
48
- #
49
- # Returns nothing.
50
- #
51
- [:trace, :debug, :info, :warn, :error, :fatal].each do |level|
40
+ [:debug, :info, :warn, :error, :fatal].each do |level|
41
+
42
+ # Public: Logs a message at the given level.
43
+ #
44
+ # message - An already evaluated message, usually a String (default: nil).
45
+ # block - An optional block which will provide a message when invoked.
46
+ #
47
+ # One of message or block must be provided. If both are provided then the
48
+ # block is preferred as it is assumed to provide more detail.
49
+ #
50
+ # Returns nothing.
51
+ #
52
52
  define_method level do |message = nil, &block|
53
53
  return unless message or block
54
54
  add level, Message.new(message, &block)
55
55
  end
56
+
57
+ # Public: Returns true if any of the appenders will log messages for the
58
+ # current context, otherwise returns false.
59
+ #
60
+ define_method "#{level}?" do
61
+ @appenders.any? { |appender| appender.enabled? level, @context }
62
+ end
63
+
56
64
  end
57
65
 
58
66
  private
@@ -67,7 +75,6 @@ module Hatchet
67
75
  # * warn
68
76
  # * info
69
77
  # * debug
70
- # * trace
71
78
  #
72
79
  # message - The message that will be logged by an appender when it is
73
80
  # configured to log at the given level or lower.
@@ -15,7 +15,7 @@ module Hatchet
15
15
  # Returns the context and message separated by a hypen.
16
16
  #
17
17
  def format(level, context, message)
18
- "#{timestamp} [#{thread_name}] #{level.to_s.upcase.ljust 5} #{message}"
18
+ "#{timestamp} [#{thread_name}] #{format_level level} #{context} - #{message}"
19
19
  end
20
20
 
21
21
  private
@@ -37,6 +37,12 @@ module Hatchet
37
37
  end
38
38
  end
39
39
 
40
+ # Private: Returns the level formatted for log output as a String.
41
+ #
42
+ def format_level(level)
43
+ level.to_s.upcase.ljust(5)
44
+ end
45
+
40
46
  end
41
47
 
42
48
  end
@@ -4,6 +4,6 @@ module Hatchet
4
4
 
5
5
  # Public: The version of Hatchet.
6
6
  #
7
- VERSION = "0.0.2"
7
+ VERSION = "0.0.3"
8
8
 
9
9
  end
@@ -0,0 +1,94 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require_relative 'spec_helper'
4
+
5
+ describe 'configuration' do
6
+ describe 'appender defaults' do
7
+ let(:set_levels) { { unique: :fake_level } }
8
+ let(:appender) { StoringAppender.new }
9
+
10
+ before do
11
+ Hatchet.configure do |config|
12
+ config.levels = set_levels
13
+ config.appenders << appender
14
+ end
15
+ end
16
+
17
+ it 'global levels when not explicitly set' do
18
+ assert set_levels == appender.levels
19
+ end
20
+
21
+ it 'formatter set as a StandardFormatter' do
22
+ assert appender.formatter.instance_of? StandardFormatter
23
+ end
24
+ end
25
+
26
+ describe 'appender overrides' do
27
+ let(:default_levels) { { unique: :fake_level } }
28
+ let(:appender_levels) { { appender: :faker_level } }
29
+ let(:appender_formatter) { SimpleFormatter.new }
30
+ let(:appender) do
31
+ StoringAppender.new do |app|
32
+ app.levels = appender_levels
33
+ app.formatter = appender_formatter
34
+ end
35
+ end
36
+
37
+ before do
38
+ Hatchet.configure do |config|
39
+ config.levels = default_levels
40
+ config.appenders << appender
41
+ end
42
+ end
43
+
44
+ it 'keeps the levels assigned to the appender' do
45
+ assert appender.levels == appender_levels
46
+ end
47
+
48
+ it 'keeps the formatter assigned to the appender' do
49
+ assert appender.formatter == appender_formatter
50
+ end
51
+ end
52
+
53
+ describe 'global default level' do
54
+ let(:appender) { StoringAppender.new }
55
+
56
+ before do
57
+ Hatchet.configure do |config|
58
+ config.appenders << appender
59
+ end
60
+ end
61
+
62
+ it 'set to info' do
63
+ assert appender.levels[nil] == :info
64
+ end
65
+ end
66
+
67
+ describe 'everything wires up' do
68
+ let(:appender) { StoringAppender.new }
69
+
70
+ before do
71
+ Hatchet.configure do |config|
72
+ config.appenders << appender
73
+ end
74
+ end
75
+
76
+ class Example
77
+ include Hatchet
78
+
79
+ def initialize
80
+ log.info { 'Creating instance' }
81
+ end
82
+ end
83
+
84
+ it 'should log a message when creating an instance' do
85
+ Example.new
86
+ msg = appender.messages.last
87
+
88
+ assert_equal :info, msg.level
89
+ assert_equal Example, msg.context
90
+ assert_equal 'Creating instance', msg.message.to_s
91
+ end
92
+ end
93
+ end
94
+
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'ostruct'
4
+
5
+ class LoggerDouble
6
+ attr_accessor :level
7
+ attr_accessor :formatter
8
+ attr_reader :messages
9
+
10
+ def initialize
11
+ @messages = []
12
+ end
13
+
14
+ [:debug, :info, :warn, :error, :fatal].each do |level|
15
+ define_method level do |message|
16
+ messages << OpenStruct.new(level: level, message: message)
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,10 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'ostruct'
4
+
5
+ class SimpleFormatter
6
+ def format(level, context, message)
7
+ OpenStruct.new(level: level, context: context, message: message)
8
+ end
9
+ end
10
+
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ class StoringAppender
4
+ include LevelManager
5
+
6
+ attr_accessor :formatter
7
+
8
+ attr_reader :messages
9
+
10
+ def initialize(default_level = nil)
11
+ @messages = []
12
+ @levels = {}
13
+ @levels[nil] = default_level unless default_level.nil?
14
+ yield self if block_given?
15
+ end
16
+
17
+ def add(level, context, message)
18
+ @messages << OpenStruct.new(level: level, context: context, message: message)
19
+ end
20
+ end
21
+
@@ -0,0 +1,65 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require_relative 'spec_helper'
4
+
5
+ describe LevelManager do
6
+ before do
7
+ @manager = Object.new
8
+ @manager.extend LevelManager
9
+ end
10
+
11
+ describe 'setting a default level of info' do
12
+ before do
13
+ @manager.level :info
14
+ end
15
+
16
+ it 'has info messages enabled for any context' do
17
+ assert @manager.enabled?(:info, 'Foo::Bar')
18
+ end
19
+
20
+ it 'has debug messages disabled for any context' do
21
+ refute @manager.enabled?(:debug, 'Foo::Bar')
22
+ end
23
+
24
+ describe 'lowering the threshold for a specific context' do
25
+ before do
26
+ @manager.level :debug, 'Foo::Bar'
27
+ end
28
+
29
+ it 'has debug messages enabled for that context' do
30
+ assert @manager.enabled?(:debug, 'Foo::Bar')
31
+ end
32
+
33
+ it 'has debug messages enabled for a child context' do
34
+ assert @manager.enabled?(:debug, 'Foo::Bar::Baz')
35
+ end
36
+
37
+ it 'has debug message disabled for the parent context' do
38
+ refute @manager.enabled?(:debug, 'Foo')
39
+ end
40
+ end
41
+
42
+ describe 'raising the threshold for a specific context' do
43
+ before do
44
+ @manager.level :fatal, 'Foo::Bar'
45
+ end
46
+
47
+ it 'has info messages disabled for that context' do
48
+ refute @manager.enabled?(:info, 'Foo::Bar')
49
+ end
50
+
51
+ it 'has fatal message enabled for that context' do
52
+ assert @manager.enabled?(:fatal, 'Foo')
53
+ end
54
+
55
+ it 'has info messages disabled for a child context' do
56
+ refute @manager.enabled?(:info, 'Foo::Bar::Baz')
57
+ end
58
+
59
+ it 'has info message enabled for the parent context' do
60
+ assert @manager.enabled?(:info, 'Foo')
61
+ end
62
+ end
63
+ end
64
+ end
65
+
@@ -0,0 +1,49 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require_relative 'spec_helper'
4
+
5
+ describe LoggerAppender do
6
+ let(:logger) { LoggerDouble.new }
7
+ let(:formatter) { SimpleFormatter.new }
8
+ let(:subject) do
9
+ LoggerAppender.new do |appender|
10
+ appender.logger = logger
11
+ appender.formatter = formatter
12
+ appender.levels = { nil => :info }
13
+ end
14
+ end
15
+
16
+ describe 'creating an appender' do
17
+ before { subject }
18
+
19
+ it 'removes filtering from the interal logger' do
20
+ assert_equal ::Logger::DEBUG, logger.level
21
+ end
22
+
23
+ it 'sets a formatter that returns the message with a trailing linebreak' do
24
+ msg = 'Plain message'
25
+ formatted_message = logger.formatter.call('sev', Time.now, 'prog', msg)
26
+ assert_equal "#{msg}\n", formatted_message
27
+ end
28
+ end
29
+
30
+ describe 'sending a message for a disabled level' do
31
+ it 'does not pass the message on to the logger' do
32
+ subject.add :debug, 'Context', 'Hello, World'
33
+ assert_empty logger.messages
34
+ end
35
+ end
36
+
37
+ describe 'sending a message of an enabled level' do
38
+ let(:message) { logger.messages.last }
39
+
40
+ it 'passes the message on to the logger' do
41
+ subject.add :info, 'Context', 'Hello, World'
42
+
43
+ assert :info, message.level
44
+ assert 'Context', message.context
45
+ assert 'Hello, World', message.message
46
+ end
47
+ end
48
+ end
49
+
@@ -0,0 +1,105 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require_relative 'spec_helper'
4
+
5
+ describe Hatchet::Logger do
6
+ let(:appender) { StoringAppender.new :debug }
7
+ let(:appenders) { [appender] }
8
+ let(:context) { Context::Class.new }
9
+ let(:subject) { Hatchet::Logger.new context, appenders }
10
+
11
+ ALL_LEVELS.each do |level|
12
+ describe "receiving #{level} messages" do
13
+ let(:message) { "#{level} message" }
14
+
15
+ it 'should store pre-evaluated messages' do
16
+ subject.send level, message
17
+ received = appender.messages.last
18
+
19
+ assert level == received.level
20
+ assert context.class == received.context
21
+ assert message == received.message.to_s
22
+ end
23
+
24
+ it 'should store block messages' do
25
+ subject.send(level) { message }
26
+ received = appender.messages.last
27
+
28
+ assert level == received.level
29
+ assert context.class == received.context
30
+ assert message == received.message.to_s
31
+ end
32
+ end
33
+ end
34
+
35
+ describe 'responding to level checks' do
36
+ let(:noop_appender) { StoringAppender.new :off }
37
+
38
+ before do
39
+ appenders << noop_appender
40
+ end
41
+
42
+ ALL_LEVELS.each do |level|
43
+ it "responds to #{level} as one of the appenders does" do
44
+ assert subject.send "#{level}?"
45
+ end
46
+ end
47
+
48
+ describe 'with no appenders responding to debug' do
49
+ before do
50
+ appenders.clear
51
+ appenders << StoringAppender.new(:info)
52
+ appenders << StoringAppender.new(:warn)
53
+ end
54
+
55
+ it 'does not respond to debug' do
56
+ refute subject.debug?
57
+ end
58
+
59
+ it 'does respond to info' do
60
+ assert subject.info?
61
+ end
62
+ end
63
+ end
64
+
65
+ describe 'empty messages' do
66
+ it 'not passed on to appender' do
67
+ subject.fatal
68
+
69
+ assert_empty appender.messages
70
+ end
71
+ end
72
+
73
+ describe 'naming context' do
74
+ let(:context_name) do
75
+ subject.fatal 'Message'
76
+ # Get the context passed to the appender.
77
+ appender.messages.last.context.to_s
78
+ end
79
+
80
+ describe 'for instances of a class' do
81
+ let(:context) { Context::Class.new }
82
+
83
+ it 'reports class name' do
84
+ assert_equal 'Context::Class', context_name
85
+ end
86
+ end
87
+
88
+ describe 'for modules' do
89
+ let(:context) { Context }
90
+
91
+ it 'reports module name' do
92
+ assert_equal 'Context', context_name
93
+ end
94
+ end
95
+
96
+ describe 'for the initial execution context of Ruby' do
97
+ let(:context) { INITIAL_EXECUTION_CONTEXT }
98
+
99
+ it 'reports "main"' do
100
+ assert_equal 'main', context_name
101
+ end
102
+ end
103
+ end
104
+ end
105
+
@@ -0,0 +1,46 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require_relative 'spec_helper'
4
+
5
+ describe Message do
6
+ describe 'providing an evaluted message' do
7
+ let(:subject) { Message.new 'Evaluated' }
8
+
9
+ it 'returns the given message' do
10
+ assert_equal 'Evaluated', subject.to_s
11
+ end
12
+ end
13
+
14
+ describe 'providing a block message' do
15
+ let(:subject) do
16
+ @evaluated = 0
17
+ Message.new do
18
+ @evaluated += 1
19
+ 'Block'
20
+ end
21
+ end
22
+
23
+ it 'returns the result of evaluating the block' do
24
+ assert_equal 'Block', subject.to_s
25
+ end
26
+
27
+ it 'only evaluates the block once for multiple calls' do
28
+ subject.to_s
29
+ subject.to_s
30
+ assert_equal 1, @evaluated
31
+ end
32
+ end
33
+
34
+ describe 'providing both an evaluated and block message' do
35
+ let(:subject) do
36
+ Message.new 'Evaluated' do
37
+ 'Block'
38
+ end
39
+ end
40
+
41
+ it 'returns the result of evaluating the block' do
42
+ assert_equal 'Block', subject.to_s
43
+ end
44
+ end
45
+ end
46
+
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'minitest/autorun'
4
+ require 'minitest/spec'
5
+ require_relative '../lib/hatchet'
6
+
7
+ # Avoid having to refer to everything absolutely all the time.
8
+ include Hatchet
9
+
10
+ require_relative 'helpers/logger_double'
11
+ require_relative 'helpers/simple_formatter'
12
+ require_relative 'helpers/storing_appender'
13
+
14
+ INITIAL_EXECUTION_CONTEXT = self
15
+
16
+ ALL_LEVELS = [:debug, :info, :warn, :error, :fatal]
17
+
18
+ module Context
19
+ class Class ; end
20
+ end
21
+
@@ -0,0 +1,71 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require_relative 'spec_helper'
4
+ require 'date'
5
+
6
+ describe StandardFormatter do
7
+ TIME_FORMAT = '%Y-%m-%d %H:%M:%S.%L'
8
+
9
+ let(:subject) { StandardFormatter.new }
10
+
11
+ describe 'when formatting a message' do
12
+ before do
13
+ @message = 'Hello, World'
14
+ @context = 'Custom::Context'
15
+ @level = :info
16
+ @formatted_message = subject.format(@level, @context, @message)
17
+ end
18
+
19
+ it 'outputs the timestamp in %Y-%m-%d %H:%M:%S.%L format' do
20
+ # Dear god parsing times in Ruby is a pain so create a correctly formatted
21
+ # string then parse them both to compare them, otherwise timezones get
22
+ # crazy.
23
+ expected = to_time format_as_string(Time.now)
24
+ actual = to_time @formatted_message
25
+ diff = expected - actual
26
+
27
+ assert diff >= 0, 'must be no later expected'
28
+ assert diff <= 50, 'must be very recent'
29
+ end
30
+
31
+ it 'outputs the pid when run in the main thread' do
32
+ assert_equal Process.pid.to_s, thread_name
33
+ end
34
+
35
+ it 'converts the level to uppercase' do
36
+ assert_includes @formatted_message, @level.to_s.upcase
37
+ end
38
+
39
+ it 'formats the message after the time as expected' do
40
+ expected = "[#{Process.pid}] #{@level.to_s.upcase.ljust 5} #{@context} - #{@message}"
41
+ actual_without_time = @formatted_message[24..-1]
42
+
43
+ assert_equal expected, actual_without_time
44
+ end
45
+
46
+ describe 'when running in a thread' do
47
+ before do
48
+ @thread = Thread.new { subject.format(:info, @context, @message) }
49
+ @formatted_message = @thread.value
50
+ end
51
+
52
+ it 'outputs the pid and the thread object_id' do
53
+ assert_equal "#{Process.pid}##{@thread.object_id}", thread_name
54
+ end
55
+ end
56
+
57
+ def format_as_string(time)
58
+ time.strftime TIME_FORMAT
59
+ end
60
+
61
+ def to_time(string)
62
+ DateTime.strptime string[0..23], TIME_FORMAT
63
+ end
64
+
65
+ def thread_name
66
+ match = /\[([^\]]+)\]/.match(@formatted_message)
67
+ match[1]
68
+ end
69
+ end
70
+
71
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hatchet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,9 +9,9 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-07 00:00:00.000000000 Z
12
+ date: 2012-07-08 00:00:00.000000000 Z
13
13
  dependencies: []
14
- description: Ruby logging library that provides the ability to add class/module specific
14
+ description: Logging library that provides the ability to add class/module specific
15
15
  filters
16
16
  email:
17
17
  - garry@robustsoftware.co.uk
@@ -27,7 +27,16 @@ files:
27
27
  - lib/hatchet/standard_formatter.rb
28
28
  - lib/hatchet/version.rb
29
29
  - lib/hatchet.rb
30
- - test/experiment.rb
30
+ - spec/configuration_spec.rb
31
+ - spec/helpers/logger_double.rb
32
+ - spec/helpers/simple_formatter.rb
33
+ - spec/helpers/storing_appender.rb
34
+ - spec/level_manager_spec.rb
35
+ - spec/logger_appender_spec.rb
36
+ - spec/logger_spec.rb
37
+ - spec/message_spec.rb
38
+ - spec/spec_helper.rb
39
+ - spec/standard_formatter_spec.rb
31
40
  - LICENSE
32
41
  homepage: https://github.com/gshutler/hatchet
33
42
  licenses: []
@@ -41,24 +50,26 @@ required_ruby_version: !ruby/object:Gem::Requirement
41
50
  - - ! '>='
42
51
  - !ruby/object:Gem::Version
43
52
  version: '0'
44
- segments:
45
- - 0
46
- hash: -2622354468754209643
47
53
  required_rubygems_version: !ruby/object:Gem::Requirement
48
54
  none: false
49
55
  requirements:
50
56
  - - ! '>='
51
57
  - !ruby/object:Gem::Version
52
58
  version: '0'
53
- segments:
54
- - 0
55
- hash: -2622354468754209643
56
59
  requirements: []
57
60
  rubyforge_project:
58
- rubygems_version: 1.8.24
61
+ rubygems_version: 1.8.17
59
62
  signing_key:
60
63
  specification_version: 3
61
- summary: Ruby logging library that provides the ability to add class/module specific
62
- filters
64
+ summary: Logging library that provides the ability to add class/module specific filters
63
65
  test_files:
64
- - test/experiment.rb
66
+ - spec/configuration_spec.rb
67
+ - spec/helpers/logger_double.rb
68
+ - spec/helpers/simple_formatter.rb
69
+ - spec/helpers/storing_appender.rb
70
+ - spec/level_manager_spec.rb
71
+ - spec/logger_appender_spec.rb
72
+ - spec/logger_spec.rb
73
+ - spec/message_spec.rb
74
+ - spec/spec_helper.rb
75
+ - spec/standard_formatter_spec.rb
@@ -1,47 +0,0 @@
1
- require_relative '../lib/hatchet'
2
- require 'logger'
3
-
4
- module Namespace
5
- class Foo
6
- include Hatchet
7
-
8
- def work
9
- log.fatal { "Fatal message will be shown" }
10
- end
11
- end
12
-
13
- module Something
14
- extend Hatchet
15
-
16
- def self.work
17
- log.info { "Info message will be shown" }
18
- logger.debug { "Debug message won't be shown" }
19
- end
20
-
21
- class Nested
22
- include Hatchet
23
-
24
- def work
25
- log.debug { "Debug message will be shown due to override" }
26
- end
27
- end
28
- end
29
- end
30
-
31
- Hatchet.configure do |config|
32
- config.level :info
33
- config.level :debug, Namespace::Something::Nested
34
-
35
- config.appenders << Hatchet::LoggerAppender.new do |appender|
36
- appender.logger = Logger.new('log/test.log')
37
- end
38
- end
39
-
40
- include Hatchet
41
-
42
- log.warn 'Warn message will be shown'
43
- thread = Thread.new { Namespace::Foo.new.work }
44
- Namespace::Something.work
45
- Namespace::Something::Nested.new.work
46
-
47
- thread.join