@arcblock/pm2-prom-module 2.6.4 → 2.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/core/pm2.js CHANGED
@@ -142,11 +142,6 @@ const detectActiveApps = () => {
142
142
  workingApp.updateStatus(entry.status);
143
143
  }
144
144
  }
145
- // Collect statistic from apps. Do it after all APPS created
146
- if (activePM2Ids.size > 0) {
147
- // logger.debug(`Collect app metrics from PIDs ${Array.from(activePM2Ids)}`);
148
- sendCollectStaticticBusEvent(Array.from(activePM2Ids));
149
- }
150
145
  // Update metric with available apps
151
146
  metrics_1.metricAvailableApps === null || metrics_1.metricAvailableApps === void 0 ? void 0 : metrics_1.metricAvailableApps.set(Object.keys(APPS).length);
152
147
  // Get all pids to monit
@@ -205,7 +200,6 @@ const detectActiveApps = () => {
205
200
  });
206
201
  };
207
202
  const startPm2Connect = (conf) => {
208
- const logger = (0, logger_1.getLogger)();
209
203
  pm2_1.default.connect((err) => {
210
204
  var _a;
211
205
  if (err)
@@ -220,32 +214,6 @@ const startPm2Connect = (conf) => {
220
214
  (0, metrics_1.initDynamicGaugeMetricClients)(additionalMetrics);
221
215
  }
222
216
  detectActiveApps();
223
- // Collect statistic from running apps
224
- pm2_1.default.launchBus((err, bus) => {
225
- if (err)
226
- return console.error(err.stack || err);
227
- logger.debug('Start bus listener');
228
- bus.on('process:msg', (packet) => {
229
- if (packet.process &&
230
- packet.raw &&
231
- packet.raw.topic === 'pm2-prom-module:metrics' &&
232
- packet.raw.data) {
233
- const { name, pm_id } = packet.process;
234
- /*logger.debug(
235
- `Got message from app=${name} and pid=${pm_id}. Message=${JSON.stringify(
236
- packet.raw.data
237
- )}`
238
- );*/
239
- if (name && APPS[name] && packet.raw.data.metrics) {
240
- (0, app_2.processAppMetrics)(conf, {
241
- pmId: pm_id,
242
- appName: name,
243
- appResponse: packet.raw.data,
244
- });
245
- }
246
- }
247
- });
248
- });
249
217
  // Start timer to update available apps
250
218
  setInterval(() => {
251
219
  detectActiveApps();
@@ -296,17 +264,3 @@ function processWorkingApp(workingApp) {
296
264
  });
297
265
  });
298
266
  }
299
- function sendCollectStaticticBusEvent(pm2Ids) {
300
- // Request available metrics from all running apps
301
- pm2Ids.forEach((pm2id) => {
302
- pm2_1.default.sendDataToProcessId(pm2id, {
303
- topic: 'pm2-prom-module:collect',
304
- data: {},
305
- // Required fields by pm2 but we do not use them
306
- id: pm2id,
307
- }, (err) => {
308
- if (err)
309
- return console.error(`pm2-prom-module: sendDataToProcessId ${err.stack || err}`);
310
- });
311
- });
312
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcblock/pm2-prom-module",
3
- "version": "2.6.4",
3
+ "version": "2.6.5",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -8,13 +8,16 @@
8
8
  "main": "index.js",
9
9
  "dependencies": {
10
10
  "@abtnode/util": "^1.16.44",
11
+ "@types/dockerode": "^3.3.42",
12
+ "dockerode": "^4.0.7",
11
13
  "internal-ip": "^6.2.0",
14
+ "lodash": "^4.17.21",
12
15
  "pidusage": "^3.0.2",
13
- "pm2": "^5.3.0",
16
+ "pm2": "^6.0.8",
14
17
  "pmx": "beta",
15
18
  "prom-client": "^15.1.3",
16
19
  "xbytes": "^1.9.1",
17
- "zx": "^4.3.0"
20
+ "zx": "^8.8.0"
18
21
  },
19
22
  "scripts": {
20
23
  "predev": "export PM2_HOME=~/.arcblock/abtnode && pm2 delete @arcblock/pm2-prom-module && pm2 uninstall @arcblock/pm2-prom-module || true && npm run build",
@@ -46,6 +49,7 @@
46
49
  "prefix": "pm2"
47
50
  },
48
51
  "devDependencies": {
52
+ "@types/fs-extra": "^11.0.4",
49
53
  "@types/node": "^18.19.50",
50
54
  "@types/pidusage": "^2.0.5",
51
55
  "bumpp": "^10.1.0",
package/utils/docker.js CHANGED
@@ -12,16 +12,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.getDockerStats = exports.getCPULimit = exports.getFreeMemory = exports.getUsedMemory = exports.getBlockletServerInfo = exports.getAvailableMemory = exports.hasDockerLimitFiles = void 0;
15
+ exports.getCPULimit = exports.getFreeMemory = exports.getUsedMemory = exports.getBlockletServerInfo = exports.getAvailableMemory = exports.hasDockerLimitFiles = void 0;
16
+ exports.getDockerStats = getDockerStats;
16
17
  const promises_1 = require("node:fs/promises");
17
18
  const node_os_1 = __importDefault(require("node:os"));
18
19
  const cpu_1 = require("./cpu");
19
- const zx_1 = require("zx");
20
- const xbytes_1 = __importDefault(require("xbytes"));
20
+ const dockerode_1 = __importDefault(require("dockerode"));
21
21
  const get_ip_1 = __importDefault(require("@abtnode/util/lib/get-ip"));
22
22
  const internal_ip_1 = require("internal-ip");
23
- // 禁用命令和结果的自动输出
24
- zx_1.$.verbose = false;
23
+ const uniq_1 = __importDefault(require("lodash/uniq"));
24
+ // 初始化 Docker 客户端
25
+ const docker = new dockerode_1.default();
25
26
  //const MEMORY_AVAILABLE = '/sys/fs/cgroup/memory.limit_in_bytes';
26
27
  //const MEMORY_USED = '/sys/fs/cgroup/memory.usage_in_bytes';
27
28
  const MEMORY_AVAILABLE = '/sys/fs/cgroup/memory.max';
@@ -97,7 +98,7 @@ const getUsedMemory = () => __awaiter(void 0, void 0, void 0, function* () {
97
98
  return usedMemory;
98
99
  }
99
100
  }
100
- catch (_b) {
101
+ catch (_a) {
101
102
  return 0;
102
103
  }
103
104
  });
@@ -126,7 +127,7 @@ const getFreeMemory = () => __awaiter(void 0, void 0, void 0, function* () {
126
127
  return systemFreeMem;
127
128
  }
128
129
  }
129
- catch (_c) {
130
+ catch (_a) {
130
131
  return 0;
131
132
  }
132
133
  });
@@ -146,41 +147,74 @@ const getCPULimit = () => __awaiter(void 0, void 0, void 0, function* () {
146
147
  }
147
148
  }
148
149
  }
