Registar a excepção com a localização

Como posso registar os meus erros em Python?

try:
    do_something()
except:
    # How can I log my exception here, complete with its traceback?
Author: Mark Amery, 2009-10-02

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
 145
Author: nosklo, 2016-04-14 22:28:04

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)
 88
Author: flycee, 2017-05-17 18:52:07
O meu trabalho recentemente encarregou-me de registar todas as tracebacks/excepções da nossa aplicação. Eu tentei inúmeras técnicas que outros tinham postado on-line, como o acima, mas estabeleceu em uma abordagem diferente. Anulando 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.

 48
Author: Brad Barrows, 2015-05-13 05:40:19

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.

 6
Author: Mark Amery, 2016-04-14 22:51:04

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
 3
Author: panchicore, 2016-04-16 10:10:49
Talvez não tão elegante, mas mais fácil.
#!/bin/bash
log="/var/log/yourlog"
/path/to/your/script.py 2>&1 | (while read; do echo "$REPLY" >> $log; done)
 0
Author: Hugo Walter, 2012-08-07 10:52:20
O que eu procurava:
import sys
import traceback

exc_type, exc_value, exc_traceback = sys.exc_info()
traceback_in_var = traceback.format_tb(exc_traceback)

Ver:

 0
Author: Martin Thoma, 2018-07-30 12:23:18

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')
 -1
Author: rogeriopvl, 2009-10-02 09:10:58