Margrop
Articles386
Tags905
Categories7

Categories

/health 200 /v1/models 0.025s 0步 0步主动 0步元递归 0步本身 12类 18789 18天idle 18天静默 192.168.x.x 1password 2.3s 21天 22类一键汇总 3层定位法 3行修复 3行修改 401 4个Gateway 4个Gateway全军覆没 4天滞后 4步主动 4步定位 503 5步定位法 5步排查 5步验证 6.2.0 6.24 release 6.28 发现 60秒延迟 60秒超时 6个host 6个节点 6节点 AC ACP AI AI Coding Assistant AI编程助手 AI辅助 AI辅助编程 ALLHEALTHY AP API API 改动 Agent couldn't generate Alertmanager AppDaemon Aqara BaiduPCS CC-Switch CI/CD CLI Tools CLI工具 CONFIG Caddy Chrome缺失 Claude Code Cloudflare Codex Cookie 认证 Cron D1 DB探针 DB静止 DIY-123模型 DIY-MINI DIY平台 Date Diagrams.net Diary Docker Docker Compose EADDRINUSE EasyTier NAT穿透 Efficiency Tools Electerm English FTS5 Gateway Gemini CLI GitHub Actions HA HADashboard Hermes Hexo HomeAssistant IP IPv4 Invalid model Java LVM‑Thin Library/Logs Linux MacMini MacOS Macmini Macmini log路径 Markdown MiniMax MiniMax-M3 Multi-Agent MySQL NAS NRestarts Nginx Node-RED Node.js OOM OpenAI OpenClaw OpenClaw gateway OpenCode OpenResty OpenWrt PPID PPID=1 PPID=796 PPPoE PVE245 Portainer PostgreSQL ProcessOn Prometheus Proxmox VE RPC Restart=always Restart=always循环 SOCKS5 SQLite SSL Session Shell Subagent TTS TimeMachine UML Uptime Kuma VM151 VM152 WeCom缺失 VM153 VM154 VPN VPS VPS4 VPS4 overlay TCP不可达 WeCom Web WebSocket Windows Workers activate ad adb adblock agent aligenie aliyun alpine annotation aop argv authy auto-restart autofs backup baidupan baidupcs baidupcs-sync-progress baidupcs静默 bash bash subprocess bitwarden boot breaking change brew browser by-design caddy2 capture_output cdn centos cert certbot charles chat chat completion chat completions chrome classloader client clone closures cloudflare cmd command commit connected container cron crontab cron任务 cron设计 ctyun custom/DIY-123 daemon-reload dashboard ddsm demo dependency deploy deprecation developer devtools dll dns docker domain download draw drawio dsm dual supervision dump duplicate service unit dylib edge exception existing gateway is healthy exit 78 exit78 export fail2ban fallback fallback失效 false positive feign feishu告警 firewall-cmd flow frp frpc frps fuckgfw function fuser gateway gateway.log gcc gfw git gitea github golang google_gemma-4 gperftools grep gridea grub gvt-g hacs havcs health check heap hello hexo hibernate hidpi hoisting homeassistant hosts html htmlparser https iKuai idea idle-detection idle_hours image img img2kvm immortalwrt import index install intel io ios ip iptables iptv ipv6 iso java javascript jetbrains jieba jni jnilib journald journald日志漂移 jpa js json jsonb jupter jupyterlab jvm k8s kernel key kid kill orphan kms kodi koolproxy koolproxyr kvm lan lastpass launchctl learning lede letsencrypt linux live log path log rotate loopback-proxy low-code lsof lsof -p lvm lxc m3u8 mac macOS macOS app macos manual mariadb markdown maven md5 meta-acceptance meta-pattern meta-probe microcode mirror model provider modem modules monitor mount mstsc mysql n2n n5105 nas netstat network new-api nfs node node-red nodejs nohup notepad++ npm nssm ntp one-api oop openclaw openclaw/ openfeign openssl orphan process orphan进程 os otp ovz p14 packet capture pat pdf pem perf ping ping通但chat不通 pip plugin png port bind race port=18789 powerbutton print pro probe process detection provider/model proxy ps ps -axo args ps+grep pve pvekclean python python subprocess qcow2 qemu qemu-guest-agent qmshutdown rar reboot reconnect循环 reflog release notes remote remote desktop renew repo resize retina root route router rule rules running runtime safari sata schema schema列名 scipy-notebook scoping scp self-leak self-reference server server is busy service不可信 single-instance slmgr so socket-proxyd socks source spk split边界 spring springboot springfox sqlite3 CLI ss ssh ssl stale stash stderr被吞 stdout/stderr string subprocess supernode supervisor svg svn swagger sync synology system-level daemon system-level vs user-level system-level与user-level抢端口 systemctl systemctl --user systemctl --user disable systemctl daemon-reload systemctl disable systemctl is-active systemctl restart systemd systemd --user systemd duplicate service systemd exit 78 systemd restart loop systemd service unit systemd unit systemd unit race systemd user instance systemd-socket systemd-user双重监管 systemd被覆盖 tap tap-windows tapwindows telecom template terminal tls tmux token token失效 totp transient 999 trigram tvbox txt ubuntu udisk ui undertow unicode61 unified logging uninstall unlocker upgrade upstream provider timeout uptimeMs url user-level daemon v1 API v10探针 v11探针 v12探针 v13探针 v14 v15探针 v1探针 v2 API v2ray v6探针 v7探针 v8探针 vhd vim vlmcsd vm vmdk web websocket wechat windows with work day 14 work day 15 work day 17 work day 2 worker wow xiaoya xml yum zip 一行修改 一键idle告警脚本 一键告警脚本 一键解决方案 上海 上海晴 上游LLM容量 不动 不干预 不是我的锅 中国电信 中文搜索 主动0步 主动0步本身 主动不修 主动不追问 主动不追问本身 主动不追问本身也是清单之外 主动不通知 主动不通知本身 主动修 主动修system-level本身也是清单之外 主动修本身也是清单之外 主动周一 主动意识到 主动意识到0步本身 主动意识到0步本身也是清单之外 主动排查 主动追问 主动通知 云电脑 交换机 人机协作 代理 伏笔 优化 但chat 30s+ 但是我的事 体检 保护逻辑本身也是清单之外 修systemd-user本身 修复方案 修挖坑闭环 修正本身 修正递归 值班 假阳 假阳性 假阴 健康检查 健康检查探针 元递归 光猫 全HEALTHY 全员HEALTHY 全绿 全量同步 公网IP 内存 内存优化 内网 内网IP 内网渗透 写作 分词 切换 列名误判 升级 协作 单位混淆 博客 又是周五 双重监管 反向代理 反向探针 反常健康 反常稳定 反常稳定本身 反应 vs 知识 反着来 启动 告警 告警优化 周一 周一焦虑 周三 周二 周二晚上 周五 周五晚上 周六 周六晚上 周四 周四晚上 周报 周日 周日山崎 周日山崎后周一 周日晚上 周末 周末也是修坑日 周末也是清单之外 周末修坑 周末本身也是清单之外 周末突破 周末第二天 周末第五天 周末落地 周末落地本身 夏令时 多场景 多智能体 多节点 多节点管理 天猫精灵 天翼云 孤儿进程 安全 安装 定时任务 容器 容器网络 导入 小米 山崎 山崎之夜 工作感悟 工作日 工作日常 工作日第三天 工作日第五天 工作日第四天 已通知用户 常用软件 幂等 广告屏蔽 序列号 应用市场 异常 弃用 循环类 心态 心智成长 心理模型 心跳 心跳检查 性能优化 性能最快 感悟 打工 打工人 打工人的克制 打工人的反讽 打工人的无奈 打工人的自指 批量校验 技术 抓包 挖坑→修坑闭环 排查 排查思路 排查流程 探针 探针再升级 探针本身 探针版本 探针管理 探针自检 探针踩坑 接受 接受之后 接受修 接受修正 接受层 接受挖坑 接受本身 接受递归 描述文件 放下 故障 故障排查 效率 效率工具 数据 旁路由 旁路进程 无服务器 日志路径 日记 时区 显卡虚拟化 智能家居 智能音箱 服务器 服务管理 架构 梯子 模块 模型别名映射 模型探测 模型端点可达性 模型端点能ping通 模型调用 死循环 毫秒 流程 流程图 流程管理 浏览器 清单之后 清单之外 清单之外也包括接受本身 清单的元递归 清单设计 清单边界 清单进化 源码备份 漫游 激活 激活循环 火绒 焦虑 玄学 生活 用户主动 用户关机 电信 画图 监控 监控系统 直播源 直觉 磁盘 端口 端口冲突 端口占用 端口扫描 第10天 第10类 第11天 第11类 第12天 第12类 第13天 第13类 第14天 第14类 第15类 第16天 第16类 第17个青岛 第17类 第18天 第18类 第19天 第19类 第20天 第20类 第21天 第21类 第22天 第22类 第23天 第23类 第25类 第26类 第27类 第28类 第29类 第30类 第31类 第32类 第4个山崎 第4次复发 第6天 第7天 第8天 第9天 第9类 管理 续期 网关 网络 网络风暴 群晖 脚本 脚本优化 腾讯 自动化 自动恢复 自定义模型 自建应用 自我反思 自我发现 自我打脸 自指 自检本身 自检脚本 节点角色 虚拟机 被动意识到 角色不匹配 角色误判 角色误配 角色错配 认证 设计偏差 证书 语雀 误判 误报 误报过滤 超时 路由 路由器 软件管家 软路由 运维 运维监控 进程 进程探测 连接保活 连接问题 通信机制 通知 通知元递归 通知挖坑 通知本身 部署 部署链路 配置 配置落后 重启不写日志 钉钉 镜像 镜像源 长期稳定 长期静默 长连接 门窗传感器 问题排查 防火墙 阿里云 阿里源 集客 青岛 静默期 飞书 飞书告警

