我如何让红宝石打印一个完整的回溯而不是截断一个?

当我获得异常时,它通常来自调用堆栈的深处。当这种情况发生时,通常情况下,真正令人讨厌的代码行对我来说是隐藏的:

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我想要转储包含完整的堆栈?

175477 次浏览

Exception#backtrace包含了整个堆栈:

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

(灵感来自Peter Cooper的Ruby内部博客)

IRB对这个糟糕的“特性”有一个设置,你可以自定义。

创建一个名为~/.irbrc的文件,包含以下行:

IRB.conf[:BACK_TRACE_LIMIT] = 100

这将允许你在irb中至少看到100个堆栈帧。我还没有找到非交互式运行时的等效设置。

关于IRB自定义的详细信息可以在鹤嘴锄的书中找到。

当我试图加载我的测试环境(通过rake test或autotest)时,我得到了这些错误,IRB建议没有帮助。我最终包装了整个test/test_helper。Rb在开始/救援块中,这解决了问题。

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

这将产生错误描述和漂亮的、缩进的stacktrace:

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

你也可以这样做,如果你想要一个简单的一行:

puts caller

这模拟了官方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”,但位置是正确的。

< p > [检查所有线程回溯以找到罪魁祸首] < br > 当您使用多个线程时,即使完全展开的调用堆栈仍然可以隐藏实际的违规代码行!< / p >

示例:一个线程正在迭代ruby Hash,另一个线程正在尝试修改它。繁荣!例外!当你试图修改“忙”哈希时,你得到的堆栈跟踪的问题是,它显示了你的函数链,直到你试图修改哈希的地方,但它并没有显示谁正在并行迭代它(谁拥有它)!下面是通过打印当前运行的所有线程的堆栈跟踪来计算的方法。你可以这样做:

# 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光一样)你实际有多少线程(与你认为你有多少线程相比——通常这两个数字是不同的;)

callstack的一行代码:

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

没有所有宝石的callstack的一行代码:

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

一个没有所有宝石和相对于当前目录的调用堆栈的一行代码

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

你也可以使用回溯 Ruby宝石(我是作者):

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

几乎每个人都回答这个问题。我的版本打印任何rails异常到日志将是:

begin
some_statement
rescue => e
puts "Exception Occurred #{e}. Message: #{e.message}. Backtrace:  \n #{e.backtrace.join("\n")}"
Rails.logger.error "Exception Occurred #{e}. Message: #{e.message}. Backtrace:  \n #{e.backtrace.join("\n")}"
end