hatchet 0.1.0 → 0.2.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/RELEASE.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Release notes
2
2
 
3
+ ## 0.2.0
4
+
5
+ * Added nested diagnostic context and Rack middleware to clear it between
6
+ requests
7
+
8
+ ### Note
9
+
10
+ The `Hatchet::Message` constructor has been altered, going forward it will take
11
+ a Hash of arguments instead of fixed arguments. It is currently backwards
12
+ compatible but this will likely be dropped for 1.0.0 so it is advised you update
13
+ your libraries now.
14
+
15
+ This should only affect custom formatters which may want to take advantage of
16
+ the nested diagnostic context which is now available anyway.
17
+
3
18
  ## 0.1.0
4
19
 
5
20
  No changes from 0.0.20, just time for a minor version release.
@@ -1,7 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
- require 'logger'
4
-
5
3
  require_relative 'hatchet/level_manager'
6
4
  require_relative 'hatchet/backtrace_formatter'
7
5
  require_relative 'hatchet/thread_name_formatter'
@@ -10,6 +8,8 @@ require_relative 'hatchet/delegating_formatter'
10
8
  require_relative 'hatchet/hatchet_logger'
11
9
  require_relative 'hatchet/logger_appender'
12
10
  require_relative 'hatchet/message'
11
+ require_relative 'hatchet/middleware'
12
+ require_relative 'hatchet/nested_diagnostic_context'
13
13
  require_relative 'hatchet/plain_formatter'
14
14
  require_relative 'hatchet/simple_formatter'
15
15
  require_relative 'hatchet/standard_formatter'
@@ -66,7 +66,7 @@ module Hatchet
66
66
  # Returns a HatchetLogger for the object.
67
67
  #
68
68
  def logger
69
- @_hatchet_logger ||= HatchetLogger.new self, Hatchet.configuration
69
+ @_hatchet_logger ||= HatchetLogger.new(self, Hatchet.configuration, Hatchet::NestedDiagnosticContext.current)
70
70
  end
71
71
 
72
72
  # Public: Returns a HatchetLogger for the object.
@@ -1,5 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
+ require 'logger'
4
+
3
5
  module Hatchet
4
6
 
5
7
  # Public: Class that handles logging calls and distributes them to all its
@@ -60,15 +62,21 @@ module Hatchet
60
62
  Logger::FATAL => :fatal
61
63
  }
62
64
 
65
+ # Public: Gets the NestedDiagnosticContext for the logger.
66
+ #
67
+ attr_reader :ndc
68
+
63
69
  # Internal: Creates a new logger.
64
70
  #
65
71
  # host - The object the logger gains its context from.
66
72
  # configuration - The configuration of Hatchet.
73
+ # ndc - The nested diagnostic context of the logger.
67
74
  #
68
- def initialize(host, configuration)
69
- @context = context host
75
+ def initialize(host, configuration, ndc)
76
+ @context = host_name(host)
70
77
  @configuration = configuration
71
78
  @appenders = configuration.appenders
79
+ @ndc = ndc
72
80
  end
73
81
 
74
82
  [:debug, :info, :warn, :error, :fatal].each do |level|
@@ -103,7 +111,7 @@ module Hatchet
103
111
  #
104
112
  define_method level do |message = nil, error = nil, &block|
105
113
  return unless message or block
106
- add level, Message.new(message, error, &block)
114
+ add level, Message.new(ndc: @ndc.context.clone, message: message, error: error, &block)
107
115
  end
108
116
 
109
117
  # Public: Returns true if any of the appenders will log messages for the
@@ -193,7 +201,7 @@ module Hatchet
193
201
  # Ruby, the host itself when the host is a module, otherwise the object's
194
202
  # class.
195
203
  #
196
- def context(host)
204
+ def host_name(host)
197
205
  if host.inspect == 'main'
198
206
  'main'
199
207
  elsif [Module, Class].include? host.class
