Skip to content

Commit 6369012

Browse files
authored
Merge pull request #3020 from Dokploy/1735-bug-in-server-monitoring
chore: update node-os-utils to version 2.0.1 and refactor lodash imports
2 parents b9324e6 + 69b7777 commit 6369012

File tree

8 files changed

+112
-39
lines changed

8 files changed

+112
-39
lines changed

apps/dokploy/components/dashboard/database/backups/restore-backup.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { zodResolver } from "@hookform/resolvers/zod";
22
import copy from "copy-to-clipboard";
3-
import { debounce } from "lodash";
3+
import _ from "lodash";
44
import {
55
CheckIcon,
66
ChevronsUpDown,
@@ -236,7 +236,7 @@ export const RestoreBackup = ({
236236
const currentDatabaseType = form.watch("databaseType");
237237
const metadata = form.watch("metadata");
238238

239-
const debouncedSetSearch = debounce((value: string) => {
239+
const debouncedSetSearch = _.debounce((value: string) => {
240240
setDebouncedSearchTerm(value);
241241
}, 350);
242242

apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { FancyAnsi } from "fancy-ansi";
2-
import { escapeRegExp } from "lodash";
2+
import _ from "lodash";
33
import { Badge } from "@/components/ui/badge";
44
import {
55
Tooltip,
@@ -47,7 +47,7 @@ export function TerminalLine({ log, noTimestamp, searchTerm }: LogLineProps) {
4747
}
4848

4949
const htmlContent = fancyAnsi.toHtml(text);
50-
const searchRegex = new RegExp(`(${escapeRegExp(term)})`, "gi");
50+
const searchRegex = new RegExp(`(${_.escapeRegExp(term)})`, "gi");
5151

5252
const modifiedContent = htmlContent.replace(
5353
searchRegex,

apps/dokploy/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@
120120
"next": "^15.3.2",
121121
"next-i18next": "^15.4.2",
122122
"next-themes": "^0.2.1",
123-
"node-os-utils": "1.3.7",
123+
"node-os-utils": "2.0.1",
124124
"node-pty": "1.0.0",
125125
"node-schedule": "2.1.1",
126126
"nodemailer": "6.9.14",
@@ -163,7 +163,6 @@
163163
"@types/lodash": "4.17.4",
164164
"@types/micromatch": "4.0.9",
165165
"@types/node": "^18.19.104",
166-
"@types/node-os-utils": "1.3.4",
167166
"@types/node-schedule": "2.1.6",
168167
"@types/nodemailer": "^6.4.17",
169168
"@types/qrcode": "^1.5.5",

apps/dokploy/server/wss/docker-stats.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import {
66
recordAdvancedStats,
77
validateRequest,
88
} from "@dokploy/server";
9+
import { OSUtils } from "node-os-utils";
910
import { WebSocketServer } from "ws";
11+
import { formatBytes } from "@/components/dashboard/database/backups/restore-backup";
1012

1113
export const setupDockerStatsMonitoringSocketServer = (
1214
server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>,
@@ -49,6 +51,83 @@ export const setupDockerStatsMonitoringSocketServer = (
4951
}
5052
const intervalId = setInterval(async () => {
5153
try {
54+
// Special case: when monitoring "dokploy", get host system stats instead of container stats
55+
if (appName === "dokploy") {
56+
const osutils = new OSUtils();
57+
58+
// Get CPU usage
59+
const cpuResult = await osutils.cpu.usage();
60+
const cpuUsage = cpuResult.success ? cpuResult.data : 0;
61+
62+
// Get memory info
63+
const memResult = await osutils.memory.info();
64+
let memUsedGB = 0;
65+
let memTotalGB = 0;
66+
let memUsedPercent = 0;
67+
if (memResult.success) {
68+
memTotalGB = memResult.data.total.toGB();
69+
memUsedGB = memResult.data.used.toGB();
70+
memUsedPercent = memResult.data.usagePercentage;
71+
}
72+
73+
// Get network stats from network.overview() or network.statsAsync()
74+
let netInputBytes = 0;
75+
let netOutputBytes = 0;
76+
const networkOverview = await osutils.network.overview();
77+
if (networkOverview.success) {
78+
netInputBytes = networkOverview.data.totalRxBytes.toBytes();
79+
netOutputBytes = networkOverview.data.totalTxBytes.toBytes();
80+
}
81+
82+
// Get Block I/O from disk.stats() (available in v2.0!)
83+
// If disk.stats() doesn't work in container, fallback to /proc/diskstats
84+
let blockReadBytes = 0;
85+
let blockWriteBytes = 0;
86+
const diskStats = await osutils.disk.stats();
87+
if (diskStats.success && diskStats.data.length > 0) {
88+
for (const stat of diskStats.data) {
89+
blockReadBytes += stat.readBytes.toBytes();
90+
blockWriteBytes += stat.writeBytes.toBytes();
91+
}
92+
}
93+
94+
// Format memory usage similar to docker stats format: "used / total"
95+
const memUsedFormatted = `${memUsedGB.toFixed(2)}GiB`;
96+
const memTotalFormatted = `${memTotalGB.toFixed(2)}GiB`;
97+
const memUsageFormatted = `${memUsedFormatted} / ${memTotalFormatted}`;
98+
99+
// Format network I/O
100+
const netInputMb = netInputBytes / (1024 * 1024);
101+
const netOutputMb = netOutputBytes / (1024 * 1024);
102+
const netIOFormatted = `${netInputMb.toFixed(2)}MB / ${netOutputMb.toFixed(2)}MB`;
103+
104+
// Format Block I/O
105+
const blockIOFormatted = `${formatBytes(blockReadBytes)} / ${formatBytes(blockWriteBytes)}`;
106+
107+
// Create a stat object compatible with recordAdvancedStats
108+
const stat = {
109+
CPUPerc: `${cpuUsage.toFixed(2)}%`,
110+
MemPerc: `${memUsedPercent.toFixed(2)}%`,
111+
MemUsage: memUsageFormatted,
112+
BlockIO: blockIOFormatted,
113+
NetIO: netIOFormatted,
114+
Container: "dokploy",
115+
ID: "host-system",
116+
Name: "dokploy",
117+
};
118+
119+
await recordAdvancedStats(stat, appName);
120+
const data = await getLastAdvancedStatsFile(appName);
121+
console.log(data);
122+
123+
ws.send(
124+
JSON.stringify({
125+
data,
126+
}),
127+
);
128+
return;
129+
}
130+
52131
const filter = {
53132
status: ["running"],
54133
...(appType === "application" && {

packages/server/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
"lodash": "4.17.21",
6262
"micromatch": "4.0.8",
6363
"nanoid": "3.3.11",
64-
"node-os-utils": "1.3.7",
64+
"node-os-utils": "2.0.1",
6565
"node-pty": "1.0.0",
6666
"node-schedule": "2.1.1",
6767
"nodemailer": "6.9.14",
@@ -88,7 +88,6 @@
8888
"@types/lodash": "4.17.4",
8989
"@types/micromatch": "4.0.9",
9090
"@types/node": "^18.19.104",
91-
"@types/node-os-utils": "1.3.4",
9291
"@types/node-schedule": "2.1.6",
9392
"@types/nodemailer": "^6.4.17",
9493
"@types/qrcode": "^1.5.5",

packages/server/src/monitoring/utils.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { promises } from "node:fs";
2-
import osUtils from "node-os-utils";
2+
import { OSUtils } from "node-os-utils";
33
import { paths } from "../constants";
4-
54
export interface Container {
65
BlockIO: string;
76
CPUPerc: string;
@@ -38,19 +37,23 @@ export const recordAdvancedStats = async (
3837
});
3938

4039
if (appName === "dokploy") {
41-
const disk = await osUtils.drive.info("/");
40+
const osutils = new OSUtils();
41+
const diskResult = await osutils.disk.usageByMountPoint("/");
4242

43-
const diskUsage = disk.usedGb;
44-
const diskTotal = disk.totalGb;
45-
const diskUsedPercentage = disk.usedPercentage;
46-
const diskFree = disk.freeGb;
43+
if (diskResult.success && diskResult.data) {
44+
const disk = diskResult.data;
45+
const diskUsage = disk.used.toGB().toFixed(2);
46+
const diskTotal = disk.total.toGB().toFixed(2);
47+
const diskUsedPercentage = disk.usagePercentage;
48+
const diskFree = disk.available.toGB().toFixed(2);
4749

48-
await updateStatsFile(appName, "disk", {
49-
diskTotal: +diskTotal,
50-
diskUsedPercentage: +diskUsedPercentage,
51-
diskUsage: +diskUsage,
52-
diskFree: +diskFree,
53-
});
50+
await updateStatsFile(appName, "disk", {
51+
diskTotal: +diskTotal,
52+
diskUsedPercentage: +diskUsedPercentage,
53+
diskUsage: +diskUsage,
54+
diskFree: +diskFree,
55+
});
56+
}
5457
}
5558
};
5659

packages/server/src/services/application.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,9 @@ export const deployPreviewApplication = async ({
419419
};
420420

421421
export const getApplicationStats = async (appName: string) => {
422+
if (appName === "dokploy") {
423+
return await getAdvancedStats(appName);
424+
}
422425
const filter = {
423426
status: ["running"],
424427
label: [`com.docker.swarm.service.name=${appName}`],

pnpm-lock.yaml

Lines changed: 8 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)