Fixed the processing to be done in backend now.

This commit is contained in:
Owaise
2025-08-09 12:14:25 +05:30
parent 765a1a24ed
commit f5d0b74ccf
5 changed files with 144 additions and 80 deletions

View File

@@ -91,6 +91,49 @@ const getFormattedPercentage = (value) => {
return `${(value * 100).toFixed(2)}%`;
};
/**
* Custom tick component for rendering network bytes per second.
*
* @param {Object} props - The properties object.
* @param {number} props.x - The x-coordinate for the tick.
* @param {number} props.y - The y-coordinate for the tick.
* @param {Object} props.payload - The payload object containing tick data.
* @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 }) => {
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`;
};
return (
<Text
x={x - 20}
y={y}
textAnchor="middle"
fill={theme.palette.primary.contrastTextTertiary}
fontSize={11}
fontWeight={400}
>
{formatBytes(payload?.value)}
</Text>
);
};
NetworkTick.propTypes = {
x: PropTypes.number,
y: PropTypes.number,
payload: PropTypes.object,
index: PropTypes.number,
};
/**
* Custom tooltip component for displaying infrastructure data.
*

View File

@@ -7,6 +7,7 @@ import InfraAreaChart from "../../../../../Pages/Infrastructure/Details/Componen
import {
TzTick,
InfrastructureTooltip,
NetworkTick,
} from "../../../../../Components/Charts/Utils/chartUtils";
import { useTheme } from "@emotion/react";
import { useTranslation } from "react-i18next";
@@ -23,6 +24,7 @@ const NetworkCharts = ({ eth0Data, dateRange }) => {
const theme = useTheme();
const { t } = useTranslation();
const configs = [
{
type: "network-bytes",
@@ -33,6 +35,7 @@ const NetworkCharts = ({ eth0Data, dateRange }) => {
gradientStartColor: theme.palette.info.main,
yLabel: t("bytesPerSecond"),
xTick: <TzTick dateRange={dateRange} />,
yTick: <NetworkTick />,
toolTip: (
<InfrastructureTooltip
dotColor={theme.palette.info.main}

View File

@@ -4,23 +4,32 @@ import NetworkCharts from "./NetworkCharts";
import MonitorTimeFrameHeader from "../../../../../Components/MonitorTimeFrameHeader";
const Network = ({ net, checks, isLoading, dateRange, setDateRange }) => {
const eth0Data = getEth0TimeSeries(checks);
const eth0Data = (checks || [])
.map((check) => {
const en0 = (check.net || []).find((iface) => iface.name === "en0");
if (!en0) return null;
return {
_id: check._id,
bytesPerSec: en0.avgBytesRecv,
packetsPerSec: en0.avgPacketsRecv,
errors: (en0.avgErrOut ?? 0),
drops: (en0.avgDropOut ?? 0)
};
})
.filter(Boolean);
console.log(eth0Data);
return (
<>
<NetworkStatBoxes
shouldRender={!isLoading}
net={net}
/>
<NetworkStatBoxes shouldRender={!isLoading} net={net} />
<MonitorTimeFrameHeader
isLoading={isLoading}
dateRange={dateRange}
setDateRange={setDateRange}
/>
<NetworkCharts
eth0Data={eth0Data}
dateRange={dateRange}
/>
<NetworkCharts eth0Data={eth0Data} dateRange={dateRange} />
</>
);
};
@@ -34,65 +43,3 @@ Network.propTypes = {
};
export default Network;
/* ---------- Helper functions ---------- */
function getEth0TimeSeries(checks) {
const sorted = [...(checks || [])].sort((a, b) => new Date(a._id) - new Date(b._id));
const series = [];
let prev = null;
for (const check of sorted) {
const eth = (check.net || []).find((iface) => iface.name === "en0");
if (!eth) {
prev = check;
continue;
}
if (prev) {
const prevEth = (prev.net || []).find((iface) => iface.name === "en0");
const t1 = new Date(check._id);
const t0 = new Date(prev._id);
if (!prevEth || isNaN(t1) || isNaN(t0)) {
prev = check;
continue;
}
const dt = (t1 - t0) / 1000;
if (dt > 0) {
const bytesField = eth.avgBytesSent;
const prevBytesField = prevEth.avgBytesSent;
if (bytesField !== undefined && prevBytesField !== undefined) {
const dataPoint = {
_id: check._id,
bytesPerSec: (bytesField - prevBytesField) / dt,
packetsPerSec: (eth.avgPacketsSent - prevEth.avgPacketsSent) / dt,
errors: (eth.avgErrIn ?? 0) + (eth.avgErrOut ?? 0),
drops: 0,
};
series.push(dataPoint);
}
}
}
prev = check;
}
// If we only have one check, create a single data point with absolute values
if (series.length === 0 && sorted.length === 1) {
const check = sorted[0];
const eth = (check.net || []).find((iface) => iface.name === "en0");
if (eth) {
series.push({
_id: check._id,
bytesPerSec: eth.avgBytesSent || 0,
packetsPerSec: eth.avgPacketsSent || 0,
errors: (eth.avgErrIn ?? 0) + (eth.avgErrOut ?? 0),
drops: 0,
});
}
}
return series;
}

