@arcblock/pm2-prom-module 2.6.3 → 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/metrics/index.js CHANGED
@@ -48,11 +48,12 @@ const initMetrics = (prefix) => {
48
48
  collect() {
49
49
  return __awaiter(this, void 0, void 0, function* () {
50
50
  const info = yield (0, docker_1.getBlockletServerInfo)();
51
+ this.reset();
51
52
  this.set(info, 1);
52
53
  });
53
54
  },
54
55
  registers: [exports.registry],
55
- labelNames: ['name', 'version', 'mode', 'internalIP'],
56
+ labelNames: ['type', 'name', 'version', 'mode', 'internalIP'],
56
57
  });
57
58
  new prom_client_1.default.Gauge({
58
59
  name: `${prefix}_${METRIC_FREE_MEMORY}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcblock/pm2-prom-module",
3
- "version": "2.6.3",
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';
@@ -59,13 +60,15 @@ const getBlockletServerInfo = () => __awaiter(void 0, void 0, void 0, function*
59
60
  if (!internalIP) {
60
61
  throw new Error('Failed to get internal IP address');
61
62
  }
62
- const response = yield fetch(`https://${internalIP.replace(/\./g, '-')}.ip.abtnet.io/.well-known/did.json`);
63
+ const url = `https://${internalIP.replace(/\./g, '-')}.ip.abtnet.io/.well-known/did.json`;
64
+ const response = yield fetch(url);
63
65
  if (response.status !== 200) {
64
- throw new Error(`Failed to get blocklet server info, ip: ${internalIP}, status: ${response.status}, statusText: ${response.statusText}`);
66
+ throw new Error(`Failed to get blocklet server info, url: ${url}, status: ${response.status}, statusText: ${response.statusText}`);
65
67
  }
66
68
  const data = yield response.json();
67
69
  const metadata = data.services.find((service) => service.type === 'server').metadata;
68
70
  return {
71
+ type: 'server',
69
72
  name: metadata.name,
70
73
  version: metadata.version,
71
74
  mode: metadata.mode,
@@ -75,6 +78,7 @@ const getBlockletServerInfo = () => __awaiter(void 0, void 0, void 0, function*
75
78
  catch (error) {
76
79
  console.error(error);
77
80
  return {
81
+ type: 'server',
78
82
  name: 'unknown',
79
83
  version: 'unknown',
80
84
  mode: 'unknown',
@@ -94,7 +98,7 @@ const getUsedMemory = () => __awaiter(void 0, void 0, void 0, function* () {
94
98
  return usedMemory;
95
99
  }
96
100
  }
97
- catch (_b) {
101
+ catch (_a) {
98
102
  return 0;
99
103
  }
100
104
  });
@@ -123,7 +127,7 @@ const getFreeMemory = () => __awaiter(void 0, void 0, void 0, function* () {
123
127
  return systemFreeMem;
124
128
  }
125
129
  }
126
- catch (_c) {
130
+ catch (_a) {
127
131
  return 0;
128
132
  }
129
133
  });
@@ -143,41 +147,74 @@ const getCPULimit = () => __awaiter(void 0, void 0, void 0, function* () {
143
147
  }
144
148
  }
145
149
  }
146
- catch (_d) { }
150
+ catch (_a) { }
147
151
  return count;
148
152
  });
149
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
+ }
150
204
  function getDockerStats(ids) {
151
205
  return __awaiter(this, void 0, void 0, function* () {
152
206
  try {
153
207
  if (!ids.length) {
154
208
  return [];
155
209
  }
156
- const result = yield (0, zx_1.$) `docker stats --no-stream --format "{{json .}}" -a`;
157
- if (result.exitCode || !result.stdout) {
158
- return [];
159
- }
160
- const statsRows = result.stdout
161
- .split('\n')
162
- .filter(Boolean)
163
- .map((x) => JSON.parse(x));
164
- const stats = statsRows.map((x) => {
165
- const [memoryUsage, totalMemory] = x.MemUsage.split('/').map((x) => xbytes_1.default.parseSize(x.trim()));
166
- return {
167
- name: x.Name,
168
- cpuUsage: +x.CPUPerc.replace('%', ''),
169
- memoryUsage: memoryUsage,
170
- totalMemory: totalMemory,
171
- };
172
- });
173
- return ids.map((id) => {
174
- return stats.find((x) => x.name === id);
175
- });
210
+ // 并行获取所有容器的统计信息
211
+ const results = yield Promise.all((0, uniq_1.default)(ids).map(id => getContainerStats(id)));
212
+ // 过滤出成功的结果
213
+ return results.filter((stat) => stat !== null);
176
214
  }
177
215
  catch (error) {
178
- console.error(error);
216
+ console.error('Error getting Docker stats:', error);
179
217
  return [];
180
218
  }
181
219
  });
182
220
  }
183
- 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;