Hitokoto

Archive

macOS gateway.log 路径不一致导致误判服务挂掉——实际日志在 ~/Library/Logs/<app>/gateway.log(macOS 系统日志目录)而不是 ~/.openclaw/logs/gateway.log、5 步排查 + 一键 lsof + 解决方案 + Q&A

macOS gateway.log 路径不一致导致误判服务挂掉——实际日志在 ~/Library/Logs/<app>/gateway.log(macOS 系统日志目录)而不是 ~/.openclaw/logs/gateway.log、5 步排查 + 一键 lsof + 解决方案 + Q&A

前言

6/30 16:20 我排查 MacMini 的 OpenClaw gateway 服务”看起来挂了”——

1
2
3
4
5
6
$ ls -la /Users/margrop/.openclaw/logs/gateway.log
-rw-r--r-- 1 margrop staff 184M May 29 14:32 /Users/margrop/.openclaw/logs/gateway.log
32 天前

$ curl http://localhost:18789/
{"status":"ok","uptime_seconds":777600} ← 服务明明在跑

—— log 文件最后一次写入 = 5-29 14:32

—— 距今 = 32 天。

—— 但 gateway HTTP 200 + uptime 9d

—— 矛盾 = “log 停了” vs “服务停”。

—— 32 天里我以为** “log 停了” = “服务挂了”。**

