import asyncio import time from collections import deque import psutil class MetricsStore: def __init__(self, maxlen: int = 720): self.samples = deque(maxlen=maxlen) self.interval = 5 def add(self, sample: dict): self.samples.append(sample) async def start_sampler(store: MetricsStore, interval: int = 5): store.interval = interval psutil.cpu_percent(interval=None) last_net = psutil.net_io_counters() while True: now = time.time() cpu = psutil.cpu_percent(interval=None) load1 = psutil.getloadavg()[0] net = psutil.net_io_counters() rx_bytes = net.bytes_recv - last_net.bytes_recv tx_bytes = net.bytes_sent - last_net.bytes_sent last_net = net rx_rate = rx_bytes / interval tx_rate = tx_bytes / interval store.add({ "ts": now, "cpu": cpu, "load1": load1, "rx_bytes": rx_bytes, "tx_bytes": tx_bytes, "rx_rate": rx_rate, "tx_rate": tx_rate, }) await asyncio.sleep(interval) def summarize(store: MetricsStore, minutes: int = 15) -> str: cutoff = time.time() - minutes * 60 data = [s for s in list(store.samples) if s["ts"] >= cutoff] if not data: return "📈 Metrics\n\n⚠️ No data yet" cpu_vals = [s["cpu"] for s in data] load_vals = [s["load1"] for s in data] rx_rates = [s["rx_rate"] for s in data] tx_rates = [s["tx_rate"] for s in data] total_rx = sum(s["rx_bytes"] for s in data) total_tx = sum(s["tx_bytes"] for s in data) def avg(vals): return sum(vals) / len(vals) if vals else 0.0 def fmt_rate(bps): if bps > 1024**2: return f"{bps / (1024**2):.2f} MiB/s" if bps > 1024: return f"{bps / 1024:.2f} KiB/s" return f"{bps:.0f} B/s" def fmt_bytes(b): if b > 1024**3: return f"{b / (1024**3):.2f} GiB" if b > 1024**2: return f"{b / (1024**2):.2f} MiB" if b > 1024: return f"{b / 1024:.2f} KiB" return f"{b} B" return ( f"📈 Metrics (last {minutes}m)\n\n" f"🧠 CPU avg: {avg(cpu_vals):.1f}% | max: {max(cpu_vals):.1f}%\n" f"⚙️ Load avg: {avg(load_vals):.2f} | max: {max(load_vals):.2f}\n" f"⬇️ RX avg: {fmt_rate(avg(rx_rates))} | max: {fmt_rate(max(rx_rates))} | total: {fmt_bytes(total_rx)}\n" f"⬆️ TX avg: {fmt_rate(avg(tx_rates))} | max: {fmt_rate(max(tx_rates))} | total: {fmt_bytes(total_tx)}" )