Excepción de registro con rastreo en python

10 minutos de lectura

avatar de usuario
TIMEX

¿Cómo puedo registrar mis errores de Python?

try:
    do_something()
except:
    # How can I log my exception here, complete with its traceback?

avatar de usuario
nosklo

Usar logging.exception desde dentro de la except: handler/block para registrar la excepción actual junto con la información de seguimiento, precedida de un mensaje.

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

Ahora mirando el archivo de registro, /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

  • Revisé el código django para esto, y asumo que la respuesta es no, pero ¿hay alguna manera de limitar el rastreo a una cierta cantidad de caracteres o profundidad? El problema es que para rastreos grandes, toma bastante tiempo.

    –Eduardo Luca

    21 de mayo de 2014 a las 13:16


  • Tenga en cuenta que si define un registrador con logger = logging.getLogger('yourlogger')tu tienes que escribir logger.exception('...')tener esto funcionando…

    – 576i

    5 de febrero de 2017 a las 12:43

  • ¿Podemos modificar esto para que el mensaje se imprima con el nivel de registro INFO?

    – NM

    31 de octubre de 2017 a las 9:38

  • Tenga en cuenta que para determinadas aplicaciones externas, como Azure Insight, el trackback no se almacena en los registros. Entonces es necesario pasarlos explícitamente a la cadena del mensaje como se muestra a continuación.

    – Édgar H.

    16 de julio de 2020 a las 7:55

  • siempre había pensado logging.exception(...) (o logger.exception(...) si así está configurado) lo haría. Pero me he confundido un poco con este método. exception en el contexto de un controlador de gancho de excepción: no parece imprimir el rastreo en este contexto. Empezando a sospechar que su código de llamada tiene que estar en un except bloquear para que esto funcione.

    – mike roedor

    10 de abril de 2021 a las 17:49

avatar de usuario
Flycee

Usar exc_info las opciones pueden ser mejores, permanece la advertencia o el título del error:

try:
    # coode in here
except Exception as e:
    logging.error(e, exc_info=True)

  • Nunca puedo recordar lo que exc_info= se llama kwarg; ¡Gracias!

    – berto

    31/10/2017 a las 15:00

  • Esto es idéntico a logging.exception, con la excepción de que el tipo se registra dos veces de forma redundante. Simplemente use logging.exception a menos que desee un nivel que no sea error.

    – Wyrmwood

    17 de noviembre de 2017 a las 16:11

  • @Wyrmwood no es idéntico, ya que debe enviar un mensaje a logging.exception

    – Pedro Madera

    21 de abril de 2018 a las 7:30

  • Usando logging.erroruno puede evitar dar mensajes incómodos como '' o ' ' a logging.exception.

    – Nuclear03020704

    16 de abril de 2021 a las 2:34

avatar de usuario
brad carretillas

Recientemente, mi trabajo me encargó registrar todos los rastreos/excepciones de nuestra aplicación. Probé numerosas técnicas que otros habían publicado en línea, como la anterior, pero opté por un enfoque diferente. Primordial traceback.print_exception.

tengo un escrito en http://www.bbarrows.com/ Sería mucho más fácil de leer, pero también lo pegaré aquí.

Cuando tuve la tarea de registrar todas las excepciones que nuestro software podría encontrar en la naturaleza, probé varias técnicas diferentes para registrar nuestros rastreos de excepciones de python. Al principio pensé que el gancho de excepción del sistema python, sys.excepthook, sería el lugar perfecto para insertar el código de registro. Estaba intentando algo similar a:

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  

Esto funcionó para el subproceso principal, pero pronto descubrí que mi sys.excepthook no existiría en ningún subproceso nuevo que comenzara mi proceso. Este es un gran problema porque casi todo sucede en subprocesos en este proyecto.

Después de buscar en Google y leer mucha documentación, la información más útil que encontré fue del rastreador de problemas de Python.

La primera publicación en el hilo muestra un ejemplo de trabajo del sys.excepthook NO persiste entre subprocesos (como se muestra a continuación). Aparentemente, este es el comportamiento 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()

Los mensajes en este hilo de Python Issue realmente dan como resultado 2 trucos sugeridos. Cualquiera de las subclases Thread y envuelva el método de ejecución en nuestro propio bloque de prueba excepto para capturar y registrar excepciones o mono parche threading.Thread.run para ejecutar en su propio intento excepto bloquear y registrar las excepciones.

