例外が発生した場合、多くの場合、コールスタックの奥深くからです。これが起こると、たいていの場合、実際の問題のコード行は私から隠されます:
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レベル...」の切り捨ては、私に多くのトラブルを引き起こしています。私はこれについてはあまり成功していません:Rubyにダンプに完全なスタックを含めることを伝えるにはどうすればよいですか?
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 ブログに触発された)
シンプルなワンライナーが必要な場合は、これを行うこともできます。
puts caller
これにより、エラーの説明と、きれいでインデントされたスタックトレースが生成されます。
begin
# Some exception throwing code
rescue => e
puts "Error during processing: #{$!}"
puts "Backtrace:\n\t#{e.backtrace.join("\n\t")}"
end
IRBには、このひどい「機能」の設定があり、カスタマイズできます。
次の行を含む~/.irbrc
というファイルを作成します。
IRB.conf[:BACK_TRACE_LIMIT] = 100
これにより、irb
に少なくとも100個のスタックフレームが表示されます。非対話型ランタイムの同等の設定を見つけることができませんでした。
IRBのカスタマイズに関する詳細情報は Pickaxe book にあります。
コールスタック用のライナー:
begin; Whatever.you.want; rescue => e; puts e.message; puts; puts e.backtrace; end
すべての宝石なしのコールスタック用のライナー:
begin; Whatever.you.want; rescue => e; puts e.message; puts; puts e.backtrace.grep_v(/\/gems\//); end
すべての宝石を含まない、現在のディレクトリに関連するコールスタック用の1つのライナー
begin; Whatever.you.want; rescue => e; puts e.message; puts; puts e.backtrace.grep_v(/\/gems\//).map { |l| l.gsub(`pwd`.strip + '/', '') }; end
これがあなたにとって重要な場合、これは公式の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」として報告しますが、場所は正しいです。
テスト環境を(rake testまたはautotestを介して)ロードしようとするとこれらのエラーが発生し、IRBの提案は役に立たなかった。結局、test/test_helper.rb全体をbegin/rescueブロックでラップしましたが、それで問題は解決しました。
begin
class ActiveSupport::TestCase
#awesome stuff
end
rescue => e
puts e.backtrace
end
backtrace Ruby gem(私は著者です)も使用できます。
require 'backtrace'
begin
# do something dangerous
rescue StandardError => e
puts Backtrace.new(e)
end
[すべてのスレッドバックトレースを調べて原因を見つける]
複数のスレッドを使用する場合、完全に拡張されたコールスタックでさえ、実際の問題のコード行を隠すことができます!
例:1つのスレッドがRubyハッシュを繰り返しており、他のスレッドがそれを変更しようとしています。ブーム!例外!そして、「ビジー」ハッシュを変更しようとするときに取得するスタックトレースの問題は、ハッシュを変更しようとしている場所まで関数のチェーンを表示することですが、現在誰がそれを並列に反復しているかを表示しません(所有者)現在実行中のすべてのスレッドのスタックトレースを出力することで、これを把握する方法を次に示します。これを行う方法は次のとおりです。
# 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つは異なる数です)。