跳至内容 跳至搜索

Active Support 错误报告器

ActiveSupport::ErrorReporter 是一个用于错误报告服务的通用接口。

要救援和报告任何未处理的错误,可以使用 handle 方法

Rails.error.handle do
  do_something!
end

如果引发了错误,它将被报告并吞噬。

或者,如果您想报告错误但不吞噬它,可以使用 record

Rails.error.record do
  do_something!
end

两种方法都可以限制为仅处理特定错误类。

maybe_tags = Rails.error.handle(Redis::BaseError) { redis.get("tags") }
命名空间
方法
D
H
N
R
S
U

常量

DEFAULT_RESCUE = [StandardError].freeze
 
DEFAULT_SOURCE = "application"
 
SEVERITIES = %i(error warning info)
 
UnexpectedError = Class.new(Exception)
 

属性

[RW] debug_mode
[RW] logger

类公共方法

new(*subscribers, logger: nil)

# File activesupport/lib/active_support/error_reporter.rb, line 35
def initialize(*subscribers, logger: nil)
  @subscribers = subscribers.flatten
  @logger = logger
  @debug_mode = false
end

实例公共方法

disable(subscriber)

阻止订阅者在代码块持续时间内收到错误通知。你可以传递订阅者本身或其类。

这在错误报告服务集成中可能很有用,当它们希望在堆栈中更高的地方处理任何错误时。

# File activesupport/lib/active_support/error_reporter.rb, line 185
def disable(subscriber)
  disabled_subscribers = (ActiveSupport::IsolatedExecutionState[self] ||= [])
  disabled_subscribers << subscriber
  begin
    yield
  ensure
    disabled_subscribers.delete(subscriber)
  end
end

handle(*error_classes, severity: :warning, context: {}, fallback: nil, source: DEFAULT_SOURCE)

评估给定的代码块,报告并吞噬任何未处理的错误。如果未引发错误,则返回代码块的返回值。否则,返回fallback.call的结果,如果未指定fallback,则返回nil

# Will report a TypeError to all subscribers and return nil.
Rails.error.handle do
  1 + '1'
end

可以限制为仅处理特定错误类。

maybe_tags = Rails.error.handle(Redis::BaseError) { redis.get("tags") }

选项

  • :severity - 此值传递给订阅者以指示错误报告的重要性。可以是:error:warning:info。默认为:warning

  • :context - 传递给订阅者的额外信息。例如

    Rails.error.handle(context: { section: "admin" }) do
      # ...
    end
    
  • :fallback - 一个可调用对象,在引发未处理的错误时提供handle的返回值。例如

    user = Rails.error.handle(fallback: -> { User.anonymous }) do
      User.find_by(params)
    end
    
  • :source - 此值传递给订阅者以指示错误的来源。订阅者可以使用此值忽略某些错误。默认为"application"

# File activesupport/lib/active_support/error_reporter.rb, line 78
def handle(*error_classes, severity: :warning, context: {}, fallback: nil, source: DEFAULT_SOURCE)
  error_classes = DEFAULT_RESCUE if error_classes.empty?
  yield
rescue *error_classes => error
  report(error, handled: true, severity: severity, context: context, source: source)
  fallback.call if fallback
end

record(*error_classes, severity: :error, context: {}, source: DEFAULT_SOURCE)

评估给定的代码块,报告并重新引发任何未处理的错误。如果未引发错误,则返回代码块的返回值。

# Will report a TypeError to all subscribers and re-raise it.
Rails.error.record do
  1 + '1'
end

可以限制为仅处理特定错误类。

tags = Rails.error.record(Redis::BaseError) { redis.get("tags") }

选项

  • :severity - 此值传递给订阅者以指示错误报告的重要性。可以是:error:warning:info。默认为:error

  • :context - 传递给订阅者的额外信息。例如

    Rails.error.record(context: { section: "admin" }) do
      # ...
    end
    
  • :source - 此值传递给订阅者以指示错误的来源。订阅者可以使用此值忽略某些错误。默认为"application"

# File activesupport/lib/active_support/error_reporter.rb, line 114
def record(*error_classes, severity: :error, context: {}, source: DEFAULT_SOURCE)
  error_classes = DEFAULT_RESCUE if error_classes.empty?
  yield
