hatchet 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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