Respostas:
Use logging.exception
de dentro do except:
manipulador / bloco para registrar a exceção atual junto com as informações de rastreamento, anexadas 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, olhando para o arquivo de 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')
precisará escrever logger.exception('...')
para que isso funcione ...
As exc_info
opções de uso podem ser melhores, permanece aviso ou título do erro:
try:
# coode in here
except Exception as e:
logging.error(e, exc_info=True)
exc_info=
kwarg é chamado; obrigado!
logging.exception
Meu trabalho recentemente me encarregou de registrar todas as tracebacks / exceções do nosso aplicativo. Tentei várias técnicas que outros haviam publicado on-line, como a acima, mas decidiram por uma abordagem diferente. Substituindotraceback.print_exception
.
Eu tenho um artigo em http://www.bbarrows.com/ Isso seria muito mais fácil de ler, mas eu vou colá-lo aqui também.
Quando encarregado de registrar todas as exceções que nosso software pode encontrar na natureza, tentei várias técnicas diferentes para registrar nossos rastreamentos de exceção em python. No começo, pensei que o gancho de exceção do sistema python, sys.excepthook, seria o lugar perfeito para inserir o código de log. Eu estava tentando 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
Isso funcionou para o thread principal, mas logo descobri que o my sys.excepthook não existiria em nenhum novo thread iniciado pelo meu processo. Esse é um problema enorme, porque quase tudo acontece nos threads deste projeto.
Depois de pesquisar no Google e ler muita documentação, as informações mais úteis que encontrei foram do rastreador do Python Issue.
O primeiro post no tópico mostra um exemplo de trabalho que sys.excepthook
NÃO persiste nos tópicos (como mostrado abaixo). Aparentemente, esse é um 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 do Python Issue realmente resultam em 2 hacks sugeridos. Subclasse Thread
e agrupe o método run em nossa própria tentativa, exceto o bloco para capturar e registrar exceções ou patch de macacothreading.Thread.run
para executar em sua própria tentativa, exceto bloquear e registrar as exceções.
O primeiro método de subclassificação Thread
parece-me menos elegante em seu código, pois você teria que importar e usar sua Thread
classe personalizada EM TODA PARTE QUE QUISER para ter um encadeamento de log. Isso acabou sendo um aborrecimento, porque eu tive que pesquisar em toda a nossa base de códigos e substituir tudo normal Threads
por esse 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 desse errado com o código de log personalizado. Um encadeamento de registro de custódia pode se parecer com o seguinte:
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 aplicação de patches de macacos threading.Thread.run
é bom, porque eu poderia executá-lo uma vez logo após__main__
e instrumentar meu código de log em todas as exceções. O patch do macaco pode ser irritante para a depuração, pois altera a funcionalidade esperada de algo. O patch sugerido do rastreador do Python Issue 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
Somente quando comecei a testar meu log de exceção, percebi que estava fazendo tudo errado.
Para testar eu tinha colocado um
raise Exception("Test")
em algum lugar do meu código. No entanto, agrupar um método que chamou esse método foi uma tentativa, exceto o bloco que imprimiu o retorno e engoliu a exceção. Isso foi muito frustrante porque vi o traceback ser impresso no STDOUT, mas não sendo registrado. Decidi então que um método muito mais fácil de registrar os tracebacks era apenas corrigir o método que todo o código python usa para imprimir os tracebacks, traceback.print_exception. Acabei com algo semelhante ao 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
Esse código grava o traceback em um buffer de string e o registra no log de erro. Eu tenho um manipulador de log personalizado que configura o logger 'customLogger' que pega os logs no nível de ERRO e os envia para casa para análise.
add_custom_print_exception
não parece estar no site ao qual você vinculou e, em vez disso, há um código final bem diferente lá. Qual deles você diria que é melhor / mais final e por quê? Obrigado!
logger.error(traceback.format_tb())
(ou format_exc () se você quiser as informações da exceção também).
Você pode registrar todas as exceções não capturadas no thread principal atribuindo um manipulador sys.excepthook
, talvez usando o exc_info
parâmetro das funções de log 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 seu programa usar threads, no entanto, observe que os threads criados usando nãothreading.Thread
serão acionados quando ocorrer uma exceção não capturada dentro deles, conforme observado no Problema 1230540 no rastreador de problemas do Python. Alguns hacks foram sugeridos para contornar essa limitação, como a correção de macacos para substituir por um método alternativo que agrupa o original em um bloco e chama de dentro do bloco. Como alternativa, você pode apenas quebrar manualmente o ponto de entrada de cada um dos seus threads em / você mesmo.sys.excepthook
Thread.__init__
self.run
run
try
sys.excepthook
except
try
except
As mensagens de exceção não capturadas vão para STDERR; portanto, em vez de implementar seu log no próprio Python, você pode enviar o STDERR para um arquivo usando qualquer shell que esteja usando para executar seu script Python. Em um script Bash, você pode fazer isso com o redirecionamento de saída, conforme descrito no guia BASH .
Anexe erros ao arquivo, outra saída ao terminal:
./test.py 2>> mylog.log
Sobrescreva o arquivo com as saídas STDOUT e STDERR intercaladas:
./test.py &> mylog.log
O que eu estava procurando:
import sys
import traceback
exc_type, exc_value, exc_traceback = sys.exc_info()
traceback_in_var = traceback.format_tb(exc_traceback)
Vejo:
Você pode obter o rastreamento usando um registrador, em qualquer nível (DEBUG, INFO, ...). Observe que logging.exception
, usando , o nível é ERRO.
# 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: :(
EDITAR:
Isso funciona também (usando python 3.6)
logging.debug("Something went wrong", exc_info=True)
Aqui está uma versão que usa 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()}
vez de {traceback.format_tb(stack)}
?
talvez não seja tão estiloso, mas mais fácil:
#!/bin/bash
log="/var/log/yourlog"
/path/to/your/script.py 2>&1 | (while read; do echo "$REPLY" >> $log; done)
Aqui está um exemplo simples, retirado da documentação do 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')