149
- catch (_d) { }
150
+ catch (_a) { }
150
151
  return count;
151
152
  });
152
153
  exports.getCPULimit = getCPULimit;
154
+ /**
155
+ * 获取单个容器的统计信息
156
+ */
157
+ function getContainerStats(containerId) {
158
+ return __awaiter(this, void 0, void 0, function* () {
159
+ var _a, _b, _c, _d, _e, _f, _g;
160
+ try {
161
+ const container = docker.getContainer(containerId);
162
+ const stats = yield container.stats({ stream: false });
163
+ // 内存使用信息
164
+ const memoryUsage = ((_a = stats.memory_stats) === null || _a === void 0 ? void 0 : _a.usage) || 0;
165
+ const memoryLimit = ((_b = stats.memory_stats) === null || _b === void 0 ? void 0 : _b.limit) || 0;
166
+ // CPU使用信息
167
+ const cpuStats = (_c = stats.cpu_stats) === null || _c === void 0 ? void 0 : _c.cpu_usage;
168
+ const preCpuStats = (_d = stats.precpu_stats) === null || _d === void 0 ? void 0 : _d.cpu_usage;
169
+ let cpuPercent = 0;
170
+ if (cpuStats && preCpuStats && ((_e = stats.cpu_stats) === null || _e === void 0 ? void 0 : _e.system_cpu_usage) && ((_f = stats.precpu_stats) === null || _f === void 0 ? void 0 : _f.system_cpu_usage)) {
171
+ // 计算容器CPU使用时间的增量(纳秒)
172
+ const cpuDelta = cpuStats.total_usage - preCpuStats.total_usage;
173
+ // 计算系统CPU时间的增量(纳秒)
174
+ const systemDelta = stats.cpu_stats.system_cpu_usage - stats.precpu_stats.system_cpu_usage;
175
+ if (systemDelta > 0 && cpuDelta >= 0) {
176
+ // 获取系统可用的CPU核心数
177
+ const onlineCpus = ((_g = stats.cpu_stats) === null || _g === void 0 ? void 0 : _g.online_cpus) || node_os_1.default.cpus().length;
178
+ // Docker CPU使用率计算公式:
179
+ // CPU% = (容器CPU增量 / 系统CPU增量) * CPU核心数 * 100%
180
+ //
181
+ // 解释:
182
+ // - cpuDelta: 容器在两次采样间隔内使用的CPU时间
183
+ // - systemDelta: 系统在两次采样间隔内所有CPU核心的总时间
184
+ // - 比值 (cpuDelta/systemDelta) 表示容器占用系统总CPU时间的比例
185
+ // - 乘以核心数是因为Docker的统计方式:单核100%使用时,在N核系统上显示为N*100%
186
+ cpuPercent = (cpuDelta / systemDelta) * onlineCpus * 100;
187
+ // 确保结果为非负数且不超过理论最大值
188
+ cpuPercent = Math.max(0, Math.min(cpuPercent, onlineCpus * 100));
189
+ }
190
+ }
191
+ return {
192
+ name: containerId,
193
+ cpuUsage: Math.round(cpuPercent * 100) / 100, // 保留2位小数
194
+ memoryUsage: memoryUsage,
195
+ totalMemory: memoryLimit,
196
+ };
197
+ }
198
+ catch (error) {
199
+ console.error(`Failed to get stats for container ${containerId}:`, error.message);
200
+ return null;
201
+ }
202
+ });
203
+ }
153
204
  function getDockerStats(ids) {
154
205
  return __awaiter(this, void 0, void 0, function* () {
155
206
  try {
156
207
  if (!ids.length) {
157
208
  return [];
158
209
  }
159
- const result = yield (0, zx_1.$) `docker stats --no-stream --format "{{json .}}" -a`;
160
- if (result.exitCode || !result.stdout) {
161
- return [];
162
- }
163
- const statsRows = result.stdout
164
- .split('\n')
165
- .filter(Boolean)
166
- .map((x) => JSON.parse(x));
167
- const stats = statsRows.map((x) => {
168
- const [memoryUsage, totalMemory] = x.MemUsage.split('/').map((x) => xbytes_1.default.parseSize(x.trim()));
169
- return {
170
- name: x.Name,
171
- cpuUsage: +x.CPUPerc.replace('%', ''),
172
- memoryUsage: memoryUsage,
173
- totalMemory: totalMemory,
174
- };
175
- });
176
- return ids.map((id) => {
177
- return stats.find((x) => x.name === id);
178
- });
210
+ // 并行获取所有容器的统计信息
211
+ const results = yield Promise.all((0, uniq_1.default)(ids).map(id => getContainerStats(id)));
212
+ // 过滤出成功的结果
213
+ return results.filter((stat) => stat !== null);
179
214
  }
180
215
  catch (error) {
181
- console.error(error);
216
+ console.error('Error getting Docker stats:', error);
182
217
  return [];
183
218
  }
184
219
  });
185
220
  }
186
- exports.getDockerStats = getDockerStats;
package/utils/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.toUndescore = void 0;
3
+ exports.toUndescore = toUndescore;
4
4
  function toUndescore(str) {
5
5
  return str.toLowerCase().replace(/\s+/g, '_');
6
6
  }
7
- exports.toUndescore = toUndescore;