chengjie 2 semanas atrás
pai
commit
09e0446efd
2 arquivos alterados com 67 adições e 1 exclusões
  1. 26 0
      doc/server_error_log_monitor.md
  2. 41 1
      scripts/collect-pm2-error-logs.js

+ 26 - 0
doc/server_error_log_monitor.md

@@ -101,6 +101,20 @@ INTERNAL_API_TOKEN=你的token pm2 start scripts/collect-pm2-error-logs.js \
101 101
   --update-env
102 102
 ```
103 103
 
104
+默认只采集 `app-24-error.log`,避免把旧服务 `app-error.log` 的历史噪音写入新表。若 PM2 进程名变化,可以通过环境变量指定:
105
+
106
+```bash
107
+PM2_ERROR_LOG_NAMES=app-24
108
+```
109
+
110
+可同时指定多个:
111
+
112
+```bash
113
+PM2_ERROR_LOG_NAMES=app-24,another-app
114
+```
115
+
116
+采集脚本也会忽略负载均衡健康检查导致的 `clb-healthcheck + write ECONNRESET` 噪音。
117
+
104 118
 检查状态:
105 119
 
106 120
 ```bash
@@ -303,6 +317,18 @@ curl -H "x-internal-token: 你的token" \
303 317
 
304 318
 现在推荐使用 `local-error-monitor/check-once.js` 的 SSH 远程 curl 模式,不需要本机直接访问公网端口。
305 319
 
320
+### 健康检查 ECONNRESET
321
+
322
+如果错误类似:
323
+
324
+```text
325
+Error: write ECONNRESET
326
+user-agent: clb-healthcheck
327
+GET /
328
+```
329
+
330
+通常是负载均衡健康检查断开连接产生的噪音。当前 `scripts/collect-pm2-error-logs.js` 已默认过滤这类日志。
331
+
306 332
 ### pm2: command not found
307 333
 
308 334
 非交互 SSH 可能不会加载完整 PATH。排查 PM2 时可以用登录 shell:

+ 41 - 1
scripts/collect-pm2-error-logs.js

@@ -9,6 +9,30 @@ const logDir = process.env.PM2_ERROR_LOG_DIR || path.join(os.homedir(), '.pm2',
9 9
 const stateFile = process.env.PM2_ERROR_LOG_STATE_FILE || path.join(process.cwd(), '.pm2-error-log-cursors.json');
10 10
 const pollIntervalMs = Number(process.env.PM2_ERROR_LOG_POLL_INTERVAL_MS || 30000);
11 11
 const readExisting = process.env.PM2_ERROR_LOG_READ_EXISTING === 'true';
12
+const includeLogNames = parseNameList(process.env.PM2_ERROR_LOG_NAMES || 'app-24');
13
+const excludeLogNames = parseNameList(process.env.PM2_ERROR_LOG_EXCLUDE_NAMES || '');
14
+
15
+function parseNameList(value) {
16
+  return new Set(
17
+    String(value || '')
18
+      .split(',')
19
+      .map((item) => item.trim())
20
+      .filter(Boolean)
21
+      .flatMap((item) => item.endsWith('-error.log') ? [item] : [item, `${item}-error.log`])
22
+  );
23
+}
24
+
25
+function shouldCollectLogFile(file) {
26
+  if (!file.endsWith('-error.log')) {
27
+    return false;
28
+  }
29
+
30
+  if (excludeLogNames.has(file)) {
31
+    return false;
32
+  }
33
+
34
+  return includeLogNames.size === 0 || includeLogNames.has(file);
35
+}
12 36
 
13 37
 async function loadState() {
14 38
   try {
@@ -26,7 +50,7 @@ async function listPm2ErrorLogs() {
26 50
   try {
27 51
     const files = await fsp.readdir(logDir);
28 52
     return files
29
-      .filter((file) => file.endsWith('-error.log'))
53
+      .filter(shouldCollectLogFile)
30 54
       .map((file) => path.join(logDir, file));
31 55
   } catch (error) {
32 56
     console.error(`[pm2-error-log-collector] cannot read log dir ${logDir}:`, error.message || error);
@@ -91,6 +115,14 @@ function getMessageFromBlock(block) {
91 115
     : 'PM2 error log';
92 116
 }
93 117
 
118
+function shouldIgnoreBlock(block) {
119
+  return (
120
+    /ECONNRESET/.test(block) &&
121
+    /write ECONNRESET/.test(block) &&
122
+    /clb-healthcheck/i.test(block)
123
+  );
124
+}
125
+
94 126
 async function collectOnce(state) {
95 127
   const files = await listPm2ErrorLogs();
96 128
 
@@ -113,6 +145,10 @@ async function collectOnce(state) {
113 145
     state[filePath] = stat.size;
114 146
 
115 147
     for (const block of splitErrorBlocks(content)) {
148
+      if (shouldIgnoreBlock(block)) {
149
+        continue;
150
+      }
151
+
116 152
       await recordError(new Error(getMessageFromBlock(block)), null, {
117 153
         source: 'pm2-log',
118 154
         stack: block,
@@ -127,6 +163,10 @@ async function collectOnce(state) {
127 163
 
128 164
 async function main() {
129 165
   console.log(`[pm2-error-log-collector] watching ${logDir}`);
166
+  console.log(`[pm2-error-log-collector] include logs: ${includeLogNames.size ? [...includeLogNames].join(', ') : 'all'}`);
167
+  if (excludeLogNames.size > 0) {
168
+    console.log(`[pm2-error-log-collector] exclude logs: ${[...excludeLogNames].join(', ')}`);
169
+  }
130 170
   const state = await loadState();
131 171
 
132 172
   await collectOnce(state);