mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-15 20:25:23 +02:00
- Added a new function `getHostSystemStats` to encapsulate the logic for retrieving host system statistics using `node-os-utils`. - Refactored Docker stats monitoring to utilize the new function, improving code clarity and maintainability. - Removed redundant OSUtils instantiation from the Docker stats monitoring logic.
225 lines
6.4 KiB
TypeScript
225 lines
6.4 KiB
TypeScript
import { promises } from "node:fs";
|
|
import { OSUtils } from "node-os-utils";
|
|
import { paths } from "../constants";
|
|
|
|
export interface Container {
|
|
BlockIO: string;
|
|
CPUPerc: string;
|
|
Container: string;
|
|
ID: string;
|
|
MemPerc: string;
|
|
MemUsage: string;
|
|
Name: string;
|
|
NetIO: string;
|
|
}
|
|
export const recordAdvancedStats = async (
|
|
stats: Container,
|
|
appName: string,
|
|
) => {
|
|
const { MONITORING_PATH } = paths();
|
|
const path = `${MONITORING_PATH}/${appName}`;
|
|
|
|
await promises.mkdir(path, { recursive: true });
|
|
|
|
await updateStatsFile(appName, "cpu", stats.CPUPerc);
|
|
await updateStatsFile(appName, "memory", {
|
|
used: stats.MemUsage.split(" ")[0],
|
|
total: stats.MemUsage.split(" ")[2],
|
|
});
|
|
|
|
await updateStatsFile(appName, "block", {
|
|
readMb: stats.BlockIO.split(" ")[0],
|
|
writeMb: stats.BlockIO.split(" ")[2],
|
|
});
|
|
|
|
await updateStatsFile(appName, "network", {
|
|
inputMb: stats.NetIO.split(" ")[0],
|
|
outputMb: stats.NetIO.split(" ")[2],
|
|
});
|
|
|
|
if (appName === "dokploy") {
|
|
const osutils = new OSUtils();
|
|
const diskResult = await osutils.disk.usageByMountPoint("/");
|
|
|
|
if (diskResult.success && diskResult.data) {
|
|
const disk = diskResult.data;
|
|
const diskUsage = disk.used.toGB().toFixed(2);
|
|
const diskTotal = disk.total.toGB().toFixed(2);
|
|
const diskUsedPercentage = disk.usagePercentage;
|
|
const diskFree = disk.available.toGB().toFixed(2);
|
|
|
|
await updateStatsFile(appName, "disk", {
|
|
diskTotal: +diskTotal,
|
|
diskUsedPercentage: +diskUsedPercentage,
|
|
diskUsage: +diskUsage,
|
|
diskFree: +diskFree,
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get host system statistics using node-os-utils
|
|
* This is used when monitoring "dokploy" to show host stats instead of container stats
|
|
*/
|
|
export const getHostSystemStats = async (): Promise<Container> => {
|
|
const osutils = new OSUtils({
|
|
disk: {
|
|
includeStats: true, // Enable disk I/O statistics
|
|
},
|
|
});
|
|
|
|
// Get CPU usage
|
|
const cpuResult = await osutils.cpu.usage();
|
|
const cpuUsage = cpuResult.success ? cpuResult.data : 0;
|
|
|
|
// Get memory info
|
|
const memResult = await osutils.memory.info();
|
|
let memUsedGB = 0;
|
|
let memTotalGB = 0;
|
|
let memUsedPercent = 0;
|
|
if (memResult.success) {
|
|
memTotalGB = memResult.data.total.toGB();
|
|
memUsedGB = memResult.data.used.toGB();
|
|
memUsedPercent = memResult.data.usagePercentage;
|
|
}
|
|
|
|
// Get network stats from network.overview()
|
|
let netInputBytes = 0;
|
|
let netOutputBytes = 0;
|
|
const networkOverview = await osutils.network.overview();
|
|
if (networkOverview.success) {
|
|
netInputBytes = networkOverview.data.totalRxBytes.toBytes();
|
|
netOutputBytes = networkOverview.data.totalTxBytes.toBytes();
|
|
}
|
|
|
|
// Get Block I/O from disk.stats()
|
|
let blockReadBytes = 0;
|
|
let blockWriteBytes = 0;
|
|
const diskStats = await osutils.disk.stats();
|
|
if (diskStats.success && diskStats.data.length > 0) {
|
|
// Filter out virtual devices (loop, ram, sr, etc.) - only include real disk devices
|
|
const excludePatterns = [/^loop/, /^ram/, /^sr\d+$/, /^fd\d+$/];
|
|
for (const stat of diskStats.data) {
|
|
// Skip virtual devices
|
|
if (
|
|
stat.device &&
|
|
excludePatterns.some((pattern) => pattern.test(stat.device))
|
|
) {
|
|
continue;
|
|
}
|
|
// readBytes and writeBytes are DataSize objects with .toBytes() method
|
|
blockReadBytes += stat.readBytes.toBytes();
|
|
blockWriteBytes += stat.writeBytes.toBytes();
|
|
}
|
|
}
|
|
|
|
// Format values similar to docker stats
|
|
const formatBytes = (bytes: number): string => {
|
|
if (bytes >= 1024 * 1024 * 1024) {
|
|
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)}GiB`;
|
|
}
|
|
if (bytes >= 1024 * 1024) {
|
|
return `${(bytes / (1024 * 1024)).toFixed(2)}MiB`;
|
|
}
|
|
if (bytes >= 1024) {
|
|
return `${(bytes / 1024).toFixed(2)}KiB`;
|
|
}
|
|
return `${bytes}B`;
|
|
};
|
|
|
|
// Format memory usage similar to docker stats format: "used / total"
|
|
const memUsedFormatted = `${memUsedGB.toFixed(2)}GiB`;
|
|
const memTotalFormatted = `${memTotalGB.toFixed(2)}GiB`;
|
|
const memUsageFormatted = `${memUsedFormatted} / ${memTotalFormatted}`;
|
|
|
|
// Format network I/O
|
|
const netInputMb = netInputBytes / (1024 * 1024);
|
|
const netOutputMb = netOutputBytes / (1024 * 1024);
|
|
const netIOFormatted = `${netInputMb.toFixed(2)}MB / ${netOutputMb.toFixed(2)}MB`;
|
|
|
|
// Format Block I/O
|
|
const blockIOFormatted = `${formatBytes(blockReadBytes)} / ${formatBytes(blockWriteBytes)}`;
|
|
|
|
// Create a stat object compatible with recordAdvancedStats
|
|
return {
|
|
CPUPerc: `${cpuUsage.toFixed(2)}%`,
|
|
MemPerc: `${memUsedPercent.toFixed(2)}%`,
|
|
MemUsage: memUsageFormatted,
|
|
BlockIO: blockIOFormatted,
|
|
NetIO: netIOFormatted,
|
|
Container: "dokploy",
|
|
ID: "host-system",
|
|
Name: "dokploy",
|
|
};
|
|
};
|
|
|
|
export const getAdvancedStats = async (appName: string) => {
|
|
return {
|
|
cpu: await readStatsFile(appName, "cpu"),
|
|
memory: await readStatsFile(appName, "memory"),
|
|
disk: await readStatsFile(appName, "disk"),
|
|
network: await readStatsFile(appName, "network"),
|
|
block: await readStatsFile(appName, "block"),
|
|
};
|
|
};
|
|
|
|
export const readStatsFile = async (
|
|
appName: string,
|
|
statType: "cpu" | "memory" | "disk" | "network" | "block",
|
|
) => {
|
|
try {
|
|
const { MONITORING_PATH } = paths();
|
|
const filePath = `${MONITORING_PATH}/${appName}/${statType}.json`;
|
|
const data = await promises.readFile(filePath, "utf-8");
|
|
return JSON.parse(data);
|
|
} catch {
|
|
return [];
|
|
}
|
|
};
|
|
|
|
export const updateStatsFile = async (
|
|
appName: string,
|
|
statType: "cpu" | "memory" | "disk" | "network" | "block",
|
|
value: number | string | unknown,
|
|
) => {
|
|
const { MONITORING_PATH } = paths();
|
|
const stats = await readStatsFile(appName, statType);
|
|
stats.push({ value, time: new Date() });
|
|
|
|
if (stats.length > 288) {
|
|
stats.shift();
|
|
}
|
|
|
|
const content = JSON.stringify(stats);
|
|
await promises.writeFile(
|
|
`${MONITORING_PATH}/${appName}/${statType}.json`,
|
|
content,
|
|
);
|
|
};
|
|
|
|
export const readLastValueStatsFile = async (
|
|
appName: string,
|
|
statType: "cpu" | "memory" | "disk" | "network" | "block",
|
|
) => {
|
|
try {
|
|
const { MONITORING_PATH } = paths();
|
|
const filePath = `${MONITORING_PATH}/${appName}/${statType}.json`;
|
|
const data = await promises.readFile(filePath, "utf-8");
|
|
const stats = JSON.parse(data);
|
|
return stats[stats.length - 1] || null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
export const getLastAdvancedStatsFile = async (appName: string) => {
|
|
return {
|
|
cpu: await readLastValueStatsFile(appName, "cpu"),
|
|
memory: await readLastValueStatsFile(appName, "memory"),
|
|
disk: await readLastValueStatsFile(appName, "disk"),
|
|
network: await readLastValueStatsFile(appName, "network"),
|
|
block: await readLastValueStatsFile(appName, "block"),
|
|
};
|
|
};
|