@@ -8,6 +8,9 @@ module Hatchet
8
8
  # If an error is associated with the message this will be available via the
9
9
  # #error attribute.
10
10
  #
11
+ # The nested diagnostic context of the message will be availble via the #ndc
12
+ # attribute.
13
+ #
11
14
  # Blocks will be lazily evaluated once for all appenders when required.
12
15
  #
13
16
  class Message
@@ -16,27 +19,61 @@ module Hatchet
16
19
  #
17
20
  attr_reader :error
18
21
 
19
- # Internal: Creates a new message.
22
+ # Public: Gets the nested diagnostic context values.
23
+ #
24
+ attr_reader :ndc
25
+
26
+ # Public: Creates a new message.
27
+ #
28
+ # args - The Hash used to provide context for the message (default: {}):
29
+ # :ndc - An Array of nested diagnostic context values
30
+ # (default: []).
31
+ # :message - An already evaluated message, usually a String
32
+ # (default: nil).
33
+ # :error - An error that is associated with the message
34
+ # (default: nil).
35
+ # block - An optional block which will provide a message when invoked.
36
+ #
37
+ # Examples
38
+ #
39
+ # Message.new(ndc: [], message: "Evaluated message", error: e)
40
+ # Message.new(ndc: %w{Foo Bar}) { "Lazily evaluated message" }
41
+ #
42
+ # The signature of the constructor was originally:
20
43
  #
21
44
  # message - An already evaluated message, usually a String (default: nil).
22
45
  # error - An error that is associated with the message (default: nil).
23
46
  # block - An optional block which will provide a message when invoked.
24
47
  #
25
- # One of message or block must be provided. If both are provided then the
26
- # block is preferred as it is assumed to provide more detail.
48
+ # This format is also supported for compatibility to version 0.1.0 and below
49
+ # and will be deprecated in the future.
27
50
  #
28
51
  # Examples
29
52
  #
30
- # Message.new "Evaluated message"
53
+ # Message.new("Evaluated message", e)
31
54
  # Message.new { "Lazily evaluated message" }
32
55
  #
33
- def initialize(message = nil, error = nil, &block)
34
- @block = block
35
- @error = error
36
- @message = message unless @block
56
+ # One of message or block must be provided. If both are provided then the
57
+ # block is preferred as it is assumed to provide more detail.
58
+ #
59
+ def initialize(args = {}, error = nil, &block)
60
+ if args.kind_of? Hash
61
+ # If args is a Hash then using new constructor format or no parameters
62
+ # specified. Either way, use the new format.
63
+ @ndc = args[:ndc] || []
64
+ @error = args[:error]
65
+ @message = args[:message] unless block
66
+ else
67
+ # Otherwise assume the old format and coerce args accordingly.
68
+ @ndc = []
69
+ @error = error
70
+ @message = args unless block
71
+ end
72
+
73
+ @block = block
37
74
  end
38
75
 
39
- # Internal: Returns the String representation of the message.
76
+ # Public: Returns the String representation of the message.
40
77
  #
41
78
  def to_s
42
79
  @message ||= @block.call