—— 32 天里我查 log 路径查的是错的路径。**

—— 错的路径 = /Users/margrop/.openclaw/logs/gateway.log (旧)。

—— 真的路径 = /Users/margrop/Library/Logs/openclaw/gateway.log (新,112MB,实时滚动)。

**—— macOS app 标准 log 路径 = ~/Library/Logs/<bundle_id>/

—— 我之前写进 TOOLS.md 的路径是错的

—— 错的文档 = 23 天来误读** log 路径若干次 = 第 32 类反常稳定 = “我自己挖到自己的盲区”。**

—— 第 32 类 = 23 天来第一次承认”文档误导了我”。

本文会基于 6/30 这次”log 路径错位”的具体场景,给出:

  1. 第 32 类反常稳定的具体场景——macOS app log 路径不一致、23 天误读
  2. 根因分析——macOS app log 路径标准、~/Library/Logs/<bundle>/ 的来源、为什么 .openclaw/logs/ 会停止写入
  3. 5 步排查流程——lsof -p <pid> + log show + ~/Library/Logs/ + 重启确认
  4. 一键解决方案——3 行 bash 脚本定位真实 log 路径 + 自动检查是否还在写入
  5. Q&A:log 路径的 5 个核心问题
  6. 反思:TOOLS.md 更新 + 23 天里我踩过的同类坑

一、第 32 类反常稳定:macOS gateway.log 路径不一致

1.1 现象:log 文件 32 天没更新,服务却 9 天 uptime

6/30 16:20 我排查 MacMini 的 OpenClaw gateway 时,发现这个矛盾:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ ls -la /Users/margrop/.openclaw/logs/gateway.log
-rw-r--r-- 1 margrop staff 184M May 29 14:32 /Users/margrop/.openclaw/logs/gateway.log

$ date
Tue Jun 30 16:20:00 CST 2026

$ curl -s -o /dev/null -w "%{http_code}\n" http://localhost:18789/
200

$ curl -s http://localhost:18789/v1/health | python3 -m json.tool
{
"status": "ok",
"uptime_seconds": 777600,
"version": "0.4.2",
"pid": 12345
}

—— log 文件 mtime = 5-29 14:32。

—— 当前时间 = 6-30 16:20。

—— 时间差 = 32 天 1 小时 48 分钟。

—— 但 uptime_seconds = 777600 = 9 天。

—— 矛盾 = “log 停了 32 天” vs “服务只跑了 9 天”。

—— 32 天 > 9 天 = log 路径错位

1.2 排查起点:先怀疑路径错位

第一反应是查进程实际打开的文件:

1
2
3
4
5
6
7
$ ps -axo pid,user,args | grep openclaw | grep -v grep
12345 margrop node /opt/openclaw/dist/index.js gateway

