Traceback в Python: извлечение и форматирование стека вызовов

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

Понимание и использование traceback для отладки

Модуль traceback предоставляет инструменты для работы с трассировкой стека вызовов при возникновении исключения. Основной способ получить полный отчет об ошибке - перехватить исключение и вызвать traceback.print_exc(). Этот метод печатает трассировку в стандартный поток ошибок (sys.stderr).

import traceback

try:
    result = 1 / 0
except ZeroDivisionError:
    traceback.print_exc()

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

Вывод будет содержать стек вызовов, файл, номер строки и само исключение. Для получения строки без вывода на консоль используют traceback.format_exc(). Это удобно для записи в лог или отправки по сети.

import traceback

try:
    with open('missing.txt') as f:
        data = f.read()
except FileNotFoundError:
    err_str = traceback.format_exc()
    # err_str содержит полную трассировку как строку
    print(err_str[:100])  # первые 100 символов

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

Типичная проблема: если исключение не перехвачено, traceback создается автоматически и программа завершается. Для перехвата и контроля следует оборачивать опасный код в try/except.

Как получить трассировку в виде структурированных данных?

Функция traceback.extract_tb() извлекает из объекта трассировки (tb) список записей FrameSummary. Каждая запись содержит имя файла, номер строки, имя функции и исходный код строки (если доступен).

import sys, traceback

try:
    x = [1, 2][5]
except IndexError:
    tb = sys.exc_info()[2]
    summary = traceback.extract_tb(tb)
    for frame in summary:
        print(f'{frame.filename}:{frame.lineno} in {frame.name}')
        print(f'  code: {frame.line}')

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

Если sys.exc_info() возвращает None (вне обработчика исключения), traceback.extract_tb() не сработает. Используйте traceback.format_stack() для получения текущего стека.

Как записать трассировку в лог через logging?

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

import logging

logging.basicConfig(level=logging.ERROR)
try:
    d = {'key': 'value'}
    print(d['missing'])
except KeyError:
    logging.exception('Ошибка доступа к ключу словаря')

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

Если logging.exception() вызывается вне блока except, он не содержит информации об исключении. Следует вызывать его только внутри обработчика.

Как кастомизировать формат вывода traceback?

Модуль позволяет контролировать отображение с помощью format_list() и format_stack(). Можно убрать лишние фреймы, изменить порядок или добавить свои аннотации.

import traceback, sys

def custom_traceback():
    stack = traceback.format_stack()[-3:-1]  # последние два фрейма
    print(''.join(stack))

def deep():
    custom_traceback()

def outer():
    deep()

outer()

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

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

Как обрабатывать цепочки исключений (raise from)?

Использование raise ... from ... создает цепочку исключений. Модуль traceback через TracebackException может рекурсивно обрабатывать всю цепочку.

import traceback

try:
    try:
        1/0
    except ZeroDivisionError as e:
        raise RuntimeError('Ошибка вычисления') from e
except RuntimeError:
    tb_exc = traceback.TracebackException(*sys.exc_info())
    print(''.join(tb_exc.format()))

При глубоких цепочках вывод становится объемным. Стоит ограничить глубину через traceback.print_exception(chain=False) или параметр limit.

- Io error python (ошибка ввода-вывода в python)
- ошибка компиляции python (ошибка компиляции (синтаксиса) в python)
- Python traceback (трассировка стека в python)

Ниже представлены расширенные примеры использования traceback в нестандартных сценариях.

Пример 1. Фильтрация фреймов по имени модуля

Пример
import traceback, sys

def filter_frames(tb, exclude_prefixes=['site-packages']):
    """Убираем из трассировки фреймы, путь к которым начинается с одного из префиксов."""
    from traceback import FrameSummary
    frames = traceback.extract_tb(tb)
    filtered = []
    for frame in frames:
        if not any(frame.filename.startswith(prefix) for prefix in exclude_prefixes):
            filtered.append(frame)
    return traceback.format_list(filtered)

try:
    import nonexistent_module
except ImportError:
    tb = sys.exc_info()[2]
    clean_lines = filter_frames(tb)
    print(''.join(clean_lines))
  File "<stdin>", line 11, in filter_frames
    frames = traceback.extract_tb(tb)
  File "<stdin>", line 1, in <module>
    import nonexistent_module
ImportError: No module named 'nonexistent_module'

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

Пример 2. Получение трассировки из другого потока

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

def worker():
    try:
        raise ValueError('Ошибка в потоке')
    except:
        # Сохраняем информацию для передачи
        err_info = traceback.format_exc()
        print(f'Ошибка в рабочем потоке:\n{err_info}')

t = threading.Thread(target=worker)
t.start()
t.join()
Ошибка в рабочем потоке:
Traceback (most recent call last):
  File "<stdin>", line 4, in worker
    raise ValueError('Ошибка в потоке')
ValueError: Ошибка в потоке

Пример 3. Кастомный обработчик с повторным выбросом

Пример
import traceback, sys

class CustomError(Exception):
    pass

def risky():
    try:
        risky2()
    except ValueError:
        # Логируем исходную трассировку и выбрасываем новое исключение
        with open('error.log', 'a') as f:
            traceback.print_exc(file=f)
        raise CustomError('Не удалось выполнить операцию') from None

def risky2():
    raise ValueError('Некорректное значение')

try:
    risky()
except CustomError:
    traceback.print_exc()
Traceback (most recent call last):
  File "<stdin>", line 17, in <module>
    risky()
  File "<stdin>", line 9, in risky
    raise CustomError('Не удалось выполнить операцию') from None
CustomError: Не удалось выполнить операцию

Здесь исходный ValueError записан в файл, а пользователю показано только новое исключение с чистым стеком.

Трассировка ошибок в Python - comments

En
Python traceback using (python)