logging 2.2.1 → 2.2.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c4be0dd542e3eac91b3ad7f7895e7a0dbfdadd66
4
- data.tar.gz: 960c2a5e039c3f455f29dd6bf4fe3a700957194b
3
+ metadata.gz: bb433e2db44c13bc4956095561ad2ad34cd4fabf
4
+ data.tar.gz: 6d6a401267834a2b1dad3c5992bb4c4c28ea05a0
5
5
  SHA512:
6
- metadata.gz: ab8acc6f8f984c8533c667c73fffb4b6e5286d1a401af2989ca1d1e0a4beac3b2147abf9c87dcf52339e32da1be8b3db41d770acb9c38c40e84c07e50b3e9776
7
- data.tar.gz: 2bdab15e9bfb48a5b3a30214bdaf375b2536dac54a334ee27ca8fafbda1ba6068a6a0aff1a91524eadd10d22525a5607eb41eacf3fc8e8d69402b08fd5ec73df
6
+ metadata.gz: 44ad1070f56d8618f9e6c118876364f70442723424d055716928e422a6699a40e744c68a86619227ed8fd5eea26e6c1fd6850f50877c74769a5160d6133b5140
7
+ data.tar.gz: bf24fb750a15937f18d75b2acc738e323a8ea718661bb9877ea64b0d23eea12def864c3175e71cd1f9b7ccff69c44d661455ff3526e5c192cc0f67c026a77a7f
@@ -1,3 +1,8 @@
1
+ == 2.2.2 / 2017-04-11
2
+
3
+ Enhancements
4
+ - limit the depth of nested exceptions [PR #167]
5
+
1
6
  == 2.2.1 / 2017-04-09
2
7
 
3
8
  Enhancements
@@ -23,6 +23,8 @@ module Logging
23
23
  PATH = ::File.expand_path('../..', __FILE__) + ::File::SEPARATOR
24
24
  LEVELS = {}
25
25
  LNAMES = []
26
+ DEFAULT_CAUSE_DEPTH = 8
27
+
26
28
  module Plugins; end
27
29
  # :startdoc:
28
30
 
@@ -255,6 +257,8 @@ module Logging
255
257
  longest = 'off' if longest.length < 3
256
258
  module_eval "MAX_LEVEL_LENGTH = #{longest.length}", __FILE__, __LINE__
257
259
 
260
+ self.cause_depth = nil unless defined? @cause_depth
261
+
258
262
  initialize_plugins
259
263
  levels.keys
260
264
  end
@@ -336,6 +340,27 @@ module Logging
336
340
 
337
341
  attr_reader :utc_offset
338
342
 
343
+ # Set the default Exception#cause depth used when formatting Exceptions.
344
+ # This sets the maximum number of nested errors that will be formatted by
345
+ # the layouts before giving up. This is used to avoid extremely large
346
+ # outputs.
347
+ #
348
+ # Logging.cause_depth = nil # set to the DEFAULT_CAUSE_DEPTH
349
+ # Logging.cause_depth = 0 # do not show any exception causes
350
+ # Logging.cause_depth = 1024 # show up to 1024 causes
351
+ # Logging.cause_depth = -1 # results in the DEFAULT_CAUSE_DEPTH
352
+ #
353
+ def cause_depth=( value )
354
+ if value.nil?
355
+ @cause_depth = DEFAULT_CAUSE_DEPTH
356
+ else
357
+ value = Integer(value)
358
+ @cause_depth = value < 0 ? DEFAULT_CAUSE_DEPTH : value
359
+ end
360
+ end
361
+
362
+ attr_reader :cause_depth
363
+
339
364
  # Used to define a `basepath` that will be removed from filenames when
340
365
  # reporting tracing information for log events. Normally you would set this
341
366
  # to the root of your project:
@@ -516,7 +541,8 @@ module Logging
516
541
  remove_instance_variable :@basepath if defined? @basepath
517
542
  remove_const :MAX_LEVEL_LENGTH if const_defined? :MAX_LEVEL_LENGTH
518
543
  remove_const :OBJ_FORMAT if const_defined? :OBJ_FORMAT
519
- self.utc_offset = nil
544
+ self.utc_offset = nil
545
+ self.cause_depth = nil
520
546
  self
521
547
  end
522
548
 
@@ -41,8 +41,9 @@ class Layout
41
41
  when :inspect, :yaml, :json; f
42
42
  else :string end
43
43
 
44
- self.backtrace = opts.fetch(:backtrace, ::Logging.backtrace)
45
- self.utc_offset = opts.fetch(:utc_offset, ::Logging.utc_offset)
44
+ self.backtrace = opts.fetch(:backtrace, ::Logging.backtrace)
45
+ self.utc_offset = opts.fetch(:utc_offset, ::Logging.utc_offset)
46
+ self.cause_depth = opts.fetch(:cause_depth, ::Logging.cause_depth)
46
47
  end
47
48
 
48
49
  # call-seq:
@@ -89,6 +90,20 @@ class Layout
89
90
  # Returns the UTC offset.
90
91
  attr_reader :utc_offset
91
92
 
93
+ #
94
+ #
95
+ def cause_depth=( value )
96
+ if value.nil?
97
+ @cause_depth = ::Logging::DEFAULT_CAUSE_DEPTH
98
+ else
99
+ value = Integer(value)
100
+ @cause_depth = value < 0 ? ::Logging::DEFAULT_CAUSE_DEPTH : value
101
+ end
102
+ end
103
+
104
+ # Returns the exception cause depth formatting limit.
105
+ attr_reader :cause_depth
106
+
92
107
  # Internal: Helper method that applies the UTC offset to the given `time`
93
108
  # instance. A new Time is returned that is equivalent to the original `time`
94
109
  # but pinned to the timezone given by the UTC offset.
@@ -142,15 +157,10 @@ class Layout
142
157
  case obj
143
158
  when String; obj
144
159
  when Exception
145
- ary = ["<#{obj.class.name}> #{obj.message}"]
146
- if backtrace? && !obj.backtrace.nil?
147
- ary.concat(obj.backtrace)
148
- end
149
- if defined?(obj.cause) && !obj.cause.nil?
150
- ary << "--- Caused by ---"
151
- ary << format_obj(obj.cause)
152
- end
153
- ary.join("\n\t")
160
+ lines = ["<#{obj.class.name}> #{obj.message}"]
161
+ lines.concat(obj.backtrace) if backtrace? && obj.backtrace
162
+ format_cause(obj, lines)
163
+ lines.join("\n\t")
154
164
  when nil; "<#{obj.class.name}> nil"
155
165
  else
156
166
  str = "<#{obj.class.name}> "
@@ -163,6 +173,64 @@ class Layout
163
173
  end
164
174
  end
165
175
 
176
+ # Internal: Format any nested exceptions found in the given exception `e`
177
+ # while respecting the maximum `cause_depth`. The lines array is used to
178
+ # capture all the output lines form the nested exceptions; the array is later
179
+ # joined by the `format_obj` method.
180
+ #
181
+ # e - Exception to format
182
+ # lines - Array of output lines
183
+ #
184
+ # Returns the input `lines` Array
185
+ def format_cause(e, lines)
186
+ return lines if cause_depth == 0
187
+
188
+ cause_depth.times do
189
+ break unless e.respond_to?(:cause) && e.cause
190
+
191
+ cause = e.cause
192
+ lines << "--- Caused by ---"
193
+ lines << "<#{cause.class.name}> #{cause.message}"
194
+ lines.concat(format_cause_backtrace(e, cause)) if backtrace? && cause.backtrace
195
+
196
+ e = cause
197
+ end
198
+
199
+ if e.respond_to?(:cause) && e.cause
200
+ lines << "--- Further #cause backtraces were omitted ---"
201
+ end
202
+
203
+ lines
204
+ end
205
+
206
+ # Internal: Format the backtrace of the nested `cause` but remove the common
207
+ # exception lines from the parent exception. This helps keep the backtraces a
208
+ # wee bit shorter and more comprehensible.
209
+ #
210
+ # e - parent exception
211
+ # cause - the nested exception generating the returned backtrace
212
+ #
213
+ # Returns an Array of backtracke lines.
214
+ def format_cause_backtrace(e, cause)
215
+ # Find where the cause's backtrace differs from the parent exception's.
216
+ backtrace = Array(e.backtrace)
217
+ cause_backtrace = Array(cause.backtrace)
218
+ index = -1
219
+ min_index = [backtrace.size, cause_backtrace.size].min * -1
220
+ just_in_case = -5000
221
+
222
+ while index > min_index && backtrace[index] == cause_backtrace[index] && index >= just_in_case
223
+ index -= 1
224
+ end
225
+
226
+ # Add on a few common frames to make it clear where the backtraces line up.
227
+ index += 3
228
+ index = -1 if index >= 0
229
+
230
+ cause_backtrace[0..index]
231
+ end
232
+
233
+
166
234
  # Attempt to format the _obj_ using yaml, but fall back to inspect style
167
235
  # formatting if yaml fails.
168
236
  #
@@ -188,7 +256,5 @@ class Layout
188
256
  rescue StandardError
189
257
  obj.inspect
190
258
  end
191
-
192
- end # class Layout
193
- end # module Logging
194
-
259
+ end
260
+ end
@@ -219,11 +219,15 @@ module Logging::Layouts
219
219
  def format_obj( obj )
220
220
  case obj
221
221
  when Exception
222
- h = { :class => obj.class.name,
223
- :message => obj.message }
224
- h[:backtrace] = obj.backtrace if backtrace? && !obj.backtrace.nil?
225
- h[:cause] = format_obj(obj.cause) if defined?(obj.cause) && !obj.cause.nil?
226
- h
222
+ hash = {
223
+ :class => obj.class.name,
224
+ :message => obj.message
225
+ }
226
+ hash[:backtrace] = obj.backtrace if backtrace? && obj.backtrace
227
+
228
+ cause = format_cause(obj)
229
+ hash[:cause] = cause unless cause.empty?
230
+ hash
227
231
  when Time
228
232
  iso8601_format(obj)
229
233
  else
@@ -231,6 +235,37 @@ module Logging::Layouts
231
235
  end
232
236
  end
233
237
 
238
+ # Internal: Format any nested exceptions found in the given exception `e`
239
+ # while respecting the maximum `cause_depth`.
240
+ #
241
+ # e - Exception to format
242
+ #
243
+ # Returns the cause formatted as a Hash
244
+ def format_cause(e)
245
+ rv = curr = {}
246
+ prev = nil
247
+
248
+ cause_depth.times do
249
+ break unless e.respond_to?(:cause) && e.cause
250
+
251
+ cause = e.cause
252
+ curr[:class] = cause.class.name
253
+ curr[:message] = cause.message
254
+ curr[:backtrace] = format_cause_backtrace(e, cause) if backtrace? && cause.backtrace
255
+
256
+ prev[:cause] = curr unless prev.nil?
257
+ prev, curr = curr, {}
258
+
259
+ e = cause
260
+ end
261
+
262
+ if e.respond_to?(:cause) && e.cause
263
+ prev[:cause] = {message: "Further #cause backtraces were omitted"}
264
+ end
265
+
266
+ rv
267
+ end
268
+
234
269
  private
235
270
 
236
271
  # Call the appropriate class level create format method based on the
@@ -258,6 +293,5 @@ module Logging::Layouts
258
293
  return str << (value.gmt_offset < 0 ? '-' : '+') << offset
259
294
  end
260
295
 
261
- end # Parseable
262
- end # Logging::Layouts
263
-
296
+ end
297
+ end
@@ -1,5 +1,5 @@
1
1
  module Logging
2
- VERSION = "2.2.1".freeze
2
+ VERSION = "2.2.2".freeze
3
3
 
4
4
  # Returns the version string for the library.
5
5
  def self.version
@@ -7,46 +7,118 @@ module TestLogging
7
7
  include LoggingTestCase
8
8
 
9
9
  def test_basic_format_obj
10
+ err = nil
10
11
  begin
11
- raise StandardError, 'nested exception'
12
- rescue
13
- raise Exception, 'root exception'
12
+ begin
13
+ raise ArgumentError, 'nested exception'
14
+ rescue
15
+ raise StandardError, 'root exception'
16
+ end
17
+ rescue => e
18
+ err = e
14
19
  end
15
- rescue Exception => e
20
+
16
21
  layout = Logging.layouts.basic({})
17
22
  log = layout.format_obj(e)
18
- assert_not_nil log.index('<Exception> root exception')
23
+ assert_not_nil log.index('<StandardError> root exception')
24
+
25
+ if err.respond_to?(:cause)
26
+ assert_not_nil log.index('<ArgumentError> nested exception')
27
+ assert(log.index('<StandardError> root exception') < log.index('<ArgumentError> nested exception'))
28
+ end
29
+ end
30
+
31
+ def test_cause_depth_limiting
32
+ err = nil
33
+ begin
34
+ begin
35
+ begin
36
+ raise TypeError, 'nested exception 2'
37
+ rescue
38
+ raise ArgumentError, 'nested exception 1'
39
+ end
40
+ rescue
41
+ raise StandardError, 'root exception'
42
+ end
43
+ rescue => e
44
+ err = e
45
+ end
46
+
47
+ layout = Logging.layouts.basic(cause_depth: 1)
48
+ log = layout.format_obj(e)
49
+ assert_not_nil log.index('<StandardError> root exception')
19
50
 
20
- if defined? e.cause
21
- assert_not_nil log.index('<StandardError> nested exception')
22
- assert_operator log.index('<Exception> root exception'), :<, log.index('<StandardError> nested exception')
51
+ if err.respond_to?(:cause)
52
+ assert_not_nil log.index('<ArgumentError> nested exception 1')
53
+ assert_nil log.index('<TypeError> nested exception 2')
54
+ assert_equal '--- Further #cause backtraces were omitted ---', log.split("\n\t").last
23
55
  end
24
56
  end
25
57
 
26
58
  def test_parseable_format_obj
59
+ err = nil
27
60
  begin
28
- raise StandardError, 'nested exception'
29
- rescue
30
- raise Exception, 'root exception'
61
+ begin
62
+ raise ArgumentError, 'nested exception'
63
+ rescue
64
+ raise StandardError, 'root exception'
65
+ end
66
+ rescue => e
67
+ err = e
31
68
  end
32
- rescue Exception => e
69
+
33
70
  layout = Logging.layouts.parseable.new
34
71
  log = layout.format_obj(e)
35
- assert_equal Exception.name, log[:class]
72
+ assert_equal 'StandardError', log[:class]
36
73
  assert_equal 'root exception', log[:message]
37
- assert_operator log[:backtrace].size, :>, 0
74
+ assert log[:backtrace].size > 0
38
75
 
39
- if defined? e.cause
76
+ if e.respond_to?(:cause)
40
77
  assert_not_nil log[:cause]
41
78
 
42
79
  log = log[:cause]
43
- assert_equal StandardError.name, log[:class]
80
+ assert_equal 'ArgumentError', log[:class]
44
81
  assert_equal 'nested exception', log[:message]
45
82
  assert_nil log[:cause]
46
- assert_operator log[:backtrace].size, :>, 0
83
+ assert log[:backtrace].size > 0
84
+ end
85
+ end
86
+
87
+ def test_parseable_cause_depth_limiting
88
+ err = nil
89
+ begin
90
+ begin
91
+ begin
92
+ raise TypeError, 'nested exception 2'
93
+ rescue
94
+ raise ArgumentError, 'nested exception 1'
95
+ end
96
+ rescue
97
+ raise StandardError, 'root exception'
98
+ end
99
+ rescue => e
100
+ err = e
101
+ end
102
+
103
+ layout = Logging.layouts.parseable.new(cause_depth: 1)
104
+ log = layout.format_obj(e)
105
+
106
+ assert_equal 'StandardError', log[:class]
107
+ assert_equal 'root exception', log[:message]
108
+ assert log[:backtrace].size > 0
109
+
110
+ if e.respond_to?(:cause)
111
+ assert_not_nil log[:cause]
112
+
113
+ log = log[:cause]
114
+ assert_equal 'ArgumentError', log[:class]
115
+ assert_equal 'nested exception 1', log[:message]
116
+ assert_equal({message: "Further #cause backtraces were omitted"}, log[:cause])
117
+ assert log[:backtrace].size > 0
47
118
  end
48
119
  end
49
120
  end
50
121
  end
51
122
  end
52
123
 
124
+ require 'pp'
@@ -54,6 +54,24 @@ module TestLogging
54
54
  assert_raise(ArgumentError) {::Logging.utc_offset = "06:00"}
55
55
  end
56
56
 
57
+ def test_cause_depth
58
+ assert_equal ::Logging::DEFAULT_CAUSE_DEPTH, ::Logging.cause_depth
59
+
60
+ ::Logging.cause_depth = 0
61
+ assert_equal 0, ::Logging.cause_depth
62
+
63
+ ::Logging.cause_depth = nil
64
+ assert_equal ::Logging::DEFAULT_CAUSE_DEPTH, ::Logging.cause_depth
65
+
66
+ ::Logging.cause_depth = "1024"
67
+ assert_equal 1024, ::Logging.cause_depth
68
+
69
+ ::Logging.cause_depth = -1
70
+ assert_equal ::Logging::DEFAULT_CAUSE_DEPTH, ::Logging.cause_depth
71
+
72
+ assert_raise(ArgumentError) {::Logging.cause_depth = "foo"}
73
+ end
74
+
57
75
  def test_basepath
58
76
  assert_nil ::Logging.basepath
59
77
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logging
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1
4
+ version: 2.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Pease
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-10 00:00:00.000000000 Z
11
+ date: 2017-04-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: little-plugger