hub v0.43.0: remote app-log diagnostics — copyable issues + context + on-demand log tails + range/dismissal fixes
- store: app_log_issues gains context/context_customer (first capture wins) + dismissed_at (resurface only on last_seen > dismissed_at); log_tail_requests (pending operator intents, consume-once) + app_log_tails (transient, keep last 2 per app) - api: /report ingests log_tails (stores + clears the request); ACK advertises log_tail_requests (same additive omit-when-empty pattern as escrow) - web: Known Issues rows click-to-expand (full copyable message + context with provenance + explicit affected-customers list); Dismiss replaces Delete; period selector now filters issues (F); ?customer= filtered view + customer-page drill-down links (H); per-app Request-log-tail button + pending badge + App Log Tails section + ordered tail view with line numbers + .log download; customer-visible log_tail_requested event - tests: store (context first-capture/late-adopt, range filter, dismissal old-window vs new-occurrence, tail request/fulfill/prune/scoping), api ACK round-trip, web render (expanded row, customer page sections, tail view + download + cross-customer 404) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
@@ -49,6 +49,7 @@
|
||||
{{else if eq .Flash "offsite_unfrozen"}}Offsite storage unfrozen — read-write restored.
|
||||
{{else if eq .Flash "blocked"}}Customer blocked — hidden from Dashboard.
|
||||
{{else if eq .Flash "unblocked"}}Customer unblocked — visible on Dashboard again.
|
||||
{{else if eq .Flash "log_tail_requested"}}Log tail requested — the controller delivers it on its next report cycle (a few minutes). A customer-visible event line was recorded.
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -616,18 +617,62 @@
|
||||
<th>Catalog Limit</th>
|
||||
<th>Errors</th>
|
||||
<th>Warnings</th>
|
||||
<th>Logs</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .AppTelemetry}}
|
||||
<tr>
|
||||
<td><a href="/apps/{{.AppName}}">{{if .DisplayName}}{{.DisplayName}}{{else}}{{.AppName}}{{end}}</a></td>
|
||||
<td><a href="/apps/{{.AppName}}?customer={{$.CustomerID}}" title="Known issues filtered to this customer">{{if .DisplayName}}{{.DisplayName}}{{else}}{{.AppName}}{{end}}</a></td>
|
||||
<td class="{{memoryColor .MemoryCurrentMB .CatalogLimit}}">{{formatFloat .MemoryCurrentMB}} MB</td>
|
||||
<td>{{formatFloat .MemoryAvgMB}} MB</td>
|
||||
<td>{{formatFloat .MemoryPeakMB}} MB</td>
|
||||
<td>{{if .CatalogLimit}}{{.CatalogLimit}}{{else}}—{{end}}</td>
|
||||
<td>{{if gt .LogErrors 0}}<span class="badge badge-error">{{.LogErrors}}</span>{{else}}0{{end}}</td>
|
||||
<td>{{if gt .LogWarnings 0}}<span class="badge badge-warn">{{.LogWarnings}}</span>{{else}}0{{end}}</td>
|
||||
<td>
|
||||
{{if index $.PendingTails .AppName}}
|
||||
<span class="badge badge-neutral" title="The controller delivers the tail on its next report cycle">tail pending</span>
|
||||
{{else}}
|
||||
<form method="POST" action="/customers/{{$.CustomerID}}/request-log-tail" style="display: inline;">
|
||||
<input type="hidden" name="_csrf" value="{{$.CSRFToken}}">
|
||||
<input type="hidden" name="app" value="{{.AppName}}">
|
||||
<button type="submit" class="btn btn-sm" title="Pull-based: the controller ships the last 200 log lines on its next report; a customer-visible event line is recorded">Request log tail</button>
|
||||
</form>
|
||||
{{end}}
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
{{end}}
|
||||
|
||||
<!-- Received log tails (on-demand, transient — last 2 per app) -->
|
||||
{{if .HasLogTails}}
|
||||
<section class="card">
|
||||
<h2>App Log Tails <span class="text-muted" style="font-size: 0.85em; font-weight: normal;">(on-demand, last 2 per app kept)</span></h2>
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>App</th>
|
||||
<th>Collected</th>
|
||||
<th>Received</th>
|
||||
<th>Lines</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .LogTails}}
|
||||
<tr>
|
||||
<td style="font-family: var(--font-mono);">{{.AppName}}</td>
|
||||
<td>{{.CollectedAt.Format "2006-01-02 15:04:05"}} ({{timeAgo .CollectedAt}})</td>
|
||||
<td>{{timeAgo .ReceivedAt}}</td>
|
||||
<td>{{len .Lines}}</td>
|
||||
<td style="white-space: nowrap;">
|
||||
<a href="/customers/{{$.CustomerID}}/log-tail/{{.ID}}" class="btn btn-sm">View</a>
|
||||
<a href="/customers/{{$.CustomerID}}/log-tail/{{.ID}}?download=1" class="btn btn-sm">Download .log</a>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
|
||||
Reference in New Issue
Block a user