Skip to content

Commit 292e221

Browse files
authored
feat: persist rolling backend logs and capture frontend console into diagnostics (#167)
## The bug The installed app writes **no log files at all**. `lib.rs` called `tracing_subscriber::fmt::init()`, which is stdout-only, and an installed Windows/macOS build has no console attached - so every `tracing::` line was discarded. `<config_dir>/app.driven/logs/` has been empty since install, the SPEC s18 diagnostic bundle shipped with no `logs/`, and field bugs (like the current `drive.unreachable` loop) are undiagnosable from a user's bundle. Separately, the webview's own `console.*` output, uncaught errors, and unhandled promise rejections were captured nowhere. ## What this does **Rolling backend logs** (`src-tauri/src/logging.rs`, new). A layered subscriber: the original stdout `fmt` layer, unchanged, plus a `tracing-appender` DAILY rolling file layer writing `driven.YYYY-MM-DD.log`. Non-blocking writer, with the `WorkerGuard` deliberately parked in a process-lifetime `OnceLock` static (dropping it shuts the writer thread down and would reproduce the empty-`logs/` bug). Honors `RUST_LOG`, else `info`. The file layer writes no ANSI escapes, and includes target, level, and timestamp. **Retention.** At startup, best-effort, never fatal: delete rolling logs older than 14 days, then oldest-first until the total fits 25 MB. `plan_prune` is a pure function over `(name, size, age)` so the policy is exhaustively unit-tested. `crash-*.txt` dumps and any foreign file in the directory are never touched, and the newest log survives a size cap it alone exceeds (it is the file the appender is about to write to). **A second, independent bug fixed.** `add_logs_and_crashes` resolved `app_config_dir()/driven/logs` (i.e. `%APPDATA%/app.driven/driven/logs`) while the panic hook wrote crash dumps to `app_config_dir()/logs`. Only the latter exists on disk, so the bundle was collecting **nothing** - not even crash dumps that were already sitting there. Both now call the shared `logging::log_dir()`, which is the single place that path is resolved. **Frontend console capture.** New `report_frontend_logs` IPC command re-emits webview entries through `tracing` under the `driven::frontend` target, so frontend and backend lines interleave in one timeline in one file. Hardened against an untrusted webview: batches over 200 entries are rejected, each text is truncated at 2000 chars, control characters are scrubbed (a crafted `console.log` must not be able to forge extra log lines or inject terminal escapes into a file that later leaves the machine), and traffic beyond 50 calls/minute is shed with exactly one warn per window. `ui/src/frontendLog.ts` wraps `console.log/info/warn/error` - always calling through to the original first - plus `window.onerror` and `unhandledrejection`, into a bounded 500-entry ring that reports its own overflow rather than dropping silently. It flushes every 5s, eagerly when large, and re-queues a failed batch **at most once** before dropping, so an unavailable backend can never grow a backlog or spin. Installed in `main.ts` before mount; no-op outside Tauri. **Bundle freshness.** The export awaits a frontend flush first, and the backend yields briefly before collecting `logs/` so the non-blocking writer's queue drains - otherwise the freshest and most relevant lines are exactly the ones missing from the bundle. ## Testing - Rust: 287 lib tests pass, including 11 new pruning tests (age rule, size rule, both composing, crash dumps spared, real-directory prune with a backdated mtime) and 12 new command tests (oversize batch rejected, oversize entry truncated, char-based truncation that cannot split a surrogate pair, control characters neutralised, rate-limit shed and window rollover). - UI: 515 vitest tests pass, including 25 new ones covering the ring, truncation, batch splitting at the backend's cap, requeue-once-then-drop, timer and eager flush with fake timers, and console passthrough still calling the original. - `cargo fmt --check`, `cargo clippy -p driven-app --all-targets -D warnings`, and `npx prettier --check src` all clean. Merged `origin/main` (toast + warning-box PRs) and re-ran every gate green afterwards.
1 parent 4b6070b commit 292e221

18 files changed

Lines changed: 1742 additions & 74 deletions

Cargo.lock

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ serde = { version = "1", features = ["derive"] }
3939
serde_json = "1"
4040
tracing = "0.1"
4141
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
42+
# Persistent rolling file logs for the installed app (src-tauri `logging.rs`).
43+
# An installed build has no console, so the previous stdout-only subscriber
44+
# discarded every line and the diagnostic bundle shipped with no logs. Provides
45+
# the DAILY rolling appender + the non-blocking writer that keeps file I/O off
46+
# the calling thread.
47+
tracing-appender = "0.2"
4248
async-trait = "0.1"
4349
bytes = "1"
4450
futures = "0.3"

design/SPEC.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,8 +1412,12 @@ containing:
14121412
Drive file_ids replaced by stable per-bundle hashes** (`fileid_<hash>`,
14131413
`path_<hash>`). The mapping is NOT included in the bundle.
14141414
- `logs/` — last 50 MB of tracing output from
1415-
`<config_dir>/driven/logs/`, **after being passed through a redaction
1416-
pipeline**:
1415+
`<config_dir>/app.driven/logs/` (the daily-rolling `driven.<date>.log`
1416+
files; `app.driven` is the bundle identifier, so this is Tauri's
1417+
`app_config_dir()` + `logs`). These files also carry the app window's
1418+
own console output, captured by `report_frontend_logs` under the
1419+
`driven::frontend` target and interleaved with the backend lines.
1420+
Everything is **passed through a redaction pipeline**:
14171421
- OAuth tokens (anything matching the access / refresh token regex)
14181422
replaced with `<token-redacted>`.
14191423
- Local paths replaced with `<path:<hash>>`.

src-tauri/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ serde.workspace = true
5050
serde_json.workspace = true
5151
tracing.workspace = true
5252
tracing-subscriber.workspace = true
53+
# Rolling on-disk logs (`logging.rs`): the installed app has no stdout, so the
54+
# file layer is the ONLY place `tracing::` output survives - and the only reason
55+
# the SPEC s18 diagnostic bundle has a non-empty `logs/`.
56+
tracing-appender.workspace = true
5357
anyhow.workspace = true
5458
# M5 app-shell async runtime (orchestrator run-loop spawn, build_and_spawn).
5559
tokio.workspace = true

0 commit comments

Comments
 (0)