Skip to content

Commit bd28c9f

Browse files
committed
feat: add additional logging for database errors
including the stack trace of the current database transaction Signed-off-by: Robin Appelman <[email protected]>
1 parent 4a4f90c commit bd28c9f

File tree

2 files changed

+71
-4
lines changed

2 files changed

+71
-4
lines changed

lib/private/DB/Connection.php

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ class Connection extends \Doctrine\DBAL\Connection {
7979
/** @var DbDataCollector|null */
8080
protected $dbDataCollector = null;
8181

82+
protected bool $logDbException = false;
83+
private ?array $transactionBacktrace = null;
84+
8285
protected bool $logRequestId;
8386
protected string $requestId;
8487

@@ -110,6 +113,7 @@ public function __construct(
110113
$this->logger = \OC::$server->get(LoggerInterface::class);
111114

112115
$this->logRequestId = $this->systemConfig->getValue('db.log_request_id', false);
116+
$this->logDbException = $this->systemConfig->getValue('db.log_exceptions', false);
113117
$this->requestId = Server::get(IRequestId::class)->getId();
114118

115119
/** @var \OCP\Profiler\IProfiler */
@@ -263,7 +267,12 @@ public function executeQuery(string $sql, array $params = [], $types = [], Query
263267
$sql = $this->finishQuery($sql);
264268
$this->queriesExecuted++;
265269
$this->logQueryToFile($sql);
266-
return parent::executeQuery($sql, $params, $types, $qcp);
270+
try {
271+
return parent::executeQuery($sql, $params, $types, $qcp);
272+
} catch (\Exception $e) {
273+
$this->logDatabaseException($e);
274+
throw $e;
275+
}
267276
}
268277

269278
/**
@@ -294,7 +303,12 @@ public function executeStatement($sql, array $params = [], array $types = []): i
294303
$sql = $this->finishQuery($sql);
295304
$this->queriesExecuted++;
296305
$this->logQueryToFile($sql);
297-
return (int)parent::executeStatement($sql, $params, $types);
306+
try {
307+
return (int)parent::executeStatement($sql, $params, $types);
308+
} catch (\Exception $e) {
309+
$this->logDatabaseException($e);
310+
throw $e;
311+
}
298312
}
299313

300314
protected function logQueryToFile(string $sql): void {
@@ -356,11 +370,21 @@ public function realLastInsertId($seqName = null) {
356370
* @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
357371
*/
358372
public function insertIfNotExist($table, $input, array $compare = null) {
359-
return $this->adapter->insertIfNotExist($table, $input, $compare);
373+
try {
374+
return $this->adapter->insertIfNotExist($table, $input, $compare);
375+
} catch (\Exception $e) {
376+
$this->logDatabaseException($e);
377+
throw $e;
378+
}
360379
}
361380

362381
public function insertIgnoreConflict(string $table, array $values) : int {
363-
return $this->adapter->insertIgnoreConflict($table, $values);
382+
try {
383+
return $this->adapter->insertIgnoreConflict($table, $values);
384+
} catch (\Exception $e) {
385+
$this->logDatabaseException($e);
386+
throw $e;
387+
}
364388
}
365389

366390
private function getType($value) {
@@ -616,4 +640,43 @@ private function getMigrator() {
616640
return new Migrator($this, $config, $dispatcher);
617641
}
618642
}
643+
644+
public function beginTransaction() {
645+
if (!$this->inTransaction()) {
646+
$this->transactionBacktrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
647+
}
648+
return parent::beginTransaction();
649+
}
650+
651+
public function commit() {
652+
$result = parent::commit();
653+
if ($this->getTransactionNestingLevel() === 0) {
654+
$this->transactionBacktrace = null;
655+
}
656+
return $result;
657+
}
658+
659+
public function rollBack() {
660+
$result = parent::rollBack();
661+
if ($this->getTransactionNestingLevel() === 0) {
662+
$this->transactionBacktrace = null;
663+
}
664+
return $result;
665+
}
666+
667+
/**
668+
* Log a database exception if enabled
669+
*
670+
* @param \Exception $exception
671+
* @return void
672+
*/
673+
public function logDatabaseException(\Exception $exception): void {
674+
if ($this->logDbException) {
675+
if ($exception instanceof Exception\UniqueConstraintViolationException) {
676+
$this->logger->info($exception->getMessage(), ['exception' => $exception, 'transaction' => $this->transactionBacktrace]);
677+
} else {
678+
$this->logger->error($exception->getMessage(), ['exception' => $exception, 'transaction' => $this->transactionBacktrace]);
679+
}
680+
}
681+
}
619682
}

lib/private/DB/ConnectionAdapter.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,4 +261,8 @@ public function getDatabaseProvider(): string {
261261
throw new \Exception('Database ' . $platform::class . ' not supported');
262262
}
263263
}
264+
265+
public function logDatabaseException(\Exception $exception) {
266+
$this->inner->logDatabaseException($exception);
267+
}
264268
}

0 commit comments

Comments
 (0)