Comment enregistrer mes erreurs Python?
try:
do_something()
except:
# How can I log my exception here, complete with its traceback?
Utilisez logging.exception
from avec un gestionnaire except:
pour consigner l'exception en cours, précédée d'un message.
import logging
LOG_FILENAME = '/tmp/logging_example.out'
logging.basicConfig(filename=LOG_FILENAME, level=logging.DEBUG)
logging.debug('This message should go to the log file')
try:
run_my_stuff()
except:
logging.exception('Got exception on main handler')
raise
En regardant maintenant le fichier journal, /tmp/logging_example.out
:
DEBUG:root:This message should go to the log file
ERROR:root:Got exception on main handler
Traceback (most recent call last):
File "/tmp/teste.py", line 9, in <module>
run_my_stuff()
NameError: name 'run_my_stuff' is not defined
Utiliser les options exc_info peut être mieux, reste l'avertissement ou le titre de l'erreur:
try:
# coode in here
except Exception as e:
logging.error(e, exc_info=True)
Mon travail m'a récemment chargé de consigner toutes les traces/exceptions de notre application. J'ai essayé de nombreuses techniques que d'autres avaient mises en ligne, comme celle ci-dessus, mais j'ai opté pour une approche différente. Remplacer traceback.print_exception
.
J'ai un article sur http://www.bbarrows.com/ Ce serait beaucoup plus facile à lire, mais je vais le coller ici aussi.
Lorsque j'ai été chargé de consigner toutes les exceptions que notre logiciel pourrait rencontrer dans la nature, j'ai essayé différentes techniques pour consigner nos traces python d'exceptions. Au début, je pensais que le hook d’exception du système python, sys.excepthook, serait l’endroit idéal pour insérer le code de journalisation. J'essayais quelque chose de similaire à:
import traceback
import StringIO
import logging
import os, sys
def my_excepthook(excType, excValue, traceback, logger=logger):
logger.error("Logging an uncaught exception",
exc_info=(excType, excValue, traceback))
sys.excepthook = my_excepthook
Cela fonctionnait pour le thread principal, mais j'ai vite constaté que mon sys.excepthook n'existerait pas dans les nouveaux threads démarrés par mon processus. C'est un gros problème car presque tout se passe dans les threads de ce projet.
Après avoir consulté Google et lu beaucoup de documentation, les informations les plus utiles que j'ai trouvées étaient issues du suivi des problèmes de Python.
Le premier article du fil de discussion montre un exemple de travail du sys.excepthook
NON persistant entre les fils de discussion (comme indiqué ci-dessous). Apparemment, c'est le comportement attendu.
import sys, threading
def log_exception(*args):
print 'got exception %s' % (args,)
sys.excepthook = log_exception
def foo():
a = 1 / 0
threading.Thread(target=foo).start()
Les messages sur ce fil de discussion Python Issue donnent vraiment 2 suggestions de piratage. Sous-classe Thread
et encapsulez la méthode d’exécution dans notre propre essai sauf bloc afin de capturer et de consigner les exceptions ou monkey patch threading.Thread.run
pour l’exécuter dans votre propre essai, sauf bloquer et enregistrer les exceptions.
La première méthode de sous-classement Thread
me semble moins élégante dans votre code que vous auriez à importer et à utiliser votre classe Thread
personnalisée PARTOUT où vous souhaitiez avoir un thread de journalisation. Cela a fini par être un problème parce que je devais parcourir toute notre base de code et remplacer tous les Threads
normaux par ce Thread
personnalisé. Cependant, il était clair que cette Thread
était en train de faire et qu'il serait plus facile pour quelqu'un de diagnostiquer et de déboguer si quelque chose n'allait pas avec le code de journalisation personnalisé. Un thread de journalisation custome pourrait ressembler à ceci:
class TracebackLoggingThread(threading.Thread):
def run(self):
try:
super(TracebackLoggingThread, self).run()
except (KeyboardInterrupt, SystemExit):
raise
except Exception, e:
logger = logging.getLogger('')
logger.exception("Logging an uncaught exception")
La deuxième méthode de correction de singe threading.Thread.run
est agréable car je pouvais juste l'exécuter une fois juste après __main__
et instrumenter mon code de journalisation dans toutes les exceptions. La correction de singe peut être gênante pour le débogage car elle modifie la fonctionnalité attendue de quelque chose. Le correctif suggéré à partir du suivi des problèmes Python était le suivant:
def installThreadExcepthook():
"""
Workaround for sys.excepthook thread bug
From
http://spyced.blogspot.com/2007/06/workaround-for-sysexcepthook-bug.html
(https://sourceforge.net/tracker/?func=detail&atid=105470&aid=1230540&group_id=5470).
Call once from __main__ before creating any threads.
If using psyco, call psyco.cannotcompile(threading.Thread.run)
since this replaces a new-style class method.
"""
init_old = threading.Thread.__init__
def init(self, *args, **kwargs):
init_old(self, *args, **kwargs)
run_old = self.run
def run_with_except_hook(*args, **kw):
try:
run_old(*args, **kw)
except (KeyboardInterrupt, SystemExit):
raise
except:
sys.excepthook(*sys.exc_info())
self.run = run_with_except_hook
threading.Thread.__init__ = init
Ce n'est que lorsque j'ai commencé à tester la journalisation des exceptions que j'ai réalisé que je m'y prenais mal.
Pour tester j'avais placé un
raise Exception("Test")
quelque part dans mon code. Cependant, encapsuler une méthode qui appelait cette méthode était un bloc try sauf qui imprimait la trace et avalait l'exception. C’était très frustrant car j’ai vu le traçage apporter imprimé à STDOUT mais ne pas être enregistré. C'est alors que j'ai décidé qu'une méthode beaucoup plus facile de journalisation des traces était simplement de corriger la méthode utilisée par tout le code python pour imprimer les traces elles-mêmes, traceback.print_exception . J'ai obtenu quelque chose de ce qui suit:
def add_custom_print_exception():
old_print_exception = traceback.print_exception
def custom_print_exception(etype, value, tb, limit=None, file=None):
tb_output = StringIO.StringIO()
traceback.print_tb(tb, limit, tb_output)
logger = logging.getLogger('customLogger')
logger.error(tb_output.getvalue())
tb_output.close()
old_print_exception(etype, value, tb, limit=None, file=None)
traceback.print_exception = custom_print_exception
Ce code écrit le suivi dans un tampon de chaîne et le consigne dans le journal ERROR. J'ai un gestionnaire de journalisation personnalisé qui configure le consignateur 'customLogger' qui prend les journaux de niveau ERROR et les envoie à leur domicile pour analyse.
Vous pouvez enregistrer toutes les exceptions non interceptées sur le thread principal en affectant un gestionnaire à sys.excepthook
, éventuellement en utilisant le paramètre exc_info
des fonctions de journalisation de Python :
import sys
import logging
logging.basicConfig(filename='/tmp/foobar.log')
def exception_hook(exc_type, exc_value, exc_traceback):
logging.error(
"Uncaught exception",
exc_info=(exc_type, exc_value, exc_traceback)
)
sys.excepthook = exception_hook
raise Exception('Boom')
Cependant, si votre programme utilise des threads, notez que les threads créés avec threading.Thread
vont pas déclenchent sys.excepthook
lorsqu'une exception non interceptée se produit à l'intérieur, comme indiqué dans Issue 1230540 sur le suivi des problèmes de Python. Certains hacks ont été suggérés pour contourner cette limitation, comme monkey-patcher Thread.__init__
pour écraser self.run
avec une autre méthode run
qui encapsule l'original dans un bloc try
et appelle sys.excepthook
depuis l'intérieur du bloc except
. Sinon, vous pouvez simplement encapsuler vous-même manuellement le point d'entrée de chacun de vos threads dans la variable try
/except
.
Les messages d'exception non capturés sont envoyés à STDERR. Ainsi, au lieu d'implémenter votre journalisation dans Python, vous pouvez envoyer STDERR dans un fichier en utilisant le shell que vous utilisez pour exécuter votre script Python. Dans un script Bash, vous pouvez le faire avec la redirection de sortie, sous la forme décrit dans le guide BASH .
Ajouter les erreurs au fichier, les autres sorties au terminal:
./test.py 2>> mylog.log
Écraser le fichier avec les sorties STDOUT et STDERR entrelacées:
./test.py &> mylog.log
peut-être pas aussi élégant, mais plus facile:
#!/bin/bash
log="/var/log/yourlog"
/path/to/your/script.py 2>&1 | (while read; do echo "$REPLY" >> $log; done)
Ce que je cherchais:
import sys
import traceback
exc_type, exc_value, exc_traceback = sys.exc_info()
traceback_in_var = traceback.format_tb(exc_traceback)
Voir: