v7.1.3.2
更多資訊請至 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 錯誤回報器會使用錯誤物件和一些選項呼叫此方法。

一些錯誤回報函式庫,例如 SentryHoneybadger,會自動為您註冊訂閱者。有關更多詳細資訊,請參閱您的供應商文件。

您也可以建立自訂訂閱者。例如

# 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' # raises TypeError
end
result # => nil
1 + 1 # This will be executed

如果區塊中沒有引發錯誤,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' # raises TypeError
end
1 + 1 # This won't be executed

如果區塊中沒有引發錯誤,Rails.error.record 會傳回區塊的結果。

1.2.3 手動回報錯誤

您也可以透過呼叫 Rails.error.report 手動回報錯誤。

begin
  # code
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"。您的訂閱者可以使用來源來忽略您不感興趣的錯誤。
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' # raises TypeError
end
1 + 1 # TypeErrors are not IOErrors, so this will *not* be executed

在此,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 }
# The reported context will be: {:a=>1, :b=>2}
Rails.error.handle(context: { b: 3 }) { raise }
# The reported context will be: {: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 Guides,以驗證問題是否已在主分支中修復。查看 Ruby on Rails 指南指南,了解樣式和慣例。

如果您發現需要修復的問題,但無法自行修補,請 開啟問題

最後,歡迎在 官方 Ruby on Rails 論壇 上討論有關 Ruby on Rails 文件的任何問題。