[Rails] レンダリングストリーム中のエラーを Rails.error に報告可能に
Context
Railsのストリーミングレンダリング機能では、レスポンスを部分的にクライアントに送信しながらビューをレンダリングできます。しかし、ストリーミング中にエラーが発生した場合、既にHTTPヘッダーが送信済みのため通常のエラーハンドリングが機能しません。
これまでストリーミング中のエラーは単にログに記録されるだけで、SentryやBugsnagなどのエラー監視サービスには通知されませんでした。これらのサービスは通常 ShowExceptions ミドルウェアにフックしてエラーを捕捉しますが、ストリーミングレンダリングではそのタイミングを逃してしまうためです。
Rails 7.0で導入された ActiveSupport::ErrorReporter API(Rails.error)により、この問題が解決されました。
Technical Detail
エラー報告メカニズムの変更
変更前:
def log_error(exception)
logger = ActionView::Base.logger
return unless logger
message = +"\n#{exception.class} (#{exception.message}):\n"
message << exception.annotated_source_code.to_s if exception.respond_to?(:annotated_source_code)
message << " " << exception.backtrace.join("\n ")
logger.fatal("#{message}\n\n")
end
変更後:
def log_error(error)
if ActiveSupport.error_reporter
ActiveSupport.error_reporter.report(error)
elsif logger = ActionView::Base.logger
message = +"\n#{error.class} (#{error.message}):\n"
message << error.annotated_source_code.to_s if error.respond_to?(:annotated_source_code)
message << " " << error.backtrace.join("\n ")
logger.fatal("#{message}\n\n")
end
end
主な変更点
-
ErrorReporter優先:
ActiveSupport.error_reporterが利用可能な場合、それを使用してエラーを報告 - 後方互換性: ErrorReporterが未設定の場合は従来通りロガーにフォールバック
-
統一されたエラー処理:
Rails.error.handleやRails.error.recordで設定されたハンドラーがストリーミングエラーも捕捉可能に
実装上の注意点
begin
@start.call(block)
rescue => error # Exception から => に変更
log_error(error)
block.call ActionView::Base.streaming_completion_on_exception
end
rescue Exception から rescue => に変更することで、SignalException や SystemExit などの致命的な例外を捕捉しないようになりました。これはRubyのベストプラクティスに準拠した変更です。
テストの追加
新しいテストケースでは、エラーが正しく報告されることを検証しています。
test "rendering with template exception reports error" do
error_report = assert_error_reported do
get "/render_streaming/basic/template_exception"
end
assert_match "Ruby was here!", error_report.error.message
end
Impact
この変更により、以下のような利点があります。
- エラー監視の完全性: Sentry、Bugsnag、Honeybadgerなどのサービスでストリーミングエラーも追跡可能に
- 統一されたエラーハンドリング: アプリケーション全体で一貫したエラー処理が可能
- デバッグの容易化: 本番環境でのストリーミングエラーを見逃さない
既存のコードへの影響はなく、ErrorReporterが設定されていない環境では従来通りの動作を維持します。