196

当我得到异常时,它通常来自调用堆栈的深处。发生这种情况时,通常情况下,实际的违规代码行对我来说是隐藏的:

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​​ 我希望转储包含完整堆栈?

4

10 回答 10

270

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博客)

于 2008-12-18T00:17:38.887 回答
195

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

puts caller
于 2011-10-11T09:52:03.067 回答
106

这会产生错误描述和干净整洁的缩进堆栈跟踪:

begin               
 # Some exception throwing code
rescue => e
  puts "Error during processing: #{$!}"
  puts "Backtrace:\n\t#{e.backtrace.join("\n\t")}"
end
于 2011-02-16T14:41:48.503 回答
49

IRB 对这个可怕的“功能”有一个设置,您可以自定义它。

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

IRB.conf[:BACK_TRACE_LIMIT] = 100

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

有关 IRB 自定义的详细信息可以在Pickaxe 书中找到。

于 2009-06-26T21:48:55.473 回答
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

一个调用堆栈的衬垫,没有所有的宝石,并且相对于当前目录

begin; Whatever.you.want; rescue => e; puts e.message; puts; puts e.backtrace.grep_v(/\/gems\//).map { |l| l.gsub(`pwd`.strip + '/', '') }; end
于 2017-02-07T19:22:33.547 回答
11

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

于 2016-02-24T11:48:43.187 回答
4

我在尝试加载我的测试环境(通过 rake 测试或自动测试)时遇到了这些错误,而 IRB 的建议没有帮助。我最终将我的整个 test/test_helper.rb 包装在一个 begin/rescue 块中,然后解决了问题。

begin
  class ActiveSupport::TestCase
    #awesome stuff
  end
rescue => e
  puts e.backtrace
end
于 2010-03-31T20:24:44.633 回答
4

几乎每个人都回答了这个问题。我将任何 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
于 2020-08-10T05:15:52.490 回答
1

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

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

于 2017-01-23T19:00:52.513 回答
-1

你也可以使用backtrace Ruby gem(我是作者):

require 'backtrace'
begin
  # do something dangerous
rescue StandardError => e
  puts Backtrace.new(e)
end
于 2018-10-12T12:11:51.043 回答