Получить описание исключения и стек вызовов, вызвавших исключение, в виде строки
Как преобразовать пойманное исключение <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 ответ(ов)
Чтобы продемонстрировать, как получить полный трейсбек в 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: что-то пошло не так!
Надеюсь, это поможет вам правильно обрабатывать и логировать ошибки в вашем коде!
В 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__
). Это позволяет легче передавать его как аргумент в другие функции для дальнейшей обработки.
Для тех, кто использует 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
Таким образом, данный подход позволяет удобно собирать и форматировать информацию о стеке вызовов в случае возникновения исключений.
Если ваша цель — сделать сообщение об исключении и трассировку стека точно такими же, как при выбрасывании ошибки в 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: Нет, не делай этого.
Если вы хотите получить ту же информацию, которая выводится при необработанном исключении, вы можете сделать что-то вроде этого. Импортируйте модуль traceback
, а затем используйте следующий код:
import traceback
try:
...
except Exception as e:
traceback.print_tb(e.__traceback__)
print(f"Исключение: {e}")
Этот код позволит вам напечатать трассировку стека исключения, что поможет понять, где именно произошло исключение. Обратите внимание, что в приведённом примере также выводится само сообщение исключения. Этот способ будет работать в Python 3.7 и более поздних версиях.
Вручную вызов (бросание) исключения в Python
Поймать и вывести полный трейсбек исключения в Python без остановки/выхода из программы
Как напечатать исключение в Python?
Как проверить, существует ли переменная?
Правильный способ использования try/except с модулем requests в Python?