如何获得红宝石来打印完整的回溯记录而不是被截断的回溯记录?


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个级别...”的截断给我带来了很多麻烦。我对此没有太大的成功:我如何告诉ruby我希望转储包括整个堆栈?


2
有没有办法从命令行执行此操作?
安德鲁·格林

Answers:


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

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


15
我会提出例外,至少是为了使示例更完整。
雷托

13
要加注,您只需要说raise。无需显式指定要引发的执行。
Timo 2014年

很好,我一直认为您必须通过先前的例外才能提出。我没有意识到它默认为所挽救的最后一个异常。
弗洛雷斯

如果您的代码没有引发异常怎么办,您只想查看它去向的堆栈跟踪信息,该怎么办?
亚历克斯·莱文

170

如果您想要一个简单的单线服务,也可以这样做:

puts caller

2
很棒的把戏。非常感谢。我不知道raise没有参数就可以使用它。我都不知道那rescue将被正确地视为一线。我也完全忽略了那些全局变量$!
2011年

11
无需提升/救援,您可以使用Kernel#caller,如下所示:puts "this line was reached by #{caller.join("\n")}"
Stephen C

嗯,发布此答案后不久我就发现了这一点,却忘记了对其进行更新。谢谢
匿名co

y caller用来打印输出,如Java堆栈跟踪。
so_mv 2012年

caller(0,2)将返回stacktrace中的两个最新条目。很适合输出缩写的堆栈跟踪。
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

这样irb至少可以看到100个堆栈帧。我没有找到非交互式运行时的等效设置。

有关IRB定制的详细信息,请参见“ 镐”一书


3
这应该是公认的答案,因为它解决了如何显示更多回溯而不是“ ... X level ...”的问题。
尼克,

13

一种用于呼叫堆栈的衬垫:

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

一个呼叫堆栈的衬里,没有所有的gem和相对于当前目录

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

3
@nurettin这是用于快速调试的目的,因此将其设置为一行可以很容易地将其粘贴复制,主要是在交互式外壳中进行
Dorian

@Dorian您使我想起了一个问题:“为什么交互式shell有用?(不包括Shell脚本)”。
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”,但位置正确。


很遗憾,我只能提出一个答复。我到处都添加了它
-Dbz

4

尝试加载测试环境时(通过rake测试或自动测试)时遇到了这些错误,IRB建议无济于事。我最终将我的整个test / test_helper.rb包裹在一个begin / rescue块中,然后进行了修复。

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

0

[检查所有线程回溯以找出问题的根源]
当您使用多个线程时,即使完全扩展的调用堆栈仍可以向您隐藏实际的有害代码行!

示例:一个线程正在迭代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射线)实际有多少个线程(相对于您以为有多少个线程-通常这两个是不同的数字;)


0

您还可以使用backtrace Ruby gem(我是作者):

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

4
您至少可以解释一下为什么我们要使用您的宝石吗?您可以显示一些示例输出吗?
ioquatix
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.