mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-05-20 08:28:48 -05:00
diagnostics
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { BaseChart } from "@/Components/v2/design-elements";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
@@ -104,3 +106,50 @@ export const Gauge = ({
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export const DetailGauge = ({
|
||||
title,
|
||||
progress,
|
||||
upperLabel,
|
||||
upperValue,
|
||||
lowerLabel,
|
||||
lowerValue,
|
||||
}: {
|
||||
title: string;
|
||||
progress: number;
|
||||
upperLabel?: string;
|
||||
upperValue?: string | number;
|
||||
lowerLabel?: string;
|
||||
lowerValue?: string | number;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<BaseChart
|
||||
icon={null}
|
||||
title={title}
|
||||
maxWidth={225}
|
||||
>
|
||||
<Stack
|
||||
alignItems={"center"}
|
||||
mb={theme.spacing(4)}
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
<Gauge progress={progress} />
|
||||
</Stack>
|
||||
<Stack
|
||||
direction={"row"}
|
||||
justifyContent={"space-between"}
|
||||
>
|
||||
<Typography>{upperLabel}</Typography>
|
||||
<Typography>{upperValue}</Typography>
|
||||
</Stack>
|
||||
<Stack
|
||||
direction={"row"}
|
||||
justifyContent={"space-between"}
|
||||
>
|
||||
<Typography>{lowerLabel}</Typography>
|
||||
<Typography>{lowerValue}</Typography>
|
||||
</Stack>
|
||||
</BaseChart>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { BaseChart, Gauge } from "@/Components/v2/design-elements";
|
||||
import { DetailGauge } from "@/Components/v2/design-elements";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getGbs, getFrequency } from "@/Utils/InfraUtils";
|
||||
@@ -8,53 +7,6 @@ import { useTheme } from "@mui/material";
|
||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||
import type { CheckSnapshot } from "@/Types/Check";
|
||||
|
||||
const InfraDetailGauge = ({
|
||||
title,
|
||||
progress,
|
||||
upperLabel,
|
||||
upperValue,
|
||||
lowerLabel,
|
||||
lowerValue,
|
||||
}: {
|
||||
title: string;
|
||||
progress: number;
|
||||
upperLabel?: string;
|
||||
upperValue?: string | number;
|
||||
lowerLabel?: string;
|
||||
lowerValue?: string | number;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<BaseChart
|
||||
icon={null}
|
||||
title={title}
|
||||
maxWidth={225}
|
||||
>
|
||||
<Stack
|
||||
alignItems={"center"}
|
||||
mb={theme.spacing(4)}
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
<Gauge progress={progress} />
|
||||
</Stack>
|
||||
<Stack
|
||||
direction={"row"}
|
||||
justifyContent={"space-between"}
|
||||
>
|
||||
<Typography>{upperLabel}</Typography>
|
||||
<Typography>{upperValue}</Typography>
|
||||
</Stack>
|
||||
<Stack
|
||||
direction={"row"}
|
||||
justifyContent={"space-between"}
|
||||
>
|
||||
<Typography>{lowerLabel}</Typography>
|
||||
<Typography>{lowerValue}</Typography>
|
||||
</Stack>
|
||||
</BaseChart>
|
||||
);
|
||||
};
|
||||
|
||||
export const InfraDetailsGauges = ({
|
||||
snapshot,
|
||||
}: {
|
||||
@@ -74,7 +26,7 @@ export const InfraDetailsGauges = ({
|
||||
spacing={theme.spacing(8)}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<InfraDetailGauge
|
||||
<DetailGauge
|
||||
title={t("pages.infrastructure.gauges.memory.title")}
|
||||
progress={(snapshot?.memory?.usage_percent || 0) * 100}
|
||||
upperLabel={t("pages.infrastructure.gauges.memory.upperLabel")}
|
||||
@@ -82,7 +34,7 @@ export const InfraDetailsGauges = ({
|
||||
lowerLabel={t("pages.infrastructure.gauges.memory.lowerLabel")}
|
||||
lowerValue={getGbs(snapshot?.memory?.total_bytes || 0)}
|
||||
/>
|
||||
<InfraDetailGauge
|
||||
<DetailGauge
|
||||
title={t("pages.infrastructure.gauges.cpu.title")}
|
||||
progress={(snapshot?.cpu?.usage_percent || 0) * 100}
|
||||
upperLabel={t("pages.infrastructure.gauges.cpu.upperLabel")}
|
||||
@@ -92,7 +44,7 @@ export const InfraDetailsGauges = ({
|
||||
/>
|
||||
{snapshot?.disk?.map((disk, idx) => {
|
||||
return (
|
||||
<InfraDetailGauge
|
||||
<DetailGauge
|
||||
key={disk?.device || 0 + idx}
|
||||
// title={`Disk ${idx} usage`}
|
||||
title={t("pages.infrastructure.gauges.disk.title", { idx })}
|
||||
|
||||
@@ -9,9 +9,9 @@ import { getPercentage, formatBytes } from "../../utils/utils.js";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
const BaseContainer = ({children}) => {
|
||||
const theme = useTheme()
|
||||
return(
|
||||
const BaseContainer = ({ children }) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
padding: theme.spacing(3),
|
||||
@@ -22,13 +22,21 @@ const BaseContainer = ({children}) => {
|
||||
[theme.breakpoints.down("md")]: {
|
||||
width: `calc(50% - (1 * ${theme.spacing(8)} / 2))`,
|
||||
},
|
||||
}}>
|
||||
{children}
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const InfrastructureStyleGauge = ({ value, heading, metricOne, valueOne, metricTwo, valueTwo }) => {
|
||||
const InfrastructureStyleGauge = ({
|
||||
value,
|
||||
heading,
|
||||
metricOne,
|
||||
valueOne,
|
||||
metricTwo,
|
||||
valueTwo,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const MetricRow = ({ label, value }) => (
|
||||
@@ -39,40 +47,58 @@ const InfrastructureStyleGauge = ({ value, heading, metricOne, valueOne, metricT
|
||||
gap={theme.spacing(2)}
|
||||
>
|
||||
<Typography>{label}</Typography>
|
||||
<Typography sx={{
|
||||
borderRadius: theme.spacing(2),
|
||||
backgroundColor: theme.palette.tertiary.main,
|
||||
width: "40%",
|
||||
mb: theme.spacing(2),
|
||||
mt: theme.spacing(2),
|
||||
pr: theme.spacing(2),
|
||||
textAlign: "right",
|
||||
}}>
|
||||
<Typography
|
||||
sx={{
|
||||
borderRadius: theme.spacing(2),
|
||||
backgroundColor: theme.palette.tertiary.main,
|
||||
width: "40%",
|
||||
mb: theme.spacing(2),
|
||||
mt: theme.spacing(2),
|
||||
pr: theme.spacing(2),
|
||||
textAlign: "right",
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
</Typography>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
return(
|
||||
return (
|
||||
<BaseContainer>
|
||||
<Stack direction="column" gap={theme.spacing(2)} alignItems="center">
|
||||
<Stack
|
||||
direction="column"
|
||||
gap={theme.spacing(2)}
|
||||
alignItems="center"
|
||||
>
|
||||
<Box
|
||||
sx = {{
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<CustomGauge progress={value} radius={100}/>
|
||||
<Typography component="h2" sx={{fontWeight: 600}}>
|
||||
<CustomGauge
|
||||
progress={value}
|
||||
radius={100}
|
||||
/>
|
||||
<Typography
|
||||
component="h2"
|
||||
sx={{ fontWeight: 600 }}
|
||||
>
|
||||
{heading}
|
||||
</Typography>
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ width:"100%", borderTop:`1px solid ${theme.palette.divider}`}}>
|
||||
<MetricRow label={metricOne} value={valueOne} />
|
||||
<Box sx={{ width: "100%", borderTop: `1px solid ${theme.palette.divider}` }}>
|
||||
<MetricRow
|
||||
label={metricOne}
|
||||
value={valueOne}
|
||||
/>
|
||||
{metricTwo && valueTwo && (
|
||||
<MetricRow label={metricTwo} value={valueTwo} />
|
||||
<MetricRow
|
||||
label={metricTwo}
|
||||
value={valueTwo}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Stack>
|
||||
@@ -147,16 +173,16 @@ Gauges.propTypes = {
|
||||
};
|
||||
|
||||
InfrastructureStyleGauge.propTypes = {
|
||||
value: PropTypes.number,
|
||||
heading: PropTypes.string,
|
||||
metricOne: PropTypes.string,
|
||||
valueOne: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
metricTwo: PropTypes.string,
|
||||
valueTwo: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
value: PropTypes.number,
|
||||
heading: PropTypes.string,
|
||||
metricOne: PropTypes.string,
|
||||
valueOne: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
metricTwo: PropTypes.string,
|
||||
valueTwo: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
};
|
||||
|
||||
|
||||
BaseContainer.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
export default Gauges;
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
import Stack from "@mui/material/Stack";
|
||||
import { DetailGauge } from "@/Components/v2/design-elements";
|
||||
|
||||
import prettyBytes from "pretty-bytes";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTheme } from "@mui/material";
|
||||
import type { Diagnostics } from "@/Types/Diagnostics";
|
||||
|
||||
interface StatGaugesProps {
|
||||
diagnostics: Diagnostics | null;
|
||||
}
|
||||
|
||||
const getPercentage = (value: number, total: number) => {
|
||||
if (!value || !total) return 0;
|
||||
return (value / total) * 100;
|
||||
};
|
||||
|
||||
const formatPerecentage = new Intl.NumberFormat("en-US", {
|
||||
style: "percent",
|
||||
minimumFractionDigits: 1,
|
||||
maximumFractionDigits: 1,
|
||||
});
|
||||
|
||||
export const StatGauges = ({ diagnostics }: StatGaugesProps) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
if (!diagnostics) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const heapTotalSize = getPercentage(
|
||||
diagnostics?.v8HeapStats?.totalHeapSizeBytes,
|
||||
diagnostics?.v8HeapStats?.heapSizeLimitBytes
|
||||
);
|
||||
|
||||
const heapUsedSize = getPercentage(
|
||||
diagnostics?.v8HeapStats?.usedHeapSizeBytes,
|
||||
diagnostics?.v8HeapStats?.heapSizeLimitBytes
|
||||
);
|
||||
|
||||
const actualHeapUsed = getPercentage(
|
||||
diagnostics?.v8HeapStats?.usedHeapSizeBytes,
|
||||
diagnostics?.v8HeapStats?.totalHeapSizeBytes
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack
|
||||
direction={{ xs: "column", md: "row" }}
|
||||
gap={theme.spacing(8)}
|
||||
>
|
||||
<DetailGauge
|
||||
title={t("pages.logs.diagnostics.gauges.heapAllocation")}
|
||||
progress={heapTotalSize}
|
||||
upperValue={formatPerecentage.format(heapTotalSize / 100)}
|
||||
lowerLabel={t("pages.logs.diagnostics.gauges.total")}
|
||||
lowerValue={prettyBytes(diagnostics.v8HeapStats?.heapSizeLimitBytes ?? 0)}
|
||||
/>
|
||||
<DetailGauge
|
||||
title={t("pages.logs.diagnostics.gauges.heapUsage")}
|
||||
progress={heapUsedSize}
|
||||
upperLabel={t("pages.logs.diagnostics.gauges.availableMemoryPercentage")}
|
||||
upperValue={formatPerecentage.format(heapUsedSize / 100)}
|
||||
lowerLabel={t("pages.logs.diagnostics.gauges.used")}
|
||||
lowerValue={prettyBytes(diagnostics.v8HeapStats?.usedHeapSizeBytes ?? 0)}
|
||||
/>
|
||||
<DetailGauge
|
||||
title={t("pages.logs.diagnostics.gauges.heapUtilization")}
|
||||
progress={actualHeapUsed}
|
||||
upperLabel={t("pages.logs.diagnostics.gauges.allocatedPercentage")}
|
||||
upperValue={formatPerecentage.format(actualHeapUsed / 100)}
|
||||
lowerLabel={t("pages.logs.diagnostics.gauges.total")}
|
||||
lowerValue={prettyBytes(diagnostics.v8HeapStats?.usedHeapSizeBytes ?? 0)}
|
||||
/>
|
||||
<DetailGauge
|
||||
title={t("pages.logs.diagnostics.gauges.instantCpuUsage")}
|
||||
progress={diagnostics.cpuUsage?.usagePercentage ?? 0}
|
||||
upperLabel={t("pages.logs.diagnostics.gauges.usedSPercentage")}
|
||||
upperValue={formatPerecentage.format(
|
||||
(diagnostics.cpuUsage?.usagePercentage ?? 0) / 100
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -26,29 +26,25 @@ export const Stats = ({ diagnostics }: StatsProps) => {
|
||||
>
|
||||
<StatBox
|
||||
title={t("pages.logs.diagnostics.stats.eventLoopDelay")}
|
||||
subtitle={prettyMilliseconds(diagnostics.eventLoopDelayMs, {
|
||||
subtitle={prettyMilliseconds(diagnostics.eventLoopDelayMs ?? 0, {
|
||||
millisecondsDecimalDigits: 2,
|
||||
})}
|
||||
/>
|
||||
<StatBox
|
||||
title={t("pages.logs.diagnostics.stats.uptime")}
|
||||
subtitle={prettyMilliseconds(diagnostics.uptimeMs, { hideSeconds: true })}
|
||||
/>
|
||||
<StatBox
|
||||
title={t("pages.logs.diagnostics.stats.uptime")}
|
||||
subtitle={prettyMilliseconds(diagnostics.uptimeMs, { hideSeconds: true })}
|
||||
subtitle={prettyMilliseconds(diagnostics.uptimeMs ?? 0, { hideSeconds: true })}
|
||||
/>
|
||||
<StatBox
|
||||
title={t("pages.logs.diagnostics.stats.usedHeapSize")}
|
||||
subtitle={prettyBytes(diagnostics.v8HeapStats.usedHeapSizeBytes)}
|
||||
subtitle={prettyBytes(diagnostics.v8HeapStats?.usedHeapSizeBytes ?? 0)}
|
||||
/>
|
||||
<StatBox
|
||||
title={t("pages.logs.diagnostics.stats.totalHeapSize")}
|
||||
subtitle={prettyBytes(diagnostics.v8HeapStats.totalHeapSizeBytes)}
|
||||
subtitle={prettyBytes(diagnostics.v8HeapStats?.totalHeapSizeBytes ?? 0)}
|
||||
/>
|
||||
<StatBox
|
||||
title={t("pages.logs.diagnostics.stats.osMemoryLimit")}
|
||||
subtitle={prettyBytes(diagnostics.osStats.totalMemoryBytes)}
|
||||
subtitle={prettyBytes(diagnostics.osStats?.totalMemoryBytes ?? 0)}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Stack from "@mui/material/Stack";
|
||||
import { Stats } from "@/Pages/Logs/Stats";
|
||||
import { StatGauges } from "@/Pages/Logs/StatGauges";
|
||||
|
||||
import { useTheme } from "@mui/material";
|
||||
import { useGet } from "@/Hooks/UseApi";
|
||||
@@ -9,14 +10,15 @@ export const TabDiagnostics = () => {
|
||||
const theme = useTheme();
|
||||
const {
|
||||
data: diagnostics,
|
||||
isLoading,
|
||||
error,
|
||||
refetch,
|
||||
} = useGet<Diagnostics>("/diagnostic/system");
|
||||
isLoading: _isLoading,
|
||||
error: _error,
|
||||
refetch: _refetch,
|
||||
} = useGet<Diagnostics>("/diagnostic/system", {}, { refreshInterval: 5000 });
|
||||
|
||||
return (
|
||||
<Stack gap={theme.spacing(8)}>
|
||||
<Stats diagnostics={diagnostics} />
|
||||
<StatGauges diagnostics={diagnostics} />
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user