Comment est-ce que Ruby peut imprimer une trace complète au lieu d’une trace tronquée?

Lorsque j’obtiens des exceptions, cela provient souvent de la stack d’appels. Lorsque cela se produit, le plus souvent, la ligne de code incriminée est masqué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 

Que “… 8 niveaux …” la troncature me cause beaucoup de problèmes. Je n’ai pas beaucoup de succès sur Google: Comment puis-je dire à ruby ​​que je veux que les dumps incluent la stack complète?

Exception # backtrace contient l’intégralité de la stack:

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

(Inspiré du blog Ruby Inside de Peter Cooper)

Vous pouvez aussi le faire si vous souhaitez un simple liner:

 puts caller 

Cela produit la description de l’erreur et une belle stack de caractères en retrait:

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

IRB a un paramètre pour cette horrible “fonctionnalité”, 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 100 frameworks de stack au moins dans irb . Je n’ai pas été en mesure de trouver un paramètre équivalent pour le runtime non interactif.

Vous trouverez des informations détaillées sur la personnalisation IRB dans le livre Pickaxe .

Un paquebot pour callstack:

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

Un paquebot pour callstack sans tous les joyaux:

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

Un paquebot pour callstack sans toutes les gemmes et relatif 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`.ssortingp + '/', '') }; end 

Cela imite la trace Ruby officielle, si cela 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 les «exceptions non gérées», en les signalant comme «RuntimeError», mais l’emplacement est correct.

J’avais ces erreurs lorsque j’essayais de charger mon environnement de test (via un test de rake ou un autotest) et les suggestions IRB ne m’avaient pas aidé. J’ai fini par emballer 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 

[examine tous les fils de trajets pour trouver le coupable]
Même une stack d’appels entièrement développée peut toujours masquer la ligne de code incriminée lorsque vous utilisez plusieurs threads!

Exemple: un thread est en train d’itérer un hash ruby, un autre thread tente de le modifier. BOOM! Exception! Et le problème avec la trace de stack que vous obtenez en essayant de modifier le hachage «occupé» est qu’il affiche la chaîne de fonctions jusqu’à l’endroit où vous essayez de modifier le hachage, mais il ne montre PAS qui le réitère en parallèle ( qui le possède)! Voici comment procéder en imprimant une trace de stack pour TOUS les threads en cours d’exécution. Voici comment procéder:

 # 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 les rayons X) combien de threads vous avez réellement (par rapport à combien vous pensiez avoir – très souvent, ces deux nombres sont différents);