Files
tcpserver-flow/app/bootstrap/SqlDebug.php
T
2026-03-08 22:58:56 +08:00

82 lines
3.1 KiB
PHP

<?php
namespace app\bootstrap;
use app\config\Config;
use Illuminate\Database\Events\QueryExecuted;
use support\Db;
use support\Log;
// 引入webman内置日志类
use Webman\Bootstrap;
/**
* 在控制台/日志文件打印执行的SQL语句
*/
class SqlDebug implements Bootstrap
{
public static function start($worker)
{
// 判断是否为控制台环境
$is_console = !$worker;
if ($is_console) {
return;
}
// 仅在调试模式下开启
$debug = config("app.debug");
if (!$debug || $debug === 'false') {
return;
}
$appPath = app_path();
if (Config::getInstance()->dbDebug) Db::connection()->listen(function (QueryExecuted $queryExecuted) use ($appPath) {
// 过滤掉 "select 1" 这类心跳检测SQL
if (isset($queryExecuted->sql) && $queryExecuted->sql !== "select 1") {
$bindings = $queryExecuted->bindings;
// 替换SQL中的?为实际绑定参数
$sql = array_reduce(
$bindings,
function ($sql, $binding) {
// 处理参数类型:字符串加引号,数值/布尔直接使用,null显示为NULL
$value = match (true) {
is_string($binding) => "'{$binding}'",
is_null($binding) => 'NULL',
is_bool($binding) => $binding ? 1 : 0,
default => $binding
};
return preg_replace('/\?/', $value, $sql, 1);
},
$queryExecuted->sql
);
// 构造基础SQL日志信息
$sqlLog = sprintf(
"%s",
$sql
);
// 定位产生SQL的业务文件/行号/方法
$traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
foreach ($traces as $trace) {
if (isset($trace['file'], $trace['function']) && str_contains($trace['file'], $appPath)) {
// 格式化文件路径(去掉项目根目录,只保留相对路径)
$file = str_replace(base_path(), '', $trace['file']);
$file = ltrim($file, '/\\');
$file = str_replace(".php", '', $file);
$file = str_replace("\\", '.', $file);
$file = str_replace("/", '.', $file);
// $file = basename($file);
// 使用Logger::debug输出日志(会同时输出到控制台和日志文件)
Log::debug(
'<SQL_LOG_F>' . $file . "</SQL_LOG_F><SQL_LOG_L>{$trace['line']}</SQL_LOG_L><SQL_LOG_M>{$trace['function']}</SQL_LOG_M>[$queryExecuted->time ms] " . $sqlLog
);
break; // 只打印第一个匹配的业务文件信息,避免重复输出
}
}
}
});
}
}