关于 macbook-metrics-setup
macbook-metrics-setup 是一份构建 Mac 本地指标采集器的方案,面向希望自行掌控长期系统数据的用户。它说明如何以低开销将系统采样存入 SQLite,并通过 launchd 定时采集和同步备份。
主要功能
- 01每分钟采集系统指标
- 02将数据存入 SQLite
- 03提供采集、诊断和报告命令
- 04使用 launchd 定时运行采集
- 05定时备份数据库到私有 GitHub 仓库
适用场景
- 01长期记录 Mac 系统资源变化
- 02检查采集器和采样器是否正常
- 03查看指定时段的指标摘要
- 04将数据库快照备份到私有仓库
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ davidondrej/ skills --skill macbook-metrics-setup安装后在 Claude Code 中输入/macbook-metrics-setup确认已加载。
详细介绍
MacBook 指标设置
如何构建一个低开销、长期运行且完全由你掌控的 Mac 指标采集器。 无需云服务、无需遥测、无需账号——一个二进制文件、一个 SQLite 文件、两个 launchd 作业、一个私有 GitHub 仓库。
理念(构建前请先阅读)
- 仅限本地且确定。 采集器绝不进行网络调用,绝不使用大模型,绝不发送分析数据。任何人都应该能够 阅读代码,并准确预知它会写入什么。
- 数据存放在仓库之外,位于
~/Library/Application Support/<project>/,因此重新安装和 Git 操作都不会触及测量数据。 - 低开销是硬性要求。 每分钟采样一次,使用 release 构建和
LowPriorityIO——监控程序绝不能成为负载来源。 - 未公开接口的读取结果必须允许为空,并在出错时明确报错。 GPU 利用率和 SMC 温度读取使用私有接口,任何 macOS 更新都可能导致其失效;将它们存储为可空列并显示错误, 绝不伪造数值。
- 决策都要记录下来。 每项架构选择都要在
docs/adr/中编写 ADR; 每项架构变更都要在docs/database/中编写带编号的 SQL 文件。Agent 和人都要遵循 ADR,将其视为契约。 - 按进程跟踪为可选功能。 默认采集系统范围的数据;监控特定应用(例如 Electron IDE)则是 独立且隔离的采集器,只有通过其专属 ADR 才会添加。
仓库结构
Sources/<ProjectLib>/ # samplers, SQLite storage, report generation
Sources/<project-cli>/ # thin CLI: collect | doctor | report
launchd/ # plist templates with __PLACEHOLDER__ tokens
scripts/ # install.sh, uninstall.sh, sync.sh
docs/adr/ # numbered architecture decision records
docs/database/ # numbered SQL migrations
backups/ # git-tracked SQLite snapshots (written by sync)
Swift + SwiftPM 是自然之选(原生 IOKit/Mach API、零依赖),但这种架构也适用于任何编译型语言。
数据采集
- 采样系统范围的 CPU %、GPU %、内存(已用/固定/压缩/ 压力)、交换空间、磁盘容量 + IO 字节/操作计数器、网络字节/数据包 计数器、电池、热状态、温度、风扇 RPM。
- 将累计的 OS 计数器(磁盘/网络)存储为每个时间间隔的增量。
- 每分钟向 SQLite(WAL 模式)写入一行。一年只有约 50 万行—— 添加一张 15 分钟汇总表,以便快速生成长期报告。
- CLI 子命令:
collect(采样一次并退出)、doctor(打印实时读数, 采样器故障时明确报错)、report --hours N(文本摘要)。
两个 launchd 作业
两者都是位于 ~/Library/LaunchAgents/ 中的用户 LaunchAgent——无需 root,也无需
守护进程。模板保存在仓库中;install.sh 会填充占位符。
1. 采集器——每隔 60 秒运行一次二进制文件并执行 collect:
RunAtLoad true、StartInterval 60、ProcessType Background、
LowPriorityIO true,标准输出/错误输出写入 ~/Library/Logs/<project>/。
进程运行不到一秒便退出;launchd 负责调度(launchctl list 显示 PID - 且状态为 0 即表示运行正常)。
2. GitHub 同步——每隔 3 小时运行一次 scripts/sync.sh
(StartInterval 10800),该脚本会:
# snapshot safely while the DB is live, then normalize for git
sqlite3 "$db_path" ".backup '$tmp'"
sqlite3 "$tmp" 'PRAGMA journal_mode=DELETE;' # fold WAL into one file
mv -f "$tmp" backups/metrics.sqlite3
# commit ONLY the backups path, skip if unchanged, push to a PRIVATE repo
git add backups && git commit -m "Backup metrics database $(date '+%Y-%m-%d %H:%M')" -- backups
git push origin HEAD
绝不要用 cp 复制正在使用的 WAL 数据库——始终使用 sqlite3 .backup。
保持仓库私有:指标会暴露你的日常活动模式。
install.sh 模式
swift build -c release
# run the binary from Application Support, NOT the repo build dir,
# so rebuilds/branch switches never break the running job
install -m 755 .build/release/<project> "$data_dir/bin/"
install -m 644 launchd/<label>.plist ~/Library/LaunchAgents/
sed -i '' -e "s|__EXECUTABLE__|$binary|" ... "$agent_path" # fill placeholders
plutil -lint "$agent_path" # validate before loading
launchctl bootout "gui/$UID" "$agent_path" 2>/dev/null || true # idempotent reinstall
launchctl bootstrap "gui/$UID" "$agent_path"
uninstall.sh 执行相反操作:bootout 两个 Agent,并移除 plist 文件。
使用类似 com.<yourname>.<project> 的反向 DNS 标签。
验证安装
launchctl list | grep <project> # exit status 0 = last run OK
sqlite3 "$db" "SELECT datetime(MAX(timestamp),'unixepoch','localtime') FROM raw_samples;"
# must be under a minute old; also run the doctor verb
如果采样停止:首先检查 ~/Library/Logs/<project>/stderr.log。
可选:隔离的单应用监控器
要监控某个应用(例如出现内存泄漏的 Electron IDE),请添加一个独立的可选采集器,并为其配置专属的架构、安装脚本和 ADR。通过 bundle 路径查找该应用的进程(绝不要按名称子串查找),使用 proc_pid_rusage/proc_pidinfo 读取每个进程的 CPU/RSS/磁盘 IO——开销低且无需 root——也绝不要在循环中启动 ps(这本身就是已知的卡顿来源)。