| """ |
| ATOM Communication Memory Production Monitoring System |
| Real-time monitoring, alerting, and performance tracking |
| """ |
|
|
| import asyncio |
| from dataclasses import asdict, dataclass |
| from datetime import datetime, timedelta |
| import json |
| import logging |
| import time |
| from typing import Any, Dict, List, Optional |
|
|
| from integrations.atom_communication_ingestion_pipeline import ingestion_pipeline, memory_manager |
|
|
| logger = logging.getLogger(__name__) |
|
|
| @dataclass |
| class MonitoringMetric: |
| """Monitoring metric data structure""" |
| name: str |
| value: float |
| unit: str |
| timestamp: datetime |
| tags: Dict[str, str] |
| threshold: Optional[float] = None |
|
|
| @dataclass |
| class Alert: |
| """Alert data structure""" |
| id: str |
| severity: str |
| title: str |
| message: str |
| timestamp: datetime |
| resolved: bool = False |
| resolved_at: Optional[datetime] = None |
| tags: Dict[str, str] |
|
|
| class AtomCommunicationMemoryMonitoring: |
| """Production monitoring system for ATOM communication memory""" |
| |
| def __init__(self): |
| self.metrics: List[MonitoringMetric] = [] |
| self.alerts: List[Alert] = [] |
| self.is_running = False |
| self.monitoring_interval = 60 |
| self.alert_thresholds = { |
| 'ingestion_rate': 0.1, |
| 'error_rate': 0.05, |
| 'memory_usage': 0.8, |
| 'search_latency': 1.0, |
| 'database_size': 100_000_000_000 |
| } |
| |
| async def start_monitoring(self): |
| """Start the monitoring system""" |
| self.is_running = True |
| logger.info("Starting ATOM communication memory monitoring") |
| |
| while self.is_running: |
| try: |
| await self.collect_metrics() |
| await self.check_alerts() |
| await asyncio.sleep(self.monitoring_interval) |
| except Exception as e: |
| logger.error(f"Error in monitoring loop: {str(e)}") |
| await asyncio.sleep(60) |
| |
| def stop_monitoring(self): |
| """Stop the monitoring system""" |
| self.is_running = False |
| logger.info("Stopping ATOM communication memory monitoring") |
| |
| async def collect_metrics(self): |
| """Collect monitoring metrics""" |
| try: |
| timestamp = datetime.now() |
| |
| |
| stats = ingestion_pipeline.get_ingestion_stats() |
| |
| |
| db_metrics = await self._collect_database_metrics(timestamp) |
| |
| |
| ingestion_metrics = await self._collect_ingestion_metrics(stats, timestamp) |
| |
| |
| performance_metrics = await self._collect_performance_metrics(timestamp) |
| |
| |
| self.metrics.extend(db_metrics + ingestion_metrics + performance_metrics) |
| |
| |
| cutoff_time = timestamp - timedelta(hours=24) |
| self.metrics = [m for m in self.metrics if m.timestamp > cutoff_time] |
| |
| logger.info(f"Collected {len(db_metrics + ingestion_metrics + performance_metrics)} metrics") |
| |
| except Exception as e: |
| logger.error(f"Error collecting metrics: {str(e)}") |
| |
| async def _collect_database_metrics(self, timestamp: datetime) -> List[MonitoringMetric]: |
| """Collect database-related metrics""" |
| metrics = [] |
| |
| try: |
| if memory_manager.connections_table: |
| |
| df = memory_manager.connections_table.to_pandas() |
| record_count = len(df) |
| |
| metrics.append(MonitoringMetric( |
| name="database_record_count", |
| value=record_count, |
| unit="records", |
| timestamp=timestamp, |
| tags={"table": "atom_communications"}, |
| threshold=self.alert_thresholds['database_size'] |
| )) |
| |
| |
| estimated_size = record_count * 1024 |
| metrics.append(MonitoringMetric( |
| name="database_size", |
| value=estimated_size, |
| unit="bytes", |
| timestamp=timestamp, |
| tags={"table": "atom_communications"}, |
| threshold=self.alert_thresholds['database_size'] |
| )) |
| |
| |
| app_dist = df["app_type"].value_counts().to_dict() |
| for app, count in app_dist.items(): |
| metrics.append(MonitoringMetric( |
| name=f"records_{app}", |
| value=count, |
| unit="records", |
| timestamp=timestamp, |
| tags={"app": app, "metric": "record_count"} |
| )) |
| |
| except Exception as e: |
| logger.error(f"Error collecting database metrics: {str(e)}") |
| |
| return metrics |
| |
| async def _collect_ingestion_metrics(self, stats: Dict[str, Any], timestamp: datetime) -> List[MonitoringMetric]: |
| """Collect ingestion-related metrics""" |
| metrics = [] |
| |
| try: |
| |
| total_messages = stats.get('total_messages', 0) |
| metrics.append(MonitoringMetric( |
| name="total_messages_ingested", |
| value=total_messages, |
| unit="messages", |
| timestamp=timestamp, |
| tags={"metric": "total_ingestion"} |
| )) |
| |
| |
| active_streams = len(stats.get('active_streams', [])) |
| metrics.append(MonitoringMetric( |
| name="active_real_time_streams", |
| value=active_streams, |
| unit="streams", |
| timestamp=timestamp, |
| tags={"metric": "active_streams"} |
| )) |
| |
| |
| configured_apps = len(stats.get('configured_apps', [])) |
| metrics.append(MonitoringMetric( |
| name="configured_apps", |
| value=configured_apps, |
| unit="apps", |
| timestamp=timestamp, |
| tags={"metric": "configured_apps"} |
| )) |
| |
| except Exception as e: |
| logger.error(f"Error collecting ingestion metrics: {str(e)}") |
| |
| return metrics |
| |
| async def _collect_performance_metrics(self, timestamp: datetime) -> List[MonitoringMetric]: |
| """Collect performance-related metrics""" |
| metrics = [] |
| |
| try: |
| |
| recent_metrics = [m for m in self.metrics |
| if m.name == "total_messages_ingested" |
| and (timestamp - m.timestamp).total_seconds() < 300] |
| |
| if len(recent_metrics) >= 2: |
| recent_metrics.sort(key=lambda x: x.timestamp) |
| latest_count = recent_metrics[-1].value |
| earliest_count = recent_metrics[0].value |
| time_diff = (recent_metrics[-1].timestamp - recent_metrics[0].timestamp).total_seconds() |
| |
| if time_diff > 0: |
| ingestion_rate = (latest_count - earliest_count) / time_diff |
| metrics.append(MonitoringMetric( |
| name="ingestion_rate", |
| value=ingestion_rate, |
| unit="messages/second", |
| timestamp=timestamp, |
| tags={"metric": "performance"}, |
| threshold=self.alert_thresholds['ingestion_rate'] |
| )) |
| |
| |
| import psutil |
| memory_percent = psutil.virtual_memory().percent / 100 |
| metrics.append(MonitoringMetric( |
| name="memory_usage", |
| value=memory_percent, |
| unit="fraction", |
| timestamp=timestamp, |
| tags={"metric": "performance"}, |
| threshold=self.alert_thresholds['memory_usage'] |
| )) |
| |
| except Exception as e: |
| logger.error(f"Error collecting performance metrics: {str(e)}") |
| |
| return metrics |
| |
| async def check_alerts(self): |
| """Check thresholds and generate alerts""" |
| try: |
| timestamp = datetime.now() |
| |
| |
| latest_metrics = {} |
| for metric in self.metrics: |
| if metric.name not in latest_metrics or metric.timestamp > latest_metrics[metric.name].timestamp: |
| latest_metrics[metric.name] = metric |
| |
| |
| for metric_name, metric in latest_metrics.items(): |
| if metric.threshold and metric.value > metric.threshold: |
| await self._create_alert( |
| severity="warning", |
| title=f"Threshold exceeded for {metric_name}", |
| message=f"{metric_name}: {metric.value:.2f} {metric.unit} (threshold: {metric.threshold})", |
| timestamp=timestamp, |
| tags=metric.tags |
| ) |
| |
| |
| if not memory_manager.db: |
| await self._create_alert( |
| severity="critical", |
| title="Database connection lost", |
| message="LanceDB database connection is not available", |
| timestamp=timestamp, |
| tags={"component": "database"} |
| ) |
| |
| except Exception as e: |
| logger.error(f"Error checking alerts: {str(e)}") |
| |
| async def _create_alert(self, severity: str, title: str, message: str, |
| timestamp: datetime, tags: Dict[str, str]): |
| """Create a new alert""" |
| alert_id = f"alert_{int(timestamp.timestamp())}_{len(self.alerts)}" |
| |
| |
| existing_alert = next((a for a in self.alerts if not a.resolved and a.title == title), None) |
| |
| if existing_alert: |
| |
| existing_alert.timestamp = timestamp |
| existing_alert.message = message |
| else: |
| |
| alert = Alert( |
| id=alert_id, |
| severity=severity, |
| title=title, |
| message=message, |
| timestamp=timestamp, |
| tags=tags |
| ) |
| |
| self.alerts.append(alert) |
| logger.warning(f"Alert created: {severity} - {title}") |
| |
| def get_metrics_summary(self, time_window: int = 3600) -> Dict[str, Any]: |
| """Get summary of metrics for the last N seconds""" |
| try: |
| cutoff_time = datetime.now() - timedelta(seconds=time_window) |
| recent_metrics = [m for m in self.metrics if m.timestamp > cutoff_time] |
| |
| |
| metrics_by_name = {} |
| for metric in recent_metrics: |
| if metric.name not in metrics_by_name: |
| metrics_by_name[metric.name] = [] |
| metrics_by_name[metric.name].append(metric) |
| |
| |
| summary = { |
| "time_window": time_window, |
| "metric_count": len(recent_metrics), |
| "metrics": {} |
| } |
| |
| for name, metric_list in metrics_by_name.items(): |
| values = [m.value for m in metric_list] |
| summary["metrics"][name] = { |
| "latest": values[-1] if values else None, |
| "average": sum(values) / len(values) if values else None, |
| "min": min(values) if values else None, |
| "max": max(values) if values else None, |
| "count": len(values), |
| "unit": metric_list[0].unit if metric_list else None |
| } |
| |
| return summary |
| |
| except Exception as e: |
| logger.error(f"Error getting metrics summary: {str(e)}") |
| return {"error": str(e)} |
| |
| def get_alerts_summary(self, include_resolved: bool = False) -> Dict[str, Any]: |
| """Get summary of alerts""" |
| try: |
| alerts = self.alerts if include_resolved else [a for a in self.alerts if not a.resolved] |
| |
| |
| severity_counts = {} |
| for alert in alerts: |
| severity_counts[alert.severity] = severity_counts.get(alert.severity, 0) + 1 |
| |
| return { |
| "total_alerts": len(alerts), |
| "unresolved_alerts": len([a for a in alerts if not a.resolved]), |
| "severity_distribution": severity_counts, |
| "recent_alerts": [ |
| { |
| "id": alert.id, |
| "severity": alert.severity, |
| "title": alert.title, |
| "message": alert.message, |
| "timestamp": alert.timestamp.isoformat(), |
| "resolved": alert.resolved |
| } |
| for alert in sorted(alerts, key=lambda x: x.timestamp, reverse=True)[:10] |
| ] |
| } |
| |
| except Exception as e: |
| logger.error(f"Error getting alerts summary: {str(e)}") |
| return {"error": str(e)} |
| |
| def get_health_status(self) -> Dict[str, Any]: |
| """Get overall system health status""" |
| try: |
| |
| health_checks = { |
| "database": memory_manager.db is not None, |
| "ingestion_pipeline": len(ingestion_pipeline.ingestion_configs) > 0, |
| "monitoring": self.is_running |
| } |
| |
| |
| recent_alerts = [a for a in self.alerts |
| if not a.resolved |
| and a.severity in ["error", "critical"] |
| and (datetime.now() - a.timestamp).total_seconds() < 3600] |
| |
| overall_status = "healthy" |
| if not all(health_checks.values()): |
| overall_status = "unhealthy" |
| elif recent_alerts: |
| overall_status = "degraded" |
| |
| return { |
| "overall_status": overall_status, |
| "timestamp": datetime.now().isoformat(), |
| "health_checks": health_checks, |
| "recent_critical_alerts": len(recent_alerts), |
| "monitoring_active": self.is_running |
| } |
| |
| except Exception as e: |
| logger.error(f"Error getting health status: {str(e)}") |
| return {"error": str(e), "overall_status": "unknown"} |
|
|
| |
| atom_memory_monitoring = AtomCommunicationMemoryMonitoring() |
|
|
| |
| __all__ = [ |
| 'AtomCommunicationMemoryMonitoring', |
| 'atom_memory_monitoring', |
| 'MonitoringMetric', |
| 'Alert' |
| ] |
|
|