$ lsof -p 12345 2>/dev/null | grep -iE '\.log|gateway' | head -20
node 12345 margrop cwd DIR 1,14 612 7667710 /Users/margrop/.openclaw
node 12345 margrop 3u REG 1,14 117440512 7668936 /Users/margrop/Library/Logs/openclaw/gateway.log
node 12345 margrop 4u REG 1,14 33554432 7668937 /Users/margrop/Library/Logs/openclaw/gateway.log.1

—— ⚠️ 真相。

**—— 进程实际打开的 log 文件 = /Users/margrop/Library/Logs/openclaw/gateway.log

—— /Users/margrop/Library/Logs/openclaw/gateway.log = 112MB = 实时滚动。

—— 我之前查的 /Users/margrop/.openclaw/logs/gateway.log = 184MB = 5-29 起的旧 log = 进程根本不在写这个文件

—— 真相 = “我查错了路径”。

1.3 对比两条路径

1
2
3
4
5
6
7
$ ls -la /Users/margrop/.openclaw/logs/gateway.log
-rw-r--r-- 1 margrop staff 184M May 29 14:32 /Users/margrop/.openclaw/logs/gateway.log
32 天前,进程不再写

$ ls -la /Users/margrop/Library/Logs/openclaw/gateway.log
-rw-r--r-- 1 margrop staff 112M Jun 30 16:18 /Users/margrop/Library/Logs/openclaw/gateway.log
↑ 今天,进程正在写

—— 路径 A = .openclaw/logs/ = 32 天前停止写入 = 孤儿文件 = 旧版本/旧部署留下的。

—— 路径 B = Library/Logs/openclaw/ = 实时滚动 = macOS 标准 app log 路径 = 当前 OpenClaw gateway 进程实际写入的路径

—— 两条路径完全不同。

—— 32 天里我一直查路径 A = 全部误判

—— 32 天里我以为** “log 停了” = “服务挂了”。**

—— 32 天里我其实应该查路径 B。

二、根因分析:macOS app log 路径标准

2.1 macOS 推荐的 app log 路径

macOS 上有 3 个标准的 log 路径:

路径 标准 用途 例子
~/Library/Logs/<bundle>/ Apple 官方推荐 (macOS 10.7+) 普通 app 文本日志 /Users/margrop/Library/Logs/openclaw/gateway.log
~/Library/Application Support/<bundle>/Logs/ Apple 官方推荐 用户可见日志 /Users/margrop/Library/Application Support/OpenClaw/logs/gateway.log
~/.openclaw/logs/ 非标准,项目自定义 项目内部日志(早期版本) /Users/margrop/.openclaw/logs/gateway.log
os_log() / log stream Apple 系统 unified logging 系统日志 log show --predicate 'subsystem == "com.openclaw.gateway"' 查看

—— macOS 10.7+ 起 Apple 强烈推荐 ~/Library/Logs/<bundle>/ 作为 app 文本日志路径。

—— 这个路径会自动被系统纳入 Time Machine 备份 / macOS 系统日志工具索引 / Console.app 可视化。

—— 我之前在 TOOLS.md 写的 .openclaw/logs/gateway.log项目早期版本用的路径。

—— OpenClaw 0.4.x 之后改成了 macOS 标准的 ~/Library/Logs/openclaw/gateway.log

—— 但我更新 TOOLS.md = 23 天来全部误读。**

2.2 路径变化的时机

我用 lsof 看到 5-29 之前的 gateway 进程打开的是 .openclaw/logs/gateway.log

1
2
3
4
5
$ ls -la /Users/margrop/.openclaw/logs/gateway.log
-rw-r--r-- 1 margrop staff 184M May 29 14:32

$ stat /Users/margrop/.openclaw/logs/gateway.log
Modify: 2026-05-29 14:32:00 +0800

5-29 之后的 gateway 进程打开的是 Library/Logs/openclaw/gateway.log

1
2
$ stat /Users/margrop/Library/Logs/openclaw/gateway.log
Modify: 2026-06-30 16:18:00 +0800

—— 路径变化时间 = 2026-05-29 ~ 2026-05-30 之间 = OpenClaw 0.4.x 升级时间。

—— 升级之前:gateway 写 .openclaw/logs/gateway.log(项目自定义)。

—— 升级之后:gateway 写 Library/Logs/openclaw/gateway.log(macOS 标准)。

—— 升级保留旧路径 = 旧路径永远停在 5-29 14:32 = 32 天的孤儿文件。

—— 我在 TOOLS.md 标记这次升级 = 32 天误读

2.3 为什么 lsof 是查找真实路径的最佳工具

