aee0f09db8
- Datafabrik: Dockerfile fix, agentorkestrering fungerar - Vision: Identify-modell, FAISS, OCR alla testade - API: Alla 7 integrationstester passerade - Upplösare: Entitetsupplösning verifierad
133 lines
3.9 KiB
Python
133 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
LIFE Runtime Monitor
|
|
Samlar operativa mätvärden under burn-in
|
|
"""
|
|
import sqlite3
|
|
import json
|
|
import time
|
|
from datetime import datetime, timedelta
|
|
from pathlib import Path
|
|
|
|
DB_PATH = "/home/bernt/.openclaw/workspace/rivp-pilot-1/rivp.db"
|
|
LOG_DIR = Path("/home/bernt/.openclaw/workspace/life-weather/logs")
|
|
METRICS_FILE = Path("/home/bernt/.openclaw/workspace/life-weather/metrics.json")
|
|
|
|
def collect_metrics():
|
|
"""Samla mätvärden från databasen"""
|
|
conn = sqlite3.connect(DB_PATH)
|
|
c = conn.cursor()
|
|
|
|
metrics = {
|
|
"timestamp": datetime.now().isoformat(),
|
|
"pipeline": {},
|
|
"observations": {},
|
|
"system": {}
|
|
}
|
|
|
|
# Pipeline-mätvärden
|
|
c.execute("SELECT COUNT(*) FROM weather_observations")
|
|
total_obs = c.fetchone()[0]
|
|
|
|
c.execute("""
|
|
SELECT COUNT(*) FROM weather_observations
|
|
WHERE created_at > datetime('now', '-1 hour')
|
|
""")
|
|
obs_last_hour = c.fetchone()[0]
|
|
|
|
# Dubbletter
|
|
c.execute("""
|
|
SELECT road_id, observation_type, timestamp, COUNT(*) as cnt
|
|
FROM weather_observations
|
|
GROUP BY road_id, observation_type, timestamp
|
|
HAVING cnt > 1
|
|
""")
|
|
duplicates = len(c.fetchall())
|
|
|
|
# Reality Latency
|
|
c.execute("""
|
|
SELECT MAX(created_at) FROM weather_observations
|
|
""")
|
|
last_obs = c.fetchone()[0]
|
|
if last_obs:
|
|
last_time = datetime.fromisoformat(last_obs)
|
|
latency_minutes = (datetime.now() - last_time).total_seconds() / 60
|
|
else:
|
|
latency_minutes = None
|
|
|
|
metrics["observations"] = {
|
|
"total": total_obs,
|
|
"last_hour": obs_last_hour,
|
|
"duplicates": duplicates,
|
|
"reality_latency_minutes": round(latency_minutes, 1) if latency_minutes else None
|
|
}
|
|
|
|
# System-mätvärden (från loggar)
|
|
log_file = LOG_DIR / "scheduler.log"
|
|
if log_file.exists():
|
|
with open(log_file) as f:
|
|
lines = f.readlines()
|
|
|
|
# Räkna fel
|
|
errors = [l for l in lines if "ERROR" in l]
|
|
warnings = [l for l in lines if "WARNING" in l]
|
|
|
|
metrics["pipeline"] = {
|
|
"total_runs": len([l for l in lines if "WEATHER JOB STARTAR" in l]),
|
|
"errors": len(errors),
|
|
"warnings": len(warnings)
|
|
}
|
|
|
|
conn.close()
|
|
|
|
# Spara mätvärden
|
|
if METRICS_FILE.exists():
|
|
with open(METRICS_FILE) as f:
|
|
history = json.load(f)
|
|
else:
|
|
history = []
|
|
|
|
history.append(metrics)
|
|
|
|
# Behåll senaste 168 timmar (7 dagar)
|
|
cutoff = datetime.now() - timedelta(hours=168)
|
|
history = [h for h in history if datetime.fromisoformat(h["timestamp"]) > cutoff]
|
|
|
|
with open(METRICS_FILE, 'w') as f:
|
|
json.dump(history, f, indent=2)
|
|
|
|
return metrics
|
|
|
|
def print_status():
|
|
"""Skriv ut aktuell status"""
|
|
metrics = collect_metrics()
|
|
|
|
print("=" * 60)
|
|
print("LIFE RUNTIME STATUS")
|
|
print("=" * 60)
|
|
print(f"Tid: {metrics['timestamp']}")
|
|
print()
|
|
print("OBSERVATIONER:")
|
|
print(f" Total: {metrics['observations']['total']}")
|
|
print(f" Senaste timmen: {metrics['observations']['last_hour']}")
|
|
print(f" Dubbletter: {metrics['observations']['duplicates']}")
|
|
print(f" Reality Latency: {metrics['observations']['reality_latency_minutes']} min")
|
|
print()
|
|
print("PIPELINE:")
|
|
print(f" Körningar: {metrics['pipeline'].get('total_runs', 0)}")
|
|
print(f" Fel: {metrics['pipeline'].get('errors', 0)}")
|
|
print(f" Varningar: {metrics['pipeline'].get('warnings', 0)}")
|
|
print()
|
|
|
|
# Beräkna success rate
|
|
total = metrics['pipeline'].get('total_runs', 0)
|
|
errors = metrics['pipeline'].get('errors', 0)
|
|
if total > 0:
|
|
success_rate = ((total - errors) / total) * 100
|
|
print(f" Success Rate: {success_rate:.1f}%")
|
|
|
|
print("=" * 60)
|
|
|
|
if __name__ == "__main__":
|
|
print_status()
|