You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Doctrine 3中setSQLLogger已弃用,如何用中间件记录查询执行时间?

Doctrine查询执行时长记录方案(替代已弃用的setSQLLogger)

Doctrine 已弃用->setSQLLogger()方法,改用Middleware机制。如果需要记录查询的开始/结束时间以计算执行时长,可以通过以下两种方案实现:

方案一:自定义Middleware实时记录单条查询耗时

通过实现Doctrine的MiddlewareInterface,可以拦截查询执行的前后节点,从而计算单条查询的执行时长:

use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Middleware;
use Psr\Log\LoggerInterface;

class QueryExecutionTimeMiddleware implements Middleware
{
    public function __construct(private LoggerInterface $logger)
    {
    }

    public function wrap(Driver $driver): Driver
    {
        return new class($driver, $this->logger) implements Driver {
            public function __construct(
                private Driver $wrappedDriver,
                private LoggerInterface $logger
            ) {
            }

            public function connect(array $params)
            {
                $connection = $this->wrappedDriver->connect($params);
                return new class($connection, $this->logger) implements Driver\Connection {
                    public function __construct(
                        private Driver\Connection $wrappedConnection,
                        private LoggerInterface $logger
                    ) {
                    }

                    public function prepare($statement)
                    {
                        $stmt = $this->wrappedConnection->prepare($statement);
                        return new class($stmt, $this->logger) implements Driver\Statement {
                            public function __construct(
                                private Driver\Statement $wrappedStatement,
                                private LoggerInterface $logger
                            ) {
                            }

                            public function execute($params = null)
                            {
                                $startTime = microtime(true);
                                $result = $this->wrappedStatement->execute($params);
                                $executionTime = (microtime(true) - $startTime) * 1000; // 转换为毫秒

                                $sql = $this->wrappedStatement->queryString;
                                $this->logger->info(
                                    'SQL查询执行完成',
                                    [
                                        'sql' => $sql,
                                        'params' => $params,
                                        'execution_time_ms' => round($executionTime, 2)
                                    ]
                                );

                                return $result;
                            }

                            // 实现Statement接口的其他方法,直接委托给wrappedStatement
                            public function bindParam($param, &$variable, int $type = Driver\ParameterType::STRING, int $length = null): bool
                            {
                                return $this->wrappedStatement->bindParam($param, $variable, $type, $length);
                            }

                            // 其余接口方法(如bindValue、rowCount等)同理委托即可
                        };
                    }

                    // 实现Connection接口的其他方法,直接委托给wrappedConnection
                    public function query($statement)
                    {
                        return $this->wrappedConnection->query($statement);
                    }

                    // 其余接口方法(如quote、beginTransaction等)同理委托即可
                };
            }

            public function getName()
            {
                return $this->wrappedDriver->getName();
            }
        };
    }
}

以Symfony为例,将Middleware注册到Doctrine连接配置中(config/packages/doctrine.yaml):

doctrine:
    dbal:
        connections:
            default:
                # 其他配置...
                middleware:
                    - App\Doctrine\Middleware\QueryExecutionTimeMiddleware

方案二:利用DebugDataHolder在请求结束批量处理

如果不需要实时记录,可在请求结束时通过DebugDataHolder批量获取所有查询的执行信息(Doctrine在Debug模式下会自动记录每个查询的耗时):

use Doctrine\SqlFormatter\SqlFormatter;
use Doctrine\DBAL\Logging\DebugDataHolder;

class DatabaseFormatter
{
    private SqlFormatter $formatter;

    public function __construct(
        private DebugDataHolder $dataHolder
    ) {
        $this->formatter = new SqlFormatter();
    }

    public function afterRequest(): void
    {
        $queries = $this->dataHolder->getData();

        foreach ($queries as $query) {
            // 格式化SQL语句
            $formattedSql = $this->formatter->format($query['sql'], $query['params']);
            // 获取执行时长(单位:毫秒)
            $executionTime = round($query['executionMS'], 2);

            // 自定义处理逻辑:写入日志、输出到控制台等
            error_log(sprintf(
                "SQL查询耗时:%sms\n格式化后SQL:%s\n",
                $executionTime,
                $formattedSql
            ));
        }

        // 可选:清空数据,避免后续请求重复处理
        $this->dataHolder->clear();
    }
}

需确保Doctrine Debug模式已开启(开发环境默认开启),并将DatabaseFormatter注册为事件订阅者,监听请求结束事件(如Symfony的KernelEvents::TERMINATE事件)。

内容的提问来源于stack exchange,提问作者Justinas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 21:08:31