Background
qwen serve currently emits most daemon-layer diagnostics to process stderr. This includes bridge errors from sendBridgeError(...) in packages/cli/src/serve/server.ts, lifecycle/shutdown messages in packages/cli/src/serve/runQwenServe.ts, and ACP child stderr forwarded from packages/cli/src/serve/httpAcpBridge.ts with a [serve pid=... cwd=...] prefix.
That works when the operator explicitly redirects stderr or runs under a supervisor that captures stderr, but it is easy to miss in SDK/Desktop/daemon deployments. For example, when a client sees POST /session/:id/prompt return HTTP 500, the useful route/session/error context is often only available from the daemon stderr stream.
The existing createDebugLogger is not a drop-in replacement for this. It is session-scoped and depends on an active debug log session; the qwen serve daemon process starts before any session exists, so using it directly from serve code can silently drop daemon logs. ACP child telemetry/debug logging also does not fully cover daemon HTTP routing, bridge lifecycle, queueing, child stderr forwarding, or daemon startup/shutdown failures.
Related but distinct: #2014 requested a standalone structured error log for external monitoring. This issue is narrower: persist qwen serve daemon diagnostics so troubleshooting does not require manual stderr redirection.
Problem
Daemon deployments need a durable local log for serve-level failures. Today the recommended workaround is effectively to capture stderr, for example qwen serve 2>serve.log, or rely on systemd/Docker/journald. That is fragile for local SDK/Desktop usage and makes HTTP 500 investigation harder than necessary.
Proposal
Add a daemon-specific logger/sink initialized by runQwenServe.
Suggested behavior:
- Write daemon logs under the runtime directory, for example
${QWEN_RUNTIME_DIR or ~/.qwen}/debug/daemon/<daemon-id>.log.
- Use a stable daemon id derived from process/workspace/listen context, such as
serve-<pid>-<workspaceHash>.
- Keep stderr output for operator visibility in terminal, systemd, Docker, and Kubernetes logs; file logging should be additive, not a replacement.
- Route serve diagnostics through a small helper that writes important lines to both stderr and the daemon log.
- Persist
QWEN_SERVE_DEBUG debug breadcrumbs to the same daemon log when enabled.
- Persist ACP child stderr forwarding lines to the daemon log with the same attribution prefix currently sent to stderr.
- Include enough context for triage: timestamp, level, route, session id if known, client id if known, workspace/cwd, child pid/channel id when available, and the error stack/message.
- Avoid reusing session debug-log state in a way that changes the normal per-session
debug/latest semantics, unless that behavior is explicitly designed and documented.
Acceptance criteria
- Starting
qwen serve creates or appends a daemon log file without requiring shell stderr redirection.
- A generic HTTP 500 from
POST /session/:id/prompt can be correlated in the daemon log by route and session id.
- ACP child stderr lines that are currently forwarded to daemon stderr are also written to the daemon log.
- Logging works before the first session is created and after all sessions are closed.
- Existing stderr behavior remains intact for operators and process managers.
- The log path and any opt-out/debug behavior are documented.
Non-goals
Background
qwen servecurrently emits most daemon-layer diagnostics to process stderr. This includes bridge errors fromsendBridgeError(...)inpackages/cli/src/serve/server.ts, lifecycle/shutdown messages inpackages/cli/src/serve/runQwenServe.ts, and ACP child stderr forwarded frompackages/cli/src/serve/httpAcpBridge.tswith a[serve pid=... cwd=...]prefix.That works when the operator explicitly redirects stderr or runs under a supervisor that captures stderr, but it is easy to miss in SDK/Desktop/daemon deployments. For example, when a client sees
POST /session/:id/promptreturn HTTP 500, the useful route/session/error context is often only available from the daemon stderr stream.The existing
createDebugLoggeris not a drop-in replacement for this. It is session-scoped and depends on an active debug log session; theqwen servedaemon process starts before any session exists, so using it directly from serve code can silently drop daemon logs. ACP child telemetry/debug logging also does not fully cover daemon HTTP routing, bridge lifecycle, queueing, child stderr forwarding, or daemon startup/shutdown failures.Related but distinct: #2014 requested a standalone structured error log for external monitoring. This issue is narrower: persist
qwen servedaemon diagnostics so troubleshooting does not require manual stderr redirection.Problem
Daemon deployments need a durable local log for serve-level failures. Today the recommended workaround is effectively to capture stderr, for example
qwen serve 2>serve.log, or rely on systemd/Docker/journald. That is fragile for local SDK/Desktop usage and makes HTTP 500 investigation harder than necessary.Proposal
Add a daemon-specific logger/sink initialized by
runQwenServe.Suggested behavior:
${QWEN_RUNTIME_DIR or ~/.qwen}/debug/daemon/<daemon-id>.log.serve-<pid>-<workspaceHash>.QWEN_SERVE_DEBUGdebug breadcrumbs to the same daemon log when enabled.debug/latestsemantics, unless that behavior is explicitly designed and documented.Acceptance criteria
qwen servecreates or appends a daemon log file without requiring shell stderr redirection.POST /session/:id/promptcan be correlated in the daemon log by route and session id.Non-goals