如何让 ruby​​ 打印完整的回溯而不是截断的回溯?

Posted

技术标签:

【中文标题】如何让 ruby​​ 打印完整的回溯而不是截断的回溯?【英文标题】:How do I get ruby to print a full backtrace instead of a truncated one? 【发布时间】:2010-09-27 10:50:24 【问题描述】:

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

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

【问题讨论】:

有没有办法从命令行执行此操作? 【参考方案1】:

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

【讨论】:

我会重新提出异常,至少为了示例的完整性。 要加注你只需要说raise。无需明确指定要引发的执行。 很好,我一直认为你必须通过前面的异常才能引发。我没有意识到它默认为最后一个获救的异常。 如果你的代码没有抛出异常,你只是想看看它去哪里的堆栈跟踪? 这个和其他解决方案的问题是我们不知道哪个文件首先引发了异常。这是第 22 条规定的情况。为了获得完整的 st,我们需要添加此代码。但是为了添加这个代码,我们必须知道错误来自哪里,我们只能知道我们是否获得了完整的 st。【参考方案2】:

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

puts caller

【讨论】:

很棒的把戏。非常感谢。我不知道 raise 可以不带参数使用。我都不知道rescue 将被正确地视为单行。我也完全忽略了像 $! 这样的全局变量。 不需要raise/rescue,你可以直接使用Kernel#caller,像这样:puts "this line was reached by #caller.join("\n")" 啊,我在发布此答案后不久就发现了这一点,但忘了更新它。谢谢 但是,在 rake 任务的错误处理步骤(rescue 块)中使用 caller 而不是 e.backtrace 不会给你被调用模型的堆栈跟踪,只是堆栈跟踪rake 任务,所以这没什么用.. 此答案不适用于 OP 声明的用例,因为它显示了代码中的当前位置,而不是发生异常的位置。【参考方案3】:

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

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

【讨论】:

【参考方案4】:

IRB 为这个可怕的“功能”提供了一个设置,您可以自定义它。

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

IRB.conf[:BACK_TRACE_LIMIT] = 100

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

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

【讨论】:

这应该是公认的答案,因为它解决了如何显示更多回溯而不是“...X级别...”的问题。【参考方案5】:

调用堆栈的一个衬里:

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

【讨论】:

当你有多个语句时,单行实际上是一件坏事。 @nurettin 这是为了快速调试目的,因此将其设为一行可以很容易地复制粘贴它,主要是在交互式 shell 中 @Dorian 你让我想起了一个问题:“为什么交互式 shell 有用?(不包括 shell 脚本)”。【参考方案6】:

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

【讨论】:

很遗憾,我只能为您的回答投上一票。我到处添加这个【参考方案7】:

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

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

【讨论】:

【参考方案8】:

几乎每个人都回答了这个问题。我将任何 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

【讨论】:

这是一个简洁明了的好模板,谢谢!【参考方案9】:

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

示例:一个线程正在迭代 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

上面的代码 sn-p 即使仅用于教育目的也很有用,因为它可以向您显示(如 X 射线)您实际拥有的线程数(相对于您认为自己拥有的线程数 - 通常这两个是不同的数字; )

【讨论】:

【参考方案10】:

也可以使用backtraceRuby gem(我是作者):

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

【讨论】:

您能至少解释一下我们为什么要使用您的 gem 吗?你能展示一些示例输出吗?

以上是关于如何让 ruby​​ 打印完整的回溯而不是截断的回溯?的主要内容,如果未能解决你的问题,请参考以下文章

Ruby如何打印/放置读取的文件内容而不是一堆数字/符号

如何打印选定的div而不是完整页面

Android - 打印完整的异常回溯到日志

当我告诉它时,如何使用 Django 的记录器来记录回溯?

打印工作,但使用回溯解决数独时返回值为 None

导航栏中的自定义字体截断使用分号而不是省略号