Initial commit: Message Gateway project
- FastAPI приложение для отправки мониторинговых алертов в мессенджеры - Поддержка Telegram и MAX/VK - Интеграция с Grafana, Zabbix, AlertManager - Автоматическое создание тикетов в Jira - Управление группами мессенджеров через API - Декораторы для авторизации и скрытия эндпоинтов - Подробная документация в папке docs/ Автор: Сергей Антропов Сайт: https://devops.org.ru
This commit is contained in:
316
app/core/metrics.py
Normal file
316
app/core/metrics.py
Normal file
@@ -0,0 +1,316 @@
|
||||
"""
|
||||
Централизованное управление метриками 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()
|
||||
Reference in New Issue
Block a user