@@ -0,0 +1,34 @@
1
+ module Hatchet
2
+
3
+ # Public: Middleware for making sure the nested diagnostic context is cleared
4
+ # between requests.
5
+ #
6
+ class Middleware
7
+ include Hatchet
8
+
9
+ # Public: Creates a new instance of the middleware, wrapping the given
10
+ # application.
11
+ #
12
+ # app - The application to wrap.
13
+ #
14
+ def initialize(app)
15
+ @app = app
16
+ end
17
+
18
+ # Public: Calls the wrapped application with the given environment, ensuring
19
+ # the nested diagnostic context is cleared afterwards.
20
+ #
21
+ # env - The enviroment Hash for the request.
22
+ #
23
+ # Returns the status, headers, body Array returned by the wrapped
24
+ # application.
25
+ #
26
+ def call(env)
27
+ @app.call(env)
28
+ ensure
29
+ log.ndc.clear!
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,160 @@
1
+ module Hatchet
2
+
3
+ # Public: Class that manages the nested diagnostic context for a thread.
4
+ #
5
+ # All access to this class is performed through internal classes.
6
+ #
7
+ class NestedDiagnosticContext
8
+
9
+ # Internal: Gets the current context stack.
10
+ #
11
+ attr_reader :context
12
+
13
+ # Internal: Gets the NestedDiagnosticContext for the current thread, lazily
14
+ # initializing it as necessary.
15
+ #
16
+ def self.current
17
+ Thread.current[:hatchet_ndc] ||= NestedDiagnosticContext.new
18
+ end
19
+
20
+ # Internal: Creates a new instance of the class.
21
+ #
22
+ def initialize
23
+ clear!
24
+ end
25
+
26
+ # Public: Adds one or more messages onto the context stack.
27
+ #
28
+ # values - One or more messages to add to the context stack.
29
+ #
30
+ # Returns nothing.
31
+ #
32
+ def push(*values)
33
+ @context.push(*values)
34
+ end
35
+
36
+ # Public: Removes one or more message from the context stack.
37
+ #
38
+ # n - The number of messages to remove from the context stack (default:
39
+ # nil). If no number is provided then one message will be removed.
40
+ #
41
+ # Returns the message or messages removed from the context stack. If n was
42
+ # not specified it will return a single message, otherwise it will return an
43
+ # Array of up to n messages.
44
+ #
45
+ def pop(n = nil)
46
+ if n
47
+ @context.pop(n)
48
+ else
49
+ @context.pop
50
+ end
51
+ end
52
+
53
+ # Public: Adds one more or message onto the context stack for the scope of
54
+ # the given block.
55
+ #
56
+ # values - One or more messages to add to the context stack for the scope of
57
+ # the given block.
58
+ # block - The block to execute with the additional messages.
59
+ #
60
+ # Returns the result of calling the block.
61
+ #
62
+ def scope(*values, &block)
63
+ before = @context.clone
64
+ push(*values)
65
+ block.call
66
+ ensure
67
+ @context = before
68
+ end
69
+
70
+ # Public: Clears all messages from the context stack.
71
+ #
72
+ # Intend for use when the current thread is, or may, be reused in the future
73
+ # and the accumlated context is no longer wanted.
74
+ #
75
+ # Returns nothing.
76
+ #
77
+ def clear!
78
+ @context = ContextStack.new
79
+ nil
80
+ end
81
+
82
+ # Public: Class for holding the context stack of a NestedDiagnosticContext.
83
+ #
84
+ # Deliberately intended to have a similar API to Array to make testing
85
+ # easier.
86
+ #
87
+ class ContextStack
88
+
89
+ # Internal: Gets the internal stack.
90
+ #
91
+ attr_reader :stack
92
+
93
+ # Internal: Creates a new instance.
94
+ #
95
+ # stack - An Array of values to initialize the stack with (default: []).
96
+ #
97
+ def initialize(stack = [])
98
+ @stack = stack
99
+ end
100
+
101
+ # Public: Returns true if the stack contains any messages, otherwise
102
+ # returns false.
103
+ #
104
+ def any?
105
+ @stack.size != 0
106
+ end
107
+
108
+ # Internal: Returns a clone of the stack.
109
+ #
110
+ def clone
111
+ ContextStack.new(@stack.clone)
112
+ end
113
+
114
+ # Public: Returns a String created by converting each message of the stack
115
+ # to a String, separated by separator.
116
+ #
117
+ # separator - The String to separate the messages of the stack with
118
+ # (default: $,).
119
+ #
120
+ # Returns a String created by converting each message of the stack to a
121
+ # String, separated by separator.
122
+ #
123
+ def join(separator = $,)
124
+ @stack.join(separator)
125
+ end
126
+
127
+ # Internal: Pushes the given messages onto the stack.
128
+ #
129
+ # values - One or more messages to add to the context stack.
130
+ #
131
+ # Returns nothing.
132
+ #
133
+ def push(*values)
134
+ @stack.push(*values)
135
+ nil
136
+ end
137
+
138
+ # Internal: Removes one or more message from the stack.
139
+ #
140
+ # n - The number of messages to remove from the cstack (default: nil). If
141
+ # no number is provided then one message will be removed.
142
+ #
143
+ # Returns the message or messages removed from the context stack. If n was
144
+ # not specified it will return a single message, otherwise it will return
145
+ # an Array of up to n messages.
146
+ #
147
+ def pop(n = nil)
148
+ if n
149
+ @stack.pop(n)
150
+ else
151
+ @stack.pop
152
+ end
153
+ end
154
+
155
+ end
156
+
157
+ end
158
+
159
+ end
160
+
@@ -15,6 +15,13 @@ module Hatchet
15
15
  #
