rescue_from_handledイベントに完全なバックトレースを格納

rails/rails

Rails 8.2では、rescue_from_handled.action_controller通知のペイロードに完全なバックトレースを含められるようになりました。config.action_controller.rescue_from_event_backtrace:arrayに設定することで、例外の全スタックトレースを取得できます。

背景

Rails 8.1で導入されたrescue_from_handled.action_controllerイベントは、#56060で指摘されたように、exception_backtraceの扱いに一貫性の問題がありました。active_job.completedイベントが完全なバックトレースオブジェクトを提供するのに対し、rescue_from_handled.action_controllerイベントはバックトレースの最初のフレームのみを文字列として提供していました。

この不一致は、可観測性ツールがイベントペイロードを処理する際に混乱を招く可能性があります。異なるイベント間でexception_backtraceの型が異なるため、統一的なエラートラッキングの実装が困難でした。

技術的な変更

ActionController::StructuredEventSubscriberに新しいクラス属性_rescue_from_event_backtraceが追加され、バックトレースの形式を制御できるようになりました。

変更前:

def rescue_from_callback(event)
  exception = event.payload[:exception]

  exception_backtrace = exception.backtrace&.first
  exception_backtrace = exception_backtrace&.delete_prefix("#{Rails.root}/") if defined?(Rails.root) && Rails.root

  emit_event("action_controller.rescue_from_handled",
    exception_class: exception.class.name,
    exception_message: exception.message,
    exception_backtrace: exception_backtrace
  )
end

変更後:

def rescue_from_callback(event)
  exception = event.payload[:exception]

  if self.class._rescue_from_event_backtrace == :array
    exception_backtrace = exception.backtrace
  else
    exception_backtrace = exception.backtrace&.first
    exception_backtrace = exception_backtrace&.delete_prefix("#{Rails.root}/") if defined?(Rails.root) && Rails.root
  end

  emit_event("action_controller.rescue_from_handled",
    exception_class: exception.class.name,
    exception_message: exception.message,
    exception_backtrace: exception_backtrace
  )
end

設定値はrailties/lib/rails/application/configuration.rbで、Rails 8.2のデフォルト値として:arrayが指定されます。この設定はaction_controller.structured_event_subscriberイニシャライザを通じてActionController::StructuredEventSubscriberに渡されます。

ActionController::LogSubscriberも更新され、配列形式のバックトレースを受け取った場合は最初の要素を取り出してログに出力します:

def rescue_from_handled(event)
  exception_class = event[:payload][:exception_class]
  exception_message = event[:payload][:exception_message]
  exception_backtrace = event[:payload][:exception_backtrace]
  if exception_backtrace.is_a?(Array)
    exception_backtrace = exception_backtrace&.first
    exception_backtrace = exception_backtrace&.delete_prefix("#{Rails.root}/") if defined?(Rails.root) && Rails.root
  end
  info { "rescue_from handled #{exception_class} (#{exception_message}) - #{exception_backtrace}" }
end

この実装により、イベントペイロードには完全なバックトレースが含まれる一方、ログ出力は従来どおり最初のフレームのみを表示します。

設計判断

既存の動作を保持しながら新しいオプションを追加する方式が採用されました。PR内の議論では、Rails 8.1で導入されたばかりのイベントを即座に変更するか、設定フラグと非推奨化プロセスを経るかが検討されました。

最終的に選ばれたアプローチは、config.action_controller.rescue_from_event_backtraceという設定項目を導入し、Rails 8.2では:arrayをデフォルトにするというものです。nilの場合は従来の動作(最初のフレームのみ)を維持し、:arrayの場合は完全なバックトレースを提供します。

この判断により、Rails 8.1ですでにこのイベントを利用している可観測性ツールに対して、CHANGELOGと設定フラグという明確な移行パスを提供しています。イベント自体が新しいため、即座に変更することも可能でしたが、より慎重なアプローチが選ばれました。

まとめ

本PRは、rescue_from_handled.action_controllerイベントのペイロードに完全なバックトレースを含められるようにした変更です。設定可能な形式にすることで、既存のイベントリスナーとの互換性を保ちながら、Active Jobイベントとの一貫性を確保しています。Rails 8.2では:arrayがデフォルトとなり、より詳細なエラートラッキングが標準で利用可能になります。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

「リード文(総論)→セクション群(各論)→まとめ(結論)」の構成が明確です。背景、技術的な変更、設計判断といった必須・任意要素がすべて含まれており、変更の文脈を深く理解できる構成になっています。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```ruby:path/to/file.rb)とGitHubのPR/Issueリンク記法([#123](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

内容はRailsエンジニアを対象としており、専門用語の使用や説明の粒度が適切です。初心者向けの過度な解説がなく、簡潔にまとめられています。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論の構成になっており、各段落はトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られています。これにより、非常に高い可読性を実現しています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内で引用されているコードブロックは、提供されたDiff情報と完全に一致しています。ファイル名も正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「ペイロード」「バックトレース」「可観測性」などの技術用語が、PRの文脈に沿って正確かつ適切に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

設定変更による挙動の変化や、その背景にある技術的な理由が、DiffとPR情報を基に正確に説明されています。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張は、PRのDescription、Diff、または参照されているIssueの情報によって裏付けられています。ハルシネーション(捏造)や根拠のない推測は見られません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#56426)、Issue番号(#56060)、バージョン番号(8.1, 8.2)などの数値や固有名詞はすべて正確です。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトルは、PRのタイトル「Make `rescue_from_handled.action_controller` store the full backtrace」の内容を的確に要約しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

記事で言及されているバージョン情報(Rails 8.1, 8.2)は、すべてPR内のDiff(コンフィグファイルやガイド)に基づいています。PRに記載のない外部知識の追加はありません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「Rails 8.1で導入された」「Rails 8.2ではデフォルトとなり」といった時間表現は、PR情報と正確に一致しています。