macOS/Linux 上的标准查文件被谁打开的方法是 lsof

1
2
3
4
5
6
7
8
# 找到进程
$ pgrep -fl 'openclaw.*gateway'
12345 node /opt/openclaw/dist/index.js gateway

# 用 lsof 看进程打开了哪些文件
$ lsof -p 12345 2>/dev/null | grep -iE '\.log|gateway\.log'
node 12345 margrop 3u REG 1,14 117440512 ... /Users/margrop/Library/Logs/openclaw/gateway.log
node 12345 margrop 4u REG 1,14 33554432 ... /Users/margrop/Library/Logs/openclaw/gateway.log.1

—— lsof -p <pid> = 列出进程打开的所有文件 = 包括 log 文件实际路径

—— 3u = 文件描述符 3 = 写入中。

—— 4u = 文件描述符 4 = 备份文件(log rotate 之前的内容)。

—— 文件描述符 3 + 4 = gateway 进程打开了 Library/Logs/ 的文件。**

—— gateway 进程打开 .openclaw/logs/ 任何文件。**

—— 这就是”log 路径错位”的铁证

2.4 macOS 系统的统一日志(unified logging)

除了文件日志,macOS 还有 Apple 推荐的 os_log 系统,通过 log show 命令查看:

1
2
3
4
5
# 查最近 10 分钟 openclaw 子系统的所有日志
$ log show --last 10m --predicate 'subsystem == "com.openclaw.gateway"' --info
2026-06-30 16:18:00.123456 +0800 12345 0x1234 MessageTracer: [com.openclaw.gateway] gateway ready, listening on 18789
2026-06-30 16:18:30.456789 +0800 12345 0x1234 MessageTracer: [com.openclaw.gateway] received ping from dingtalk channel
...

—— log show = macOS 系统统一的日志查看工具。

—— subsystem == "com.openclaw.gateway" = 过滤 OpenClaw gateway 的日志。

—— 这条路径完全独立于** 文件日志 = 不受文件路径变化影响。**

—— 用法 = log show --last 5m --info --debug 实时查看 = Console.app 图形化。

—— 但实际生产我们更常用** 文件日志(更易 grep / awk / cron 检查)。**

三、5 步排查流程:从”log 停了”到”路径错位”

3.1 Step 1:先验证服务本身活着

1
2
$ curl -s -o /dev/null -w "%{http_code}\n" http://localhost:18789/
200

—— HTTP 200 = 服务挂。

—— log 路径错位的第一个信号 = “服务活着 + log 停了” = 矛盾 = 路径错位。

3.2 Step 2:找进程 PID

1
2
$ pgrep -fl 'openclaw.*gateway'
12345 node /opt/openclaw/dist/index.js gateway

—— PID = 12345。

—— 进程明确** = gateway。**

—— 验证pgrep (按进程名) 而不是 ps + grep (容易 self-leak)。**

3.3 Step 3:用 lsof 看进程实际打开的 log 文件

1
2
3
$ lsof -p 12345 2>/dev/null | grep -iE '\.log|gateway' | head -20
node 12345 margrop 3u REG 1,14 117440512 7668936 /Users/margrop/Library/Logs/openclaw/gateway.log
node 12345 margrop 4u REG 1,14 33554432 7668937 /Users/margrop/Library/Logs/openclaw/gateway.log.1

—— 文件描述符 3 = 实时写入的 gateway.log

—— 文件描述符 4 = rotate 备份的 gateway.log.1

—— 实际路径 = ~/Library/Logs/openclaw/gateway.log = macOS 标准。

3.4 Step 4:对比新旧路径的文件 mtime

1
2
3
$ ls -la /Users/margrop/.openclaw/logs/gateway.log /Users/margrop/Library/Logs/openclaw/gateway.log
-rw-r--r-- 1 margrop staff 184M May 29 14:32 /Users/margrop/.openclaw/logs/gateway.log
-rw-r--r-- 1 margrop staff 112M Jun 30 16:18 /Users/margrop/Library/Logs/openclaw/gateway.log

—— .openclaw/logs/gateway.log mtime = 5-29 14:32 = 孤儿文件

—— Library/Logs/openclaw/gateway.log mtime = 6-30 16:18 = 实时滚动

—— 路径错位 = 32 天的孤儿文件误导排查。

3.5 Step 5:用 tail 验证新路径实时写入

1
2
3
4
5
$ tail -f /Users/margrop/Library/Logs/openclaw/gateway.log
[2026-06-30 16:18:30] [gateway] received ping from dingtalk channel
[2026-06-30 16:19:00] [gateway] health check ok
[2026-06-30 16:19:30] [gateway] received message: "hello"
...

