FIX-13: local opt-in, metadata-only structured logging for CLI/app server#36
Open
ChelseaKR wants to merge 1 commit into
Open
FIX-13: local opt-in, metadata-only structured logging for CLI/app server#36ChelseaKR wants to merge 1 commit into
ChelseaKR wants to merge 1 commit into
Conversation
…rver
Add on-device, opt-in structured JSON logging for the two local surfaces
(the `habitable` CLI and the loopback app server), mirroring the optional
relay's metadata-only discipline (`src/habitable/relay.py`). It exists so an
operator debugging their own device can trace *what happened* without ever
emitting anything that must stay on-device.
- New `src/habitable/obslog.py`: a shared `_JsonFormatter` (one compact JSON
object per line: ts/level/msg + an explicit `event_fields` map),
`configure_logging()` installing it idempotently on a dedicated `habitable`
logger (propagate=False), `enabled_from_env()` (only `HABITABLE_LOG=json`
opts in), and `log_event()` — the privacy gate: it is a no-op until logging
is configured and refuses any non-scalar field, so a dict/list/bytes/Path
payload can never ride onto a log line.
- CLI: top-level `--log-format json` flag; logging enabled by the flag or
`HABITABLE_LOG=json`; a metadata-only command-boundary event (name, ok,
duration_ms) — never arguments, paths, ids, or secrets. Off by default,
emitted to stderr so stdout stays clean.
- App server: honors `HABITABLE_LOG=json`; per-request access lines use a
redacted `_route_label` (e.g. `/api/issues/{issue}/timeline`) so no issue
id, static path, or query value is logged, and never a body.
- capture.py / sync.py: emit metadata-only events (capture result booleans,
redundant-authority counts, sync ciphertext sizes and merge counts).
- tests/test_obslog.py: formatter shape, env gating, the scalar-only guard,
CLI end-to-end JSON to stderr, off-by-default silence, app-server redacted
routes, and a no-plaintext guard paralleling tests/test_relay.py — a real
init/capture/export flow with sentinel passphrase, filename, media bytes,
and case text asserts none of them (nor the device fingerprint) appear in
the log stream.
- ROADMAP.md: mark the observability item shipped (FIX-13).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add on-device, opt-in structured JSON logging for the two local surfaces
(the
habitableCLI and the loopback app server), mirroring the optionalrelay's metadata-only discipline (
src/habitable/relay.py). It exists so anoperator debugging their own device can trace what happened without ever
emitting anything that must stay on-device.
src/habitable/obslog.py: a shared_JsonFormatter(one compact JSONobject per line: ts/level/msg + an explicit
event_fieldsmap),configure_logging()installing it idempotently on a dedicatedhabitablelogger (propagate=False),
enabled_from_env()(onlyHABITABLE_LOG=jsonopts in), and
log_event()— the privacy gate: it is a no-op until loggingis configured and refuses any non-scalar field, so a dict/list/bytes/Path
payload can never ride onto a log line.
--log-format jsonflag; logging enabled by the flag orHABITABLE_LOG=json; a metadata-only command-boundary event (name, ok,duration_ms) — never arguments, paths, ids, or secrets. Off by default,
emitted to stderr so stdout stays clean.
HABITABLE_LOG=json; per-request access lines use aredacted
_route_label(e.g./api/issues/{issue}/timeline) so no issueid, static path, or query value is logged, and never a body.
redundant-authority counts, sync ciphertext sizes and merge counts).
CLI end-to-end JSON to stderr, off-by-default silence, app-server redacted
routes, and a no-plaintext guard paralleling tests/test_relay.py — a real
init/capture/export flow with sentinel passphrase, filename, media bytes,
and case text asserts none of them (nor the device fingerprint) appear in
the log stream.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Backfill PR for a completed roadmap item (
roadmap/fix-13-local-opt-in-structured-logging-f, 1 commit(s) overmain). Part of the portfolio roadmap batch.