pm2-perfmonitor 2.2.0 → 2.3.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/alert.js ADDED
@@ -0,0 +1,29 @@
1
+ const { getExeca } = require('./execa-helper')
2
+
3
+ /**
4
+ * 发送警告
5
+ * @param { object } options
6
+ * @param { string } options.cmd - bash 脚本 path(默认值:/var/job/alert.sh)
7
+ * @param { string } options.env - 环境(默认值: prod)
8
+ * @param { string } options.level - 报警级别(默认值: Sev-2)
9
+ * @param { string } options.title - 报警标题
10
+ * @param { string } options.content - 报警正文
11
+ */
12
+ const sendAlert = async (options) => {
13
+ try {
14
+ const execa = await getExeca()
15
+
16
+ const args = [options.env, options.level, options.title, options.content]
17
+
18
+ await execa(options.cmd, args)
19
+
20
+ return true
21
+ } catch (err) {
22
+ console.error('[Send Alert Error]:', err)
23
+ return false
24
+ }
25
+ }
26
+
27
+ module.exports = {
28
+ sendAlert,
29
+ }
package/lib/app.js CHANGED
@@ -11,6 +11,7 @@ const {
11
11
  const { defaultOptions } = require('./defaults')
12
12
  const { sendMessage } = require('./message')
13
13
  const { performPerfSampling } = require('./perf-sampler')
14
+ const { sendAlert } = require('./alert')
14
15
 
15
16
  const conf = pmx.initModule({}, (err, incomingConf) => {
16
17
  if (err) {
@@ -51,6 +52,10 @@ const enableNodeInspectorCollection = parseBool(
51
52
  const nodeInspectorSampleDuration = parseParamToNumber(
52
53
  conf.nodeInspectorSampleDuration,
53
54
  )
55
+ const enableAlert = parseBool(conf.enableAlert)
56
+ const alertCmdPath = conf.alertCmdPath
57
+ const alertEnv = conf.alertEnv
58
+ const alertLevel = conf.alertLevel
54
59
 
55
60
  // 存储每个进程的 CPU 采样历史(pm_id -> [cpu1, cpu2, ...])
56
61
  const zombieCpuHistory = new Map()
@@ -61,6 +66,8 @@ const cpuOverloadHistory = new Map()
61
66
  const cpuOverloadRestartHistory = new Map()
62
67
  const cpuOverloadRestartFailedHistory = new Map()
63
68
 
69
+ let isProcessCheckerRunning = false
70
+
64
71
  /**
65
72
  * perf 样本是否采集中
66
73
  * @type { Map<number,boolean> }
@@ -159,7 +166,22 @@ const setCpuOverloadRestartFailedHistory = (pm_id) => {
159
166
  }
160
167
  }
161
168
 
162
- let isProcessCheckerRunning = false
169
+ /**
170
+ * 发送重启警告
171
+ * @param {string} title
172
+ * @param {string} content
173
+ */
174
+ const sendRestartAlert = async (title, content) => {
175
+ if (!enableAlert) return
176
+
177
+ return await sendAlert({
178
+ cmd: alertCmdPath,
179
+ env: alertEnv,
180
+ level: alertLevel,
181
+ title: `[${MODULE_NAME}] Alert: ${title}`,
182
+ content,
183
+ })
184
+ }
163
185
 
164
186
  /**
165
187
  * check process
@@ -176,9 +198,11 @@ const processChecker = async () => {
176
198
  const { name, pid, pm_id, monit, pm2_env } = app
177
199
 
178
200
  const sysCpuUsage = await getSysCpuUsageByPid(pid)
201
+ const pm2CpuUsage = monit?.cpu
179
202
 
180
203
  const appStatus = pm2_env?.status
181
- const appCpuUsage = sysCpuUsage
204
+ const appCpuUsage =
205
+ typeof sysCpuUsage === 'number' ? sysCpuUsage : pm2CpuUsage
182
206
 
183
207
  // 非目标应用,跳过
184
208
  if (
@@ -243,6 +267,14 @@ const processChecker = async () => {
243
267
  `[ZOMBIE] Restarted ${name} (pm_id: ${pm_id}) successfully!!! Restarted ${zombieRestartHistory.get(pm_id)} times`,
244
268
  )
245
269
 
270
+ await sendRestartAlert(
271
+ `The zombie process has been restarted!`,
272
+ `appName: ${name}, \n
273
+ pid: ${pid}, \n
274
+ pm_id: ${pm_id}, \n
275
+ restarted: ${zombieRestartHistory.get(pm_id)} times`,
276
+ )
277
+
246
278
  // 重启后清除该进程的历史记录,避免刚重启又被判定为僵尸
247
279
  zombieCpuHistory.delete(pm_id)
248
280
  } catch (restartErr) {
@@ -297,6 +329,14 @@ const processChecker = async () => {
297
329
  `[CPU OVERLOAD] Restarted ${name} (pm_id: ${pm_id}) successfully!!! Restarted ${cpuOverloadRestartHistory.get(pm_id)} times`,
298
330
  )
299
331
 
332
+ await sendRestartAlert(
333
+ `CPU overload process restarted!`,
334
+ `appName: ${name}, \n
335
+ pid: ${pid}, \n
336
+ pm_id: ${pm_id}, \n
337
+ restarted: ${cpuOverloadRestartHistory.get(pm_id)} times`,
338
+ )
339
+
300
340
  cpuOverloadHistory.delete(pm_id)
301
341
  } catch (restartErr) {
302
342
  logger(
package/lib/defaults.js CHANGED
@@ -78,6 +78,23 @@ const defaultOptions = {
78
78
  * node:inspector 性能采集持续时间 (s)
79
79
  */
80
80
  nodeInspectorSampleDuration: 10,
81
+
82
+ /**
83
+ * 指定是否开启报警
84
+ */
85
+ enableAlert: false,
86
+ /**
87
+ * 指定报警执行 bash 脚本位置
88
+ */
89
+ alertCmdPath: '/var/job/alert.sh',
90
+ /**
91
+ * 指定报警环境类别
92
+ */
93
+ alertEnv: 'prod',
94
+ /**
95
+ * 指定报警级别
96
+ */
97
+ alertLevel: 'Sev-2',
81
98
  }
82
99
 
83
100
  module.exports = {
@@ -0,0 +1,17 @@
1
+ let execaCommandCache
2
+
3
+ /**
4
+ * 获取 execa 函数(缓存)
5
+ * @returns { import('execa')['execa'] }
6
+ */
7
+ const getExeca = async () => {
8
+ if (!execaCommandCache) {
9
+ const execaModule = await import('execa')
10
+ execaCommandCache = execaModule.execa
11
+ }
12
+ return execaCommandCache
13
+ }
14
+
15
+ module.exports = {
16
+ getExeca,
17
+ }
@@ -1,19 +1,6 @@
1
1
  const fs = require('fs-extra')
2
2
  const path = require('path')
3
-
4
- let execaCommandCache
5
-
6
- /**
7
- * 获取 execa 函数(缓存)
8
- * @returns { import('execa')['execa'] }
9
- */
10
- const getExeca = async () => {
11
- if (!execaCommandCache) {
12
- const execaModule = await import('execa')
13
- execaCommandCache = execaModule.execa
14
- }
15
- return execaCommandCache
16
- }
3
+ const { getExeca } = require('./execa-helper')
17
4
 
18
5
  /**
19
6
  * 执行命令(不通过 shell,直接使用参数数组)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pm2-perfmonitor",
3
- "version": "2.2.0",
3
+ "version": "2.3.1",
4
4
  "description": "A pm2 module for performance monitoring. Automatically detect zombie processes and restart it",
5
5
  "author": {
6
6
  "name": "elenh",