Files
ulthon_admin/source/docker/xhprof-bootstrap.php

124 lines
4.8 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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 写入 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);
},
];
// 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();
} catch (\Throwable $e) {
error_log('[xhprof] ' . $e->getMessage());
}