lib/logging/layout.rb in logging-2.2.1 vs lib/logging/layout.rb in logging-2.2.2
- old
+ new
@@ -39,12 +39,13 @@
@obj_format = case f
when :inspect, :yaml, :json; f
else :string end
- self.backtrace = opts.fetch(:backtrace, ::Logging.backtrace)
- self.utc_offset = opts.fetch(:utc_offset, ::Logging.utc_offset)
+ self.backtrace = opts.fetch(:backtrace, ::Logging.backtrace)
+ self.utc_offset = opts.fetch(:utc_offset, ::Logging.utc_offset)
+ self.cause_depth = opts.fetch(:cause_depth, ::Logging.cause_depth)
end
# call-seq:
# layout.backtrace = true
#
@@ -87,10 +88,24 @@
end
# Returns the UTC offset.
attr_reader :utc_offset
+ #
+ #
+ def cause_depth=( value )
+ if value.nil?
+ @cause_depth = ::Logging::DEFAULT_CAUSE_DEPTH
+ else
+ value = Integer(value)
+ @cause_depth = value < 0 ? ::Logging::DEFAULT_CAUSE_DEPTH : value
+ end
+ end
+
+ # Returns the exception cause depth formatting limit.
+ attr_reader :cause_depth
+
# Internal: Helper method that applies the UTC offset to the given `time`
# instance. A new Time is returned that is equivalent to the original `time`
# but pinned to the timezone given by the UTC offset.
#
# If a UTC offset has not been set, then the original `time` instance is
@@ -140,19 +155,14 @@
#
def format_obj( obj )
case obj
when String; obj
when Exception
- ary = ["<#{obj.class.name}> #{obj.message}"]
- if backtrace? && !obj.backtrace.nil?
- ary.concat(obj.backtrace)
- end
- if defined?(obj.cause) && !obj.cause.nil?
- ary << "--- Caused by ---"
- ary << format_obj(obj.cause)
- end
- ary.join("\n\t")
+ lines = ["<#{obj.class.name}> #{obj.message}"]
+ lines.concat(obj.backtrace) if backtrace? && obj.backtrace
+ format_cause(obj, lines)
+ lines.join("\n\t")
when nil; "<#{obj.class.name}> nil"
else
str = "<#{obj.class.name}> "
str << case @obj_format
when :inspect; obj.inspect
@@ -161,10 +171,68 @@
else obj.to_s end
str
end
end
+ # Internal: Format any nested exceptions found in the given exception `e`
+ # while respecting the maximum `cause_depth`. The lines array is used to
+ # capture all the output lines form the nested exceptions; the array is later
+ # joined by the `format_obj` method.
+ #
+ # e - Exception to format
+ # lines - Array of output lines
+ #
+ # Returns the input `lines` Array
+ def format_cause(e, lines)
+ return lines if cause_depth == 0
+
+ cause_depth.times do
+ break unless e.respond_to?(:cause) && e.cause
+
+ cause = e.cause
+ lines << "--- Caused by ---"
+ lines << "<#{cause.class.name}> #{cause.message}"
+ lines.concat(format_cause_backtrace(e, cause)) if backtrace? && cause.backtrace
+
+ e = cause
+ end
+
+ if e.respond_to?(:cause) && e.cause
+ lines << "--- Further #cause backtraces were omitted ---"
+ end
+
+ lines
+ end
+
+ # Internal: Format the backtrace of the nested `cause` but remove the common
+ # exception lines from the parent exception. This helps keep the backtraces a
+ # wee bit shorter and more comprehensible.
+ #
+ # e - parent exception
+ # cause - the nested exception generating the returned backtrace
+ #
+ # Returns an Array of backtracke lines.
+ def format_cause_backtrace(e, cause)
+ # Find where the cause's backtrace differs from the parent exception's.
+ backtrace = Array(e.backtrace)
+ cause_backtrace = Array(cause.backtrace)
+ index = -1
+ min_index = [backtrace.size, cause_backtrace.size].min * -1
+ just_in_case = -5000
+
+ while index > min_index && backtrace[index] == cause_backtrace[index] && index >= just_in_case
+ index -= 1
+ end
+
+ # Add on a few common frames to make it clear where the backtraces line up.
+ index += 3
+ index = -1 if index >= 0
+
+ cause_backtrace[0..index]
+ end
+
+
# Attempt to format the _obj_ using yaml, but fall back to inspect style
# formatting if yaml fails.
#
# obj - The Object to format.
#
@@ -186,9 +254,7 @@
def try_json( obj )
MultiJson.encode(obj)
rescue StandardError
obj.inspect
end
-
-end # class Layout
-end # module Logging
-
+end
+end