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


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.