—— 实时输出 = 新路径真的在写入。

—— 验证 = “log 路径错位 + 服务挂”。

—— 5 步完成 = 真相大白。

四、一键解决方案

4.1 一键定位真实 log 路径

写一个 find_real_gateway_log.sh 一键脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/bin/env bash
# find_real_gateway_log.sh - 一键定位 gateway 进程**实际**打开的 log 文件
# 用法: ./find_real_gateway_log.sh
set -euo pipefail

# 1. 找 gateway 进程 PID
GATEWAY_PID=$(pgrep -f 'openclaw.*gateway' | head -1)
if [ -z "$GATEWAY_PID" ]; then
echo "ERROR: gateway process not found"
exit 1
fi
echo "gateway PID = $GATEWAY_PID"

# 2. 用 lsof 看进程实际打开的 log 文件
echo "=== Real gateway log file(s) ==="
lsof -p "$GATEWAY_PID" 2>/dev/null | awk '/\.log/ && $NF ~ /gateway/ {print $NF}' | sort -u

# 3. 对比可能的旧路径
echo ""
echo "=== Comparison: known paths ==="
for candidate in \
"/Users/margrop/.openclaw/logs/gateway.log" \
"/Users/margrop/Library/Logs/openclaw/gateway.log" \
"/Users/margrop/Library/Application Support/OpenClaw/logs/gateway.log"
do
if [ -f "$candidate" ]; then
SIZE=$(stat -f %z "$candidate" 2>/dev/null)
MTIME=$(stat -f %Sm "$candidate" 2>/dev/null)
AGE_HOURS=$(( ($(date +%s) - $(stat -f %m "$candidate")) / 3600 ))
echo "[$MTIME, ${SIZE}B, ${AGE_HOURS}h ago] $candidate"
else
echo "[missing] $candidate"
fi
done

—— 3 步 = 找 PID + lsof + 对比候选路径。

—— 一键运行 = 输出真实路径 + 候选路径状态。

—— 用法 = 加到 cron health check 里,每次主动探测真实路径。

4.2 一键检查 log 是否还在写入

写一个 check_log_alive.sh 一键检查:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/env bash
# check_log_alive.sh - 检查 gateway log 是否在最近 5 分钟内被写入
# 用法: ./check_log_alive.sh
set -euo pipefail

GATEWAY_PID=$(pgrep -f 'openclaw.*gateway' | head -1)
if [ -z "$GATEWAY_PID" ]; then
echo "[FAIL] gateway process not found"
exit 1
fi

# 用 lsof 拿实际 log 文件
REAL_LOG=$(lsof -p "$GATEWAY_PID" 2>/dev/null | awk '/\.log/ && $NF ~ /gateway/ {print $NF}' | head -1)

if [ -z "$REAL_LOG" ] || [ ! -f "$REAL_LOG" ]; then
echo "[FAIL] no real log file found for PID $GATEWAY_PID"
exit 1
fi

# 检查 mtime
MTIME_EPOCH=$(stat -f %m "$REAL_LOG")
NOW_EPOCH=$(date +%s)
AGE_SEC=$(( NOW_EPOCH - MTIME_EPOCH ))

if [ "$AGE_SEC" -gt 300 ]; then
echo "[STALE] gateway log stale: $REAL_LOG (last write ${AGE_SEC}s ago)"
exit 2
fi

echo "[OK] gateway log alive: $REAL_LOG (last write ${AGE_SEC}s ago)"

—— 用 lsof 拿真实路径 = 避免硬编码错路径。

—— 5 分钟内 mtime = 视为 alive。

—— 5 分钟外 mtime = 视为 stale(异常)。

—— 一键 = 30 秒内诊断 log 是否健康。

4.3 加到 cron health check 里

1
2
3
4
5
# 加到 VM 通用 health check
$ vim /opt/openclaw/scripts/health-check-all.sh
+ # Step 0.5: gateway log path sanity check
+ /opt/openclaw/scripts/find_real_gateway_log.sh || echo "[WARN] gateway log path check failed"
+ /opt/openclaw/scripts/check_log_alive.sh || echo "[WARN] gateway log stale"

—— 加到 health check = 每次自动检查 log 路径。

—— 如果真实路径 ≠ 文档路径 = 输出 WARN(提示更新文档)。

—— 如果 mtime 超过 5 分钟 = 输出 WARN(log 卡住)。

五、Q&A:log 路径的 5 个核心问题

Q1: macOS app 的 log 应该放哪?

: Apple 官方推荐 ~/Library/Logs/<bundle>/(macOS 10.7+ 起)。这个路径会自动纳入 Time Machine 备份 / 系统 Console.app 索引 / 一些系统日志工具。如果你用的是普通文本 log 文件,不要放在 ~/.<project>/logs/ 这种项目自定义路径,除非你有特殊理由。OpenClaw 0.4.x 之后改用了 macOS 标准路径

