Files
MyGoNavi/frontend/src/utils/jvmMonitoringPresentation.ts
tianqijiuyun-latiao 5fc29a6fd3 feat(i18n): 推进多语言剩余切片闭环
- 补齐 DataGrid、DataViewer、DefinitionViewer、JVM 等模块多语言文案与回归测试
- 收口 JVM 前后端展示、诊断、监控和资源呈现相关多语言路径
- 更新六语言共享词典并保留 raw 边界
2026-06-16 12:40:33 +08:00

218 lines
6.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type {
JVMMonitoringPoint,
JVMMonitoringRecentGCEvent,
JVMMonitoringSessionState,
} from "../types";
import {
getCurrentLanguage,
t,
type SupportedLanguage,
} from "../i18n";
const METRIC_LABELS: Record<string, string> = {
"heap.used": "jvm_monitoring_presentation.metric.heap_used",
"heap.non_heap": "jvm_monitoring_presentation.metric.heap_non_heap",
"gc.count": "jvm_monitoring_presentation.metric.gc_count",
"gc.time": "jvm_monitoring_presentation.metric.gc_time",
"gc.events": "jvm_monitoring_presentation.metric.gc_events",
"thread.count": "jvm_monitoring_presentation.metric.thread_count",
"thread.states": "jvm_monitoring_presentation.metric.thread_states",
"class.loading": "jvm_monitoring_presentation.metric.class_loading",
"cpu.process": "jvm_monitoring_presentation.metric.cpu_process",
"cpu.system": "jvm_monitoring_presentation.metric.cpu_system",
"memory.rss": "jvm_monitoring_presentation.metric.memory_rss",
"memory.virtual": "jvm_monitoring_presentation.metric.memory_virtual",
};
export type JVMMonitoringProviderMode = JVMMonitoringSessionState["providerMode"];
const MONITORING_PROVIDER_MODES: JVMMonitoringProviderMode[] = [
"jmx",
"endpoint",
"agent",
];
const THREAD_STATE_LABELS: Record<string, string> = {
NEW: "jvm_monitoring_presentation.thread_state.new",
RUNNABLE: "jvm_monitoring_presentation.thread_state.runnable",
BLOCKED: "jvm_monitoring_presentation.thread_state.blocked",
WAITING: "jvm_monitoring_presentation.thread_state.waiting",
TIMED_WAITING: "jvm_monitoring_presentation.thread_state.timed_waiting",
TERMINATED: "jvm_monitoring_presentation.thread_state.terminated",
};
const resolveLanguage = (language?: SupportedLanguage): SupportedLanguage =>
language ?? getCurrentLanguage();
const createTimeFormatter = (language?: SupportedLanguage) => new Intl.DateTimeFormat(resolveLanguage(language), {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false,
});
export type MonitoringChartPoint = JVMMonitoringPoint & {
timeLabel: string;
};
export const resolveMonitoringMetricLabel = (
metric: string,
language?: SupportedLanguage,
): string => {
const normalized = String(metric || "").trim();
const key = METRIC_LABELS[normalized];
return key ? t(key, undefined, resolveLanguage(language)) : normalized;
};
export const resolveThreadStateLabel = (
state?: string | null,
language?: SupportedLanguage,
): string => {
const normalized = String(state || "").trim().toUpperCase();
const key = THREAD_STATE_LABELS[normalized];
return key ? t(key, undefined, resolveLanguage(language)) : String(state || "").trim();
};
export const formatMonitoringTime = (
timestamp?: number,
language?: SupportedLanguage,
): string => {
if (typeof timestamp !== "number" || !Number.isFinite(timestamp)) {
return "--";
}
return createTimeFormatter(language).format(new Date(timestamp));
};
export const formatBytes = (value?: number): string => {
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
return "--";
}
const units = ["B", "KB", "MB", "GB", "TB"];
let next = value;
let unitIndex = 0;
while (next >= 1024 && unitIndex < units.length - 1) {
next /= 1024;
unitIndex += 1;
}
const precision = next >= 100 || unitIndex === 0 ? 0 : next >= 10 ? 1 : 2;
return `${next.toFixed(precision)} ${units[unitIndex]}`;
};
export const formatMonitoringAxisBytes = (value?: number): string => formatBytes(value);
export const formatPercent = (value?: number): string => {
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
return "--";
}
return `${(value * 100).toFixed(1)}%`;
};
export const formatCompactNumber = (
value?: number,
language?: SupportedLanguage,
): string => {
if (typeof value !== "number" || !Number.isFinite(value)) {
return "--";
}
return value.toLocaleString(resolveLanguage(language));
};
export const formatDurationMs = (value?: number): string => {
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
return "--";
}
return `${Math.round(value)}ms`;
};
export const normalizeMonitoringProviderMode = (
value: unknown,
fallback: JVMMonitoringProviderMode = "jmx",
): JVMMonitoringProviderMode => {
const normalized = String(value || "").trim().toLowerCase();
if (MONITORING_PROVIDER_MODES.includes(normalized as JVMMonitoringProviderMode)) {
return normalized as JVMMonitoringProviderMode;
}
return MONITORING_PROVIDER_MODES.includes(fallback) ? fallback : "jmx";
};
export const buildMonitoringAvailabilityText = ({
missingMetrics,
providerWarnings,
}: Pick<JVMMonitoringSessionState, "missingMetrics" | "providerWarnings">,
language?: SupportedLanguage,
): string => {
const resolvedLanguage = resolveLanguage(language);
const fragments: string[] = [];
if (Array.isArray(missingMetrics) && missingMetrics.length > 0) {
const metrics = missingMetrics
.map((metric) => resolveMonitoringMetricLabel(metric, resolvedLanguage))
.join(resolvedLanguage.startsWith("zh") ? "、" : ", ");
fragments.push(
t("jvm_monitoring_presentation.availability.missing_metrics", {
metrics,
}, resolvedLanguage),
);
}
if (Array.isArray(providerWarnings) && providerWarnings.length > 0) {
fragments.push(
t("jvm_monitoring_presentation.availability.provider_warnings", {
warnings: providerWarnings.join(resolvedLanguage.startsWith("zh") ? "" : "; "),
}, resolvedLanguage),
);
}
if (fragments.length === 0) {
return t(
"jvm_monitoring_presentation.availability.no_obvious_degradation",
undefined,
resolvedLanguage,
);
}
return fragments.join(" | ");
};
export const formatRecentGCLabel = (
event: JVMMonitoringRecentGCEvent,
language?: SupportedLanguage,
): string => {
const parts = [
formatMonitoringTime(event.timestamp, language),
String(event.name || "").trim(),
typeof event.durationMs === "number" ? `${event.durationMs}ms` : "",
String(event.cause || "").trim(),
].filter(Boolean);
return parts.join(" · ");
};
export const buildMonitoringChartPoints = (
points: JVMMonitoringPoint[] = [],
language?: SupportedLanguage,
): MonitoringChartPoint[] =>
points.map((point) => ({
...point,
timeLabel: formatMonitoringTime(point.timestamp, language),
}));
export const extractThreadStateRows = (
point?: JVMMonitoringPoint,
language?: SupportedLanguage,
): Array<{ state: string; label: string; count: number }> =>
Object.entries(point?.threadStateCounts || {})
.map(([state, count]) => ({
state,
label: resolveThreadStateLabel(state, language),
count: Number(count) || 0,
}))
.sort((left, right) => right.count - left.count);
export const monitoringMetricAvailable = (
session: Pick<JVMMonitoringSessionState, "availableMetrics"> | undefined,
metric: string,
): boolean =>
Array.isArray(session?.availableMetrics) &&
session.availableMetrics.includes(metric);