- Обновлен Dockerfile для использования Alpine вместо Slim, улучшая размер образа. - Удален устаревший RAGService и добавлен RagApiClient для работы с внешним RAG API. - Обновлены переменные окружения в env.example для настройки нового RAG API. - Обновлен ScoringManager для интеграции с RagApiClient. - Упрощена структура проекта, удалены ненужные файлы и зависимости, связанные с векторным хранилищем. - Обновлены обработчики и функции для работы с новым API, включая получение статистики и обработку ошибок.
208 lines
8.6 KiB
Markdown
208 lines
8.6 KiB
Markdown
---
|
||
description: "Обработка ошибок, исключения и логирование"
|
||
alwaysApply: false
|
||
---
|
||
|
||
# Обработка ошибок и исключений
|
||
|
||
## Иерархия исключений
|
||
|
||
Каждый модуль должен иметь свой файл `exceptions.py` с иерархией исключений:
|
||
|
||
```python
|
||
# Базовое исключение модуля
|
||
class ModuleError(Exception):
|
||
"""Базовое исключение для модуля"""
|
||
pass
|
||
|
||
# Специализированные исключения
|
||
class UserNotFoundError(ModuleError):
|
||
"""Исключение при отсутствии пользователя"""
|
||
pass
|
||
|
||
class InvalidInputError(ModuleError):
|
||
"""Исключение при некорректном вводе данных"""
|
||
pass
|
||
```
|
||
|
||
## Обработка в Handlers
|
||
|
||
### Паттерн try-except
|
||
|
||
```python
|
||
@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` для автоматической обработки:
|
||
|
||
```python
|
||
from .decorators import error_handler
|
||
|
||
@error_handler
|
||
@router.message(...)
|
||
async def handler(...):
|
||
# Код handler
|
||
# Ошибки автоматически логируются и отправляются в important_logs
|
||
...
|
||
```
|
||
|
||
## Обработка в Services
|
||
|
||
```python
|
||
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`:
|
||
|
||
```python
|
||
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 и сервисами используйте следующий паттерн:
|
||
|
||
```python
|
||
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` автоматически отслеживает ошибки:
|
||
|
||
```python
|
||
@track_errors("module_name", "method_name")
|
||
async def some_method():
|
||
# Ошибки автоматически записываются в метрики
|
||
...
|
||
```
|
||
|
||
## Централизованная обработка
|
||
|
||
### В admin handlers
|
||
|
||
Используется функция `handle_admin_error()`:
|
||
|
||
```python
|
||
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
|