ruby/debugメンテナーが教える2025年のデバッグ便利技集(翻訳)
これは、私が編み出したRubyのデバッグ技と推奨事項を未完成のまま取り急ぎまとめたものです。
- 🔗 VS CodeのRuby LSP拡張を使ってdebug.gemに接続することも可能です。
この場合、launch.json設定をわずかに変更する必要があります(例を参照)。これによって接続時の問題に対処するためのエラー処理が改善されます。
- 🔗
launch.json設定ファイルで、attachの代わりにlaunchリクエストを使ってみてください。
これにより、デバッグサーバーを手動で起動/停止する必要がなくなるので、デバッグ作業がシンプルになります。ほとんどのRailsプロジェクトは、以下のようなシンプルなエントリで十分です。
{
"version": "0.2.0",
"configurations": [
{
"type": "ruby_lsp",
"name": "Launch Server",
"request": "launch",
"program": "bin/rails s",
},
]
}
- 🔗 デバッグセッションの効率は、メソッド/クラス/ファイル間を自在に移動する能力に大きく依存します。エディタの設定を万全にしておきましょう(Ruby LSPなど)。
- Ruby LSPは、VS Code以外の多くのエディタやIDEでも利用できます。
- 🔗 Ruby LSPのCode Lens機能は、ターミナルとVS Codeのどちらでもテストのデバッグを楽にしてくれます。
- なお、RSpecのテストにcode lensを提供するためにruby-lsp-rspec gemを作りました。
- 🔗
Gemfileにgem "debug", require: "debug/prelude"と記述しましょう。
debug.gemは必要に応じてアクティブになりますが、デバッグしていないときは不要です。
require: "debug/prelude"を指定することで、breakpointやbinding.breakに似たブレークポイント用メソッドを定義しても、このgemはすぐにはアクティブになりません。
(この振る舞いは、Rails 7.2以降で生成されたプロジェクトではデフォルトになっています) -
🔗 debug.gemを特定の環境でのみ無効にしておきたい場合は、
RUBY_DEBUG_ENABLEを0に設定できます。
たとえば、これをCIに設定しておけば、消し忘れのdebuggerやbinding.breakでエラーがraiseされるようになり、デバッガのせいでCIが立ち往生せずに済みます。
begin
# debugの読み込みを試みるが、誤って有効にされないよう
# configコンポーネントだけを読み込む
require "debug/config"
zeitwerk_paths = Gem.loaded_specs["zeitwerk"].full_require_paths.freeze
bootsnap_paths = Gem.loaded_specs["bootsnap"].full_require_paths.freeze
DEBUGGER__::CONFIG[:skip_path] = Array(DEBUGGER__::CONFIG[:skip_path]) + zeitwerk_paths + bootsnap_paths
rescue LoadError
# debug.gemが何らかの理由で読み込まれていなかった場合。
# 例: このファイルがproduction環境で読み込まれた場合
# (通常はproduction環境にdebug.gemをインストールしない)
end
例: Sorbetを使っている場合は、無視するgemリストに以下のようにsorbet-runtimeを追加できます。
sorbet_paths = Gem.loaded_specs["sorbet-runtime"].full_require_paths.freeze
DEBUGGER__::CONFIG[:skip_path] = Array(DEBUGGER__::CONFIG[:skip_path]) + sorbet_paths
- 🔗 debug.gemとIRBの統合を有効にするとデバッグのエクスペリエンスが改善されるので、ほとんどのRubyユーザーにおすすめできる便利技です。これは以下の設定で行えます。
RUBY_DEBUG_IRB_CONSOLEを1に設定するDEBUGGER__::CONFIG[:irb_console]をtrueに設定する
- 🔗 以下のdebug.gemコマンドは、Enterキーを押すと同じコマンドを繰り返せます。
step(s)next(n)continue(c)finish(fin)until(u)updown
たとえば、sを入力してからEnterキーを押し、もう一度Enterキーを押すとstepコマンドが繰り返されます。
- 🔗 ブレークポイントに到達した後でコマンドを自動実行するには、
debugger(do: "...")やdebugger(pre: "...")が使えます。
# ローカル変数を出力してからコンソールを開く
debugger(pre: "info locals")
# ローカル変数を出力してからプログラムを続行する
debugger(do: "info locals")
- 🔗
trace exceptionコマンドとcatch [exception]コマンドを組み合わせると、制御フローに関連するデバッグがやりやすくなります。trace exceptionは、例外がraiseされたときにトレースを出力するcatch [exception]は、例外がraiseされたときに実行を中断する
- 🔗
bt [n]コマンドとup/downコマンドの組み合わせは、同じコードパスにブレークポイントを複数設定するよりも多くの場合効率的です。
- 🔗 debug.gemは、ブレークポイントにさしかかるとすべてのスレッドをフリーズさせます。これが問題になる場合は、代わりに
binding.irbをお使いください。binding.irbはREPLで簡単なデバッグを行えます。そこでdebugコマンドを実行するとdebug.gemを有効にして本格的にデバッグできます。
- 🔗 デバッグの基本的な概念について学びたい方には、私がRubyKaigi 2022で登壇したときの動画が今でも役立つでしょう。
以上のデバッグ技がお役に立てば幸いです。Happy debugging!
概要
元サイトの許諾を得て翻訳・公開いたします。
日本語タイトルは内容に即したものにしました。