v0.116.0: observability pass — always-on debug ring + leveled sweep + agent tab + self-log pull — MinAgent: 0.81.0

Capture layer: LogBuffer always exists; logger = MultiWriter(LevelFilterWriter
(stdout, logging.level), ring) so DEBUG detail exists remotely without a config
flip while docker logs keep respecting the level. New internal/logx leveled
helpers. Report ACK gains controller_log_requested (additive); next report
ships controller_log_tail (128KB, consume-once, app-tail wire byte-compatible).
Debug page: Vezérlő|Ügynök tabs; agent tab proxies agent /debug/logs with the
pre-0.83 notice on typed 404. Sweep: netstorage_job phases, netprobe, handler
validation refusals + orphan WARN, SupportsWithSource gate line, agentapi
per-call DEBUG, migrate phase lines, tier2/offbox unswallowed persists.
Red-proofs: filter-disabled, drain-removed, dropped-phase-line all FAIL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
2026-07-11 16:45:57 +02:00
parent 647116480a
commit 26a43708b7
25 changed files with 797 additions and 31 deletions
+39
View File
@@ -5,6 +5,7 @@ import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"net/http"
"os"
@@ -13,6 +14,7 @@ import (
"strings"
"time"
"gitea.dooplex.hu/admin/felhom-controller/internal/agentapi"
"gitea.dooplex.hu/admin/felhom-controller/internal/appexport"
"gitea.dooplex.hu/admin/felhom-controller/internal/monitor"
"gitea.dooplex.hu/admin/felhom-controller/internal/report"
@@ -79,6 +81,8 @@ func (s *Server) handleDebugAPI(w http.ResponseWriter, r *http.Request) {
// Section 8: Log viewer
case subpath == "logs" && r.Method == http.MethodGet:
s.debugLogBuffer(w, r)
case subpath == "agent-logs" && r.Method == http.MethodGet:
s.debugAgentLogs(w, r)
// Section 9: App Export/Import
case subpath == "appexport/status" && r.Method == http.MethodGet:
@@ -528,6 +532,41 @@ func (s *Server) debugLogBuffer(w http.ResponseWriter, r *http.Request) {
})
}
// debugAgentLogs proxies the host agent's always-DEBUG capture ring (agent ≥ 0.83.0)
// for the Debug page's "Ügynök" tab. A pre-0.83 agent has no such route — the typed
// 404 (StatusError, the features.go precedent) renders the "available after the
// agent's next update" notice instead of an error; nothing else is gated on it.
func (s *Server) debugAgentLogs(w http.ResponseWriter, r *http.Request) {
fetch := s.agentLogsFn
if fetch == nil {
client, err := s.agentClient()
if err != nil {
writeDebugJSON(w, http.StatusOK, false, "Az ügynök nincs konfigurálva ezen a rendszeren.", nil)
return
}
fetch = client.DebugLogs
}
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
resp, err := fetch(ctx)
if err != nil {
var se *agentapi.StatusError
if errors.As(err, &se) && se.Code == http.StatusNotFound {
writeDebugJSON(w, http.StatusOK, true, "", map[string]interface{}{
"unsupported": true,
"notice": "Az ügynök naplónézete az ügynök következő frissítése után érhető el.",
})
return
}
writeDebugJSON(w, http.StatusOK, false, err.Error(), nil)
return
}
writeDebugJSON(w, http.StatusOK, true, "", map[string]interface{}{
"entries": resp.Entries,
"total": resp.Total,
})
}
// ── Section 9: App Export/Import ─────────────────────────────────────
func (s *Server) debugAppExportStatus(w http.ResponseWriter, r *http.Request) {