| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- #!/usr/bin/env python3
- """采集本机运行指标 → JSON (跨平台: Linux/macOS/Windows-WSL)"""
- import json, os, subprocess, datetime, socket, sys
- WORK = os.path.expanduser("~/.fmode-harness-agent")
- def sh(cmd, timeout=15):
- try:
- return subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout).stdout.strip()
- except Exception as e:
- return f"ERR:{e}"
- def collect():
- now = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S+08:00")
- data = {
- "timestamp": now,
- "agent": os.environ.get("AGENT_NAME", socket.gethostname()),
- "host": {
- "platform": sys.platform,
- "hostname": socket.gethostname(),
- },
- "resources": {},
- "concurrency": {},
- "services": {},
- "events": {}
- }
- # --- CPU/内存 (跨平台: /proc/meminfo + uptime) ---
- try:
- meminfo = {}
- for line in open("/proc/meminfo"):
- k, v = line.split(":")
- meminfo[k.strip()] = int(v.strip().split()[0]) # kB
- total = meminfo["MemTotal"]
- avail = meminfo["MemAvailable"]
- data["resources"]["memory"] = {
- "total_mb": round(total / 1024),
- "used_mb": round((total - avail) / 1024),
- "percent": round((total - avail) / total * 100, 1)
- }
- load = open("/proc/loadavg").read().split()
- data["resources"]["cpu_load"] = {"m1": float(load[0]), "m5": float(load[1]), "m15": float(load[2])}
- # CPU%
- cpu = sh("top -bn1 | grep 'Cpu(s)' | awk '{print $2+$4}'")
- data["resources"]["cpu_percent"] = round(float(cpu), 1) if cpu and not cpu.startswith("ERR") else None
- except Exception as e:
- data["resources"]["error"] = str(e)[:100]
- # --- 磁盘 (工作区所在分区) ---
- disk = sh(f"df -B1 {WORK} | tail -1 | awk '{{print $2, $3, $5}}'")
- if disk and not disk.startswith("ERR"):
- t, u, p = disk.split()
- data["resources"]["disk"] = {"total_gb": round(int(t)/1e9,1), "used_gb": round(int(u)/1e9,1), "percent": p}
- # --- 网络 (累计上下行, 取主网卡) ---
- net = sh("cat /proc/net/dev | awk 'NR>2 && $1!~/lo:/ {gsub(\":\",\"\",$1); print $1, $2, $10}' | sort -k2 -rn | head -1")
- if net and not net.startswith("ERR"):
- iface, rx, tx = net.split()
- data["resources"]["network"] = {"iface": iface, "rx_total_mb": round(int(rx)/1e6,1), "tx_total_mb": round(int(tx)/1e6,1)}
- # --- GPU (有则记) ---
- gpu = sh("nvidia-smi --query-gpu=utilization.gpu,memory.used,memory.total --format=csv,noheader,nounits 2>/dev/null")
- if gpu:
- u, mu, mt = gpu.split(", ")
- data["resources"]["gpu"] = {"percent": int(u), "mem_used_mb": int(mu), "mem_total_mb": int(mt)}
- # --- 并发: profile / session / subagent ---
- profiles = sh("ls /opt/data/profiles/ 2>/dev/null | wc -l")
- data["concurrency"]["profiles_total"] = int(profiles) if profiles.isdigit() else None
- # 活跃session(hermes state.db最近30分钟)
- try:
- import sqlite3
- conn = sqlite3.connect("file:/opt/data/state.db?mode=ro", uri=True, timeout=5)
- cur = conn.cursor()
- cur.execute("SELECT COUNT(DISTINCT session_id) FROM messages WHERE timestamp > ?", (time.time()-1800,))
- data["concurrency"]["active_sessions_30min"] = cur.fetchone()[0]
- cur.execute("SELECT COUNT(*) FROM async_delegations WHERE status='running'")
- data["concurrency"]["running_subagents"] = cur.fetchone()[0]
- conn.close()
- except Exception:
- data["concurrency"]["active_sessions_30min"] = None
- # --- 服务存活 ---
- for svc, pat in [("gateway", "hermes gateway"), ("dashboard", "hermes-dashboard"), ("studio", "fmode-studio")]:
- out = sh(f"pgrep -f '{pat}' | wc -l")
- data["services"][svc] = "up" if out.isdigit() and int(out) > 0 else "down"
- # --- 事件: 最近1小时错误日志计数 ---
- errc = sh("find /opt/data/logs -name '*.log' -mmin -60 -exec grep -ci 'error\\|fatal' {} + 2>/dev/null | awk -F: '{s+=$2} END {print s}'")
- data["events"]["errors_last_1h"] = int(errc) if errc.isdigit() else None
- rest = sh("grep -c 'Reconnected' /opt/data/logs/gateway.log 2>/dev/null")
- data["events"]["wecom_reconnects_total"] = int(rest) if rest.isdigit() else None
- return data
- if __name__ == "__main__":
- import time
- out = os.path.join(WORK, "tmp")
- os.makedirs(out, exist_ok=True)
- d = collect()
- path = os.path.join(out, "agent-log.json")
- json.dump(d, open(path, "w"), ensure_ascii=False, indent=2)
- print(json.dumps(d, ensure_ascii=False)[:300])
- print(f"\nsaved: {path}")
|