Skip to content

Commit 8016708

Browse files
authored
Merge pull request #3021 from Dokploy/1735-bug-in-server-monitoring
1735 bug in server monitoring
2 parents 6369012 + 09a98a2 commit 8016708

File tree

2 files changed

+99
-65
lines changed

2 files changed

+99
-65
lines changed

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

Lines changed: 2 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ import type http from "node:http";
22
import {
33
docker,
44
execAsync,
5+
getHostSystemStats,
56
getLastAdvancedStatsFile,
67
recordAdvancedStats,
78
validateRequest,
89
} from "@dokploy/server";
9-
import { OSUtils } from "node-os-utils";
1010
import { WebSocketServer } from "ws";
11-
import { formatBytes } from "@/components/dashboard/database/backups/restore-backup";
1211

1312
export const setupDockerStatsMonitoringSocketServer = (
1413
server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>,
@@ -53,72 +52,10 @@ export const setupDockerStatsMonitoringSocketServer = (
5352
try {
5453
// Special case: when monitoring "dokploy", get host system stats instead of container stats
5554
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-
};
55+
const stat = await getHostSystemStats();
11856

11957
await recordAdvancedStats(stat, appName);
12058
const data = await getLastAdvancedStatsFile(appName);
121-
console.log(data);
12259

12360
ws.send(
12461
JSON.stringify({

packages/server/src/monitoring/utils.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { promises } from "node:fs";
22
import { OSUtils } from "node-os-utils";
33
import { paths } from "../constants";
4+
45
export interface Container {
56
BlockIO: string;
67
CPUPerc: string;
@@ -57,6 +58,102 @@ export const recordAdvancedStats = async (
5758
}
5859
};
5960

61+
/**
62+
* Get host system statistics using node-os-utils
63+
* This is used when monitoring "dokploy" to show host stats instead of container stats
64+
*/
65+
export const getHostSystemStats = async (): Promise<Container> => {
66+
const osutils = new OSUtils({
67+
disk: {
68+
includeStats: true, // Enable disk I/O statistics
69+
},
70+
});
71+
72+
// Get CPU usage
73+
const cpuResult = await osutils.cpu.usage();
74+
const cpuUsage = cpuResult.success ? cpuResult.data : 0;
75+
76+
// Get memory info
77+
const memResult = await osutils.memory.info();
78+
let memUsedGB = 0;
79+
let memTotalGB = 0;
80+
let memUsedPercent = 0;
81+
if (memResult.success) {
82+
memTotalGB = memResult.data.total.toGB();
83+
memUsedGB = memResult.data.used.toGB();
84+
memUsedPercent = memResult.data.usagePercentage;
85+
}
86+
87+
// Get network stats from network.overview()
88+
let netInputBytes = 0;
89+
let netOutputBytes = 0;
90+
const networkOverview = await osutils.network.overview();
91+
if (networkOverview.success) {
92+
netInputBytes = networkOverview.data.totalRxBytes.toBytes();
93+
netOutputBytes = networkOverview.data.totalTxBytes.toBytes();
94+
}
95+
96+
// Get Block I/O from disk.stats()
97+
let blockReadBytes = 0;
98+
let blockWriteBytes = 0;
99+
const diskStats = await osutils.disk.stats();
100+
if (diskStats.success && diskStats.data.length > 0) {
101+
// Filter out virtual devices (loop, ram, sr, etc.) - only include real disk devices
102+
const excludePatterns = [/^loop/, /^ram/, /^sr\d+$/, /^fd\d+$/];
103+
for (const stat of diskStats.data) {
104+
// Skip virtual devices
105+
if (
106+
stat.device &&
107+
excludePatterns.some((pattern) => pattern.test(stat.device))
108+
) {
109+
continue;
110+
}
111+
// readBytes and writeBytes are DataSize objects with .toBytes() method
112+
blockReadBytes += stat.readBytes.toBytes();
113+
blockWriteBytes += stat.writeBytes.toBytes();
114+
}
115+
}
116+
117+
// Format values similar to docker stats
118+
const formatBytes = (bytes: number): string => {
119+
if (bytes >= 1024 * 1024 * 1024) {
120+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)}GiB`;
121+
}
122+
if (bytes >= 1024 * 1024) {
123+
return `${(bytes / (1024 * 1024)).toFixed(2)}MiB`;
124+
}
125+
if (bytes >= 1024) {
126+
return `${(bytes / 1024).toFixed(2)}KiB`;
127+
}
128+
return `${bytes}B`;
129+
};
130+
131+
// Format memory usage similar to docker stats format: "used / total"
132+
const memUsedFormatted = `${memUsedGB.toFixed(2)}GiB`;
133+
const memTotalFormatted = `${memTotalGB.toFixed(2)}GiB`;
134+
const memUsageFormatted = `${memUsedFormatted} / ${memTotalFormatted}`;
135+
136+
// Format network I/O
137+
const netInputMb = netInputBytes / (1024 * 1024);
138+
const netOutputMb = netOutputBytes / (1024 * 1024);
139+
const netIOFormatted = `${netInputMb.toFixed(2)}MB / ${netOutputMb.toFixed(2)}MB`;
140+
141+
// Format Block I/O
142+
const blockIOFormatted = `${formatBytes(blockReadBytes)} / ${formatBytes(blockWriteBytes)}`;
143+
144+
// Create a stat object compatible with recordAdvancedStats
145+
return {
146+
CPUPerc: `${cpuUsage.toFixed(2)}%`,
147+
MemPerc: `${memUsedPercent.toFixed(2)}%`,
148+
MemUsage: memUsageFormatted,
149+
BlockIO: blockIOFormatted,
150+
NetIO: netIOFormatted,
151+
Container: "dokploy",
152+
ID: "host-system",
153+
Name: "dokploy",
154+
};
155+
};
156+
60157
export const getAdvancedStats = async (appName: string) => {
61158
return {
62159
cpu: await readStatsFile(appName, "cpu"),

0 commit comments

Comments
 (0)