web-dev-qa-db-fra.com

Comment faire en sorte que Ruby imprime une trace complète au lieu d'une trace tronquée?

Lorsque j'obtiens des exceptions, cela vient souvent du plus profond de la pile d'appels. Lorsque cela se produit, le plus souvent, la ligne de code incriminée m'est cachée:

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

Cette troncature "... 8 niveaux ..." me cause beaucoup de problèmes. Je n'ai pas beaucoup de succès dans la recherche de celui-ci sur Google: comment dire à Ruby que je veux que les dumps incluent la pile complète?

138
Sniggerfardimungus

Exception # backtrace contient toute la pile:

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

(Inspiré par Peter Cooper/ Ruby Inside blog)

201
Gareth

Vous pouvez également le faire si vous souhaitez une ligne simple:

puts caller
153
anonymous coward

Ceci produit la description de l’erreur et Nice propre, stacktrace indent:

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

L'IRB dispose d'un paramètre pour cette "fonctionnalité" affreuse, que vous pouvez personnaliser.

Créez un fichier appelé ~/.irbrc comprenant la ligne suivante:

IRB.conf[:BACK_TRACE_LIMIT] = 100

Cela vous permettra de voir au moins 100 cadres de pile dans irb. Je n'ai pas été en mesure de trouver un paramètre équivalent pour le runtime non interactif.

Des informations détaillées sur la personnalisation de la CISR sont disponibles dans le livre Pickaxe .

43
robinluckey

Une doublure pour callstack:

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

Une doublure pour callstack sans toutes les gemmes:

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

Une doublure pour callstack sans toutes les gemmes et relative au répertoire courant

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

Cela imite la trace officielle de Ruby, si c'est important pour vous.

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

De manière amusante, il ne gère pas correctement «l'exception non gérée», il la signale comme «RuntimeError», mais l'emplacement est correct.

7
android.weasel

J'avais ces erreurs en essayant de charger mon environnement de test (via un test de rake ou un autotest) et les suggestions de l'IRB ne m'ont pas aidé. J'ai fini par envelopper tout mon test/test_helper.rb dans un bloc begin/rescue et cela a résolu le problème.

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

Vous pouvez également utiliser backtrace Ruby gem (je suis l'auteur):

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

[examine toutes les traces de fils pour trouver le coupable]
Même une pile d’appels complètement développée peut toujours vous cacher la ligne de code incriminée lorsque vous utilisez plusieurs threads!

Exemple: un thread itère Ruby Hash, un autre thread tente de le modifier. BOOM! Exception! Et le problème avec la trace de pile que vous obtenez en essayant de modifier le hachage "occupé", c’est qu’elle vous montre la chaîne de fonctions jusqu’à l’endroit où vous essayez de modifier le hachage, mais ne montre PAS qui le répète actuellement en parallèle ( à qui appartient-il)! Voici comment résoudre ce problème en imprimant une trace de pile pour TOUS les threads en cours d'exécution. Voici comment vous faites cela:

# 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

L'extrait de code ci-dessus est utile même à des fins éducatives car il peut vous montrer (comme une radiographie) le nombre de fils que vous avez réellement (par rapport au nombre que vous pensiez avoir - bien souvent, ces deux nombres sont différents;)

0
Dmitry Shevkoplyas