rescue *error_classes => error
  report(error, handled: false, severity: severity, context: context, source: source)
  raise
end

report(error, handled: true, severity: handled ? :warning : :error, context: {}, source: DEFAULT_SOURCE)

直接向订阅者报告错误。当基于代码块的 handlerecord 方法不适合时,可以使用此方法。

Rails.error.report(error)
# File activesupport/lib/active_support/error_reporter.rb, line 210
def report(error, handled: true, severity: handled ? :warning : :error, context: {}, source: DEFAULT_SOURCE)
  return if error.instance_variable_defined?(:@__rails_error_reported)
  ensure_backtrace(error)

  unless SEVERITIES.include?(severity)
    raise ArgumentError, "severity must be one of #{SEVERITIES.map(&:inspect).join(", ")}, got: #{severity.inspect}"
  end

  full_context = ActiveSupport::ExecutionContext.to_h.merge(context)
  disabled_subscribers = ActiveSupport::IsolatedExecutionState[self]
  @subscribers.each do |subscriber|
    unless disabled_subscribers&.any? { |s| s === subscriber }
      subscriber.report(error, handled: handled, severity: severity, context: full_context, source: source)
    end
  rescue => subscriber_error
    if logger
      logger.fatal(
        "Error subscriber raised an error: #{subscriber_error.message} (#{subscriber_error.class})\n" +
        subscriber_error.backtrace.join("\n")
      )
    else
      raise
    end
  end

  unless error.frozen?
    error.instance_variable_set(:@__rails_error_reported, true)
  end

  nil
end

set_context(...)

更新错误订阅者可以访问的执行上下文。传递给 handlerecordreport 的任何上下文都将与在此设置的上下文合并。

Rails.error.set_context(section: "checkout", user_id: @user.id)
# File activesupport/lib/active_support/error_reporter.rb, line 201
def set_context(...)
  ActiveSupport::ExecutionContext.set(...)
end

subscribe(subscriber)

注册新的错误订阅者。订阅者必须响应

report(Exception, handled: Boolean, severity: (:error OR :warning OR :info), context: Hash, source: String)

report 方法 **绝不** 应该引发错误。

# File activesupport/lib/active_support/error_reporter.rb, line 161
def subscribe(subscriber)
  unless subscriber.respond_to?(:report)
    raise ArgumentError, "Error subscribers must respond to #report"
  end
  @subscribers << subscriber
end

unexpected(error, severity: :warning, context: {}, source: DEFAULT_SOURCE)

在生产环境中报告给定的错误,或者在开发或测试环境中引发它。

在生产环境中调用时,在报告错误后,此方法将返回 nil,并且执行将继续。

在开发环境中调用时,原始错误将包装在不同的错误类中,以确保它不被堆栈中更高的地方救援,并且将显示给开发人员。

此方法旨在用于报告关于先决条件的违反断言,或类似可以在生产环境中优雅处理但不应该发生的情况。

错误可以是异常实例或 String

example:

  def edit
    if published?
      Rails.error.unexpected("[BUG] Attempting to edit a published article, that shouldn't be possible")
      return false
    end
    # ...
  end
# File activesupport/lib/active_support/error_reporter.rb, line 145
def unexpected(error, severity: :warning, context: {}, source: DEFAULT_SOURCE)
  error = RuntimeError.new(error) if error.is_a?(String)

  if @debug_mode
    ensure_backtrace(error)
    raise UnexpectedError, "#{error.class.name}: #{error.message}", error.backtrace, cause: error
  else
    report(error, handled: true, severity: severity, context: context, source: source)
  end
end

unsubscribe(subscriber)

取消注册错误订阅者。接受订阅者或类。

subscriber = MyErrorSubscriber.new
Rails.error.subscribe(subscriber)

Rails.error.unsubscribe(subscriber)
# or
Rails.error.unsubscribe(MyErrorSubscriber)
# File activesupport/lib/active_support/error_reporter.rb, line 176
def unsubscribe(subscriber)
  @subscribers.delete_if { |s| subscriber === s }
end