Registar a excepção com a localização
try:
do_something()
except:
# How can I log my exception here, complete with its traceback?
8 answers
Utilizar logging.exception
de um manipulador except:
para registar a excepção actual, pré-carregada com uma mensagem.
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
Agora a olhar para o ficheiro de Registo, /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
Use as opções exc_ info podem ser melhores, continua a ser o título de aviso ou erro:
try:
# coode in here
except Exception as e:
logging.error(e, exc_info=True)
traceback.print_exception
.
Tenho uma carta escrita em http://www.bbarrows.com isso seria muito mais fácil de ler, mas vou colar aqui também.
Quando me encarregaram de registar todas as excepções que o nosso software possa encontrar na natureza, tentei ... número de técnicas diferentes para registar as nossas tracebacks de excepção python. No início pensei que o gancho de exceção do sistema python, sys.excepto que seria o lugar perfeito para inserir o código de Registo. Estava a tentar algo parecido com: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
Isto funcionou para o fio principal, mas logo descobri que o meu sistema.excepto que não existiria através de novos fios que o meu processo começou. Esta é uma questão enorme, porque a maioria das coisas acontece em threads neste projeto.
Depois de pesquisar no Google e lendo muita documentação, a informação mais útil que encontrei foi do Rastreador de edição Python.
O primeiro post no tópico mostra um exemplo de trabalho do sys.excepthook
não persistindo através dos tópicos (como mostrado abaixo). Aparentemente este é o comportamento esperado.
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()
As mensagens neste tópico de edição em Python realmente resultam em 2 hacks sugeridos. Quer a subclasse Thread
e embrulhar o método de execução na nossa própria tentativa, excepto em bloco, a fim de apanhar e registar excepções ou em monkey patch threading.Thread.run
para correr na sua própria tentativa, excepto bloquear e registar as excepções.
O primeiro método de subclassificação {[[8]} parece-me ser menos elegante no seu código, uma vez que teria de importar e usar a sua classe personalizada Thread
em todo o lado que quisesse ter um tópico de Registo. Isto acabou sendo um aborrecimento porque eu tive que pesquisar toda a nossa base de código e substituir todos os normais Threads
com este costume Thread
. No entanto, ficou claro o que isso Thread
estava fazendo e seria mais fácil para alguém diagnosticar e depurar se algo correr mal com o código de registo personalizado. Um tópico de Registo custome pode ser parecido com este:
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")
O segundo método de remendar macacos threading.Thread.run
é bom porque eu poderia apenas executá-lo uma vez logo após __main__
e instrumento o meu código de registo em todas as excepções. Mackey patching pode ser irritante para depurar, embora como ele muda a funcionalidade esperada de algo. O patch sugerido do emissor Python foi:
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
Não foi até eu começar a testar o meu ... registo de exceção percebi que estava a fazer tudo errado.
Para testar tinha colocado um
raise Exception("Test")
Algures no meu código. No entanto, embrulhar um método que chamou este método foi uma tentativa, exceto bloquear que imprimiu o traceback e engoliu a exceção. Isso foi muito frustrante porque eu vi o traceback trazer impresso para STDOUT, mas não sendo registrado. Foi então que eu decidi que um método muito mais fácil de registar as tracebacks era apenas para monkey patch o método que todos o código python usa para imprimir os tracebacks eles mesmos, traceback.print_ exception.
Eu acabei com algo parecido com o seguinte:
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
Este código Escreve a localização para um Buffer de texto e regista-o no erro de Registo. Eu tenho um controlador de registro personalizado configurar o logger 'customLogger' que pega os registros do nível de erro e enviá-los para casa para análise.
Você pode registar todas as excepções por limpar no tópico principal, atribuindo um manipulador a sys.excepthook
, talvez usando o exc_info
parâmetro das funções de registo do 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 o programa utiliza threads, no entanto, em seguida, observe que tópicos criados usando threading.Thread
vai não trigger sys.excepthook
quando uma exceção não identificada ocorre dentro deles, como observado em Problema 1230540 em Python issue tracker. Alguns hacks foram sugeridos lá para trabalhar em torno desta limitação, como monkey-patching Thread.__init__
para substituir self.run
com um método alternativo run
que envolve o original em um bloco try
e chama sys.excepthook
de dentro do bloco except
. Alternativamente, você pode apenas embrulhar manualmente o ponto de entrada para cada um dos seus tópicos em try
/except
tu mesmo.
As mensagens de excepção que não foram verificadas vão para o STDERR, por isso, em vez de implementar o seu registo no próprio Python, poderá enviar o STDERR para um ficheiro com qualquer linha de comandos que esteja a usar para executar o seu programa em Python. Num programa da Bash, poderá fazer isto com o redireccionamento do resultado, como descrito no Guia da BASH .
Exemplos
Adicione erros ao ficheiro, outros resultados ao terminal:
./test.py 2>> mylog.log
Sobrepor o ficheiro com 'interleaved STDOUT' e 'STDERR output':
./test.py &> mylog.log
#!/bin/bash
log="/var/log/yourlog"
/path/to/your/script.py 2>&1 | (while read; do echo "$REPLY" >> $log; done)
import sys
import traceback
exc_type, exc_value, exc_traceback = sys.exc_info()
traceback_in_var = traceback.format_tb(exc_traceback)
Ver:
Hera um exemplo simples retirado da documentação em 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')