16
16
  self.config.hatchet = Hatchet.configuration
17
17
 
18
+ # Add the Hatchet::Middleware to the middleware stack to enable nested
19
+ # diagnostic context clearance between requests.
20
+ #
21
+ initializer "hatchet_railtie.insert_middleware" do |app|
22
+ app.config.middleware.use Hatchet::Middleware
23
+ end
24
+
18
25
  # Wrap the default Rails.logger, Rails.application.assets.logger, and all
19
26
  # log subscribers found in ActiveSupport::LogSubscriber.log_subscribers
20
27
  # collection on initialization.
@@ -31,11 +31,12 @@ module Hatchet
31
31
  #
32
32
  def format(level, context, message)
33
33
  msg = message.to_s.strip
34
+ thread = thread_context ? "[#{thread_name}] - " : nil
34
35
 
35
- if thread_context
36
- msg = "[#{thread_name}] - #{level.to_s.upcase} - #{context} - #{msg}"
36
+ if message.ndc.any?
37
+ msg = "#{thread}#{level.to_s.upcase} - #{context} #{message.ndc.join(' ')} - #{msg}"
37
38
  else
38
- msg = "#{level.to_s.upcase} - #{context} - #{msg}"
39
+ msg = "#{thread}#{level.to_s.upcase} - #{context} - #{msg}"
39
40
  end
40
41
 
41
42
  with_backtrace(message, msg)
@@ -12,6 +12,7 @@ module Hatchet
12
12
  #
13
13
  def initialize
14
14
  @secs = 0
15
+ @level_cache = {}
15
16
  end
16
17
 
17
18
  # Public: Returns the formatted message.
@@ -29,7 +30,12 @@ module Hatchet
29
30
  #
30
31
  def format(level, context, message)
31
32
  msg = message.to_s.strip
32
- msg = "#{timestamp} [#{thread_name}] #{format_level level} #{context} - #{msg}"
33
+
34
+ if message.ndc.any?
35
+ msg = "#{timestamp} [#{thread_name}] #{format_level(level)} #{context} #{message.ndc.join(' ')} - #{msg}"
36
+ else
37
+ msg = "#{timestamp} [#{thread_name}] #{format_level(level)} #{context} - #{msg}"
38
+ end
33
39
 
34
40
  with_backtrace(message, msg)
35
41
  end
@@ -58,7 +64,7 @@ module Hatchet
58
64
  # Private: Returns the level formatted for log output as a String.
59
65
  #
60
66
  def format_level(level)
61
- level.to_s.upcase.ljust(5)
67
+ @level_cache[level] ||= level.to_s.upcase.ljust(5)
62
68
  end
63
69
 
64
70
  end
@@ -4,6 +4,6 @@ module Hatchet
4
4
 
5
5
  # Public: The version of Hatchet.
6
6
  #
