Enhance monitoring configuration by adding status update interval and alert delays for CPU, RAM, and disk metrics. Update Makefile to include dependency checks for testing, and modify requirements to include requests library. Refactor message sender and metrics collector for improved logging and alert handling.

This commit is contained in:
2025-09-04 00:45:06 +03:00
parent 18d6f3d441
commit 567e5b3aa3
14 changed files with 1337 additions and 184 deletions

View File

@@ -18,6 +18,9 @@ class MessageSender:
self.group_for_logs = os.getenv('GROUP_MONITORING_FOR_LOGS')
self.important_logs = os.getenv('IMPORTANT_MONITORING_LOGS')
# Интервал отправки статуса в минутах (по умолчанию 2 минуты)
self.status_update_interval_minutes = int(os.getenv('STATUS_UPDATE_INTERVAL_MINUTES', 2))
# Создаем экземпляр сборщика метрик
self.metrics_collector = MetricsCollector()
@@ -30,6 +33,8 @@ class MessageSender:
logger.warning("GROUP_MONITORING_FOR_LOGS не установлен в переменных окружения")
if not self.important_logs:
logger.warning("IMPORTANT_MONITORING_LOGS не установлен в переменных окружения")
logger.info(f"Интервал отправки статуса установлен: {self.status_update_interval_minutes} минут")
async def send_telegram_message(self, chat_id: str, message: str) -> bool:
"""Отправка сообщения в Telegram через прямое обращение к API"""
@@ -60,18 +65,29 @@ class MessageSender:
return False
def should_send_status(self) -> bool:
"""Проверка, нужно ли отправить статус (каждые 4 часа в 00 минут)"""
"""Проверка, нужно ли отправить статус (каждые N минут)"""
now = datetime.now()
# Проверяем, что сейчас 00 минут часа и час кратен 4 (0, 4, 8, 12, 16, 20)
if now.minute == 0 and now.hour % 4 == 0:
# Проверяем, не отправляли ли мы уже статус в этот час
if (self.last_status_time is None or
self.last_status_time.hour != now.hour or
self.last_status_time.day != now.day):
self.last_status_time = now
return True
# Логируем для диагностики
import logging
logger = logging.getLogger(__name__)
if self.last_status_time is None:
logger.info(f"should_send_status: last_status_time is None, отправляем статус")
self.last_status_time = now
return True
# Вычисляем разницу в минутах
time_diff_minutes = (now - self.last_status_time).total_seconds() / 60
logger.info(f"should_send_status: прошло {time_diff_minutes:.1f} минут с последней отправки, нужно {self.status_update_interval_minutes} минут")
# Проверяем, что прошло N минут с последней отправки
if time_diff_minutes >= self.status_update_interval_minutes:
logger.info(f"should_send_status: отправляем статус (прошло {time_diff_minutes:.1f} минут)")
self.last_status_time = now
return True
logger.info(f"should_send_status: статус не отправляем (прошло {time_diff_minutes:.1f} минут)")
return False
def should_send_startup_status(self) -> bool:
@@ -87,23 +103,73 @@ class MessageSender:
else:
return "🚨"
def _get_cpu_emoji(self, cpu_percent: float) -> str:
"""Получение эмодзи для CPU"""
if cpu_percent < 50:
return "🟢"
elif cpu_percent < 80:
return "⚠️"
else:
return "🚨"
def _get_memory_emoji(self, memory_percent: float) -> str:
"""Получение эмодзи для памяти (RAM/Swap)"""
if memory_percent < 60:
return "🟢"
elif memory_percent < 85:
return "⚠️"
else:
return "🚨"
def _get_load_average_emoji(self, load_avg: float, cpu_count: int) -> str:
"""Получение эмодзи для Load Average"""
# Load Average считается нормальным если < 1.0 на ядро
# Критичным если > 2.0 на ядро
load_per_core = load_avg / cpu_count
if load_per_core < 1.0:
return "🟢"
elif load_per_core < 2.0:
return "⚠️"
else:
return "🚨"
def _get_io_wait_emoji(self, io_wait_percent: float) -> str:
"""Получение эмодзи для IO Wait"""
# IO Wait считается нормальным если < 5%
# Критичным если > 20%
if io_wait_percent < 5:
return "🟢"
elif io_wait_percent < 20:
return "⚠️"
else:
return "🚨"
def get_status_message(self, system_info: Dict) -> str:
"""Формирование сообщения со статусом сервера"""
try:
voice_bot_status, voice_bot_uptime = self.metrics_collector.check_process_status('voice_bot')
helper_bot_status, helper_bot_uptime = self.metrics_collector.check_process_status('helper_bot')
# Получаем эмодзи для дискового пространства
# Получаем эмодзи для всех метрик
cpu_emoji = self._get_cpu_emoji(system_info['cpu_percent'])
ram_emoji = self._get_memory_emoji(system_info['ram_percent'])
swap_emoji = self._get_memory_emoji(system_info['swap_percent'])
la_emoji = self._get_load_average_emoji(system_info['load_avg_1m'], system_info['cpu_count'])
io_wait_emoji = self._get_io_wait_emoji(system_info['io_wait_percent'])
disk_emoji = self._get_disk_space_emoji(system_info['disk_percent'])
message = f"""🖥 **Статус Сервера** | <code>{system_info['current_time']}</code>
# Определяем уровень мониторинга
monitoring_level = system_info.get('monitoring_level', 'unknown')
level_emoji = "🖥️" if monitoring_level == 'host' else "📦"
level_text = "Хост" if monitoring_level == 'host' else "Контейнер"
message = f"""{level_emoji} **Статус {level_text}** | <code>{system_info['current_time']}</code>
---------------------------------
**📊 Общая нагрузка:**
CPU: <b>{system_info['cpu_percent']}%</b> | LA: <b>{system_info['load_avg_1m']} / {system_info['cpu_count']}</b> | IO Wait: <b>{system_info['disk_percent']}%</b>
CPU: <b>{system_info['cpu_percent']}%</b> {cpu_emoji} | LA: <b>{system_info['load_avg_1m']} / {system_info['cpu_count']}</b> {la_emoji} | IO Wait: <b>{system_info['io_wait_percent']}%</b> {io_wait_emoji}
**💾 Память:**
RAM: <b>{system_info['ram_used']}/{system_info['ram_total']} GB</b> ({system_info['ram_percent']}%)
Swap: <b>{system_info['swap_used']}/{system_info['swap_total']} GB</b> ({system_info['swap_percent']}%)
RAM: <b>{system_info['ram_used']}/{system_info['ram_total']} GB</b> ({system_info['ram_percent']}%) {ram_emoji}
Swap: <b>{system_info['swap_used']}/{system_info['swap_total']} GB</b> ({system_info['swap_percent']}%) {swap_emoji}
**🗂️ Дисковое пространство:**
Диск (/): <b>{system_info['disk_used']}/{system_info['disk_total']} GB</b> ({system_info['disk_percent']}%) {disk_emoji}
@@ -113,10 +179,10 @@ Read: <b>{system_info['disk_read_speed']}</b> | Write: <b>{system_info['disk_wri
Диск загружен: <b>{system_info['disk_io_percent']}%</b>
**🤖 Процессы:**
{voice_bot_status} voice-bot - {voice_bot_uptime}
{helper_bot_status} helper-bot - {helper_bot_uptime}
---------------------------------
⏰ Uptime сервера: {system_info['system_uptime']}"""
⏰ Uptime сервера: {system_info['system_uptime']}
🔍 Уровень мониторинга: {level_text} ({monitoring_level})"""
return message
@@ -127,6 +193,17 @@ Read: <b>{system_info['disk_read_speed']}</b> | Write: <b>{system_info['disk_wri
def get_alert_message(self, metric_name: str, current_value: float, details: str) -> str:
"""Формирование сообщения об алерте"""
try:
# Получаем информацию о задержке для данного метрика
delay_info = ""
if hasattr(self.metrics_collector, 'alert_delays'):
metric_type = metric_name.lower().replace('использование ', '').replace('заполнение диска (/)', 'disk')
if 'cpu' in metric_type:
delay_info = f"⏱️ Задержка срабатывания: {self.metrics_collector.alert_delays['cpu']} сек"
elif 'память' in metric_type or 'ram' in metric_type:
delay_info = f"⏱️ Задержка срабатывания: {self.metrics_collector.alert_delays['ram']} сек"
elif 'диск' in metric_type or 'disk' in metric_type:
delay_info = f"⏱️ Задержка срабатывания: {self.metrics_collector.alert_delays['disk']} сек"
message = f"""🚨 **ALERT: Высокая нагрузка на сервере!**
---------------------------------
**Показатель:** {metric_name}
@@ -136,6 +213,8 @@ Read: <b>{system_info['disk_read_speed']}</b> | Write: <b>{system_info['disk_wri
**Детали:**
{details}
{delay_info}
**Сервер:** `{self.metrics_collector.os_type.upper()}`
**Время:** `{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}`
---------------------------------"""