本ガイドでは、Ruby on Rails アプリケーションのさまざまなデバッグ技法をご紹介します。
このガイドの内容:
変数にどんな値が入っているかを確認する作業は何かと必要になります。Railsでは以下の3つのメソッドを利用できます。
debugto_yamlinspectdebugdebugヘルパーは<pre>タグを返します。このタグの中にYAML形式でオブジェクトが出力されます。これにより、あらゆるオブジェクトを人間が読めるデータに変換できます。たとえば、以下のコードがビューにあるとします。
<%= debug @article %> <p> <b>Title:</b> <%= @article.title %> </p>
ここから以下のような出力を得られます。
--- !ruby/object Article
attributes:
updated_at: 2008-09-05 22:55:47
body: It's a very helpful guide for debugging your Rails app.
title: Rails debugging guide
published: t
id: "1"
created_at: 2008-09-05 22:55:47
attributes_cache: {}
Title: Rails debugging guide
to_yaml他の方法として、任意のオブジェクトに対してto_yamlを呼び出すことでYAMLに変換できます。変換したこのオブジェクトは、simple_formatヘルパーメソッドに渡して出力を整形できます。これはdebugのマジックです。
インスタンス変数や、その他のあらゆるオブジェクトやメソッドをYAML形式で表示します。以下のような感じで使います。
<%= simple_format @article.to_yaml %> <p> <b>Title:</b> <%= @article.title %> </p>
これにより、以下のような結果がビューに表示されます。
--- !ruby/object Article
attributes:
updated_at: 2008-09-05 22:55:47
body: It's a very helpful guide for debugging your Rails app.
title: Rails debugging guide
published: t
id: "1"
created_at: 2008-09-05 22:55:47
attributes_cache: {}
Title: Rails debugging guide
inspectオブジェクトの値を表示するのに便利なメソッドとしてinspectも利用できます。特に、配列やハッシュを扱うときに便利です。このメソッドはオブジェクトの値を文字列として出力します。以下に例を示します。
<%= [1, 2, 3, 4, 5].inspect %> <p> <b>Title:</b> <%= @article.title %> </p>
上のコードから以下の出力を得られます。
[1, 2, 3, 4, 5] Title: Rails debugging guide
実行時に情報をログに保存できるとさらに便利です。Railsは実行環境ごとに異なるログファイルを出力するようになっています。
RailsはActiveSupport::Loggerクラスを利用してログ情報を出力します。必要に応じて、Log4rなど別のロガーに差し替えることもできます。
別のロガーの指定は、config/application.rbまたは環境ごとの設定ファイルで行います。
config.logger = Logger.new(STDOUT) config.logger = Log4r::Logger.new("Application Log")
あるいは、Initializerセクションに以下のいずれかを追加します。
Rails.logger = Logger.new(STDOUT) Rails.logger = Log4r::Logger.new("Application Log")
ログの保存場所は、デフォルトではRails.root/log/になります。ログのファイル名は、アプリケーションが実行されるときの環境 (development/test/productionなど) が使われます。
ログに出力されるメッセージのログレベルが、設定済みのログレベル以上になった場合に、対応するログファイルにそのメッセージが出力されます。現在のログレベルを知りたい場合は、Rails.logger.levelメソッドを呼び出します。
指定可能なログレベルは:debug、:info、:warn、:error、:fatal、:unknownの6つであり、それぞれ0から5までの数字に対応します。デフォルトのログレベルを変更するには以下のようにします。
config.log_level = :warn # 環境ごとのイニシャライザで利用可能 Rails.logger.level = 0 # いつでも利用可能
これは、development環境やstaging環境ではログを出力し、production環境では不要な情報をログに出力したくない場合などに便利です。
Railsのデフォルトログレベルは全環境でdebugです。
コントローラ、モデル、メーラーから現在のログに書き込みたい場合は、logger.(debug|info|warn|error|fatal)を使います。
logger.debug "Person attributes hash: #{@person.attributes.inspect}" logger.info "Processing the request..." logger.fatal "Terminating application, raised unrecoverable error!!!"
例として、ログに別の情報を追加する機能を装備したメソッドを以下に示します。
class ArticlesController < ApplicationController # ... def create @article = Article.new(article_params) logger.debug "新しい記事: #{@article.attributes.inspect}" logger.debug "記事が正しいかどうか: #{@article.valid?}" if @article.save flash[:notice] = 'Article was successfully created.' logger.debug "記事は正常に保存され、ユーザーをリダイレクト中..." redirect_to @article, notice: '記事は正常に作成されました。' else render :new end end # ... private def article_params params.require(:article).permit(:title, :body, :published) end end
上のコントローラのアクションを実行すると、以下のようなログが生成されます。
Started POST "/articles" for 127.0.0.1 at 2017-08-20 20:53:10 +0900
Processing by ArticlesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"xhuIbSBFytHCE1agHgvrlKnSVIOGD6jltW2tO+P6a/ACjQ3igjpV4OdbsZjIhC98QizWH9YdKokrqxBCJrtoqQ==", "article"=>{"title"=>"Debugging Rails", "body"=>"I'm learning how to print in logs!!!", "published"=>"0"}, "commit"=>"Create Article"}
New article: {"id"=>nil, "title"=>"Debugging Rails", "body"=>"I'm learning how to print in logs!!!", "published"=>false, "created_at"=>nil, "updated_at"=>nil}
記事が正しいかどうか: true
(0.1ms) BEGIN
SQL (0.4ms) INSERT INTO "articles" ("title", "body", "published", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["title", "Debugging Rails"], ["body", "I'm learning how to print in logs!!!"], ["published", "f"], ["created_at", "2017-08-20 11:53:10.010435"], ["updated_at", "2017-08-20 11:53:10.010435"]]
(0.3ms) COMMIT
記事は正常に保存され、ユーザーをリダイレクト中...
Redirected to http://localhost:3000/articles/1
Completed 302 Found in 4ms (ActiveRecord: 0.8ms)
このようにログに独自の情報を追加すると、予想外の異常な動作をログで見つけやすくなります。ログに独自の情報を追加する場合は、productionログが意味のない大量のメッセージでうずまることのないよう、適切なログレベルを使うようにしてください。
ユーザーとアカウントを多数使うアプリケーションを実行するときに、何らかのカスタムルールを設定してログをフィルタできると便利です。Active SupportのTaggedLoggingを使えば、サブドメインやリクエストIDなどを指定してログを絞り込むことができ、このようなアプリケーションのデバッグがはかどります。
logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)) logger.tagged("BCX") { logger.info "Stuff" } # Logs "[BCX] Stuff" logger.tagged("BCX", "Jason") { logger.info "Stuff" } # Logs "[BCX] [Jason] Stuff" logger.tagged("BCX") { logger.tagged("Jason") { logger.info "Stuff" } } # Logs "[BCX] [Jason] Stuff"
ログ出力はRailsアプリケーションのパフォーマンスに小さな影響を常に与えます。ログをディスクに保存する場合は特にそうです。さらに、場合によっては小さな影響とは言い切れないことがあります。
ログレベル:debugは、:fatalと比べてはるかに多くの文字列が評価および(ディスクなどに)出力されるため、パフォーマンスに与える影響がずっと大きくなります。
他にも、以下のようにLoggerの呼び出しを多数実行した場合には落とし穴に注意する必要があります。
logger.debug "Person attributes hash: #{@person.attributes.inspect}"
上の例では、たとえログ出力レベルをdebugにしなかった場合でもパフォーマンスが低下します。その理由は、上のコードでは文字列を評価する必要があり、その際に比較的動作が重いStringオブジェクトのインスタンス化と、実行に時間のかかる変数の式展開 (interpolation) が行われているからです。
したがって、ロガーメソッドに渡すものはブロックの形にしておくことをお勧めします。ブロックとして渡しておけば、ブロックの評価は出力レベルが設定レベル以上になった場合にしか行われない (遅延読み込みなど) ためです。これに従って上のコードを書き直すと以下のようになります。
logger.debug {"Person attributes hash: #{@person.attributes.inspect}"}
渡したブロックの内容 (ここでは文字列の式展開) は、debug が有効になっている場合にしか評価されません。この方法によるパフォーマンスの改善は、大量のログを出力しているときでないとそれほど実感できないかもしれませんが、それでも採用する価値があります。
byebug gemでデバッグするコードが期待どおりに動作しない場合は、ログやコンソールに出力して問題を診断することができます。ただし、この方法ではエラー追跡を何度も繰り返さねばならず、根本的な原因を突き止めるには能率がよいとは言えません。 実行中のコードに探りを入れる必要があるのであれば、最も頼りになるのはやはりデバッガーです。
デバッガーは、Railsのソースコードを追うときに、そのコードをどこで開始するのかがを知りたいときにも有用です。アプリケーションへのリクエストをすべてデバッグし、自分が書いたコードからRailsのもっと深いところへダイブする方法を本ガイドから学びましょう。
byebug gemを使うと、Railsコードにブレークポイントを設定してステップ実行できます。次を実行するだけでインストールできます。
$ gem install byebug
後はRailsアプリケーション内でbyebugメソッドを呼び出せばいつでもデバッガーを起動できます。
以下に例を示します。
class PeopleController < ApplicationController def new byebug @person = Person.new end end
アプリケーションでbyebugを呼び出すと、アプリケーションサーバーを実行しているターミナルウィンドウ内のデバッガーシェルで即座にデバッガーが起動し、(byebug)というプロンプトが表示されます。
実行しようとしている行の前後のコードがプロンプトの前に表示され、'=>'で現在の行が示されます。以下に例を示します。
[1, 10] in /PathTo/project/app/controllers/articles_controller.rb
3:
4: # GET /articles
5: # GET /articles.json
6: def index
7: byebug
=> 8: @articles = Article.find_recent
9:
10: respond_to do |format|
11: format.html # index.html.erb
12: format.json { render json: @articles }
(byebug)
ブラウザからのリクエストによってデバッグ行に到達した場合、リクエストしたブラウザのタブ上の処理は、デバッガが終了してリクエストの処理が完全に終了するまで中断します。
以下に例を示します。
=> Booting Puma => Rails 5.1.0 application starting in development on http://0.0.0.0:3000 => Run `rails server -h` for more startup options Puma starting in single mode... * Version 3.4.0 (ruby 2.3.1-p112), codename: Owl Bowl Brawl * Min threads: 5, max threads: 5 * Environment: development * Listening on tcp://localhost:3000 Use Ctrl-C to stop Started GET "/" for 127.0.0.1 at 2014-04-11 13:11:48 +0200 ActiveRecord::SchemaMigration Load (0.2ms) SELECT "schema_migrations".* FROM "schema_migrations" Processing by ArticlesController#index as HTML [3, 12] in /PathTo/project/app/controllers/articles_controller.rb 3: 4: # GET /articles 5: # GET /articles.json 6: def index 7: byebug => 8: @articles = Article.find_recent 9: 10: respond_to do |format| 11: format.html # index.html.erb 12: format.json { render json: @articles } (byebug)
それではアプリケーションをもっと詳しく見てみましょう。まずはデバッガーのヘルプを表示してみるのがよいでしょう。helpと入力します。
(byebug) help break -- Sets breakpoints in the source code catch -- Handles exception catchpoints condition -- Sets conditions on breakpoints continue -- Runs until program ends, hits a breakpoint or reaches a line debug -- Spawns a subdebugger delete -- Deletes breakpoints disable -- Disables breakpoints or displays display -- Evaluates expressions every time the debugger stops down -- Moves to a lower frame in the stack trace edit -- Edits source files enable -- Enables breakpoints or displays finish -- Runs the program until frame returns frame -- Moves to a frame in the call stack help -- Helps you using byebug history -- Shows byebug's history of commands info -- Shows several informations about the program being debugged interrupt -- Interrupts the program irb -- Starts an IRB session kill -- Sends a signal to the current process list -- Lists lines of source code method -- Shows methods of an object, class or module next -- Runs one or more lines of code pry -- Starts a Pry session quit -- Exits byebug restart -- Restarts the debugged program save -- Saves current byebug session to a file set -- Modifies byebug settings show -- Shows byebug settings source -- Restores a previously saved byebug session step -- Steps into blocks or methods one or more times thread -- Commands to manipulate threads tracevar -- Enables tracing of a global variable undisplay -- Stops displaying all or some expressions when program stops untracevar -- Stops tracing a global variable up -- Moves to a higher frame in the stack trace var -- Shows variables and its values where -- Displays the backtrace (byebug)
前の10行を表示するには、list- (または l-) と入力します。
(byebug) l- [1, 10] in /PathTo/project/app/controllers/articles_controller.rb 1 class ArticlesController < ApplicationController 2 before_action :set_article, only: [:show, :edit, :update, :destroy] 3 4 # GET /articles 5 # GET /articles.json 6 def index 7 byebug 8 @articles = Article.find_recent 9 10 respond_to do |format|
上に示したように、該当のファイルに移動して、byebug呼び出しを追加した行の前を表示できます。最後に、list=と入力して現在の位置を再び表示してみましょう。
(byebug) list=
[3, 12] in /PathTo/project/app/controllers/articles_controller.rb
3:
4: # GET /articles
5: # GET /articles.json
6: def index
7: byebug
=> 8: @articles = Article.find_recent
9:
10: respond_to do |format|
11: format.html # index.html.erb
12: format.json { render json: @articles }
(byebug)
アプリケーションのデバッグ中は、通常と異なる「コンテキスト」に置かれます。具体的には、スタックの別の部分を通って進むコンテキストです。
デバッガーは、停止位置やイベントに到達するときに「コンテキスト」を作成します。作成されたコンテキストには、中断しているプログラムに関する情報が含まれており、デバッガーはこの情報を用いて、フレームスタックの検査やデバッグ中のプログラムにおける変数の評価を行い、デバッグ中のプログラムが停止している位置の情報を認識します。
backtraceコマンド (またはそのエイリアスであるwhereコマンド) を使えば、いつでもアプリケーションのバックトレースを出力できます。これは、コードのその位置に至るまでの経過を知るうえで非常に便利です。コードのある行にたどりついたとき、その経緯を知りたければbacktraceでわかります。
(byebug) where
--> #0 ArticlesController.index
at /PathToProject/app/controllers/articles_controller.rb:8
#1 ActionController::BasicImplicitRender.send_action(method#String, *args#Array)
at /PathToGems/actionpack-5.1.0/lib/action_controller/metal/basic_implicit_render.rb:4
#2 AbstractController::Base.process_action(action#NilClass, *args#Array)
at /PathToGems/actionpack-5.1.0/lib/abstract_controller/base.rb:181
#3 ActionController::Rendering.process_action(action, *args)
at /PathToGems/actionpack-5.1.0/lib/action_controller/metal/rendering.rb:30
...
現在のフレームは-->で示されます。frame nコマンド (nはフレーム番号) を使えば、トレース内のどのコンテキストにも自由に移動できます。このコマンドを実行すると、byebugは新しいコンテキストを表示します。
(byebug) frame 2 [176, 185] in /PathToGems/actionpack-5.1.0/lib/abstract_controller/base.rb 176: # is the intended way to override action dispatching. 177: # 178: # Notice that the first argument is the method to be dispatched 179: # which is *not* necessarily the same as the action name. 180: def process_action(method_name, *args) => 181: send_action(method_name, *args) 182: end 183: 184: # Actually call the method associated with the action. Override 185: # this method if you wish to change how action methods are called, (byebug)
コードを1行ずつ実行していた場合、利用できる変数は同一です。つまり、これこそがデバッグという作業です。
up [n] (短縮形のuも可) コマンドやdown [n]コマンドを使って、スタックを nフレーム上または下に移動し、コンテキストを切り替えることもできます。upはスタックフレーム番号の大きい方に進み、downは小さい方に進みます。
デバッガーでthread(短縮形はth) コマンドを使うと、スレッド実行中にスレッドのリスト表示/停止/再開/切り替えを行えます。このコマンドには以下のささやかなオプションがあります。
threadは現在のスレッドを表示します。thread listはすべてのスレッドのリストをステータス付きで表示します。現在実行中のスレッドは「+」記号と数字で示されます。thread stop nはスレッド**n_を停止します。thread resume nはスレッドnを再開します。thread switch nは現在のスレッドコンテキストをnに切り替えます。このコマンドは、同時実行(コンカレント)スレッドのデバッグ中に、コードで競合状態が発生していないかどうかの確認が必要な場合にも非常に便利です。
すべての式は、現在のコンテキストで評価されます。式を評価するには、単にその式を入力します。
次の例では、現在のコンテキスト内で定義されたインスタンス変数を出力する方法を示しています。
[3, 12] in /PathTo/project/app/controllers/articles_controller.rb
3:
4: # GET /articles
5: # GET /articles.json
6: def index
7: byebug
=> 8: @articles = Article.find_recent
9:
10: respond_to do |format|
11: format.html # index.html.erb
12: format.json { render json: @articles }
(byebug) instance_variables
[:@_action_has_layout, :@_routes, :@_request, :@_response, :@_lookup_context,
:@_action_name, :@_response_body, :@marked_for_same_origin_verification,
:@_config]
見ての通り、コントローラからアクセスできるすべての変数が表示されています。表示される変数リストは、コードの実行に伴って動的に更新されます。
たとえば、nextコマンドで次の行に進んだとします (このコマンドの詳細については後述します)。
(byebug) next
[5, 14] in /PathTo/project/app/controllers/articles_controller.rb
5 # GET /articles.json
6 def index
7 byebug
8 @articles = Article.find_recent
9
=> 10 respond_to do |format|
11 format.html # index.html.erb
12 format.json { render json: @articles }
13 end
14 end
15
(byebug)
それではinstance_variablesをもう一度調べてみましょう。
(byebug) instance_variables [:@_action_has_layout, :@_routes, :@_request, :@_response, :@_lookup_context, :@_action_name, :@_response_body, :@marked_for_same_origin_verification, :@_config, :@articles]
定義行が実行されたことによって、今度は@articlesもインスタンス変数に表示されます。
irbコマンドを使うことで、irbモードで実行できます。
これにより、呼び出し中のコンテキスト内でirbセッションが開始されます。
変数と値のリストを表示するのに便利なのは何と言ってもvarメソッドでしょう。
byebugでこのメソッドを使ってみましょう。
(byebug) help var [v]ar <subcommand> Shows variables and its values var all -- Shows local, global and instance variables of self. var args -- Information about arguments of the current scope var const -- Shows constants of an object. var global -- Shows global variables. var instance -- Shows instance variables of self or a specific object. var local -- Shows local variables in current scope.
このメソッドは、現在のコンテキストでの変数の値を検査するのにうってつけの方法です。たとえば、現時点でローカル変数が何も定義されていないことを確認してみましょう。
(byebug) var local (byebug)
以下の方法でオブジェクトのメソッドを検査することもできます。
(byebug) var instance Article.new
@_start_transaction_state = {}
@aggregation_cache = {}
@association_cache = {}
@attributes = #<ActiveRecord::AttributeSet:0x007fd0682a9b18 @attributes={"id"=>#<ActiveRecord::Attribute::FromDatabase:0x007fd0682a9a00 @name="id", @value_be...
@destroyed = false
@destroyed_by_association = nil
@marked_for_destruction = false
@new_record = true
@readonly = false
@transaction_state = nil
displayコマンドを使って変数をウォッチすることもできます。これは、デバッガーで実行を進めながら変数の値の移り変わりを追跡するのに大変便利です。
(byebug) display @articles 1: @articles = nil
スタック内で移動するたびに、そのときの変数と値のリストが出力されます。変数の表示を止めるには、undisplay n(n は変数番号) を実行します。上の例では変数番号は 1 になっています。
これで、トレース実行中に現在の実行位置を確認し、利用可能な変数をいつでも確認できるようになりました。アプリケーションの実行について引き続き学んでみましょう。
stepコマンド (短縮形はs) を使うと、プログラムの実行を継続し、次の論理的な停止行まで進んだらデバッガーに制御を返します。nextはstepと似ていますが、stepがコードを1ステップだけ実行して次の行で停止するのに対し、nextは次の行に進む際にメソッド内の呼び出し先に移動しない点が異なります。
たとえば、次のような状況を考えてみましょう
Started GET "/" for 127.0.0.1 at 2014-04-11 13:39:23 +0200
Processing by ArticlesController#index as HTML
[1, 6] in /PathToProject/app/models/article.rb
1: class Article < ApplicationRecord
2: def self.find_recent(limit = 10)
3: byebug
=> 4: where('created_at > ?', 1.week.ago).limit(limit)
5: end
6: end
(byebug)
nextを使うと、メソッドの呼び出し先は表示されません。byebugはその代わりに、単に同じコンテキストの次の行に進みます。この例の場合、次の行とは現在のメソッドの最終行になります。つまり、byebugは呼び出し元メソッドの次の行に戻ります。
(byebug) next
[4, 13] in /PathToProject/app/controllers/articles_controller.rb
4: # GET /articles
5: # GET /articles.json
6: def index
7: @articles = Article.find_recent
8:
=> 9: respond_to do |format|
10: format.html # index.html.erb
11: format.json { render json: @articles }
12: end
13: end
(byebug)
同じ状況でstepを使うと、byebugは文字通り「Rubyコードの、次に実行すべき行」に進みます。ここではActive Supportのweekメソッドにジャンプして進むことになります。
(byebug) step [49, 58] in /PathToGems/activesupport-5.1.0/lib/active_support/core_ext/numeric/time.rb 49: 50: # Returns a Duration instance matching the number of weeks provided. 51: # 52: # 2.weeks # => 14 days 53: def weeks => 54: ActiveSupport::Duration.weeks(self) 55: end 56: alias :week :weeks 57: 58: # Returns a Duration instance matching the number of fortnights provided. (byebug)
これは自分のコードのバグを見つけ出す方法として非常に優れています。
nステップ進めたい場合は、step nやnext nでステップ数を指定できます。
ブレークポイントとは、アプリケーションの実行がプログラムの特定の場所に達した時に停止する位置を指します。そしてその場所でデバッガーシェルが起動します。
break (またはb) コマンドを使ってブレークポイントを動的に追加できます。
手動でブレークポイントを追加する方法は次の3とおりです。
break n: 現在のソースファイルのn行目に示された行にブレークポイントを設定します。break ファイル名:n [if 式]: ファイル名のn行目にブレークポイントを設定します。式を指定すると、この式がtrueと評価された場合にのみデバッガが起動します。break class(.|\#)method [if 式]: classに定義されているmethodにブレークポイントを設定します (「.」と「#」はそれぞれクラスとインスタンスメソッドを指す)。式の動作はファイル名:nの場合と同じです。さっきと同じ状況を例に説明します。
[4, 13] in /PathToProject/app/controllers/articles_controller.rb
4: # GET /articles
5: # GET /articles.json
6: def index
7: @articles = Article.find_recent
8:
=> 9: respond_to do |format|
10: format.html # index.html.erb
11: format.json { render json: @articles }
12: end
13: end
(byebug) break 11
Successfully created breakpoint with id 1
ブレークポイントをリスト表示するには、info breakpointsを使います。番号を指定すると、その番号のブレークポイントをリスト表示します。番号を指定しない場合は、すべてのブレークポイントをリスト表示します。
(byebug) info breakpoints Num Enb What 1 y at /PathToProject/app/controllers/articles_controller.rb:11
delete nコマンドを使うとn番のブレークポイントを削除できます。番号を指定しない場合、現在有効なブレークポイントをすべて削除します。
(byebug) delete 1 (byebug) info breakpoints No breakpoints.
ブレークポイントを有効にしたり、無効にしたりすることもできます。
enable ブレークポイント [n [m [...]]]: 指定したブレークポイントのリスト (無指定の場合はすべてのブレークポイント) でのプログラムの停止を有効にします。ブレークポイントを作成するとデフォルトでこの状態になります。disable breakpoints [n [m [...]]]: 指定した(指定しない場合はすべての)ブレークポイントのリストで停止しなくなります。catch exception-name (省略形は cat exception-name) を使うと、例外を受けるハンドラが他にないと考えられる場合に、exception-nameで例外の種類を指定してインターセプトできます。
例外のキャッチポイントをすべてリスト表示するには単にcatchと入力します。
デバッガーで停止したアプリケーションの再開方法は2種類あります。
continue [n]: スクリプトが直前に停止していたアドレスからプログラムの実行を再開します。このとき、そのアドレスに設定されていたブレークポイントはすべて無視されます。オプションとして、特定の行番号nをワンタイムブレークポイントとして指定できます。このブレークポイントは、ワンタイムブレークポイントに達すると削除されます。finish [n]: 指定のスタックフレームが返るまで実行を続けます。フレーム番号が指定されていない場合は、現在選択しているフレームが返るまで実行を続けます。現在選択しているフレームは直近のフレームから開始され、フレーム位置の指定操作(upやdownやフレーム番号指定など)が行われていない場合は0から開始されます。フレーム番号を指定すると、そのフレームが返るまで実行を続けます。デバッガー上のコードをエディタで開くためのコマンドは2種類あります。
edit [file:n]: ファイル名fileをエディタで開きます。エディタはEDITOR環境変数で指定します。n行で行数を指定することもできます。デバッガーを終了するには、quitコマンド (短縮形は q) を使います。q!と入力すると、「Really quit? (y/n)」というプロンプトをスキップして無条件に終了します。
単にquitを実行すると、事実上すべてのスレッドを終了しようとします。これによりサーバーが停止するので、サーバーを再起動する必要があります。
byebugの振る舞いを変更するためのオプションがいくつかあります。
(byebug) help set set <setting> <value> Modifies byebug settings Boolean values take "on", "off", "true", "false", "1" or "0". If you don't specify a value, the boolean setting will be enabled. Conversely, you can use "set no<setting>" to disable them. You can see these environment settings with the "show" command. List of supported settings: autosave -- Automatically save command history record on exit autolist -- Invoke list command on every stop width -- Number of characters per line in byebug's output autoirb -- Invoke IRB on every stop basename -- <file>:<line> information after every stop uses short paths linetrace -- Enable line execution tracing autopry -- Invoke Pry on every stop stack_on_error -- Display stack trace when `eval` raises an exception fullpath -- Display full file names in backtraces histfile -- File where cmd history is saved to. Default: ./.byebug_history listsize -- Set number of source lines to list by default post_mortem -- Enable/disable post-mortem mode callstyle -- Set how you want method call parameters to be displayed histsize -- Maximum number of commands that can be stored in byebug history savefile -- File where settings are saved to. Default: ~/.byebug_save
これらの設定は、ホームディレクトリの.byebugrcファイルに保存しておくことができます。
デバッガーが起動すると、この設定がグローバルに適用されます。以下に例を示します。
set callstyle short
set listsize 25
web-console gemによるデバッグWeb Consoleはbyebugと似ていますが、ブラウザ上で動作する点が異なります。開発中のどのページでも、ビューやコントローラのコンテキストでコンソールをリクエストできます。コンソールは、HTMLコンテンツの隣に表示されます。
consoleメソッドを呼び出すことで、任意のコントローラのアクションやビューでいつでもコンソールを呼び出せます。
たとえば、コントローラで以下のように呼び出せます。
class PostsController < ApplicationController def new console @post = Post.new end end
ビューでも以下のように呼び出せます。
<% console %> <h2>New Post</h2>
上のコードは、ビューの内部でコンソールを出力します。consoleを呼び出す位置を気にする必要はありません。コンソールは、呼び出し位置にかかわらず、HTMLコンテンツの隣りに出力されます。
コンソールでは純粋なRubyコードを実行できます。ここでカスタムクラスの定義やインスタンス化を行ったり、新しいモデルを作成したり、変数を検査したりすることができます。
一回のリクエストで出力できるコンソールは1つだけです。console呼び出しを2回以上行うとweb-consoleでエラーが発生します。
instance_variablesを呼び出すと、コンテキストで利用可能なインスタンス変数をすべてリスト表示できます。すべてのローカル変数をリスト表示したい場合は、local_variablesを使います。
config.web_console.whitelisted_ips: 認証済みの IPv4/IPv6アドレスとネットワークのリストです (デフォルト値: 127.0.0.1/8、::1).config.web_console.whiny_requests: コンソール出力が抑制されている場合にメッセージをログ出力します (デフォルト値: true).web-consoleはサーバー上の純粋なRubyコードをリモート評価できるので、production環境では絶対に使わないください。
Railsに限らず、Rubyアプリケーションではメモリーリークが発生することがあります。リークはRubyコードレベルのこともあれば、Cコードレベルであることもあります。
このセクションでは、Valgrindなどのツールを使ってこうしたメモリーリークの検出と修正を行う方法をご紹介します。
Valgrindはアプリケーションであり、Cコードベースのメモリーリークや競合状態の検出を行います。
Valgrindには、さまざまなメモリー管理上のバグやスレッドバグなどを自動検出し、プログラムの詳細なプロファイリングを行うための各種ツールがあります。たとえば、インタプリタ内にあるC拡張機能がmalloc()を呼び出した後free()を正しく呼び出さなかった場合、このメモリーはアプリケーションが終了するまで利用できなくなります。
Valgrindのインストール方法とRuby内での利用方法については、ValgrindとRuby(Evan Weaver著、英語) を参照してください。
アプリケーションのエラーを検出し、デバッグするためのRailsプラグインがあります。デバッグ用に便利なプラグインのリストを以下にご紹介します。
Railsガイドは GitHub の yasslab/railsguides.jp で管理・公開されております。本ガイドを読んで気になる文章や間違ったコードを見かけたら、気軽に Pull Request を出して頂けると嬉しいです。Pull Request の送り方については GitHub の README をご参照ください。
原著における間違いを見つけたら『Rails のドキュメントに貢献する』を参考にしながらぜひ Rails コミュニティに貢献してみてください 🛠💨✨
本ガイドの品質向上に向けて、皆さまのご協力が得られれば嬉しいです。
Railsガイド運営チーム (@RailsGuidesJP)
Railsガイドは下記の協賛企業から継続的な支援を受けています。支援・協賛にご興味あれば協賛プランからお問い合わせいただけると嬉しいです。