- FastAPI приложение для отправки мониторинговых алертов в мессенджеры - Поддержка Telegram и MAX/VK - Интеграция с Grafana, Zabbix, AlertManager - Автоматическое создание тикетов в Jira - Управление группами мессенджеров через API - Декораторы для авторизации и скрытия эндпоинтов - Подробная документация в папке docs/ Автор: Сергей Антропов Сайт: https://devops.org.ru
317 lines
10 KiB
Python
317 lines
10 KiB
Python
"""
|
||
Централизованное управление метриками Prometheus.
|
||
|
||
Автор: Сергей Антропов
|
||
Сайт: https://devops.org.ru
|
||
"""
|
||
import logging
|
||
from typing import Optional
|
||
from prometheus_client import Counter, CollectorRegistry, push_to_gateway
|
||
from functools import lru_cache
|
||
|
||
from app.core.config import settings
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class MetricsManager:
|
||
"""Менеджер метрик Prometheus."""
|
||
|
||
def __init__(self):
|
||
"""Инициализация менеджера метрик."""
|
||
self.registry = CollectorRegistry()
|
||
self._init_metrics()
|
||
|
||
def _init_metrics(self) -> None:
|
||
"""Инициализация всех метрик."""
|
||
# API эндпоинты
|
||
self.api_endpoint_count = Counter(
|
||
'tg_monitoring_gateway_api_endpoint_total',
|
||
'Общее количество обращений к эндпоинтам API',
|
||
labelnames=['endpoint'],
|
||
registry=self.registry
|
||
)
|
||
|
||
# Сообщения по источникам
|
||
self.total_messages = Counter(
|
||
'tg_monitoring_gateway_total_messages',
|
||
'Всего сообщений получено',
|
||
labelnames=['source', 'k8s_cluster', 'chat', 'thread'],
|
||
registry=self.registry
|
||
)
|
||
|
||
self.sent_messages = Counter(
|
||
'tg_monitoring_gateway_sent_messages',
|
||
'Сообщений успешно отправлено',
|
||
labelnames=['source', 'k8s_cluster', 'chat', 'thread'],
|
||
registry=self.registry
|
||
)
|
||
|
||
self.reject_messages = Counter(
|
||
'tg_monitoring_gateway_reject_messages',
|
||
'Сообщений отклонено (стоп-слова)',
|
||
labelnames=['source', 'k8s_cluster', 'chat', 'thread'],
|
||
registry=self.registry
|
||
)
|
||
|
||
self.error_messages = Counter(
|
||
'tg_monitoring_gateway_error_messages',
|
||
'Ошибок отправки сообщений',
|
||
labelnames=['source', 'k8s_cluster', 'chat', 'thread'],
|
||
registry=self.registry
|
||
)
|
||
|
||
self.firing_messages = Counter(
|
||
'tg_monitoring_gateway_firing_messages',
|
||
'Горящих алертов',
|
||
labelnames=['source', 'k8s_cluster', 'chat', 'thread'],
|
||
registry=self.registry
|
||
)
|
||
|
||
self.critical_messages = Counter(
|
||
'tg_monitoring_gateway_critical_messages',
|
||
'Критических алертов',
|
||
labelnames=['source', 'k8s_cluster', 'chat', 'thread'],
|
||
registry=self.registry
|
||
)
|
||
|
||
self.resolved_messages = Counter(
|
||
'tg_monitoring_gateway_resolved_messages',
|
||
'Исправленных алертов',
|
||
labelnames=['source', 'k8s_cluster', 'chat', 'thread'],
|
||
registry=self.registry
|
||
)
|
||
|
||
# Jira метрики
|
||
self.jira_tickets_created = Counter(
|
||
'tg_monitoring_gateway_jira_tickets_created',
|
||
'Jira тикетов создано',
|
||
labelnames=['source', 'k8s_cluster', 'chat', 'thread', 'project'],
|
||
registry=self.registry
|
||
)
|
||
|
||
self.jira_tickets_errors = Counter(
|
||
'tg_monitoring_gateway_jira_tickets_errors',
|
||
'Ошибок создания Jira тикетов',
|
||
labelnames=['source', 'k8s_cluster', 'chat', 'thread'],
|
||
registry=self.registry
|
||
)
|
||
|
||
def increment_api_endpoint(self, endpoint: str) -> None:
|
||
"""
|
||
Увеличить счетчик обращений к эндпоинту API.
|
||
|
||
Args:
|
||
endpoint: Имя эндпоинта.
|
||
"""
|
||
self.api_endpoint_count.labels(endpoint=endpoint).inc()
|
||
self._push_metrics()
|
||
|
||
def increment_total_message(
|
||
self,
|
||
source: str,
|
||
k8s_cluster: Optional[str] = None,
|
||
chat: Optional[str] = None,
|
||
thread: Optional[int] = None
|
||
) -> None:
|
||
"""
|
||
Увеличить счетчик полученных сообщений.
|
||
|
||
Args:
|
||
source: Источник сообщения (grafana, zabbix, alertmanager).
|
||
k8s_cluster: Имя Kubernetes кластера (опционально).
|
||
chat: Имя чата (опционально).
|
||
thread: ID треда (опционально).
|
||
"""
|
||
k8s_cluster = k8s_cluster or ""
|
||
chat = chat or ""
|
||
thread = thread or 0
|
||
self.total_messages.labels(
|
||
source=source,
|
||
k8s_cluster=k8s_cluster,
|
||
chat=chat,
|
||
thread=thread
|
||
).inc()
|
||
self._push_metrics()
|
||
|
||
def increment_sent_message(
|
||
self,
|
||
source: str,
|
||
k8s_cluster: Optional[str] = None,
|
||
chat: Optional[str] = None,
|
||
thread: Optional[int] = None
|
||
) -> None:
|
||
"""Увеличить счетчик отправленных сообщений."""
|
||
k8s_cluster = k8s_cluster or ""
|
||
chat = chat or ""
|
||
thread = thread or 0
|
||
self.sent_messages.labels(
|
||
source=source,
|
||
k8s_cluster=k8s_cluster,
|
||
chat=chat,
|
||
thread=thread
|
||
).inc()
|
||
self._push_metrics()
|
||
|
||
def increment_reject_message(
|
||
self,
|
||
source: str,
|
||
k8s_cluster: Optional[str] = None,
|
||
chat: Optional[str] = None,
|
||
thread: Optional[int] = None
|
||
) -> None:
|
||
"""Увеличить счетчик отклоненных сообщений."""
|
||
k8s_cluster = k8s_cluster or ""
|
||
chat = chat or ""
|
||
thread = thread or 0
|
||
self.reject_messages.labels(
|
||
source=source,
|
||
k8s_cluster=k8s_cluster,
|
||
chat=chat,
|
||
thread=thread
|
||
).inc()
|
||
self._push_metrics()
|
||
|
||
def increment_error_message(
|
||
self,
|
||
source: str,
|
||
k8s_cluster: Optional[str] = None,
|
||
chat: Optional[str] = None,
|
||
thread: Optional[int] = None
|
||
) -> None:
|
||
"""Увеличить счетчик ошибок отправки."""
|
||
k8s_cluster = k8s_cluster or ""
|
||
chat = chat or ""
|
||
thread = thread or 0
|
||
self.error_messages.labels(
|
||
source=source,
|
||
k8s_cluster=k8s_cluster,
|
||
chat=chat,
|
||
thread=thread
|
||
).inc()
|
||
self._push_metrics()
|
||
|
||
def increment_firing_message(
|
||
self,
|
||
source: str,
|
||
k8s_cluster: Optional[str] = None,
|
||
chat: Optional[str] = None,
|
||
thread: Optional[int] = None
|
||
) -> None:
|
||
"""Увеличить счетчик горящих алертов."""
|
||
k8s_cluster = k8s_cluster or ""
|
||
chat = chat or ""
|
||
thread = thread or 0
|
||
self.firing_messages.labels(
|
||
source=source,
|
||
k8s_cluster=k8s_cluster,
|
||
chat=chat,
|
||
thread=thread
|
||
).inc()
|
||
self._push_metrics()
|
||
|
||
def increment_critical_message(
|
||
self,
|
||
source: str,
|
||
k8s_cluster: Optional[str] = None,
|
||
chat: Optional[str] = None,
|
||
thread: Optional[int] = None
|
||
) -> None:
|
||
"""Увеличить счетчик критических алертов."""
|
||
k8s_cluster = k8s_cluster or ""
|
||
chat = chat or ""
|
||
thread = thread or 0
|
||
self.critical_messages.labels(
|
||
source=source,
|
||
k8s_cluster=k8s_cluster,
|
||
chat=chat,
|
||
thread=thread
|
||
).inc()
|
||
self._push_metrics()
|
||
|
||
def increment_resolved_message(
|
||
self,
|
||
source: str,
|
||
k8s_cluster: Optional[str] = None,
|
||
chat: Optional[str] = None,
|
||
thread: Optional[int] = None
|
||
) -> None:
|
||
"""Увеличить счетчик исправленных алертов."""
|
||
k8s_cluster = k8s_cluster or ""
|
||
chat = chat or ""
|
||
thread = thread or 0
|
||
self.resolved_messages.labels(
|
||
source=source,
|
||
k8s_cluster=k8s_cluster,
|
||
chat=chat,
|
||
thread=thread
|
||
).inc()
|
||
self._push_metrics()
|
||
|
||
def increment_jira_ticket_created(
|
||
self,
|
||
source: str,
|
||
project: str,
|
||
k8s_cluster: Optional[str] = None,
|
||
chat: Optional[str] = None,
|
||
thread: Optional[int] = None
|
||
) -> None:
|
||
"""Увеличить счетчик созданных Jira тикетов."""
|
||
k8s_cluster = k8s_cluster or ""
|
||
chat = chat or ""
|
||
thread = thread or 0
|
||
self.jira_tickets_created.labels(
|
||
source=source,
|
||
k8s_cluster=k8s_cluster,
|
||
chat=chat,
|
||
thread=thread,
|
||
project=project
|
||
).inc()
|
||
self._push_metrics()
|
||
|
||
def increment_jira_ticket_error(
|
||
self,
|
||
source: str,
|
||
k8s_cluster: Optional[str] = None,
|
||
chat: Optional[str] = None,
|
||
thread: Optional[int] = None
|
||
) -> None:
|
||
"""Увеличить счетчик ошибок создания Jira тикетов."""
|
||
k8s_cluster = k8s_cluster or ""
|
||
chat = chat or ""
|
||
thread = thread or 0
|
||
self.jira_tickets_errors.labels(
|
||
source=source,
|
||
k8s_cluster=k8s_cluster,
|
||
chat=chat,
|
||
thread=thread
|
||
).inc()
|
||
self._push_metrics()
|
||
|
||
def _push_metrics(self) -> None:
|
||
"""Отправить метрики в Pushgateway."""
|
||
from app.core.config import get_settings
|
||
settings = get_settings()
|
||
|
||
if not settings.pushgateway_url:
|
||
return
|
||
|
||
try:
|
||
push_to_gateway(
|
||
settings.pushgateway_url,
|
||
job=settings.pushgateway_job,
|
||
registry=self.registry
|
||
)
|
||
except Exception as e:
|
||
logger.error(f"Ошибка отправки метрик в Pushgateway: {e}")
|
||
|
||
|
||
# Глобальный экземпляр менеджера метрик
|
||
@lru_cache(maxsize=1)
|
||
def get_metrics_manager() -> MetricsManager:
|
||
"""Получить глобальный экземпляр менеджера метрик."""
|
||
return MetricsManager()
|
||
|
||
|
||
metrics = get_metrics_manager()
|