Como faço para registar um erro em Python com a informação de depuração?

estou a imprimir mensagens de excepção em Python num ficheiro de registo com logging.error:

import logging
try:
    1/0
except ZeroDivisionError as e:
    logging.error(e)  # ERROR:root:division by zero

é possível imprimir informações mais detalhadas sobre a excepção e o código que a gerou do que apenas o texto de excepção? Coisas como números de linha ou traços de pilha seria óptimo.

Author: Steven Vascellaro, 2011-03-04

9 answers

logger.exception irá enviar um traço de pilha ao lado da mensagem de erro.

Por exemplo:

import logging
try:
    1/0
except ZeroDivisionError as e:
    logging.exception("message")

Resultado:

ERROR:root:message
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero

@Paulo Cheque notas, " esteja ciente de que no Python 3 você deve chamar o método logging.exception logo dentro da parte except. Se você chamar este método em um lugar arbitrário você pode obter uma exceção bizarra. Os médicos alertaram sobre isso."

 522
Author: SiggyF, 2018-05-23 15:15:43

Uma coisa boa sobre logging.exception que SiggyF a resposta não mostra é que você pode passar uma mensagem arbitrária, e o log mostrará ainda o traceback completo com todos os detalhes da excepção:

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

Com o comportamento por omissão (nas versões recentes) de registo de erros de impressão em sys.stderr, parece-se com isto:

>>> 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 "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero
 157
Author: ncoghlan, 2018-05-23 15:14:51

Se usar as opções exc_ info poderá ser melhor, para lhe permitir escolher o nível de erro (se usar exception, ele irá sempre mostrar error):

try:
    # do something here
except Exception as e:
    logging.fatal(e, exc_info=True)  # log exception info at FATAL log level
 83
Author: flycee, 2018-03-15 09:02:54

Citar

E se a sua aplicação registar de outra forma – não usar o Módulo logging?

Agora, traceback pode ser usado aqui.

import traceback

def log_traceback(ex, ex_traceback=None):
    if ex_traceback is None:
        ex_traceback = ex.__traceback__
    tb_lines = [ line.rstrip('\n') for line in
                 traceback.format_exception(ex.__class__, ex, ex_traceback)]
    exception_logger.log(tb_lines)
  • Usa-o em Python 2:

    try:
        # your function call is here
    except Exception as ex:
        _, _, ex_traceback = sys.exc_info()
        log_traceback(ex, ex_traceback)
    
  • Usa-o em Python 3:

    try:
        x = get_number()
    except Exception as ex:
        log_traceback(ex)
    
 26
Author: zangw, 2015-10-19 10:21:36

Se usar registos simples - todos os seus registos devem corresponder a esta regra: one record = one line. Seguindo esta regra, você pode usar grep e outras ferramentas para processar seus arquivos de registro.

Mas a informação de rastreamento é multi-linha. Então minha resposta é uma versão estendida da solução proposta porzangw acima neste tópico. O problema é que as linhas de traceback podem ter \n lá dentro, por isso precisamos de fazer um trabalho extra para nos livrarmos destas terminações de linha:
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.rstrip('\n') for line in e_traceback]:
        traceback_lines.extend(line.splitlines())
    logger.log(level, traceback_lines.__str__())
[6]Depois disso (quando você vai ser ao analisar os seus registos) poderá copiar / colar as linhas de traceback necessárias do seu ficheiro de registo e fazer isto:
ex_traceback = ['line 1', 'line 2', ...]
for line in ex_traceback:
    print(line)
Lucro!
 11
Author: doomatel, 2017-05-23 11:55:19
{[3]esta resposta se constrói a partir dos acima excelentes. Na maioria das aplicações, não vai ligar para o registo.excepção e) directamente. O mais provável é que tenha definido um logger personalizado específico para a sua aplicação ou módulo como este:
# 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)

Neste caso, basta usar o logger para chamar a excepção(e) assim:

try:
    1/0
except ZeroDivisionError, e:
    my_logger.exception(e)
 9
Author: Will, 2018-05-23 15:19:03
Um pouco de tratamento para decoradores (muito vagamente inspirado pelo talvez monad e levantamento). Você pode remover com segurança as anotações do tipo Python 3.6 e usar um estilo de formatação de mensagens mais antigo.

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

Demo:

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 <function div at 0x10d3c6ae8> 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 "<ipython-input-17-e056bd886b5c>", line 3, in div
    return a / b

In [5]: repr(res)
'None'

Você também pode modificar esta solução para devolver algo um pouco mais significativo do que None da parte except (ou mesmo tornar a solução genérica, especificando este valor de retorno nos argumentos de fallible).

 0
Author: Eli Korvigo, 2018-07-12 17:09:50

Se consegue lidar com a dependência extra, então use o twisted.log, você não tem que log explicitamente erros e também retorna todo o traceback e tempo para o arquivo ou transmissão.

 -1
Author: Jakob Bowyer, 2011-03-15 01:12:53

Uma forma limpa de o fazer é usando format_exc() e, em seguida, analisar a saída para obter a parte relevante:

from traceback import format_exc

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

Relativamente a

 -6
Author: caraconan, 2013-02-20 16:50:22