-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
eece0b3
commit 23c874e
Showing
4 changed files
with
413 additions
and
518 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
'use strict'; | ||
|
||
const os = require('os'); | ||
const fs = require('fs'); | ||
const fsx = require('fs').promises; | ||
const path = require('path'); | ||
const exec = require('child_process').exec; | ||
|
||
const REPORT_INTERVAL = 60 * 1000; | ||
|
||
// if user renamed core file, then only check those with the | ||
// prefix | ||
function check(prefixList, item) { | ||
return prefixList.some((prefix) => { | ||
return item.startsWith(prefix) && !item.endsWith('.gz'); | ||
}); | ||
} | ||
|
||
class ListCoreJob { | ||
/** | ||
* 在以下3种路径下查找core文件 | ||
* 1. 用户指定路径 config.json里面 coredir:[dir1, dir2, ...] | ||
* 2. 用户在 /proc/sys/kernel/core_pattern 指定core生成格式 | ||
* /proc/sys/kernel/core_pattern 可以设置格式化的 core 文件保存位置或文件名 | ||
* echo “/opt/corefile/core-%e-%p-%t” > /proc/sys/kernel/core_pattern | ||
* 将会控制所产生的 core 文件会存放到 /corefile | ||
* 以下是参数列表 : | ||
* %p - insert pid into filename | ||
* %u - insert current uid into filename | ||
* %g - insert current gid into filename | ||
* %s - insert signal that caused the coredump into the filename | ||
* %t - insert UNIX time that the coredump occurred into filename | ||
* %h - insert hostname where the coredump happened into filename | ||
* %e - insert coredumping executable name into filename | ||
* 注: 只处理上面这种形式,对于第三方处理的情况不考虑 | ||
* 3. 那么对于linux系统,在Node进程的pwd目录,对于Mac在/cores下查找 | ||
*/ | ||
constructor(config) { | ||
this.coreFileNamePrefix = ['core']; | ||
|
||
if (config && config.coredir) { | ||
if (Array.isArray(config.coredir)) { | ||
this.coredir = config.coredir; | ||
} else if (typeof config.coredir === 'string') { | ||
this.coredir = [config.coredir]; | ||
} | ||
} else { | ||
this.coredir = []; | ||
} | ||
|
||
// for linux, check | ||
// core dir specified by /proc/sys/kernal/core_pattern e.g. '/tmp/core_%e.%p' | ||
if (os.platform() === 'linux') { | ||
if (!fs.existsSync('/proc/sys/kernel/core_pattern')) { | ||
return; | ||
} | ||
|
||
const patt = fs.readFileSync('/proc/sys/kernel/core_pattern', 'utf8').trim().split(' ')[0]; | ||
if (patt.indexOf('%') > 0) { | ||
// /tmp/core_%e.%p | ||
const coredir_ = path.parse(patt).dir; | ||
if (fs.existsSync(coredir_)) { | ||
try { | ||
fs.accessSync(coredir_, fs.R_OK); | ||
exports.coredir.push(coredir_); | ||
const prefix = path.parse(patt).name.split('%')[0]; | ||
if (prefix !== this.coreFileNamePrefix[0]) { | ||
this.coreFileNamePrefix.push(prefix); | ||
} | ||
} catch (e) { | ||
console.log(coredir_ + ' is unaccessible: ' + e.message); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
async getNodePids() { | ||
const nodes = await exec('ps -e -o pid,args | grep -E "node " | grep -v grep'); | ||
const pids = []; | ||
const processes = nodes.toString().trim().split('\n'); | ||
for (let i = 0; i < processes.length; i++) { | ||
if (processes[i] && processes[i].split(' ')[0]) { | ||
pids.push(processes[i].split(' ')[0]); | ||
} | ||
} | ||
return pids; | ||
} | ||
|
||
async getNodePWD(pid) { | ||
const path = `/proc/${pid}/environ`; | ||
try { | ||
await fsx.access(path, fs.constants.R_OK); | ||
} catch (err) { | ||
// 忽略该进程 | ||
return null; | ||
} | ||
|
||
const env = await fsx.readFile(path, 'utf8'); | ||
const envs = env.toString().trim().split('\u0000'); | ||
for (let i = 0; i < envs.length; i++) { | ||
if (envs[i].indexOf('PWD') === 0) { | ||
return envs[i].split('=')[1]; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
async findCores(dirs) { | ||
const corelist = { ok: true, data: [] }; | ||
const count = dirs.length; | ||
for (let i = 0; i < count; i++) { | ||
const dir = dirs[i]; | ||
const list = await this.statDir(dir); | ||
corelist.data = [...corelist.data, ...list]; | ||
} | ||
return corelist; | ||
} | ||
|
||
async statDir(dir) { | ||
const results = []; | ||
const files = await fsx.readdir(dir); | ||
for (let i = 0; i < files.length; i++) { | ||
if (check(this.coreFileNamePrefix, files[i])) { | ||
const file = path.join(dir, files[i]); | ||
const stat = await fsx.stat(file); | ||
// bypass directory | ||
if (!stat.isFile()) { | ||
continue; | ||
} | ||
|
||
// bypass core created before agentx startup | ||
if (stat.ctimeMs < Date.now() - REPORT_INTERVAL) { | ||
continue; | ||
} | ||
|
||
results.push({ | ||
path: file, | ||
size: stat.size, | ||
ctime: stat.ctime | ||
}); | ||
} | ||
} | ||
} | ||
|
||
async run() { | ||
// 非 linux,不处理,不上报 | ||
if (os.platform() !== 'linux') { | ||
return null; | ||
} | ||
|
||
// 查找当前运行中的 Node 进程 pid 列表 | ||
const pids = await this.getNodePids(); | ||
|
||
const pwds = []; | ||
for (let i = 0; i < pids.length; i++) { | ||
// 根据进程 ID,获取进程的 PWD 目录 | ||
const pwd = await this.getNodePWD(pids[i]); | ||
pwds.push(pwd); | ||
} | ||
|
||
// 合并目录并去重 | ||
const dirs = Array.from(new Set([...this.coredir, ...pwds])); | ||
if (dirs.length === 0) { | ||
return null; | ||
} | ||
|
||
// 从目录中查找符合条件的 core 文件列表 | ||
const cores = await this.findCores(dirs); | ||
const result = { | ||
type: 'coredump', | ||
metrics: cores | ||
}; | ||
return result; | ||
} | ||
|
||
static reportInterval = REPORT_INTERVAL; // 1 min | ||
} | ||
|
||
module.exports = ListCoreJob; |
Oops, something went wrong.