lib/semantic_logger/base.rb in semantic_logger-2.16.0 vs lib/semantic_logger/base.rb in semantic_logger-2.17.0

- old
+ new

@@ -312,11 +312,14 @@ # exception # Ruby Exception object to log # # metric [Object] # Object supplied when benchmark_x was called - Log = Struct.new(:level, :thread_name, :name, :message, :payload, :time, :duration, :tags, :level_index, :exception, :metric) + # + # backtrace [Array<String>] + # The backtrace captured at source when the log level >= SemanticLogger.backtrace_level + Log = Struct.new(:level, :thread_name, :name, :message, :payload, :time, :duration, :tags, :level_index, :exception, :metric, :backtrace) # Whether to log the supplied message based on the current filter if any def include_message?(struct) return true if @filter.nil? @@ -327,18 +330,22 @@ end end # Log message at the specified level def log_internal(level, index, message=nil, payload=nil, exception=nil, &block) + # Detect exception being logged if exception.nil? && payload.nil? && message.kind_of?(Exception) exception = message message = exception.inspect - elsif exception.nil? && payload && payload.is_a?(Exception) + elsif exception.nil? && payload && payload.respond_to?(:backtrace) && payload.respond_to?(:message) + # Under JRuby a java exception is not a Ruby Exception + # Java::JavaLang::ClassCastException.new.is_a?(Exception) => false exception = payload payload = nil end + # Add result of block as message or payload if not nil if block && (result = block.call) if result.is_a?(String) message = message.nil? ? result : "#{message} -- #{result}" elsif payload && payload.respond_to?(:merge) payload.merge(result) @@ -349,17 +356,28 @@ # Add scoped payload if self.payload payload = payload.nil? ? self.payload : self.payload.merge(payload) end - struct = Log.new(level, Thread.current.name, name, message, payload, Time.now, nil, tags, index, exception) + + # Add caller stack trace + backtrace = + if !exception && (index >= SemanticLogger.backtrace_level_index) + trace = caller + # Remove call to this internal method + trace.shift(1) + trace + end + + struct = Log.new(level, Thread.current.name, name, message, payload, Time.now, nil, tags, index, exception, nil, backtrace) log(struct) if include_message?(struct) end # Measure the supplied block and log the message def benchmark_internal(level, index, message, params, &block) - start = Time.now + start = Time.now + exception = nil begin if block result = if silence_level = params[:silence] # In case someone accidentally sets `silence: true` instead of `silence: :error` @@ -392,10 +410,11 @@ if self.payload payload = payload.nil? ? self.payload : self.payload.merge(payload) end if exception logged_exception = exception + backtrace = nil case log_exception when :full # On exception change the log level if on_exception_level level = on_exception_level @@ -407,19 +426,32 @@ level = on_exception_level index = SemanticLogger.level_to_index(level) end message = "#{message} -- Exception: #{exception.class}: #{exception.message}" logged_exception = nil + backtrace = exception.backtrace else # Log the message with its duration but leave out the exception that was raised logged_exception = nil + backtrace = exception.backtrace end - struct = Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, logged_exception, metric) + struct = Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, logged_exception, metric, backtrace) log(struct) if include_message?(struct) raise exception elsif duration >= min_duration # Only log if the block took longer than 'min_duration' to complete - struct = Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, nil, metric) + # Add caller stack trace + backtrace = + if index >= SemanticLogger.backtrace_level_index + trace = caller + # Remove call to this internal method + trace.shift + # Ruby 1.9 has additional stack entry for parent that calls this method + trace.shift if RUBY_VERSION.to_f <= 2.0 + trace + end + + struct = Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, nil, metric, backtrace) log(struct) if include_message?(struct) end end end