""" Обработчики команд для мониторинга rate limiting """ from aiogram import F, Router, types from aiogram.filters import Command, MagicData from aiogram.fsm.context import FSMContext from aiogram.types import FSInputFile from helper_bot.filters.main import ChatTypeFilter from helper_bot.middlewares.dependencies_middleware import \ DependenciesMiddleware # Local imports - metrics from helper_bot.utils.metrics import track_errors, track_time from helper_bot.utils.rate_limit_metrics import ( get_rate_limit_metrics_summary, update_rate_limit_gauges) from helper_bot.utils.rate_limit_monitor import (get_rate_limit_summary, rate_limit_monitor) from logs.custom_logger import logger class RateLimitHandlers: def __init__(self, db, settings): self.db = db.get_db() if hasattr(db, "get_db") else db self.settings = settings self.router = Router() self._setup_handlers() self._setup_middleware() def _setup_middleware(self): self.router.message.middleware(DependenciesMiddleware()) def _setup_handlers(self): # Команда для просмотра статистики rate limiting self.router.message.register( self.rate_limit_stats_handler, ChatTypeFilter(chat_type=["private"]), Command("ratelimit_stats"), ) # Команда для сброса статистики rate limiting self.router.message.register( self.reset_rate_limit_stats_handler, ChatTypeFilter(chat_type=["private"]), Command("reset_ratelimit_stats"), ) # Команда для просмотра ошибок rate limiting self.router.message.register( self.rate_limit_errors_handler, ChatTypeFilter(chat_type=["private"]), Command("ratelimit_errors"), ) # Команда для просмотра Prometheus метрик self.router.message.register( self.rate_limit_prometheus_handler, ChatTypeFilter(chat_type=["private"]), Command("ratelimit_prometheus"), ) @track_time("rate_limit_stats_handler", "rate_limit_handlers") @track_errors("rate_limit_handlers", "rate_limit_stats_handler") async def rate_limit_stats_handler( self, message: types.Message, state: FSMContext, bot_db: MagicData("bot_db"), settings: MagicData("settings"), ): """Показывает статистику rate limiting""" try: # Проверяем права администратора if not await bot_db.is_admin(message.from_user.id): await message.answer("У вас нет прав для выполнения этой команды.") return # Получаем сводку summary = get_rate_limit_summary() global_stats = rate_limit_monitor.get_global_stats() # Формируем сообщение со статистикой stats_text = ( f"📊 Статистика Rate Limiting\n\n" f"🔢 Общая статистика:\n" f"• Всего запросов: {summary['total_requests']}\n" f"• Процент успеха: {summary['success_rate']:.1%}\n" f"• Процент ошибок: {summary['error_rate']:.1%}\n" f"• Запросов в минуту: {summary['requests_per_minute']:.1f}\n" f"• Среднее время ожидания: {summary['average_wait_time']:.2f}с\n" f"• Активных чатов: {summary['active_chats']}\n" f"• Ошибок за час: {summary['recent_errors_count']}\n\n" ) # Добавляем детальную статистику stats_text += f"🔍 Детальная статистика:\n" stats_text += f"• Успешных запросов: {global_stats.successful_requests}\n" stats_text += f"• Неудачных запросов: {global_stats.failed_requests}\n" stats_text += f"• RetryAfter ошибок: {global_stats.retry_after_errors}\n" stats_text += f"• Других ошибок: {global_stats.other_errors}\n" stats_text += ( f"• Общее время ожидания: {global_stats.total_wait_time:.2f}с\n\n" ) # Добавляем топ чатов по запросам top_chats = rate_limit_monitor.get_top_chats_by_requests(5) if top_chats: stats_text += f"📈 Топ-5 чатов по запросам:\n" for i, (chat_id, chat_stats) in enumerate(top_chats, 1): stats_text += f"{i}. Chat {chat_id}: {chat_stats.total_requests} запросов ({chat_stats.success_rate:.1%} успех)\n" stats_text += "\n" # Добавляем чаты с высоким процентом ошибок high_error_chats = rate_limit_monitor.get_chats_with_high_error_rate(0.1) if high_error_chats: stats_text += f"⚠️ Чаты с высоким процентом ошибок (>10%):\n" for chat_id, chat_stats in high_error_chats[:3]: stats_text += f"• Chat {chat_id}: {chat_stats.error_rate:.1%} ошибок ({chat_stats.failed_requests}/{chat_stats.total_requests})\n" await message.answer(stats_text, parse_mode="HTML") except Exception as e: logger.error(f"Ошибка при получении статистики rate limiting: {e}") await message.answer("Произошла ошибка при получении статистики.") @track_time("reset_rate_limit_stats_handler", "rate_limit_handlers") @track_errors("rate_limit_handlers", "reset_rate_limit_stats_handler") async def reset_rate_limit_stats_handler( self, message: types.Message, state: FSMContext, bot_db: MagicData("bot_db"), settings: MagicData("settings"), ): """Сбрасывает статистику rate limiting""" try: # Проверяем права администратора if not await bot_db.is_admin(message.from_user.id): await message.answer("У вас нет прав для выполнения этой команды.") return # Сбрасываем статистику rate_limit_monitor.reset_stats() await message.answer("✅ Статистика rate limiting сброшена.") except Exception as e: logger.error(f"Ошибка при сбросе статистики rate limiting: {e}") await message.answer("Произошла ошибка при сбросе статистики.") @track_time("rate_limit_errors_handler", "rate_limit_handlers") @track_errors("rate_limit_handlers", "rate_limit_errors_handler") async def rate_limit_errors_handler( self, message: types.Message, state: FSMContext, bot_db: MagicData("bot_db"), settings: MagicData("settings"), ): """Показывает недавние ошибки rate limiting""" try: # Проверяем права администратора if not await bot_db.is_admin(message.from_user.id): await message.answer("У вас нет прав для выполнения этой команды.") return # Получаем ошибки за последний час recent_errors = rate_limit_monitor.get_recent_errors(60) error_summary = rate_limit_monitor.get_error_summary(60) if not recent_errors: await message.answer( "✅ Ошибок rate limiting за последний час не было." ) return # Формируем сообщение с ошибками errors_text = f"🚨 Ошибки Rate Limiting (последний час)\n\n" errors_text += f"📊 Сводка ошибок:\n" for error_type, count in error_summary.items(): errors_text += f"• {error_type}: {count}\n" errors_text += f"\nВсего ошибок: {len(recent_errors)}\n\n" # Показываем последние 10 ошибок errors_text += f"🔍 Последние ошибки:\n" for i, error in enumerate(recent_errors[-10:], 1): from datetime import datetime timestamp = datetime.fromtimestamp(error["timestamp"]).strftime( "%H:%M:%S" ) errors_text += f"{i}. {timestamp} - Chat {error['chat_id']} - {error['error_type']}\n" # Если сообщение слишком длинное, разбиваем на части if len(errors_text) > 4000: # Отправляем сводку summary_text = f"🚨 Ошибки Rate Limiting (последний час)\n\n" summary_text += f"📊 Сводка ошибок:\n" for error_type, count in error_summary.items(): summary_text += f"• {error_type}: {count}\n" summary_text += f"\nВсего ошибок: {len(recent_errors)}" await message.answer(summary_text, parse_mode="HTML") # Отправляем детали отдельным сообщением details_text = f"🔍 Последние ошибки:\n" for i, error in enumerate(recent_errors[-10:], 1): from datetime import datetime timestamp = datetime.fromtimestamp(error["timestamp"]).strftime( "%H:%M:%S" ) details_text += f"{i}. {timestamp} - Chat {error['chat_id']} - {error['error_type']}\n" await message.answer(details_text, parse_mode="HTML") else: await message.answer(errors_text, parse_mode="HTML") except Exception as e: logger.error(f"Ошибка при получении ошибок rate limiting: {e}") await message.answer( "Произошла ошибка при получении информации об ошибках." ) @track_time("rate_limit_prometheus_handler", "rate_limit_handlers") @track_errors("rate_limit_handlers", "rate_limit_prometheus_handler") async def rate_limit_prometheus_handler( self, message: types.Message, state: FSMContext, bot_db: MagicData("bot_db"), settings: MagicData("settings"), ): """Показывает Prometheus метрики rate limiting""" try: # Проверяем права администратора if not await bot_db.is_admin(message.from_user.id): await message.answer("У вас нет прав для выполнения этой команды.") return # Обновляем gauge метрики update_rate_limit_gauges() # Получаем сводку метрик metrics_summary = get_rate_limit_metrics_summary() # Формируем сообщение с метриками metrics_text = ( f"📊 Prometheus метрики Rate Limiting\n\n" f"🔢 Основные метрики:\n" f"• rate_limit_requests_total: {metrics_summary['total_requests']}\n" f"• rate_limit_success_rate: {metrics_summary['success_rate']:.3f}\n" f"• rate_limit_error_rate: {metrics_summary['error_rate']:.3f}\n" f"• rate_limit_requests_per_minute: {metrics_summary['requests_per_minute']:.1f}\n" f"• rate_limit_avg_wait_time: {metrics_summary['average_wait_time']:.3f}s\n" f"• rate_limit_active_chats: {metrics_summary['active_chats']}\n\n" ) # Добавляем детальные метрики metrics_text += f"🔍 Детальные метрики:\n" metrics_text += ( f"• Успешных запросов: {metrics_summary['successful_requests']}\n" ) metrics_text += ( f"• Неудачных запросов: {metrics_summary['failed_requests']}\n" ) metrics_text += ( f"• RetryAfter ошибок: {metrics_summary['retry_after_errors']}\n" ) metrics_text += f"• Других ошибок: {metrics_summary['other_errors']}\n" metrics_text += ( f"• Общее время ожидания: {metrics_summary['total_wait_time']:.2f}s\n\n" ) # Добавляем информацию о доступных метриках metrics_text += f"📈 Доступные Prometheus метрики:\n" metrics_text += f"• rate_limit_requests_total - общее количество запросов\n" metrics_text += f"• rate_limit_errors_total - количество ошибок по типам\n" metrics_text += f"• rate_limit_wait_duration_seconds - время ожидания\n" metrics_text += ( f"• rate_limit_request_interval_seconds - интервалы между запросами\n" ) metrics_text += f"• rate_limit_active_chats - количество активных чатов\n" metrics_text += f"• rate_limit_success_rate - процент успеха по чатам\n" metrics_text += f"• rate_limit_requests_per_minute - запросов в минуту\n" metrics_text += f"• rate_limit_total_requests - общее количество запросов\n" metrics_text += f"• rate_limit_total_errors - количество ошибок\n" metrics_text += f"• rate_limit_avg_wait_time - среднее время ожидания\n" await message.answer(metrics_text, parse_mode="HTML") except Exception as e: logger.error(f"Ошибка при получении Prometheus метрик: {e}") await message.answer("Произошла ошибка при получении метрик.")