6

Получить описание исключения и стек вызовов, вызвавших исключение, в виде строки

1

Как преобразовать пойманное исключение <code>Exception</code> (его описание и трассировку стека) в строку <code>str</code> для внешнего использования?

try:
    method_that_can_raise_an_exception(params)
except Exception as e:
    print(complete_exception_description(e))

В этом коде у меня возникает проблема с тем, как корректно извлечь полное описание исключения вместе с трассировкой стека, чтобы можно было использовать его в дальнейшем (например, для логирования или отображения пользователю). Как можно реализовать функцию <code>complete_exception_description(e)</code>, которая вернёт эту информацию в виде строки?

5 ответ(ов)

1

Чтобы продемонстрировать, как получить полный трейсбек в Python, давайте создадим небольшой код, который вызывает ошибку:

def raise_error():
    raise RuntimeError('что-то пошло не так!')

def do_something_that_might_error():
    raise_error()

Логирование полного трассировки

Хорошей практикой является настройка логгера для вашего модуля. Это позволяет логгеру знать название модуля и изменять уровни логирования (среди других атрибутов, таких как обработчики и т.д.):

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

Теперь мы можем использовать этот логгер для получения ошибки:

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

В результате мы получим такое сообщение в логах:

ERROR:__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: что-то пошло не так!

Таким образом, вывод будет схож с тем, что мы получаем при обычном возникновении ошибки:

>>> 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: что-то пошло не так!

Получение лишь строки

Если вам действительно нужна только строка с трассировкой, вы можете использовать функцию traceback.format_exc. Пример использования:

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

В этом случае мы получим в логах следующее:

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: что-то пошло не так!

Надеюсь, это поможет вам правильно обрабатывать и логировать ошибки в вашем коде!

0

В Python 3 следующий код форматирует объект Exception точно так же, как это делает traceback.format_exc():

import traceback

try: 
    method_that_can_raise_an_exception(params)
except Exception as ex:
    print(''.join(traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__)))

Преимущество данного подхода в том, что для него нужен только сам объект Exception (благодаря сохраненному атрибуту __traceback__). Это позволяет легче передавать его как аргумент в другие функции для дальнейшей обработки.

0

Для тех, кто использует Python-3

С помощью модуля traceback и свойства exception.__traceback__ можно извлечь трассировку стека следующим образом:

  • Получите текущую трассировку стека с помощью traceback.extract_stack()
  • Удалите три последних элемента (они относятся к вызовам, которые привели к выполнению вашей функции отладки)
  • Добавьте __traceback__ из объекта исключения, используя traceback.extract_tb()
  • Отформатируйте все это с помощью traceback.format_list()
import traceback
def exception_to_string(excp):
    stack = traceback.extract_stack()[:-3] + traceback.extract_tb(excp.__traceback__)  # добавьте limit=?? 
    pretty = traceback.format_list(stack)
    return ''.join(pretty) + '\n  {} {}'.format(excp.__class__, excp)

Простая демонстрация:

def foo():
    try:
        something_invalid()
    except Exception as e:
        print(exception_to_string(e))

def bar():
    return foo()

Когда мы вызываем bar(), получаем следующий вывод:

  File "./test.py", line 57, in <module>
    bar()
  File "./test.py", line 55, in bar
    return foo()
  File "./test.py", line 50, in foo
    something_invalid()

  <class 'NameError'> name 'something_invalid' is not defined

Таким образом, данный подход позволяет удобно собирать и форматировать информацию о стеке вызовов в случае возникновения исключений.

0

Если ваша цель — сделать сообщение об исключении и трассировку стека точно такими же, как при выбрасывании ошибки в Python, то следующий код будет работать как в Python 2, так и в Python 3:

import sys, traceback

def format_stacktrace():
    parts = ["Traceback (most recent call last):\n"]
    parts.extend(traceback.format_stack(limit=25)[:-2])
    parts.extend(traceback.format_exception(*sys.exc_info())[1:])
    return "".join(parts)

# ПРИМЕР НИЖЕ...

def a():
    b()

def b():
    c()

def c():
    d()

def d():
    assert False, "Нет, не делай этого."

print("ЭТО ОФОРМЛЕННАЯ СТРОКА")
print("============================\n")

try:
    a()
except:
    stacktrace = format_stacktrace()
    print(stacktrace)

print("ТАК ЭТО ДЕЛАЕТ PYTHON")
print("==========================\n")
a()

Этот код работает путем удаления последнего вызова format_stacktrace() из стека и объединения остальных. При выполнении приведенный выше пример выдаст следующий вывод:

ЭТО ОФОРМЛЕННАЯ СТРОКА
============================

Traceback (most recent call last):
  File "test.py", line 31, in <module>
    a()
  File "test.py", line 12, in a
    b()
  File "test.py", line 16, in b
    c()
  File "test.py", line 20, in c
    d()
  File "test.py", line 24, in d
    assert False, "Нет, не делай этого."
AssertionError: Нет, не делай этого.

ТАК ЭТО ДЕЛАЕТ PYTHON
==========================

Traceback (most recent call last):
  File "test.py", line 38, in <module>
    a()
  File "test.py", line 12, in a
    b()
  File "test.py", line 16, in b
    c()
  File "test.py", line 20, in c
    d()
  File "test.py", line 24, in d
    assert False, "Нет, не делай этого."
AssertionError: Нет, не делай этого.
0

Если вы хотите получить ту же информацию, которая выводится при необработанном исключении, вы можете сделать что-то вроде этого. Импортируйте модуль traceback, а затем используйте следующий код:

import traceback

try:
    ...
except Exception as e:
    traceback.print_tb(e.__traceback__)
    print(f"Исключение: {e}")

Этот код позволит вам напечатать трассировку стека исключения, что поможет понять, где именно произошло исключение. Обратите внимание, что в приведённом примере также выводится само сообщение исключения. Этот способ будет работать в Python 3.7 и более поздних версиях.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь