ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Codex 日志写爆 SSD 的 TRACE 大 bug:把 SQLite 写入改到 TaoToken 前先看这份排查清单

Codex 日志写爆 SSD 的 TRACE 大 bug:把 SQLite 写入改到 TaoToken 前先看这份排查清单 1. Codex 日志写爆 SSD 的 TRACE 大 bug 到底怎么回事Codex 日志写爆 SSD 的 TRACE 大 bug本质上是本地 SQLite 数据库在高频写入 TRACE 级别诊断日志导致 SSD 在文件大小几乎不变的情况下持续承受写入压力。如果你正在用 Codex 做日常编码、跑 Agent 任务或者把它挂在后台长时间运行这个问题就值得你花十分钟排查一遍。它不会立刻把硬盘写坏但会让你的 SSD 在不知不觉中消耗 TBW 寿命尤其是新机器、新硬盘提前止损比事后换盘划算得多。我先把场景说清楚。Codex 在本机维护状态和日志时会用到这样一个文件~/.codex/logs_2.sqlite配套的还有 WAL 文件~/.codex/logs_2.sqlite-wal这个 SQLite 数据库里有一张logs表用来存 Codex 自己的内部诊断信息。问题出在日志级别上当 TRACE 级别被大量开启时网络连接状态、websocket 收发、SSE/HTTP 客户端内部事件、MCP 连接管理、内部调试信息都会被写进去。这些内容对普通用户几乎没有价值但写入频率可能非常高。更隐蔽的是 SQLite 的写入机制。你打开 Finder 或资源管理器看logs_2.sqlite可能只有两三百 MB看起来没怎么涨。但 SQLite 在 WAL 模式下会不断追加、合并、刷盘实际落到 SSD 上的写入量远大于文件大小的变化。也就是说文件大小不变不代表硬盘没被写。这是很多人误判的地方。从三个角度定位烧盘根因第一日志级别。TRACE 是最高详细级别通常只在深度排障时开启。如果它长期处于开启状态写入量会明显高于 INFO、WARN、ERROR。第二trigger 触发链。SQLite 里的 trigger 可能在每次插入时触发额外动作如果 trigger 逻辑设计不当会放大写入次数。排查时要看sqlite_master里有哪些 trigger以及它们绑定了哪张表。第三写入频率。单次写入不可怕可怕的是每秒多次。用iostat或系统自带磁盘监控看一段时间内的写入速率就能判断是否异常。适合谁看这份清单三类人一是 Codex 长期挂后台的开发者二是用 MacBook 或轻薄本、SSD 容量和寿命都比较敏感的用户三是已经感觉 Codex 越用越卡、切换会话变慢、风扇发热的人。下面我从排查到处理一步步给你可复制的命令。2. TaoToken 前置准备把模型调用和本地日志治理分开在动手改 SQLite 之前我建议先把模型调用链路和本地日志治理分开看。Codex 的日志问题出在本地 SQLite而模型请求走的是 API 通道两者互不影响。但如果你打算在排查之后继续用 Codex 做长期编码或 Agent 任务把 API 入口配置清楚会让后续维护更省心。TaoToken 在这里的角色是提供统一的模型 API 入口。官网地址是https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentAPI 基础地址是https://taotoken.net/api注意 API 地址后面不加 UTM 参数保持干净。你需要先在控制台创建 API Key然后把它填到 Codex 或相关工具的配置里。控制台入口https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewriteAPI Keys 管理页https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite如果你用的是 Claude Code 这类工具对应的接入说明在https://taotoken.net/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentClaudeCodeAnthropicutm_campaignrewrite模型对话调试页可以用来验证 Key 是否可用https://taotoken.net/model?utm_sourcetaotoken_aicg_blog_endutm_contentmodelutm_campaignrewrite长期编码或 Agent 场景可以看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite为什么要在日志排查文章里提这些因为很多人排查完日志问题后会顺手把 Codex 的模型通道也整理一遍。把 Base URL、Key、Model ID 三件套写清楚能避免后面出现 401 或连接失败时又回头翻配置。这里的三件套是Base URL: https://taotoken.net/api API Key: 在控制台创建后复制 Model ID: 按你使用的模型填写例如 claude-sonnet-4-20250514 或对应编码模型如果你用 CC Switch 或 Cline MCP 管理配置也要把这三项填全。Codex 的auth.json里同样需要确认 Base URL 和 Key 对应。配置本身不复杂复杂的是很多人只填了 Key 忘了 Base URL结果请求打到默认地址上报错信息又不直观。前置准备还包括一件事确认 Codex 当前没有在跑重要任务。改 SQLite 之前最好退出 Codex避免数据库正在被写入时执行 trigger 操作。你可以先用ps看一下进程ps aux | grep -i codex如果有活跃进程先正常退出再继续下面的步骤。这样备份和 trigger 创建都会更稳。3. 可复制配置SQLite PRAGMA、TRACE 开关脚本与 trigger 审计 SQL这一节是核心操作区。我按“先看再改”的顺序来每一步都给可复制的命令。你不需要全部执行按自己的情况选。3.1 先看日志级别分布和估算写入量打开终端先看文件大小ls -lh ~/.codex/logs_2.sqlite ~/.codex/logs_2.sqlite-wal然后看日志级别分布sqlite3 ~/.codex/logs_2.sqlite SELECT level, COUNT(*) FROM logs GROUP BY level ORDER BY COUNT(*) DESC;如果输出里 TRACE 占大头比如TRACE|128430 INFO|3200 DEBUG|890 WARN|120 ERROR|15那基本可以确认问题特征。接着看估算写入量sqlite3 ~/.codex/logs_2.sqlite SELECT level, COUNT(*), SUM(estimated_bytes) FROM logs GROUP BY level ORDER BY SUM(estimated_bytes) DESC;如果 TRACE 的SUM(estimated_bytes)明显高于其他级别说明它贡献了主要写入。再观察一分钟是否还在增长sqlite3 ~/.codex/logs_2.sqlite SELECT COUNT(*), SUM(estimated_bytes) FROM logs; sleep 60 sqlite3 ~/.codex/logs_2.sqlite SELECT COUNT(*), SUM(estimated_bytes) FROM logs;两次结果如果差异明显说明日志仍在持续写入。3.2 审计现有 trigger在加自己的 trigger 之前先看数据库里已经有哪些 triggersqlite3 ~/.codex/logs_2.sqlite SELECT type, name, tbl_name, sql FROM sqlite_master WHERE typetrigger;这一步很重要。如果已经有 trigger 绑在logs表上你要先理解它的逻辑避免叠加后出现意外行为。审计 SQL 也可以顺便看表结构sqlite3 ~/.codex/logs_2.sqlite .schema logs3.3 备份数据库改之前先备份这是习惯问题sqlite3 ~/.codex/logs_2.sqlite .backup $HOME/.codex/logs_2.sqlite.bak-before-block-log-inserts备份文件大概和原文件同大小。确认几天没问题后可以删掉。3.4 创建阻止插入的 trigger这是最推荐普通用户用的方案sqlite3 ~/.codex/logs_2.sqlite CREATE TRIGGER IF NOT EXISTS block_log_inserts BEFORE INSERT ON logs BEGIN SELECT RAISE(IGNORE); END;它的作用是Codex 想往logs表插入时SQLite 直接忽略这次插入。不影响项目代码、不影响 Git、不影响 Codex 读写文件和跑命令。代价是本机少了内部诊断日志对普通用户影响很小。3.5 SQLite PRAGMA 配置参考如果你希望进一步控制 SQLite 的写入行为可以在连接时设置 PRAGMA。注意这些设置要在 Codex 打开数据库之前生效或者通过 SQLite 命令行临时查看sqlite3 ~/.codex/logs_2.sqlite PRAGMA journal_mode; sqlite3 ~/.codex/logs_2.sqlite PRAGMA synchronous; sqlite3 ~/.codex/logs_2.sqlite PRAGMA wal_autocheckpoint;常见的写入优化方向包括把journal_mode设为WALCodex 默认可能已是、把synchronous设为NORMAL、调整wal_autocheckpoint频率。但我不建议你直接改 Codex 的数据库 PRAGMA因为 Codex 自己管理连接参数外部改动可能被覆盖。更稳的做法还是用 trigger 挡住无意义插入。3.6 TRACE 开关脚本如果你不想直接加 trigger而是想先降低 TRACE 级别可以写一个检查脚本。Codex 的日志级别通常由环境变量或配置文件控制具体名称随版本变化。你可以先查当前环境env | grep -i -E codex|log|trace如果找到类似CODEX_LOG_LEVEL或RUST_LOG的变量可以临时调整export CODEX_LOG_LEVELinfo或者在启动 Codex 时带上CODEX_LOG_LEVELinfo codex这样 TRACE 日志会减少但已经写入的历史数据还在。所以脚本方案适合“预防”trigger 方案适合“止损”两者可以配合。3.7 用 iostat 验证写入量在 Linux 上可以用iostat看磁盘写入iostat -dx 2 5重点看w/s每秒写次数和wkB/s每秒写入 KB。在 macOS 上可以用iostat -d -w 2 -c 5或者用活动监视器的磁盘标签页观察。记录加 trigger 前后的数值对比会很明显。4. 验证请求与成功结果确认日志不再增长改完之后必须验证否则你不知道 trigger 是否真的生效。4.1 确认 trigger 存在sqlite3 ~/.codex/logs_2.sqlite SELECT type, name, tbl_name, sql FROM sqlite_master WHERE typetrigger;如果看到trigger|block_log_inserts|logs|CREATE TRIGGER block_log_inserts BEFORE INSERT ON logs BEGIN SELECT RAISE(IGNORE); END;说明 trigger 已创建。4.2 观察一分钟行数变化sqlite3 ~/.codex/logs_2.sqlite SELECT COUNT(*), SUM(estimated_bytes) FROM logs; sleep 60 sqlite3 ~/.codex/logs_2.sqlite SELECT COUNT(*), SUM(estimated_bytes) FROM logs;如果两次结果完全一样说明插入已经被挡住。如果还在增长检查是不是有别的表或别的数据库文件在写。4.3 用 iostat 对比前后写入量在加 trigger 前记录一次iostat -dx 2 5 | grep -E Device|sd|nvme加 trigger 后再记录一次。正常情况下wkB/s会明显下降。我实测下来TRACE 占大头时写入速率可能到每秒几百 KB 甚至更高挡住之后会回落到接近空闲水平。4.4 验证 Codex 功能正常退出并重新打开 Codex跑一个简单任务比如让它读一个文件、改一行代码、执行一条命令。确认这些功能不受影响。trigger 只挡logs表的插入不影响 Codex 的核心逻辑。4.5 检查 WAL 文件是否稳定ls -lh ~/.codex/logs_2.sqlite-wal观察一段时间如果 WAL 文件不再持续变大说明写入压力已经缓解。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth这一节对照真实报错来排查。很多问题其实不在 SQLite而在模型通道配置上。5.1 401 Unauthorized如果你在验证模型请求时看到 401先检查三件套Base URL: https://taotoken.net/api API Key: 控制台创建后复制注意不要有多余空格 Model ID: 按实际使用的模型填写常见原因是 Key 没填、Key 过期、或者 Base URL 写成了带路径的地址。API 地址就是https://taotoken.net/api不要在后面加/v1之外的额外路径除非文档明确说明。5.2 local proxy failed这个报错通常出现在本地代理配置上。检查你的环境变量env | grep -i proxy如果有HTTP_PROXY或HTTPS_PROXY指向一个不可用的地址请求会失败。临时清掉unset HTTP_PROXY HTTPS_PROXY然后重试。注意这里说的是本地环境变量清理不涉及任何网络工具。5.3 reading choices 报错这个错误一般出现在响应解析阶段说明返回结构不符合预期。可能原因Model ID 填错、Base URL 指向了不兼容的端点、或者请求体格式不对。先用模型对话页验证 Key 和模型是否可用https://taotoken.net/model?utm_sourcetaotoken_aicg_blog_endutm_contentmodelutm_campaignrewrite如果那边正常说明是本地配置问题。5.4 OAuth 相关报错如果你用 Claude Code 或类似工具OAuth 流程可能涉及回调地址。检查auth.json或对应配置文件里的字段是否完整。CC Switch、Cline MCP、Codex auth.json 这三类配置都要写全 Base URL、Key、Model ID。缺一项就可能在 OAuth 或 token 刷新时报错。5.5 trigger 没生效如果加了 trigger 但日志还在涨检查sqlite3 ~/.codex/logs_2.sqlite SELECT name FROM sqlite_master WHERE typetrigger;确认block_log_inserts在列表里。如果不在可能是数据库路径不对或者 Codex 用了另一个数据库文件。用find找一下find ~/.codex -name *.sqlite* -maxdepth 35.6 恢复原状想移除 triggersqlite3 ~/.codex/logs_2.sqlite DROP TRIGGER IF EXISTS block_log_inserts;想恢复备份cp ~/.codex/logs_2.sqlite.bak-before-block-log-inserts ~/.codex/logs_2.sqlite恢复前先退出 Codex避免数据库正在被使用。6. 语义一致 CTA排查完日志顺手把模型通道也理清日志问题处理完之后如果你还在用 Codex 做长期编码或 Agent 任务建议把模型通道也整理一遍。排障和接入相关的入口在这里API Keys 管理https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite验证模型是否可用https://taotoken.net/model?utm_sourcetaotoken_aicg_blog_endutm_contentmodelutm_campaignrewrite长期编码或 Agent 场景https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite控制台https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewriteClaude Code 接入https://taotoken.net/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentClaudeCodeAnthropicutm_campaignrewrite最后给你一个实用建议把备份文件保留三到五天确认 Codex 日常使用没有异常后再删。trigger 方案成本很低但它是直接改数据库留个回退点心里踏实。另外如果你有多台机器每台都按“先看级别分布、再备份、再加 trigger、最后用 iostat 验证”的顺序走一遍十分钟能搞定一台。SSD 寿命这件事平时感觉不到等感觉到就晚了。
返回列表