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.
This commit is contained in:
@@ -492,23 +492,36 @@ func (s *Server) listCerts(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---- conn logs ----
|
// ---- conn logs ----
|
||||||
|
|
||||||
func (s *Server) listConnLogs(c *gin.Context) {
|
func (s *Server) listConnLogs(c *gin.Context) {
|
||||||
|
// 诊断信息:返回每个实例 status.log 的存在情况和原始内容前 200 字节
|
||||||
|
instanceID := c.Query("instance")
|
||||||
|
|
||||||
// 1) 数据库里的历史日志(有断开时间的老连接)
|
// 1) 数据库里的历史日志(有断开时间的老连接)
|
||||||
history := s.Svc.Store.ListConnLogs(c.Query("instance"), 200)
|
history := s.Svc.Store.ListConnLogs(instanceID, 200)
|
||||||
|
|
||||||
// 2) 实时解析所有实例的 status.log,生成当前在线用户
|
// 2) 实时解析所有实例的 status.log,生成当前在线用户
|
||||||
var live []model.ConnectionLog
|
var live []model.ConnectionLog
|
||||||
|
var diag []map[string]any
|
||||||
instances := s.Svc.Store.ListInstances()
|
instances := s.Svc.Store.ListInstances()
|
||||||
for _, in := range instances {
|
for _, in := range instances {
|
||||||
if c.Query("instance") != "" && c.Query("instance") != in.ID {
|
if instanceID != "" && instanceID != in.ID {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
statusPath := filepath.Join(s.Svc.Cfg.InstanceDir(in.Name), "status.log")
|
statusPath := filepath.Join(s.Svc.Cfg.InstanceDir(in.Name), "status.log")
|
||||||
entries, err := s.Svc.Ovm.ParseStatus(statusPath)
|
fi, statErr := os.Stat(statusPath)
|
||||||
if err != nil {
|
d := map[string]any{
|
||||||
continue
|
"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 {
|
for _, e := range entries {
|
||||||
live = append(live, model.ConnectionLog{
|
live = append(live, model.ConnectionLog{
|
||||||
InstanceID: in.ID,
|
InstanceID: in.ID,
|
||||||
@@ -535,7 +548,11 @@ func (s *Server) listConnLogs(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
merged = append(merged, h)
|
merged = append(merged, h)
|
||||||
}
|
}
|
||||||
c.JSON(200, merged)
|
// 同时返回诊断信息(方便排查),前端忽略 _diag 字段
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"logs": merged,
|
||||||
|
"_diag": diag,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- backups ----
|
// ---- backups ----
|
||||||
|
|||||||
@@ -33,7 +33,11 @@ const list = ref([])
|
|||||||
const instances = ref([])
|
const instances = ref([])
|
||||||
const instanceId = 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) {
|
function fmt(n) {
|
||||||
if (!n) return '0'
|
if (!n) return '0'
|
||||||
const u = ['B','KB','MB','GB','TB']; let i=0,v=n
|
const u = ['B','KB','MB','GB','TB']; let i=0,v=n
|
||||||
|
|||||||
Reference in New Issue
Block a user