7
- VERSION = '0.1.0'
7
+ VERSION = '0.2.0'
8
8
 
9
9
  end
@@ -17,5 +17,6 @@ class StoringAppender
17
17
  def add(level, context, message)
18
18
  @messages << OpenStruct.new(level: level, context: context, message: message)
19
19
  end
20
+
20
21
  end
21
22
 
@@ -8,9 +8,15 @@ describe HatchetLogger do
8
8
  let(:appenders) { [appender, disabled_appender] }
9
9
  let(:configuration) { Configuration.new }
10
10
  let(:context) { Context::Class.new }
11
+ let(:ndc) { NestedDiagnosticContext.new }
12
+
13
+ def last_message
14
+ appender.messages.last.message
15
+ end
16
+
11
17
  let(:subject) do
12
18
  configuration.appenders.push(*appenders)
13
- HatchetLogger.new context, configuration
19
+ HatchetLogger.new context, configuration, ndc
14
20
  end
15
21
 
16
22
  ALL_LEVELS.each do |level|
@@ -139,5 +145,81 @@ describe HatchetLogger do
139
145
  end
140
146
  end
141
147
  end
148
+
149
+ describe 'nested diagnostic context' do
150
+
151
+ describe 'pushing context' do
152
+
153
+ it 'passes the context with the message' do
154
+ subject.ndc.push(:foo)
155
+ subject.info 'Message'
156
+ assert_equal [:foo], last_message.ndc.stack
157
+ end
158
+
159
+ it 'stacks contexts with the message' do
160
+ subject.ndc.push(:foo, :bar, :baz)
161
+ subject.info 'Message'
162
+ assert_equal [:foo, :bar, :baz], last_message.ndc.stack
163
+ end
164
+
165
+ it 'pops contexts individually' do
166
+ subject.ndc.push(:foo, :bar, :baz)
167
+
168
+ subject.info 'Message'
169
+ assert_equal [:foo, :bar, :baz], last_message.ndc.stack
170
+
171
+ subject.ndc.pop
172
+ subject.info 'Message'
173
+ assert_equal [:foo, :bar], last_message.ndc.stack
174
+
175
+ subject.ndc.pop
176
+ subject.info 'Message'
177
+ assert_equal [:foo], last_message.ndc.stack
178
+
179
+ subject.ndc.pop
180
+ subject.info 'Message'
181
+ assert_equal [], last_message.ndc.stack
182
+ end
183
+
184
+ it 'pops a specified number of contexts' do
185
+ subject.ndc.push(:foo, :bar, :baz)
186
+
187
+ subject.info 'Message'
188
+ assert_equal [:foo, :bar, :baz], last_message.ndc.stack
189
+
190
+ subject.ndc.pop(2)
191
+ subject.info 'Message'
192
+ assert_equal [:foo], last_message.ndc.stack
193
+
194
+ subject.ndc.pop(2)
195
+ subject.info 'Message'
196
+ assert_equal [], last_message.ndc.stack
197
+ end
198
+
199
+ it 'scopes contexts when used with blocks' do
200
+ subject.ndc.scope(:foo) do
201
+ subject.info 'Message'
202
+ assert_equal [:foo], last_message.ndc.stack
203
+ end
204
+
205
+ subject.info 'Message'
206
+ assert_equal [], last_message.ndc.stack
207
+ end
208
+
209
+ it 'can clear all contexts' do
210
+ subject.ndc.push(:foo, :bar, :baz)
211
+
212
+ subject.info 'Message'
213
+ assert_equal [:foo, :bar, :baz], last_message.ndc.stack
214
+
215
+ subject.ndc.clear!
216
+
217
+ subject.info 'Message'
218
+ assert_equal [], last_message.ndc.stack
219
+ end
220
+
221
+ end
222
+
223
+ end
142
224
  end
143
225
 
@@ -4,7 +4,7 @@ require_relative 'spec_helper'
4
4
 
