You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Container stdout/stderr from processes started via sandbox.startProcess() should be continuously forwarded to Workers Observability (dashboard logs, wrangler tail), similar to how console.log() in Workers is captured automatically.
Current behavior
console.log() in the Worker → visible in Workers Observability ✅
Container startProcess() stdout/stderr → not forwarded to Workers Observability ❌
observability.logs.enabled: true in container config captures Sandbox runtime logs, but not stdout/stderr from user processes started via startProcess()
The SDK's canonical structured events (process.exit, command.exec, etc.) do reach Workers Logs (auto-indexed when emitted as objects), but these are lifecycle events — not the raw stdout/stderr content of the process
process.getLogs() returns a point-in-time snapshot — full buffer, no offset/cursor, no incremental reads
What already exists (partial)
Since this was filed, the SDK does expose real-time streaming of a process's output:
sandbox.streamProcessLogs(processId) → ReadableStream of SSE LogEvents (type: stdout | stderr | exit | error), consumed via parseSSEStream<LogEvent>()
execStream() / exec(command, { onOutput }) for command output
This satisfies the event-based-API request below. It does not close the gap, because:
It follows only the exact PID passed to startProcess(). If that process spawns children (backgrounded subprocesses, or a supervisor shell that runs the real workload as a child), their output is visible only as long as they inherit the handle process's stdout fd — and a child that re-execs or is re-parented (a full-process restart, setsid, daemonization) drops off the stream entirely. getLogs() has the same limitation.
There is still no path to Workers Observability / wrangler tail without writing a Worker that consumes the stream and re-emits each line via console.log().
Workarounds
GET /debug/logs endpoint — Worker route that calls process.getLogs() on demand and returns stdout/stderr as JSON. Point-in-time; requires manual polling.
streamProcessLogs() + console.log() re-emit — consume the SSE stream in the Worker and forward each LogEvent to console.log() so it lands in Workers Observability. Works, but is boilerplate every project re-implements, and only for handle-held processes.
SSH (wrangler containers ssh <instance-id>) — interactive access, not suitable for monitoring.
None of these provide zero-config, continuous log visibility comparable to docker logs -f or platform-managed containers.
Proposed solution
Either would close the remaining gap:
Automatic forwarding — startProcess() stdout/stderr automatically appears in Workers Observability when observability.logs.enabled: true, with no Worker code. (Most ergonomic.)
Incremental reads — process.getLogs({ since: timestamp }) or process.getLogs({ offset: byteOffset }) for efficient polling without re-reading the full buffer.
(The earlier "event-based streaming API" ask is now met by streamProcessLogs() — see above.)
Use case
Running an app inside a Cloudflare Container via the Sandbox SDK. The app produces structured logs on stdout that are essential for debugging failures and configuration problems. Today those logs are invisible in Observability unless you SSH in or write a custom stream-and-re-emit Worker.
Container stdout/stderr from processes started via sandbox.startProcess() should be continuously forwarded to Workers Observability (dashboard logs, wrangler tail), similar to how console.log() in Workers is captured automatically.
Current behavior
console.log() in the Worker → visible in Workers Observability ✅
Container startProcess() stdout/stderr → not forwarded to Workers Observability ❌
observability.logs.enabled: true in container config captures Sandbox runtime logs, but not stdout/stderr from user processes started via startProcess()
The SDK's canonical structured events (process.exit, command.exec, etc.) do reach Workers Logs (auto-indexed when emitted as objects), but these are lifecycle events — not the raw stdout/stderr content of the process
process.getLogs() returns a point-in-time snapshot — full buffer, no offset/cursor, no incremental reads
What already exists (partial)
Since this was filed, the SDK does expose real-time streaming of a process's output:
sandbox.streamProcessLogs(processId) → ReadableStream of SSE LogEvents (type: stdout | stderr | exit | error), consumed via parseSSEStream<LogEvent>()
execStream() / exec(command, { onOutput }) for command output
This satisfies the event-based-API request below. It does not close the gap, because:
It follows only the exact PID passed to startProcess(). If that process spawns children (backgrounded subprocesses, or a supervisor shell that runs the real workload as a child), their output is visible only as long as they inherit the handle process's stdout fd — and a child that re-execs or is re-parented (a full-process restart, setsid, daemonization) drops off the stream entirely. getLogs() has the same limitation.
There is still no path to Workers Observability / wrangler tail without writing a Worker that consumes the stream and re-emits each line via console.log().
Workarounds
GET /debug/logs endpoint — Worker route that calls process.getLogs() on demand and returns stdout/stderr as JSON. Point-in-time; requires manual polling.
streamProcessLogs() + console.log() re-emit — consume the SSE stream in the Worker and forward each LogEvent to console.log() so it lands in Workers Observability. Works, but is boilerplate every project re-implements, and only for handle-held processes.
SSH (wrangler containers ssh <instance-id>) — interactive access, not suitable for monitoring.
None of these provide zero-config, continuous log visibility comparable to docker logs -f or platform-managed containers.
Proposed solution
Either would close the remaining gap:
Automatic forwarding — startProcess() stdout/stderr automatically appears in Workers Observability when observability.logs.enabled: true, with no Worker code. (Most ergonomic.)
Incremental reads — process.getLogs({ since: timestamp }) or process.getLogs({ offset: byteOffset }) for efficient polling without re-reading the full buffer.
(The earlier "event-based streaming API" ask is now met by streamProcessLogs() — see above.)
Use case
Running an app inside a Cloudflare Container via the Sandbox SDK. The app produces structured logs on stdout that are essential for debugging failures and configuration problems. Today those logs are invisible in Observability unless you SSH in or write a custom stream-and-re-emit Worker.
product:containersRelating to Cloudflare Containers: https://developers.cloudflare.com/containers/
2 participants
Converted from issue
This discussion was converted from issue #12998 on April 01, 2026 09:41.
Heading
Bold
Italic
Quote
Code
Link
Numbered list
Unordered list
Task list
Attach files
Mention
Reference
Menu
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Describe the solution
Summary
Container stdout/stderr from processes started via
sandbox.startProcess()should be continuously forwarded to Workers Observability (dashboard logs,wrangler tail), similar to howconsole.log()in Workers is captured automatically.Current behavior
console.log()in the Worker → visible in Workers Observability ✅startProcess()stdout/stderr → not forwarded to Workers Observability ❌observability.logs.enabled: truein container config captures Sandbox runtime logs, but not stdout/stderr from user processes started viastartProcess()process.exit,command.exec, etc.) do reach Workers Logs (auto-indexed when emitted as objects), but these are lifecycle events — not the raw stdout/stderr content of the processprocess.getLogs()returns a point-in-time snapshot — full buffer, no offset/cursor, no incremental readsWhat already exists (partial)
Since this was filed, the SDK does expose real-time streaming of a process's output:
sandbox.streamProcessLogs(processId)→ReadableStreamof SSELogEvents (type: stdout | stderr | exit | error), consumed viaparseSSEStream<LogEvent>()execStream()/exec(command, { onOutput })for command outputThis satisfies the event-based-API request below. It does not close the gap, because:
startProcess(). If that process spawns children (backgrounded subprocesses, or a supervisor shell that runs the real workload as a child), their output is visible only as long as they inherit the handle process's stdout fd — and a child that re-execs or is re-parented (a full-process restart,setsid, daemonization) drops off the stream entirely.getLogs()has the same limitation.wrangler tailwithout writing a Worker that consumes the stream and re-emits each line viaconsole.log().Workarounds
GET /debug/logsendpoint — Worker route that callsprocess.getLogs()on demand and returns stdout/stderr as JSON. Point-in-time; requires manual polling.streamProcessLogs()+console.log()re-emit — consume the SSE stream in the Worker and forward eachLogEventtoconsole.log()so it lands in Workers Observability. Works, but is boilerplate every project re-implements, and only for handle-held processes.wrangler containers ssh <instance-id>) — interactive access, not suitable for monitoring.None of these provide zero-config, continuous log visibility comparable to
docker logs -for platform-managed containers.Proposed solution
Either would close the remaining gap:
startProcess()stdout/stderr automatically appears in Workers Observability whenobservability.logs.enabled: true, with no Worker code. (Most ergonomic.)process.getLogs({ since: timestamp })orprocess.getLogs({ offset: byteOffset })for efficient polling without re-reading the full buffer.(The earlier "event-based streaming API" ask is now met by
streamProcessLogs()— see above.)Use case
Running an app inside a Cloudflare Container via the Sandbox SDK. The app produces structured logs on stdout that are essential for debugging failures and configuration problems. Today those logs are invisible in Observability unless you SSH in or write a custom stream-and-re-emit Worker.
Environment
@cloudflare/sandbox0.7.21 (streaming APIs verified present)All reactions