本文目录导读:

PHP 动态追踪主要有以下几种方法,从简单到复杂:
内置函数追踪(最简单)
使用 debug_backtrace()
function trace() {
$trace = debug_backtrace();
foreach ($trace as $index => $frame) {
echo "#{$index} {$frame['file']}:{$frame['line']} - ";
echo (isset($frame['class']) ? $frame['class'].'->' : '') . $frame['function'] . "()\n";
}
}
// 在代码中调用
function getUser($id) {
trace(); // 查看调用栈
return "User $id";
}
使用 error_log() 记录
error_log("Debug: Variable value = " . var_export($var, true));
Xdebug 扩展(最常用)
安装配置
# php.ini zend_extension=xdebug xdebug.mode=debug,trace xdebug.start_with_request=yes xdebug.trace_output_dir=/tmp/xdebug xdebug.trace_format=1
运行时启用追踪
// 开始追踪
xdebug_start_trace('/tmp/trace.log');
// 你的代码...
// 停止追踪
xdebug_stop_trace();
查看追踪结果
# 查看函数调用树 php -dxdebug.auto_trace=1 your_script.php
PHP 内置调试服务器(开发环境)
# 启动内置服务器 php -S localhost:8000 -d display_errors=1 -d error_reporting=E_ALL # 使用 xdebug 远程调试 php -dxdebug.mode=debug -dxdebug.start_with_request=yes -d xdebug.client_host=127.0.0.1 -d xdebug.client_port=9003 your_script.php
使用第三方工具
Tideways / Blackfire
# tideways 配置 extension=tideways_xhprof.so tideways_xhprof.clock_use_gettimeofday=1 # 启动追踪 tideways_xhprof_enable(TIDEWAYS_XHPROF_FLAGS_MEMORY | TIDEWAYS_XHPROF_FLAGS_CPU); // 你的代码... $data = tideways_xhprof_disable(); // 保存到数据库或文件
使用 psysh 交互式调试
composer require psy/psysh
// 在代码中插入断点
if ($_SERVER['REMOTE_ADDR'] == '127.0.0.1') {
\Psy\Shell::debug(get_defined_vars());
}
日志系统追踪
Monolog 配合上下文
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$logger = new Logger('app');
$logger->pushHandler(new StreamHandler('/tmp/app.log', Logger::DEBUG));
// 添加上下文
$logger->debug('User operation', [
'user_id' => 123,
'action' => 'update',
'trace' => (new \Exception())->getTraceAsString()
]);
生产环境轻量追踪
自定义追踪类
class DebugTracer {
private static $traces = [];
private static $enabled = false;
public static function enable() {
self::$enabled = true;
self::$traces['start'] = microtime(true);
}
public static function trace($message, $data = null) {
if (!self::$enabled) return;
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$caller = $backtrace[1] ?? null;
self::$traces[] = [
'time' => microtime(true) - self::$traces['start'],
'message' => $message,
'data' => $data,
'caller' => ($caller ? $caller['function'] : 'unknown'),
'file' => ($caller ? $caller['file'] : ''),
'line' => ($caller ? $caller['line'] : 0)
];
}
public static function getTraces() {
return self::$traces;
}
public static function dump() {
if (!self::$enabled) return;
echo "=== Trace Dump ===\n";
foreach (self::$traces as $trace) {
echo sprintf("[%6.3fs] %s:%d - %s %s\n",
$trace['time'],
basename($trace['file']),
$trace['line'],
$trace['message'],
$trace['data'] ? json_encode($trace['data']) : ''
);
}
}
}
// 使用
DebugTracer::enable();
DebugTracer::trace('开始处理订单', ['order_id' => 123]);
// ... 业务代码
DebugTracer::trace('订单处理完成', ['status' => 'success']);
DebugTracer::dump();
APM 工具(生产环境)
使用 SkyWalking / Zipkin
// 创建 span
$span = $tracer->startSpan('Operation');
$span->setAttribute('user.id', $userId);
// 业务代码...
$span->stop();
最佳实践建议
环境区分
// 根据环境启用不同级别的追踪
if (getenv('APP_ENV') === 'development') {
error_reporting(E_ALL);
ini_set('display_errors', 1);
xdebug_start_trace('/tmp/dev_trace.log');
} elseif (getenv('APP_ENV') === 'production') {
error_reporting(0);
// 只记录错误级别的日志
}
性能考虑
// 只追踪关键操作
if (should_trace('user.operation')) {
$start = microtime(true);
// 业务代码
trace_time('user.operation', microtime(true) - $start);
}
安全注意
// 永远不要在日志中记录敏感信息
$logger->info('登录成功', [
'user_id' => $userId,
// 不要记录密码、令牌等
'token' => hash('sha256', $token) // 只记录哈希
]);
选择哪种方法取决于你的需求:
- 开发调试:使用 Xdebug + IDE
- 简单追踪:使用 debug_backtrace()
- 生产环境:使用 APM 工具(Tideways/New Relic)
- 特定场景:自定义追踪类
记住在生产环境要控制追踪的级别和量,避免影响性能和数据安全。