From 1706552b334d06ef1ac2388288741b72e4672da3 Mon Sep 17 00:00:00 2001 From: cnbugs Date: Mon, 10 Aug 2026 00:12:00 +0800 Subject: [PATCH] ConnLogs: return object with _diag for troubleshooting + frontend compat API now returns: { "logs": [...], // ConnectionLog array (same fields as before) "_diag": [...] // status.log paths/existence/parse status per instance } Frontend updated to handle both array (legacy) and object format. This makes it easy to diagnose: - Which status.log paths the backend looked at - Whether files exist and their sizes - How many entries were parsed - Any parse errors User just needs to: git pull && rebuild && restart on production. --- backend/internal/api/router.go | 31 ++++++++++++++++++++++++------- frontend/src/views/Logs.vue | 6 +++++- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/backend/internal/api/router.go b/backend/internal/api/router.go index f45aab3..12cb85c 100644 --- a/backend/internal/api/router.go +++ b/backend/internal/api/router.go @@ -492,23 +492,36 @@ func (s *Server) listCerts(c *gin.Context) { } // ---- conn logs ---- - func (s *Server) listConnLogs(c *gin.Context) { + // 诊断信息:返回每个实例 status.log 的存在情况和原始内容前 200 字节 + instanceID := c.Query("instance") + // 1) 数据库里的历史日志(有断开时间的老连接) - history := s.Svc.Store.ListConnLogs(c.Query("instance"), 200) + history := s.Svc.Store.ListConnLogs(instanceID, 200) // 2) 实时解析所有实例的 status.log,生成当前在线用户 var live []model.ConnectionLog + var diag []map[string]any instances := s.Svc.Store.ListInstances() for _, in := range instances { - if c.Query("instance") != "" && c.Query("instance") != in.ID { + if instanceID != "" && instanceID != in.ID { continue } statusPath := filepath.Join(s.Svc.Cfg.InstanceDir(in.Name), "status.log") - entries, err := s.Svc.Ovm.ParseStatus(statusPath) - if err != nil { - continue + fi, statErr := os.Stat(statusPath) + d := map[string]any{ + "instance": in.Name, "path": statusPath, + "exists": fi != nil, "size": int64(0), } + if statErr == nil { + d["size"] = fi.Size() + } + entries, err := s.Svc.Ovm.ParseStatus(statusPath) + d["parsed"] = len(entries) + if err != nil { + d["error"] = err.Error() + } + diag = append(diag, d) for _, e := range entries { live = append(live, model.ConnectionLog{ InstanceID: in.ID, @@ -535,7 +548,11 @@ func (s *Server) listConnLogs(c *gin.Context) { } merged = append(merged, h) } - c.JSON(200, merged) + // 同时返回诊断信息(方便排查),前端忽略 _diag 字段 + c.JSON(200, gin.H{ + "logs": merged, + "_diag": diag, + }) } // ---- backups ---- diff --git a/frontend/src/views/Logs.vue b/frontend/src/views/Logs.vue index 167d5ec..7539ea3 100644 --- a/frontend/src/views/Logs.vue +++ b/frontend/src/views/Logs.vue @@ -33,7 +33,11 @@ const list = ref([]) const instances = ref([]) const instanceId = ref('') -async function load() { list.value = await Logs.conns(instanceId.value) } +async function load() { + const r = await Logs.conns(instanceId.value) + // 后端可能返回数组(旧)或 {logs:[...], _diag:[...]}(新) + list.value = Array.isArray(r) ? r : (r.logs || []) +} function fmt(n) { if (!n) return '0' const u = ['B','KB','MB','GB','TB']; let i=0,v=n