1 錯誤回報
Rails 的 錯誤回報器 提供了一種標準方式來收集應用程式中發生的錯誤,並將它們回報給您偏好的服務或位置 (例如,您可以將錯誤回報給 Sentry 等監控服務)。
它的目標是取代像這樣的樣板錯誤處理程式碼
begin
do_something
rescue SomethingIsBroken => error
MyErrorReportingService.notify(error)
end
使用一致的介面
Rails.error.handle(SomethingIsBroken) do
do_something
end
Rails 將所有執行 (例如 HTTP 請求、工作 和 rails runner 調用) 包裹在錯誤回報器中,因此您的應用程式中任何未處理的錯誤都會透過其訂閱者自動回報給您的錯誤回報服務。
這表示第三方錯誤回報函式庫不再需要插入 Rack 中介軟體或進行任何 monkey-patching 來捕獲未處理的錯誤。使用 Active Support 的函式庫也可以使用此功能來不突兀地回報先前會在日誌中遺失的警告。
使用 Rails 錯誤回報器是可選的,因為其他捕獲錯誤的方式仍然有效。
1.1 訂閱回報器
若要將錯誤回報器與外部服務搭配使用,您需要一個訂閱者。訂閱者可以是任何具有 report
方法的 Ruby 物件。當您的應用程式中發生錯誤或手動回報時,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.error.unsubscribe
來取消註冊訂閱者。如果您想要取代或移除其中一個相依性新增的訂閱者,這可能會很有用。subscribe
和 unsubscribe
都可以採用訂閱者或類別,如下所示
subscriber = ErrorSubscriber.new
Rails.error.unsubscribe(subscriber)
# or
Rails.error.unsubscribe(ErrorSubscriber)
Rails 錯誤回報器始終會呼叫已註冊的訂閱者,無論您的環境為何。但是,許多錯誤回報服務預設只會在生產環境中回報錯誤。您應該根據需要設定並測試跨環境的設定。
1.2 使用錯誤回報器
Rails 錯誤回報器有四個方法可讓您以不同方式回報方法
Rails.error.handle
Rails.error.record
Rails.error.report
Rails.error.unexpected
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(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.2.4 回報意外錯誤
您可以透過呼叫 Rails.error.unexpected
來回報任何意外錯誤。
在生產環境中呼叫時,此方法會在回報錯誤後傳回 nil,並且您的程式碼執行將會繼續。
在開發環境中呼叫時,錯誤將會包裝在新錯誤類別中 (以確保它不會在堆疊中較高的地方被救援),並呈現給開發人員進行偵錯。
例如
def edit
if published?
Rails.error.unexpected("[BUG] Attempting to edit a published article, that shouldn't be possible")
false
end
# ...
end
此方法的目的是優雅地處理生產環境中可能發生的任何錯誤,但這些錯誤並非預期是典型使用造成的。
1.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 全域設定上下文
除了透過 context
選項設定上下文之外,您還可以透過 Rails.error.set_context
來設定上下文。例如
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.5 依錯誤類別篩選
使用 Rails.error.handle
和 Rails.error.record
,您也可以選擇僅回報特定類別的錯誤。例如
Rails.error.handle(IOError) do
1 + "1" # raises TypeError
end
1 + 1 # TypeErrors are not IOErrors, so this will *not* be executed
在此範例中,Rails 錯誤回報器將不會捕獲 TypeError
。只會回報 IOError
及其後代的執行個體。任何其他錯誤都將正常引發。
1.6 停用通知
您可以透過呼叫 Rails.error.disable
來防止訂閱者在區塊執行期間收到錯誤通知。與 subscribe
和 unsubscribe
類似,您可以傳入訂閱者本身或其類別。
Rails.error.disable(ErrorSubscriber) do
1 + "1" # TypeError will not be reported via the ErrorSubscriber
end
這對於可能希望以不同方式或在堆疊更高層級管理錯誤處理的第三方錯誤回報服務也很有幫助。
2 錯誤回報函式庫
錯誤回報函式庫可以在 Railtie 中註冊它們的訂閱者。
module MySdk
class Railtie < ::Rails::Railtie
initializer "my_sdk.error_subscribe" do
Rails.error.subscribe(MyErrorSubscriber.new)
end
end
end
如果您註冊了一個錯誤訂閱者,但仍然有其他錯誤機制(如 Rack 中介軟體),您最終可能會多次回報錯誤。您應該移除其他機制,或調整回報功能,使其跳過回報之前已看到的錯誤。