Comment enregistrer une erreur Python avec des informations de débogage?

logging.error 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é que la simple chaîne d’exception? Des choses comme les numéros de ligne ou les traces de stack seraient excellentes.

logger.exception affichera une trace de stack à 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 "", line 2, in  ZeroDivisionError: integer division or modulo by zero 

Notes @Paulo Check , ” Sachez 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 à un endroit quelconque, vous pouvez obtenir une exception bizarre.

Une chose intéressante à propos de logging.exception à logging.exception que la réponse de SiggyF ne s’affiche pas, est que vous pouvez transmettre un message arbitraire et que la journalisation affichera toujours la trace complète avec tous les détails de l’exception:

 import logging try: 1/0 except ZeroDivisionError: logging.exception("Deliberate divide by zero traceback") 

Avec le comportement de journalisation par défaut (dans les versions récentes) sys.stderr à simplement imprimer des erreurs sur sys.stderr , cela 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 "", line 2, in  ZeroDivisionError: integer division or modulo by zero 

Utiliser les options exc_info peut être mieux, pour vous permettre de choisir le niveau d’erreur (si vous utilisez une exception , cela affichera toujours une error ):

 try: # do something here except Exception as e: logging.fatal(e, exc_info=True) # log exception info at FATAL log level 

Citant

Que se passe-t-il si votre application se connecte d’une autre manière – n’utilise pas le module de 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.rssortingp('\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 simples – tous vos enregistrements de journal doivent correspondre à cette règle: one record = one line . Suivant cette règle, vous pouvez utiliser grep et d’autres outils pour traiter vos fichiers journaux.

Mais les informations de retraçage sont multi-lignes. 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 trace peuvent avoir \n intérieur, donc nous devons faire un travail supplémentaire pour nous débarrasser de ces terminaisons de ligne:

 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.rssortingp('\n') for line in e_traceback]: traceback_lines.extend(line.splitlines()) logger.log(level, traceback_lines.__str__()) 

Après cela (lorsque vous parsingrez vos journaux), vous pouvez copier / coller les lignes de trace requirejses à partir de votre fichier journal et procédez comme suit:

 ex_traceback = ['line 1', 'line 2', ...] for line in ex_traceback: print(line) 

Profit!

Cette réponse se base sur les excellentes précédentes.

Dans la plupart des applications, vous n’appellerez pas directement logging.exception (e). Vous avez probablement défini un enregistreur 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 consignateur 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 vaguement inspiré de la monade Maybe et du lifting). Vous pouvez supprimer en toute sécurité les annotations de type Python 3.6 et utiliser un ancien style de formatage des messages.

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  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 "", line 3, in div return a / b In [5]: repr(res) 'None' 

Vous pouvez également modifier cette solution pour renvoyer quelque chose d’un peu plus significatif que None depuis la partie except (ou même rendre la solution générique, en spécifiant cette valeur de retour dans les arguments de fallible).

Si vous pouvez faire face à la dépendance supplémentaire, utilisez twisted.log, vous n’avez pas à consigner explicitement les erreurs et il renvoie également la traceback complète et l’heure au fichier ou au stream.

Une méthode propre consiste à utiliser format_exc() , puis à parsingr le résultat pour obtenir la partie appropriée:

 from traceback import format_exc try: 1/0 except Exception: print 'the relevant part is: '+format_exc().split('\n')[-2] 

Cordialement