Làm thế nào để tôi có được ruby ​​để in một backtrace đầy đủ thay vì cắt ngắn?


170

Khi tôi nhận được ngoại lệ, nó thường từ sâu trong ngăn xếp cuộc gọi. Khi điều này xảy ra, thường xuyên hơn không, dòng mã vi phạm thực tế bị ẩn khỏi tôi:

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

Việc cắt ngắn "... 8 cấp ..." đang gây cho tôi rất nhiều rắc rối. Tôi không có nhiều thành công trong việc này: Làm thế nào để tôi nói với ruby ​​rằng tôi muốn các bãi chứa bao gồm toàn bộ?


2
Có cách nào để làm điều này từ dòng lệnh thay thế?
Andrew Grimm

Câu trả lời:


241

Ngoại lệ # backtrace có toàn bộ ngăn xếp trong đó:

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

(Lấy cảm hứng từ blog Ruby Inside của Peter Cooper )


15
Tôi sẽ lấy lại ngoại lệ, ít nhất là vì sự hoàn thiện của các ví dụ.
reto

13
Để reraise bạn chỉ cần nói raise. Không cần phải xác định rõ ràng việc thực thi mà bạn muốn nâng lên.
Timo

Thật tuyệt, tôi luôn nghĩ bạn phải vượt qua ngoại lệ trước đó để nâng cao. Tôi đã không nhận ra rằng nó mặc định được giải cứu ngoại lệ cuối cùng.
mở cửa

170

Bạn cũng có thể làm điều này nếu bạn muốn một lớp lót đơn giản:

puts caller

2
Thủ thuật tuyệt vời. Cảm ơn rất nhiều. Tôi không biết rằng raisecó thể được sử dụng mà không có đối số. Tôi cũng không biết rằng rescuesẽ được đối xử chính xác như một lớp lót. Tôi cũng hoàn toàn bỏ qua những vars toàn cầu như thế $!.
Dmytrii Nagirniak

11
không cần phải nâng / cứu, bạn chỉ có thể sử dụng trình gọi Kernel #, như vậy:puts "this line was reached by #{caller.join("\n")}"
Stephen C

Ah, tôi đã phát hiện ra điều đó ngay sau khi đăng câu trả lời này và quên cập nhật nó. Cảm ơn
kẻ hèn nhát vô danh

Tôi sử dụng y callerđể in đầu ra như dấu vết ngăn xếp Java.
so_mv

caller(0,2)sẽ trả về hai mục mới nhất trong stacktrace. Đẹp để xuất ra stacktraces viết tắt.
Magne

100

Điều này tạo ra mô tả lỗi và stacktrace sạch đẹp, thụt lề:

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

49

IRB có một cài đặt cho "tính năng" khủng khiếp này, mà bạn có thể tùy chỉnh.

Tạo một tệp được gọi là ~/.irbrcbao gồm dòng sau:

IRB.conf[:BACK_TRACE_LIMIT] = 100

Điều này sẽ cho phép bạn nhìn thấy irbít nhất 100 khung stack . Tôi chưa thể tìm thấy một cài đặt tương đương cho thời gian chạy không tương tác.

Thông tin chi tiết về tùy chỉnh IRB có thể được tìm thấy trong cuốn sách Pickaxe .


3
Đây phải là câu trả lời được chấp nhận, bởi vì nó giải quyết câu hỏi làm thế nào để hiển thị nhiều backtrace thay vì "... cấp X ...".
nickh

13

Một lớp lót cho Callstack:

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

Một lớp lót cho callstack mà không cần tất cả các loại đá quý:

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

Một lớp lót cho callstack không có tất cả các gem và liên quan đến thư mục hiện tại

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

2
one-liner thực sự là một điều tồi tệ khi bạn có nhiều câu lệnh.
Nurettin

3
@nurettin đây là mục đích gỡ lỗi nhanh, vì vậy làm cho nó thành một dòng giúp dễ dàng sao chép dán nó, chủ yếu là trong các vỏ tương tác
Dorian

@Dorian Bạn nhắc tôi về một câu hỏi mà tôi đã có: "Tại sao shell tương tác lại hữu ích? (Không bao gồm Shell-script)".
Sapphire_Brick

9

Điều này bắt chước dấu vết Ruby chính thức, nếu điều đó quan trọng với bạn.

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

Thật thú vị, nó không xử lý 'ngoại lệ chưa được xử lý' một cách chính xác, báo cáo nó là 'RuntimeError', nhưng vị trí là chính xác.


Tôi rất tiếc rằng tôi có nhưng một upvote để đưa ra câu trả lời của bạn. Tôi thêm cái này ở mọi nơi
Dbz

4

Tôi đã gặp những lỗi này khi thử tải môi trường thử nghiệm của mình (thông qua kiểm tra cào hoặc tự động kiểm tra) và các đề xuất của IRB không giúp được gì. Tôi đã kết thúc việc gói toàn bộ test / test_helper.rb của mình trong một khối bắt đầu / giải cứu và điều đó đã được sửa.

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

0

[kiểm tra tất cả các backtraces của chủ đề để tìm ra thủ phạm]
Ngay cả ngăn xếp cuộc gọi được mở rộng hoàn toàn vẫn có thể ẩn dòng mã vi phạm thực tế khỏi bạn khi bạn sử dụng nhiều hơn một luồng!

Ví dụ: Một luồng đang lặp lại ruby ​​Hash, luồng khác đang cố gắng sửa đổi nó. BÙM! Ngoại lệ! Và vấn đề với dấu vết ngăn xếp mà bạn gặp phải khi cố gắng sửa đổi hàm băm 'bận rộn' là nó hiển thị cho bạn chuỗi chức năng đến nơi bạn đang cố gắng sửa đổi hàm băm, nhưng nó không hiển thị ai đang lặp lại song song ( ai sở hữu nó)! Đây là cách để tìm ra điều đó bằng cách in theo dõi ngăn xếp cho TẤT CẢ các luồng hiện đang chạy. Đây là cách bạn làm điều này:

# 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

Đoạn mã trên rất hữu ích ngay cả chỉ cho mục đích giáo dục vì nó có thể cho bạn thấy (như x-quang) có bao nhiêu chủ đề bạn thực sự có (so với số lượng bạn nghĩ bạn có - thường là hai số đó là các số khác nhau;)


0

Bạn cũng có thể sử dụng đá quý Ruby backtrace (Tôi là tác giả):

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

4
Ít nhất bạn có thể giải thích lý do tại sao chúng tôi muốn sử dụng đá quý của bạn? Bạn có thể hiển thị một số đầu ra mẫu?
ioquatix
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.