Implement audio record management features in AsyncBotDB and AudioRepository
- Added methods to delete audio moderation records and retrieve all audio records in async_db.py. - Enhanced AudioRepository with functionality to delete audio records by file name and retrieve all audio message records. - Improved logging for audio record operations to enhance monitoring and debugging capabilities. - Updated related handlers to ensure proper integration of new audio management features.
This commit is contained in:
@@ -7,10 +7,13 @@ from typing import Dict, Any, Optional
|
||||
from prometheus_client import Counter, Histogram, Gauge, generate_latest, CONTENT_TYPE_LATEST
|
||||
from prometheus_client.core import CollectorRegistry
|
||||
import time
|
||||
import os
|
||||
from functools import wraps
|
||||
import asyncio
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
# Метрики rate limiter теперь создаются в основном классе
|
||||
|
||||
|
||||
class BotMetrics:
|
||||
"""Central class for managing all bot metrics."""
|
||||
@@ -18,6 +21,9 @@ class BotMetrics:
|
||||
def __init__(self):
|
||||
self.registry = CollectorRegistry()
|
||||
|
||||
# Создаем метрики rate limiter в том же registry
|
||||
self._create_rate_limit_metrics()
|
||||
|
||||
# Bot commands counter
|
||||
self.bot_commands_total = Counter(
|
||||
'bot_commands_total',
|
||||
@@ -158,6 +164,78 @@ class BotMetrics:
|
||||
registry=self.registry
|
||||
)
|
||||
|
||||
def _create_rate_limit_metrics(self):
|
||||
"""Создает метрики rate limiter в основном registry"""
|
||||
try:
|
||||
# Создаем метрики rate limiter в том же registry
|
||||
self.rate_limit_requests_total = Counter(
|
||||
'rate_limit_requests_total',
|
||||
'Total number of rate limited requests',
|
||||
['chat_id', 'status', 'error_type'],
|
||||
registry=self.registry
|
||||
)
|
||||
|
||||
self.rate_limit_errors_total = Counter(
|
||||
'rate_limit_errors_total',
|
||||
'Total number of rate limit errors',
|
||||
['error_type', 'chat_id'],
|
||||
registry=self.registry
|
||||
)
|
||||
|
||||
self.rate_limit_wait_duration_seconds = Histogram(
|
||||
'rate_limit_wait_duration_seconds',
|
||||
'Time spent waiting due to rate limiting',
|
||||
['chat_id'],
|
||||
buckets=[0.1, 0.5, 1.0, 2.0, 5.0, 10.0, 30.0, 60.0],
|
||||
registry=self.registry
|
||||
)
|
||||
|
||||
self.rate_limit_active_chats = Gauge(
|
||||
'rate_limit_active_chats',
|
||||
'Number of active chats with rate limiting',
|
||||
registry=self.registry
|
||||
)
|
||||
|
||||
self.rate_limit_success_rate = Gauge(
|
||||
'rate_limit_success_rate',
|
||||
'Success rate of rate limited requests',
|
||||
['chat_id'],
|
||||
registry=self.registry
|
||||
)
|
||||
|
||||
self.rate_limit_requests_per_minute = Gauge(
|
||||
'rate_limit_requests_per_minute',
|
||||
'Requests per minute',
|
||||
['chat_id'],
|
||||
registry=self.registry
|
||||
)
|
||||
|
||||
self.rate_limit_total_requests = Gauge(
|
||||
'rate_limit_total_requests',
|
||||
'Total number of requests',
|
||||
['chat_id'],
|
||||
registry=self.registry
|
||||
)
|
||||
|
||||
self.rate_limit_total_errors = Gauge(
|
||||
'rate_limit_total_errors',
|
||||
'Total number of errors',
|
||||
['chat_id', 'error_type'],
|
||||
registry=self.registry
|
||||
)
|
||||
|
||||
self.rate_limit_avg_wait_time_seconds = Gauge(
|
||||
'rate_limit_avg_wait_time_seconds',
|
||||
'Average wait time in seconds',
|
||||
['chat_id'],
|
||||
registry=self.registry
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
# Логируем ошибку, но не прерываем инициализацию
|
||||
import logging
|
||||
logging.warning(f"Failed to create rate limit metrics: {e}")
|
||||
|
||||
def record_command(self, command_type: str, handler_type: str = "unknown", user_type: str = "unknown", status: str = "success"):
|
||||
"""Record a bot command execution."""
|
||||
self.bot_commands_total.labels(
|
||||
@@ -267,8 +345,97 @@ class BotMetrics:
|
||||
method_name="add_in_db_media"
|
||||
).inc()
|
||||
|
||||
def record_db_error(self, error_type: str, query_type: str = "unknown", table_name: str = "unknown", operation: str = "unknown"):
|
||||
"""Record database error occurrence."""
|
||||
self.db_errors_total.labels(
|
||||
error_type=error_type,
|
||||
query_type=query_type,
|
||||
table_name=table_name,
|
||||
operation=operation
|
||||
).inc()
|
||||
|
||||
def record_rate_limit_request(self, chat_id: int, success: bool, wait_time: float = 0.0, error_type: str = None):
|
||||
"""Record rate limit request metrics."""
|
||||
try:
|
||||
# Определяем статус
|
||||
status = "success" if success else "error"
|
||||
|
||||
# Записываем счетчик запросов
|
||||
self.rate_limit_requests_total.labels(
|
||||
chat_id=str(chat_id),
|
||||
status=status,
|
||||
error_type=error_type or "none"
|
||||
).inc()
|
||||
|
||||
# Записываем время ожидания
|
||||
if wait_time > 0:
|
||||
self.rate_limit_wait_duration_seconds.labels(
|
||||
chat_id=str(chat_id)
|
||||
).observe(wait_time)
|
||||
|
||||
# Записываем ошибки
|
||||
if not success and error_type:
|
||||
self.rate_limit_errors_total.labels(
|
||||
error_type=error_type,
|
||||
chat_id=str(chat_id)
|
||||
).inc()
|
||||
except Exception as e:
|
||||
import logging
|
||||
logging.warning(f"Failed to record rate limit request: {e}")
|
||||
|
||||
def update_rate_limit_gauges(self):
|
||||
"""Update rate limit gauge metrics."""
|
||||
try:
|
||||
from .rate_limit_monitor import rate_limit_monitor
|
||||
|
||||
# Обновляем количество активных чатов
|
||||
self.rate_limit_active_chats.set(len(rate_limit_monitor.stats))
|
||||
|
||||
# Обновляем метрики для каждого чата
|
||||
for chat_id, chat_stats in rate_limit_monitor.stats.items():
|
||||
chat_id_str = str(chat_id)
|
||||
|
||||
# Процент успеха
|
||||
self.rate_limit_success_rate.labels(
|
||||
chat_id=chat_id_str
|
||||
).set(chat_stats.success_rate)
|
||||
|
||||
# Запросов в минуту
|
||||
self.rate_limit_requests_per_minute.labels(
|
||||
chat_id=chat_id_str
|
||||
).set(chat_stats.requests_per_minute)
|
||||
|
||||
# Общее количество запросов
|
||||
self.rate_limit_total_requests.labels(
|
||||
chat_id=chat_id_str
|
||||
).set(chat_stats.total_requests)
|
||||
|
||||
# Среднее время ожидания
|
||||
self.rate_limit_avg_wait_time_seconds.labels(
|
||||
chat_id=chat_id_str
|
||||
).set(chat_stats.average_wait_time)
|
||||
|
||||
# Количество ошибок по типам
|
||||
if chat_stats.retry_after_errors > 0:
|
||||
self.rate_limit_total_errors.labels(
|
||||
chat_id=chat_id_str,
|
||||
error_type="RetryAfter"
|
||||
).set(chat_stats.retry_after_errors)
|
||||
|
||||
if chat_stats.other_errors > 0:
|
||||
self.rate_limit_total_errors.labels(
|
||||
chat_id=chat_id_str,
|
||||
error_type="Other"
|
||||
).set(chat_stats.other_errors)
|
||||
except Exception as e:
|
||||
import logging
|
||||
logging.warning(f"Failed to update rate limit gauges: {e}")
|
||||
|
||||
def get_metrics(self) -> bytes:
|
||||
"""Generate metrics in Prometheus format."""
|
||||
# Обновляем gauge метрики rate limiter перед генерацией
|
||||
self.update_rate_limit_gauges()
|
||||
|
||||
return generate_latest(self.registry)
|
||||
|
||||
|
||||
@@ -449,3 +616,89 @@ async def track_middleware(middleware_name: str):
|
||||
middleware_name
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
def track_media_processing(content_type: str = "unknown"):
|
||||
"""Decorator to track media processing operations."""
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
async def async_wrapper(*args, **kwargs):
|
||||
start_time = time.time()
|
||||
try:
|
||||
result = await func(*args, **kwargs)
|
||||
duration = time.time() - start_time
|
||||
metrics.record_media_processing(content_type, duration, True)
|
||||
return result
|
||||
except Exception as e:
|
||||
duration = time.time() - start_time
|
||||
metrics.record_media_processing(content_type, duration, False)
|
||||
raise
|
||||
|
||||
@wraps(func)
|
||||
def sync_wrapper(*args, **kwargs):
|
||||
start_time = time.time()
|
||||
try:
|
||||
result = func(*args, **kwargs)
|
||||
duration = time.time() - start_time
|
||||
metrics.record_media_processing(content_type, duration, True)
|
||||
return result
|
||||
except Exception as e:
|
||||
duration = time.time() - start_time
|
||||
metrics.record_media_processing(content_type, duration, False)
|
||||
raise
|
||||
|
||||
if asyncio.iscoroutinefunction(func):
|
||||
return async_wrapper
|
||||
return sync_wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
def track_file_operations(content_type: str = "unknown"):
|
||||
"""Decorator to track file download/upload operations."""
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
async def async_wrapper(*args, **kwargs):
|
||||
start_time = time.time()
|
||||
try:
|
||||
result = await func(*args, **kwargs)
|
||||
duration = time.time() - start_time
|
||||
|
||||
# Получаем размер файла из результата
|
||||
file_size = 0
|
||||
if result and isinstance(result, str) and os.path.exists(result):
|
||||
file_size = os.path.getsize(result)
|
||||
|
||||
# Записываем метрики
|
||||
metrics.record_file_download(content_type, file_size, duration)
|
||||
|
||||
return result
|
||||
except Exception as e:
|
||||
duration = time.time() - start_time
|
||||
metrics.record_file_download_error(content_type, str(e))
|
||||
raise
|
||||
|
||||
@wraps(func)
|
||||
def sync_wrapper(*args, **kwargs):
|
||||
start_time = time.time()
|
||||
try:
|
||||
result = func(*args, **kwargs)
|
||||
duration = time.time() - start_time
|
||||
|
||||
# Получаем размер файла из результата
|
||||
file_size = 0
|
||||
if result and isinstance(result, str) and os.path.exists(result):
|
||||
file_size = os.path.getsize(result)
|
||||
|
||||
# Записываем метрики
|
||||
metrics.record_file_download(content_type, file_size, duration)
|
||||
|
||||
return result
|
||||
except Exception as e:
|
||||
duration = time.time() - start_time
|
||||
metrics.record_file_download_error(content_type, str(e))
|
||||
raise
|
||||
|
||||
if asyncio.iscoroutinefunction(func):
|
||||
return async_wrapper
|
||||
return sync_wrapper
|
||||
return decorator
|
||||
|
||||
Reference in New Issue
Block a user