Як мені отримати рубін, щоб надрукувати повний зворотний бік замість усіченого?


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 рівнів ..." завдає мені великих клопотів. Я не маю великого успіху в гуглені для цього: Як я можу сказати рубіну, що я хочу, щоб звалища включали повний стек?


2
Чи є спосіб зробити це замість командного рядка?
Ендрю Грімм

Відповіді:


241

Виняток # backtrace містить у собі весь стек:

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

(Натхненний блогом Пітера Купера Ruby Inside )


15
Я б переосмислив виняток, принаймні заради повноти прикладів.
reto

13
Щоб переоцінити, потрібно просто сказати raise. Не потрібно чітко вказувати виконання, яке потрібно підняти.
Тимо

Приємно, я завжди думав, що ти повинен пройти попередній виняток для підвищення. Я не усвідомлював, що це було врятовано за замовчуванням до останнього винятку.
розблоковується

Що робити, якщо ваш код не кидає виняток, ви просто хочете побачити стек стеження того, куди він пішов?
Олексій Левін

170

Ви також можете це зробити, якщо хочете простий однолінійний:

puts caller

2
Дивовижна хитрість. Дуже дякую. Я не знав, що це raiseможе бути використано без аргументів. Ні я не знав, що до rescueцього відносяться правильно як до однолінійного. Я також повністю ігнорую такі глобальні зміни, як $!.
Дмитрій Нагірняк

11
немає необхідності піднімати / рятувати, ви можете просто використовувати Kernel # calller, наприклад так:puts "this line was reached by #{caller.join("\n")}"
Stephen C

Ах, я дізнався про це незабаром після публікації цієї відповіді і забув її оновити. Спасибі
анонімний трус

Я використовую y callerдля друку результатів, як слід стека Java.
so_mv

caller(0,2)поверне два останні записи в стек-трасі. Приємно для виведення скорочених стек.
Магне

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 можна знайти в книзі Pickaxe .


3
Це має бути прийнятою відповіддю, оскільки вона стосується питання про те, як відобразити більшу кількість відсталих замість "... X рівнів ...".
нік

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

2
one-lineer - це насправді погано, коли у вас є кілька заяв.
Нуреттін

3
@nurettin це для швидкої налагодження, завдяки чому в один рядок полегшується копіювання вставлення, здебільшого в інтерактивні оболонки
Доріан,

@Dorian Ви нагадуєте мені про питання, яке у мене виникло: "Чому корисні інтерактивні оболонки? (Крім Shell-скрипту)".
Sapphire_Brick

9

Це імітує офіційний слід Рубі, якщо це важливо для вас.

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", але місце правильне.


Я шкодую, що за вашу відповідь маю лише одне голосування.
Додаю

4

Я отримував ці помилки при спробі завантажити моє тестове середовище (за допомогою тесту граблі або автотесту), і пропозиції IRB не допомогли. Я завершив завершення всього свого тесту / test_helper.rb у блоці початку / порятунку, і це вирішило.

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

0

[вивчіть усі зворотні потоки потоків, щоб знайти винуватця]
Навіть повністю розширений стек виклику все ще може приховати від вас фактичний рядок коду, коли ви використовуєте більше ніж один потік!

Приклад: Один потік - ітераційний рубін Хеш, інший - намагається його змінити. БУМ! Виняток! Проблема зі слідом стека, який ви отримуєте, намагаючись змінити хеш-зайнятість, полягає в тому, що він показує вам ланцюжок функцій до того місця, де ви намагаєтесь змінити хеш, але він НЕ показує, хто в даний час паралельно його повторює ( кому належить)! Ось як це зрозуміти, надрукувавши слід стека для ВСІХ поточних запущених потоків. Ось як це зробити:

# 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

Вищенаведений фрагмент коду корисний навіть для навчальних цілей, оскільки він може показати вам (як рентген), скільки ниток у вас є насправді (порівняно з тим, скільки ви думали, що у вас є - досить часто ці дві різні цифри;)


0

Ви також можете використовувати трасування Рубіновий камінь (я автор):

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

4
Ви можете принаймні пояснити, чому ми хотіли б використовувати ваш дорогоцінний камінь? Чи можете ви показати деякий вибірки?
ioquatix
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.