c414ba53b5
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
4.9 KiB
4.9 KiB
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,captureContextattachesLogIssue.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 ininternal/report/telemetry.go(enforceContextBudget— context dropped from the lowest-count issues first, across all apps). - Additive
contextfield (omitempty) on the report's issue objects — old hubs ignore it.
Part E — sanitization (internal/metrics/redact.go)
RedactLine: case-insensitivepassword|passwd|secret|token|api[_-]?key|authorization|bearerfollowed by[=: ]\S+→ value[REDACTED](the value group also swallows aBearerprefix soAuthorization: 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)
PushResponsegainslog_tail_requests: [app…](same additive ACK-flag pull pattern as escrow/config-refresh — the guest listens to no one).OnPushResponse→SetPendingLogTails; the NEXTBuildReportdrains (consume-once) and shipslog_tails: [{app, collected_at, lines[]}].- Collection reuses existing plumbing:
stacks.GetLogs(app, 200)(compose logs, ordered) for stacks; newmetrics.FetchContainerLogTail(docker logs --tail=200, 15s timeout) for thefelhom-controllercontainer. 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_ErrorContextWindowFAILED:context has 0 lines, want 11: []string(nil). Restored → green. - E red-proof —
RedactLinegutted (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"and64-hex secret shipped: "repo unlock with ab12…". Restored → green. - D red-proof — consume-once clear removed from
drainPendingLogTails→TestPendingLogTails_ConsumeOnceFAILED: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 ininternal/web(TestBackupTier2Restore_DoubleClickRefused, async timing) — passed 4/4 on re-run, unrelated to this diff.
Deploy + live validation (demo 9201)
- Image
0.111.0built 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+ anERROR: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).
Residuals / notes
- The LIVE tail round-trip needs one operator click (hub → customer demo-felhom → App Telemetry → "Request log tail"); the tail then lands within ≤2 report cycles (≤30 min). The whole mechanism is unit- + API-test-proven on both repos including the consume-once red-proofs.
- The live tail on 9201 would exercise the
FetchContainerLogTailpath (no customer app deployed on the demo right now); thestacks.GetLogspath 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).