PHP 怎么动态追踪

wen PHP项目 2

本文目录导读:

PHP 怎么动态追踪

  1. 内置函数追踪(最简单)
  2. Xdebug 扩展(最常用)
  3. PHP 内置调试服务器(开发环境)
  4. 使用第三方工具
  5. 日志系统追踪
  6. 生产环境轻量追踪
  7. APM 工具(生产环境)
  8. 最佳实践建议

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)
  • 特定场景:自定义追踪类

记住在生产环境要控制追踪的级别和量,避免影响性能和数据安全。

抱歉,评论功能暂时关闭!