diff options
author | Robin Appelman <robin@icewind.nl> | 2024-04-11 17:27:20 +0200 |
---|---|---|
committer | Robin Appelman <robin@icewind.nl> | 2024-07-24 15:15:22 +0200 |
commit | 3af3610963ac1fdc14770b2a4b9e0585b765e4f4 (patch) | |
tree | 1be0324f0f5d247b6cc58962cfc502c8cdc6b6c6 | |
parent | 06fb25b6a2220b6b2402e16c034c0f83e5c980ee (diff) | |
download | nextcloud-server-db-error-logging-28.tar.gz nextcloud-server-db-error-logging-28.zip |
feat: add additional logging for database errorsdb-error-logging-28
Signed-off-by: Robin Appelman <robin@icewind.nl>
-rw-r--r-- | lib/private/DB/Connection.php | 68 | ||||
-rw-r--r-- | lib/private/DB/ConnectionAdapter.php | 4 | ||||
-rw-r--r-- | lib/private/DB/QueryBuilder/QueryBuilder.php | 7 |
3 files changed, 73 insertions, 6 deletions
diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index ab0b35c11b0..cd0606ca135 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -79,6 +79,8 @@ class Connection extends \Doctrine\DBAL\Connection { /** @var DbDataCollector|null */ protected $dbDataCollector = null; + private ?array $transactionBacktrace = null; + protected bool $logRequestId; protected string $requestId; @@ -263,7 +265,12 @@ class Connection extends \Doctrine\DBAL\Connection { $sql = $this->finishQuery($sql); $this->queriesExecuted++; $this->logQueryToFile($sql); - return parent::executeQuery($sql, $params, $types, $qcp); + try { + return parent::executeQuery($sql, $params, $types, $qcp); + } catch (\Exception $e) { + $this->logDatabaseException($e); + throw $e; + } } /** @@ -273,7 +280,12 @@ class Connection extends \Doctrine\DBAL\Connection { $sql = $this->finishQuery($sql); $this->queriesExecuted++; $this->logQueryToFile($sql); - return parent::executeUpdate($sql, $params, $types); + try { + return parent::executeUpdate($sql, $params, $types); + } catch (\Exception $e) { + $this->logDatabaseException($e); + throw $e; + } } /** @@ -294,7 +306,12 @@ class Connection extends \Doctrine\DBAL\Connection { $sql = $this->finishQuery($sql); $this->queriesExecuted++; $this->logQueryToFile($sql); - return (int)parent::executeStatement($sql, $params, $types); + try { + return (int)parent::executeStatement($sql, $params, $types); + } catch (\Exception $e) { + $this->logDatabaseException($e); + throw $e; + } } protected function logQueryToFile(string $sql): void { @@ -356,11 +373,21 @@ class Connection extends \Doctrine\DBAL\Connection { * @deprecated 15.0.0 - use unique index and "try { $db->insert() } catch (UniqueConstraintViolationException $e) {}" instead, because it is more reliable and does not have the risk for deadlocks - see https://github.com/nextcloud/server/pull/12371 */ public function insertIfNotExist($table, $input, array $compare = null) { - return $this->adapter->insertIfNotExist($table, $input, $compare); + try { + return $this->adapter->insertIfNotExist($table, $input, $compare); + } catch (\Exception $e) { + $this->logDatabaseException($e); + throw $e; + } } public function insertIgnoreConflict(string $table, array $values) : int { - return $this->adapter->insertIgnoreConflict($table, $values); + try { + return $this->adapter->insertIgnoreConflict($table, $values); + } catch (\Exception $e) { + $this->logDatabaseException($e); + throw $e; + } } private function getType($value) { @@ -616,4 +643,35 @@ class Connection extends \Doctrine\DBAL\Connection { return new Migrator($this, $config, $dispatcher); } } + + public function beginTransaction() { + if (!$this->inTransaction()) { + $this->transactionBacktrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + } + return parent::beginTransaction(); + } + + public function commit() { + $result = parent::commit(); + if ($this->getTransactionNestingLevel() === 0) { + $this->transactionBacktrace = null; + } + return $result; + } + + public function rollBack() { + $result = parent::rollBack(); + if ($this->getTransactionNestingLevel() === 0) { + $this->transactionBacktrace = null; + } + return $result; + } + + public function logDatabaseException(\Exception $exception): void { + if ($exception instanceof Exception\UniqueConstraintViolationException) { + $this->logger->info($exception->getMessage(), ['exception' => $exception, 'transaction' => $this->transactionBacktrace]); + } else { + $this->logger->error($exception->getMessage(), ['exception' => $exception, 'transaction' => $this->transactionBacktrace]); + } + } } diff --git a/lib/private/DB/ConnectionAdapter.php b/lib/private/DB/ConnectionAdapter.php index e27c98194fb..71be59c4d35 100644 --- a/lib/private/DB/ConnectionAdapter.php +++ b/lib/private/DB/ConnectionAdapter.php @@ -261,4 +261,8 @@ class ConnectionAdapter implements IDBConnection { throw new \Exception('Database ' . $platform::class . ' not supported'); } } + + public function logDatabaseException(\Exception $exception) { + $this->inner->logDatabaseException($exception); + } } diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index d78cf50e319..e9594369a71 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -277,7 +277,12 @@ class QueryBuilder implements IQueryBuilder { ]); } - $result = $this->queryBuilder->execute(); + try { + $result = $this->queryBuilder->execute(); + } catch (\Exception $e) { + $this->connection->logDatabaseException($e); + throw $e; + } if (is_int($result)) { return $result; } |