Comment enregistrer une erreur Python avec des informations de débogage?
j'imprime des messages d'exception Python dans un fichier journal avec logging.error
:
import logging
try:
1/0
except ZeroDivisionError as e:
logging.error(e) # ERROR:root:division by zero
est-il possible d'imprimer des informations plus détaillées sur l'exception et le code qui l'a générée que la chaîne d'exception? Des choses comme les numéros de ligne ou les traces de pile seraient grandes.
9 réponses
logger.exception
produira une trace de pile à côté du message d'erreur.
par exemple:
import logging
try:
1/0
except ZeroDivisionError as e:
logging.exception("message")
sortie:
ERROR:root:message
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero
@Paulo chèque notes," soyez conscient que dans Python 3 vous devez appeler la méthode logging.exception
juste à l'intérieur de la partie except
. Si vous appelez cette méthode dans un endroit arbitraire vous pouvez obtenir une exception bizarre. Les docs d'alerte à ce sujet."
une bonne chose au sujet de logging.exception
que la réponse de SiggyF ne montre pas est que vous pouvez passer dans un message arbitraire, et la journalisation montrera toujours le retraçage complet avec tous les détails d'exception:
import logging
try:
1/0
except ZeroDivisionError:
logging.exception("Deliberate divide by zero traceback")
avec le comportement par défaut (dans les versions récentes) de journalisation des erreurs d'impression juste à sys.stderr
, il ressemble à ceci:
>>> import logging
>>> try:
... 1/0
... except ZeroDivisionError:
... logging.exception("Deliberate divide by zero traceback")
...
ERROR:root:Deliberate divide by zero traceback
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero
utiliser les options exc_info peut être préférable, pour vous permettre de choisir le niveau d'erreur (si vous utilisez exception
, il affichera toujours error
):
try:
# do something here
except Exception as e:
logging.fatal(e, exc_info=True) # log exception info at FATAL log level
que se passe – t-il si votre application effectue une journalisation d'une autre manière-sans utiliser le module
logging
?
maintenant, traceback
peut être utilisé ici.
import traceback
def log_traceback(ex, ex_traceback=None):
if ex_traceback is None:
ex_traceback = ex.__traceback__
tb_lines = [ line.rstrip('\n') for line in
traceback.format_exception(ex.__class__, ex, ex_traceback)]
exception_logger.log(tb_lines)
-
utilisez-le dans Python 2 :
try: # your function call is here except Exception as ex: _, _, ex_traceback = sys.exc_info() log_traceback(ex, ex_traceback)
-
utilisez-le dans Python 3 :
try: x = get_number() except Exception as ex: log_traceback(ex)
si vous utilisez des journaux ordinaires - tous vos journaux doivent correspondre à cette règle: one record = one line
. En suivant cette règle, vous pouvez utiliser grep
et d'autres outils pour traiter vos fichiers journaux.
mais l'information de retraçage est multi-ligne. Donc, ma réponse est une version étendue de la solution proposée par zangw ci-dessus dans ce fil. Le problème est que les lignes de traceback pourraient avoir \n
à l'intérieur, donc nous avons besoin de faire un travail supplémentaire pour se débarrasser de cette ligne fin:
import logging
logger = logging.getLogger('your_logger_here')
def log_app_error(e: BaseException, level=logging.ERROR) -> None:
e_traceback = traceback.format_exception(e.__class__, e, e.__traceback__)
traceback_lines = []
for line in [line.rstrip('\n') for line in e_traceback]:
traceback_lines.extend(line.splitlines())
logger.log(level, traceback_lines.__str__())
après cela (lorsque vous analyserez vos journaux) vous pouvez copier / coller les lignes de traceback nécessaires à partir de votre fichier journal et faire ceci:
ex_traceback = ['line 1', 'line 2', ...]
for line in ex_traceback:
print(line)
Profit!
cette réponse se construit à partir des excellents ci-dessus.
dans la plupart des applications, vous n'appellerez pas la journalisation.exception e) directement. Vous avez probablement défini un logger personnalisé spécifique à votre application ou module comme ceci:
# Set the name of the app or module
my_logger = logging.getLogger('NEM Sequencer')
# Set the log level
my_logger.setLevel(logging.INFO)
# Let's say we want to be fancy and log to a graylog2 log server
graylog_handler = graypy.GELFHandler('some_server_ip', 12201)
graylog_handler.setLevel(logging.INFO)
my_logger.addHandler(graylog_handler)
dans ce cas, utilisez simplement le logger pour appeler l'exception (e) comme ceci:
try:
1/0
except ZeroDivisionError, e:
my_logger.exception(e)
un peu de traitement de décorateur (très librement inspiré par la Peut-être monade et le levage). Vous pouvez supprimer en toute sécurité les annotations de type Python 3.6 et utiliser un ancien style de formatage de message.
fallible.py
from functools import wraps
from typing import Callable, TypeVar, Optional
import logging
A = TypeVar('A')
def fallible(*exceptions, logger=None) \
-> Callable[[Callable[..., A]], Callable[..., Optional[A]]]:
"""
:param exceptions: a list of exceptions to catch
:param logger: pass a custom logger; None means the default logger,
False disables logging altogether.
"""
def fwrap(f: Callable[..., A]) -> Callable[..., Optional[A]]:
@wraps(f)
def wrapped(*args, **kwargs):
try:
return f(*args, **kwargs)
except exceptions:
message = f'called {f} with *args={args} and **kwargs={kwargs}'
if logger:
logger.exception(message)
if logger is None:
logging.exception(message)
return None
return wrapped
return fwrap
Démo:
In [1] from fallible import fallible
In [2]: @fallible(ArithmeticError)
...: def div(a, b):
...: return a / b
...:
...:
In [3]: div(1, 2)
Out[3]: 0.5
In [4]: res = div(1, 0)
ERROR:root:called <function div at 0x10d3c6ae8> with *args=(1, 0) and **kwargs={}
Traceback (most recent call last):
File "/Users/user/fallible.py", line 17, in wrapped
return f(*args, **kwargs)
File "<ipython-input-17-e056bd886b5c>", line 3, in div
return a / b
In [5]: repr(res)
'None'
vous pouvez également modifier cette solution pour retourner quelque chose d'un peu plus significatif que None
de la partie except
(ou même faire le solution generic, en spécifiant cette valeur de retour dans les arguments de fallible
).
si vous pouvez gérer la dépendance supplémentaire, utilisez twisted.log, vous n'avez pas besoin de log explicitement les erreurs et aussi il renvoie la totalité du traceback et du temps au fichier ou au flux.
propre façon de le faire est d'utiliser format_exc()
et ensuite d'analyser la sortie pour obtenir la partie pertinente:
from traceback import format_exc
try:
1/0
except Exception:
print 'the relevant part is: '+format_exc().split('\n')[-2]
concerne