dbDebug) Db::connection()->listen(function (QueryExecuted $queryExecuted) use ($pluginPath, $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) || str_contains($trace['file'], $pluginPath))) {
// 格式化文件路径(去掉项目根目录,只保留相对路径)
$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(
'' . $file . "{$trace['line']}{$trace['function']}[$queryExecuted->time ms] " . $sqlLog
);
break; // 只打印第一个匹配的业务文件信息,避免重复输出
}
}
}
});
}
}