Как использовать трассировку стека в Python (модуль traceback)

Раздел: Python -> Ошибки и исключения

Основы трассировки стека и модуль traceback

Трассировка стека (stack trace или traceback) показывает последовательность вызовов функций, которая привела к возникновению исключения. В Python за работу с ней отвечает встроенный модуль traceback. Он позволяет получать, форматировать и выводить диагностическую информацию об ошибках.

Как эффективно получить и залогировать полный traceback?

Основной способ - использовать функцию traceback.format_exc() или traceback.print_exc(). Первая возвращает строку с отформатированным traceback, вторая выводит его в stderr. Обычно их вызывают в блоке except.

import traceback

try:
    1 / 0
except ZeroDivisionError:
    tb_str = traceback.format_exc()
    print('Получен traceback:')
    print(tb_str)

Client error python (ошибка http-клиента в python)

Получен traceback:
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero

No installed python found (python не найден в системе)

Проблема: При использовании print_exc() без аргументов вывод идёт в sys.stderr, что может быть неудобно при перенаправлении потоков. Решение - передать файловый объект или использовать форматирование в строку.

Как сохранить traceback в переменную для дальнейшего анализа?

Функция traceback.format_exc() возвращает строку. Её можно сохранить в лог, отправить по сети или вывести в GUI.

import traceback, logging

try:
    open('nonexistent.file')
except FileNotFoundError:
    tb = traceback.format_exc()
    logging.error('Ошибка открытия файла:\n%s', tb)

Python traceback using (трассировка ошибок в python)

Типичная ошибка: Забывают вызвать форматирование внутри блока except, когда исключение активно. Если вызов происходит вне except, sys.exc_info() возвращает None, и traceback будет пустым. Всегда проверяйте, что код находится в обработчике исключения.

Как записать traceback в лог с помощью logging?

Модуль logging предоставляет метод logging.exception(), который автоматически добавляет traceback текущего исключения в сообщение лога.

import logging

logging.basicConfig(level=logging.ERROR)
try:
    x = [1, 2, 3][10]
except IndexError:
    logging.exception('Произошла ошибка индекса')

Python pip not found (ошибка 'pip not found' в python)

ERROR:root:Произошла ошибка индекса
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
IndexError: list index out of range

Unable to locate package python (ошибка 'unable to locate package' в python)

Особенность: logging.exception() использует уровень ERROR. Для других уровней (например, WARNING) нужно вызывать logger.warning(tb, exc_info=True). Иначе traceback не будет добавлен.

Как извлечь только верхние или нижние фреймы стека?

Функция traceback.extract_tb() возвращает список объектов FrameSummary. Параметр limit ограничивает количество фреймов. Отрицательное значение limit возвращает фреймы с конца.

import traceback, sys

def f():
    return 1/0

def g():
    f()

try:
    g()
except:
    _, _, tb = sys.exc_info()
    frames = traceback.extract_tb(tb, limit=2)  # только последние 2 фрейма
    for frame in frames:
        print(f'{frame.filename}:{frame.lineno} in {frame.name}')

File not found python (ошибка filenotfounderror в python)

<stdin>:3 in f
<stdin>:6 in g

Python modulenotfounderror no module named (ошибка modulenotfounderror)

Проблема: При использовании sys.exc_info() важно не присваивать результат нескольким переменным, иначе можно потерять ссылку на трейсбек. Лучше сразу извлекать tb и работать с ним.

Как получить traceback для текущего стека без исключения?

Функция traceback.print_stack() выводит текущий стек вызовов. Полезна для отладки, чтобы понять, откуда была вызвана функция.

import traceback

def alpha():
    beta()

def beta():
    traceback.print_stack()

alpha()

Io error python (ошибка ввода-вывода в python)

  File "<stdin>", line 9, in <module>
    alpha()
  File "<stdin>", line 4, in alpha
    beta()
  File "<stdin>", line 7, in beta
    traceback.print_stack()

ошибка компиляции python (ошибка компиляции (синтаксиса) в python)

Предостережение: print_stack() выводит в stderr. Для сохранения используйте traceback.format_stack(), которая возвращает список строк.

Как обработать исключение с сохранением цепочки стека (chained exceptions)?

При повторном возбуждении исключения используйте конструкцию raise ... from, чтобы сохранить исходный traceback. Это помогает отследить первопричину.

def parse(data):
    try:
        return int(data)
    except ValueError as e:
        raise RuntimeError('Неверный формат') from e

try:
    parse('abc')
except RuntimeError as err:
    print('Исключение с цепочкой:')
    traceback.print_exc()

Python traceback (трассировка стека в python)

Исключение с цепочкой:
Traceback (most recent call last):
  File "<stdin>", line 16, in parse
    return int(data)