View File

@@ -326,10 +326,53 @@ class MonitorModule {
}
};
processNetworkRates = (checks) => {
if (!Array.isArray(checks) || checks.length === 0) return [];
const sorted = [...checks].sort((a, b) => new Date(a._id) - new Date(b._id));
const lastSeen = {};
for (const check of sorted) {
if (!Array.isArray(check.net)) {
check.net = [];
continue;
}
const newNet = [];
for (const iface of check.net) {
const prev = lastSeen[iface.name];
const t1 = new Date(check._id);
if (prev) {
const t0 = new Date(prev._id);
const dt = (t1 - t0) / 1000;
if (dt > 0) {
newNet.push({
name: iface.name,
avgBytesRecv: (iface.avgBytesRecv - prev.avgBytesRecv),
avgPacketsRecv: (iface.avgPacketsRecv - prev.avgPacketsRecv),
avgErrOut: iface.avgErrOut - prev.avgErrOut,
avgDropOut: iface.avgDropOut,
});
}
}
lastSeen[iface.name] = { ...iface, _id: check._id };
}
check.net = newNet;
}
return sorted;
};
getHardwareDetailsById = async ({ monitorId, dateRange }) => {
try {
const monitor = await this.Monitor.findById(monitorId);
const dates = this.getDateRange(dateRange);
const formatLookup = {
recent: "%Y-%m-%dT%H:%M:00Z",
day: "%Y-%m-%dT%H:00:00Z",
@@ -337,13 +380,19 @@ class MonitorModule {
month: "%Y-%m-%dT00:00:00Z",
};
const dateString = formatLookup[dateRange];
const hardwareStats = await this.HardwareCheck.aggregate(buildHardwareDetailsPipeline(monitor, dates, dateString));
const monitorStats = {
const stats = hardwareStats[0];
if (stats && stats.checks) {
// Replace net with per-second rates
stats.checks = this.processNetworkRates(stats.checks);
}
return {
...monitor.toObject(),
stats: hardwareStats[0],
stats,
};
return monitorStats;
} catch (error) {
error.service = SERVICE_NAME;
error.method = "getHardwareDetailsById";

View File

@@ -393,7 +393,7 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
0,
],
},
bytesSent: {
avgBytesSent: {
$avg: {
$map: {
input: "$net",
@@ -404,7 +404,7 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
},
},
},
bytesRecv: {
avgBytesRecv: {
$avg: {
$map: {
input: "$net",
@@ -415,7 +415,7 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
},
},
},
packetsSent: {
avgPacketsSent: {
$avg: {
$map: {
input: "$net",
@@ -426,7 +426,7 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
},
},
},
packetsRecv: {
avgPacketsRecv: {
$avg: {
$map: {
input: "$net",
@@ -437,7 +437,7 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
},
},
},
errIn: {
avgErrIn: {
$avg: {
$map: {
input: "$net",
@@ -448,7 +448,7 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
},
},
},
errOut: {
avgErrOut: {
$avg: {
$map: {
input: "$net",
@@ -459,6 +459,28 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => {
},
},
},
avgDropIn: {
$avg: {
$map: {
input: "$net",
as: "netArray",
in: {
$arrayElemAt: ["$$netArray.drop_in", "$$netIndex"],
},
},
},
},
avgDropOut: {
$avg: {
$map: {
input: "$net",
as: "netArray",
in: {
$arrayElemAt: ["$$netArray.drop_out", "$$netIndex"],
},
},
},
},
},
},
},