- Обновлен Dockerfile для использования Alpine вместо Slim, улучшая размер образа. - Удален устаревший RAGService и добавлен RagApiClient для работы с внешним RAG API. - Обновлены переменные окружения в env.example для настройки нового RAG API. - Обновлен ScoringManager для интеграции с RagApiClient. - Упрощена структура проекта, удалены ненужные файлы и зависимости, связанные с векторным хранилищем. - Обновлены обработчики и функции для работы с новым API, включая получение статистики и обработку ошибок.
8.6 KiB
8.6 KiB
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
- Всегда логируйте ошибки перед пробросом или обработкой
- Используйте специфичные исключения вместо общих
Exception - Пробрасывайте исключения из сервисов в handlers для обработки
- Не глотайте исключения без логирования
- Используйте
exc_info=Trueдля логирования traceback критических ошибок - Обрабатывайте ошибки на правильном уровне: бизнес-логика в сервисах, пользовательские сообщения в handlers