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
相关产品推荐
相关产品推荐

