mirror of
https://devops.lemonos.cn/lawson/FendxPHP.git
synced 2026-06-15 23:12:49 +08:00
- 创建用户表(users)包含基本信息和认证字段 - 创建角色表(roles)用于权限控制 - 创建权限表(permissions)定义系统权限 - 创建用户角色关联表(user_roles)建立用户与角色关系 - 创建角色权限关联表(role_permissions)建立角色与权限关系 - 创建迁移记录表(migrations)追踪数据库变更 - 添加AdminController提供管理员面板功能 - 实现系统监控、配置管理、缓存清理等功能 - 添加AOP切面编程支持的各种通知类型 - 实现告警管理AlertManager支持多渠道告警 - 添加文档注解接口规范
650 lines
24 KiB
JavaScript
650 lines
24 KiB
JavaScript
// 全局变量
|
|
let currentSection = 'dashboard';
|
|
let refreshInterval = null;
|
|
let charts = {};
|
|
|
|
// API基础URL
|
|
const API_BASE = '/monitor';
|
|
|
|
// 初始化
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
initializeDashboard();
|
|
startAutoRefresh();
|
|
});
|
|
|
|
// 初始化仪表盘
|
|
function initializeDashboard() {
|
|
showSection('dashboard');
|
|
loadDashboardData();
|
|
}
|
|
|
|
// 显示指定区域
|
|
function showSection(section) {
|
|
// 隐藏所有区域
|
|
document.querySelectorAll('.section').forEach(el => {
|
|
el.classList.add('hidden');
|
|
});
|
|
|
|
// 显示指定区域
|
|
document.getElementById(section + '-section').classList.remove('hidden');
|
|
|
|
// 更新侧边栏状态
|
|
document.querySelectorAll('.sidebar-item').forEach(el => {
|
|
el.classList.remove('active');
|
|
});
|
|
event.currentTarget.classList.add('active');
|
|
|
|
// 更新标题
|
|
const titles = {
|
|
'dashboard': '仪表盘',
|
|
'health': '健康检查',
|
|
'metrics': '性能指标',
|
|
'errors': '错误追踪',
|
|
'logs': '日志分析',
|
|
'alerts': '告警管理',
|
|
'settings': '系统设置'
|
|
};
|
|
document.getElementById('section-title').textContent = titles[section];
|
|
|
|
currentSection = section;
|
|
|
|
// 加载对应数据
|
|
loadSectionData(section);
|
|
}
|
|
|
|
// 加载区域数据
|
|
function loadSectionData(section) {
|
|
switch(section) {
|
|
case 'dashboard':
|
|
loadDashboardData();
|
|
break;
|
|
case 'health':
|
|
loadHealthData();
|
|
break;
|
|
case 'metrics':
|
|
loadMetricsData();
|
|
break;
|
|
case 'errors':
|
|
loadErrorsData();
|
|
break;
|
|
case 'logs':
|
|
// 日志数据需要用户主动搜索
|
|
break;
|
|
case 'alerts':
|
|
loadAlertsData();
|
|
break;
|
|
case 'settings':
|
|
loadSettingsData();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 加载仪表盘数据
|
|
async function loadDashboardData() {
|
|
try {
|
|
// 加载健康状态
|
|
const healthResponse = await fetch(`${API_BASE}/health`);
|
|
const health = await healthResponse.json();
|
|
updateDashboardHealth(health.data);
|
|
|
|
// 加载指标数据
|
|
const metricsResponse = await fetch(`${API_BASE}/metrics`);
|
|
const metrics = await metricsResponse.json();
|
|
updateDashboardMetrics(metrics.data);
|
|
|
|
// 加载告警数据
|
|
const alertsResponse = await fetch(`${API_BASE}/alerts/active`);
|
|
const alerts = await alertsResponse.json();
|
|
updateDashboardAlerts(alerts.data);
|
|
|
|
// 加载图表数据
|
|
loadDashboardCharts();
|
|
|
|
// 更新时间
|
|
updateLastUpdateTime();
|
|
|
|
} catch (error) {
|
|
console.error('加载仪表盘数据失败:', error);
|
|
showError('加载仪表盘数据失败');
|
|
}
|
|
}
|
|
|
|
// 更新仪表盘健康状态
|
|
function updateDashboardHealth(health) {
|
|
const statusEl = document.getElementById('system-status');
|
|
const statusClass = health.status === 'healthy' ? 'status-healthy' :
|
|
health.status === 'warning' ? 'status-warning' : 'status-critical';
|
|
|
|
statusEl.textContent = health.status === 'healthy' ? '健康' :
|
|
health.status === 'warning' ? '警告' : '严重';
|
|
statusEl.className = `text-2xl font-bold ${statusClass}`;
|
|
}
|
|
|
|
// 更新仪表盘指标
|
|
function updateDashboardMetrics(metrics) {
|
|
if (metrics.system) {
|
|
const memoryUsage = metrics.system.memory_usage;
|
|
const memoryLimit = metrics.system.memory_limit;
|
|
const memoryPercent = memoryLimit > 0 ? (memoryUsage / memoryLimit * 100).toFixed(1) : 0;
|
|
|
|
document.getElementById('memory-usage').textContent = memoryPercent + '%';
|
|
}
|
|
|
|
// 计算平均响应时间
|
|
if (metrics.histograms && metrics.histograms.http_request_duration) {
|
|
const avgTime = metrics.histograms.http_request_duration.mean || 0;
|
|
document.getElementById('response-time').textContent = avgTime.toFixed(2) + 'ms';
|
|
}
|
|
}
|
|
|
|
// 更新仪表盘告警
|
|
function updateDashboardAlerts(alerts) {
|
|
document.getElementById('active-alerts').textContent = alerts.length;
|
|
|
|
const recentAlertsEl = document.getElementById('recent-alerts');
|
|
if (alerts.length === 0) {
|
|
recentAlertsEl.innerHTML = '<p class="text-gray-500 text-center py-4">暂无告警</p>';
|
|
} else {
|
|
recentAlertsEl.innerHTML = alerts.slice(0, 5).map(alert => `
|
|
<div class="alert-item flex items-center justify-between p-3 bg-${alert.severity === 'critical' ? 'red' : 'yellow'}-50 border border-${alert.severity === 'critical' ? 'red' : 'yellow'}-200 rounded-lg">
|
|
<div class="flex items-center">
|
|
<div class="w-2 h-2 bg-${alert.severity === 'critical' ? 'red' : 'yellow'}-500 rounded-full mr-3"></div>
|
|
<div>
|
|
<p class="font-medium text-gray-800">${alert.type}</p>
|
|
<p class="text-sm text-gray-600">${alert.message}</p>
|
|
</div>
|
|
</div>
|
|
<div class="text-right">
|
|
<p class="text-xs text-gray-500">${formatTime(alert.timestamp)}</p>
|
|
<button onclick="acknowledgeAlert('${alert.id}')" class="text-xs text-blue-600 hover:text-blue-800">确认</button>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
}
|
|
|
|
// 加载仪表盘图表
|
|
async function loadDashboardCharts() {
|
|
try {
|
|
// 加载请求数据
|
|
const statsResponse = await fetch(`${API_BASE}/stats`);
|
|
const stats = await statsResponse.json();
|
|
|
|
// 请求趋势图
|
|
if (charts.requests) {
|
|
charts.requests.destroy();
|
|
}
|
|
|
|
const requestsChart = {
|
|
series: [{
|
|
name: '请求数',
|
|
data: generateTimeSeriesData()
|
|
}],
|
|
chart: {
|
|
type: 'area',
|
|
height: 300,
|
|
toolbar: { show: false }
|
|
},
|
|
dataLabels: { enabled: false },
|
|
stroke: { curve: 'smooth', width: 2 },
|
|
fill: {
|
|
type: 'gradient',
|
|
gradient: {
|
|
shadeIntensity: 1,
|
|
opacityFrom: 0.7,
|
|
opacityTo: 0.3
|
|
}
|
|
},
|
|
xaxis: {
|
|
categories: generateTimeLabels()
|
|
},
|
|
yaxis: { title: { text: '请求数' } },
|
|
colors: ['#3b82f6']
|
|
};
|
|
|
|
charts.requests = new ApexCharts(document.getElementById('requests-chart'), requestsChart);
|
|
charts.requests.render();
|
|
|
|
// 错误分布图
|
|
if (charts.errors) {
|
|
charts.errors.destroy();
|
|
}
|
|
|
|
const errorsData = stats.data?.system?.error_count || 0;
|
|
const errorsChart = {
|
|
series: [errorsData, Math.max(0, 100 - errorsData)],
|
|
chart: {
|
|
type: 'donut',
|
|
height: 300
|
|
},
|
|
labels: ['错误', '正常'],
|
|
colors: ['#ef4444', '#10b981'],
|
|
legend: { position: 'bottom' },
|
|
dataLabels: { enabled: true }
|
|
};
|
|
|
|
charts.errors = new ApexCharts(document.getElementById('errors-chart'), errorsChart);
|
|
charts.errors.render();
|
|
|
|
} catch (error) {
|
|
console.error('加载图表失败:', error);
|
|
}
|
|
}
|
|
|
|
// 加载健康检查数据
|
|
async function loadHealthData() {
|
|
try {
|
|
const response = await fetch(`${API_BASE}/health`);
|
|
const result = await response.json();
|
|
|
|
const healthChecksEl = document.getElementById('health-checks');
|
|
const checks = result.data.checks;
|
|
|
|
healthChecksEl.innerHTML = Object.entries(checks).map(([name, check]) => `
|
|
<div class="flex items-center justify-between p-4 border rounded-lg">
|
|
<div class="flex items-center">
|
|
<div class="w-3 h-3 bg-${check.status === 'healthy' ? 'green' : check.status === 'warning' ? 'yellow' : 'red'}-500 rounded-full mr-3"></div>
|
|
<div>
|
|
<p class="font-medium text-gray-800">${getComponentName(name)}</p>
|
|
<p class="text-sm text-gray-600">${check.message || '运行正常'}</p>
|
|
</div>
|
|
</div>
|
|
<div class="text-right">
|
|
<span class="px-2 py-1 text-xs font-medium rounded-full bg-${check.status === 'healthy' ? 'green' : check.status === 'warning' ? 'yellow' : 'red'}-100 text-${check.status === 'healthy' ? 'green' : check.status === 'warning' ? 'yellow' : 'red'}-800">
|
|
${check.status === 'healthy' ? '健康' : check.status === 'warning' ? '警告' : '严重'}
|
|
</span>
|
|
<p class="text-xs text-gray-500 mt-1">${check.duration ? check.duration.toFixed(3) + 's' : ''}</p>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
|
|
} catch (error) {
|
|
console.error('加载健康检查数据失败:', error);
|
|
showError('加载健康检查数据失败');
|
|
}
|
|
}
|
|
|
|
// 加载指标数据
|
|
async function loadMetricsData() {
|
|
try {
|
|
const response = await fetch(`${API_BASE}/metrics`);
|
|
const result = await response.json();
|
|
const metrics = result.data;
|
|
|
|
// 系统指标
|
|
const systemMetricsEl = document.getElementById('system-metrics');
|
|
if (metrics.system) {
|
|
systemMetricsEl.innerHTML = `
|
|
<div class="space-y-3">
|
|
<div class="flex justify-between">
|
|
<span class="text-gray-600">内存使用</span>
|
|
<span class="font-medium">${formatBytes(metrics.system.memory_usage)} / ${formatBytes(metrics.system.memory_limit)}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-gray-600">CPU使用率</span>
|
|
<span class="font-medium">${(metrics.system.cpu_usage * 100).toFixed(1)}%</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-gray-600">运行时间</span>
|
|
<span class="font-medium">${formatUptime(metrics.system.uptime)}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-gray-600">负载均衡</span>
|
|
<span class="font-medium">${metrics.system.load_avg ? metrics.system.load_avg[0].toFixed(2) : 'N/A'}</span>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
// 应用指标
|
|
const appMetricsEl = document.getElementById('application-metrics');
|
|
const totalRequests = Object.values(metrics.counters || {}).reduce((sum, count) => sum + count, 0);
|
|
const totalErrors = Object.values(metrics.counters || {}).filter(key => key.includes('error')).reduce((sum, count) => sum + count, 0);
|
|
|
|
appMetricsEl.innerHTML = `
|
|
<div class="space-y-3">
|
|
<div class="flex justify-between">
|
|
<span class="text-gray-600">总请求数</span>
|
|
<span class="font-medium">${totalRequests}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-gray-600">总错误数</span>
|
|
<span class="font-medium">${totalErrors}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-gray-600">成功率</span>
|
|
<span class="font-medium">${totalRequests > 0 ? ((totalRequests - totalErrors) / totalRequests * 100).toFixed(1) : 100}%</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-gray-600">活跃连接</span>
|
|
<span class="font-medium">${metrics.gauges?.requests_in_progress || 0}</span>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
} catch (error) {
|
|
console.error('加载指标数据失败:', error);
|
|
showError('加载指标数据失败');
|
|
}
|
|
}
|
|
|
|
// 加载错误数据
|
|
async function loadErrorsData() {
|
|
try {
|
|
const filter = document.getElementById('error-filter').value;
|
|
const response = await fetch(`${API_BASE}/errors${filter ? '?severity=' + filter : ''}`);
|
|
const result = await response.json();
|
|
|
|
const errorsListEl = document.getElementById('errors-list');
|
|
if (result.data.length === 0) {
|
|
errorsListEl.innerHTML = '<p class="text-gray-500 text-center py-4">暂无错误记录</p>';
|
|
} else {
|
|
errorsListEl.innerHTML = result.data.map(error => `
|
|
<div class="border rounded-lg p-4">
|
|
<div class="flex items-start justify-between">
|
|
<div class="flex-1">
|
|
<div class="flex items-center mb-2">
|
|
<span class="px-2 py-1 text-xs font-medium rounded-full bg-${error.severity === 'critical' ? 'red' : error.severity === 'warning' ? 'yellow' : 'blue'}-100 text-${error.severity === 'critical' ? 'red' : error.severity === 'warning' ? 'yellow' : 'blue'}-800">
|
|
${error.severity.toUpperCase()}
|
|
</span>
|
|
<span class="ml-2 text-sm text-gray-500">${error.type}</span>
|
|
</div>
|
|
<p class="text-gray-800 mb-2">${error.message}</p>
|
|
<div class="text-sm text-gray-600">
|
|
<p>文件: ${error.file || 'N/A'}:${error.line || 'N/A'}</p>
|
|
<p>时间: ${formatTime(error.timestamp)}</p>
|
|
<p>Trace ID: ${error.trace_id || 'N/A'}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('加载错误数据失败:', error);
|
|
showError('加载错误数据失败');
|
|
}
|
|
}
|
|
|
|
// 搜索日志
|
|
async function searchLogs() {
|
|
const level = document.getElementById('log-level').value;
|
|
const search = document.getElementById('log-search').value;
|
|
|
|
const params = new URLSearchParams();
|
|
if (level) params.append('level', level);
|
|
if (search) params.append('message', search);
|
|
params.append('limit', '50');
|
|
|
|
try {
|
|
const response = await fetch(`${API_BASE}/logs/search?${params}`);
|
|
const result = await response.json();
|
|
|
|
const logsContainerEl = document.getElementById('logs-container');
|
|
if (result.data.logs.length === 0) {
|
|
logsContainerEl.innerHTML = '<p class="text-gray-500 text-center py-4">未找到匹配的日志</p>';
|
|
} else {
|
|
logsContainerEl.innerHTML = result.data.logs.map(log => `
|
|
<div class="border-l-4 border-${getLogLevelColor(log.level)} pl-4 py-2">
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center">
|
|
<span class="px-2 py-1 text-xs font-medium rounded bg-${getLogLevelColor(log.level)}-100 text-${getLogLevelColor(log.level)}-800">
|
|
${log.level}
|
|
</span>
|
|
<span class="ml-2 text-sm text-gray-500">${log.datetime}</span>
|
|
</div>
|
|
<span class="text-xs text-gray-500">${log.trace_id || ''}</span>
|
|
</div>
|
|
<p class="text-gray-800 mt-1">${log.message}</p>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('搜索日志失败:', error);
|
|
showError('搜索日志失败');
|
|
}
|
|
}
|
|
|
|
// 加载告警数据
|
|
async function loadAlertsData() {
|
|
try {
|
|
const status = document.getElementById('alert-status').value;
|
|
const response = await fetch(`${API_BASE}/alerts${status ? '?status=' + status : ''}`);
|
|
const result = await response.json();
|
|
|
|
const alertsListEl = document.getElementById('alerts-list');
|
|
if (result.data.length === 0) {
|
|
alertsListEl.innerHTML = '<p class="text-gray-500 text-center py-4">暂无告警</p>';
|
|
} else {
|
|
alertsListEl.innerHTML = result.data.map(alert => `
|
|
<div class="border rounded-lg p-4">
|
|
<div class="flex items-start justify-between">
|
|
<div class="flex-1">
|
|
<div class="flex items-center mb-2">
|
|
<div class="w-3 h-3 bg-${alert.severity === 'critical' ? 'red' : 'yellow'}-500 rounded-full mr-2"></div>
|
|
<span class="font-medium text-gray-800">${alert.type}</span>
|
|
<span class="ml-2 px-2 py-1 text-xs font-medium rounded-full bg-${alert.status === 'active' ? 'red' : alert.status === 'acknowledged' ? 'yellow' : 'green'}-100 text-${alert.status === 'active' ? 'red' : alert.status === 'acknowledged' ? 'yellow' : 'green'}-800">
|
|
${alert.status === 'active' ? '活跃' : alert.status === 'acknowledged' ? '已确认' : '已解决'}
|
|
</span>
|
|
</div>
|
|
<p class="text-gray-800 mb-2">${alert.message}</p>
|
|
<p class="text-sm text-gray-500">时间: ${formatTime(alert.timestamp)}</p>
|
|
</div>
|
|
<div class="flex space-x-2">
|
|
${alert.status === 'active' ? `
|
|
<button onclick="acknowledgeAlert('${alert.id}')" class="text-sm text-blue-600 hover:text-blue-800">确认</button>
|
|
<button onclick="resolveAlert('${alert.id}')" class="text-sm text-green-600 hover:text-green-800">解决</button>
|
|
` : ''}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('加载告警数据失败:', error);
|
|
showError('加载告警数据失败');
|
|
}
|
|
}
|
|
|
|
// 加载设置数据
|
|
async function loadSettingsData() {
|
|
try {
|
|
// 这里可以加载实际的配置数据
|
|
// 暂时使用默认值
|
|
document.getElementById('sample-rate').value = '1.0';
|
|
document.getElementById('retention').value = '3600';
|
|
document.getElementById('error-threshold').value = '0.05';
|
|
document.getElementById('memory-threshold').value = '0.8';
|
|
|
|
} catch (error) {
|
|
console.error('加载设置数据失败:', error);
|
|
showError('加载设置数据失败');
|
|
}
|
|
}
|
|
|
|
// 确认告警
|
|
async function acknowledgeAlert(alertId) {
|
|
try {
|
|
const response = await fetch(`${API_BASE}/alerts/${alertId}/acknowledge`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ acknowledged_by: 'admin' })
|
|
});
|
|
|
|
if (response.ok) {
|
|
showSuccess('告警已确认');
|
|
if (currentSection === 'dashboard') {
|
|
loadDashboardData();
|
|
} else if (currentSection === 'alerts') {
|
|
loadAlertsData();
|
|
}
|
|
} else {
|
|
showError('确认告警失败');
|
|
}
|
|
} catch (error) {
|
|
console.error('确认告警失败:', error);
|
|
showError('确认告警失败');
|
|
}
|
|
}
|
|
|
|
// 解决告警
|
|
async function resolveAlert(alertId) {
|
|
try {
|
|
const response = await fetch(`${API_BASE}/alerts/${alertId}/resolve`, {
|
|
method: 'POST'
|
|
});
|
|
|
|
if (response.ok) {
|
|
showSuccess('告警已解决');
|
|
if (currentSection === 'dashboard') {
|
|
loadDashboardData();
|
|
} else if (currentSection === 'alerts') {
|
|
loadAlertsData();
|
|
}
|
|
} else {
|
|
showError('解决告警失败');
|
|
}
|
|
} catch (error) {
|
|
console.error('解决告警失败:', error);
|
|
showError('解决告警失败');
|
|
}
|
|
}
|
|
|
|
// 保存设置
|
|
async function saveSettings() {
|
|
try {
|
|
const settings = {
|
|
sample_rate: parseFloat(document.getElementById('sample-rate').value),
|
|
retention: parseInt(document.getElementById('retention').value),
|
|
error_threshold: parseFloat(document.getElementById('error-threshold').value),
|
|
memory_threshold: parseFloat(document.getElementById('memory-threshold').value)
|
|
};
|
|
|
|
// 这里应该发送到后端保存
|
|
console.log('保存设置:', settings);
|
|
showSuccess('设置已保存');
|
|
|
|
} catch (error) {
|
|
console.error('保存设置失败:', error);
|
|
showError('保存设置失败');
|
|
}
|
|
}
|
|
|
|
// 刷新数据
|
|
function refreshData() {
|
|
loadSectionData(currentSection);
|
|
}
|
|
|
|
// 刷新错误数据
|
|
function refreshErrors() {
|
|
loadErrorsData();
|
|
}
|
|
|
|
// 刷新告警数据
|
|
function refreshAlerts() {
|
|
loadAlertsData();
|
|
}
|
|
|
|
// 开始自动刷新
|
|
function startAutoRefresh() {
|
|
refreshInterval = setInterval(() => {
|
|
if (currentSection !== 'settings') {
|
|
loadSectionData(currentSection);
|
|
}
|
|
}, 30000); // 30秒刷新一次
|
|
}
|
|
|
|
// 停止自动刷新
|
|
function stopAutoRefresh() {
|
|
if (refreshInterval) {
|
|
clearInterval(refreshInterval);
|
|
refreshInterval = null;
|
|
}
|
|
}
|
|
|
|
// 更新最后更新时间
|
|
function updateLastUpdateTime() {
|
|
document.getElementById('last-update').textContent = '最后更新: ' + new Date().toLocaleTimeString();
|
|
}
|
|
|
|
// 工具函数
|
|
function formatTime(timestamp) {
|
|
return new Date(timestamp * 1000).toLocaleString();
|
|
}
|
|
|
|
function formatBytes(bytes) {
|
|
if (bytes === 0) return '0 B';
|
|
const k = 1024;
|
|
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
}
|
|
|
|
function formatUptime(seconds) {
|
|
const days = Math.floor(seconds / 86400);
|
|
const hours = Math.floor((seconds % 86400) / 3600);
|
|
const minutes = Math.floor((seconds % 3600) / 60);
|
|
|
|
if (days > 0) {
|
|
return `${days}天 ${hours}小时`;
|
|
} else if (hours > 0) {
|
|
return `${hours}小时 ${minutes}分钟`;
|
|
} else {
|
|
return `${minutes}分钟`;
|
|
}
|
|
}
|
|
|
|
function getComponentName(name) {
|
|
const names = {
|
|
'database': '数据库',
|
|
'cache': '缓存',
|
|
'filesystem': '文件系统',
|
|
'memory': '内存',
|
|
'disk': '磁盘',
|
|
'external_services': '外部服务'
|
|
};
|
|
return names[name] || name;
|
|
}
|
|
|
|
function getLogLevelColor(level) {
|
|
const colors = {
|
|
'DEBUG': 'gray',
|
|
'INFO': 'blue',
|
|
'WARNING': 'yellow',
|
|
'ERROR': 'red',
|
|
'CRITICAL': 'red'
|
|
};
|
|
return colors[level] || 'gray';
|
|
}
|
|
|
|
function generateTimeSeriesData() {
|
|
// 生成模拟数据
|
|
return Array.from({length: 24}, () => Math.floor(Math.random() * 100) + 50);
|
|
}
|
|
|
|
function generateTimeLabels() {
|
|
const labels = [];
|
|
const now = new Date();
|
|
for (let i = 23; i >= 0; i--) {
|
|
const time = new Date(now.getTime() - i * 60 * 60 * 1000);
|
|
labels.push(time.getHours() + ':00');
|
|
}
|
|
return labels;
|
|
}
|
|
|
|
function showSuccess(message) {
|
|
// 显示成功消息
|
|
console.log('Success:', message);
|
|
}
|
|
|
|
function showError(message) {
|
|
// 显示错误消息
|
|
console.error('Error:', message);
|
|
}
|
|
|
|
// 页面卸载时清理
|
|
window.addEventListener('beforeunload', stopAutoRefresh);
|