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?
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)
Vous pouvez également le faire si vous souhaitez une ligne simple:
puts caller
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
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 .
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
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.
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
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
[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;)