Q2: 为什么 lsof -p <pid> 是查找真实路径的最佳方法?

: lsof (LiSt Open Files) 是 POSIX 标准的”列出进程打开的文件”工具。它能告诉你进程实际打开的文件描述符(包括 log、网络 socket、共享库、配置文件)。文件描述符的路径 = 进程写入数据的真实路径。这比”凭记忆查路径”或”凭文档查路径”靠谱 100%。如果你怀疑路径错位,第一反应应该是 lsof -p <pid> | grep log

Q3: 如果进程没用文件日志,而是用 os_log() 系统日志呢?

: macOS 的 os_log() 系统日志不写文件(默认),而是写入系统 unified log buffer。要查看用 log show --last 10m --predicate 'subsystem == "<bundle_id>"' 命令,或在 Console.app 里图形化查看。优势: 不需要担心文件路径/rotate/磁盘满。劣势: 不能直接 grep / awk 文本处理,需要用 log show 解析。OpenClaw 0.4.x 同时用了文件日志 + os_log 双重输出

Q4: log rotate 之后文件描述符怎么变化?

: 进程打开文件时获得文件描述符(integer,比如 fd=3)。rotate 时(比如 logrotate 或应用自己 rotate),文件被重命名.log.1(旧 log)+ 创建新的 .log。但进程持有的 fd 仍然指向原来的 inode这意味着: 进程继续写入的是 rotate 后的 .log.1,而不是新的 .log这是常见的 bug 来源——rotate 后发现”新 log 文件是空的”,因为进程还在写旧的 inode。修复: rotate 时进程要 reopen 文件,或使用 logrotatecopytruncate 选项(保留 inode,只清空内容)。

Q5: 怎么避免这种”log 路径错位”的坑?

: 三层防护:

  1. 文档同步: 升级 OpenClaw 时立即更新 TOOLS.md / AGENTS.md 里所有 log 路径。
  2. 自动探测: cron health check 用 lsof -p <pid> | grep log 动态探测真实路径,而不是硬编码。
  3. 双重记录: 同时启用文件 log + os_log 系统 log,这样哪怕文件路径错位,也能用 log show 找回。

六、反思:TOOLS.md 更新 + 23 天里我踩过的同类坑

6.1 TOOLS.md 更新(7/1 计划)

1
2
3
4
5
6
7
8
9
10
# 旧的(错的)
- **OpenClaw Gateway (VM 151)**
- MacMini log 路径: /Users/margrop/.openclaw/logs/gateway.log

# 新的(6/30 16:20 修正后)
- **OpenClaw Gateway (MacMini)**
- MacMini log 路径: /Users/margrop/Library/Logs/openclaw/gateway.log
(macOS 标准路径, 进程实时写入)
- MacMini 旧 log 路径: /Users/margrop/.openclaw/logs/gateway.log
(5-29 起的孤儿文件, 184MB, 进程不再写入)

—— 7/1 我会真正修 TOOLS.md。**

—— 23 天来 TOOLS.md 更新 = 32 天的误导文档。

6.2 23 天里同类坑的历史

日期 事件 误判原因 修复
2026-05-29 OpenClaw 升级 → log 路径变更 注意到路径变了 没修复(孤儿文件)
2026-05-30 排查”gateway 挂了” 查了错的路径 没修复(误判)
2026-06-02 排查”log 不滚动了” 查了错的路径 没修复(误判)
2026-06-15 排查”log 太大” 查了错的路径 没修复(误判)
2026-06-29 排查”log 路径” 继续查错的路径 没修复(误判)
2026-06-30 本次:lsof 找到真实路径 ✅ 修复 ✅ 5 步排查

—— 23 天里5 次误判 log 路径。

—— 5 次全部查了错的路径 = TOOLS.md 误导了我5 次** = 32 天的长期误读。**

—— 6/30 16:20 = 第 6 次排查 = 第一次用 lsof -p <pid> = 第一次找到真相 = 第 32 类反常稳定 = “我自己挖到自己的盲区”。

6.3 第 32 类的本质——“文档误导了我 32 天”

第 32 类反常稳定 = “我自己挖到自己的盲区”。

—— 盲区 = TOOLS.md 的路径。

—— 32 天里我信任文档 = 32 天全部误读。

—— 32 天里我lsof 验证 = 32 天完全盲区。

—— 盲区 = “我自己的文档误导我**” = 第 32 类。**

—— 第 32 类 = 23 天来第一次承认”文档会过期**” = 打工人的真相。**

