v0.111.0: remote app-log diagnostics — error context capture (±5 lines, capped+redacted) + on-demand log tails via ACK pull pattern

- metrics: LogIssue.Context on first-occurrence errors (≤11 lines, ≤400 chars/line, warns carry none); RedactLine sanitizer (password/token/api-key/bearer/64-hex) applied to everything shipped; FetchContainerLogTail
- report: 16KB per-report context budget (lowest-count issues dropped first); log_tail_requests ACK flag → next report ships log_tails (200 lines, ≤64KB/app head-truncated, ordered, redacted); consume-once drain
- tests: synthetic-window context capture, caps, redaction, budget order, consume-once, fetch-error skip

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
2026-07-10 15:43:29 +02:00
parent 332725a024
commit 544c42a618
12 changed files with 670 additions and 8 deletions
+13
View File
@@ -155,6 +155,19 @@ func BuildReport(
// App telemetry (metrics + log scan)
r.AppTelemetry = buildAppTelemetrySection(stackMgr, metricsStore, logger)
// On-demand log tails (v0.111.0): drain the ACK-requested apps and collect their tails.
// Stacks go through the existing compose-logs plumbing; the controller's own container
// through the scanner's docker-logs plumbing. Consume-once: drained here, re-armed by
// the next ACK if the push fails.
if tailApps := drainPendingLogTails(); len(tailApps) > 0 {
r.LogTails = buildLogTailsSection(tailApps, func(app string, lines int) (string, error) {
if app == controllerContainerName {
return metrics.FetchContainerLogTail(app, lines)
}
return stackMgr.GetLogs(app, lines)
}, logger)
}
// Geo-restriction status — ALWAYS present (even when never configured) so the hub
// always renders the section. A nil pointer (omitempty) made the hub hide the whole
// section for a never-configured controller; a present-but-disabled report renders