diff --git a/client/src/Components/Charts/Utils/chartUtils.jsx b/client/src/Components/Charts/Utils/chartUtils.jsx
index 40eb5b539..a7db3ea93 100644
--- a/client/src/Components/Charts/Utils/chartUtils.jsx
+++ b/client/src/Components/Charts/Utils/chartUtils.jsx
@@ -101,16 +101,17 @@ const getFormattedPercentage = (value) => {
* @param {number} props.index - The index of the tick.
* @returns {JSX.Element|null} The rendered tick component or null for the first tick.
*/
-export const NetworkTick = ({ x, y, payload, index }) => {
+export const NetworkTick = ({ x, y, payload, index, formatter}) => {
const theme = useTheme();
if (index === 0) return null;
- const formatBytes = (bytes) => {
- if (bytes >= 1_000_000_000) return `${(bytes / 1_000_000_000).toFixed(1)} GB/s`;
- if (bytes >= 1_000_000) return `${(bytes / 1_000_000).toFixed(1)} MB/s`;
- if (bytes >= 1_000) return `${(bytes / 1_000).toFixed(1)} KB/s`;
- return `${bytes} B/s`;
- };
+ if (formatter === undefined) {
+ formatter = (value, space=false) => {
+ if (typeof value !== "number") return value;
+ // need to add space between value and unit
+ return `${(value / 1024).toFixed(2)}${space ? " " : ""}Kbps`;
+ };
+ }
return (
{
fontSize={11}
fontWeight={400}
>
- {formatBytes(payload?.value)}
+ {formatter(payload?.value, true)}
);
};
@@ -131,6 +132,7 @@ NetworkTick.propTypes = {
y: PropTypes.number,
payload: PropTypes.object,
index: PropTypes.number,
+ formatter: PropTypes.func,
};
/**
diff --git a/client/src/Pages/Infrastructure/Details/Components/NetworkStats/NetworkCharts.jsx b/client/src/Pages/Infrastructure/Details/Components/NetworkStats/NetworkCharts.jsx
index 5dd9feaad..84325c29e 100644
--- a/client/src/Pages/Infrastructure/Details/Components/NetworkStats/NetworkCharts.jsx
+++ b/client/src/Pages/Infrastructure/Details/Components/NetworkStats/NetworkCharts.jsx
@@ -1,9 +1,7 @@
-// NetworkCharts.jsx
import PropTypes from "prop-types";
-import { Stack } from "@mui/material";
+import { Stack, Typography } from "@mui/material";
import InfraAreaChart from "../../../../../Pages/Infrastructure/Details/Components/AreaChartBoxes/InfraAreaChart";
-// Utils
import {
TzTick,
InfrastructureTooltip,
@@ -11,43 +9,42 @@ import {
} from "../../../../../Components/Charts/Utils/chartUtils";
import { useTheme } from "@emotion/react";
import { useTranslation } from "react-i18next";
+import { useHardwareUtils } from "../../Hooks/useHardwareUtils";
-const getFormattedNetworkMetric = (value) => {
- if (typeof value !== "number" || isNaN(value)) return "0";
- if (value >= 1024 ** 3) return `${(value / 1024 ** 3).toFixed(1)} GB/s`;
- if (value >= 1024 ** 2) return `${(value / 1024 ** 2).toFixed(1)} MB/s`;
- if (value >= 1024) return `${(value / 1024).toFixed(1)} KB/s`;
- return `${Math.round(value)} B/s`;
-};
-
-const NetworkCharts = ({ eth0Data, dateRange }) => {
+const NetworkCharts = ({ ethernetData, dateRange }) => {
const theme = useTheme();
const { t } = useTranslation();
+ const {formatBytesString} = useHardwareUtils();
+
+ if (!ethernetData?.length) {
+ return {t("noNetworkStatsAvailable")};
+ }
+
const configs = [
{
type: "network-bytes",
- data: eth0Data,
+ data: ethernetData,
dataKeys: ["bytesPerSec"],
heading: t("bytesPerSecond"),
strokeColor: theme.palette.info.main,
gradientStartColor: theme.palette.info.main,
yLabel: t("bytesPerSecond"),
xTick: ,
- yTick: ,
+ yTick: ,
toolTip: (
),
},
{
type: "network-packets",
- data: eth0Data,
+ data: ethernetData,
dataKeys: ["packetsPerSec"],
heading: t("packetsPerSecond"),
strokeColor: theme.palette.success.main,
@@ -66,7 +63,7 @@ const NetworkCharts = ({ eth0Data, dateRange }) => {
},
{
type: "network-errors",
- data: eth0Data,
+ data: ethernetData,
dataKeys: ["errors"],
heading: t("errors"),
strokeColor: theme.palette.error.main,
@@ -85,7 +82,7 @@ const NetworkCharts = ({ eth0Data, dateRange }) => {
},
{
type: "network-drops",
- data: eth0Data,
+ data: ethernetData,
dataKeys: ["drops"],
heading: t("drops"),
strokeColor: theme.palette.warning.main,
@@ -127,7 +124,7 @@ const NetworkCharts = ({ eth0Data, dateRange }) => {
};
NetworkCharts.propTypes = {
- eth0Data: PropTypes.array.isRequired,
+ ethernetData: PropTypes.array.isRequired,
dateRange: PropTypes.string.isRequired,
};
diff --git a/client/src/Pages/Infrastructure/Details/Components/NetworkStats/NetworkStatBoxes.jsx b/client/src/Pages/Infrastructure/Details/Components/NetworkStats/NetworkStatBoxes.jsx
index 3125e86a9..98ba90dae 100644
--- a/client/src/Pages/Infrastructure/Details/Components/NetworkStats/NetworkStatBoxes.jsx
+++ b/client/src/Pages/Infrastructure/Details/Components/NetworkStats/NetworkStatBoxes.jsx
@@ -1,27 +1,19 @@
-// NetworkStatBoxes.jsx
import PropTypes from "prop-types";
import StatusBoxes from "../../../../../Components/StatusBoxes";
import StatBox from "../../../../../Components/StatBox";
import { Typography } from "@mui/material";
import { useTranslation } from "react-i18next";
+import { useHardwareUtils } from "../../Hooks/useHardwareUtils";
-function formatBytes(bytes) {
- if (bytes === 0 || bytes == null) return "0 B";
- const k = 1024;
- const sizes = ["B", "KB", "MB", "GB", "TB"];
- const i = Math.floor(Math.log(bytes) / Math.log(k));
- return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`;
-}
-
-// Format numbers with commas
function formatNumber(num) {
return num != null ? num.toLocaleString() : "0";
}
-const NetworkStatBoxes = ({ shouldRender, net }) => {
+const NetworkStatBoxes = ({ shouldRender, net, ifaceName }) => {
const { t } = useTranslation();
- const filtered =
- net?.filter((iface) => iface.name === "en0" || iface.name === "wlan0") || [];
+ const { formatBytes } = useHardwareUtils();
+
+ const filtered = net?.filter((iface) => iface.name === ifaceName) || [];
if (!net?.length) {
return {t("noNetworkStatsAvailable")};
@@ -83,6 +75,7 @@ NetworkStatBoxes.propTypes = {
err_out: PropTypes.number,
})
),
+ ifaceName: PropTypes.string.isRequired,
};
export default NetworkStatBoxes;
diff --git a/client/src/Pages/Infrastructure/Details/Components/NetworkStats/index.jsx b/client/src/Pages/Infrastructure/Details/Components/NetworkStats/index.jsx
index 22a6c3112..623720aba 100644
--- a/client/src/Pages/Infrastructure/Details/Components/NetworkStats/index.jsx
+++ b/client/src/Pages/Infrastructure/Details/Components/NetworkStats/index.jsx
@@ -3,37 +3,40 @@ import NetworkStatBoxes from "./NetworkStatBoxes";
import NetworkCharts from "./NetworkCharts";
import MonitorTimeFrameHeader from "../../../../../Components/MonitorTimeFrameHeader";
-const getNetworkInterfaceData = (checks) => {
+const getInterfaceName = (net) => {
const interfaceNames = ["eth0", "Ethernet", "en0"];
+ const found = (net || []).find((iface) => interfaceNames.includes(iface.name));
+ return found ? found.name : null;
+};
+const getNetworkInterfaceData = (checks, ifaceName) => {
return (checks || [])
.map((check) => {
- const networkInterface = (check.net || []).find((iface) =>
- interfaceNames.includes(iface.name)
+ const networkInterface = (check.net || []).find(
+ (iface) => iface.name === ifaceName
);
-
- if (!networkInterface) {
- return null;
- }
-
+ if (!networkInterface) return null;
return {
_id: check._id,
- bytesPerSec: networkInterface.avgBytesRecv,
- packetsPerSec: networkInterface.avgPacketsRecv,
- errors: networkInterface.avgErrOut ?? 0,
- drops: networkInterface.avgDropOut ?? 0,
+ bytesPerSec: networkInterface.deltaBytesRecv,
+ packetsPerSec: networkInterface.deltaPacketsRecv,
+ errors: networkInterface.deltaErrOut ?? 0,
+ drops: networkInterface.deltaDropOut ?? 0,
};
})
.filter(Boolean);
};
const Network = ({ net, checks, isLoading, dateRange, setDateRange }) => {
- const eth0Data = getNetworkInterfaceData(checks);
+ const ifaceName = getInterfaceName(net);
+ const ethernetData = getNetworkInterfaceData(checks, ifaceName);
+
return (
<>
{
setDateRange={setDateRange}
/>
>
diff --git a/client/src/Pages/Infrastructure/Details/Hooks/useHardwareUtils.jsx b/client/src/Pages/Infrastructure/Details/Hooks/useHardwareUtils.jsx
index a2e4c5153..930ca7ba3 100644
--- a/client/src/Pages/Infrastructure/Details/Hooks/useHardwareUtils.jsx
+++ b/client/src/Pages/Infrastructure/Details/Hooks/useHardwareUtils.jsx
@@ -57,7 +57,7 @@ const useHardwareUtils = () => {
if (GB >= 1) {
return (
<>
- {Number(GB.toFixed(0))}
+ {Number(GB.toFixed(2))}
{space ? " " : ""}
{t("gb")}
>
@@ -65,7 +65,7 @@ const useHardwareUtils = () => {
} else {
return (
<>
- {Number(MB.toFixed(0))}
+ {Number(MB.toFixed(2))}
{space ? " " : ""}
{t("mb")}
>
@@ -73,6 +73,26 @@ const useHardwareUtils = () => {
}
};
+ const formatBytesString = (bytes, space = false) => {
+ if (
+ bytes === undefined ||
+ bytes === null ||
+ typeof bytes !== "number" ||
+ bytes === 0
+ ) {
+ return `0${space ? " " : ""}${t("gb")}`;
+ }
+
+ const GB = bytes / (1024 * 1024 * 1024);
+ const MB = bytes / (1024 * 1024);
+
+ if (GB >= 1) {
+ return `${Number(GB.toFixed(2))}${space ? " " : ""}${t("gb")}`;
+ } else {
+ return `${Number(MB.toFixed(2))}${space ? " " : ""}${t("mb")}`;
+ }
+ };
+
/**
* Converts a decimal value to a percentage
*
@@ -134,6 +154,7 @@ const useHardwareUtils = () => {
decimalToPercentage,
buildTemps,
getDimensions,
+ formatBytesString,
};
};
diff --git a/server/src/db/mongo/modules/monitorModule.js b/server/src/db/mongo/modules/monitorModule.js
index 0e301cdcf..3afe664f6 100755
--- a/server/src/db/mongo/modules/monitorModule.js
+++ b/server/src/db/mongo/modules/monitorModule.js
@@ -343,6 +343,17 @@ class MonitorModule {
const stats = hardwareStats[0];
+ if (stats?.net?.length) {
+ const elapsedSeconds = (new Date(dates.end).getTime() - new Date(dates.start).getTime()) / 1000;
+ stats.net = stats.net.map((iface) => ({
+ ...iface,
+ bytesSentPerSec: iface.deltaBytesSent / elapsedSeconds,
+ bytesRecvPerSec: iface.deltaBytesRecv / elapsedSeconds,
+ packetsSentPerSec: iface.deltaPacketsSent / elapsedSeconds,
+ packetsRecvPerSec: iface.deltaPacketsRecv / elapsedSeconds,
+ }));
+ }
+
return {
...monitor.toObject(),
stats,
diff --git a/server/src/db/mongo/modules/monitorModuleQueries.js b/server/src/db/mongo/modules/monitorModuleQueries.js
index c6812b1bc..91fb83444 100755
--- a/server/src/db/mongo/modules/monitorModuleQueries.js
+++ b/server/src/db/mongo/modules/monitorModuleQueries.js
@@ -395,7 +395,7 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
"$$netIndex",
],
},
- avgBytesSent: {
+ deltaBytesSent: {
$subtract: [
{
$arrayElemAt: [
@@ -417,7 +417,7 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
},
],
},
- avgBytesRecv: {
+ deltaBytesRecv: {
$subtract: [
{
$arrayElemAt: [
@@ -439,7 +439,7 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
},
],
},
- avgPacketsSent: {
+ deltaPacketsSent: {
$subtract: [
{
$arrayElemAt: [
@@ -461,7 +461,7 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
},
],
},
- avgPacketsRecv: {
+ deltaPacketsRecv: {
$subtract: [
{
$arrayElemAt: [
@@ -483,6 +483,120 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
},
],
},
+ deltaErrIn: {
+ $subtract: [
+ {
+ $arrayElemAt: [
+ {
+ $map: {
+ input: { $arrayElemAt: ["$net", { $subtract: [{ $size: "$net" }, 1] }] },
+ as: "iface",
+ in: "$$iface.err_in",
+ },
+ },
+ "$$netIndex",
+ ],
+ },
+ {
+ $arrayElemAt: [{ $map: { input: { $arrayElemAt: ["$net", 0] }, as: "iface", in: "$$iface.err_in" } }, "$$netIndex"],
+ },
+ ],
+ },
+ deltaErrOut: {
+ $subtract: [
+ {
+ $arrayElemAt: [
+ {
+ $map: {
+ input: { $arrayElemAt: ["$net", { $subtract: [{ $size: "$net" }, 1] }] },
+ as: "iface",
+ in: "$$iface.err_out",
+ },
+ },
+ "$$netIndex",
+ ],
+ },
+ {
+ $arrayElemAt: [{ $map: { input: { $arrayElemAt: ["$net", 0] }, as: "iface", in: "$$iface.err_out" } }, "$$netIndex"],
+ },
+ ],
+ },
+ deltaDropIn: {
+ $subtract: [
+ {
+ $arrayElemAt: [
+ {
+ $map: {
+ input: { $arrayElemAt: ["$net", { $subtract: [{ $size: "$net" }, 1] }] },
+ as: "iface",
+ in: "$$iface.drop_in",
+ },
+ },
+ "$$netIndex",
+ ],
+ },
+ {
+ $arrayElemAt: [{ $map: { input: { $arrayElemAt: ["$net", 0] }, as: "iface", in: "$$iface.drop_in" } }, "$$netIndex"],
+ },
+ ],
+ },
+ deltaDropOut: {
+ $subtract: [
+ {
+ $arrayElemAt: [
+ {
+ $map: {
+ input: { $arrayElemAt: ["$net", { $subtract: [{ $size: "$net" }, 1] }] },
+ as: "iface",
+ in: "$$iface.drop_out",
+ },
+ },
+ "$$netIndex",
+ ],
+ },
+ {
+ $arrayElemAt: [{ $map: { input: { $arrayElemAt: ["$net", 0] }, as: "iface", in: "$$iface.drop_out" } }, "$$netIndex"],
+ },
+ ],
+ },
+ deltaFifoIn: {
+ $subtract: [
+ {
+ $arrayElemAt: [
+ {
+ $map: {
+ input: { $arrayElemAt: ["$net", { $subtract: [{ $size: "$net" }, 1] }] },
+ as: "iface",
+ in: "$$iface.fifo_in",
+ },
+ },
+ "$$netIndex",
+ ],
+ },
+ {
+ $arrayElemAt: [{ $map: { input: { $arrayElemAt: ["$net", 0] }, as: "iface", in: "$$iface.fifo_in" } }, "$$netIndex"],
+ },
+ ],
+ },
+ deltaFifoOut: {
+ $subtract: [
+ {
+ $arrayElemAt: [
+ {
+ $map: {
+ input: { $arrayElemAt: ["$net", { $subtract: [{ $size: "$net" }, 1] }] },
+ as: "iface",
+ in: "$$iface.fifo_out",
+ },
+ },
+ "$$netIndex",
+ ],
+ },
+ {
+ $arrayElemAt: [{ $map: { input: { $arrayElemAt: ["$net", 0] }, as: "iface", in: "$$iface.fifo_out" } }, "$$netIndex"],
+ },
+ ],
+ },
},
},
},