Kesik yerine tam geriye dönük iz yazdırmak için nasıl yakut elde ederim?


170

İstisnalar aldığımda, bu genellikle çağrı yığını içinde derinden olur. Bu olduğunda, çoğu zaman olmamak üzere, asıl rahatsız edici kod satırı benden gizlenir:

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

Bu "... 8 seviye ..." kısaltması bana büyük sıkıntılar çıkarıyor. Bunun için pek başarılı olamıyorum: Ruby'e, çöplüklerin tam yığını içermesini istediğimi nasıl söyleyebilirim?


2
Bunun yerine komut satırından bunu yapmanın bir yolu var mı?
Andrew Grimm

Yanıtlar:


241

İstisna # backtrace içinde tüm yığın bulunur:

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

(Peter Cooper'ın Ruby Inside blogundan esinlenilmiştir )


15
En azından örneklerin tamamlanması uğruna istisnayı yeniden değerlendirirdim.
reto

13
Yeniden canlandırmak için söylemelisin raise. Yükseltmek istediğiniz yürütmeyi açıkça belirtmenize gerek yoktur.
Timo

Güzel, hep yükseltmek için bir önceki istisnayı geçmek zorunda olduğunu düşündüm. Ben kurtarıldı son istisna varsayılan olduğunu fark etmedi.
9'da çiçeklenmemiş

Kodunuz bir istisna atmazsa, sadece nereye gittiğinin yığın izini görmek istersiniz?
Alex Levine

170

Basit bir tek astar istiyorsanız bunu da yapabilirsiniz:

puts caller

2
Müthiş numara. Çok teşekkürler. Bunun raiseargüman olmadan kullanılabileceğini bilmiyordum . Ben rescuede bunun tek astar olarak doğru muamele göreceğini bilmiyordum . Ayrıca küresel değişkenleri de tamamen görmezden geliyorum $!.
Dmytrii Nagirniak

11
yükseltmeye / kurtarmaya gerek yok, sadece Çekirdek # arayanını kullanabilirsiniz, şöyle:puts "this line was reached by #{caller.join("\n")}"
Stephen C

Ah, bu cevabı gönderdikten kısa bir süre sonra öğrendim ve güncellemeyi unuttum. Teşekkürler
anonim korkak

y callerÇıktıyı Java yığın izlemesi gibi yazdırmak için kullanıyorum .
so_mv

caller(0,2)yığın izlemesindeki en son iki girdiyi döndürür. Kısaltılmış yığın izlerini çıktılamak için güzel.
Magne

100

Bu, hata açıklaması ve güzel, temiz, girintili yığın izlemesi üretir:

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

49

IRB, özelleştirebileceğiniz bu korkunç "özellik" için bir ayara sahiptir.

~/.irbrcAşağıdaki satırı içeren bir dosya oluşturun :

IRB.conf[:BACK_TRACE_LIMIT] = 100

Bu irb, en azından 100 yığın kareyi görmenizi sağlar . Etkileşimli olmayan çalışma zamanı için eşdeğer bir ayar bulamadım.

IRB özelleştirmesi hakkında ayrıntılı bilgi Pickaxe kitabında bulunabilir .


3
Bu kabul edilen cevap olmalı, çünkü "... X seviyeleri ..." yerine geri izlemenin nasıl daha fazla gösterileceği sorusunu ele alıyor.
nickh

13

Calltack için bir astar:

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

Tüm taşlar olmadan çağrı için bir astar:

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

Tüm taşlar olmadan ve geçerli dizine göre çağrı sabitleme için bir astar

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

2
birden fazla ifadeniz olduğunda tek satırlık aslında kötü bir şeydir.
nurettin

3
@nurettin hızlı hata ayıklama amaçlıdır, bu yüzden bir satır yapmak çoğunlukla interaktif kabuklarda kopyalamayı yapıştırmayı kolaylaştırır
Dorian

@Dorian Bana sahip olduğum bir soruyu hatırlatıyorsunuz: "Etkileşimli kabuklar neden yararlıdır? (Shell-script hariç)".
Sapphire_Brick

9

Bu sizin için önemliyse, resmi Ruby izini taklit eder.

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

Eğlenceli bir şekilde, 'RuntimeError' olarak bildirerek 'işlenmeyen istisna' düzgün işlemez, ancak konum doğrudur.


Cevabınız için vereceğim tek bir oylama olduğundan pişmanım. Bunu her yere
ekliyorum

4

Test ortamımı (komisyon testi veya otomatik test yoluyla) yüklemeye çalışırken bu hataları alıyordum ve IRB önerileri yardımcı olmadı. Tüm test / test_helper.rb'yi bir başlangıç ​​/ kurtarma bloğuna tamamladım ve işleri düzelttim.

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

0

[suçlu bulmak için tüm iş parçacığı geriye dönük incelemek]
Tamamen genişletilmiş çağrı yığını bile birden fazla iş parçacığı kullandığınızda gerçek rahatsız edici kod satırı sizden gizleyebilirsiniz!

Örnek: Bir iş parçacığı ruby ​​Hash'i yineliyor, diğer iş parçacığı onu değiştirmeye çalışıyor. BOOM! İstisna! 'Meşgul' karmasını değiştirmeye çalışırken aldığınız yığın izlemesindeki sorun, karmayı değiştirmeye çalıştığınız yere işlev zincirini göstermesidir, ancak şu anda kimin paralel olarak yinelediğini göstermez ( kimin sahibi)! İşte o anda çalışan TÜM iş parçacıkları için yığın iz yazdırarak anlamanın yolu. Bunu şu şekilde yapabilirsiniz:

# 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

Yukarıdaki kod snippet'i, sadece x-ışını gibi gerçekte kaç tane iş parçacığına sahip olduğunuzu gösterebildiğinden (eğitimde bile) yararlıdır (kaç tane düşündüğünüze karşı - genellikle bu ikisi farklı sayılardır;)


0

Ayrıca backtrace Ruby gem kullanabilirsiniz (ben yazarım):

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

4
En azından gemini neden kullanmak istediğimizi açıklayabilir misin? Bazı örnek çıktı gösterebilir misiniz?
ioquatix
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.