Skip to content

logs_2.sqlite TRACE churn: root cause (payload callsites bypass the sink filter) and minimal fix #31542

Description

@Gloridust

Summary

There is a large cluster of open reports about ~/.codex/logs_2.sqlite (and its WAL) growing and being written to continuously at TRACE, even with RUST_LOG=warn (#17320, #24275, #28997, #29463, #29674, #30236, #30431, #30780, #31111, #31132, #31142, #31478, and ~15 more). They are all the same defect. This issue is not another repro — it's the root cause and a minimal fix, reproduced against current main (f1affbac5e).

Root cause

Two callsites serialize model payloads into a log event on the hot path, both guarded by enabled!(Level::TRACE):

  • codex-rs/http-client/src/transport.rs:99 and :131 — the full request body (the entire prompt context, tool outputs and images included) on every model call.
  • codex-rs/codex-api/src/sse/responses.rs:530 — every streamed SSE event's payload, one event per delta during a turn.

Those enabled!(TRACE) guards were written on the assumption that TRACE is off in production. It isn't: the SQLite log sink installed by both the TUI and the app-server persists TRACE for every target. See codex_state::log_db::default_filter() in codex-rs/state/src/log_db.rs — Targets::new().with_default(LevelFilter::TRACE) with only a handful of targets turned off. So the guard always passes and both payloads get serialized and written.

This is also why RUST_LOG=warn does nothing (#29463, #30236, #30780, #31111): RUST_LOG filters the stderr layer only. The sink has its own filter and ignores it. The level is effectively hardcoded (#31132).

Impact, measured

On a real ~/.codex:

  • 87% of retained log content (33 of 38 MiB) came from the transport target alone, with individual rows up to 4 MiB.
  • The sink enforces a 10 MiB per-partition retention budget. A single 4 MiB request-body row eats most of that budget in one write and evicts everything else — so this defect also degrades what /feedback can attach, on top of the disk churn.
  • Independent samples in Codex desktop writes high-frequency TRACE logs to ~/.codex/logs_2.sqlite despite RUST_LOG=warn #31111 show 25–41 rows/sec written during normal interactive use, 97% of them TRACE.

Separate but adjacent: logs_2.sqlite is opened with auto_vacuum=INCREMENTAL (codex-rs/state/src/runtime.rs), but nothing ever runs PRAGMA incremental_vacuum. Pages freed by retention and per-partition pruning stay on the freelist and the file never shrinks below its high-water mark. My logs_2.sqlite was 72 MB with 42% of its pages (30 MB) on the freelist. This is the mechanism behind #28997 and #30431.

Proposed fix

Both changes are small and follow the exact pattern of prior noise-filtering fixes (#26396, #29457, #29599):

  1. Cap codex_http_client::transport and codex_api::sse at DEBUG in log_db::default_filter(). Because no installed sink then enables those targets at TRACE, the enabled!(TRACE) callsites are disabled outright — the multi-MB serialization is skipped, not just the write. RUST_LOG=trace on stderr still works for local debugging.
  2. Run a capped PRAGMA incremental_vacuum(2048) (8 MiB at the default page size) from logs startup maintenance, between the retention delete and the passive WAL checkpoint, so oversized files converge over a few restarts without a large write burst.

I've implemented and tested both (just test -p codex-state, 153 passed):

Per the contribution guidelines I'm not opening a PR unsolicited — happy to if the team wants either or both.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    CLIIssues related to the Codex CLITUIIssues related to the terminal user interface: text input, menus and dialogs, and terminal displaybugSomething isn't workingperformance

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions