edge
更多信息请访问 rubyonrails.org: 更多 Ruby on Rails

Rails应用程序中的错误报告

本指南介绍了在Ruby on Rails应用程序中处理异常的方法。

阅读本指南后,您将了解以下内容:

1 错误报告

Rails的错误报告器提供了一种标准的方式来收集应用程序中发生的异常并将其报告给您首选的服务或位置。

错误报告器旨在取代类似于以下代码的样板错误处理代码:

begin
  do_something
rescue SomethingIsBroken => error
  MyErrorReportingService.notify(error)
end

使用一致的接口:

Rails.error.handle(SomethingIsBroken) do
  do_something
end

Rails将所有执行操作(如HTTP请求、作业和rails runner调用)都包装在错误报告器中,因此在您的应用程序中引发的任何未处理错误都将自动通过订阅者报告给您的错误报告服务。

这意味着第三方错误报告库不再需要插入Rack中间件或进行任何猴子补丁来捕获未处理的异常。使用ActiveSupport的库也可以使用此功能非侵入性地报告以前在日志中丢失的警告。

使用Rails的错误报告器不是必需的。所有其他捕获错误的方法仍然有效。

1.1 订阅报告器

要使用错误报告器,您需要一个订阅者。订阅者是具有report方法的任何对象。当应用程序中发生错误或手动报告错误时,Rails错误报告器将使用错误对象和一些选项调用此方法。

某些错误报告库(例如Sentry的Honeybadger的)会自动为您注册一个订阅者。有关更多详细信息,请参阅您提供程序的文档。

您还可以创建自定义订阅者。例如:

# config/initializers/error_subscriber.rb
class ErrorSubscriber
  def report(error, handled:, severity:, context:, source: nil)
    MyErrorReportingService.report_error(error, context: context, handled: handled, level: severity)
  end
end

定义订阅者类后,通过调用Rails.error.subscribe方法注册它:

Rails.error.subscribe(ErrorSubscriber.new)

您可以注册任意数量的订阅者。Rails将按照注册的顺序依次调用它们。

注意:Rails错误报告器将始终调用已注册的订阅者,而不考虑您的环境。但是,许多错误报告服务默认仅在生产环境中报告错误。您应根据需要配置和测试跨环境的设置。

1.2 使用错误报告器

有三种方法可以使用错误报告器:

1.2.1 报告和忽略错误

Rails.error.handle将报告在块内引发的任何错误。然后它将忽略该错误,并且块外部的其余代码将继续正常执行。

result = Rails.error.handle do
  1 + '1' # 引发TypeError
end
result # => nil
1 + 1 # 这将被执行

如果在块内没有引发错误,Rails.error.handle将返回块的结果,否则将返回nil。您可以通过提供一个fallback来覆盖此行为:

user = Rails.error.handle(fallback: -> { User.anonymous }) do
  User.find_by(params[:id])
end

1.2.2 报告和重新引发错误

Rails.error.record将向所有注册的订阅者报告错误,然后重新引发错误,这意味着您的代码的其余部分将不会执行。

Rails.error.record do
  1 + '1' # 引发TypeError
end
1 + 1 # 这将不会被执行

如果在块内没有引发错误,Rails.error.record将返回块的结果。

1.2.3 手动报告错误

您还可以通过调用Rails.error.report来手动报告错误:

begin
  # 代码
rescue StandardError => e
  Rails.error.report(e)
end

您传递的任何选项都将传递给错误订阅者。

1.3 错误报告选项

所有3个报告API(#handle#record#report)都支持以下选项,然后将其传递给所有注册的订阅者:

  • handled:一个Boolean,指示错误是否已处理。默认设置为true#record将其设置为false
  • severity:描述错误严重性的Symbol。预期值为::error:warning:info#handle将其设置为:warning,而#record将其设置为:error
  • context:提供有关错误的更多上下文的Hash,例如请求或用户详细信息
  • source:关于错误来源的String。默认来源为"application"。内部库报告的错误可能会设置其他来源;例如,Redis缓存库可以使用"redis_cache_store.active_support"。您的订阅者可以使用来源来忽略您不感兴趣的错误。 ruby Rails.error.handle(context: { user_id: user.id }, severity: :info) do # ... end

1.4 按错误类过滤

通过 Rails.error.handleRails.error.record,您还可以选择仅报告特定类的错误。例如:

Rails.error.handle(IOError) do
  1 + '1' # 抛出 TypeError
end
1 + 1 # TypeErrors 不是 IOError,所以这个语句 *不会* 被执行

在这里,TypeError 不会被 Rails 错误报告器捕获。只有 IOError 及其子类的实例才会被报告。其他任何错误都会正常抛出。

1.5 全局设置上下文

除了通过 context 选项设置上下文外,您还可以使用 #set_context API。例如:

Rails.error.set_context(section: "checkout", user_id: @user.id)

以这种方式设置的任何上下文都将与 context 选项合并

Rails.error.set_context(a: 1)
Rails.error.handle(context: { b: 2 }) { raise }
# 报告的上下文将是:{:a=>1, :b=>2}
Rails.error.handle(context: { b: 3 }) { raise }
# 报告的上下文将是:{:a=>1, :b=>3}

1.6 对于库

错误报告库可以在 Railtie 中注册其订阅者:

module MySdk
  class Railtie < ::Rails::Railtie
    initializer "my_sdk.error_subscribe" do
      Rails.error.subscribe(MyErrorSubscriber.new)
    end
  end
end

如果您注册了错误订阅者,但仍然有其他错误机制,比如 Rack 中间件,可能会导致错误多次报告。您应该删除其他机制或调整报告功能,以便跳过已经看到的异常的报告。

反馈

欢迎您帮助改进本指南的质量。

如果您发现任何拼写错误或事实错误,请贡献您的意见。 要开始,请阅读我们的 文档贡献 部分。

您还可能会发现不完整的内容或过时的内容。 请为主要内容添加任何缺失的文档。请先检查 Edge 指南,以验证问题是否已经修复或尚未修复。 请参阅 Ruby on Rails 指南准则 以了解样式和规范。

如果您发现需要修复但无法自行修复的问题,请 提交问题

最后但同样重要的是,欢迎您在 官方 Ruby on Rails 论坛 上讨论有关 Ruby on Rails 文档的任何问题。