Repository navigation
[Bug]: Desktop-hosted T3 Connect keeps no record of the managed cloudflared's output #16331
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/desktop
Steps to reproduce
- Run the desktop app with T3 Connect enabled. Don't use the background service.
- Confirm the managed connector is running as a child of the desktop backend:
.../.t3/tools/cloudflared/2026.5.2/darwin-arm64/cloudflared tunnel --no-autoupdate --loglevel info --output default run - Wait for the tunnel to register (or flap), then look for its output under
~/.t3/userdata/logs/.
Expected behavior
A desktop-hosted connector leaves a record on disk, at least the Relay client … messages the server already logs (registered connections, transport warnings, exits, crash-loop backoff). That way a T3 Connect outage can be diagnosed after it happens. The background service already does this, because its stdout goes to boot-service.log.
Actual behavior
On a desktop host, nothing the connector prints is persisted anywhere. Here the connector had been running for 2+ hours and none of its output was on disk.
How the output is dropped (at 204a639):
observeConnectorOutputsends each cloudflared line to the Effect logger. The server installs onlyconsolePretty+tracerLogger(OTLP replaces the tracer logger when it's configured).- stdout: the desktop pipes the backend's stdout/stderr, but
DesktopObservabilityonly keeps the last 1 MiB in memory. It writes them toserver-child.logonly when the backend crashes or fails to start. This is intentional (thet3 triageprompt says so), but it means a healthy backend with a flapping tunnel never writes anything. There is noserver-child.logon this machine. - traces:
tracerLoggeronly records messages logged inside an active span. The output observer andsuperviseConnectorare forked into the connector scope (L375-L376) and keep running after thereconcileConfigspan ends, so their messages never reachserver.trace.ndjson. Over the same period, allserver.trace.ndjson*files together contain onlyRelay client process startedandRelay client stopped. Both are logged insidereconcileConfig. Meanwhileboot-service.log, from the background-service connector that was running at the same time, has 29Relay client tunnel connection registeredand 215Relay client reported a transport warning. Even the two in-span events age out fast: 10 × 10 MiB trace files cover about 90 minutes here.
As a result, issues like #16258, #7447, and #15897 can't be diagnosed after the fact on desktop hosts, which is the most common setup.
Related, smaller gap that affects the service log too: cloudflared INF lines other than Registered tunnel connection are logged at Debug (classifyRelayClientOutput), which is below the default T3CODE_LOG_LEVEL=Info. So lines like the version banner and Unregistered tunnel connection are dropped everywhere. boot-service.log has 0 Relay client output entries.
Possible directions (for maintainers to choose):
- Pass
--logfile/--log-directoryto the managed cloudflared, pointing underlogsDirso cloudflared writes and rotates its own file. - Or write the connector lines the server already handles to a small rotating file next to the trace (
RotatingLogFileWriteralready exists on the desktop side).
Impact
Minor bug or occasional failure
Version or commit
Desktop 0.0.46-nightly.20261005.2702; source checked at main @ 204a639
Environment
macOS 26.6.2 (arm64), T3 Code (Nightly) desktop, managed cloudflared 2026.5.2
Logs or stack traces
$ ls ~/.t3/userdata/logs/ | grep -E 'server-child|server.trace'
server.trace.ndjson
server.trace.ndjson.1
...
server.trace.ndjson.10
# no server-child*.log
$ grep -c 'Relay client tunnel connection registered' ~/.t3/userdata/logs/server.trace.ndjson*
# 0 in every fileWorkaround
Run the host as the background service (t3 service install), whose stdout is captured in boot-service.log. Or run the desktop in development mode, which echoes backend output to the terminal.
Activity
juliusmarminge commentedon Oct 6, 2026
Note
Grok responding on behalf of Julius.
Triage
Thanks for the thorough investigation, @mats16! What you describe lines up with current
main(204a639). This is separate from #16258, #7447, and #15897; those are the Connect failures this gap makes hard to reconstruct after the fact.What I found
DesktopBackendOutputLogkeeps a 1 MiB ring buffer and only writesserver-child.logfrompersistFailure/persistFailureSnapshot, whichDesktopBackendManagercalls on an unexpected exit. If the backend stays up, the buffer is discarded. The background service fares better only because its process stdout isboot-service.log, whereLogger.consolePretty()output lands.reconcileConfigreturns.Logger.tracerLoggerattaches messages to the fiber's current span, and the local file span snapshots events only when the span ends.observeConnectorOutputandsuperviseConnectorare forked onto the connector scope, so they outlive theCloudManagedEndpointRuntime.reconcileConfigspan. Later registration, transport-warning, and crash-loop lines get attached to an already-exported span and are never flushed. That's consistent with a trace showing onlyRelay client process startedandRelay client stopped. An OTLP logs endpoint would capture these remotely, but a default desktop install has none configured.boot-service.log.classifyRelayClientOutputmaps every cloudflaredINFline exceptRegistered tunnel connectiontoDebug, below the defaultInfolevel, soUnregistered tunnel connectionnever shows up during a flap. An existing test keepsINF Starting metrics serverat debug, so any change here would likely be a narrow match on registration lifecycle lines.Likely fix area
Some options:
observeConnectorOutput.--logfileto the managed cloudflared. That would also capture the unregister lines, but it bypasses that redaction.INFlines inclassifyRelayClientOutputtoInfo.Persisting all backend stdout looks heavier than needed. Until something lands,
t3 service install(stdout goes toboot-service.log) or a dev desktop still give partial visibility, though without the unregister lines unlessT3CODE_LOG_LEVEL=Debug.A maintainer will decide on the fix direction.