5
5
  describe Message do
6
6
  describe 'providing an evaluted message' do
7
- let(:subject) { Message.new 'Evaluated' }
7
+ let(:subject) { Message.new(ndc: [], message: 'Evaluated') }
8
8
 
9
9
  it 'returns the given message' do
10
10
  assert_equal 'Evaluated', subject.to_s
@@ -14,7 +14,7 @@ describe Message do
14
14
  describe 'providing a block message' do
15
15
  let(:subject) do
16
16
  @evaluated = 0
17
- Message.new do
17
+ Message.new(ndc: []) do
18
18
  @evaluated += 1
19
19
  'Block'
20
20
  end
@@ -33,7 +33,7 @@ describe Message do
33
33
 
34
34
  describe 'providing both an evaluated and block message' do
35
35
  let(:subject) do
36
- Message.new 'Evaluated' do
36
+ Message.new(ndc: [], message: 'Evaluated') do
37
37
  'Block'
38
38
  end
39
39
  end
@@ -42,5 +42,47 @@ describe Message do
42
42
  assert_equal 'Block', subject.to_s
43
43
  end
44
44
  end
45
+
46
+ describe 'supporting the old constructor format' do
47
+ describe 'providing an evaluted message' do
48
+ let(:subject) { Message.new('Evaluated') }
49
+
50
+ it 'returns the given message' do
51
+ assert_equal 'Evaluated', subject.to_s
52
+ end
53
+ end
54
+
55
+ describe 'providing a block message' do
56
+ let(:subject) do
57
+ @evaluated = 0
58
+ Message.new do
59
+ @evaluated += 1
60
+ 'Block'
61
+ end
62
+ end
63
+
64
+ it 'returns the result of evaluating the block' do
65
+ assert_equal 'Block', subject.to_s
66
+ end
67
+
68
+ it 'only evaluates the block once for multiple calls' do
69
+ subject.to_s
70
+ subject.to_s
71
+ assert_equal 1, @evaluated
72
+ end
73
+ end
74
+
75
+ describe 'providing both an evaluated and block message' do
76
+ let(:subject) do
77
+ Message.new('Evaluated') do
78
+ 'Block'
79
+ end
80
+ end
81
+
82
+ it 'returns the result of evaluating the block' do
83
+ assert_equal 'Block', subject.to_s
84
+ end
85
+ end
86
+ end
45
87
  end
46
88
 
