|
|
@ -537,6 +537,7 @@ |
|
|
|
<button class="btn-small" id="reset-logs-btn" title="重置日志">重置</button> |
|
|
|
<button class="btn-small" id="add-test-data-btn" title="添加测试数据">测试</button> |
|
|
|
<button class="btn-small" id="reconnect-btn" title="重新连接">重连</button> |
|
|
|
<button class="btn-small" id="debug-btn" title="调试信息">调试</button> |
|
|
|
<div class="column-settings"> |
|
|
|
<button class="btn-small" id="column-settings-btn" title="列设置">列设置</button> |
|
|
|
<div class="column-settings-dropdown" id="column-settings-dropdown"> |
|
|
@ -620,6 +621,7 @@ |
|
|
|
const resetLogsBtn = document.getElementById('reset-logs-btn'); |
|
|
|
const addTestDataBtn = document.getElementById('add-test-data-btn'); |
|
|
|
const reconnectBtn = document.getElementById('reconnect-btn'); |
|
|
|
const debugBtn = document.getElementById('debug-btn'); |
|
|
|
const logListPanel = document.querySelector('.log-list-panel'); |
|
|
|
const resizer = document.getElementById('drag-resizer'); |
|
|
|
const layerFilterOptions = document.getElementById('layer-filter-options'); |
|
|
@ -931,13 +933,30 @@ |
|
|
|
let note = ''; |
|
|
|
|
|
|
|
if (log && typeof log.Timestamp === 'number') { |
|
|
|
timestamp = formatDuration(log.Timestamp); |
|
|
|
title = `Duration from start: ${timestamp} (Raw: ${log.Timestamp}ms)`; |
|
|
|
// 检查是否为 Unix 时间戳(13位数字,通常大于 1000000000000) |
|
|
|
if (log.Timestamp > 1000000000000) { |
|
|
|
// Unix 时间戳,转换为本地时间 |
|
|
|
const date = new Date(log.Timestamp); |
|
|
|
timestamp = date.toLocaleString('zh-CN', { |
|
|
|
year: 'numeric', |
|
|
|
month: '2-digit', |
|
|
|
day: '2-digit', |
|
|
|
hour: '2-digit', |
|
|
|
minute: '2-digit', |
|
|
|
second: '2-digit', |
|
|
|
fractionalSecondDigits: 3 |
|
|
|
}); |
|
|
|
title = `Unix 时间戳: ${log.Timestamp} -> ${date.toISOString()}`; |
|
|
|
} else { |
|
|
|
// 持续时间(毫秒数) |
|
|
|
timestamp = formatDuration(log.Timestamp); |
|
|
|
title = `持续时间: ${timestamp} (${log.Timestamp}ms)`; |
|
|
|
} |
|
|
|
} else { |
|
|
|
timestamp = 'Invalid Time'; |
|
|
|
title = 'Timestamp from server was invalid or missing.'; |
|
|
|
title = '时间戳无效或缺失'; |
|
|
|
note = `<br><small class="text-muted">${title}</small>`; |
|
|
|
console.warn('Timestamp missing or invalid, using fallback for log:', log); |
|
|
|
console.warn('时间戳缺失或无效,使用默认值:', log); |
|
|
|
} |
|
|
|
|
|
|
|
return { timestamp, title, note }; |
|
|
@ -960,7 +979,7 @@ |
|
|
|
} |
|
|
|
|
|
|
|
// 更新日志列表 |
|
|
|
function updateLogList(logs, prepend = false) { |
|
|
|
function updateLogList(logs, prepend = false, isHistory = false) { |
|
|
|
if (!logs || logs.length === 0) return; |
|
|
|
|
|
|
|
// 收集新的日志层 |
|
|
@ -973,25 +992,35 @@ |
|
|
|
// 更新过滤器选项 |
|
|
|
updateLayerFilter(); |
|
|
|
|
|
|
|
const newRows = logs.map((log, i) => formatLogItem(log, allLogsData.length + i)); |
|
|
|
|
|
|
|
if (prepend) { |
|
|
|
clusterize.prepend(newRows); |
|
|
|
if (isHistory) { |
|
|
|
// 历史日志:直接替换所有数据 |
|
|
|
allLogsData = [...logs]; |
|
|
|
const rows = logs.map((log, i) => formatLogItem(log, i)); |
|
|
|
clusterize.clear(); |
|
|
|
clusterize.append(rows); |
|
|
|
showInfo(`加载了 ${logs.length} 条历史日志`); |
|
|
|
} else { |
|
|
|
clusterize.append(newRows); |
|
|
|
// 新日志:添加到现有数据 |
|
|
|
const newRows = logs.map((log, i) => formatLogItem(log, allLogsData.length + i)); |
|
|
|
|
|
|
|
if (prepend) { |
|
|
|
clusterize.prepend(newRows); |
|
|
|
} else { |
|
|
|
clusterize.append(newRows); |
|
|
|
} |
|
|
|
allLogsData.push(...logs); |
|
|
|
|
|
|
|
// 显示新日志数量 |
|
|
|
if (!prepend) { |
|
|
|
newLogsCountEl.textContent = `+${logs.length}`; |
|
|
|
setTimeout(() => { |
|
|
|
newLogsCountEl.textContent = ''; |
|
|
|
}, 2000); |
|
|
|
} |
|
|
|
} |
|
|
|
allLogsData.push(...logs); |
|
|
|
|
|
|
|
// 根据当前过滤器更新显示 |
|
|
|
refreshLogList(); |
|
|
|
|
|
|
|
// 显示新日志数量 |
|
|
|
if (!prepend) { |
|
|
|
newLogsCountEl.textContent = `+${logs.length}`; |
|
|
|
setTimeout(() => { |
|
|
|
newLogsCountEl.textContent = ''; |
|
|
|
}, 2000); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 显示日志详情 |
|
|
@ -1039,24 +1068,25 @@ |
|
|
|
} |
|
|
|
|
|
|
|
updateConnectionStatus('connecting', '连接中...'); |
|
|
|
console.log('开始建立SSE连接...'); |
|
|
|
|
|
|
|
eventSource = new EventSource('/api/websocket/logs/stream'); |
|
|
|
|
|
|
|
eventSource.addEventListener('connected', function(event) { |
|
|
|
console.log("SSE连接已建立"); |
|
|
|
console.log("SSE连接已建立", event); |
|
|
|
updateConnectionStatus('connected', '已连接'); |
|
|
|
showInfo('日志流连接已建立'); |
|
|
|
reconnectAttempts = 0; // 重置重连计数 |
|
|
|
}); |
|
|
|
|
|
|
|
eventSource.addEventListener('history', function(event) { |
|
|
|
console.log("接收到历史日志...", event.data); |
|
|
|
console.log("接收到历史日志事件", event); |
|
|
|
try { |
|
|
|
const data = JSON.parse(event.data); |
|
|
|
console.log("历史日志数据:", data); |
|
|
|
if (data.logs && Array.isArray(data.logs)) { |
|
|
|
updateLogList(data.logs); |
|
|
|
showInfo(`加载了 ${data.logs.length} 条历史日志`); |
|
|
|
console.log(`处理 ${data.logs.length} 条历史日志`); |
|
|
|
updateLogList(data.logs, false, true); // 标记为历史日志 |
|
|
|
} else { |
|
|
|
console.warn("历史日志数据格式不正确:", data); |
|
|
|
showError("历史日志数据格式不正确"); |
|
|
@ -1068,12 +1098,13 @@ |
|
|
|
}); |
|
|
|
|
|
|
|
eventSource.addEventListener('new_logs', function(event) { |
|
|
|
console.log("接收到新日志...", event.data); |
|
|
|
console.log("接收到新日志事件", event); |
|
|
|
try { |
|
|
|
const data = JSON.parse(event.data); |
|
|
|
console.log("新日志数据:", data); |
|
|
|
if (data.logs && Array.isArray(data.logs)) { |
|
|
|
updateLogList(data.logs); |
|
|
|
console.log(`处理 ${data.logs.length} 条新日志`); |
|
|
|
updateLogList(data.logs, false, false); // 标记为新日志 |
|
|
|
} else { |
|
|
|
console.warn("新日志数据格式不正确:", data); |
|
|
|
} |
|
|
@ -1084,9 +1115,10 @@ |
|
|
|
}); |
|
|
|
|
|
|
|
eventSource.addEventListener('reset', function(event) { |
|
|
|
console.log("日志缓存已重置"); |
|
|
|
console.log("接收到重置事件", event); |
|
|
|
try { |
|
|
|
const data = JSON.parse(event.data); |
|
|
|
console.log("重置事件数据:", data); |
|
|
|
clearLogsDisplay(); |
|
|
|
showInfo('日志缓存已重置,等待新日志以建立时间线'); |
|
|
|
} catch (error) { |
|
|
@ -1095,9 +1127,10 @@ |
|
|
|
}); |
|
|
|
|
|
|
|
eventSource.addEventListener('error', function(event) { |
|
|
|
console.log("接收到错误事件"); |
|
|
|
console.log("接收到错误事件", event); |
|
|
|
try { |
|
|
|
const data = JSON.parse(event.data); |
|
|
|
console.log("错误事件数据:", data); |
|
|
|
updateConnectionStatus('error', '连接错误'); |
|
|
|
showError(`连接错误: ${data.message}`); |
|
|
|
} catch (error) { |
|
|
@ -1106,15 +1139,16 @@ |
|
|
|
}); |
|
|
|
|
|
|
|
eventSource.addEventListener('disconnected', function(event) { |
|
|
|
console.log("连接已断开"); |
|
|
|
console.log("接收到断开连接事件", event); |
|
|
|
updateConnectionStatus('disconnected', '已断开'); |
|
|
|
showInfo('日志流连接已断开'); |
|
|
|
}); |
|
|
|
|
|
|
|
eventSource.addEventListener('fatal_error', function(event) { |
|
|
|
console.error("致命错误"); |
|
|
|
console.error("接收到致命错误事件", event); |
|
|
|
try { |
|
|
|
const data = JSON.parse(event.data); |
|
|
|
console.log("致命错误事件数据:", data); |
|
|
|
updateConnectionStatus('error', '服务器错误'); |
|
|
|
showError(`服务器错误: ${data.message}`); |
|
|
|
} catch (error) { |
|
|
@ -1123,15 +1157,17 @@ |
|
|
|
}); |
|
|
|
|
|
|
|
eventSource.onerror = function (err) { |
|
|
|
console.error("SSE 错误:", err); |
|
|
|
console.error("SSE 连接错误:", err); |
|
|
|
updateConnectionStatus('error', '连接失败'); |
|
|
|
|
|
|
|
// 自动重连 |
|
|
|
if (reconnectAttempts < maxReconnectAttempts) { |
|
|
|
reconnectAttempts++; |
|
|
|
console.log(`连接失败,${reconnectDelay/1000}秒后重试 (${reconnectAttempts}/${maxReconnectAttempts})`); |
|
|
|
showError(`连接失败,${reconnectDelay/1000}秒后重试 (${reconnectAttempts}/${maxReconnectAttempts})`); |
|
|
|
setTimeout(connectSSE, reconnectDelay); |
|
|
|
} else { |
|
|
|
console.log('连接失败,已达到最大重试次数'); |
|
|
|
showError('连接失败,已达到最大重试次数'); |
|
|
|
updateConnectionStatus('disconnected', '连接失败'); |
|
|
|
} |
|
|
@ -1225,6 +1261,35 @@ |
|
|
|
connectSSE(); |
|
|
|
}); |
|
|
|
|
|
|
|
debugBtn.addEventListener('click', function() { |
|
|
|
// 显示调试信息 |
|
|
|
const debugInfo = { |
|
|
|
totalLogs: allLogsData.length, |
|
|
|
availableLayers: Array.from(availableLayers), |
|
|
|
selectedLayers: Array.from(selectedLayers), |
|
|
|
connectionStatus: statusText.textContent, |
|
|
|
eventSourceReadyState: eventSource ? eventSource.readyState : 'null', |
|
|
|
sortField: sortField, |
|
|
|
sortDirection: sortDirection, |
|
|
|
columnVisibility: columnVisibility, |
|
|
|
clusterizeRows: clusterize.rows.length |
|
|
|
}; |
|
|
|
|
|
|
|
console.log('调试信息:', debugInfo); |
|
|
|
|
|
|
|
// 检查服务器端日志缓存状态 |
|
|
|
fetch('/api/websocket/logs/debug') |
|
|
|
.then(response => response.json()) |
|
|
|
.then(data => { |
|
|
|
console.log('服务器端日志缓存状态:', data); |
|
|
|
showInfo(`调试信息已输出到控制台。服务器缓存: ${data.totalLogs} 条日志`); |
|
|
|
}) |
|
|
|
.catch(error => { |
|
|
|
console.error('获取服务器调试信息失败:', error); |
|
|
|
showError('获取服务器调试信息失败: ' + error.message); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
// --- Resizer Logic --- |
|
|
|
let isResizing = false; |
|
|
|
|
|
|
|