Detecte e imprima el seguimiento completo de la excepción de Python sin detener/salir del programa

11 minutos de lectura

avatar de usuario de chriscauley
chriscauley

Quiero capturar y registrar excepciones sin salir, por ejemplo,

try:
    do_stuff()
except Exception as err:
    print(Exception, err)
    # I want to print the entire traceback here,
    # not just the exception name and details

Quiero imprimir exactamente el mismo resultado que se imprime cuando se genera la excepción sin que try/except intercepte la excepción, y lo hago no quiero que salga de mi programa.

  • No es una respuesta completa, pero es posible que alguien quiera saber que puede acceder a mucha información investigando err.__traceback__ (al menos en Python 3.x)

    – Vito gentil

    25 de febrero de 2021 a las 12:10


  • Parece que soy el único en el mundo que quiere imprimir la pila cuando no hay error (= solo para ver cómo llegué aquí en esta línea precisa (no es mi código, y es tan feo que no puedo entender cómo llegó aquí!)).

    –Olivier Pons

    2 de junio de 2021 a las 8:23

  • Para entender el verdadero Zen de Python, @PavelVlasov, también debes saber cuándo el Zen de Python no es Zen. Antes de la iluminación, siguiendo los rastros de la pila, después de la iluminación, siguiendo los rastros de la pila. Un día comprenderás y estarás en paz, saltamontes. No regreses a la corrupción de Java de donde viniste.

    – NeilG

    28 de abril a las 1:01


avatar de usuario de volting
Voltaje

traceback.format_exc() o sys.exc_info() dará más información si eso es lo que quieres.