El primer método de subclasificación Thread me parece menos elegante en su código, ya que tendría que importar y usar su código personalizado Thread class EN TODAS PARTES que quisieras tener un hilo de registro. Esto terminó siendo una molestia porque tuve que buscar en toda nuestra base de código y reemplazar todo lo normal Threads con esta costumbre Thread. Sin embargo, estaba claro en cuanto a lo que esto Thread estaba haciendo y sería más fácil para alguien diagnosticar y depurar si algo salió mal con el código de registro personalizado. Un subproceso de registro personalizado podría tener este aspecto:

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")

El segundo método de parcheo de mono. threading.Thread.run es bueno porque podría ejecutarlo una vez justo después __main__ e instrumente mi código de registro en todas las excepciones. Sin embargo, la aplicación de parches mono puede ser molesta para depurar, ya que cambia la funcionalidad esperada de algo. El parche sugerido del rastreador de problemas de Python fue:

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

No fue hasta que comencé a probar mi registro de excepciones que me di cuenta de que lo estaba haciendo todo mal.

Para probar había colocado un

raise Exception("Test")

en algún lugar de mi código. Sin embargo, envolver un método que llamó a este método fue un bloque de prueba excepto que imprimió el rastreo y absorbió la excepción. Esto fue muy frustrante porque vi que el rastreo se traía impreso a STDOUT pero no se registraba. Fue entonces cuando decidí que un método mucho más fácil de registrar los rastreos era simplemente parchear el método que usa todo el código de Python para imprimir los rastreos, traceback.print_exception. Terminé con algo similar a lo siguiente:

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 escribe el rastreo en un búfer de cadena y lo registra en ERROR de registro. Tengo un controlador de registro personalizado que configuró el registrador ‘customLogger’ que toma los registros de nivel de ERROR y los envía a casa para su análisis.

  • Un enfoque bastante interesante. Una pregunta – add_custom_print_exception no parece estar en el sitio al que se vinculó y, en cambio, hay un código final bastante diferente allí. ¿Cuál dirías que es mejor/más final y por qué? ¡Gracias!

    – fantabuloso

    31 de julio de 2014 a las 0:53

  • Gracias, gran respuesta!

    – 101

    12 de enero de 2016 a las 3:45

  • Hay un error tipográfico de cortar y pegar. en la llamada delegada a old_print_exception, límite y archivo deben pasarse límite y archivo, no Ninguno — old_print_exception(etype, value, tb, limit, file)

    – Marvin

    27 mayo 2016 a las 15:15


  • Para su último bloque de código, en lugar de inicializar un StringIO e imprimirle la excepción, puede simplemente llamar logger.error(traceback.format_tb()) (o format_exc() si también desea la información de excepción).

    – Jaime

    06/10/2018 a las 21:01


  • Tu enlace compartido no funciona ahora. Tampoco puedo registrar el rastreo utilizando el método anterior. llamé raise Exception en el código, y antes de llamar al método que definiste. Nada esta pasando. @Brad y @Christian

    – Vaibhav Grover

    19 de abril de 2021 a las 6:07


Puede registrar todas las excepciones no detectadas en el subproceso principal asignando un controlador a sys.excepthooktal vez usando el exc_info parámetro de las funciones de registro de 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')

Sin embargo, si su programa usa subprocesos, tenga en cuenta que los subprocesos creados usando threading.Thread voluntad no generar sys.excepthook cuando ocurre una excepción no detectada dentro de ellos, como se indica en Problema 1230540 en el rastreador de problemas de Python. Se han sugerido algunos trucos para evitar esta limitación, como parches de mono Thread.__init__ sobrescribir self.run con una alternativa run método que envuelve el original en una try bloqueo y llamadas sys.excepthook desde dentro de la except bloquear. Alternativamente, puede envolver manualmente el punto de entrada para cada uno de sus subprocesos en try/except tú mismo.

Lo que estaba buscando:

import sys
import traceback

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

Ver:

Puede obtener el rastreo utilizando un registrador, en cualquier nivel (DEBUG, INFO, …). Tenga en cuenta que usando logging.exceptionel nivel es ERROR.

# 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:

Esto también funciona (usando Python 3.6)

logging.debug("Something went wrong", exc_info=True)

avatar de usuario
marca amery

Los mensajes de excepción no detectados van a STDERR, por lo que en lugar de implementar su inicio de sesión en Python, puede enviar STDERR a un archivo usando cualquier shell que esté usando para ejecutar su secuencia de comandos de Python. En un script Bash, puede hacer esto con la redirección de salida, como descrito en la guía BASH.

Ejemplos

Agregar errores al archivo, otra salida a la terminal:

./test.py 2>> mylog.log

Sobrescribir archivo con salida STDOUT y STDERR intercalada:

./test.py &> mylog.log

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad