Files
MessageGateway/app/core/metrics.py
Sergey Antropov b90def35ed Initial commit: Message Gateway project
- FastAPI приложение для отправки мониторинговых алертов в мессенджеры
- Поддержка Telegram и MAX/VK
- Интеграция с Grafana, Zabbix, AlertManager
- Автоматическое создание тикетов в Jira
- Управление группами мессенджеров через API
- Декораторы для авторизации и скрытия эндпоинтов
- Подробная документация в папке docs/

Автор: Сергей Антропов
Сайт: https://devops.org.ru
2025-11-12 20:25:11 +03:00

317 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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