--- 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