pm2-perfmonitor 2.4.2 → 2.5.1

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/lib/utils.js CHANGED
@@ -1,62 +1,62 @@
1
- const pidusage = require('pidusage')
2
-
3
- const parseParamToArray = (value, defaultVal = []) => {
4
- if (Array.isArray(value)) return value
5
-
6
- if (typeof value === 'string') {
7
- return value.split(',').map((v) => v.trim())
8
- }
9
-
10
- return defaultVal
11
- }
12
-
13
- const parseParamToNumber = (value) => {
14
- if (typeof value === 'number') return value
15
- if (!value) return 0
16
-
17
- if (typeof value === 'string') {
18
- return Number(value)
19
- }
20
-
21
- return 0
22
- }
23
-
24
- const parseBool = (value, defaultVal = false) => {
25
- if (typeof value === 'boolean') return value
26
-
27
- if (value === 'true') return true
28
- if (value === 'false') return false
29
-
30
- return defaultVal
31
- }
32
-
33
- /**
34
- * @param { number} duration - sleep duration (ms)
35
- */
36
- const sleepAsync = (duration = 0) => {
37
- return new Promise((resolve) => {
38
- setTimeout(resolve, duration)
39
- })
40
- }
41
-
42
- /**
43
- * 获取指定进程的CPU使用率
44
- * @param {string| number} pid
45
- * @returns { Promise<number> } CPU 使用率
46
- */
47
- const getSysCpuUsageByPid = async (pid) => {
48
- try {
49
- const stats = await pidusage(pid)
50
- return stats.cpu
51
- } catch (err) {
52
- console.error('Call pidusage error:', err)
53
- }
54
- }
55
-
56
- module.exports = {
57
- parseParamToArray,
58
- parseParamToNumber,
59
- parseBool,
60
- sleepAsync,
61
- getSysCpuUsageByPid,
62
- }
1
+ const pidusage = require('pidusage')
2
+
3
+ const parseParamToArray = (value, defaultVal = []) => {
4
+ if (Array.isArray(value)) return value
5
+
6
+ if (typeof value === 'string') {
7
+ return value.split(',').map((v) => v.trim())
8
+ }
9
+
10
+ return defaultVal
11
+ }
12
+
13
+ const parseParamToNumber = (value) => {
14
+ if (typeof value === 'number') return value
15
+ if (!value) return 0
16
+
17
+ if (typeof value === 'string') {
18
+ return Number(value)
19
+ }
20
+
21
+ return 0
22
+ }
23
+
24
+ const parseBool = (value, defaultVal = false) => {
25
+ if (typeof value === 'boolean') return value
26
+
27
+ if (value === 'true') return true
28
+ if (value === 'false') return false
29
+
30
+ return defaultVal
31
+ }
32
+
33
+ /**
34
+ * @param { number} duration - sleep duration (ms)
35
+ */
36
+ const sleepAsync = (duration = 0) => {
37
+ return new Promise((resolve) => {
38
+ setTimeout(resolve, duration)
39
+ })
40
+ }
41
+
42
+ /**
43
+ * 获取指定进程的CPU使用率
44
+ * @param {string| number} pid
45
+ * @returns { Promise<number> } CPU 使用率
46
+ */
47
+ const getSysCpuUsageByPid = async (pid) => {
48
+ try {
49
+ const stats = await pidusage(pid)
50
+ return stats.cpu
51
+ } catch (err) {
52
+ console.error('Call pidusage error:', err)
53
+ }
54
+ }
55
+
56
+ module.exports = {
57
+ parseParamToArray,
58
+ parseParamToNumber,
59
+ parseBool,
60
+ sleepAsync,
61
+ getSysCpuUsageByPid,
62
+ }
@@ -0,0 +1,65 @@
1
+ const { getExeca } = require('./execa-helper')
2
+
3
+ /**
4
+ * Linux/macOS 专属:判定进程是否为原生僵尸进程(仅靠系统状态,无CPU检测)
5
+ * @param {number} pid 进程PID(必须为正整数)
6
+ * @returns {Promise<{
7
+ * isZombie: boolean, // 是否为僵尸进程(state=Z/<defunct>)
8
+ * exists: boolean // 进程是否存在
9
+ * failed?: boolean // 是否获取进程状态失败
10
+ * }>}
11
+ */
12
+ const isZombieStateProcess = async (pid) => {
13
+ // 1. 基础参数校验
14
+ if (!Number.isInteger(pid) || pid <= 0) {
15
+ return { isZombie: false, exists: false }
16
+ }
17
+
18
+ try {
19
+ // 2. 执行ps命令(Linux/macOS通用,仅获取PID、状态、进程名)
20
+ const cmdArgs =
21
+ process.platform === 'linux'
22
+ ? ['-o', 'pid,state,comm', '-p', pid]
23
+ : ['-o', 'pid,state,command', '-p', pid] // macOS 调整进程名字段
24
+
25
+ const execa = await getExeca()
26
+
27
+ const { stdout } = await execa('ps', cmdArgs, {
28
+ timeout: 3000, // 3秒超时保护
29
+ reject: false, // 进程不存在时不抛异常,自行解析
30
+ })
31
+
32
+ // 3. 解析输出(格式:PID S COMM → 1234 Z node)
33
+ const lines = stdout.trim().split('\n')
34
+ if (lines.length < 2) {
35
+ return { isZombie: false, exists: false } // 进程不存在
36
+ }
37
+
38
+ const [pidStr, state, name] = lines[1].trim().split(/\s+/)
39
+ const processExists = Number(pidStr) === pid
40
+ // 4. 核心判定:state=Z 或 进程名包含<defunct>
41
+ const isZombie =
42
+ processExists && (state === 'Z' || name?.includes('<defunct>'))
43
+
44
+ return { isZombie, exists: processExists }
45
+ } catch (err) {
46
+ // 权限不足/命令执行失败 → 兜底返回
47
+ console.warn(`[${process.platform}] 检测进程 ${pid} 失败:`, err.message)
48
+ return { isZombie: false, exists: false, failed: true }
49
+ }
50
+ }
51
+
52
+ /**
53
+ * 通过 cpu 值判断是否为僵尸进程:最近连续 N 次 cpu 负载值全是 0%
54
+ * @param { object } opts
55
+ * @param { number[] } opts.cpus - 连续的 cpu 负载值
56
+ * @param { number } opts.maxHits - 最大命中次数
57
+ */
58
+ const isZombieCpuProcess = (opts) => {
59
+ const cpus = opts.cpus
60
+
61
+ return cpus.length >= opts.maxHits && cpus.every((v) => v === 0)
62
+ }
63
+
64
+ // 导出函数
65
+ module.exports = { isZombieStateProcess, isZombieCpuProcess }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pm2-perfmonitor",
3
- "version": "2.4.2",
3
+ "version": "2.5.1",
4
4
  "description": "A pm2 module for performance monitoring. Automatically detect zombie processes and restart it",
5
5
  "author": {
6
6
  "name": "elenh",
@@ -49,8 +49,17 @@
49
49
  "pmx": "latest"
50
50
  },
51
51
  "devDependencies": {
52
+ "@eslint/compat": "^2.0.3",
53
+ "@eslint/js": "^9.39.4",
52
54
  "changelogen": "^0.6.2",
53
55
  "cz-conventional-changelog": "^3.3.0",
54
- "minimist": "^1.2.8"
56
+ "eslint": "^9.39.4",
57
+ "eslint-config-prettier": "^10.1.8",
58
+ "eslint-plugin-import": "^2.32.0",
59
+ "eslint-plugin-n": "^17.24.0",
60
+ "eslint-plugin-prettier": "^5.5.5",
61
+ "globals": "^17.4.0",
62
+ "minimist": "^1.2.8",
63
+ "prettier": "^3.8.1"
55
64
  }
56
65
  }