logging 2.2.1 → 2.2.2

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