Record command history and add mapbox history - #56
Conversation
Rebuilt on top of the local run log (#56), which now carries the shared parts: http::send and its guard, dated_jsonl, and cli() returning the exit code. What is left here is telemetry's own: telemetry_event, telemetry_sink (delivering through dated_jsonl), and one reporting call beside each of the log's. The event is still written only into a config directory that already exists. Telemetry finishes before the log so that the log creating ~/.mapbox does not change that for the same run.
Built from the run_record the local log (#56) already collects, so no call site changes here: telemetry_event chooses each field from the record under its privacy rules, and telemetry_sink delivers the line (to a local file for now, through dated_jsonl). run_record::finish hands the record to telemetry before the log, because the event is written only into a config directory that already existed and the log may create one.
Each run appends one line of execution metadata to ~/.mapbox/history/<UTC date>.jsonl, kept 30 days: the command path from the command tree, invocation, exit code, error code, duration, request count and the last five request ids. Never an argument value, URL, error message or account: arguments carry search terms, file paths and ids. The file is written through dated_jsonl: private, one file per UTC day, pruned to the retention window. On by default. `mapbox config set history off` turns it off for good, MAPBOX_HISTORY=0 or =1 for a session over the setting. With it off, nothing is written and no directory is created. With it on, the config directory is created when missing, so history works the same for someone who only ever set MAPBOX_ACCESS_TOKEN. Not recorded: --help, --version, completion, history itself, and runs under sudo, whose root-owned files would stop the user's own runs appending (aws/aws-cli#10031). `mapbox history list` shows the most recent runs, newest first, and `mapbox history show [id]` one run, the newest by default, found by any prefix of its id. The read-only-command tests that held ~/.mapbox untouched now check both sides: with history on only `history/` may appear, with it off nothing.
- `history list` text output gets a header row. - Tests that hold a run to leaving nothing on disk turn history off in their helper instead of looping over both states; tests/history.rs checks what history leaves, and that skipped runs create nothing. - README says turning history off keeps what was already recorded. - Drop the setting count from config.rs's module doc, which every new key would have to edit.
Moves dated_jsonl::shed here from the diagnostic-log PR so history, on by default, is bounded by size as well as by age. trim now keeps exactly the bytes shed hands it; the margin was applied twice before.
For each run command history records, `mapbox config set log on` (or MAPBOX_LOG=1) adds a line of detail to ~/.mapbox/logs/<UTC date>.jsonl, linked to the history record by its id: the command line with tokens redacted, which token was used, each request (method, redacted URL, status, request id, timing) and the error message. Off by default. Logging needs history. With history off it never runs, and `config set log on` refuses with history_required rather than store a setting that does nothing. A run history doesn't record gets no log either, since nothing could lead back to it. `mapbox history show` includes the log and says what became of it: diagnostics.status is captured, not_captured (logging was off) or unavailable (captured, since expired or evicted). The history record carries diagnosticsCaptured so the last two can be told apart. There is no separate logs command. Logs are kept up to 30 days and 100 MB in total. Past the limit the oldest go first, down to the line, and their history records stay. On every run, a day of logs whose day of history has expired or gone is deleted, logging on or off.
- History and the log each read the clock, so a run finishing across UTC midnight could put its log a day after its history, and the same run's cleanup then deleted it. Both lines now share one time and one day's file, which also drops next_date. - The log no longer repeats what the history record has (command, invocation, exitCode, durationMs, version). - scrub_tokens now finds a token that starts inside a word, as after a percent-encoded `=` (`%3Dpk.`) or in a short-flag cluster (`-ytpk.`). - tests/history.rs clears MAPBOX_LOG like the other suites. - README: a day of logs goes with its day of history, and the refusal follows the `history` setting, not MAPBOX_HISTORY.
mattpodwysocki
left a comment
There was a problem hiding this comment.
Built and tested this too, live, not just read the diff: ran a few real commands with a scratch MAPBOX_CONFIG_DIR, checked mapbox history list/show in both output modes, toggled config set history off and MAPBOX_HISTORY=1 to confirm the override actually overrides, and checked file permissions on disk (0600 on the files, 0700 on the directory). Everything matched what the PR body claims, including the privacy boundary: I grepped the actual jsonl line and confirmed no argument values, URLs, or account info are in it, only what the module doc promises.
Also traced through the id-prefix matching, the not-found/ambiguous errors, and the shed/prune math by hand against the existing unit tests (the three-day, 100-byte-line shedding test correctly drops the oldest whole day, then trims the next to make up the remainder, leaving the newest day whole).
One thing worth naming since it compounds: finish_locked now calls run_history::write directly rather than main() orchestrating it. Given the panic hook has to call finish_locked too (main() never gets control back after a panic), this is probably the right call, calling out to consumers from inside finish_locked is the only way a panicking run still gets a history line, but it does mean run_record is accumulating direct knowledge of every consumer as more of these land. Not asking for a change here, just flagging it for whoever's stacking #46 on top.
Approving.
Add diagnostic logs, shown through mapbox history show
Stacked on #57. #58 (diagnostic logs) and #46 (telemetry events) are stacked on this.
Adds command history: a local record of recent runs, read back by
mapbox history, so a user or support can see what ran and how it ended.~/.mapbox/history/<UTC date>.jsonl, kept 30 days and at most 10 MB: the command path (search forward), invocation, exit code, error code, duration, request count and the last five request ids. No argument values, URLs, error messages or account — arguments carry search terms, file paths and ids.mapbox config set history offturns it off;MAPBOX_HISTORY=0/=1overrides that for a session. With it off, nothing is written and no directory is created.--help,--version,completion,historyitself, and runs undersudo(root-owned files would block the user's own runs, as in v2.33.9: Permission denied on ~/.aws/config when running as non-root user after initialization aws/aws-cli#10031).mapbox history listshows recent runs, newest first.mapbox history show [id]shows one run, the newest by default, matched by any prefix of its id. New error codes:history_not_found,history_ambiguous_id,history_empty.dated_jsonl: private (0700/0600), one file per UTC day, pruned by age and size, and it deletes only files named exactlyYYYY-MM-DD.jsonl(added toMAY_DELETE). Add diagnostic logs, shown through mapbox history show #58 and WIP: Record one cli.command telemetry event per run #46 reuse it.Behavior changes
~/.mapbox/history/, and~/.mapboxif missing, even for read-only commands. Tests that hold a command to creating nothing now run with history off;tests/history.rschecks what history leaves.mapbox config listreports a second key,history.Not checked: pruning happens on a day's first write, so with history off, old records stay until deleted (the README says so). The 10 MB limit is only unit-tested. The
#[cfg(not(unix))]paths only compile in CI.