Files
voidea/template/docs/08-error-handling.md
T

107 lines
3.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Обработка ошибок
## Матрица ошибок по слоям
| Слой | Что делаем | Пример |
|------|-----------|--------|
| **API** | HTTPException с detail и status_code | `raise HTTPException(404, detail="Not found")` |
| **Services** | Бизнес-исключения без HTTP-статусов | `raise IdeaNotFoundError(idea_id)` |
| **Integrations** | try/except с fallback | `return AIResult(success=False, error=...)` |
| **Data/DB** | Ошибки не всплывают выше | Ловим в сервисе |
---
## Иерархия исключений
```python
# app/core/exceptions.py
class AppError(Exception):
"""Базовое исключение приложения."""
def __init__(self, message: str, details: dict | None = None):
self.message = message
self.details = details or {}
class NotFoundError(AppError):
"""Ресурс не найден."""
def __init__(self, resource: str, resource_id: str):
super().__init__(f"{resource} not found: {resource_id}", {"resource": resource, "id": resource_id})
class ValidationError(AppError):
"""Ошибка валидации."""
def __init__(self, field: str, message: str):
super().__init__(message, {"field": field})
class AuthError(AppError):
"""Ошибка аутентификации."""
def __init__(self, message: str = "Authentication failed"):
super().__init__(message)
class ForbiddenError(AppError):
"""Нет прав."""
def __init__(self, message: str = "Access denied"):
super().__init__(message)
```
---
## Fallback Pattern (для внешних вызовов)
```python
# Паттерн для всех вызовов внешних API
async def call_with_fallback(provider: AIProvider, prompt: str) -> AIResult:
max_retries = 2
last_error = None
for attempt in range(max_retries + 1):
try:
result = await asyncio.wait_for(
provider.analyze(prompt),
timeout=10.0
)
if result.success:
return result
last_error = result
except asyncio.TimeoutError:
last_error = AIResult(success=False, error="Timeout")
except Exception as e:
last_error = AIResult(success=False, error=str(e))
if attempt < max_retries:
await asyncio.sleep(2 if attempt == 0 else 5)
return last_error
```
---
## Логирование ошибок
| Уровень | Когда | Пример |
|---------|-------|--------|
| DEBUG | Входящие параметры | `Request params: id=123` |
| INFO | Успешная операция | `User created: id=456` |
| WARNING | Timeout, retry | `Yandex GPT timeout, retry 1/2` |
| ERROR | Ошибка внешнего API | `GigaChat 500: Internal error` |
| CRITICAL | Исчерпаны все retry | `All AI providers failed for idea 789` |
---
## Graceful Degradation
Когда внешний сервис недоступен:
1. **DB недоступна** → 503 Service Unavailable
2. **Redis недоступен** → работаем без кэша (log WARNING)
3. **AI провайдер недоступен** → возвращаем fallback результат
4. **Celery недоступен** → выполняем задачу синхронно
---
## [ASK] Вопросы по обработке ошибок
- Нужны ли пользовательские исключения для всех бизнес-сценариев?
- Нужен ли sentry или аналогичный мониторинг ошибок?
- Как обрабатывать ошибки валидации на фронтенде?