这个问题已经存在很久了,官方到现在仍然没有彻底修复。
Powered by Codex & OWL
Version 26.707.62119
我是 macOS 上的 Codex / ChatGPT 桌面端,打开以后,它会把大量本地运行日志写进 ~/.codex/logs_2.sqlite,同时不断更新 SQLite 的 WAL 文件。多次升级以后,这个现象还在。RUST_LOG=warn、关闭 analytics 这些办法,我这边都没有稳定挡住 SQLite 里的 TRACE 日志落盘。
其他操作系统按照文中方法自己测试一下,估计大概率存在。
我最后采用的办法是:运行时把日志库放到 RAM disk,退出后再 checkpoint、裁剪、压缩、回写到默认位置。它不是官方修复,但至少能把高频写入从 SSD 挪走。
问题
我机器上打开日志库的进程是桌面版Codex / ChatGPT:
/Applications/ChatGPT.app/.../codex app-server
相关文件在这里:
~/.codex/logs_2.sqlite
~/.codex/logs_2.sqlite-wal
~/.codex/logs_2.sqlite-shm
logs_2.sqlite 是主库,-wal 是 SQLite write-ahead log,-shm 是辅助文件。正常应用写一点诊断日志没什么。麻烦在于 Codex 会把很细的 TRACE 级别内容也持久化下来,包括 HTTP transport、streaming、WebSocket/SSE、token delta、工具调用等。
我见过比较夸张的情况:
logs_2.sqlite: 2GB+
最近 60 秒:
TRACE 900+
INFO 80+
DEBUG 50+
还有单条日志几 MB 的情况:
target: codex_http_client::transport
level: TRACE
single row: 2MB 到 7MB+
所以有时候你执行了 VACUUM,文件还是不怎么变小,不一定是压缩没生效。可能只是最近保留下来的 TRACE 本身就很大。
影响
第一是 SSD 写入和寿命。SQLite WAL 本身没问题,但 TRACE 太密,logs_2.sqlite-wal 就会长期活跃。SSD 的寿命通常按 TBW,也就是总写入字节数来估算。这个问题不会让 SSD 立刻坏掉,但它会制造额外写入,长期挂着 Codex 时就在慢慢消耗写入额度。写入速度越高、开机时间越长、SSD 本身 TBW 越低,越不值得忽视。
这里容易被低估的一点是:你看到的 logs_2.sqlite 只有几 GB,不代表实际写入也只有几 GB。SQLite WAL、checkpoint、清理、重写、文件系统元数据、备份和索引都可能放大真实写入量。尤其是 TRACE 日志持续进来又被清理时,文件大小可能变化不大,但 SSD 仍然在忙。
第二是空间。logs_2.sqlite 可以滚到几百 MB,甚至几个 GB。如果 WAL 没及时 checkpoint/truncate,-wal 也会一起占空间。
第三是机器负担。持续 SQLite 写入会带来 I/O、CPU wakeup、文件系统事件、备份和索引干扰。笔记本上可能表现为发热、电量掉得快,或者磁盘活动看起来一直停不下来。即使 SSD 寿命不是你最担心的点,这些后台小写入也会让机器没那么安静。
第四是日志内容。TRACE 日志不是你的会话正文,但里面可能有路径、请求片段、响应片段、工具调用和上下文摘要。排查问题时可以看,不要随便把整个 logs_2.sqlite 发出去。
让 Codex 自己测,也让它自己修
可以直接复制:
请帮我检测并修复 Codex 的高频 SQLite 日志写盘问题。默认日志库是 ~/.codex/logs_2.sqlite,包含 -wal 和 -shm。
先检测问题是否存在:
1. 显示 ~/.codex/logs_2.sqlite、-wal、-shm 的大小。
2. 运行 lsof ~/.codex/logs_2.sqlite ~/.codex/logs_2.sqlite-wal ~/.codex/logs_2.sqlite-shm,确认是否有 Codex 正在打开。
3. 做 15 秒采样:比较日志库和 WAL 的 mtime/大小变化,并查询 logs 表的 count(*)、max(id)、TRACE/DEBUG/INFO 数量变化。
4. 查询最近 60 秒各 level 的日志数量。
5. 查询 estimated_bytes 最大的 20 条日志,看看是否是 TRACE / codex_http_client::transport。
如果问题不存在,只说明检测结果,不要改文件。
如果问题存在,且当前没有活跃 SQLite 句柄,请把 Codex 的高频 SQLite 日志写入改成 RAM disk 往返模式,目标是减少 SSD 写入,同时在退出 Codex 后把日志安全复制回默认位置。如果当前有活跃句柄,不要改文件,告诉我先退出 Codex,再继续。
要求:
1. 默认日志库是 ~/.codex/logs_2.sqlite,包含 -wal 和 -shm。
2. 不要热迁移活跃 SQLite。如果 lsof ~/.codex/logs_2.sqlite ~/.codex/logs_2.sqlite-wal ~/.codex/logs_2.sqlite-shm 显示 Codex 正在使用,脚本必须拒绝迁移,并提示我先退出 Codex。
3. 写一个脚本 ~/.codex/bin/codex-logs-ramdisk,支持:
- status:显示默认日志路径是否为 symlink、RAM disk 是否挂载、是否有活跃句柄。
- apply:创建 RAM disk,把现有 ~/.codex/logs_2.sqlite checkpoint 后复制到 RAM disk,再把默认路径替换成指向 RAM disk 的 symlink。
- restore / flush:在 Codex 退出后,对 RAM disk 里的 SQLite 执行 PRAGMA wal_checkpoint(TRUNCATE),裁剪旧日志,VACUUM INTO 压缩,复制回 ~/.codex/logs_2.sqlite,恢复成普通磁盘文件,并卸载 RAM disk。
- compact:在 Codex 完全退出且默认库是普通磁盘文件时,checkpoint、裁剪旧日志、VACUUM INTO 压缩。若有活跃句柄必须拒绝。
- run:执行 apply,打开真正的 /Applications/ChatGPT.app 或 /Applications/Codex.app,等待它退出,然后自动 restore。
- watch:等待日志库被打开过,再等待关闭,然后自动 restore 一次。
4. RAM disk 默认 3072 MiB,卷名默认 CodexLogsRam,允许用环境变量 CODEX_LOGS_RAMDISK_MB 和 CODEX_LOGS_RAMDISK_NAME 覆盖。
5. 日志默认只保留最近 1 天,允许用 CODEX_LOGS_RETENTION_DAYS 覆盖;设为 0 时只压缩,不删除 logs 表记录。
6. 在 ~/.codex/config.toml 中加入:
[analytics]
enabled = false
如果已有 [analytics],只设置或合并 enabled = false,不要破坏其它配置。
7. 创建一个包装 App:~/Applications/Codex RAM Logs.app。
- 它不要修改原始 /Applications/ChatGPT.app 或 /Applications/Codex.app。
- 可复用原 App 的 electron.icns 图标。
- 它的可执行脚本调用 ~/.codex/bin/codex-logs-ramdisk run。
- stdout/stderr 写到 ~/.codex/codex-ram-logs-wrapper.log。
- bundle id 可用 local.<username>.codex-ram-logs。
8. 验证:
- zsh -n ~/.codex/bin/codex-logs-ramdisk 通过。
- plutil -lint ~/Applications/Codex\ RAM\ Logs.app/Contents/Info.plist 通过。
- 用临时 CODEX_HOME 和测试 RAM disk 做 roundtrip:磁盘旧日志 -> RAM -> 新写入 -> restore 回磁盘,确认行数和内容都保留。
- 当前 Codex 活跃时,apply、restore、compact 应安全拒绝,不能移动或压缩正在打开的库。
- 启动后用 lsof 确认 Codex 打开的是 /Volumes/CodexLogsRam/logs_2.sqlite,而不是 SSD 上的 ~/.codex/logs_2.sqlite。
9. 最终说明以后应打开 Codex RAM Logs.app,退出 Codex 时用 Cmd+Q。脚本会等进程真正退出后再 restore。重启或重新启动后,我会再问“问题修复了吗?再检测一下”,你需要用 status、lsof 和 15 秒采样确认写入是否已经落到 RAM disk。
这段 prompt 的重点不是让 Codex 盲目改文件,而是让它按顺序来:先测,能安全修就修,不能安全修就停。lsof 里还有活跃句柄时,不要硬来。
这段 prompt 是针对MacOS的,你可以让Codex采用这样策略适配到你的操作系统。
修复完成后,需要重启,然后再问它问题修复了吗就可以。
务必让 Codex 教会你怎么用它写的脚本,主要是如何打开和关闭 Codex,避免日志丢失(其实这些日志也没啥,丢了也就丢了)。
最终效果:RAM disk 往返
我的目标不是让 Codex 找不到日志路径,而是让它照常写,只是写到 RAM 里。
运行期间:
~/.codex/logs_2.sqlite -> /Volumes/CodexLogsRam/logs_2.sqlite
退出后:
/Volumes/CodexLogsRam/logs_2.sqlite
-> checkpoint
-> 删除过期日志
-> VACUUM INTO 压缩
-> 回写 ~/.codex/logs_2.sqlite
-> 卸载 RAM disk
我给这个方案留了三条底线:
- 不碰正在使用的 SQLite。
- RAM disk 里的东西退出后要回写。
- 旧 TRACE 默认裁剪掉,不让本地诊断日志越滚越大。
脚本在这里:
~/.codex/bin/codex-logs-ramdisk
常用命令:
~/.codex/bin/codex-logs-ramdisk status
~/.codex/bin/codex-logs-ramdisk apply
~/.codex/bin/codex-logs-ramdisk restore
~/.codex/bin/codex-logs-ramdisk compact
~/.codex/bin/codex-logs-ramdisk run
~/.codex/bin/codex-logs-ramdisk watch
我平时用包装 App 启动:
~/Applications/Codex RAM Logs.app
它实际调用:
~/.codex/bin/codex-logs-ramdisk run
日常流程很简单:
- 从
Codex RAM Logs.app启动。 - 退出时用
Cmd+Q。 - 等脚本自动 restore。
如果 Codex 已经退出,想手动压缩:
~/.codex/bin/codex-logs-ramdisk compact
如果还有活跃句柄,它会停下来,不会硬压正在使用的库。
保留多久的日志
我一开始保留 7 天,后来发现没什么意义。最近 7 天本身就可能有 2GB TRACE。现在默认只保留最近 1 天:
CODEX_LOGS_RETENTION_DAYS=1
我自己的取舍是:这些 TRACE 日志平时价值很低,保留 1 天足够排查刚发生的问题。session 对话不在 logs 表里,裁剪日志不会删掉会话正文。
退出和重启
正常退出用 Cmd+Q。退出后理想状态是:
default_db_symlink: no
ramdisk_mounted: no
active_handles: no
如果退出后还是这样:
default_db_symlink: yes
ramdisk_mounted: yes
active_handles: no
可以手动 restore:
~/.codex/bin/codex-logs-ramdisk restore
系统重启或断电时,RAM disk 里的本次运行日志可能丢失。对我来说可以接受,因为丢的是本地诊断日志,不是 session 对话。
RAM disk 容量要给够。如果 logs_2.sqlite 已经 2GB,就不要只给 1GB。我现在默认用 3072 MiB:
CODEX_LOGS_RAMDISK_MB=3072
如果还不够,可以临时提高:
CODEX_LOGS_RAMDISK_MB=4096 ~/.codex/bin/codex-logs-ramdisk run
最后怎么取舍
这不是一个优雅方案。真正应该做的是官方修:降低持久化日志级别,限制日志体积,加日志轮转或压缩,提供明确配置项。
但在那之前,我不想让本地 TRACE 日志一直打 SSD。RAM disk 往返模式至少把高频写入挪到了内存里,退出时再保守地回写。对我来说,这是现在比较能接受的折中。
参考链接
- GitHub issue: Codex still writes TRACE-level records into ~/.codex/logs_2.sqlite
- GitHub issue: Codex 0.142.0 still writes high-volume TRACE logs into logs_2.sqlite
- GitHub issue: Codex SQLite feedback logs can write ~640 TB/year
- GitHub issue: High-frequency TRACE writes to logs_2.sqlite after restart
- GitHub issue: Codex App writes high-volume TRACE logs to logs_2.sqlite
- OpenAI Community: Disk space exhaustion when leaving Codex open
- WindowsReport: OpenAI Codex CLI bug may wear out SSDs with excessive logging