Skip to content

feat: buffer connection logs and flush them on failure - #1100

Draft
aqandrew wants to merge 4 commits into
mainfrom
aqandrew/devex-669-vs-code-add-log-buffer
Draft

feat: buffer connection logs and flush them on failure#1100
aqandrew wants to merge 4 commits into
mainfrom
aqandrew/devex-669-vs-code-add-log-buffer

Conversation

@aqandrew

Copy link
Copy Markdown
Contributor

Implements RFC requirement 13 / DEVEX-669: buffer connection debug logs in memory below the current log level and flush them on a genuine connection failure, so a support bundle captures the detail leading up to the failure without the user having enabled debug logging beforehand.

What this does

  • Adds a BufferingLogger decorator (src/logging/logBuffer.ts) that wraps the "Coder" output channel and keeps a bounded, in-memory ring of the log lines that sit below the channel's current level — the ones it would otherwise drop. Only below-level lines are buffered, so nothing already written is duplicated.
  • On a connection failure, flush(reason) re-emits the captured lines into the output channel (each marked [buffered] with its original ISO timestamp and level) at the least-verbose level the channel still persists, so they land on disk and in support bundles.
  • Wires the buffer into ServiceContainer and adds the coder.connectionLogBuffer.size setting (default 1000, 0 disables).
  • Flushes only on genuine failures, never on transient drops or intentional teardown:
    • a reconnecting WebSocket terminal failure (unrecoverable_close, unrecoverable_http, certificate_error);
    • a WorkspaceMonitor socket error;
    • an agent reported as disconnected during connection.
  • A short suppression window (5s) coalesces the burst of signals a single outage often triggers into one flush.
  • Documents the behavior, config, hard-kill/OOM loss limitation, and SSH log scope in CONTRIBUTING.md.

Scope notes

  • Extension SSH debug logs that pass through the shared logger are buffered. The CLI ProxyCommand writes its own file logs under coder.proxyLogDirectory, which support bundles already collect from disk, so those are not buffered here.
  • The buffer lives in memory, so a hard kill or out-of-memory event loses it (documented).

Commits

  1. BufferingLogger + unit tests
  2. container wiring + config
  3. failure-site wiring + tests
  4. docs

Testing

  • pnpm typecheck, pnpm format:check, and pnpm lint are clean.
  • Affected/dependent unit suites pass (logBuffer, reconnectingWebSocket, workspaceMonitor, workspaceStateMachine, coderApi, plus the container-mock consumers).
Implementation plan & design decisions

Design

  • Buffer: bounded entry-count ring; captures only calls whose severity is below the channel's current level; oldest-eviction; live-resizable via config.
  • Flush target (D3): replay into the existing "Coder" output channel at a level that still persists, with a [buffered] marker plus original level/timestamp, chronologically next to the real failure logs. Support bundles already collect the on-disk VS Code logs, so no separate sink is needed.
  • Flush reasons (D4): genuine, surfaced connection failures only — reconnecting-socket terminal failures, WorkspaceMonitor.notifyError, and agent disconnected. Never on transient retrying drops or intentional teardown (manual_disconnect, normal_close, replaced, dispose/deactivate/reload). A single isConnectionFailure(reason) predicate gates the socket sites, and a short suppression window coalesces bursts.
  • SSH scope (D5): buffer extension SSH debug passing through the shared Logger; do not buffer CLI ProxyCommand file logs already handled via coder.proxyLogDirectory.

Decisions

  • D1: buffer all below-level session logs.
  • D2: bound by entry count.
  • D3: replay into the existing Coder output channel at a persisted level with [buffered] marker/original level/timestamp.
  • D4: flush only on genuine connection failure; not transient or intentional teardown.
  • D5: buffer extension SSH debug through the shared Logger; not CLI ProxyCommand file logs.

🤖 Generated with Coder Agents. Reviewed and authored on behalf of @aqandrew.

Wraps a Logger and keeps a bounded in-memory ring of entries below the sink's
current level (the ones it would drop). flush() replays them into the sink at a
level guaranteed to be written, so a connection failure can preserve the debug
detail leading up to it without the user having enabled debug logging.

Only below-level entries are buffered (no duplication of what the sink already
writes); flush is coalesced by a short suppression window.
@linear-code

linear-code Bot commented Aug 26, 2026

Copy link
Copy Markdown

DEVEX-669

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