ses-monitor/ses_monitor/log_client.py
Maksim Totmin 7394b14b52 Initial commit: ses-monitor watchdog for SES
Универсальный watchdog-мониторинг Service Engine Server (SES):
- Liveness-проверки (API, лицензия, роботы)
- Диалоговые сценарии из YAML с агрегацией в 6 фиксированных Zabbix-метрик
- Доставка: zabbix_sender (active push) или UserParameter (пассивный опрос)
- Поддержка Python 3.7+, Zabbix 5.0
2026-06-19 20:26:26 +07:00

97 lines
3.5 KiB
Python

"""
Чтение логов SES через POST /ses/log.
Позволяет получить логи за указанную дату для анализа ошибок.
Функциональность пока отключена по умолчанию (config.yaml: monitoring.log_enabled),
так как запрос логов может быть ресурсоёмким.
"""
from __future__ import annotations
import logging
from dataclasses import dataclass
from datetime import datetime
from typing import Any, Dict, List, Optional
from ses_monitor.session_client import SESClient
log = logging.getLogger("ses_monitor.log_client")
@dataclass
class LogCheckResult:
"""Результат проверки логов."""
date: str = ""
fetch_error: str = ""
lines_count: int = 0
# Паттерны для поиска ошибок (расширяются по мере необходимости)
error_patterns: Dict[str, int] = None # type: ignore
def __post_init__(self):
if self.error_patterns is None:
self.error_patterns = {}
class LogChecker:
"""Проверяет логи SES на наличие ошибок.
Ищет паттерны:
- "ERROR" / "CRITICAL" — критические ошибки.
- "HTTPError" / "RequestException" — ошибки внешних API.
- "json.JSONDecodeError" — ошибки парсинга.
- "connection refused" / "timeout" — сетевые проблемы.
"""
ERROR_PATTERNS = [
("error_count", "ERROR"),
("critical_count", "CRITICAL"),
("http_error_count", "HTTPError"),
("request_exception_count", "RequestException"),
("json_error_count", "JSONDecodeError"),
("timeout_count", "timeout"),
("refused_count", "Connection refused"),
]
async def check(self, client: SESClient) -> LogCheckResult:
"""Запрашивает и анализирует логи за сегодняшний день.
Args:
client: HTTP-клиент для SES API.
Returns:
Результат анализа логов.
"""
result = LogCheckResult(date=datetime.now().strftime("%Y-%m-%d"))
try:
log_data = await client.get_logs(result.date)
except Exception as e:
result.fetch_error = str(e)
log.error("Log check: ошибка получения логов: %s", e)
return result
# Если ответ — строка (plain text логов)
if isinstance(log_data, str):
result.lines_count = log_data.count("\n") + 1
for key, pattern in self.ERROR_PATTERNS:
result.error_patterns[key] = log_data.count(pattern)
# Если ответ — словарь (структурированные данные)
elif isinstance(log_data, dict):
if log_data.get("error") == 0 and "data" in log_data:
data = log_data["data"]
if isinstance(data, str):
result.lines_count = data.count("\n") + 1
for key, pattern in self.ERROR_PATTERNS:
result.error_patterns[key] = data.count(pattern)
log.info(
"Log check: %d строк за %s, ошибок: %s",
result.lines_count,
result.date,
{k: v for k, v in result.error_patterns.items() if v > 0},
)
return result