Skip to content

Commit

Permalink
f
Browse files Browse the repository at this point in the history
  • Loading branch information
fengmk2 committed Mar 10, 2025
1 parent f56ef1b commit 6c9caa3
Show file tree
Hide file tree
Showing 12 changed files with 92 additions and 323 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
{os: "macos-latest", arch: "x64"},
{os: "ubuntu-latest", arch: "x64"},
]
node-version: [ 12, 14, 16, 18, 20, 21, 22 ]
node-version: [ 18, 20, 22 ]
steps:
- name: Set up Python
uses: actions/setup-python@v4
Expand Down
123 changes: 53 additions & 70 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

Easy-Monitor v3.0 Node.js Runtime 插件,输出性能日志,并且可以进行实时的运行时状态采样。

点击访问 [控制台 Demo](http://www.devtoolx.com/easy-monitor) 进行体验,完整的性能监控部署文档:https://www.yuque.com/hyj1991/easy-monitor
点击访问 [控制台 Demo](http://www.devtoolx.com/easy-monitor) 进行体验,完整的性能监控部署文档:<https://www.yuque.com/hyj1991/easy-monitor>

## 兼容性

Expand All @@ -18,24 +18,16 @@ xprofiler 插件支持三大主流操作系统:
- Linux (x64 / arm64)
- macosX (x64 / arm64)

支持的 node.js runtime 版本:
支持的 Node.js runtime 版本:

- v12.x
- v13.x
- v14.x
- v15.x
- v16.x
- v17.x
- v18.x
- v19.x
- v20.x
- v21.x
- v22.x

更低的版本因为在此项目创建时已经不在官方 LTS 计划中,故正常情况下不再支持。

需要在 `node-v12.x` 以下的版本中使用,可以使用 `v1.x-staging` 分支的的最新版本:[1.4.0](https://github.com/X-Profiler/xprofiler/tree/v1.x-staging)
更低的版本已经不在 Node.js 官方 LTS 计划中,故正常情况下不再支持。

- 需要在 `node-v16.x` 以下的版本中使用,可以使用 `2.x` 分支的的最新版本:<https://github.com/X-Profiler/xprofiler/tree/2.x>
- 需要在 `node-v12.x` 以下的版本中使用,可以使用 `1.x` 分支的的最新版本:[1.4.0](https://github.com/X-Profiler/xprofiler/tree/v1.x-staging)

## 快速开始

Expand All @@ -47,10 +39,10 @@ xprofiler 插件支持三大主流操作系统:
npm i xprofiler --save --xprofiler_binary_host_mirror=https://npmmirror.com/mirrors/xprofiler
```

如果 npmmirror 镜像暂时没同步的话,也可以执行阿里云镜像
如果 npmmirror 镜像暂时没同步的话,也可以从 GitHub Releases 下载

```bash
npm i xprofiler --save --xprofiler_binary_host_mirror=http://devtoolx.com/xprofiler
npm i xprofiler --save
```

> 这里使用了 `@mapbox/node-pre-gyp` 帮助开发者无需进行本地编译即可使用此插件,默认插件位于 [Github Release](https://github.com/X-Profiler/xprofiler/releases) 页面,国内访问可能较慢,你也可以自行镜像到其它位置加速安装。
Expand All @@ -74,45 +66,41 @@ require('xprofiler')();

本插件提供方以下的可配置参数:

* **log_dir**: 内核输出日志和性能文件的目录,默认为 `os.tmpdir()`
* **log_interval**: 内核对性能日志采样的时间间隔,默认为 `60s`
* **log_level**: 输出日志信息级别,0 info,1 error,2 debug,默认只输出 info 和 error 日志,默认为 `1`
* **log_type**: 日志输出位置,0 文件,1 控制台,默认为 `0`
* **log_format_alinode**: 是否以 Node.js 性能平台(原 AliNode)的格式输出性能分析日志,默认为 `false`
* **patch_http**: 是否对原生 http 模块进行 patch,输出 http 请求相关信息,默认 `true`
* **patch_http_with_diagnostics_channel**: 是否对通过 diagnostics_channel 模块进行 patch,输出 http 请求相关信息,在 esm 下需要开启,默认 `false`
* **patch_http_timeout**: 默认 http 请求超时时间,单位秒,作为 http 请求统计,默认 `30`
* **check_throw**: `xprofiler` 启动时检测错误时是否需要 throw,默认 `true`
* **auto_incr_heap_limit_size**: `enable_auto_incr_heap_limit` 开启后,每次自动增加的堆上限大小,默认为 `256` (MB)
* **enable_log_uv_handles**: 是否要采集 libuv 句柄的详细分类信息,比如 tcp 句柄数量,timers 数量,文件句柄数量等,默认为 `true`
* **enable_fatal_error_hook**: 是否需要在 V8 出现 FatalError 时配置钩子,默认 `true`
* **enable_fatal_error_report**: 是否需要在 V8 出现 FataLError 时导出 Report 文件,默认 `true`
* **enable_fatal_error_coredump**: 是否需要在 V8 出现 FataLError 时 Coredump,默认 `false`
* **enable_http_profiling**: 是否需要 CPU 采样时进行 HTTP Profiling。默认 `false`
* **enable_auto_incr_heap_limit**: 是否需要在 Node.js 进程达到堆上限时自动增加堆上限防止 OOM,默认 `false`

- **log_dir**: 内核输出日志和性能文件的目录,默认为 `os.tmpdir()`
- **log_interval**: 内核对性能日志采样的时间间隔,默认为 `60s`
- **log_level**: 输出日志信息级别,0 info,1 error,2 debug,默认只输出 info 和 error 日志,默认为 `1`
- **log_type**: 日志输出位置,0 文件,1 控制台,默认为 `0`
- **log_format_alinode**: 是否以 Node.js 性能平台(原 AliNode)的格式输出性能分析日志,默认为 `false`
- **patch_http**: 是否对原生 http 模块进行 patch,输出 http 请求相关信息,默认 `true`
- **patch_http_timeout**: 默认 http 请求超时时间,单位秒,作为 http 请求统计,默认 `30`
- **check_throw**: `xprofiler` 启动时检测错误时是否需要 throw,默认 `true`
- **auto_incr_heap_limit_size**: `enable_auto_incr_heap_limit` 开启后,每次自动增加的堆上限大小,默认为 `256` (MB)
- **enable_log_uv_handles**: 是否要采集 libuv 句柄的详细分类信息,比如 tcp 句柄数量,timers 数量,文件句柄数量等,默认为 `true`
- **enable_fatal_error_hook**: 是否需要在 V8 出现 FatalError 时配置钩子,默认 `true`
- **enable_fatal_error_report**: 是否需要在 V8 出现 FataLError 时导出 Report 文件,默认 `true`
- **enable_fatal_error_coredump**: 是否需要在 V8 出现 FataLError 时 Coredump,默认 `false`
- **enable_http_profiling**: 是否需要 CPU 采样时进行 HTTP Profiling。默认 `false`
- **enable_auto_incr_heap_limit**: 是否需要在 Node.js 进程达到堆上限时自动增加堆上限防止 OOM,默认 `false`

您可以通过环境变量或者在 JavaScript 代码中引入插件时传入配置的方式来使用这些配置,具体如下所示:

#### 1. 环境变量配置

* **XPROFILER_LOG_DIR**: 其值为 String,覆盖 `log_dir`
* **XPROFILER_LOG_INTERVAL**: 其值为 Number,覆盖 `log_interval`
* **XPROFILER_LOG_LEVEL**: 其值为 String,覆盖 `log_level`
* **XPROFILER_LOG_TYPE**: 其值为 String,覆盖 `log_type`
* **XPROFILER_LOG_FORMAT_ALINODE**: 其值为 YES/NO,覆盖 `log_format_alinode`
* **XPROFILER_PATCH_HTTP**: 其值为 YES/NO,覆盖 `patch_http`
* **XPROFILER_PATCH_HTTP_WITH_DIAGNOSTICS_CHANNEL**: 其值为 YES/NO,覆盖 `patch_http_with_diagnostics_channel`
* **XPROFILER_PATCH_HTTP_TIMEOUT**: 其值为 String,覆盖 `patch_http_timeout`
* **XPROFILER_CHECK_THROW**: 其值为 YES/NO 覆盖 `check_throw`
* **XPROFILER_AUTO_INCR_HEAP_LIMIT_SIZE**: 其值为 String 覆盖 `auto_incr_heap_limit_size`
* **XPROFILER_ENABLE_LOG_UV_HANDLES**: 其值为 YES/NO,覆盖 `enable_log_uv_handles`
* **XPROFILER_ENABLE_FATAL_ERROR_HOOK**: 其值为 YES/NO,覆盖 `enable_fatal_error_hook`
* **XPROFILER_ENABLE_FATAL_ERROR_REPORT**: 其值为 YES/NO,覆盖 `enable_fatal_error_report`
* **XPROFILER_ENABLE_FATAL_ERROR_COREDUMP**: 其值为 YES/NO,覆盖 `enable_fatal_error_coredump`
* **XPROFILER_ENABLE_HTTP_PROFILING**: 其值为 YES/NO,覆盖 `enable_http_profiling`
* **XPROFILER_ENABLE_AUTO_INCR_HEAP_LIMIT**: 其值为 YES/NO 覆盖 `enable_auto_incr_heap_limit`

- **XPROFILER_LOG_DIR**: 其值为 String,覆盖 `log_dir`
- **XPROFILER_LOG_INTERVAL**: 其值为 Number,覆盖 `log_interval`
- **XPROFILER_LOG_LEVEL**: 其值为 String,覆盖 `log_level`
- **XPROFILER_LOG_TYPE**: 其值为 String,覆盖 `log_type`
- **XPROFILER_LOG_FORMAT_ALINODE**: 其值为 YES/NO,覆盖 `log_format_alinode`
- **XPROFILER_PATCH_HTTP**: 其值为 YES/NO,覆盖 `patch_http`
- **XPROFILER_PATCH_HTTP_TIMEOUT**: 其值为 String,覆盖 `patch_http_timeout`
- **XPROFILER_CHECK_THROW**: 其值为 YES/NO 覆盖 `check_throw`
- **XPROFILER_AUTO_INCR_HEAP_LIMIT_SIZE**: 其值为 String 覆盖 `auto_incr_heap_limit_size`
- **XPROFILER_ENABLE_LOG_UV_HANDLES**: 其值为 YES/NO,覆盖 `enable_log_uv_handles`
- **XPROFILER_ENABLE_FATAL_ERROR_HOOK**: 其值为 YES/NO,覆盖 `enable_fatal_error_hook`
- **XPROFILER_ENABLE_FATAL_ERROR_REPORT**: 其值为 YES/NO,覆盖 `enable_fatal_error_report`
- **XPROFILER_ENABLE_FATAL_ERROR_COREDUMP**: 其值为 YES/NO,覆盖 `enable_fatal_error_coredump`
- **XPROFILER_ENABLE_HTTP_PROFILING**: 其值为 YES/NO,覆盖 `enable_http_profiling`
- **XPROFILER_ENABLE_AUTO_INCR_HEAP_LIMIT**: 其值为 YES/NO 覆盖 `enable_auto_incr_heap_limit`

#### 2. 引入插件时传入配置

Expand Down Expand Up @@ -140,7 +128,6 @@ const defaultConfig = {
log_type: 0,
log_format_alinode: false,
patch_http: true,
patch_http_with_diagnostics_channel: false,
patch_http_timeout: 30, // seconds,
check_throw: true,
auto_incr_heap_limit_size: 128 // MB,
Expand All @@ -161,14 +148,14 @@ const xprofilerConfig = Object.assign({}, defaultConfig, envConfig, userConfig);

本插件按照固定的格式来输出性能分析日志,但是也目前为了兼容已有的 alinode 运行时日志,提供了对 alinode 的支持,通过 `log_format_alinode: true` 配置或者 `XPROFILER_LOG_FORMAT_ALINODE=YES` 环境变量来启用 alinode 日志。两种日志文件名区别如下:

* 默认 xprofiler 日志文件名称
* info 日志:xprofiler-${YYYYMMDD}.log
* error 日志:xprofiler-error-${YYYYMMDD}.log
* debug 日志:xprofiler-debug-${YYYYMMDD}.log
* 以 alinode 方式输出日志文件名称
* info 日志:node-${YYYYMMDD}.log
* error 日志:node-error-${YYYYMMDD}.log
* debug 日志:node-debug-${YYYYMMDD}.log
- 默认 xprofiler 日志文件名称
- info 日志:xprofiler-${YYYYMMDD}.log
- error 日志:xprofiler-error-${YYYYMMDD}.log
- debug 日志:xprofiler-debug-${YYYYMMDD}.log
- 以 alinode 方式输出日志文件名称
- info 日志:node-${YYYYMMDD}.log
- error 日志:node-error-${YYYYMMDD}.log
- debug 日志:node-debug-${YYYYMMDD}.log

默认的 xprofiler 和 alinode 两种日志的主要区别在日志前缀上(当然输出的部分内容也不尽相同),其中 xprofiler 日志前缀如下:

Expand Down Expand Up @@ -231,16 +218,14 @@ xprofctl <action> -p <pid> [-w <thread_id>] [-t profiling_time]
copyright 2019
```


## 插件架构和实现原理

参见 [文档 - 整体架构](https://www.yuque.com/hyj1991/easy-monitor/architecture)


## 稳定性

xprofiler 每个 commit 都会通过 github actions 进行在 windows/linux/macos 下的编译以及单元测试,如果您在使用中依旧出现意外的 crash,可以反馈到 [Issue 列表](https://github.com/X-Profiler/xprofiler/issues) 进行跟踪处理。

xprofiler 每个 commit 都会通过 github actions 进行在 windows/linux/macos 下的编译以及单元测试,
如果您在使用中依旧出现意外的 crash,可以反馈到 [Issue 列表](https://github.com/X-Profiler/xprofiler/issues) 进行跟踪处理。

## 测试与代码覆盖率

Expand All @@ -252,19 +237,17 @@ npm run cov

master 分支的代码覆盖率情况可以参见 [codecov badge](https://codecov.io/gh/X-Profiler/xprofiler/branch/master)


## 如何参与贡献

* fork 本仓库至你自己的 github 仓库列表中
* clone 你 fork 出来的仓库至本地开发
* 进行本地开发,添加功能或者修改 bug,并且附上必要的测试
* 请在 commit 中描述下添加的功能或者修改 bug 的详细信息,并提交至你的远程仓库
*[PR](https://github.com/X-Profiler/xprofiler/pulls) 页面选择 New Pull Request,继续选择 compare across forks,在列表中选中你的 Fork,然后创建新的 PR
* 查看 pr 下的 travis/appveypr CI 状态,如果执行失败请到对应页面查看失败原因并在你自己的仓库下进行修复,修复 commit 会自动同步到此 pr 下,无需关闭重启发起
- fork 本仓库至你自己的 github 仓库列表中
- clone 你 fork 出来的仓库至本地开发
- 进行本地开发,添加功能或者修改 bug,并且附上必要的测试
- 请在 commit 中描述下添加的功能或者修改 bug 的详细信息,并提交至你的远程仓库
-[PR](https://github.com/X-Profiler/xprofiler/pulls) 页面选择 New Pull Request,继续选择 compare across forks,在列表中选中你的 Fork,然后创建新的 PR
- 查看 pr 下的 GitHub Action CI 状态,如果执行失败请到对应页面查看失败原因并在你自己的仓库下进行修复,修复 commit 会自动同步到此 pr 下,无需关闭重启发起

我们将在 review 后选择合并至本仓库内,贡献者也会加入到本项目的协作者列表中。


## 贡献者

[![contributors](https://contrib.rocks/image?repo=x-profiler/xprofiler&max=240&columns=26)](https://github.com/x-profiler/xprofiler/graphs/contributors)
Expand Down
9 changes: 0 additions & 9 deletions configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,6 @@ module.exports = () => {
...config('patch_http', 'XPROFILER_PATCH_HTTP', 'boolean', true),
},

{
...xprofctl(false),
...config('patch_http_with_diagnostics_channel',
'XPROFILER_PATCH_HTTP_WITH_DIAGNOSTICS_CHANNEL',
'boolean',
false,
),
},

{
...xprofctl(false),
...config('patch_http_timeout', 'XPROFILER_PATCH_HTTP_TIMEOUT', 'number', 30), // seconds
Expand Down
1 change: 0 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export interface XprofilerConfig {
log_format_alinode?: boolean;
enable_log_uv_handles?: boolean;
patch_http?: boolean;
patch_http_with_diagnostics_channel?: boolean;
patch_http_timeout?: number;
check_throw?: boolean;
auto_incr_heap_limit_size?: number;
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"name": "xprofiler",
"version": "2.6.1",
"engines": {
"node": ">=18.19.0"
},
"description": "node.js addon to output runtime logs",
"bin": {
"xprofctl": "bin/xprofctl"
Expand Down
71 changes: 1 addition & 70 deletions patch/http.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
'use strict';

const shimmer = require('./shimmer');
const http = require('http');
const https = require('https');

const httpConfig = {
http_detail_profiling: false,
start_time: 0,
Expand All @@ -16,71 +12,6 @@ function getRequestDetail(req, res, start, sent) {
return `${offset},${url},${req.method},${sent},${res.statusCode},${rt}`;
}

function requestListenerWrapper(original, methods) {
const { setHttpConfig, addLiveRequest, addCloseRequest, addSentRequest,
addRequestTimeout, addHttpStatusCode, addHttpProfilingDetail, patch_http_timeout } = methods;

setHttpConfig(httpConfig);

return function (req, res) {
addLiveRequest();

const start = Date.now();

const timer = setTimeout(() => {
addRequestTimeout();
if (httpConfig.http_detail_profiling) {
const detail = getRequestDetail(req, res, start, 0);
addHttpProfilingDetail(detail);
}
}, patch_http_timeout * 1000);
timer.unref();

res.on('finish', () => {
addHttpStatusCode(res.statusCode);
addSentRequest(Date.now() - start);
clearTimeout(timer);
if (httpConfig.http_detail_profiling) {
const detail = getRequestDetail(req, res, start, 1);
addHttpProfilingDetail(detail);
}
});

res.on('close', () => {
addCloseRequest();
clearTimeout(timer);
});

// call origin function
const returned = original.apply(this, arguments);
return returned;
};
}

function serverWrapper(methods, original) {
return function (opts, requestListener) {
const args = Array.from(arguments);
let returned;

if (typeof opts === 'function') {
args.splice(0, 1, requestListenerWrapper(opts, methods));
} else if (typeof requestListener === 'function') {
args.splice(1, 1, requestListenerWrapper(requestListener, methods));
}

returned = original.apply(this, args);

return returned;
};
}

function patchHttp(methods) {
// patch http server
shimmer.wrap(http, 'createServer', serverWrapper.bind(this, methods));
// patch https server
shimmer.wrap(https, 'createServer', serverWrapper.bind(this, methods));
}

let _onHttpServerRequestStart;
function subscribeHttpServerRequestStart(options) {
const { setHttpConfig, addLiveRequest, addCloseRequest, addSentRequest,
Expand Down Expand Up @@ -134,4 +65,4 @@ function unsubscribeHttpServerRequestStart() {
diagnosticsChannel.unsubscribe('http.server.request.start', _onHttpServerRequestStart);
}

module.exports = { patchHttp, subscribeHttpServerRequestStart, unsubscribeHttpServerRequestStart };
module.exports = { subscribeHttpServerRequestStart, unsubscribeHttpServerRequestStart };
8 changes: 2 additions & 6 deletions patch/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
'use strict';

const { patchHttp, subscribeHttpServerRequestStart } = require('./http');
const { subscribeHttpServerRequestStart } = require('./http');

function patch(config, methods) {
if (config.patch_http) {
const options = { ...methods, patch_http_timeout: config.patch_http_timeout };
if (config.patch_http_with_diagnostics_channel) {
subscribeHttpServerRequestStart(options);
} else {
patchHttp(options);
}
subscribeHttpServerRequestStart(options);
}
}

Expand Down
4 changes: 1 addition & 3 deletions test/fixtures/cases/command.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,6 @@ exports = module.exports = function (logdir) {
{ key: 'data.log_level', rule: /^2$/ },
{ key: 'data.log_type', rule: /^1$/ },
{ key: 'data.patch_http', rule: { label: 'true', test: value => value === true } },
{ key: 'data.patch_http_with_diagnostics_channel', rule: { label: 'false', test: value => value === false } },
{ key: 'data.patch_http_timeout', rule: /^30$/ },
{ key: 'data.check_throw', rule: { label: 'false', test: value => value === false } },
{ key: 'data.auto_incr_heap_limit_size', rule: /^256$/ },
Expand Down Expand Up @@ -258,8 +257,7 @@ exports = module.exports = function (logdir) {
+ ' - log_type: 1\n'
+ ' - m_mmap_threshold: 128\n'
+ ' - patch_http: true\n'
+ ' - patch_http_timeout: 30\n'
+ ' - patch_http_with_diagnostics_channel: false')
+ ' - patch_http_timeout: 30')
];
}
},
Expand Down
6 changes: 0 additions & 6 deletions test/fixtures/cases/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,6 @@ const configure = {
envValue: 'NO',
userValue: false
},
patch_http_with_diagnostics_channel: {
defaultValue: false,
envKey: 'XPROFILER_PATCH_HTTP_WITH_DIAGNOSTICS_CHANNEL',
envValue: 'YES',
userValue: true
},
patch_http_timeout: {
defaultValue: 30,
envKey: 'XPROFILER_PATCH_HTTP_TIMEOUT',
Expand Down
1 change: 0 additions & 1 deletion test/fixtures/cases/logbypass.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ function getTestCases(title, logdirBlocking, logdirNonBlocking, envConfig, struc
const commonEnvConfig = Object.assign({}, process.env, {
XPROFILER_LOG_INTERVAL: 1,
XPROFILER_PATCH_HTTP_TIMEOUT: 1,
XPROFILER_PATCH_HTTP_WITH_DIAGNOSTICS_CHANNEL: 0,
});

// common case config
Expand Down
Loading

0 comments on commit 6c9caa3

Please sign in to comment.