python - ejemplos - django
¿Cómo registro un error de Python con información de depuración? (9)
Estoy imprimiendo mensajes de excepción de Python en un archivo de registro con logging.error
:
import logging
try:
1/0
except ZeroDivisionError as e:
logging.error(e) # ERROR:root:division by zero
¿Es posible imprimir información más detallada sobre la excepción y el código que la generó que solo la cadena de excepción? Cosas como números de línea o trazas de pila serían geniales.
Esta respuesta se acumula a partir de los excelentes anteriores.
En la mayoría de las aplicaciones, no llamará a logging.exception (e) directamente. Lo más probable es que haya definido un registrador personalizado específico para su aplicación o 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)
En este caso, solo use el registrador para llamar a la excepción (e) así:
try:
1/0
except ZeroDivisionError, e:
my_logger.exception(e)
Si puede hacer frente a la dependencia adicional y luego usar twisted.log, no tiene que registrar explícitamente los errores y también devuelve todo el seguimiento y el tiempo al archivo o al flujo.
Si usa registros simples, todos sus registros deben corresponder a esta regla: one record = one line
. Siguiendo esta regla, puede usar grep
y otras herramientas para procesar sus archivos de registro.
Pero la información de rastreo es multilínea. Así que mi respuesta es una versión extendida de la solución propuesta por zangw arriba en este hilo. El problema es que las líneas de rastreo podrían tener /n
dentro, por lo que necesitamos hacer un trabajo adicional para deshacernos de los finales de esta línea:
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__())
Después de eso (cuando esté analizando sus registros), podría copiar / pegar las líneas de rastreo requeridas de su archivo de registro y hacer esto:
ex_traceback = [''line 1'', ''line 2'', ...]
for line in ex_traceback:
print(line)
¡Lucro!
Un poco de tratamiento decorador (muy poco inspirado en la mónada Maybe y el lifting). Puede eliminar de forma segura las anotaciones de tipo Python 3.6 y utilizar un estilo de formato de mensaje anterior.
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
Manifestación:
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''
También puede modificar esta solución para devolver algo un poco más significativo que None
de la parte except
(o incluso hacer que la solución sea genérica, especificando este valor de retorno en los argumentos de fallible
).
Una cosa buena sobre logging.exception
que share no muestra es que puede pasar un mensaje arbitrario, y el registro seguirá mostrando el rastreo completo con todos los detalles de la excepción:
import logging
try:
1/0
except ZeroDivisionError:
logging.exception("Deliberate divide by zero traceback")
Con el comportamiento de registro predeterminado (en versiones recientes) de solo imprimir errores a sys.stderr
, se ve así:
>>> 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
Una forma limpia de hacerlo es usar format_exc()
y luego analizar la salida para obtener la parte relevante:
from traceback import format_exc
try:
1/0
except Exception:
print ''the relevant part is: ''+format_exc().split(''/n'')[-2]
Saludos
Usar las opciones de exc_info puede ser mejor, para permitirte elegir el nivel de error (si usas una exception
, siempre mostrará el error
):
try:
# do something here
except Exception as e:
logging.fatal(e, exc_info=True) # log exception info at FATAL log level
logger.exception
generará un seguimiento de pila junto con el mensaje de error.
Por ejemplo:
import logging
try:
1/0
except ZeroDivisionError as e:
logging.exception("message")
Salida:
ERROR:root:message
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero
@Paulo Verifique las notas, "tenga en cuenta que en Python 3 debe llamar al método logging.exception
justo dentro de la parte de except
. Si llama a este método en un lugar arbitrario, puede obtener una extraña excepción. La documentación advierte sobre eso".
¿Qué sucede si su aplicación registra de otra manera, sin utilizar el módulo de
logging
?
Ahora, el traceback
podría ser utilizado aquí.
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)
Utilízalo en Python 2 :
try: # your function call is here except Exception as ex: _, _, ex_traceback = sys.exc_info() log_traceback(ex, ex_traceback)
Utilízalo en Python 3 :
try: x = get_number() except Exception as ex: log_traceback(ex)