mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-08-31 05:05:33 +08:00
124 lines
4.8 KiB
PHP
124 lines
4.8 KiB
PHP
<?php
|
||
/**
|
||
* XHProf 性能分析采集器初始化脚本
|
||
*
|
||
* 通过 PHP auto_prepend_file 机制自动加载,零侵入业务代码。
|
||
*
|
||
* 采集数据落盘方式(环境变量 XHPROF_SAVER,默认 file):
|
||
* - file 本地 jsonl 文件落盘(默认;按天分文件,由后台导入定时任务增量入库)
|
||
* - upload HTTP Upload 推送到 XHGui 可视化服务(旧行为,兼容保留)
|
||
* - stack file + upload 双写
|
||
*
|
||
* 环境变量控制:
|
||
* - XHPROF_ENABLE=true 开启采集(默认关闭)
|
||
* - 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 手动触发:
|
||
* - 浏览器请求带 cookie _profiler=1 时强制采集(忽略采样率)
|
||
*/
|
||
|
||
// 1. 环境变量开关检查(默认关闭)
|
||
if (getenv('XHPROF_ENABLE') !== 'true') {
|
||
return;
|
||
}
|
||
|
||
// 2. 优雅降级:php-profiler 未安装时静默退出,不报错不影响应用
|
||
if (!is_file('/opt/xhprof/vendor/autoload.php')) {
|
||
return;
|
||
}
|
||
|
||
// 3. 加载 php-profiler autoloader
|
||
require_once '/opt/xhprof/vendor/autoload.php';
|
||
|
||
use Xhgui\Profiler\Profiler;
|
||
use Xhgui\Profiler\ProfilingFlags;
|
||
|
||
// 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' => [
|
||
ProfilingFlags::CPU,
|
||
ProfilingFlags::MEMORY,
|
||
ProfilingFlags::NO_BUILTINS,
|
||
ProfilingFlags::NO_SPANS,
|
||
],
|
||
// 采集控制:cookie 强制触发 或 1% 随机采样
|
||
'profiler.enable' => function () {
|
||
// 浏览器带 cookie _profiler 时强制采集(手动调试)
|
||
if (!empty($_COOKIE['_profiler'])) {
|
||
return true;
|
||
}
|
||
// 默认 1% 采样率
|
||
return mt_rand(1, 100) === 1;
|
||
},
|
||
// URL 规范化:采集时原生计算 simple_url 写入 meta(path 去 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);
|
||
},
|
||
];
|
||
|
||
// 7. 落盘方式分派(未知值回退 file)
|
||
if ($saver === Profiler::SAVER_UPLOAD) {
|
||
// upload:HTTP POST 到 XHGui(旧行为,兼容保留)
|
||
$config['save.handler'] = Profiler::SAVER_UPLOAD;
|
||
} elseif ($saver === Profiler::SAVER_STACK) {
|
||
// stack:file + 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}.jsonl):filename 在每个请求求值(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();
|
||
} catch (\Throwable $e) {
|
||
error_log('[xhprof] ' . $e->getMessage());
|
||
}
|