@@ -0,0 +1,115 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe Middleware do
4
+
5
+ class App
6
+ include Hatchet
7
+
8
+ def initialize
9
+ @request = 1
10
+ end
11
+
12
+ def call(env)
13
+ log.ndc.push(@request)
14
+
15
+ log.info "Start"
16
+
17
+ one = One.new
18
+ one.run(env)
19
+
20
+ log.ndc.push(:end)
21
+ log.info "Finish"
22
+
23
+ @request += 1
24
+
25
+ [:status, :headers, :body]
26
+ end
27
+
28
+ class One
29
+ include Hatchet
30
+
31
+ def run(env)
32
+ log.ndc.push(:one)
33
+ log.info env[:one]
34
+ end
35
+ end
36
+
37
+ end
38
+
39
+ let(:storing_appender) { StoringAppender.new :debug }
40
+ let(:messages) { storing_appender.messages }
41
+
42
+ let(:subject) do
43
+ Hatchet.configure do |config|
44
+ config.reset!
45
+ config.appenders << storing_appender
46
+ end
47
+
48
+ app = App.new
49
+ Middleware.new(app)
50
+ end
51
+
52
+ before do
53
+ messages.clear
54
+ end
55
+
56
+ describe "maintaining the result" do
57
+
58
+ before do
59
+ @status, @headers, @body = subject.call(one: 'Testing')
60
+ end
61
+
62
+ it "maintains the status" do
63
+ assert_equal :status, @status
64
+ end
65
+
66
+ it "maintains the headers" do
67
+ assert_equal :headers, @headers
68
+ end
69
+
70
+ it "maintains the body" do
71
+ assert_equal :body, @body
72
+ end
73
+
74
+ end
75
+
76
+ describe "accumulating context" do
77
+
78
+ before do
79
+ subject.call(one: 'Testing')
80
+ end
81
+
82
+ it "logs accumulating context" do
83
+ assert_equal "Start", messages[0].message.to_s
84
+ assert_equal [1], messages[0].message.ndc.stack
85
+
86
+ assert_equal 'Testing', messages[1].message.to_s
87
+ assert_equal [1, :one], messages[1].message.ndc.stack
88
+
89
+ assert_equal 'Finish', messages[2].message.to_s
90
+ assert_equal [1, :one, :end], messages[2].message.ndc.stack
91
+ end
92
+
93
+ end
94
+
95
+ describe "clearing context between requests" do
96
+
97
+ before do
98
+ subject.call(one: 'Testing')
99
+ subject.call(one: 'TestingAgain')
100
+ end
101
+
102
+ it "clears context from previous requests" do
103
+ assert_equal "Start", messages[3].message.to_s
104
+ assert_equal [2], messages[3].message.ndc.stack
105
+
106
+ assert_equal 'TestingAgain', messages[4].message.to_s
107
+ assert_equal [2, :one], messages[4].message.ndc.stack
108
+
109
+ assert_equal 'Finish', messages[5].message.to_s
110
+ assert_equal [2, :one, :end], messages[5].message.ndc.stack
111
+ end
112
+
113
+ end
114
+
115
+ end
@@ -10,7 +10,7 @@ describe PlainFormatter do
10
10
  describe 'without an error' do
11
11
 
12
12
  before do
13
- @message = Message.new(' Hello, World ')
13
+ @message = Message.new(ndc: [], message: ' Hello, World ')
14
14
  end
15
15
 
16
16
  it 'outputs the message in the MESSAGE format' do
@@ -24,7 +24,7 @@ describe PlainFormatter do
24
24
 
25
25
  before do
26
26
  error = OpenStruct.new(message: 'Boom!', backtrace: ['foo.rb:1:a', 'foo.rb:20:b'])
27
- @message = Message.new(' Hello, World ', error)
27
+ @message = Message.new(ndc: [], message: ' Hello, World ', error: error)
28
28
  end
29
29
 
30
30
  describe 'with backtraces enabled' do
@@ -10,7 +10,7 @@ describe SimpleFormatter do
10
10
  describe 'without an error' do
11
11
 
12
12
  before do
13
- @message = Message.new(' Hello, World ')
13
+ @message = Message.new(ndc: [], message: ' Hello, World ')
14
14
  end
15
15
 
16
16
  it 'outputs the message in the LEVEL - CONTEXT - MESSAGE format' do
@@ -24,7 +24,7 @@ describe SimpleFormatter do
24
24
 
25
25
  before do
26
26
  error = OpenStruct.new(message: 'Boom!', backtrace: ['foo.rb:1:a', 'foo.rb:20:b'])
27
- @message = Message.new(' Hello, World ', error)
27
+ @message = Message.new(ndc: [], message: ' Hello, World ', error: error)
28
28
  end
29
29
 
30
30
  describe 'with backtraces enabled' do
@@ -57,7 +57,7 @@ describe SimpleFormatter do
57
57
 
58
58
  before do
59
59
  subject.thread_context = true
60
- @message = Message.new(' Hello, World ')
60
+ @message = Message.new(ndc: [], message: ' Hello, World ')
61
61
  end
62
62
 
63
63
  it 'outputs the message in the [THREAD] - LEVEL - CONTEXT - MESSAGE format' do
@@ -67,6 +67,19 @@ describe SimpleFormatter do
67
67
 
68
68
  end
69
69
 
