feat(stack): XHProf 采集切换文件落盘并独立 xhgui 供应

This commit is contained in:
augushong
2026-08-14 21:58:29 +08:00
parent e0e2580646
commit da388a8c4f
6 changed files with 138 additions and 47 deletions

View File

@@ -3,11 +3,19 @@
* XHProf 性能分析采集器初始化脚本
*
* 通过 PHP auto_prepend_file 机制自动加载,零侵入业务代码。
* 采集数据通过 HTTP Upload 发送到 XHGui 可视化服务。
*
* 采集数据落盘方式(环境变量 XHPROF_SAVER默认 file
* - file 本地 jsonl 文件落盘(默认;按天分文件,由后台导入定时任务增量入库)
* - upload HTTP Upload 推送到 XHGui 可视化服务(旧行为,兼容保留)
* - stack file + upload 双写
*
* 环境变量控制:
* - XHPROF_ENABLE=true 开启采集(默认关闭)
* - XHGUI_UPLOAD_URL XHGui 上传地址(默认 http://xhgui:80/run/import
* - XHPROF_SAVER 落盘方式file / upload / stack默认 file
* - XHPROF_FILE_DIR jsonl 落盘目录(默认 /var/www/html/runtime/xhprof
* 采集端与导入任务均以 getenv 读取此变量,保持单一来源,
* 禁止与 ThinkPHP env() 混用,两处漂移会导致导入读不到文件)
* - XHGUI_UPLOAD_URL XHGui 上传地址upload/stack 模式使用,默认 http://xhgui:80/run/import
* - XHGUI_UPLOAD_TOKEN 上传令牌(默认 ulthon-admin-xhprof
*
* Cookie 手动触发:
@@ -30,7 +38,19 @@ require_once '/opt/xhprof/vendor/autoload.php';
use Xhgui\Profiler\Profiler;
use Xhgui\Profiler\ProfilingFlags;
// 4. 配置 Profiler
// 4. 落盘目录单一来源XHPROF_FILE_DIR导入定时任务读同一变量
$fileDir = getenv('XHPROF_FILE_DIR') ?: '/var/www/html/runtime/xhprof';
// 5. 落盘方式(默认 file
$saver = getenv('XHPROF_SAVER') ?: Profiler::SAVER_FILE;
// stack 依赖上传地址XHGUI_UPLOAD_URL 为空时降级 file 单写,防止 upload 分支静默丢数据
if ($saver === Profiler::SAVER_STACK && !getenv('XHGUI_UPLOAD_URL')) {
error_log('[xhprof] XHPROF_SAVER=stack 但 XHGUI_UPLOAD_URL 为空,降级为 file 单写');
$saver = Profiler::SAVER_FILE;
}
// 6. 配置 Profiler
$config = [
// 采集标志CPU + 内存 + 不记录内置函数 + 不记录 span
'profiler.flags' => [
@@ -39,13 +59,6 @@ $config = [
ProfilingFlags::NO_BUILTINS,
ProfilingFlags::NO_SPANS,
],
// 数据上传方式HTTP POST 到 XHGui
'save.handler' => Profiler::SAVER_UPLOAD,
'save.handler.upload' => [
'url' => getenv('XHGUI_UPLOAD_URL') ?: 'http://xhgui:80/run/import',
'timeout' => 3,
'token' => getenv('XHGUI_UPLOAD_TOKEN') ?: 'ulthon-admin-xhprof',
],
// 采集控制cookie 强制触发 或 1% 随机采样
'profiler.enable' => function () {
// 浏览器带 cookie _profiler 时强制采集(手动调试)
@@ -55,9 +68,53 @@ $config = [
// 默认 1% 采样率
return mt_rand(1, 100) === 1;
},
// URL 规范化:采集时原生计算 simple_url 写入 metapath 去 query、纯数字段替换 :id
// 后台导入任务直接读 meta.simple_url仅缺失时兜底计算此为唯一实现点
'profiler.simple_url' => function ($url) {
$path = parse_url($url, PHP_URL_PATH);
$parts = explode('/', trim($path, '/'));
foreach ($parts as &$p) {
if ($p !== '' && ctype_digit($p)) {
$p = ':id';
}
}
return implode('/', $parts);
},
];
// 5. 启动采集(异常不影响应用正常运行
// 7. 落盘方式分派(未知值回退 file
if ($saver === Profiler::SAVER_UPLOAD) {
// uploadHTTP POST 到 XHGui旧行为兼容保留
$config['save.handler'] = Profiler::SAVER_UPLOAD;
} elseif ($saver === Profiler::SAVER_STACK) {
// stackfile + upload 双写SaverFactory 递归创建 savers 数组中的每个 saver
$config['save.handler'] = Profiler::SAVER_STACK;
$config['save.handler.stack'] = [
'savers' => [Profiler::SAVER_FILE, Profiler::SAVER_UPLOAD],
];
} else {
// file本地 jsonl 文件落盘(默认)
$config['save.handler'] = Profiler::SAVER_FILE;
}
// FileSaver 不自建目录start 前确保目录存在
if ($saver !== Profiler::SAVER_UPLOAD) {
@mkdir($fileDir, 0777, true);
}
// 按天分文件runs-{Ymd}.jsonlfilename 在每个请求求值bootstrap 每请求执行),
// date() 跨天自然滚动;导入任务可安全删除已导完的整天文件
$config['save.handler.file'] = [
'filename' => $fileDir . '/runs-' . date('Ymd') . '.jsonl',
];
$config['save.handler.upload'] = [
'url' => getenv('XHGUI_UPLOAD_URL') ?: 'http://xhgui:80/run/import',
'timeout' => 3,
'token' => getenv('XHGUI_UPLOAD_TOKEN') ?: 'ulthon-admin-xhprof',
];
// 8. 启动采集(异常不影响应用正常运行)
try {
$profiler = new Profiler($config);
$profiler->start();