Files
telegram-helper-bot/.cursor/rules/error-handling.md
Andrey feee7f010c refactor: обновление системы ML-скоринга и переход на RAG API
- Обновлен Dockerfile для использования Alpine вместо Slim, улучшая размер образа.
- Удален устаревший RAGService и добавлен RagApiClient для работы с внешним RAG API.
- Обновлены переменные окружения в env.example для настройки нового RAG API.
- Обновлен ScoringManager для интеграции с RagApiClient.
- Упрощена структура проекта, удалены ненужные файлы и зависимости, связанные с векторным хранилищем.
- Обновлены обработчики и функции для работы с новым API, включая получение статистики и обработку ошибок.
2026-01-26 22:03:15 +03:00

8.6 KiB
Raw Blame History

description, alwaysApply
description alwaysApply
Обработка ошибок, исключения и логирование false

Обработка ошибок и исключений

Иерархия исключений

Каждый модуль должен иметь свой файл exceptions.py с иерархией исключений:

# Базовое исключение модуля
class ModuleError(Exception):
    """Базовое исключение для модуля"""
    pass

# Специализированные исключения
class UserNotFoundError(ModuleError):
    """Исключение при отсутствии пользователя"""
    pass

class InvalidInputError(ModuleError):
    """Исключение при некорректном вводе данных"""
    pass

Обработка в Handlers

Паттерн try-except

@router.message(...)
async def handler(message: types.Message, state: FSMContext, **kwargs):
    try:
        # Основная логика
        service = SomeService(bot_db, settings)
        result = await service.do_something()
        await message.answer("Успех")
    except UserNotFoundError as e:
        # Обработка специфичной ошибки
        await message.answer(f"Пользователь не найден: {str(e)}")
        logger.warning(f"Пользователь не найден: {e}")
    except InvalidInputError as e:
        # Обработка ошибки валидации
        await message.answer(f"Некорректный ввод: {str(e)}")
        logger.warning(f"Некорректный ввод: {e}")
    except Exception as e:
        # Обработка неожиданных ошибок
        await handle_error(message, e, state)
        logger.error(f"Неожиданная ошибка в handler: {e}", exc_info=True)

Декоратор error_handler

Некоторые модули используют декоратор @error_handler для автоматической обработки:

from .decorators import error_handler

@error_handler
@router.message(...)
async def handler(...):
    # Код handler
    # Ошибки автоматически логируются и отправляются в important_logs
    ...

Обработка в Services

class SomeService:
    async def do_something(self):
        try:
            # Бизнес-логика
            data = await self.bot_db.get_data()
            if not data:
                raise UserNotFoundError("Пользователь не найден")
            return self._process(data)
        except UserNotFoundError:
            # Пробрасываем специфичные исключения дальше
            raise
        except Exception as e:
            # Логируем и пробрасываем неожиданные ошибки
            logger.error(f"Ошибка в сервисе: {e}", exc_info=True)
            raise

Логирование

Использование logger

Всегда используйте logs.custom_logger.logger:

from logs.custom_logger import logger

# Информационные сообщения
logger.info(f"Пользователь {user_id} выполнил действие")

# Предупреждения
logger.warning(f"Попытка доступа к несуществующему ресурсу: {resource_id}")

# Ошибки
logger.error(f"Ошибка при выполнении операции: {e}")

# Ошибки с traceback
logger.error(f"Критическая ошибка: {e}", exc_info=True)

Уровни логирования

  • logger.debug() - отладочная информация (детали выполнения, промежуточные значения, HTTP запросы(не используется в проекте))
  • logger.info() - информационные сообщения о работе (успешные операции, важные события)
  • logger.warning() - предупреждения о потенциальных проблемах (некритичные ошибки, таймауты)
  • logger.error() - ошибки, требующие внимания (исключения, сбои)
  • logger.critical() - критические ошибки

Паттерн логирования в сервисах

При работе с внешними API и сервисами используйте следующий паттерн:

from logs.custom_logger import logger

class ApiClient:
    async def calculate_score(self, text: str) -> Score:
        # Логируем начало операции (debug)
        logger.debug(f"ApiClient: Отправка запроса на расчет скора (text_preview='{text[:50]}')")
        
        try:
            response = await self._client.post(url, json=data)
            
            # Логируем статус ответа (debug)
            logger.debug(f"ApiClient: Получен ответ (status={response.status_code})")
            
            # Обрабатываем ответ
            if response.status_code == 200:
                result = response.json()
                # Логируем успешный результат (info)
                logger.info(f"ApiClient: Скор успешно получен (score={result['score']:.4f})")
                return result
            else:
                # Логируем ошибку (error)
                logger.error(f"ApiClient: Ошибка API (status={response.status_code})")
                raise ApiError(f"Ошибка API: {response.status_code}")
                
        except httpx.TimeoutException:
            # Логируем таймаут (error)
            logger.error(f"ApiClient: Таймаут запроса (>{timeout}с)")
            raise
        except httpx.RequestError as e:
            # Логируем ошибку подключения (error)
            logger.error(f"ApiClient: Ошибка подключения: {e}")
            raise
        except Exception as e:
            # Логируем неожиданные ошибки (error)
            logger.error(f"ApiClient: Неожиданная ошибка: {e}", exc_info=True)
            raise

Принципы:

  • logger.debug() - для деталей выполнения (URL, параметры запроса, статус ответа)
  • logger.info() - для успешных операций с важными результатами
  • logger.warning() - для некритичных проблем (валидация, таймауты в неважных операциях)
  • logger.error() - для всех ошибок перед пробросом исключения
  • Всегда логируйте ошибки перед raise
  • Используйте exc_info=True для критических ошибок

Метрики ошибок

Декоратор @track_errors автоматически отслеживает ошибки:

@track_errors("module_name", "method_name")
async def some_method():
    # Ошибки автоматически записываются в метрики
    ...

Централизованная обработка

В admin handlers

Используется функция handle_admin_error():

from helper_bot.handlers.admin.utils import handle_admin_error

try:
    # Код
except Exception as e:
    await handle_admin_error(message, e, state, "context_name")

В других модулях

Создавайте аналогичные утилиты для централизованной обработки ошибок модуля.

Best Practices

  1. Всегда логируйте ошибки перед пробросом или обработкой
  2. Используйте специфичные исключения вместо общих Exception
  3. Пробрасывайте исключения из сервисов в handlers для обработки
  4. Не глотайте исключения без логирования
  5. Используйте exc_info=True для логирования traceback критических ошибок
  6. Обрабатывайте ошибки на правильном уровне: бизнес-логика в сервисах, пользовательские сообщения в handlers