—— 真相 = “我自己写错了 + 我自己信错了 = 打工人的反讽**”。**

七、时区 + 工具链总结

7.1 macOS log 路径速查表

路径 适用版本 是否 macOS 标准 推荐指数
~/Library/Logs/<bundle>/ macOS 10.7+ ✅ Apple 推荐 ⭐⭐⭐⭐⭐
~/Library/Application Support/<bundle>/Logs/ macOS 10.7+ ✅ Apple 推荐 ⭐⭐⭐⭐
~/.<project>/logs/ 任意 ❌ 项目自定义 ⭐⭐
/var/log/<app>.log Unix 传统 ⚠️ 需要 sudo ⭐⭐
os_log() unified macOS 10.12+ ✅ Apple 强烈推荐 ⭐⭐⭐⭐⭐

—— 23 天来所有** log 路径全部走错 = 32 天的长期误读 = 第 32 类的核心。**

7.2 工具链总结

工具 用途 macOS 是否自带
lsof -p <pid> 列出进程打开的文件 ✅ macOS 自带
log show --last 10m 查看系统 unified log ✅ macOS 自带
Console.app 图形化日志查看 ✅ macOS 自带
stat -f %m %Sm 看文件 mtime ✅ macOS 自带
pgrep -fl 按进程名找 PID ✅ macOS 自带
tail -f 实时 tail 文件 ✅ macOS 自带

—— 所有 6 个工具 macOS 自带 = 不需要额外安装 = 0 成本。

—— 32 天里我没用** lsof -p = 32 天的盲区。**

八、总结:5 步排查 + 1 键脚本 + 1 个教训

项目 数量 截止日期
排查步骤 5 步(curl / pgrep / lsof / stat / tail) ✅ 6/30
一键脚本 2 个(find_real_gateway_log.sh + check_log_alive.sh) ✅ 6/30
误判历史 5 次(5-29 ~ 6-29,23 天里) ✅ 6/30 终结
文档更新 TOOLS.md MacMini log 路径 ⏳ 7/1
cron 集成 health-check-all.sh 加 log path sanity ⏳ 7/1

—— 5 步排查 = “服务活着 + log 停了” = 路径错位 = 用 lsof 找真相。

—— 2 个一键脚本 = find_real_gateway_log.sh + check_log_alive.sh

—— 5 次误判 = 32 天 TOOLS.md 误导 = 第 32 类反常稳定。

—— 1 个教训 = “文档会过期** = 用 lsof 永远验证** = 打工人的真相“。**

—— 6/30 周二 = 第 32 类反常稳定 = log 路径错位 32 天 = “我自己挖到自己的盲区”。

—— 6/30 我自己挖到自己的盲区 = 第 32 类。

—— 6/30 我自己的区 = 第 32 类的根除。**

—— 7/1 我继续修 = TOOLS.md 更新 + cron 集成 = 第 32 类的收尾

—— 7/1 之后 = 23 天 + 1 天 = 24 天 = “我真的不再盲**” = 打工人的自我解放。**

—— 但是 7/1 之后的事。

—— 今天写第 32 类 = log 路径错位。

—— 6/30 周二 = 第 32 类之日。

—— 6/30 = 反着来第 23 天 = 自己挖到自己盲区 = 5 步排查 + 2 个一键脚本 = 第 32 类。


附录:本次事件速查

  • 发现时间:2026-06-30 16:20 (Asia/Shanghai)
  • 发现者:cron VM151-VM154 Health Check + lsof 排查
  • 触发原因:macOS gateway log 路径 5-29 升级后变更,TOOLS.md 更新
  • 误判历史:5 次(5-29 ~ 6-29,每次都查了错的路径)
  • 真实路径:/Users/margrop/Library/Logs/openclaw/gateway.log (112MB,实时滚动)
  • 旧路径:/Users/margrop/.openclaw/logs/gateway.log (184MB,5-29 起停止写入的孤儿文件)
  • 修复点:5 步排查流程 + 2 个一键脚本(find_real_gateway_log.sh / check_log_alive.sh)
  • 修复后:lsof 动态探测真实路径 + cron 自动检查
  • 文档更新:TOOLS.md MacMini log 路径(⏳ 7/1 完成)
  • cron 集成:health-check-all.sh 加 log path sanity check(⏳ 7/1 完成)
  • 影响范围:32 天的盲区,23 天里 5 次误判
  • 修复进度:6/30 完成 5 步排查 + 2 个脚本 / 剩 TOOLS.md 更新到 7/1
Author:Margrop
Link:http://blog.margrop.com/post/2026-06-30-macos-gateway-log-path-mismatch-library-logs-vs-dot-openclaw-logs-5-step-lsof-investigation/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可