""" Metrics collection utilities for system monitoring. """ import time import logging from typing import Dict, Any, List from datetime import datetime, timedelta from django.db import connection from django.core.cache import cache from celery import current_app as celery_app import os import requests logger = logging.getLogger(__name__) SUPABASE_URL = os.environ.get('SUPABASE_URL', 'https://api.thrillwiki.com') SUPABASE_SERVICE_KEY = os.environ.get('SUPABASE_SERVICE_ROLE_KEY') class MetricsCollector: """Collects various system metrics for anomaly detection.""" @staticmethod def get_error_rate() -> float: """ Calculate error rate from recent logs. Returns percentage of error logs in the last minute. """ cache_key = 'metrics:error_rate' cached_value = cache.get(cache_key) if cached_value is not None: return cached_value # In production, query actual error logs # For now, return a mock value error_rate = 0.0 cache.set(cache_key, error_rate, 60) return error_rate @staticmethod def get_api_response_time() -> float: """ Get average API response time in milliseconds. Returns average response time from recent requests. """ cache_key = 'metrics:avg_response_time' cached_value = cache.get(cache_key) if cached_value is not None: return cached_value # In production, calculate from middleware metrics # For now, return a mock value response_time = 150.0 # milliseconds cache.set(cache_key, response_time, 60) return response_time @staticmethod def get_celery_queue_size() -> int: """ Get current Celery queue size across all queues. """ try: inspect = celery_app.control.inspect() active_tasks = inspect.active() or {} scheduled_tasks = inspect.scheduled() or {} total_active = sum(len(tasks) for tasks in active_tasks.values()) total_scheduled = sum(len(tasks) for tasks in scheduled_tasks.values()) return total_active + total_scheduled except Exception as e: logger.error(f"Error getting Celery queue size: {e}") return 0 @staticmethod def get_database_connection_count() -> int: """ Get current number of active database connections. """ try: with connection.cursor() as cursor: cursor.execute("SELECT count(*) FROM pg_stat_activity WHERE state = 'active';") count = cursor.fetchone()[0] return count except Exception as e: logger.error(f"Error getting database connection count: {e}") return 0 @staticmethod def get_cache_hit_rate() -> float: """ Calculate cache hit rate percentage. """ cache_key_hits = 'metrics:cache_hits' cache_key_misses = 'metrics:cache_misses' hits = cache.get(cache_key_hits, 0) misses = cache.get(cache_key_misses, 0) total = hits + misses if total == 0: return 100.0 return (hits / total) * 100 @staticmethod def record_metric(metric_name: str, metric_value: float, metric_category: str = 'system') -> bool: """ Record a metric to Supabase metric_time_series table. """ if not SUPABASE_SERVICE_KEY: logger.warning("SUPABASE_SERVICE_ROLE_KEY not configured, skipping metric recording") return False try: headers = { 'apikey': SUPABASE_SERVICE_KEY, 'Authorization': f'Bearer {SUPABASE_SERVICE_KEY}', 'Content-Type': 'application/json', } data = { 'metric_name': metric_name, 'metric_value': metric_value, 'metric_category': metric_category, 'timestamp': datetime.utcnow().isoformat(), } response = requests.post( f'{SUPABASE_URL}/rest/v1/metric_time_series', headers=headers, json=data, timeout=5 ) if response.status_code in [200, 201]: logger.info(f"Recorded metric: {metric_name} = {metric_value}") return True else: logger.error(f"Failed to record metric: {response.status_code} - {response.text}") return False except Exception as e: logger.error(f"Error recording metric {metric_name}: {e}") return False @staticmethod def collect_all_metrics() -> Dict[str, Any]: """ Collect all system metrics and record them. Returns a summary of collected metrics. """ metrics = {} try: # Collect error rate error_rate = MetricsCollector.get_error_rate() metrics['error_rate'] = error_rate MetricsCollector.record_metric('error_rate', error_rate, 'performance') # Collect API response time response_time = MetricsCollector.get_api_response_time() metrics['api_response_time'] = response_time MetricsCollector.record_metric('api_response_time', response_time, 'performance') # Collect queue size queue_size = MetricsCollector.get_celery_queue_size() metrics['celery_queue_size'] = queue_size MetricsCollector.record_metric('celery_queue_size', queue_size, 'system') # Collect database connections db_connections = MetricsCollector.get_database_connection_count() metrics['database_connections'] = db_connections MetricsCollector.record_metric('database_connections', db_connections, 'system') # Collect cache hit rate cache_hit_rate = MetricsCollector.get_cache_hit_rate() metrics['cache_hit_rate'] = cache_hit_rate MetricsCollector.record_metric('cache_hit_rate', cache_hit_rate, 'performance') logger.info(f"Successfully collected {len(metrics)} metrics") except Exception as e: logger.error(f"Error collecting metrics: {e}", exc_info=True) return metrics