Commit Graph
31 Commits
Author SHA1 Message Date
Alexi Christakis 0b94751cc4 Add telemetry for SQLite log persistence (#40726)
## Why

SQLite log persistence needs visibility into batch size, write latency,
failures, and entries dropped before they reach the writer. Exporter
diagnostics must not feed back into the SQLite log sink and keep metric
exports active indefinitely.

## What changed

- Record write count, duration, batch bytes, entry count, largest-entry size,
  and queue drops with bounded outcome tags.
- Add explicit byte histogram boundaries for log batch metrics.
- Filter OTLP transport noise and the benign unsolicited HTTP/2 PING warning
  from SQLite log persistence to prevent export cycles while retaining other
  HTTP/2 warnings.

## Testing

- Cover metric values, tags, queue-drop reasons, and histogram boundaries.
- Verify successful and failed OTLP HTTP and gRPC exports become idle instead
  of generating recurring SQLite log-write metrics.

GitOrigin-RevId: a63224ae15945fe84adc977a8b7d55bb47a4de53
2026-08-25 22:46:49 +00:00
Adam Perry @ OpenAI 2f4b105514 Prevent telemetry exporters from feeding back into themselves (#40658)
## Why

Telemetry transport and storage can emit their own diagnostics, causing exports
or SQLite writes to recursively generate more telemetry.

## What changed

- Exclude `h2` spans from trace export so OTLP transport activity cannot trigger
  additional OTLP exports.
- Exclude all `sqlx`, `sqlx_core`, and `sqlx_sqlite` diagnostics from the SQLite
  log sink, including when no external subscriber filter is installed.
- Keep similarly named targets such as `sqlx_application` eligible for storage.

## Testing

- Extend the SQLite log-filter test to cover the broader SQLx exclusion and the
  unrelated-target case.

GitOrigin-RevId: 13a832e1f228e9564a31cc08f00add286342bac1
2026-08-25 17:59:32 +00:00
Adam Perry @ OpenAI 493e0efb7b Prevent SQLx warnings from feeding back into SQLite logs (#39592)
## Why

Warnings emitted while querying SQLite or acquiring a connection must not be
written back through the same SQLite log sink.

## What changed

- Disable the `sqlx::query` and `sqlx::pool::acquire` targets for the SQLite
  log filter.
- Keep warnings from other `sqlx` targets so useful diagnostics remain
  available.

## Testing

Extend the log filter test to verify that query and pool-acquisition warnings
are dropped while another `sqlx` warning is retained.

GitOrigin-RevId: cec9d4e07d113de8eccec7445ede0ae68e21886d
2026-08-20 05:16:59 +00:00
Eric Traut 701db965e5 Increase SQLite log sink batching (#39294)
## What changed

- Increase the default bounded log queue capacity from 512 to 2,048 entries.
- Raise the default insert batch size from 128 to 512 entries.
- Extend the default periodic flush interval from 2 to 10 seconds.

GitOrigin-RevId: e96be0996152b4a2a84d88c23d36959436685682
2026-08-18 22:05:07 +00:00
Tamir Duberstein dad1db87bb Limit TUI streaming traces in SQLite logs (#38036)
## Why

Per-chunk streaming traces can flood the bounded SQLite log queue.

## What changed

- Persist `codex_tui::streaming::controller` and
  `codex_tui::streaming::table_holdback` events at `DEBUG` and above.
- Keep `TRACE` events enabled for other TUI streaming targets.

## Testing

Extended the SQLite log filter test to cover dropped streaming traces, retained
debug diagnostics, and unaffected streaming targets.

GitOrigin-RevId: e7eb999a0770eba6cce5f389005896e64928f02b
2026-08-11 17:07:24 +00:00
dshakiba-OAI 62b7386b07 Limit payload traces in diagnostic logs (#37497)
## Why

High-volume request and streamed-response payloads can overwhelm the SQLite log database and diagnostic ring buffer used for reports.

## What changed

- Limit HTTP transport, SSE, and WebSocket diagnostics to `DEBUG` in persistent log sinks while leaving unrelated trace diagnostics available.
- Log known unhandled response events and delta events at `TRACE`, and surface unexpected event kinds at `DEBUG` without including their payloads.
- Report structured parse-error metadata instead of logging an unparseable SSE payload.

## Testing

- Cover filtering for transport, SSE, WebSocket, and unrelated trace records in both report and SQLite log sinks.
- Cover unknown and unsupported delta response events.

GitOrigin-RevId: 6d9121e093ddadf6834da394df544d9c09d8aeb9
2026-08-07 19:56:29 +00:00
jif d98eb72e18 Limit RMCP logs persisted to SQLite (#36904)
## What changed

- Persist `rmcp` and `codex_rmcp_client` events only at `INFO` level or above.
- Cover nested targets from both namespaces in the SQLite log filter test.

GitOrigin-RevId: dfeeec302a9562f533a09a735b392d202c1f7801
2026-08-04 14:56:06 +00:00
Adam Perry @ OpenAI c769a05340 Honor the configured SQLite home across state consumers (#34994)
## Why

Codex and SQLite data can use separate home directories, but state consumers
could reconstruct database paths from the Codex home instead of consistently
using the resolved SQLite configuration.

## What changed

- Pass `SqliteConfig` through the core, rollout, state runtime, and thread store
  instead of passing a directory and rebuilding the configuration downstream.
- Use that shared configuration for state, logs, memories, goals, and paginated
  thread-history database access, including integrity checks and cleanup.
- Reject state database handles whose SQLite configuration does not match the
  requesting store.

## Testing

Add coverage with separate Codex and SQLite homes that verifies startup
backfill, thread listing, and paginated history all use the configured SQLite
directory.

GitOrigin-RevId: 1de1cdd1d6ff1d70bbb6c360c8352e6543fb8ebf
2026-07-23 19:19:35 +00:00
jif 66bd101fff Avoid unnecessary post-sampling token estimates (#34789)
## Why

The post-sampling trace calculated an estimated token count even when only the
always-on diagnostic and state log sinks were listening, adding unnecessary work
after every sample.

## What changed

- Move the estimate to a dedicated trace target and calculate it only when that
  event is enabled.
- Disable the target for the always-on diagnostic and state log sinks while
  keeping it available to explicitly configured subscribers.

## Testing

- Verify that the combined always-on subscribers do not enable the estimate
  event.

GitOrigin-RevId: ea572584597dda313e7719897d0ab1fdf7362745
2026-07-22 16:50:09 +00:00
Tom 9993fb838a Improve Responses WebSocket timing telemetry (#32256)
## What changed

- Emit `responsesapi.websocket_timing` payloads as opt-in trace events with request context, while excluding them from diagnostic uploads and persisted logs by default.
- Preserve fractional-millisecond TBT values in telemetry histograms and runtime summaries, rounding only when formatting the TUI label.

## Testing

- Cover timing-log filtering, fractional TBT collection and display, and existing whole-millisecond duration behavior.

GitOrigin-RevId: 5f0a1c60c237abed17cc8b2d569ca77fbbc6ec41
2026-07-10 20:01:47 +00:00
jif bfa3eeb8bf Filter routine Hyper logs from SQLite (#31791)
## Why

The SQLite feedback log currently persists Hyper's TRACE, DEBUG, and
INFO connection-pool bookkeeping. Healthy connects, checkouts, reuse,
and timer activity generated thousands of small rows during active
sessions without adding useful feedback context.

## What changed

- Set the `hyper_util` target prefix to WARN for the SQLite feedback-log
layer.
- Continue persisting Hyper warnings and errors.
- Leave other tracing and OpenTelemetry subscribers unchanged.

This removes routine dependency chatter while preserving actionable
failures.

Related to #28224.
2026-07-09 14:35:09 +01:00
jif acb78a41a0 Stop persisting RMCP service traces (#31789)
## Why

The SQLite feedback log enables TRACE by default. `rmcp::service`
renders complete MCP service events at TRACE, including large tool
catalogs and other protocol payloads. In a busy local sample, this was
the largest retained source and individual rows reached about 2 MiB.

These rows can quickly consume the per-thread retention budget, displace
smaller diagnostics, and add unnecessary SQLite insert-and-prune work.

## What changed

- Persist `rmcp::service` at INFO and above in the SQLite feedback log.
- Keep service initialization, shutdown, warnings, and errors available
for feedback.
- Leave other tracing and OpenTelemetry subscribers unchanged.

This is intentionally target-specific. Broader persistent-log policy
changes can remain separate.

Related to #28224.
2026-07-09 14:34:47 +01:00
jif 63f8f547c0 Stop persisting bridged log events (#29599)
## Why

The 0.142.0 persistent-log filter disables target=log, but bridged log
records are filtered using their original dependency target before
tracing-log emits them as target=log. This allowed high-volume
dependency TRACE events to keep reaching SQLite.

This is a follow-up to #28224.

## What changed

- Reject bridged target=log events inside the SQLite sink before
formatting or queueing them.
2026-06-23 12:18:25 +02:00
jif e98d43ac37 Filter noisy targets from persistent logs (#29457)
## Why

The local SQLite log sink currently enables TRACE for every target. This
persists high-volume dependency logs bridged through `target=log` and
duplicates OpenTelemetry mirror events in `codex_otel.log_only` and
`codex_otel.trace_safe`.

These records rapidly consume the per-partition log budget and cause
unnecessary SQLite insert-and-prune churn.

## What changed

- Keep TRACE persistence for other targets.
- Exclude bridged `target=log` events from the SQLite sink.
- Exclude the two `codex_otel` mirror targets from the SQLite sink.
- Share the same filter between app-server and TUI.

Remote OpenTelemetry export and metrics are unchanged.
2026-06-22 18:17:04 +02:00
Zanie Blue d81fcdf8ef Reduce SQLite contention from OpenTelemetry SDK debug logs (#26396)
## Summary

- skip `opentelemetry_sdk` DEBUG and TRACE events before formatting or
queueing them for the SQLite log sink
- preserve INFO, WARN, and ERROR events from the SDK, along with TRACE
events from application targets
- add a persistence-level regression test for the target and level
policy

## Why

OpenTelemetry's batch log processor emits internal
`BatchLogProcessor.ExportingDueToTimer` meta-events every second per
Codex process. In measured high-fanout `logs_2.sqlite` databases,
low-level `opentelemetry_sdk` events accounted for over 30% of retained
rows (30-60% on the machines of people I asked to check).

Persisting this SDK bookkeeping across many processes adds substantial
write volume and contention without representing application activity.

## Validation

- `just test -p codex-state` (132/132 tests passed, plus bench smoke)
- `just fix -p codex-state`
- `just fmt`
2026-06-04 10:12:08 -05:00
Dylan Hurd 7f7c7c2c07 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
2026-04-28 12:08:41 -07:00
Rasmus Rygaard 5378cccd8a 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.
2026-04-24 16:27:39 -07:00
jif-oai d4223091d0 fix: windows flake (#18127)
Fix `sqlite_feedback_logs_match_feedback_formatter_shape` by explicitly
flushing the async log DB layer before querying SQLite.
2026-04-16 13:52:21 +01:00
jif-oai 868ac158d7 feat: log db better maintenance (#16330)
Run a DB clean-up more frequently with an incremental `VACCUM` in it
2026-03-31 19:15:44 +02:00
Charley CunninghamandCodex ebbbc52ce4 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>
2026-03-18 22:44:31 +00:00
jif-oai cf143bf71e feat: simplify DB further (#13771) 2026-03-07 03:48:36 -08:00
Charley CunninghamandCodex ad98504d74 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>
2026-03-06 11:15:28 -08:00
Charley CunninghamandCodex 4e6c6193a1 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>
2026-03-06 10:54:20 -08:00
jif-oai c8f4b5bc1e feat: limit number of rows per log (#13763)
avoid DB explosion. This is a temp solution
2026-03-06 18:51:42 +01:00
Charley CunninghamandCodex 9f91c7f90f 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>
2026-03-05 16:53:37 -08:00
Charley Cunningham c4bd0aa3b9 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`
2026-03-03 11:17:06 -08:00
Charley Cunningham fce4ad9cf4 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
2026-02-14 17:27:22 -08:00
jif-oai d6631fb5a9 feat: add log retention and delete them after 90 days (#10151) 2026-01-29 16:55:01 +01:00
jif-oai 89c5f3c4d4 feat: adding thread ID to logs + filter in the client (#10150) 2026-01-29 16:53:30 +01:00
jif-oai 4ba911d48c 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"
/>
2026-01-29 11:25:22 +01:00
jif-oai 780482da84 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.
2026-01-29 10:23:03 +01:00