Add normalization to gossip transaction processing to preserve type field
Some checks failed
Blockchain Synchronization Verification / sync-verification (push) Successful in 2s
Integration Tests / test-service-integration (push) Failing after 10s
Multi-Node Blockchain Health Monitoring / health-check (push) Successful in 2s
P2P Network Verification / p2p-verification (push) Successful in 2s
Python Tests / test-python (push) Successful in 11s
Security Scanning / security-scan (push) Has been cancelled
Some checks failed
Blockchain Synchronization Verification / sync-verification (push) Successful in 2s
Integration Tests / test-service-integration (push) Failing after 10s
Multi-Node Blockchain Health Monitoring / health-check (push) Successful in 2s
P2P Network Verification / p2p-verification (push) Successful in 2s
Python Tests / test-python (push) Successful in 11s
Security Scanning / security-scan (push) Has been cancelled
This commit is contained in:
259
aitbc/monitoring.py
Normal file
259
aitbc/monitoring.py
Normal file
@@ -0,0 +1,259 @@
|
||||
"""
|
||||
AITBC Monitoring and Metrics Utilities
|
||||
Monitoring and metrics collection for AITBC applications
|
||||
"""
|
||||
|
||||
import time
|
||||
from typing import Dict, Any, Optional
|
||||
from collections import defaultdict
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
class MetricsCollector:
|
||||
"""
|
||||
Simple in-memory metrics collector for AITBC applications.
|
||||
Tracks counters, timers, and gauges.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize metrics collector."""
|
||||
self.counters: Dict[str, int] = defaultdict(int)
|
||||
self.timers: Dict[str, list] = defaultdict(list)
|
||||
self.gauges: Dict[str, float] = {}
|
||||
self.timestamps: Dict[str, datetime] = {}
|
||||
|
||||
def increment(self, metric: str, value: int = 1) -> None:
|
||||
"""
|
||||
Increment a counter metric.
|
||||
|
||||
Args:
|
||||
metric: Metric name
|
||||
value: Value to increment by
|
||||
"""
|
||||
self.counters[metric] += value
|
||||
self.timestamps[metric] = datetime.now()
|
||||
|
||||
def decrement(self, metric: str, value: int = 1) -> None:
|
||||
"""
|
||||
Decrement a counter metric.
|
||||
|
||||
Args:
|
||||
metric: Metric name
|
||||
value: Value to decrement by
|
||||
"""
|
||||
self.counters[metric] -= value
|
||||
self.timestamps[metric] = datetime.now()
|
||||
|
||||
def timing(self, metric: str, duration: float) -> None:
|
||||
"""
|
||||
Record a timing metric.
|
||||
|
||||
Args:
|
||||
metric: Metric name
|
||||
duration: Duration in seconds
|
||||
"""
|
||||
self.timers[metric].append(duration)
|
||||
self.timestamps[metric] = datetime.now()
|
||||
|
||||
def set_gauge(self, metric: str, value: float) -> None:
|
||||
"""
|
||||
Set a gauge metric.
|
||||
|
||||
Args:
|
||||
metric: Metric name
|
||||
value: Gauge value
|
||||
"""
|
||||
self.gauges[metric] = value
|
||||
self.timestamps[metric] = datetime.now()
|
||||
|
||||
def get_counter(self, metric: str) -> int:
|
||||
"""
|
||||
Get counter value.
|
||||
|
||||
Args:
|
||||
metric: Metric name
|
||||
|
||||
Returns:
|
||||
Counter value
|
||||
"""
|
||||
return self.counters.get(metric, 0)
|
||||
|
||||
def get_timer_stats(self, metric: str) -> Dict[str, float]:
|
||||
"""
|
||||
Get timer statistics for a metric.
|
||||
|
||||
Args:
|
||||
metric: Metric name
|
||||
|
||||
Returns:
|
||||
Dictionary with min, max, avg, count
|
||||
"""
|
||||
timings = self.timers.get(metric, [])
|
||||
if not timings:
|
||||
return {"min": 0, "max": 0, "avg": 0, "count": 0}
|
||||
|
||||
return {
|
||||
"min": min(timings),
|
||||
"max": max(timings),
|
||||
"avg": sum(timings) / len(timings),
|
||||
"count": len(timings)
|
||||
}
|
||||
|
||||
def get_gauge(self, metric: str) -> Optional[float]:
|
||||
"""
|
||||
Get gauge value.
|
||||
|
||||
Args:
|
||||
metric: Metric name
|
||||
|
||||
Returns:
|
||||
Gauge value or None
|
||||
"""
|
||||
return self.gauges.get(metric)
|
||||
|
||||
def get_all_metrics(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Get all collected metrics.
|
||||
|
||||
Returns:
|
||||
Dictionary of all metrics
|
||||
"""
|
||||
return {
|
||||
"counters": dict(self.counters),
|
||||
"timers": {k: self.get_timer_stats(k) for k in self.timers},
|
||||
"gauges": dict(self.gauges),
|
||||
"timestamps": {k: v.isoformat() for k, v in self.timestamps.items()}
|
||||
}
|
||||
|
||||
def reset_metric(self, metric: str) -> None:
|
||||
"""
|
||||
Reset a specific metric.
|
||||
|
||||
Args:
|
||||
metric: Metric name
|
||||
"""
|
||||
if metric in self.counters:
|
||||
del self.counters[metric]
|
||||
if metric in self.timers:
|
||||
del self.timers[metric]
|
||||
if metric in self.gauges:
|
||||
del self.gauges[metric]
|
||||
if metric in self.timestamps:
|
||||
del self.timestamps[metric]
|
||||
|
||||
def reset_all(self) -> None:
|
||||
"""Reset all metrics."""
|
||||
self.counters.clear()
|
||||
self.timers.clear()
|
||||
self.gauges.clear()
|
||||
self.timestamps.clear()
|
||||
|
||||
|
||||
class PerformanceTimer:
|
||||
"""
|
||||
Context manager for timing operations.
|
||||
"""
|
||||
|
||||
def __init__(self, collector: MetricsCollector, metric: str):
|
||||
"""
|
||||
Initialize timer.
|
||||
|
||||
Args:
|
||||
collector: MetricsCollector instance
|
||||
metric: Metric name
|
||||
"""
|
||||
self.collector = collector
|
||||
self.metric = metric
|
||||
self.start_time = None
|
||||
|
||||
def __enter__(self):
|
||||
"""Start timing."""
|
||||
self.start_time = time.time()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
"""Stop timing and record metric."""
|
||||
if self.start_time:
|
||||
duration = time.time() - self.start_time
|
||||
self.collector.timing(self.metric, duration)
|
||||
|
||||
|
||||
class HealthChecker:
|
||||
"""
|
||||
Health check utilities for AITBC applications.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize health checker."""
|
||||
self.checks: Dict[str, Any] = {}
|
||||
self.last_check: Optional[datetime] = None
|
||||
|
||||
def add_check(self, name: str, check_func: callable) -> None:
|
||||
"""
|
||||
Add a health check.
|
||||
|
||||
Args:
|
||||
name: Check name
|
||||
check_func: Function that returns (status, message)
|
||||
"""
|
||||
self.checks[name] = check_func
|
||||
|
||||
def run_check(self, name: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Run a specific health check.
|
||||
|
||||
Args:
|
||||
name: Check name
|
||||
|
||||
Returns:
|
||||
Check result with status and message
|
||||
"""
|
||||
if name not in self.checks:
|
||||
return {"status": "unknown", "message": f"Check '{name}' not found"}
|
||||
|
||||
try:
|
||||
status, message = self.checks[name]()
|
||||
return {"status": status, "message": message}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
|
||||
def run_all_checks(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Run all health checks.
|
||||
|
||||
Returns:
|
||||
Dictionary of all check results
|
||||
"""
|
||||
self.last_check = datetime.now()
|
||||
results = {}
|
||||
|
||||
for name in self.checks:
|
||||
results[name] = self.run_check(name)
|
||||
|
||||
return {
|
||||
"checks": results,
|
||||
"overall_status": self._get_overall_status(results),
|
||||
"timestamp": self.last_check.isoformat()
|
||||
}
|
||||
|
||||
def _get_overall_status(self, results: Dict[str, Any]) -> str:
|
||||
"""
|
||||
Determine overall health status.
|
||||
|
||||
Args:
|
||||
results: Check results
|
||||
|
||||
Returns:
|
||||
Overall status (healthy, degraded, unhealthy)
|
||||
"""
|
||||
if not results:
|
||||
return "unknown"
|
||||
|
||||
statuses = [r.get("status", "unknown") for r in results.values()]
|
||||
|
||||
if all(s == "healthy" for s in statuses):
|
||||
return "healthy"
|
||||
elif any(s == "unhealthy" for s in statuses):
|
||||
return "unhealthy"
|
||||
else:
|
||||
return "degraded"
|
||||
Reference in New Issue
Block a user