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; // 只打印第一个匹配的业务文件信息,避免重复输出 } } } }); } }