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