mirror of
https://github.com/andwati/dokploy.git
synced 2026-11-01 09:42:16 +00:00
fix: parse Block I/O and Network I/O stats to numeric MB so monitoring charts render
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import { formatMb, toMb } from "@dokploy/server/monitoring/units";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts";
|
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts";
|
||||||
import {
|
import {
|
||||||
@@ -29,8 +30,8 @@ export const DockerBlockChart = ({ accumulativeData }: Props) => {
|
|||||||
const transformedData = accumulativeData.map((item, index) => ({
|
const transformedData = accumulativeData.map((item, index) => ({
|
||||||
time: item.time,
|
time: item.time,
|
||||||
name: `Point ${index + 1}`,
|
name: `Point ${index + 1}`,
|
||||||
readMb: item.value.readMb,
|
readMb: toMb(item.value.readMb),
|
||||||
writeMb: item.value.writeMb,
|
writeMb: toMb(item.value.writeMb),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -77,7 +78,7 @@ export const DockerBlockChart = ({ accumulativeData }: Props) => {
|
|||||||
}}
|
}}
|
||||||
formatter={(value, name) => {
|
formatter={(value, name) => {
|
||||||
const label = name === "readMb" ? "Read" : "Write";
|
const label = name === "readMb" ? "Read" : "Write";
|
||||||
return [`${value} MB`, label];
|
return [formatMb(Number(value)), label];
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -1,3 +1,4 @@
|
|||||||
|
import { formatMb, toMb } from "@dokploy/server/monitoring/units";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts";
|
import { Area, AreaChart, CartesianGrid, YAxis } from "recharts";
|
||||||
import {
|
import {
|
||||||
@@ -29,8 +30,8 @@ export const DockerNetworkChart = ({ accumulativeData }: Props) => {
|
|||||||
const transformedData = accumulativeData.map((item, index) => ({
|
const transformedData = accumulativeData.map((item, index) => ({
|
||||||
time: item.time,
|
time: item.time,
|
||||||
name: `Point ${index + 1}`,
|
name: `Point ${index + 1}`,
|
||||||
inMB: item.value.inputMb,
|
inMB: toMb(item.value.inputMb),
|
||||||
outMB: item.value.outputMb,
|
outMB: toMb(item.value.outputMb),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -73,7 +74,7 @@ export const DockerNetworkChart = ({ accumulativeData }: Props) => {
|
|||||||
}}
|
}}
|
||||||
formatter={(value, name) => {
|
formatter={(value, name) => {
|
||||||
const label = name === "inMB" ? "In" : "Out";
|
const label = name === "inMB" ? "In" : "Out";
|
||||||
return [`${value} MB`, label];
|
return [formatMb(Number(value)), label];
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -1,3 +1,4 @@
|
|||||||
|
import { formatMb } from "@dokploy/server/monitoring/units";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Progress } from "@/components/ui/progress";
|
import { Progress } from "@/components/ui/progress";
|
||||||
@@ -305,7 +306,7 @@ export const ContainerFreeMonitoring = ({
|
|||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="flex flex-col gap-2 w-full">
|
<div className="flex flex-col gap-2 w-full">
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="text-sm text-muted-foreground">
|
||||||
{`Read: ${currentData.block.value.readMb} / Write: ${currentData.block.value.writeMb} `}
|
{`Read: ${formatMb(currentData.block.value.readMb)} / Write: ${formatMb(currentData.block.value.writeMb)}`}
|
||||||
</span>
|
</span>
|
||||||
<DockerBlockChart accumulativeData={accumulativeData.block} />
|
<DockerBlockChart accumulativeData={accumulativeData.block} />
|
||||||
</div>
|
</div>
|
||||||
@@ -318,7 +319,7 @@ export const ContainerFreeMonitoring = ({
|
|||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="flex flex-col gap-2 w-full">
|
<div className="flex flex-col gap-2 w-full">
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="text-sm text-muted-foreground">
|
||||||
{`In MB: ${currentData.network.value.inputMb} / Out MB: ${currentData.network.value.outputMb} `}
|
{`In: ${formatMb(currentData.network.value.inputMb)} / Out: ${formatMb(currentData.network.value.outputMb)}`}
|
||||||
</span>
|
</span>
|
||||||
<DockerNetworkChart accumulativeData={accumulativeData.network} />
|
<DockerNetworkChart accumulativeData={accumulativeData.network} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
const IO_UNIT_FACTORS: Record<string, number> = {
|
||||||
|
B: 1 / 1e6,
|
||||||
|
kB: 1e-3,
|
||||||
|
KB: 1e-3,
|
||||||
|
MB: 1,
|
||||||
|
GB: 1e3,
|
||||||
|
TB: 1e6,
|
||||||
|
KiB: 1024 / 1e6,
|
||||||
|
MiB: 1024 ** 2 / 1e6,
|
||||||
|
GiB: 1024 ** 3 / 1e6,
|
||||||
|
TiB: 1024 ** 4 / 1e6,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const toMb = (value: number | string | undefined): number => {
|
||||||
|
if (typeof value === "number") {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (!value) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const parsed = Number.parseFloat(value);
|
||||||
|
if (Number.isNaN(parsed)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const unit = value.replace(/[0-9.\s]/g, "");
|
||||||
|
return parsed * (IO_UNIT_FACTORS[unit] ?? 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const parseIoToMb = (raw: string | undefined): number =>
|
||||||
|
Number(toMb(raw).toFixed(6));
|
||||||
|
|
||||||
|
export const formatMb = (value: number | string | undefined): string => {
|
||||||
|
const mb = toMb(value);
|
||||||
|
if (mb >= 1000) {
|
||||||
|
return `${(mb / 1000).toFixed(2)} GB`;
|
||||||
|
}
|
||||||
|
if (mb < 0.01 && mb > 0) {
|
||||||
|
return `${(mb * 1000).toFixed(2)} kB`;
|
||||||
|
}
|
||||||
|
return `${mb.toFixed(2)} MB`;
|
||||||
|
};
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { promises } from "node:fs";
|
import { promises } from "node:fs";
|
||||||
import { OSUtils } from "node-os-utils";
|
import { OSUtils } from "node-os-utils";
|
||||||
import { paths } from "../constants";
|
import { paths } from "../constants";
|
||||||
|
import { parseIoToMb } from "./units";
|
||||||
|
|
||||||
export interface Container {
|
export interface Container {
|
||||||
BlockIO: string;
|
BlockIO: string;
|
||||||
@@ -28,13 +29,13 @@ export const recordAdvancedStats = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
await updateStatsFile(appName, "block", {
|
await updateStatsFile(appName, "block", {
|
||||||
readMb: stats.BlockIO.split(" ")[0],
|
readMb: parseIoToMb(stats.BlockIO.split(" ")[0]),
|
||||||
writeMb: stats.BlockIO.split(" ")[2],
|
writeMb: parseIoToMb(stats.BlockIO.split(" ")[2]),
|
||||||
});
|
});
|
||||||
|
|
||||||
await updateStatsFile(appName, "network", {
|
await updateStatsFile(appName, "network", {
|
||||||
inputMb: stats.NetIO.split(" ")[0],
|
inputMb: parseIoToMb(stats.NetIO.split(" ")[0]),
|
||||||
outputMb: stats.NetIO.split(" ")[2],
|
outputMb: parseIoToMb(stats.NetIO.split(" ")[2]),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (appName === "dokploy") {
|
if (appName === "dokploy") {
|
||||||
|
|||||||
Reference in New Issue
Block a user