import traceback
import sys

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    # or
    print(sys.exc_info()[2])

  • print(sys.exc_info()[0] huellas dactilares <class 'Exception'>.

    – weberc2

    28 de agosto de 2019 a las 15:52

  • no use exc… el rastreo contiene toda la información stackoverflow.com/questions/4564559/…

    – droide192

    8 sep 2019 a las 18:11

  • print(sys.exc_info()[2]) rendimientos <traceback object at 0x0000028A79E6B2C8>.

    – Teepeemm

    14 oct 2020 a las 14:23

  • print(traceback.format_exc()) es mejor que traceback.print_tb(exc.__traceback__). print(sys.exc_info()) devuelve toda la tupla y parece (<class 'UnicodeDecodeError'>, UnicodeDecodeError('utf-8', b'\x81', 0, 1, 'invalid start byte'), <traceback object at 0x7f179d64ae00>) Así que de hecho traceback.format_exc() es realmente superior porque eso imprime Traceback (most recent call last): File "<ipython-input-15-9e3d6e01ef04>", line 2, in <module> b"\x81".decode() UnicodeDecodeError: 'utf-8' codec can't decode byte 0x81 in position 0: invalid start byte

    – Marca

    7 mayo 2021 a las 20:47


  • quita eso exc_info() No funciona.

    – usamec

    2 de agosto de 2022 a las 12:26

Avatar de usuario de Sylvain Leroux
Sylvain Leroux

Alguna otra respuesta ya ha señalado el rastrear módulo.

Tenga en cuenta que con print_exc, en algunos casos de esquina, no obtendrá lo que esperaría. En Python 2.x:

import traceback

try:
    raise TypeError("Oups!")
except Exception, err:
    try:
        raise TypeError("Again !?!")
    except:
        pass

    traceback.print_exc()

… mostrará el rastreo de la último excepción:

Traceback (most recent call last):
  File "e.py", line 7, in <module>
    raise TypeError("Again !?!")
TypeError: Again !?!

Si realmente necesita acceder al original rastrear una solución es almacenar en caché el información de excepción como regresado de exc_info en una variable local y mostrarlo usando print_exception:

import traceback
import sys

try:
    raise TypeError("Oups!")
except Exception, err:
    try:
        exc_info = sys.exc_info()

        # do you usefull stuff here
        # (potentially raising an exception)
        try:
            raise TypeError("Again !?!")
        except:
            pass
        # end of useful stuff


    finally:
        # Display the *original* exception
        traceback.print_exception(*exc_info)
        del exc_info

Productor:

Traceback (most recent call last):
  File "t.py", line 6, in <module>
    raise TypeError("Oups!")
TypeError: Oups!

Sin embargo, algunas trampas con esto:

  • Del documento de sys_info:

    Asignar el valor de retorno de rastreo a una variable local en una función que está manejando una excepción causará una referencia circular. Esto evitará que cualquier elemento referenciado por una variable local en la misma función o por el rastreo sea recolectado como basura. […] Si necesita el rastreo, asegúrese de eliminarlo después de usarlo (es mejor hacerlo con un intento… finalmente declaración)

  • pero, del mismo documento:

    A partir de Python 2.2, dichos ciclos se recuperan automáticamente cuando la recolección de basura está habilitada y se vuelven inalcanzables, pero sigue siendo más eficiente para evitar la creación de ciclos.


Por otro lado, al permitirle acceder al rastreo asociado con una excepción, Python 3 produce un resultado menos sorprendente:

import traceback

try:
    raise TypeError("Oups!")
except Exception as err:
    try:
        raise TypeError("Again !?!")
    except:
        pass

    traceback.print_tb(err.__traceback__)

… mostrará:

  File "e3.py", line 4, in <module>
    raise TypeError("Oups!")

avatar de usuario de dimo414
dimo414

Si está depurando y solo quiere ver el seguimiento de la pila actual, simplemente puede llamar:

traceback.print_stack()

No es necesario generar manualmente una excepción solo para volver a detectarla.

  • El módulo de rastreo hace exactamente eso: genera y detecta una excepción.

    – pimienta

    7 de noviembre de 2015 a las 19:08

  • La salida va a STDERR por defecto, por cierto. No aparecía en mis registros porque estaba siendo redirigido a otro lugar.

    – mpen

    11 de diciembre de 2018 a las 0:18

  • @pppery No puedo verlo con python 3.8. Y la cosa con try y catch es que no muestra el rastreo completo, solo de raise a except.

    – x-yuri

    25 de junio de 2020 a las 19:12


Rusia debe eliminar el avatar de usuario de Putin
Rusia debe sacar a Putin

¿Cómo imprimir el rastreo completo sin detener el programa?

Cuando no desea detener su programa por un error, debe manejar ese error con un intento/excepto:

try:
    do_something_that_might_error()
except Exception as error:
    handle_the_error(error)

Para extraer el rastreo completo, usaremos el traceback módulo de la biblioteca estándar:

import traceback

Y para crear un stacktrace decentemente complicado para demostrar que obtenemos el stacktrace completo:

def raise_error():
    raise RuntimeError('something bad happened!')

def do_something_that_might_error():
    raise_error()

Impresión

A imprimir el rastreo completo, use el traceback.print_exc método:

try:
    do_something_that_might_error()
except Exception as error:
    traceback.print_exc()

Que imprime:

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

Mejor que imprimir, iniciar sesión:

Sin embargo, una mejor práctica es tener un registrador configurado para su módulo. Sabrá el nombre del módulo y podrá cambiar los niveles (entre otros atributos, como los controladores)

import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

En cuyo caso, querrá el logger.exception función en su lugar:

try:
    do_something_that_might_error()
except Exception as error:
    logger.exception(error)

Que registra:

ERROR:__main__:something bad happened!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

O tal vez solo quiera la cuerda, en cuyo caso, querrá la traceback.format_exc función en su lugar:

try:
    do_something_that_might_error()
except Exception as error:
    logger.debug(traceback.format_exc())

Que registra:

DEBUG:__main__:Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

Conclusión

Y para las tres opciones, vemos que obtenemos el mismo resultado que cuando tenemos un error:

>>> do_something_that_might_error()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

cual usar

Las preocupaciones de rendimiento no son importantes aquí, ya que IO suele dominar. Preferiría, ya que hace precisamente lo que se solicita de una manera compatible con versiones posteriores:

logger.exception(error)

Los niveles de registro y las salidas se pueden ajustar, lo que facilita el apagado sin tocar el código. Y, por lo general, hacer lo que se necesita directamente es la forma más eficiente de hacerlo.

Avatar de usuario de Anthony
Antonio

Primero, no use prints para el registro, existe un estable, probado y bien pensado stdlib módulo para hacer eso: logging. Tu definitivamente debería úsalo en su lugar.

En segundo lugar, no caiga en la tentación de hacer una desorden con herramientas no relacionadas cuando existe un enfoque nativo y simple. Aquí lo tienes:

log = logging.getLogger(__name__)

try:
    call_code_that_fails()
except MyError:
    log.exception('Any extra info you want to see in your logs')

Eso es todo. Ya has terminado.

Explicación para cualquiera que esté interesado en cómo funcionan las cosas debajo del capó

Qué log.exception lo que realmente está haciendo es solo una llamada a log.error (es decir, evento de registro con nivel ERROR) y imprima el rastreo entonces.

¿Por qué es mejor?

Bueno, aquí hay algunas consideraciones:

  • es sólo bien;
  • es sencillo;
  • Es simple.

¿Por qué nadie debería usar traceback o registrador de llamadas con exc_info=True o ensuciarse las manos con sys.exc_info?

Bueno, ¡solo porque sí! Todos ellos existen para diferentes propósitos. Por ejemplo, traceback.print_excLa salida de es un poco diferente de los rastreos producidos por el propio intérprete. Si lo usa, confundirá a cualquiera que lea sus registros, se golpeará la cabeza contra ellos.

Paso exc_info=True registrar llamadas es simplemente inapropiado. Peroes útil cuando detecta errores recuperables y desea registrarlos (utilizando, por ejemplo, INFO nivel) con trazabilidades también, porque log.exception produce registros de un solo nivel – ERROR.

Y definitivamente deberías evitar jugar con sys.exc_info tanto como puedas. Simplemente no es una interfaz pública, es una interfaz interna: usted poder utilícelo si definitivamente sabe lo que está haciendo. No está diseñado solo para imprimir excepciones.

  • Tampoco funciona como está. Eso no es todo. No he terminado ahora: esta respuesta solo hace perder el tiempo.

    – A. Rager

    27 de abril de 2019 a las 16:07


  • También agregaría que solo puedes hacer logging.exception(). No es necesario crear una instancia de registro a menos que tenga requisitos especiales.

    – Shital Shah

    18 de mayo de 2019 a las 6:35

  • Encuentro esta respuesta un poco ridícula. Está lleno de “haz/no hagas esto solo porque sí” sin explicar por qué. Sus puntos en “¿por qué es mejor?” Prácticamente es todo decir lo mismo: “porque es”. Lo cual no encuentro útil. Al menos explicaste un poco.

    – Romper

    18 de diciembre de 2020 a las 1:28

  • Buena información (no sabía sobre logging.exception) pero un poco condescendiente. Creo que esto se debe a la barrera del idioma más que a una intención maliciosa.

    – rjh

    5 sep 2021 a las 14:49


  • Lo que dijo este tipo. En mi empresa despediremos a cualquiera que inicie sesión utilizando la impresión. /s

    – omni

    14 de enero de 2022 a las 12:33

traceback.format_exception(exception_object)

Si solo tiene el objeto de excepción, puede obtener el rastreo como una cadena desde cualquier punto del código en Python 3 con:

import traceback

''.join(traceback.format_exception(None, exc_obj, exc_obj.__traceback__))

Ejemplo completo:

#!/usr/bin/env python3

import traceback

def f():
    g()

def g():
    raise Exception('asdf')

try:
    g()
except Exception as e:
    exc_obj = e

tb_str="".join(traceback.format_exception(None, exc_obj, exc_obj.__traceback__))
print(tb_str)

Producción:

Traceback (most recent call last):
  File "./main.py", line 12, in <module>
    g()
  File "./main.py", line 9, in g
    raise Exception('asdf')
Exception: asdf

Documentación: https://docs.python.org/3.9/library/traceback.html#traceback.format_exception

Ver también: Extraer información de rastreo de un objeto de excepción

Probado en Python 3.9

  • Tampoco funciona como está. Eso no es todo. No he terminado ahora: esta respuesta solo hace perder el tiempo.

    – A. Rager

    27 de abril de 2019 a las 16:07


  • También agregaría que solo puedes hacer logging.exception(). No es necesario crear una instancia de registro a menos que tenga requisitos especiales.

    – Shital Shah

    18 de mayo de 2019 a las 6:35

  • Encuentro esta respuesta un poco ridícula. Está lleno de “haz/no hagas esto solo porque sí” sin explicar por qué. Sus puntos en “¿por qué es mejor?” Prácticamente es todo decir lo mismo: “porque es”. Lo cual no encuentro útil. Al menos explicaste un poco.

    – Romper

    18 de diciembre de 2020 a las 1:28

  • Buena información (no sabía sobre logging.exception) pero un poco condescendiente. Creo que esto se debe a la barrera del idioma más que a una intención maliciosa.

    – rjh

    5 sep 2021 a las 14:49


  • Lo que dijo este tipo. En mi empresa despediremos a cualquiera que inicie sesión utilizando la impresión. /s

    – omni

    14 de enero de 2022 a las 12:33

Avatar de usuario de Neuron
Neurona

Además de la respuesta de Aaron Hall, si está iniciando sesión, pero no quiere usar logging.exception() (dado que se registra en el nivel de ERROR), puede usar un nivel más bajo y pasar exc_info=True. p.ej

try:
    do_something_that_might_error()
except Exception:
    logging.info('General exception noted.', exc_info=True)

  • Esto también es bueno cuando se trata de una falla detectada en el registro… es decir, cuando por alguna razón no ha podido crear un objeto registrador real.

    – mike roedor

    21 de diciembre de 2021 a las 17:09

¿Ha sido útil esta solución?