70
+ describe 'with ndc' do
71
+
72
+ before do
73
+ @message = Message.new(ndc: [:foo, 123], message: ' Hello, World ')
74
+ end
75
+
76
+ it 'outputs the message in the [THREAD] - LEVEL - CONTEXT NDC - MESSAGE format' do
77
+ message = subject.format(:info, 'Custom::Context', @message)
78
+ assert "INFO - Custom::Context foo 123 - Hello, World" == message, "got #{message}"
79
+ end
80
+
81
+ end
82
+
70
83
  end
71
84
 
72
85
  end
@@ -10,7 +10,8 @@ describe StandardFormatter do
10
10
 
11
11
  describe 'when formatting a message' do
12
12
  before do
13
- @message = Message.new('Hello, World')
13
+ ndc = NestedDiagnosticContext::ContextStack.new([:foo, 12])
14
+ @message = Message.new(ndc: ndc, message: 'Hello, World')
14
15
  @context = 'Custom::Context'
15
16
  @level = :info
16
17
  @formatted_message = subject.format(@level, @context, @message)
@@ -37,7 +38,7 @@ describe StandardFormatter do
37
38
  end
38
39
 
39
40
  it 'formats the message after the time as expected' do
40
- expected = "[#{Process.pid}] #{@level.to_s.upcase.ljust 5} #{@context} - #{@message}"
41
+ expected = "[#{Process.pid}] #{@level.to_s.upcase.ljust 5} #{@context} foo 12 - #{@message}"
41
42
  actual_without_time = @formatted_message[24..-1]
42
43
 
43
44
  assert_equal expected, actual_without_time
@@ -58,7 +59,7 @@ describe StandardFormatter do
58
59
 
59
60
  before do
60
61
  error = OpenStruct.new(message: 'Boom!', backtrace: ['foo.rb:1:a', 'foo.rb:20:b'])
61
- @message = Message.new(' Hello, World ', error)
62
+ @message = Message.new(ndc: [], message: ' Hello, World ', error: error)
62
63
  end
63
64
 
64
65
  describe 'with backtraces enabled' do
@@ -98,7 +99,8 @@ describe StandardFormatter do
98
99
  end
99
100
 
100
101
  took = Time.now - start
101
- limit = 0.4
102
+ limit = 0.6
103
+ puts "\nMessages took #{took} to generate\n"
102
104
  assert took < limit, "Expected messages to take less than #{limit} but took #{took}"
103
105
  end
104
106
  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.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-29 00:00:00.000000000 Z
12
+ date: 2013-01-10 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Logging library that provides the ability to add class/module specific
15
15
  filters
@@ -26,6 +26,8 @@ files:
26
26
  - lib/hatchet/level_manager.rb
27
27
  - lib/hatchet/logger_appender.rb
28
28
  - lib/hatchet/message.rb
29
+ - lib/hatchet/middleware.rb
30
+ - lib/hatchet/nested_diagnostic_context.rb
29
31
  - lib/hatchet/plain_formatter.rb
30
32
  - lib/hatchet/railtie.rb
31
33
  - lib/hatchet/simple_formatter.rb
@@ -44,6 +46,7 @@ files:
44
46
  - spec/logger_appender_spec.rb
45
47
  - spec/logger_spec.rb
46
48
  - spec/message_spec.rb
49
+ - spec/middleware_spec.rb
47
50
  - spec/plain_formatter_spec.rb
48
51
  - spec/simple_formatter_spec.rb
49
52
  - spec/spec_helper.rb
@@ -87,6 +90,7 @@ test_files:
87
90
  - spec/logger_appender_spec.rb
88
91
  - spec/logger_spec.rb
89
92
  - spec/message_spec.rb
93
+ - spec/middleware_spec.rb
90
94
  - spec/plain_formatter_spec.rb
91
95
  - spec/simple_formatter_spec.rb
92
96
  - spec/spec_helper.rb