Commit Graph

16 Commits

  • Fix log db batch flush flake (#19959)
    ## Why
    
    The log DB writer batches tracing events before inserting them into
    SQLite, but `tokio::time::interval` produces an immediate first tick.
    That meant the inserter could flush the first accepted log entry before
    `batch_size` was reached, making
    `configured_batch_size_flushes_without_explicit_flush` timing-sensitive
    in CI.
    
    ## What Changed
    
    - Consume the interval's startup tick before entering the inserter loop,
    so interval flushing starts after the configured delay.
    - Remove the test's startup sleep, which was masking the race instead of
    proving the batch-size behavior.
    
    ## Validation
    
    - `cargo test -p codex-state`
    - `cargo test -p codex-state
    configured_batch_size_flushes_without_explicit_flush` passed 3
    consecutive focused runs
    - PR checks passed across `rust-ci`, Bazel, `ci`, `sdk`, `cargo-deny`,
    Codespell, blob-size policy, and CLA
  • Refactor log DB into LogWriter interface (#19234)
    ## Why
    
    This prepares feedback log capture for a future remote app-server hook
    sink without changing the current local SQLite upload path. The
    important boundary is now intentionally small: a log sink is a tracing
    `Layer` that can also flush entries it has accepted.
    
    That keeps the existing SQLite implementation simple while giving the
    upcoming gRPC sink a place to fit beside it. SQLite and gRPC have
    different worker/write semantics, so this PR avoids introducing a shared
    buffered-sink abstraction and instead lets each `LogWriter` own the
    buffering mechanics it needs.
    
    ## What Changed
    
    - Added `LogSinkQueueConfig` with the existing local defaults: queue
    capacity `512`, batch size `128`, and flush interval `2s`.
    - Added `LogDbLayer::start_with_config(...)` while preserving
    `LogDbLayer::start(...)` and `log_db::start(...)` defaults.
    - Introduced the `LogWriter` trait as the minimal shared interface:
    `tracing_subscriber::Layer` plus `flush()`.
    - Made `LogDbLayer` implement `LogWriter`.
    - Kept tracing event formatting inside `LogDbLayer`; it still creates
    one `LogEntry` per tracing event before queueing it for SQLite.
    - Kept normal event capture best-effort and non-blocking via bounded
    `try_send`.
    
    ## Behavior Notes
    
    This does not change the SQLite schema, retention behavior,
    `/feedback/upload`, or Sentry upload behavior. Normal log events still
    drop when the queue is full; explicit `flush()` still waits for queue
    capacity and receiver processing before returning.
    
    ## Verification
    
    - `cargo test -p codex-state log_db`
    - `cargo test -p codex-state`
    - `just fix -p codex-state`
    
    The added tests cover configured batch-size flushing, configured
    interval flushing, queue-full drops, and the flush barrier semantics.
  • fix: windows flake (#18127)
    Fix `sqlite_feedback_logs_match_feedback_formatter_shape` by explicitly
    flushing the async log DB layer before querying SQLite.
  • feat: log db better maintenance (#16330)
    Run a DB clean-up more frequently with an incremental `VACCUM` in it
  • Align SQLite feedback logs with feedback formatter (#13494)
    ## Summary
    - store a pre-rendered `feedback_log_body` in SQLite so `/feedback`
    exports keep span prefixes and structured event fields
    - render SQLite feedback exports with timestamps and level prefixes to
    match the old in-memory feedback formatter, while preserving existing
    trailing newlines
    - count `feedback_log_body` in the SQLite retention budget so structured
    or span-prefixed rows still prune correctly
    - bound `/feedback` row loading in SQL with the retention estimate, then
    apply exact whole-line truncation in Rust so uploads stay capped without
    splitting lines
    
    ## Details
    - add a `feedback_log_body` column to `logs` and backfill it from
    `message` for existing rows
    - capture span names plus formatted span and event fields at write time,
    since SQLite does not retain enough structure to reconstruct the old
    formatter later
    - keep SQLite feedback queries scoped to the requested thread plus
    same-process threadless rows
    - restore a SQL-side cumulative `estimated_bytes` cap for feedback
    export queries so over-retained partitions do not load every matching
    row before truncation
    - add focused formatting coverage for exported feedback lines and parity
    coverage against `tracing_subscriber`
    
    ## Testing
    - cargo test -p codex-state
    - just fix -p codex-state
    - just fmt
    
    codex author: `codex resume 019ca1b0-0ecc-78b1-85eb-6befdd7e4f1f`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Reduce SQLite log retention to 10 days (#13781)
    ## Summary
    - reduce the SQLite-backed log retention window from 90 days to 10 days
    
    ## Testing
    - just fmt
    - cargo test -p codex-state
    
    Co-authored-by: Codex <noreply@openai.com>
  • Move sqlite logs to a dedicated database (#13772)
    ## Summary
    - move sqlite log reads and writes onto a dedicated `logs_1.sqlite`
    database to reduce lock contention with the main state DB
    - add a dedicated logs migrator and route `codex-state-logs` to the new
    database path
    - leave the old `logs` table in the existing state DB untouched for now
    
    ## Testing
    - just fmt
    - cargo test -p codex-state
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • feat: limit number of rows per log (#13763)
    avoid DB explosion. This is a temp solution
  • Add timestamped SQLite /feedback logs without schema changes (#13645)
    ## Summary
    - keep the SQLite schema unchanged (no migrations)
    - add timestamps to SQLite-backed `/feedback` log exports
    - keep the existing SQL-side byte cap behavior and newline handling
    - document the remaining fidelity gap (span prefixes + structured
    fields) with TODOs
    
    ## Details
    - update `query_feedback_logs` to format each exported line as:
      - `YYYY-MM-DDTHH:MM:SS.ffffffZ {level} {message}`
    - continue scoping rows to requested-thread + same-process threadless
    logs
    - continue capping in SQL before returning rows
    - keep the existing fallback behavior unchanged when SQLite returns no
    rows
    - update parity tests to normalize away the new timestamp prefix while
    we still only store `message`
    
    ## Follow-up
    - TODO already in code: persist enough span/event metadata in SQLite to
    reproduce span prefixes and structured fields in `/feedback` exports
    
    ## Testing
    - `cargo test -p codex-state`
    - `just fmt`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • app-server: source /feedback logs from sqlite at trace level (#12969)
    ## Summary
    - write app-server SQLite logs at TRACE level when SQLite is enabled
    - source app-server `/feedback` log attachments from SQLite for the
    requested thread when available
    - flush buffered SQLite log writes before `/feedback` queries them so
    newly emitted events are not lost behind the async inserter
    - include same-process threadless SQLite rows in those `/feedback` logs
    so the attachment matches the process-wide feedback buffer more closely
    - keep the existing in-memory ring buffer fallback unchanged, including
    when the SQLite query returns no rows
    
    ## Details
    - add a byte-bounded `query_feedback_logs` helper in `codex-state` so
    `/feedback` does not fetch all rows before truncating
    - scope SQLite feedback logs to the requested thread plus threadless
    rows from the same `process_uuid`
    - format exported SQLite feedback lines with the log level prefix to
    better match the in-memory feedback formatter
    - add an explicit `LogDbLayer::flush()` control path and await it in
    app-server before querying SQLite for feedback logs
    - pass optional SQLite log bytes through `codex-feedback` as the
    `codex-logs.log` attachment override
    - leave TUI behavior unchanged apart from the updated `upload_feedback`
    call signature
    - add regression coverage for:
      - newest-within-budget ordering
      - excluding oversized newest rows
      - including same-process threadless rows
      - keeping the newest suffix across mixed thread and threadless rows
      - matching the feedback formatter shape aside from span prefixes
      - falling back to the in-memory snapshot when SQLite returns no logs
      - flushing buffered SQLite rows before querying
    
    ## Follow-up
    - SQLite feedback exports still do not reproduce span prefixes like
    `feedback-thread{thread_id=...}:`; there is a `TODO(ccunningham)` in
    `codex-rs/state/src/log_db.rs` for that follow-up.
    
    ## Testing
    - `cd codex-rs && cargo test -p codex-state`
    - `cd codex-rs && cargo test -p codex-app-server`
    - `cd codex-rs && just fmt`
  • Add process_uuid to sqlite logs (#11534)
    ## Summary
    This PR is the first slice of the per-session `/feedback` logging work:
    it adds a process-unique identifier to SQLite log rows.
    
    It does **not** change `/feedback` sourcing behavior yet.
    
    ## Changes
    - Add migration `0009_logs_process_id.sql` to extend `logs` with:
      - `process_uuid TEXT`
      - `idx_logs_process_uuid` index
    - Extend state log models:
      - `LogEntry.process_uuid: Option<String>`
      - `LogRow.process_uuid: Option<String>`
    - Stamp each log row with a stable per-process UUID in the sqlite log
    layer:
      - generated once per process as `pid:<pid>:<uuid>`
    - Update sqlite log insert/query paths to persist and read
    `process_uuid`:
      - `INSERT INTO logs (..., process_uuid, ...)`
      - `SELECT ..., process_uuid, ... FROM logs`
    
    ## Why
    App-server runs many sessions in one process. This change provides a
    process-scoping primitive we need for follow-up `/feedback` work, so
    threadless/process-level logs can be associated with the emitting
    process without mixing across processes.
    
    ## Non-goals in this PR
    - No `/feedback` transport/source changes
    - No attachment size changes
    - No sqlite retention/trim policy changes
    
    ## Testing
    - `just fmt`
    - CI will run the full checks
  • chore: improve client (#10149)
    <img width="883" height="84" alt="Screenshot 2026-01-29 at 11 13 12"
    src="https://github.com/user-attachments/assets/090a2fec-94ed-4c0f-aee5-1653ed8b1439"
    />
  • feat: add log db (#10086)
    Add a log DB. The goal is just to store our logs in a `.sqlite` DB to
    make it easier to crawl them and drop the oldest ones.