增加系统版本维护页面

This commit is contained in:
dengqichen 2025-12-09 18:34:46 +08:00
parent 2d08651f62
commit ef7efe0497
3 changed files with 238 additions and 7 deletions

View File

@ -188,9 +188,40 @@ const MetricsDashboard: React.FC = () => {
process: formatPercent(metrics.cpu.processCpu * 100) + '%', process: formatPercent(metrics.cpu.processCpu * 100) + '%',
}, },
memory: { memory: {
heapUsed: formatBytes(metrics.memory.heapUsed), heap: {
heapMax: formatBytes(metrics.memory.heapMax), used: formatBytes(metrics.memory.heapUsed),
heapPercent: formatPercent(metrics.memory.heapPercent) + '%', max: formatBytes(metrics.memory.heapMax),
committed: formatBytes(metrics.memory.heapCommitted),
percent: formatPercent(metrics.memory.heapPercent) + '%',
},
nonHeap: {
used: formatBytes(metrics.memory.nonHeapUsed),
max: formatBytes(metrics.memory.nonHeapMax),
committed: formatBytes(metrics.memory.nonHeapCommitted),
percent: formatPercent(metrics.memory.nonHeapPercent) + '%',
},
regions: metrics.memory.regions ? {
eden: metrics.memory.regions.eden ? {
used: formatBytes(metrics.memory.regions.eden.used),
max: formatBytes(metrics.memory.regions.eden.max),
committed: formatBytes(metrics.memory.regions.eden.committed),
} : null,
oldGen: metrics.memory.regions.oldGen ? {
used: formatBytes(metrics.memory.regions.oldGen.used),
max: formatBytes(metrics.memory.regions.oldGen.max),
committed: formatBytes(metrics.memory.regions.oldGen.committed),
} : null,
survivor: metrics.memory.regions.survivor ? {
used: formatBytes(metrics.memory.regions.survivor.used),
max: formatBytes(metrics.memory.regions.survivor.max),
committed: formatBytes(metrics.memory.regions.survivor.committed),
} : null,
metaspace: metrics.memory.regions.metaspace ? {
used: formatBytes(metrics.memory.regions.metaspace.used),
max: formatBytes(metrics.memory.regions.metaspace.max),
committed: formatBytes(metrics.memory.regions.metaspace.committed),
} : null,
} : null,
}, },
threads: metrics.threads, threads: metrics.threads,
hikari: metrics.hikari, hikari: metrics.hikari,
@ -350,8 +381,9 @@ const MetricsDashboard: React.FC = () => {
<div className={`text-2xl font-bold ${getStatusColor(memoryPercent, THRESHOLDS.memory)}`}> <div className={`text-2xl font-bold ${getStatusColor(memoryPercent, THRESHOLDS.memory)}`}>
{formatBytes(metrics.memory.heapUsed)} {formatBytes(metrics.memory.heapUsed)}
</div> </div>
<div className="text-xs text-muted-foreground"> <div className="text-xs text-muted-foreground space-y-0.5">
/ {formatBytes(metrics.memory.heapMax)} <div>: {formatBytes(metrics.memory.heapMax)}</div>
<div>: {formatBytes(metrics.memory.heapCommitted)}</div>
</div> </div>
<Progress value={memoryPercent} className="h-2" /> <Progress value={memoryPercent} className="h-2" />
{memoryPercent >= THRESHOLDS.memory.warning && ( {memoryPercent >= THRESHOLDS.memory.warning && (
@ -549,6 +581,118 @@ const MetricsDashboard: React.FC = () => {
</Card> </Card>
</div> </div>
{/* 内存区域详情(如果可用) */}
{metrics.memory.regions && (
<Card>
<CardHeader>
<CardTitle>JVM </CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{/* Eden 区 */}
{metrics.memory.regions.eden && (
<div className="p-4 bg-gradient-to-br from-green-50 to-emerald-50 dark:from-green-950/20 dark:to-emerald-950/20 rounded-lg border border-green-200 dark:border-green-900">
<div className="text-sm font-medium text-muted-foreground mb-2">Eden </div>
<div className="space-y-1 text-xs">
<div className="flex justify-between">
<span className="text-muted-foreground">使:</span>
<span className="font-medium">{formatBytes(metrics.memory.regions.eden.used)}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">:</span>
<span className="font-medium">{formatBytes(metrics.memory.regions.eden.committed)}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">:</span>
<span className="font-medium">{formatBytes(metrics.memory.regions.eden.max)}</span>
</div>
<Progress
value={(metrics.memory.regions.eden.used / metrics.memory.regions.eden.max) * 100}
className="h-1 mt-2"
/>
</div>
</div>
)}
{/* Old Gen */}
{metrics.memory.regions.oldGen && (
<div className="p-4 bg-gradient-to-br from-blue-50 to-indigo-50 dark:from-blue-950/20 dark:to-indigo-950/20 rounded-lg border border-blue-200 dark:border-blue-900">
<div className="text-sm font-medium text-muted-foreground mb-2">Old Gen</div>
<div className="space-y-1 text-xs">
<div className="flex justify-between">
<span className="text-muted-foreground">使:</span>
<span className="font-medium">{formatBytes(metrics.memory.regions.oldGen.used)}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">:</span>
<span className="font-medium">{formatBytes(metrics.memory.regions.oldGen.committed)}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">:</span>
<span className="font-medium">{formatBytes(metrics.memory.regions.oldGen.max)}</span>
</div>
<Progress
value={(metrics.memory.regions.oldGen.used / metrics.memory.regions.oldGen.max) * 100}
className="h-1 mt-2"
/>
</div>
</div>
)}
{/* Survivor 区 */}
{metrics.memory.regions.survivor && (
<div className="p-4 bg-gradient-to-br from-yellow-50 to-orange-50 dark:from-yellow-950/20 dark:to-orange-950/20 rounded-lg border border-yellow-200 dark:border-yellow-900">
<div className="text-sm font-medium text-muted-foreground mb-2">Survivor </div>
<div className="space-y-1 text-xs">
<div className="flex justify-between">
<span className="text-muted-foreground">使:</span>
<span className="font-medium">{formatBytes(metrics.memory.regions.survivor.used)}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">:</span>
<span className="font-medium">{formatBytes(metrics.memory.regions.survivor.committed)}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">:</span>
<span className="font-medium">{formatBytes(metrics.memory.regions.survivor.max)}</span>
</div>
<Progress
value={(metrics.memory.regions.survivor.used / metrics.memory.regions.survivor.max) * 100}
className="h-1 mt-2"
/>
</div>
</div>
)}
{/* Metaspace */}
{metrics.memory.regions.metaspace && (
<div className="p-4 bg-gradient-to-br from-purple-50 to-pink-50 dark:from-purple-950/20 dark:to-pink-950/20 rounded-lg border border-purple-200 dark:border-purple-900">
<div className="text-sm font-medium text-muted-foreground mb-2">Metaspace</div>
<div className="space-y-1 text-xs">
<div className="flex justify-between">
<span className="text-muted-foreground">使:</span>
<span className="font-medium">{formatBytes(metrics.memory.regions.metaspace.used)}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">:</span>
<span className="font-medium">{formatBytes(metrics.memory.regions.metaspace.committed)}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">:</span>
<span className="font-medium">{formatBytes(metrics.memory.regions.metaspace.max)}</span>
</div>
<Progress
value={(metrics.memory.regions.metaspace.used / metrics.memory.regions.metaspace.max) * 100}
className="h-1 mt-2"
/>
</div>
</div>
)}
</div>
</CardContent>
</Card>
)}
{/* GC 统计 */} {/* GC 统计 */}
<Card> <Card>
<CardHeader> <CardHeader>

View File

@ -42,28 +42,95 @@ export const getMetric = (metricName: string, tag?: string) => {
}; };
/** /**
* JVM * JVM committed
*/ */
export const getJvmMemory = async (): Promise<JvmMemoryInfo> => { export const getJvmMemory = async (): Promise<JvmMemoryInfo> => {
const [heapUsedRes, heapMaxRes, nonHeapUsedRes, nonHeapMaxRes] = await Promise.all([ // 基础堆和非堆内存指标
const [
heapUsedRes,
heapMaxRes,
heapCommittedRes,
nonHeapUsedRes,
nonHeapMaxRes,
nonHeapCommittedRes
] = await Promise.all([
getMetric('jvm.memory.used', 'area:heap'), getMetric('jvm.memory.used', 'area:heap'),
getMetric('jvm.memory.max', 'area:heap'), getMetric('jvm.memory.max', 'area:heap'),
getMetric('jvm.memory.committed', 'area:heap'),
getMetric('jvm.memory.used', 'area:nonheap'), getMetric('jvm.memory.used', 'area:nonheap'),
getMetric('jvm.memory.max', 'area:nonheap'), getMetric('jvm.memory.max', 'area:nonheap'),
getMetric('jvm.memory.committed', 'area:nonheap'),
]); ]);
const heapUsed = heapUsedRes.measurements[0].value; const heapUsed = heapUsedRes.measurements[0].value;
const heapMax = heapMaxRes.measurements[0].value; const heapMax = heapMaxRes.measurements[0].value;
const heapCommitted = heapCommittedRes.measurements[0].value;
const nonHeapUsed = nonHeapUsedRes.measurements[0].value; const nonHeapUsed = nonHeapUsedRes.measurements[0].value;
const nonHeapMax = nonHeapMaxRes.measurements[0].value; const nonHeapMax = nonHeapMaxRes.measurements[0].value;
const nonHeapCommitted = nonHeapCommittedRes.measurements[0].value;
// 尝试获取详细区域信息可能因GC类型不同而失败
let regions;
try {
const [edenUsed, edenMax, edenCommitted, oldUsed, oldMax, oldCommitted,
survivorUsed, survivorMax, survivorCommitted, metaspaceUsed, metaspaceMax, metaspaceCommitted] =
await Promise.all([
// Eden 区
getMetric('jvm.memory.used', 'id:G1 Eden Space').catch(() => null),
getMetric('jvm.memory.max', 'id:G1 Eden Space').catch(() => null),
getMetric('jvm.memory.committed', 'id:G1 Eden Space').catch(() => null),
// Old Gen
getMetric('jvm.memory.used', 'id:G1 Old Gen').catch(() => null),
getMetric('jvm.memory.max', 'id:G1 Old Gen').catch(() => null),
getMetric('jvm.memory.committed', 'id:G1 Old Gen').catch(() => null),
// Survivor
getMetric('jvm.memory.used', 'id:G1 Survivor Space').catch(() => null),
getMetric('jvm.memory.max', 'id:G1 Survivor Space').catch(() => null),
getMetric('jvm.memory.committed', 'id:G1 Survivor Space').catch(() => null),
// Metaspace
getMetric('jvm.memory.used', 'id:Metaspace').catch(() => null),
getMetric('jvm.memory.max', 'id:Metaspace').catch(() => null),
getMetric('jvm.memory.committed', 'id:Metaspace').catch(() => null),
]);
regions = {
eden: edenUsed && edenMax && edenCommitted ? {
used: edenUsed.measurements[0].value,
max: edenMax.measurements[0].value,
committed: edenCommitted.measurements[0].value,
} : undefined,
oldGen: oldUsed && oldMax && oldCommitted ? {
used: oldUsed.measurements[0].value,
max: oldMax.measurements[0].value,
committed: oldCommitted.measurements[0].value,
} : undefined,
survivor: survivorUsed && survivorMax && survivorCommitted ? {
used: survivorUsed.measurements[0].value,
max: survivorMax.measurements[0].value,
committed: survivorCommitted.measurements[0].value,
} : undefined,
metaspace: metaspaceUsed && metaspaceMax && metaspaceCommitted ? {
used: metaspaceUsed.measurements[0].value,
max: metaspaceMax.measurements[0].value,
committed: metaspaceCommitted.measurements[0].value,
} : undefined,
};
} catch (error) {
// 如果区域详情获取失败可能是非G1 GC忽略错误
console.debug('无法获取内存区域详情可能使用了非G1 GC收集器');
regions = undefined;
}
return { return {
heapUsed, heapUsed,
heapMax, heapMax,
heapCommitted,
heapPercent: (heapUsed / heapMax) * 100, heapPercent: (heapUsed / heapMax) * 100,
nonHeapUsed, nonHeapUsed,
nonHeapMax, nonHeapMax,
nonHeapCommitted,
nonHeapPercent: (nonHeapUsed / nonHeapMax) * 100, nonHeapPercent: (nonHeapUsed / nonHeapMax) * 100,
regions,
}; };
}; };
@ -232,9 +299,11 @@ export const getAllMetrics = async (): Promise<SystemMetrics> => {
memory: memoryResult.status === 'fulfilled' ? memoryResult.value : { memory: memoryResult.status === 'fulfilled' ? memoryResult.value : {
heapUsed: 0, heapUsed: 0,
heapMax: 0, heapMax: 0,
heapCommitted: 0,
heapPercent: 0, heapPercent: 0,
nonHeapUsed: 0, nonHeapUsed: 0,
nonHeapMax: 0, nonHeapMax: 0,
nonHeapCommitted: 0,
nonHeapPercent: 0, nonHeapPercent: 0,
}, },
cpu: cpuResult.status === 'fulfilled' ? cpuResult.value : { systemCpu: 0, processCpu: 0 }, cpu: cpuResult.status === 'fulfilled' ? cpuResult.value : { systemCpu: 0, processCpu: 0 },

View File

@ -49,16 +49,34 @@ export interface MetricResponse {
}>; }>;
} }
/**
* JVM
*/
export interface MemoryRegion {
used: number; // 已使用(字节)
max: number; // 最大值(字节)
committed: number; // 已分配(字节)
}
/** /**
* JVM * JVM
*/ */
export interface JvmMemoryInfo { export interface JvmMemoryInfo {
heapUsed: number; // 堆内存使用量(字节) heapUsed: number; // 堆内存使用量(字节)
heapMax: number; // 堆内存最大值(字节) heapMax: number; // 堆内存最大值(字节)
heapCommitted: number; // 堆内存已分配(字节)
heapPercent: number; // 堆内存使用百分比 heapPercent: number; // 堆内存使用百分比
nonHeapUsed: number; // 非堆内存使用量(字节) nonHeapUsed: number; // 非堆内存使用量(字节)
nonHeapMax: number; // 非堆内存最大值(字节) nonHeapMax: number; // 非堆内存最大值(字节)
nonHeapCommitted: number; // 非堆内存已分配(字节)
nonHeapPercent: number; // 非堆内存使用百分比 nonHeapPercent: number; // 非堆内存使用百分比
// 详细区域信息(可选)
regions?: {
eden?: MemoryRegion; // Eden 区(年轻代)
oldGen?: MemoryRegion; // Old Gen老年代
survivor?: MemoryRegion; // Survivor 区
metaspace?: MemoryRegion; // Metaspace元空间
};
} }
/** /**