refactor(public): improve metrics loading and display logic

- Updated the metrics API endpoint from '/metrics' to '/api/metrics' for better organization.
- Added data validation to ensure the metrics data received is an object, enhancing error handling.
- Refactored the updateMetricsDisplay function to dynamically create metric items, improving the display of metrics.
- Implemented HTML escaping for metric keys and values to prevent XSS vulnerabilities.
- Cleared existing content in the metrics container before updating, ensuring a clean display.
This commit is contained in:
wood chen 2024-12-01 01:49:50 +08:00
parent 50e6abaa2f
commit 260bddb56b

View File

@ -210,77 +210,53 @@
async function loadMetrics() { async function loadMetrics() {
try { try {
const response = await fetch('/metrics'); const response = await fetch('/api/metrics');
const metrics = await response.json(); const data = await response.json();
updateMetricsDisplay(metrics);
// 添加数据验证
if (!data || typeof data !== 'object') {
throw new Error('Invalid metrics data received');
}
updateMetricsDisplay(data);
} catch (error) { } catch (error) {
console.error('Error loading metrics:', error); console.error('Error loading metrics:', error);
document.getElementById('metrics-container').innerHTML =
'<p class="error">加载指标数据时出错,请稍后重试</p>';
} }
} }
function updateMetricsDisplay(metrics) { function updateMetricsDisplay(metricsData) {
const metricsHtml = ` // 确保 metricsData 是有效对象
<div class="metrics-container"> if (!metricsData || typeof metricsData !== 'object') {
<div class="metrics-section"> console.error('Invalid metrics data');
<h3>基础指标</h3> return;
<div class="metrics-grid"> }
<div class="metric-item">运行时间:${formatDuration(metrics?.uptime || 0)}</div>
<div class="metric-item">启动时间:${new Date(metrics?.start_time || Date.now()).toLocaleString()}</div> const container = document.getElementById('metrics-container');
</div> container.innerHTML = ''; // 清空现有内容
</div>
// 使用 Object.entries 之前进行安全检查
<div class="metrics-section"> const entries = Object.entries(metricsData).filter(([key, value]) => key && value !== undefined);
<h3>系统指标</h3>
<div class="metrics-grid">
<div class="metric-item">CPU核心数${metrics?.num_cpu || 0}</div>
<div class="metric-item">Goroutine数${metrics?.num_goroutine || 0}</div>
<div class="metric-item">内存使用:${formatBytes(metrics?.memory_stats?.heap_alloc || 0)}</div>
<div class="metric-item">系统内存:${formatBytes(metrics?.memory_stats?.heap_sys || 0)}</div>
<div class="metric-item">平均延迟:${formatLatency(metrics?.average_latency || 0)}</div>
</div>
</div>
<div class="metrics-section">
<h3>热门引用来源 (5分钟统计)</h3>
<div class="top-referers">
${metrics?.top_referers ? Object.entries(metrics.top_referers)
.sort(([, a], [, b]) => b - a)
.slice(0, 10)
.map(([referer, count]) => `
<div class="referer-item">
<span class="referer">${referer}</span>
<span class="count">${count}</span>
</div>
`).join('') : ''}
</div>
</div>
</div>
`;
const metricsElement = document.getElementById('system-metrics'); entries.forEach(([key, value]) => {
if (metricsElement) { const metricDiv = document.createElement('div');
metricsElement.innerHTML = metricsHtml; metricDiv.className = 'metric-item';
} metricDiv.innerHTML = `
<h3>${escapeHtml(key)}</h3>
<p>${escapeHtml(String(value))}</p>
`;
container.appendChild(metricDiv);
});
} }
function formatBytes(bytes) { function escapeHtml(unsafe) {
const units = ['B', 'KB', 'MB', 'GB']; return unsafe
let size = bytes; .replace(/&/g, "&amp;")
let unitIndex = 0; .replace(/</g, "&lt;")
while (size >= 1024 && unitIndex < units.length - 1) { .replace(/>/g, "&gt;")
size /= 1024; .replace(/"/g, "&quot;")
unitIndex++; .replace(/'/g, "&#039;");
}
return `${size.toFixed(2)} ${units[unitIndex]}`;
}
function formatDuration(ns) {
const duration = ns / 1e9; // 转换为秒
const days = Math.floor(duration / 86400);
const hours = Math.floor((duration % 86400) / 3600);
const minutes = Math.floor((duration % 3600) / 60);
const seconds = Math.floor(duration % 60);
return `${days}天 ${hours}时 ${minutes}分 ${seconds}秒`;
} }
// 定期更新监控数据 // 定期更新监控数据