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:
@@ -440,6 +440,42 @@ func (s *Store) migrate() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// v0.43.0 — remote app-log diagnostics. Additive columns on app_log_issues:
|
||||
// context = JSON array of ±5 redacted lines around the FIRST occurrence (first capture
|
||||
// wins — stable repro context, no churn); context_customer = whose box it came from
|
||||
// (provenance); dismissed_at = dismissal instead of futile deletion (the controller
|
||||
// re-sends recurring issues — a delete is undone minutes later; a dismissal stays until
|
||||
// a genuinely NEW occurrence with last_seen > dismissed_at resurfaces the row).
|
||||
s.db.Exec(`ALTER TABLE app_log_issues ADD COLUMN context TEXT`)
|
||||
s.db.Exec(`ALTER TABLE app_log_issues ADD COLUMN context_customer TEXT`)
|
||||
s.db.Exec(`ALTER TABLE app_log_issues ADD COLUMN dismissed_at DATETIME`)
|
||||
|
||||
// log_tail_requests: the operator's pending "send me this app's logs" intents — the
|
||||
// pull-based ACK flag (the hub NEVER connects into a box; the controller sees the flag
|
||||
// in its report ACK and ships the tail on its next cycle). One active request per
|
||||
// (customer, app) — a re-click refreshes requested_at. Cleared when the tail arrives
|
||||
// (consume-once). app_log_tails: the received tails, transient — keep the last 2 per
|
||||
// (customer, app), older pruned at insert.
|
||||
_, err = s.db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS log_tail_requests (
|
||||
customer_id TEXT NOT NULL,
|
||||
app_name TEXT NOT NULL,
|
||||
requested_at DATETIME NOT NULL,
|
||||
PRIMARY KEY (customer_id, app_name)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS app_log_tails (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
customer_id TEXT NOT NULL,
|
||||
app_name TEXT NOT NULL,
|
||||
collected_at DATETIME NOT NULL,
|
||||
received_at DATETIME NOT NULL,
|
||||
lines_json TEXT NOT NULL
|
||||
);
|
||||
`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user