|
|
@@ -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);
|