Risposte:
Utilizzare logging.exception
dall'interno del except:
gestore / blocco per registrare l'eccezione corrente insieme alle informazioni di traccia, precedute da un messaggio.
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
Ora guardando il file di log, /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
logger = logging.getLogger('yourlogger')
devi scrivere logger.exception('...')
per far funzionare questo ...
Utilizzare le exc_info
opzioni potrebbe essere migliore, rimane un avviso o un titolo di errore:
try:
# coode in here
except Exception as e:
logging.error(e, exc_info=True)
exc_info=
si chiama il kwarg; Grazie!
logging.exception
Il mio lavoro recentemente mi ha assegnato il compito di registrare tutti i traceback / eccezioni dalla nostra applicazione. Ho provato numerose tecniche che altri avevano pubblicato online come quella sopra, ma ho optato per un approccio diverso. Prevalente traceback.print_exception
.
Ho una recensione su http://www.bbarrows.com/ Sarebbe molto più facile da leggere, ma lo incollerò anche qui.
Quando mi è stato assegnato il compito di registrare tutte le eccezioni che il nostro software potrebbe incontrare in natura, ho provato una serie di tecniche diverse per registrare i nostri traceback di eccezioni python. All'inizio ho pensato che l'hook di eccezione del sistema python, sys.excepthook, sarebbe stato il posto perfetto per inserire il codice di registrazione. Stavo provando qualcosa di simile a:
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
Questo ha funzionato per il thread principale, ma ho presto scoperto che il mio sys.excepthook non esisteva tra i nuovi thread avviati dal mio processo. Questo è un grosso problema perché quasi tutto accade nei thread di questo progetto.
Dopo aver cercato su Google e letto molta documentazione, le informazioni più utili che ho trovato sono state dal tracker Issue di Python.
Il primo post sul thread mostra un esempio funzionante del sys.excepthook
NON persistente tra i thread (come mostrato di seguito). Apparentemente questo è un comportamento previsto.
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()
I messaggi su questo thread di Python Issue danno come risultato 2 hack suggeriti. Sottoclasse Thread
e racchiudi il metodo di esecuzione nel nostro tentativo tranne blocco per catturare e registrare le eccezioni o patch scimmia threading.Thread.run
da eseguire nel tuo tentativo tranne bloccare e registrare le eccezioni.
Il primo metodo di sottoclasse Thread
mi sembra meno elegante nel tuo codice in quanto dovresti importare e utilizzare la tua Thread
classe personalizzata OVUNQUE dove vuoi avere un thread di registrazione. Questo finì per essere una seccatura perché dovevo cercare in tutta la nostra base di codice e sostituire tutto normale Threads
con questa abitudine Thread
. Tuttavia, era chiaro cosa Thread
stava facendo e sarebbe stato più facile per qualcuno diagnosticare ed eseguire il debug in caso di problemi con il codice di registrazione personalizzato. Un thread di registrazione personalizzato potrebbe essere simile al seguente:
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")
Il secondo metodo di patching delle scimmie threading.Thread.run
è carino perché potrei semplicemente eseguirlo subito dopo __main__
e strumentare il mio codice di registrazione in tutte le eccezioni. Il patching delle scimmie può essere fastidioso per il debug, poiché modifica la funzionalità prevista di qualcosa. La patch suggerita dal tracker Python Issue era:
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
Solo quando ho iniziato a testare la mia registrazione delle eccezioni mi sono reso conto che stavo andando tutto male.
Per testare avevo messo a
raise Exception("Test")
da qualche parte nel mio codice. Tuttavia, il wrapping di un metodo che ha chiamato questo metodo è stato un tentativo tranne il blocco che ha stampato il traceback e ha ingoiato l'eccezione. Questo è stato molto frustrante perché ho visto il traceback portato in STDOUT ma non registrato. Fu allora che decisi che un metodo molto più semplice per registrare i traceback era semplicemente quello di applicare una patch al metodo che tutto il codice python utilizza per stampare i traceback stessi, traceback.print_exception. Ho finito con qualcosa di simile al seguente:
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
Questo codice scrive il traceback in un buffer di stringhe e lo registra nella registrazione di ERRORE. Ho un gestore di registrazione personalizzato impostato il logger 'customLogger' che prende i registri di livello ERRORE e li invia a casa per l'analisi.
add_custom_print_exception
non sembra trovarsi sul sito a cui ti sei collegato e invece c'è un codice finale abbastanza diverso lì. Quale diresti migliore / più definitivo e perché? Grazie!
logger.error(traceback.format_tb())
(o format_exc () se vuoi anche le informazioni sull'eccezione).
Puoi registrare tutte le eccezioni non rilevate sul thread principale assegnando un gestore a sys.excepthook
, forse usando il exc_info
parametro delle funzioni di registrazione di 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')
Se il vostro programma usa le discussioni, però, quindi notare che i thread creati utilizzando threading.Thread
volontà non grilletto sys.excepthook
quando si verifica un'eccezione non rilevata al loro interno, come indicato nella Problema 1.230.540 su issue tracker di Python. Alcuni hack sono stati suggeriti lì per aggirare questa limitazione, come il patching delle scimmie Thread.__init__
da sovrascrivere self.run
con un run
metodo alternativo che avvolge l'originale in un try
blocco e chiama sys.excepthook
dall'interno del except
blocco. In alternativa, si può solo avvolgere manualmente il punto di ingresso per ognuno dei vostri thread in try
/ except
te stesso.
I messaggi di eccezione non rilevati vanno a STDERR, quindi invece di implementare la registrazione in Python stesso, è possibile inviare STDERR a un file usando qualsiasi shell che si sta utilizzando per eseguire lo script Python. In uno script Bash, puoi farlo con il reindirizzamento dell'output, come descritto nella guida BASH .
Aggiungi errori al file, altri output al terminale:
./test.py 2>> mylog.log
Sovrascrivi file con output STDOUT e STDERR interlacciati:
./test.py &> mylog.log
Cosa stavo cercando:
import sys
import traceback
exc_type, exc_value, exc_traceback = sys.exc_info()
traceback_in_var = traceback.format_tb(exc_traceback)
Vedere:
Puoi ottenere il traceback usando un logger, a qualsiasi livello (DEBUG, INFO, ...). Si noti che utilizzando logging.exception
, il livello è ERRORE.
# test_app.py
import sys
import logging
logging.basicConfig(level="DEBUG")
def do_something():
raise ValueError(":(")
try:
do_something()
except Exception:
logging.debug("Something went wrong", exc_info=sys.exc_info())
DEBUG:root:Something went wrong
Traceback (most recent call last):
File "test_app.py", line 10, in <module>
do_something()
File "test_app.py", line 7, in do_something
raise ValueError(":(")
ValueError: :(
MODIFICARE:
Anche questo funziona (usando python 3.6)
logging.debug("Something went wrong", exc_info=True)
Ecco una versione che utilizza sys.excepthook
import traceback
import sys
logger = logging.getLogger()
def handle_excepthook(type, message, stack):
logger.error(f'An unhandled exception occured: {message}. Traceback: {traceback.format_tb(stack)}')
sys.excepthook = handle_excepthook
{traceback.format_exc()}
invece di {traceback.format_tb(stack)}
?
forse non così elegante, ma più semplice:
#!/bin/bash
log="/var/log/yourlog"
/path/to/your/script.py 2>&1 | (while read; do echo "$REPLY" >> $log; done)
Ecco un semplice esempio tratto dalla documentazione di Python 2.6 :
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')