feat(diagnostics): make the logging + crash infrastructure actually work (W7-2, W7-3, W7-4)

The Foundation tier — answers the original "make it easier to diagnose problems" ask.

- W7-2 (keystone): the app-level Logger file sink was never initialized, so LOG/LOGF/
  VERBOSE_LOGF went nowhere and dragonx-debug.log didn't exist on Linux/macOS at all.
  main() now calls Logger::init(<config>/dragonx-debug.log) on every platform. Fixed a
  latent deadlock this exposed: init() wrote its banner via write(), which re-locks the
  non-recursive mutex_ it already holds — now written directly. On Windows the raw
  stdout/stderr freopen moved to a separate dragonx-stdout.log so the two writers don't
  contend on one file. Added testLoggerFileSink (also a deadlock guard — it would hang if
  the fix regressed).

- W7-3: no crash handler existed on Linux/macOS. Added an async-signal-safe sigaction
  handler (SIGSEGV/ABRT/BUS/FPE/ILL) that writes the signal id + a backtrace_symbols_fd
  backtrace to dragonx-crash.log, then re-raises the default disposition for a core dump —
  the POSIX counterpart of the Windows SEH filter.

- W7-4: Logger::init now rotates the log to a single .1 backup past 10 MB, so a long or
  verbose session can't grow it unbounded.

Build-clean; ctest 1/1. Remaining Foundation: the QoL bundle (mostly UI). See
docs/wallet-hardening.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-02 15:31:23 -05:00
parent 05b00b158b
commit 207f9074db
4 changed files with 138 additions and 9 deletions

View File

@@ -21,7 +21,7 @@ Status legend: ☐ not started · ◐ in progress · ☑ landed & verified
| **P1-A** | W3-1, W3-2, W3-4 ✓ · W3-3 ⚑ | Migrate-to-seed correctness (fund-adjacent) | ◐ 3/4 |
| **P1-B** | W1-1, W1-2, W1-3, W1-4 ✓ + startup guard | Missing/wrong wallet-file safety | ☑ |
| **P2** | W5-1, W5-2, W6-1, W6-3 ✓ · W6-2 ☐ | Stale state & lite save-failure surfacing | ◐ 4/5 |
| **F** | W7-2, W7-3, W7-4, QoL | Diagnostics foundation + QoL bundle | |
| **F** | W7-2, W7-3, W7-4 ✓ · QoL | Diagnostics foundation + QoL bundle | ◐ infra done |
---
@@ -112,6 +112,11 @@ Land W7-2 first — it unblocks the rest.
## Progress log
- **Foundation / W7-2 · W7-3 · W7-4 (diagnostics infrastructure)** — ☑ landed (answers the original "easier to diagnose" ask — the logging/crash foundation now actually works):
- **W7-2 (Med, keystone):** the app-level `Logger` file sink was never initialized, so `LOG`/`LOGF`/`VERBOSE_LOGF` went nowhere and `dragonx-debug.log` didn't exist on Linux/macOS at all. `main()` now calls `Logger::init(<config>/dragonx-debug.log)` on all platforms. Also fixed a **latent deadlock** this exposed: `init()` wrote its banner via `write()`, which re-locks the non-recursive `mutex_` it already holds — now written directly. On Windows the raw stdout/stderr `freopen` was moved to a separate `dragonx-stdout.log` so the two writers don't contend. New `testLoggerFileSink` (also a deadlock guard — it would hang if that regressed).
- **W7-3 (Med):** no crash handler existed on Linux/macOS. Added an **async-signal-safe** `sigaction` handler (SIGSEGV/ABRT/BUS/FPE/ILL) that writes a signal id + `backtrace_symbols_fd` backtrace to `dragonx-crash.log`, then re-raises the default disposition for a core dump — the POSIX counterpart of the Windows SEH filter.
- **W7-4 (Low):** `Logger::init` now rotates the log to a single `.1` backup when it exceeds 10 MB, so a long/verbose session can't grow it unbounded.
Build-clean; `ctest` 1/1. **Remaining Foundation:** the QoL bundle (mostly UI) — "copy diagnostics for support", an "open log folder" action, persistent alert history, a daemon/RPC error banner, and the W6-2 refresh-staleness badge.
- **P2 / W5-1 · W5-2 · W6-1 · W6-3 (localized batch)** — ☑ landed:
- **W5-1 (Med):** `persistAfterBroadcast` (lite send/shield save) returned false on a persistent save failure but both callers discarded it and it never logged — completely silent. It now `liteLog`s the failure (the note re-derives on next sync, so it's a robustness gap, not fund loss).
- **W5-2 (Med):** the post-**sync** and post-**rescan** `save` results (in the detached scan threads) were ignored; both now `liteLog` on failure (`LiteDiagnostics::log` is mutex-guarded, safe from those threads).