Skip to content

fix(cli): initialize and flush logging for every subcommand (#194)#195

Merged
samsoir merged 2 commits into
mainfrom
bugfix/194-cli-logging-init
May 11, 2026
Merged

fix(cli): initialize and flush logging for every subcommand (#194)#195
samsoir merged 2 commits into
mainfrom
bugfix/194-cli-logging-init

Conversation

@samsoir

@samsoir samsoir commented May 11, 2026

Copy link
Copy Markdown
Owner

Summary

Three coordinated changes that take the CLI's log file from "exists but empty for the commands that need it most" to "useful for diagnosing real package-install failures." Filed as #194 after dee-sea's diagnostic round-trip on #186 went sideways because `RUST_LOG=trace xearthlayer packages install eu` produced no log file.

1. Initialize logging for every subcommand (`20b1e85`)

Previously only `run` and `patches` initialized the tracing subscriber, because the call to `init_logging_full` lived inside `CliRunner::with_options` and only those two commands constructed a `CliRunner`. Every other CLI subcommand ran with `tracing` events going nowhere.

Lift logging initialization out of `CliRunner` and into `main.rs` so it runs once before subcommand dispatch. `CliRunner` is now config-only.

Promote `--debug` and `--profile` to top-level `Cli` flags with `global = true`. They now apply to every subcommand. `xearthlayer run --debug` keeps working unchanged thanks to clap's global-flag positioning, and `xearthlayer --debug packages install eu` is now possible.

2. Flush log on error exit (`55b0e3b`, first half)

Discovered while verifying #1: `CliError::exit` called `std::process::exit` directly, which skips local destructors. The `LoggingGuard` therefore never dropped on the error path, and `tracing-appender`'s background writer was killed mid-flight with its queue unflushed — every failed CLI invocation produced an empty log file even after #1.

Replace `exit` with `report() -> u8`; have `main` return `ExitCode` so the guard drops normally before process termination.

3. Structured tracing across the install pipeline (`55b0e3b`, second half)

Added INFO events at every stage transition in `installer.rs` — install started, metadata fetched, download phase started/completed (with bytes + elapsed_secs), reassembly started/completed, extraction started/completed, install completed. All emitted with structured fields (region, version, parts, bytes, etc.) for grep / awk analysis.

In `download/orchestrator.rs`: DEBUG event when a part download starts, DEBUG on success, DEBUG on abort. WARN on each retry. ERROR when retries exhaust.

Test plan

  • `make pre-commit` passes (2,404 unit tests + clippy + fmt)
  • `xearthlayer --debug packages install eu` (errors AlreadyInstalled) writes "package install started" to the log instead of leaving it empty
  • `xearthlayer --debug diagnostics` (success path) still writes wgpu detection events
  • `xearthlayer run --debug` and `xearthlayer --debug run` both accept the flag (clap `global = true`)
  • `xearthlayer --help` and `xearthlayer run --help` both surface the global flags

What dee-sea will see when he retries on `main`

When he runs `RUST_LOG=trace xearthlayer packages install eu` from a fresh build, his failed install will now produce a log file showing exactly which parts failed, how many retries each got, and what the underlying error message was — exactly the diagnostic we asked for in #186 but couldn't capture before.

Fixes #194

🤖 Generated with Claude Code

samsoir and others added 2 commits May 10, 2026 16:40
Previously only `run` and `patches` initialized the tracing subscriber,
because the call to `init_logging_full` lived inside `CliRunner::with_options`
and only those two commands constructed a `CliRunner`. Every other CLI
subcommand (`packages`, `config`, `diagnostics`, `cache`, `scenery-index`,
`publish`, `setup`, `init`, `download`) ran with `tracing` events going
nowhere — `RUST_LOG=trace` produced no log file. This bit us during the
#186 / #187 diagnostic round-trip with dee-sea.

Lift logging initialization out of `CliRunner` and into `main.rs` so it
runs once before subcommand dispatch. `CliRunner` is now config-only;
the logging guard is held for the lifetime of `main` via a local binding.

Promote `--debug` and `--profile` to top-level `Cli` flags with
`global = true`. They now apply to every subcommand and are visible to
`init_cli_logging` before dispatch. `xearthlayer run --debug` keeps
working unchanged thanks to clap's global-flag positioning, and
`xearthlayer --debug packages install eu` is now possible — exactly
the diagnostic flow that #186 needed.

New module `logging_init` with `derive_log_paths` (pure helper) and
`init_cli_logging` (config-aware wrapper around `init_logging_full`).
Four unit tests cover path derivation edge cases (normal, bare
filename, root path, deeply nested).

Robustness: if `ConfigFile::load()` fails (parse error or first-run
without a config file), main falls back to `ConfigFile::default()` so
logging still initializes. If `init_cli_logging` itself fails (e.g.,
log directory unwritable), main prints a stderr warning and continues
without a log file rather than aborting.

Manual verification:
- `RUST_LOG=trace xearthlayer config get general.update_check`
  → `~/.xearthlayer/xearthlayer.log` is created (was missing before)
- `xearthlayer --debug diagnostics`
  → log file populated with INFO events from wgpu detection
- `xearthlayer run --debug --help` and `xearthlayer --help` both
  surface the global flags correctly

Fixes #194

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… error exit

Two related changes in service of #194's underlying goal — making the
log file actually useful for diagnosing package-install failures like
the one dee-sea reported in #186.

Flush on error exit:
  Previously `CliError::exit` called `std::process::exit` directly,
  which skips local destructors. The `LoggingGuard` in main therefore
  never dropped on the error path, and `tracing-appender`'s background
  writer was killed mid-flight with its queue unflushed — every failed
  CLI invocation produced an empty log file. Replace `exit` with
  `report() -> u8`; have `main` return `ExitCode` so the guard drops
  normally and the appender flushes before the process terminates.

Structured tracing across the install pipeline:
  Add INFO events at every stage transition in `installer.rs` —
  package install started, metadata fetched, download phase started /
  completed (with bytes + elapsed_secs), reassembly started / completed,
  extraction started / completed, package install completed. All
  emitted with structured fields (region, version, parts, bytes, etc.)
  for grep / awk analysis.

  In `download/orchestrator.rs`: DEBUG event when a part download
  starts (with url and filename), DEBUG on success (with bytes), DEBUG
  on abort (sibling failed first). WARN on each retry (with attempt
  number and last error). ERROR when retries exhaust (with attempts
  count and last error).

Manual verification:
  - `xearthlayer --debug packages install eu` (errors AlreadyInstalled)
    now writes "package install started" to the log instead of leaving
    it empty
  - `xearthlayer --debug diagnostics` (success path) still writes its
    wgpu detection events
  - All 2,404 unit tests still pass via `make pre-commit`

This unblocks the #186 diagnostic loop: when dee-sea retries on his
build of `main`, his failed install will produce a structured log
showing exactly which parts failed, how many retries each got, and
what the underlying error message was.

Refs #194

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@samsoir samsoir merged commit ebba5c8 into main May 11, 2026
2 checks passed
@samsoir samsoir deleted the bugfix/194-cli-logging-init branch May 11, 2026 02:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: log file not written for packages CLI commands

1 participant