ValueError: invalid literal for int() with base 10: 'abc'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 23, in <module>
    parse('abc')
  File "<stdin>", line 18, in parse
    raise RuntimeError('Неверный формат') from e
RuntimeError: Неверный формат

Script not found python (ошибка 'script not found')

Ошибка: Если используется raise ... from None, цепочка обрывается, и исходный traceback теряется. Применяйте это только если исходное исключение не должно быть видно.

Как настроить формат traceback в тестах (unittest)?

Модуль unittest выводит traceback автоматически при падении тестов. Если нужен свой формат, можно заменить вывод с помощью traceback.print_exception() в кастомном TestResult.

import unittest, traceback, io

class CustomTestResult(unittest.TextTestResult):
    def addFailure(self, test, err):
        with io.StringIO() as buf:
            traceback.print_exception(*err, file=buf)
            super().addFailure(test, (err[0], err[1], err[2]))

if __name__ == '__main__':
    unittest.main(testRunner=unittest.TextTestRunner(resultclass=CustomTestResult))

Сложность: Для глубокой кастомизации потребуется переопределить методы TestResult. Необходимо хорошо понимать внутреннее устройство unittest.

- Python file exceptions (исключения файлов в python)
- Python no module named pip (ошибка 'no module named pip' в python)
- Python externally managed environment (ошибка externally managed environment в python)

Расширенные примеры работы с traceback

В этом разделе приведены более сложные сценарии использования модуля traceback, которые часто встречаются на практике.

Кастомное форматирование с TracebackException

Класс traceback.TracebackException позволяет детально управлять форматированием: исключать контекст, ограничивать глубину, изменять представление стека.

Пример
import traceback, sys

try:
    1 / 0
except ZeroDivisionError:
    exc_type, exc_value, exc_tb = sys.exc_info()
    tbe = traceback.TracebackException(exc_type, exc_value, exc_tb, limit=2)
    # Пропускаем первую строку 'Traceback...'
    formatted = list(tbe.format())[1:]  # список строк без заголовка
    for line in formatted:
        print(line, end='')
  File "<stdin>", line 3, in <module>
ZeroDivisionError: division by zero

Цепочка исключений с явным указанием причины

При перехвате одного исключения и возбуждении другого с сохранением контекста (использование raise ... from) модуль traceback показывает обе трассировки.

Пример
import traceback

def read_data(filepath):
    try:
        with open(filepath) as f:
            return f.read()
    except FileNotFoundError as e:
        raise ValueError(f'Файл {filepath} не найден') from e

try:
    read_data('/nonexistent')
except ValueError:
    traceback.print_exc()
Traceback (most recent call last):
  File "<stdin>", line 6, in read_data
    with open(filepath) as f:
FileNotFoundError: [Errno 2] No such file or directory: '/nonexistent'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 14, in <module>
    read_data('/nonexistent')
  File "<stdin>", line 8, in read_data
    raise ValueError(f'Файл {filepath} не найден') from e
ValueError: Файл /nonexistent не найден

Извлечение стека из нескольких потоков

Для получения стека вызовов другого потока используется traceback.format_stack(sys._current_frames()[thread_id]). Это полезно при анализе взаимоблокировок.

Пример
import sys, threading, traceback

def worker():
    import time
    time.sleep(10)

t = threading.Thread(target=worker)
t.start()
import time
time.sleep(0.1)  # даём потоку запуститься
# Получаем стек потока t
frames = sys._current_frames()
if t.ident in frames:
    stack = traceback.format_stack(frames[t.ident])
    print(''.join(stack))
  File "<stdin>", line 8, in worker
    import time
  File "<stdin>", line 9, in worker
    time.sleep(10)

Ручное построение traceback из объектов фреймов

Иногда требуется создать собственный traceback, например, для эмуляции исключения при тестировании. Можно использовать traceback.FrameSummary и traceback.StackSummary.

Пример
import traceback

# Создаём один фрейм вручную
frame_summary = traceback.FrameSummary('myscript.py', 42, 'calculate')
stack = traceback.StackSummary.from_list([frame_summary])
# Форматируем как строку
result = stack.format()
print(''.join(result))
  File "myscript.py", line 42, in calculate

Логирование traceback с кастомным форматтером

Можно настроить обработчик логов так, чтобы он добавлял traceback только определённого уровня, используя exc_info в сообщении.

Пример
import logging, traceback

logger = logging.getLogger('app')
handler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.WARNING)

try:
    1/0
except ZeroDivisionError:
    # exc_info=True добавит traceback к сообщению
    logger.warning('Обнаружено деление на ноль', exc_info=True)
2025-03-21 12:00:00,000 - WARNING - Обнаружено деление на ноль
Traceback (most recent call last):
  File "<stdin>", line 11, in <module>
    1/0
ZeroDivisionError: division by zero

Трассировка стека в Python - comments

En
Python traceback (python)