Comment faire en sorte que les exceptions non capturées soient générées via le module logging
plutôt que vers stderr
?
Je réalise que la meilleure façon de faire serait:
try:
raise Exception, 'Throwing a boring exception'
except Exception, e:
logging.exception(e)
Mais ma situation est telle qu’il serait vraiment gentil si logging.exception(...)
était invoquée automatiquement chaque fois qu’une exception n’est pas interceptée.
Comme Ned l'a souligné, sys.excepthook
est invoqué chaque fois qu'une exception est levée et non capturée. L'implication pratique de ceci est que dans votre code, vous pouvez remplacer le comportement par défaut de sys.excepthook
faire tout ce que vous voulez (y compris en utilisant logging.exception
).
Comme exemple d'homme de paille:
>>> import sys
>>> def foo(exctype, value, tb):
... print 'My Error Information'
... print 'Type:', exctype
... print 'Value:', value
... print 'Traceback:', tb
...
Passer outre sys.excepthook
:
>>> sys.excepthook = foo
Commettez une erreur de syntaxe évidente (laissez les deux-points) et récupérez les informations d'erreur personnalisées:
>>> def bar(a, b)
My Error Information
Type: <type 'exceptions.SyntaxError'>
Value: invalid syntax (<stdin>, line 1)
Traceback: None
Pour plus d'informations sur sys.excepthook
: http://docs.python.org/library/sys.html#sys.excepthook
Voici un petit exemple complet qui inclut également quelques autres astuces:
import sys
import logging
logger = logging.getLogger(__name__)
handler = logging.StreamHandler(stream=sys.stdout)
logger.addHandler(handler)
def handle_exception(exc_type, exc_value, exc_traceback):
if issubclass(exc_type, KeyboardInterrupt):
sys.__excepthook__(exc_type, exc_value, exc_traceback)
return
logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
sys.excepthook = handle_exception
if __== "__main__":
raise RuntimeError("Test unhandled")
Ignore KeyboardInterrupt pour qu'un programme console python puisse se fermer avec Ctrl + C.
Comptez entièrement sur le module de journalisation de python pour formater l'exception.
Utilisez un enregistreur personnalisé avec un exemple de gestionnaire. Celui-ci modifie l'exception non gérée pour aller sur stdout plutôt que sur stderr, mais vous pouvez ajouter toutes sortes de gestionnaires dans ce même style à l'objet enregistreur.
La méthode sys.excepthook
sera invoqué si une exception n'est pas capturée: http://docs.python.org/library/sys.html#sys.excepthook
Lorsqu'une exception est déclenchée et non capturée, l'interprète appelle sys.excepthook avec trois arguments, la classe d'exception, l'instance d'exception et un objet traceback. Dans une session interactive, cela se produit juste avant que le contrôle soit renvoyé à l'invite; Dans un programme Python, cela se produit juste avant la fermeture du programme. Vous pouvez personnaliser le traitement de ces exceptions de niveau supérieur en affectant une autre fonction à trois arguments à sys.excepthook.
Pourquoi pas:
import sys
import logging
import traceback
def log_except_hook(*exc_info):
text = "".join(traceback.format_exception(*exc_info))
logging.error("Unhandled exception: %s", text)
sys.excepthook = log_except_hook
None()
Voici la sortie avec sys.excepthook
comme vu ci-dessus:
$ python tb.py
ERROR:root:Unhandled exception: Traceback (most recent call last):
File "tb.py", line 11, in <module>
None()
TypeError: 'NoneType' object is not callable
Voici la sortie avec le sys.excepthook
commenté:
$ python tb.py
Traceback (most recent call last):
File "tb.py", line 11, in <module>
None()
TypeError: 'NoneType' object is not callable
La seule différence est que l'ancien a ERROR:root:Unhandled exception:
au début de la première ligne.
Pour construire sur la réponse de Jacinda, mais en utilisant un objet logger:
def catchException(logger, typ, value, traceback):
logger.critical("My Error Information")
logger.critical("Type: %s" % typ)
logger.critical("Value: %s" % value)
logger.critical("Traceback: %s" % traceback)
# Use a partially applied function
func = lambda typ, value, traceback: catchException(logger, typ, value, traceback)
sys.excepthook = func
Enveloppez votre appel d'entrée d'application dans un try...except
bloquer afin que vous puissiez attraper et enregistrer (et peut-être ré-augmenter) toutes les exceptions non capturées. Par exemple. au lieu de:
if __== '__main__':
main()
Faire ceci:
if __== '__main__':
try:
main()
except Exception as e:
logger.exception(e)
raise
Peut-être que vous pourriez faire quelque chose en haut d'un module qui redirige stderr vers un fichier, puis enregistrez ce fichier en bas
sock = open('error.log', 'w')
sys.stderr = sock
doSomething() #makes errors and they will log to error.log
logging.exception(open('error.log', 'r').read() )
Bien que la réponse de @ gnu_lorien me donne un bon point de départ, mon programme se bloque lors de la première exception.
Je suis arrivé avec une solution personnalisée (et/ou améliorée), qui enregistre silencieusement les exceptions de fonctions décorées avec @handle_error
.
import logging
__author__ = 'ahmed'
logging.basicConfig(filename='error.log', level=logging.DEBUG)
def handle_exception(exc_type, exc_value, exc_traceback):
import sys
if issubclass(exc_type, KeyboardInterrupt):
sys.__excepthook__(exc_type, exc_value, exc_traceback)
return
logging.critical(exc_value.message, exc_info=(exc_type, exc_value, exc_traceback))
def handle_error(func):
import sys
def __inner(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception, e:
exc_type, exc_value, exc_tb = sys.exc_info()
handle_exception(exc_type, exc_value, exc_tb)
finally:
print(e.message)
return __inner
@handle_error
def main():
raise RuntimeError("RuntimeError")
if __== "__main__":
for _ in xrange(1, 20):
main()
Pour répondre à la question de M. Zeus abordée dans la section de commentaire de la réponse acceptée, je l'utilise pour consigner les exceptions non capturées dans une console interactive (testée avec PyCharm 2018-2019). J'ai découvert que sys.excepthook
Ne fonctionnait pas dans un shell python), j'ai donc cherché plus profondément et découvert que je pouvais utiliser sys.exc_info
À la place. Cependant, sys.exc_info
ne prend aucun argument contrairement à sys.excepthook
qui prend 3 arguments.
Ici, j'utilise sys.excepthook
Et sys.exc_info
Pour consigner les deux exceptions dans une console interactive et un script avec une fonction wrapper. Pour attacher une fonction de hook aux deux fonctions, j'ai deux interfaces différentes selon que les arguments sont donnés ou non.
Voici le code:
def log_exception(exctype, value, traceback):
logger.error("Uncaught exception occurred!",
exc_info=(exctype, value, traceback))
def attach_hook(hook_func, run_func):
def inner(*args, **kwargs):
if not (args or kwargs):
# This condition is for sys.exc_info
local_args = run_func()
hook_func(*local_args)
else:
# This condition is for sys.excepthook
hook_func(*args, **kwargs)
return run_func(*args, **kwargs)
return inner
sys.exc_info = attach_hook(log_exception, sys.exc_info)
sys.excepthook = attach_hook(log_exception, sys.excepthook)
La configuration de la journalisation se trouve dans la réponse de gnu_lorien.