101 lines
3 KiB
Python
101 lines
3 KiB
Python
#!/usr/bin/env python3
|
|
"""Telemetry ring-buffer snapshot — runs every 5min via cron.
|
|
Appends system metrics to telemetry_history.json (288 points = 24h).
|
|
"""
|
|
import json, os, time, subprocess
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
|
|
DATA_DIR = Path(__file__).parent / 'data'
|
|
HISTORY_FILE = DATA_DIR / 'telemetry_history.json'
|
|
STATE_FILE = DATA_DIR / 'telemetry_netstate.json'
|
|
MAX_POINTS = 288 # 24h @ 5min
|
|
|
|
|
|
def shell(cmd, timeout=5):
|
|
try:
|
|
r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout)
|
|
return r.stdout.strip()
|
|
except Exception:
|
|
return ''
|
|
|
|
|
|
def gather():
|
|
load = shell("cat /proc/loadavg | awk '{print $1}'")
|
|
ncpu = shell("nproc")
|
|
try:
|
|
cpu = round(min(float(load) / max(int(ncpu), 1) * 100, 100), 1)
|
|
except Exception:
|
|
cpu = 0
|
|
|
|
mem = shell("free | awk '/Mem:/{printf \"%.1f\", $3/$2*100}'")
|
|
try:
|
|
mem = float(mem)
|
|
except Exception:
|
|
mem = 0
|
|
|
|
net_raw = shell("cat /proc/net/dev | awk '/eth0|ens|enp/{print $2,$10; exit}'")
|
|
parts = net_raw.split()
|
|
rx = int(parts[0]) if len(parts) >= 2 else 0
|
|
tx = int(parts[1]) if len(parts) >= 2 else 0
|
|
|
|
# Rate calc from previous state
|
|
prev = {}
|
|
if STATE_FILE.exists():
|
|
try:
|
|
prev = json.loads(STATE_FILE.read_text())
|
|
except Exception:
|
|
prev = {}
|
|
|
|
now = time.time()
|
|
rx_bps = tx_bps = 0
|
|
if prev.get('ts') and now > prev['ts']:
|
|
dt = now - prev['ts']
|
|
rx_bps = max(0, int((rx - prev.get('rx', 0)) / dt))
|
|
tx_bps = max(0, int((tx - prev.get('tx', 0)) / dt))
|
|
|
|
STATE_FILE.write_text(json.dumps({'rx': rx, 'tx': tx, 'ts': now}))
|
|
|
|
return {
|
|
'cpu': cpu,
|
|
'mem': mem,
|
|
'net_rx': rx_bps,
|
|
'net_tx': tx_bps,
|
|
'ts': datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ'),
|
|
}
|
|
|
|
|
|
def append_history(point):
|
|
data = {'cpu': [], 'mem': [], 'net_rx': [], 'net_tx': [], 'timestamps': []}
|
|
if HISTORY_FILE.exists():
|
|
try:
|
|
data = json.loads(HISTORY_FILE.read_text())
|
|
# Ensure all keys exist
|
|
for k in ('cpu', 'mem', 'net_rx', 'net_tx', 'timestamps'):
|
|
data.setdefault(k, [])
|
|
except Exception:
|
|
pass
|
|
|
|
data['cpu'].append(point['cpu'])
|
|
data['mem'].append(point['mem'])
|
|
data['net_rx'].append(point['net_rx'])
|
|
data['net_tx'].append(point['net_tx'])
|
|
data['timestamps'].append(point['ts'])
|
|
|
|
# Ring buffer trim
|
|
for k in ('cpu', 'mem', 'net_rx', 'net_tx', 'timestamps'):
|
|
if len(data[k]) > MAX_POINTS:
|
|
data[k] = data[k][-MAX_POINTS:]
|
|
|
|
DATA_DIR.mkdir(parents=True, exist_ok=True)
|
|
HISTORY_FILE.write_text(json.dumps(data))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
try:
|
|
point = gather()
|
|
append_history(point)
|
|
print(f"[{point['ts']}] cpu={point['cpu']}% mem={point['mem']}% rx={point['net_rx']}bps tx={point['net_tx']}bps")
|
|
except Exception as e:
|
|
print(f"[ERROR] {e}")
|
|
raise
|