b1250ad5a0
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
74 lines
5.0 KiB
Markdown
74 lines
5.0 KiB
Markdown
# REPORT — v0.111.0: remote app-log diagnostics (error context + on-demand log tails)
|
|
|
|
**Date:** 2026-07-10 · **Version:** v0.111.0 (from v0.110.0) · **Pairs with:** hub v0.43.0
|
|
**Spec:** TASK — remote app-log diagnostics (Parts B, D-controller, E)
|
|
|
|
## What shipped
|
|
|
|
### Part B — error context capture (`internal/metrics/logscanner.go`)
|
|
- The scan loop was extracted into the pure `analyzeLogLines(lines []string)` (first-ever unit tests
|
|
for the scanner). On the FIRST occurrence of an error-severity fingerprint in a scrape window,
|
|
`captureContext` attaches `LogIssue.Context`: up to 5 raw lines before + the line + up to 5 after,
|
|
ordered as emitted. Repeats never re-capture; warns never carry context.
|
|
- Caps: ≤11 lines; each line ANSI-stripped + rune-truncated to 400 chars with `…`; and a HARD 16KB
|
|
per-report budget in `internal/report/telemetry.go` (`enforceContextBudget` — context dropped from
|
|
the lowest-count issues first, across all apps).
|
|
- Additive `context` field (omitempty) on the report's issue objects — old hubs ignore it.
|
|
|
|
### Part E — sanitization (`internal/metrics/redact.go`)
|
|
- `RedactLine`: case-insensitive `password|passwd|secret|token|api[_-]?key|authorization|bearer`
|
|
followed by `[=: ]\S+` → value `[REDACTED]` (the value group also swallows a `Bearer ` prefix so
|
|
`Authorization: Bearer <tok>` redacts fully in one pass); any 64-hex string → `[REDACTED-HEX64]`.
|
|
Applied to EVERY context line and tail line before it leaves the box — controller-side, authoritative.
|
|
|
|
### Part D (controller half) — on-demand log tails (`internal/report/logtail.go`)
|
|
- `PushResponse` gains `log_tail_requests: [app…]` (same additive ACK-flag pull pattern as
|
|
escrow/config-refresh — the guest listens to no one). `OnPushResponse` → `SetPendingLogTails`;
|
|
the NEXT `BuildReport` drains (consume-once) and ships `log_tails: [{app, collected_at, lines[]}]`.
|
|
- Collection reuses existing plumbing: `stacks.GetLogs(app, 200)` (compose logs, ordered) for stacks;
|
|
new `metrics.FetchContainerLogTail` (`docker logs --tail=200`, 15s timeout) for the
|
|
`felhom-controller` container. Caps: ≤400 chars/line, ≤64KB/app HEAD-truncated (newest lines kept),
|
|
every line redacted.
|
|
- Fail-safe: fetch or push failure leaves the hub's request pending → the next ACK re-arms it.
|
|
|
|
## Tests + red-proofs (all executed; failures verbatim)
|
|
|
|
- **B red-proof** — capture call dropped → `TestAnalyzeLogLines_ErrorContextWindow` FAILED:
|
|
`context has 0 lines, want 11: []string(nil)`. Restored → green.
|
|
- **E red-proof** — `RedactLine` gutted (return s) → all redact tests FAILED with the secrets visible,
|
|
e.g. `RedactLine("password=hunter2 token: abc Bearer xyz") = "password=hunter2 token: abc Bearer xyz"`
|
|
and `64-hex secret shipped: "repo unlock with ab12…"`. Restored → green.
|
|
- **D red-proof** — consume-once clear removed from `drainPendingLogTails` →
|
|
`TestPendingLogTails_ConsumeOnce` FAILED: `second drain = [gokapi cwa] — tails would ship on EVERY
|
|
report`. Restored → green.
|
|
- Non-hollow coverage: exact ±5 ordered window; repeat keeps the FIRST occurrence's context; warn has
|
|
no context; 400-char truncation (`…`); budget drops lowest-count first / under-budget untouched;
|
|
window clamps at buffer edges; 10-issue cap + count ordering intact; tail order + redaction +
|
|
line-cap + 64KB newest-kept + fetch-error skip + empty-ACK clears stale pending.
|
|
- Full suite green (`go build && go vet && go test ./...`). One PRE-EXISTING flake observed once in
|
|
`internal/web` (`TestBackupTier2Restore_DoubleClickRefused`, async timing) — passed 4/4 on re-run,
|
|
unrelated to this diff.
|
|
|
|
## Deploy + live validation (demo 9201)
|
|
|
|
- Image `0.111.0` built on 180, deployed via the bootstrap mechanism →
|
|
`gitea.dooplex.hu/admin/felhom-controller:0.111.0 Up (healthy)`, clean startup, channel up.
|
|
- Synthetic error injected into the controller container's own log stream (the controller rides the
|
|
same telemetry pipeline as apps): neighbor lines + `password=hunter2` + an `ERROR:` line —
|
|
hub-side landing verified in the hub REPORT (felhom.eu) including LIVE redaction of the secret.
|
|
- Method note: server-side pipeline exercised end-to-end (report cycle → hub ingest → hub DB); the
|
|
hub UI click-path is render-tested (the hub operator password is Viktor-only — CC cannot log in).
|
|
|
|
## Live tail round-trip — COMPLETED (Viktor logged the browser in; CC drove the hub UI)
|
|
|
|
Request clicked (14:35:20Z) → "tail pending" badge + transparency event → 14:37:34Z report's ACK armed
|
|
the controller → 14:52:34Z report: `log-tail collected for felhom-controller: 200 lines` (push 2,545 B →
|
|
18,849 B) → hub stored tail #1, pending badge cleared (consume-once), ordered 200-line viewer +
|
|
Download .log verified in the browser. Details in felhom.eu/REPORT.md.
|
|
|
|
## Residuals / notes
|
|
|
|
- The live tail exercised the `FetchContainerLogTail` path (no customer app deployed on the demo right
|
|
now); the `stacks.GetLogs` path is unit-covered via the fetch seam.
|
|
- Do NOT request tails from Peti's box as part of this task — Viktor's call from the UI (per spec).
|