""" Централизованное управление метриками 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()