macOS log 日志查询与系统故障排查指南
macOS 的 log 命令可以查询统一日志,适合追踪 USB 插拔、通知投递、网络变化和应用错误。排查时先记录复现时间,再从应用追到系统服务;不要一开始就在全天日志中搜索 error。
统一日志不是某个可直接 tail -f 的文本文件,应用自己写入的文件日志也未必进入这里。关于存储机制,参见 Apple Logging 文档 (opens new window)。
# 常用命令与过滤方式
先选历史查询或实时观察
| 目的 | 命令 | 说明 |
|---|---|---|
| 查询最近 10 分钟 | /usr/bin/log show --last 10m --style compact | 不加级别参数时不包含 Info、Debug |
| 查询指定应用 | /usr/bin/log show --last 10m --info --predicate 'process == "ExampleApp"' --style compact | 进程名不一定等于 App 显示名称 |
| 实时观察指定应用 | /usr/bin/log stream --level info --predicate 'process == "ExampleApp"' --style compact | 先启动,再复现;按 Control + C 停止 |
| 临时包含 Debug | /usr/bin/log stream --level debug --predicate 'process == "ExampleApp"' --style compact | 用于短时间诊断,输出可能很多 |
| 只看 Error、Fault | /usr/bin/log show --last 10m --predicate 'logType == "error" OR logType == "fault"' --style compact | 容易漏掉正常流程和策略拦截原因 |
| 查进程实际名称 | pgrep -fl 'ExampleApp' | 主进程、helper、内核可能分别记录日志 |
| 本机语法帮助 | /usr/bin/log help show、/usr/bin/log help stream、/usr/bin/log help predicates | 以当前 macOS 提供的参数为准 |
--last 支持 s、m、h、d。show 用 --info --debug 包含更多级别;stream 可用 --level info 或 --level debug。它们控制本次读取范围,不能补回过去没有保存的记录,也不需要为排查永久开启生产 Debug 日志。
按时间缩小范围
将示例时间替换为实际复现时间,先查前后各一两分钟:
/usr/bin/log show \
--start '2026-10-20 10:00:00' \
--end '2026-10-20 10:03:00' \
--info --debug --style compact \
--predicate 'process == "ExampleApp"'
2
3
4
5
对照日志时注意时区。需要跨机器比较时,可加 --timezone UTC,并在问题记录中写明时间与时区。
谓词速查
谓词整体用 Shell 单引号,内部字符串用双引号;[c] 表示忽略大小写。多个 OR 条件与 AND 组合时加括号。
| 过滤对象 | 谓词示例 | 用途 |
|---|---|---|
| 进程名 | process == "ExampleApp" | 查询某个进程 |
| PID | processIdentifier == 1234 | 区分同名进程;重启后 PID 会变化 |
| 子系统 | subsystem == "com.apple.UserNotifications" | 查看通知相关记录,不保证覆盖所有通知服务 |
| 子系统前缀 | subsystem BEGINSWITH "com.apple." | 探索相关组件 |
| 消息关键词 | eventMessage CONTAINS[c] "timeout" | 发现超时记录 |
| 组合条件 | process == "ExampleApp" AND (eventMessage CONTAINS[c] "config" OR eventMessage CONTAINS[c] "reload") | 配置修改、加载链路 |
eventMessage 是常用的消息过滤写法;部分版本的字段帮助列出 composedMessage。若报字段错误,按本机 log help predicates 调整。不要只依赖 App 的 bundle ID 过滤 subsystem,应用可能使用其他子系统,或没有填写子系统。
优先用谓词减少读取量,再用 rg 做二次筛选;rg 需要本机已安装。例如:
/usr/bin/log show --last 5m --info --style compact \
--predicate 'process == "ExampleApp"' \
| rg -i 'config|reload|error|fail'
2
3
二次筛选会丢掉上下文,发现关键行后,应回到对应时间段查看未经过关键词筛选的记录。
# USB 插拔、掉线与识别失败
先看系统当前是否识别设备
| 命令 | 观察内容 | 限制 |
|---|---|---|
system_profiler SPUSBDataType | USB 总线、已识别设备及连接信息 | 当前快照,不是插拔历史;可能包含序列号 |
ioreg -p IOUSB -l -w 0 | IORegistry 中的 USB 设备树及属性 | 属性较多,适合比较插拔前后状态 |
system_profiler SPThunderboltDataType | 雷雳设备及拓扑 | USB-C 是接口形态,不代表连接协议一定是 USB |
diskutil list external | 外接存储设备是否成为磁盘 | 仅用于存储类设备,不能判断键盘、串口等设备 |
实时观察插拔
先执行下面的命令,再连接设备,记录时间;需要拔出存储设备时先正常推出。这里只把 USB 相关子系统和内核中的 USB 关键词当作发现入口,具体字段和输出会随 macOS、硬件及驱动变化。
/usr/bin/log stream --level info --style compact \
--predicate 'subsystem CONTAINS[c] "usb" OR (process == "kernel" AND eventMessage CONTAINS[c] "usb")'
2
若设备变化但输出很少,可在复现的短时间内改为 --level debug。看到相关进程、子系统或设备标识后,用实际值收窄过滤;不要假定所有设备都会记录统一的 attach / detach 消息。
查询刚才的事件
/usr/bin/log show --last 10m --info --debug --style compact \
--predicate 'subsystem CONTAINS[c] "usb" OR (process == "kernel" AND eventMessage CONTAINS[c] "usb")'
2
先保留连接前后的记录,再重点找 disconnect、reset、enumerat、timeout、overcurrent、power 等线索。这些只是检索词,不能凭单条关键词就认定设备损坏;反复重置和掉线也可能来自线材、扩展坞、供电或驱动。
按故障层次判断下一步
| 现象 | 下一步 |
|---|---|
| USB 树里完全没有设备 | 对比插拔前后快照;换支持数据传输的线、换接口、绕过扩展坞,并检查配件连接授权 |
| 能识别,但反复断开、重置 | 对齐日志时间,逐项替换线材、端口、扩展坞和供电条件,比较是否仍复现 |
| USB 树中存在,但应用找不到 | 查对应驱动或应用日志;USB 枚举成功不代表上层协议和权限都正常 |
外接磁盘已在 diskutil 中,Finder 不显示 | 继续检查卷是否挂载、文件系统及 Finder 显示设置,不直接格式化 |
| 出现“USB 设备已停用”或电量不足 | 直连 Mac、换端口,检查有源扩展坞电源;参见 Apple USB 供电排查 (opens new window) |
在支持配件安全控制的 Mac 上,也要留意解锁后是否出现允许连接提示,参见 Apple 配件连接授权说明 (opens new window)。命令能看到什么,与设备连接协议和系统授权有关,不能把“USB 日志为空”直接等同于“没有插拔”。
# 通知不显示:从应用追到系统
案例:ClashX Meta 没有配置变化、系统代理变化提示
现象是 App 已允许通知,但配置修改和代理变化时看不到提醒。排查重点是区分三步:应用是否检测到变化、是否成功提交通知、系统是否允许展示。最终系统日志出现 DND 延迟投递记录,结合手机“工作”专注模式同步到 Mac 的状态,以及关闭后的恢复结果,确认通知被专注模式抑制。
先看 App 侧提交行为:
/usr/bin/log show --last 10m --info --debug --style compact \
--predicate 'process == "ClashX Meta" AND (subsystem CONTAINS[c] "UserNotifications" OR eventMessage CONTAINS[c] "noti")'
2
再看通知服务;这里的消息字段匹配的是日志中出现的应用标识,不等于按进程名查询:
/usr/bin/log show --last 10m --info --debug --style compact \
--predicate '(process == "usernoted" OR process == "NotificationCenter") AND eventMessage CONTAINS[c] "com.metacubex.clashx.meta"'
2
bundle ID 可从应用包内读取;其他 App 替换路径和上述标识即可:
/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' \
'/Applications/ClashX Meta.app/Contents/Info.plist'
2
若按 bundle ID 查不到,不要立即判断没有投递。服务后续行可能只保留通知请求 ID;从应用的提交记录取出该次请求 ID,在相同时间段追踪它,并核实所属 App,避免仅凭短 ID 匹配到别的通知。
下面是简化并脱敏的日志片段,省略时间、线程和随机标识,仅用于解释含义;具体文案随系统版本变化:
Added notification request: [ hasError: 0 hasCompletionHandler: 1 ]
interruptionSuppression: delay delivery
muted by DND suppression: delay
2
3
| 证据 | 可以判断什么 | 不能据此判断什么 |
|---|---|---|
hasError: 0 | 该次通知提交未报错 | 用户一定看到了横幅 |
DND suppression: delay | 该次通知被专注模式/勿扰策略延迟 | App 文件监听失效 |
removeAllDeliveredNotifications | App 请求清除此前已投递通知 | 新通知必然被清除,或这是本次根因 |
| 内核接口响应正常 | 查询时内核仍可用 | 系统代理始终开启、图标从未置灰 |
处理时检查当前专注模式的 App 放行规则,以及是否开启“在设备之间共享”。若不想电脑跟随手机,可在 Mac 关闭共享,再关闭当前专注模式;共享开关与当前模式开关分别处理。参见 Apple 专注模式选项说明 (opens new window)。
代理图标置灰需要另外取证
scutil --proxy
pgrep -fl 'Clash|mihomo'
2
在故障出现时记录系统代理是否仍指向预期本地端口、主进程和内核是否存在。scutil --proxy 是当前状态快照,不能据此追溯哪个进程修改过代理;TUN、PAC 等模式也不能只看 HTTPEnable。专注模式能解释提示缺失,不能单独解释图标为什么置灰。
# 其他常见场景
先用以下范围找到相关进程或子系统,再围绕复现时间收窄;进程名和日志文案可能因版本不同而变化。
| 问题 | 查询入口 | 配合检查 |
|---|---|---|
| 配置保存后没生效 | process == "ExampleApp" AND (eventMessage CONTAINS[c] "config" OR eventMessage CONTAINS[c] "reload") | 当前选中配置、修改时间、解析错误;有检测通知不代表已重载 |
| 网络或代理异常 | process == "configd" OR process == "networkserviceproxy" | scutil --proxy、scutil --dns;这些服务不覆盖所有网络问题 |
| DNS 解析异常 | process == "mDNSResponder" | scutil --dns,结合失败请求时间,留意域名可能被隐去 |
| 睡眠后设备掉线 | process == "powerd" | pmset -g log,比较睡眠、唤醒与设备掉线时间 |
| 蓝牙断连 | process == "bluetoothd" | 先复现,记录设备和连接方式,不直接推断无线干扰 |
| 应用退出或崩溃 | process == "ExampleApp" OR process == "ReportCrash" | 在“控制台”查看崩溃报告;统一日志不能替代崩溃栈 |
# 留存证据与排查顺序
保存可回查的记录
文本输出保留时间戳,适合对照操作步骤。下面只导出指定应用最近 5 分钟的日志:
/usr/bin/log show --last 5m --info --debug --style compact \
--predicate 'process == "ExampleApp"' > ./app-diagnostic.txt
2
需要保留可重新筛选的系统日志时,再收集短时间归档;此操作可能需要管理员权限,且范围比单应用文本更大:
sudo /usr/bin/log collect --last 10m --output ./incident.logarchive
/usr/bin/log show --archive ./incident.logarchive \
--info --debug --style compact \
--predicate 'process == "ExampleApp"'
2
3
4
日志分享前先脱敏
系统日志、USB 快照和归档可能包含用户名、个人路径、设备序列号、网络地址、文件名、请求 URL 或凭据。原始采集文件只在本地保留,不提交到博客仓库;分享时另取必要片段,将标识替换成一致的占位符,同时保留时间顺序和错误信息。系统自动生成的 <private> 不代表整份日志已完成隐私清理。
遇到问题按这个顺序走
- 记录现象、准确时间、时区及触发动作,先保留现场。
- 查当前状态:进程、设备树、系统代理等,区分 App 界面与实际系统状态。
- 开始
log stream,只复现一次;保存故障前后的时间窗口。 - 先查应用,再追到它依赖的系统服务,用时间与请求 ID 对齐记录。
- 根据证据一次改变一个条件,例如关闭专注模式或更换 USB 线,再重复同样动作验证。
- 记录“已确认的原因”和“尚未解释的现象”,不要让一个结论覆盖所有症状。
| 查询障碍 | 处理方式 |
|---|---|
| 没有结果 | 依次检查时间、进程名、过滤条件和级别;短时实时复现,必要时改查应用文件日志 |
| 输出太多 | 缩短到几分钟,优先按进程、子系统过滤,再加关键词 |
| 提示权限不足 | 在本机终端对确有权限要求的读取命令加 sudo;它不会恢复未保存或已脱敏的历史字段 |
提示 Cannot run while sandboxed | 由执行环境授予相应权限,或在普通本机终端运行;不能简单认定需要关闭系统安全功能 |
只有 <private> | 用仍可见的时间、进程、请求 ID 关联,不把隐藏字段当作空值 |
| Info/Debug 历史缺失 | 提高查询级别不会补回记录;先开实时观察再复现 |
| 想用图形界面 | 打开“控制台(Console)”,选择本机并开始流传输,再搜索进程或消息 |
参数参考以本机 /usr/bin/log help、man log 为准;统一日志概念参见 Apple Logging (opens new window)。