跳至內文 跳至搜尋

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