本文目录导读:

在 PHP 中跟踪 Bug 是一个系统性的过程,我把它分为排查工具、调试技巧和预防机制三个层面来详细说明:
基础排查工具(必装)
开启错误显示与日志
开发环境开启显示,生产环境只记日志:
// 开发环境 - 显示所有错误
ini_set('display_errors', 1);
error_reporting(E_ALL);
// 生产环境 - 只记录日志,不显示
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', '/path/to/php-error.log');
使用 Xdebug(最强大的调试工具)
; php.ini 配置 [xdebug] zend_extension=xdebug.so xdebug.mode=debug xdebug.start_with_request=yes xdebug.client_host=127.0.0.1 xdebug.client_port=9003 xdebug.log=/path/to/xdebug.log
配合 IDE(PHPStorm/VSCode)实现断点调试。
运行时调试技巧
打印调试(快速定位)
// 基础打印
var_dump($variable); // 带类型和长度
print_r($array); // 可读性更好
die('调试到此为止'); // 提前终止
// 更专业的调试函数
function dd($data) {
echo '<pre>';
var_dump($data);
echo '</pre>';
die();
}
堆栈追踪(定位调用链)
// 打印当前函数调用栈
debug_print_backtrace();
// 获取堆栈信息
$trace = debug_backtrace();
foreach ($trace as $item) {
echo $item['file'] . ':' . $item['line'] . ' - ' .
($item['class'] ?? '') . ($item['type'] ?? '') . $item['function'] . "\n";
}
异常捕获与记录
try {
// 你的代码
} catch (Exception $e) {
// 记录完整错误信息
error_log(
"错误信息: " . $e->getMessage() . "\n" .
"文件位置: " . $e->getFile() . ":" . $e->getLine() . "\n" .
"堆栈追踪: " . $e->getTraceAsString()
);
}
系统性日志方案
使用 Monolog(主流日志库)
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$logger = new Logger('app');
$logger->pushHandler(new StreamHandler('/path/to/app.log'));
// 记录不同级别
$logger->info('用户登录成功', ['user_id' => 123]);
$logger->error('数据库连接失败', ['db' => 'mysql']);
$logger->debug('调试信息', ['request' => $_REQUEST]);
自定义日志函数
function writeLog($level, $message, $context = []) {
$logData = [
'time' => date('Y-m-d H:i:s'),
'level' => $level,
'message' => $message,
'context' => json_encode($context, JSON_UNESCAPED_UNICODE),
'trace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5)
];
$logLine = json_encode($logData, JSON_UNESCAPED_UNICODE) . "\n";
file_put_contents('/path/to/debug.log', $logLine, FILE_APPEND);
}
常见 Bug 类型排查技巧
逻辑类 Bug
// 使用断言确保数据正确
assert($count > 0, '数量必须大于0');
// 检查变量类型
if (!is_array($data)) {
throw new InvalidArgumentException('数据类型错误');
}
性能瓶颈
// 计算执行时间
$start = microtime(true);
// 要检测的代码
$end = microtime(true);
error_log("执行时间: " . ($end - $start) . " 秒");
内存泄漏
// 检查内存使用
error_log("峰值内存: " . memory_get_peak_usage());
error_log("当前内存: " . memory_get_usage());
框架内调试(以 Laravel 为例)
// 使用 Laravel 内置日志
\Log::info('用户操作', ['user' => $user->id, 'action' => 'create']);
// 使用 Telescope(Laravel 调试面板)
// 查看请求、SQL、异常等所有信息
// 浏览器访问 /telescope
// 使用 Clockwork 浏览器扩展
// Chrome 插件 + 扩展包,开发时实时查看
生产环境监控
接入第三方工具
- Sentry:实时错误追踪
- New Relic:性能监控
- Bugsnag:崩溃报告
配置 Sentry
use Sentry\SentrySdk;
use Sentry\State\Scope;
Sentry\init(['dsn' => 'your-dsn']);
// 捕获异常
try {
// 代码
} catch (Exception $e) {
Sentry\captureException($e);
}
最佳实践流程
- 复现问题 → 记下步骤、输入数据
- 开启调试 → 显示错误、记录日志
- 二分法定位 → 注释代码块,缩小范围
- 写最小测试 → 独立脚本或测试用例
- 修复并验证 → 确认问题已解决
- 添加回归测试 → 防止再次出现
实用小技巧
// 快速查看文件最后修改时间(确认是否用了旧代码)
filemtime(__FILE__);
// 查看加载的配置文件
phpinfo();
print_r(ini_get_all());
// 检测未定义变量
error_reporting(E_ALL); // 会显示 Notice
// 条件断点(结合 IDE)
if ($_GET['id'] == 123) {
xdebug_break(); // 触发断点
}
- 开发环境:显示所有错误 + Xdebug 断点 + 日志
- 测试环境:记录日志 + 模拟数据
- 生产环境:只记录日志 + 监控工具 + 错误追踪
最实用的排查顺序:
- 查看错误日志(
tail -f实时监控) - 开启 E_ALL(显示所有错误级别)
- 打印关键变量(
var_dump+die) - 堆栈追踪(
debug_backtrace) - 断点调试(Xdebug + IDE)
遇到任何具体 Bug 场景,欢迎提供更多细节,我可以给出更有针对性的排查方案。