rubyで切り捨てられたバックトレースの代わりに完全なバックトレースを印刷するにはどうすればよいですか?


170

例外が発生した場合、多くの場合、コールスタック内の深いところから発生します。これが発生すると、多くの場合、実際の問題のコード行は私から隠されます。

tmp.rb:7:in `t': undefined method `bar' for nil:NilClass (NoMethodError)
        from tmp.rb:10:in `s'
        from tmp.rb:13:in `r'
        from tmp.rb:16:in `q'
        from tmp.rb:19:in `p'
        from tmp.rb:22:in `o'
        from tmp.rb:25:in `n'
        from tmp.rb:28:in `m'
        from tmp.rb:31:in `l'
         ... 8 levels...
        from tmp.rb:58:in `c'
        from tmp.rb:61:in `b'
        from tmp.rb:64:in `a'
        from tmp.rb:67

その「... 8レベル...」の切り捨ては、私に多大なトラブルを引き起こしています。私はこれについてググるのにあまり成功していません:完全なスタックをダンプに含めたいことをルビーにどのように伝えるのですか?


2
代わりにコマンドラインからこれを行う方法はありますか?
アンドリューグリム

回答:


241

Exception#backtraceにはスタック全体が含まれています。

def do_division_by_zero; 5 / 0; end
begin
  do_division_by_zero
rescue => exception
  puts exception.backtrace
  raise # always reraise
end

(ピータークーパーの Ruby Insideブログに)


15
少なくとも例の完成度を上げるために、例外を再度取り上げます。
レト

13
リレイズするには、言うだけですraise。発生させたい免除を明示的に指定する必要はありません。
Timo

いいですね、私はいつもあなたがレイズするには前の例外をパスしなければならないと思っていました。私はそれがデフォルトで救出された最後の例外になることを知りませんでした。
unflores

コードが例外をスローしない場合は、それがどこに行ったかのスタックトレースを見たいだけですか?
Alex Levine

170

単純なワンライナーが必要な場合も、これを行うことができます。

puts caller

2
素晴らしいトリック。どうもありがとう。raise引数なしで使用できることを知りませんでした。どちらもrescue、ワンライナーとして正しく扱われることは知りませんでした。また、のようなグローバル変数は完全に無視し$!ます。
Dmytrii Nagirniak 2011年

11
:なし昇給/救助の必要性、あなたはちょうどそうのようなカーネル#発信者を、使用することができますputs "this line was reached by #{caller.join("\n")}"
スティーブンC

ああ、私はこの回答を投稿した直後にそれについて知り、それを更新するのを忘れていました。ありがとう
匿名の臆病者2012年

y callerJavaスタックトレースのような出力を出力するために使用します。
so_mv 2012年

caller(0,2)スタックトレースの最新の2つのエントリを返します。省略されたスタックトレースを出力するのに最適です。
Magne、

100

これにより、エラーの説明ときれいでインデントされたスタックトレースが生成されます。

begin               
 # Some exception throwing code
rescue => e
  puts "Error during processing: #{$!}"
  puts "Backtrace:\n\t#{e.backtrace.join("\n\t")}"
end

49

IRBには、このひどい「機能」の設定があり、カスタマイズできます。

~/.irbrc次の行を含むというファイルを作成します。

IRB.conf[:BACK_TRACE_LIMIT] = 100

これにより、100個のスタックフレームを表示できます。 irb、少なくとも。非インタラクティブランタイムの同等の設定を見つけることができませんでした。

IRBカスタマイズの詳細については、Pickaxeブックを参照してください


3
これは、「... Xレベル...」の代わりにバックトレースをより多く表示する方法の問題に対処するため、受け入れられる答えになるはずです。
nickh 2013年

13

コールスタック用の1つのライナー:

begin; Whatever.you.want; rescue => e; puts e.message; puts; puts e.backtrace; end

すべてのgemを含まないコールスタックの1つのライナー:

begin; Whatever.you.want; rescue => e; puts e.message; puts; puts e.backtrace.grep_v(/\/gems\//); end

すべてのgemがなく、現在のディレクトリに相対的なコールスタック用の1つのライナー

begin; Whatever.you.want; rescue => e; puts e.message; puts; puts e.backtrace.grep_v(/\/gems\//).map { |l| l.gsub(`pwd`.strip + '/', '') }; end

2
複数のステートメントがある場合、ワンライナーは実際には悪いことです。
nurettin 2017年

3
@nurettinこれは迅速なデバッグを目的としているため、1行にすることで、ほとんどの場合インタラクティブシェルで簡単にコピーアンドペーストできます
Dorian

@Dorian「インタラクティブシェルが便利な理由(シェルスクリプトを除く)」についての質問を思い出してください。
Sapphire_Brick

9

これがあなたにとって重要である場合、これは公式のRubyトレースを模倣します。

begin
  0/0  # or some other nonsense
rescue => e
  puts e.backtrace.join("\n\t")
       .sub("\n\t", ": #{e}#{e.class ? " (#{e.class})" : ''}\n\t")
end

面白いことに、「未処理の例外」を適切に処理せず、「RuntimeError」として報告しますが、場所は正しいです。


私はあなたの答えに答える賛成票が1つだけあることを後悔しています。私はこれをどこにでも追加します
Dbz

4

(rakeテストまたは自動テストを介して)テスト環境をロードしようとしたときにこれらのエラーが発生し、IRBの提案が役に立たなかった。結局、test / test_helper.rb全体をbegin / rescueブロックでラップし、修正しました。

begin
  class ActiveSupport::TestCase
    #awesome stuff
  end
rescue => e
  puts e.backtrace
end

0

[すべてのスレッドのバックトレースを調べて原因を突き止めます]
完全に拡張されたコールスタックでも、複数のスレッドを使用すると、実際の問題のコード行を非表示にすることができます。

例:1つのスレッドがルビーハッシュを反復処理しており、他のスレッドがそれを変更しようとしています。ブーム!例外!そして、「ビジー」ハッシュを変更しようとしているときに取得するスタックトレースの問題は、ハッシュを変更しようとしている場所まで関数のチェーンを表示しますが、現在それを並行して反復している人は表示しないことです(それを所有している人)!現在実行中のすべてのスレッドのスタックトレースを出力することで、これを理解する方法は次のとおりです。これを行う方法は次のとおりです。

# This solution was found in comment by @thedarkone on https://github.com/rails/rails/issues/24627
rescue Object => boom

    thread_count = 0
    Thread.list.each do |t|
      thread_count += 1
      err_msg += "--- thread #{thread_count} of total #{Thread.list.size} #{t.object_id} backtrace begin \n"
      # Lets see if we are able to pin down the culprit
      # by collecting backtrace for all existing threads:
      err_msg += t.backtrace.join("\n")
      err_msg += "\n---thread #{thread_count} of total #{Thread.list.size} #{t.object_id} backtrace end \n"
    end

    # and just print it somewhere you like:
    $stderr.puts(err_msg)

    raise # always reraise
end

上記のコードスニペットは、実際のスレッド数(x線など)を表示できるので(教育用としても)役立ちます(実際に持っていると思ったスレッドの数とは異なり、多くの場合、これら2つは異なる数値です)。


0

また、バックトレース Ruby gemを使用することもできます(私は作成者です)。

require 'backtrace'
begin
  # do something dangerous
rescue StandardError => e
  puts Backtrace.new(e)
end

4
私たちがあなたの宝石を使いたい理由を少なくとも説明できますか?出力例をいくつか示していただけますか?
ioquatix
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.