Log to stderr and stop discarding With attributes (closes #82)
All checks were successful
check / check (push) Successful in 4m20s
All checks were successful
check / check (push) Successful in 4m20s
Closes #97. internal/log attached both handlers to os.Stdout, so any record that was not suppressed landed in the middle of a --json document. WARN and ERROR are never suppressed, so this was not hypothetical: a config file with permissions looser than 0600 was enough to break `vaultik snapshot list --json | jq`. Both handlers now write to os.Stderr, and the TTY-vs-JSON format choice tests os.Stderr rather than os.Stdout - the format has to follow the stream the records land on, or a redirected stderr gets colorized whenever stdout happens to be a terminal. User-visible: --verbose and --debug output moves to stderr too, so `vaultik snapshot list -v > out.txt` no longer captures diagnostics. --quiet and --cron semantics are unchanged. TTYHandler.WithAttrs and WithGroup discarded their arguments and returned the receiver, while their doc comments claimed otherwise, so attributes passed through the exported log.With vanished. The effect was environment-dependent in the worst direction: handler choice is by TTY-ness, so attributes disappeared on a terminal - where a developer is debugging - and appeared correctly in CI. Both now return a new handler with copied state rather than mutating the receiver, since slog permits a handler to be shared and derived from concurrently. A test asserts the TTY and JSON handlers emit the same attribute set, which is the test that would have caught the original defect. The local workaround in snapshot_list.go is removed now that the logger no longer writes to stdout. The collect-then-emit machinery is kept, but for a different reason than it was added: emitting from the fetch workers would order warnings by network timing, whereas key-order emission after group.Wait() is deterministic run to run. Not yet complete: --json stdout still carries the startup banner, which internal/cli/entry.go writes before cobra parses and which bannerSuppressedInArgs does not recognise --json for. That is the remaining stdout contamination path and is tracked in #106.
This commit was merged in pull request #107.
This commit is contained in:
34
README.md
34
README.md
@@ -113,11 +113,32 @@ vaultik version
|
||||
### global flags
|
||||
|
||||
* `--config <path>`: Path to config file (default: `$VAULTIK_CONFIG`, then platform config dir, then `/etc/vaultik/config.yml`)
|
||||
* `--verbose`, `-v`: Enable verbose output
|
||||
* `--debug`: Enable debug output
|
||||
* `--verbose`, `-v`: Enable verbose output (on stderr — see below)
|
||||
* `--debug`: Enable debug output (on stderr — see below)
|
||||
* `--quiet`, `-q`: Suppress non-error output (also suppresses startup banner)
|
||||
* `--skip-errors`: Continue past per-file errors instead of aborting (applies to `snapshot create` and `restore`)
|
||||
|
||||
### stdout and stderr
|
||||
|
||||
Log output — everything from `--verbose` and `--debug`, and every
|
||||
warning and error the logger emits — goes to **stderr**. stdout carries
|
||||
the output you asked for: tables, and the documents produced by `--json`.
|
||||
|
||||
This means `vaultik snapshot list --verbose > out.txt` captures the
|
||||
listing and leaves the diagnostics on your terminal. To capture both,
|
||||
redirect stderr as well (`> out.txt 2> log.txt`, or `> out.txt 2>&1` to
|
||||
interleave them).
|
||||
|
||||
The split is what makes `--json` usable from a script. Warnings and
|
||||
errors are never suppressed — not by `--quiet`, not by `--cron` — so a
|
||||
logger on stdout would eventually land a log line inside a JSON
|
||||
document and break the parse. A config file with group- or
|
||||
world-readable permissions is enough to trigger it.
|
||||
|
||||
Format follows the stream: when stderr is a terminal the records are
|
||||
colorized one-liners, and when it is redirected or piped they are
|
||||
JSON, one object per line.
|
||||
|
||||
### environment variables
|
||||
|
||||
* `VAULTIK_AGE_SECRET_KEY`: Age private key for decryption (required for `snapshot restore` and `snapshot verify --deep`)
|
||||
@@ -208,8 +229,9 @@ local index alone, and still exits zero.
|
||||
(whether the snapshot is in the local index), `remote_key` (the full
|
||||
64-character storage key), and `remote_present` (whether it was seen
|
||||
on the destination store, or `null` if the destination could not be
|
||||
listed). The warning about an unlistable destination goes to stderr
|
||||
so stdout stays a single parseable document.
|
||||
listed). Warnings about an unlistable destination, unreadable
|
||||
manifests, and a truncated listing all go to stderr through the
|
||||
logger, so stdout stays a single parseable document.
|
||||
|
||||
**`snapshot verify`**: Verify snapshot integrity.
|
||||
* Default (shallow): checks that all blobs referenced in the manifest exist in storage
|
||||
@@ -504,6 +526,10 @@ All user-facing output goes through helpers in `internal/ui` and conforms
|
||||
to a uniform style. Color is enabled when stdout is a TTY and the
|
||||
`NO_COLOR` environment variable is unset (https://no-color.org/).
|
||||
|
||||
`internal/ui` writes to stdout; it is the output the user asked for.
|
||||
Structured log records are a different thing and go through
|
||||
`internal/log`, which writes to stderr (see "stdout and stderr" above).
|
||||
|
||||
Message classes:
|
||||
|
||||
| Class | Marker | Alignment | Use for |
|
||||
|
||||
Reference in New Issue
Block a user