Skip to content

feat(dev): tee stdout/stderr to logs/dev-YYYY-MM-DD.log - #444

Merged
icebear0828 merged 1 commit into
devfrom
feat/dev-file-logger
May 6, 2026
Merged

feat(dev): tee stdout/stderr to logs/dev-YYYY-MM-DD.log#444
icebear0828 merged 1 commit into
devfrom
feat/dev-file-logger

Conversation

@icebear0828

Copy link
Copy Markdown
Owner

Summary

  • Dev mode auto-tees process.stdout / process.stderr to logs/dev-YYYY-MM-DD.log. Patches the write functions at fd-level so console.log, console.warn, process.stdout.write, and the existing structured log.* helper all land in the file.
  • Skipped in production (NODE_ENV=production — log shippers handle persistence there) and under Vitest (VITEST / NODE_ENV=test); manual opt-out via CODEX_PROXY_FILE_LOG=0.
  • Motivation: dev logs previously lived only in the tsx watch terminal scrollback. Closing the pane or scrolling past the buffer lost them, leaving no audit trail when an upstream 422 (e.g. No tool output found for function call call_xxx) hit. We want every dev session's full stdout/stderr on disk by default.

Implementation

  • src/utils/log-file.tsinstallFileLogger({ dir, filename? }) opens an append fd with openSync, wraps process.stdout.write / process.stderr.write to call writeSync(fd, ...) first then forward to the original, returns uninstall() that restores the exact original function reference (not a .bind clone, so identity comparisons hold).
  • src/utils/install-dev-logger.ts — side-effect import that gates on NODE_ENV / VITEST / CODEX_PROXY_FILE_LOG and installs into ${cwd}/logs/. Errors are swallowed to stderr only — file logging must never break the process.
  • src/index.ts — added as the very first import so startup logs ([Init], [Config], [ModelStore], etc.) get captured too.
  • logs/ is already covered by the root *.log gitignore.

Test plan

  • npx vitest run tests/unit/utils/log-file.test.ts — 7 cases covering stdout/stderr tee, nested directory creation, uninstall restoration, append-mode persistence across reopens, default dev-YYYY-MM-DD.log filename, boolean return value preservation.
  • npx vitest run tests/unit/ — all 1490 unit tests still pass.
  • npx tsc --noEmit — clean.
  • Live verified: tsx watch auto-reloaded, logs/dev-2026-05-06.log materialized with full startup banner + subsequent request logs.

Out of scope

  • Log rotation / retention. Files accumulate one per day; cleanup is manual for now.
  • Capturing in production. Log aggregators consume the JSON stdout there directly.

Dev logs used to live only in the tsx-watch terminal scrollback — closing
the window or scrolling past the buffer lost them, so debugging an
intermittent upstream 422 had no evidence trail. installFileLogger opens
a per-day append fd and patches process.stdout/stderr.write to also fan
out to the file. Skipped in production (log shippers handle it), under
Vitest, and via CODEX_PROXY_FILE_LOG=0.
@icebear0828
icebear0828 merged commit 7a93c6e into dev May 6, 2026
1 check passed
@icebear0828
icebear0828 deleted the feat/dev-file-logger branch May 6, 2026 12:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant