Skip to content

Commit de8e3d7

Browse files
committed
The way logging works has been revised. Now there are fewer cases where a query is not logged and queries that fail are now included in logging.
1 parent 70b48df commit de8e3d7

File tree

8 files changed

+217
-62
lines changed

8 files changed

+217
-62
lines changed

src/Builder/QueryStatement.php

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,8 @@ public function setFetchMode(int $mode = PDO::FETCH_ASSOC, $arg0 = null, ?array
6363
* @return $this
6464
*/
6565
public function execute(array $params = []) {
66-
$this->exceptionHandler(function() use ($params) {
67-
$timer = microtime(true);
66+
$this->exceptionHandler($this->query, function() use ($params) {
6867
$response = $this->statement->execute($params);
69-
$this->queryLoggers->log($this->query, microtime(true)-$timer);
7068
if(!$response) {
7169
throw new SqlException('Execution returned with "false".');
7270
}
@@ -81,7 +79,7 @@ public function execute(array $params = []) {
8179
* @return array<mixed, mixed>
8280
*/
8381
public function fetchAll($fetchStyle = PDO::FETCH_ASSOC, $fetchArgument = null, array $ctorArgs = []): array {
84-
return $this->exceptionHandler(function() use ($fetchStyle, $fetchArgument, $ctorArgs) {
82+
return $this->exceptionHandler($this->query, function() use ($fetchStyle, $fetchArgument, $ctorArgs) {
8583
if($fetchArgument !== null) {
8684
return $this->statement->fetchAll($fetchStyle, $fetchArgument, ...$ctorArgs);
8785
}
@@ -96,7 +94,7 @@ public function fetchAll($fetchStyle = PDO::FETCH_ASSOC, $fetchArgument = null,
9694
* @return mixed
9795
*/
9896
public function fetch($fetchStyle = PDO::FETCH_ASSOC, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) {
99-
return $this->exceptionHandler(function() use ($fetchStyle, $cursorOrientation, $cursorOffset) {
97+
return $this->exceptionHandler($this->query, function() use ($fetchStyle, $cursorOrientation, $cursorOffset) {
10098
return $this->statement->fetch($fetchStyle, $cursorOrientation, $cursorOffset);
10199
});
102100
}
@@ -106,7 +104,7 @@ public function fetch($fetchStyle = PDO::FETCH_ASSOC, $cursorOrientation = PDO::
106104
* @return mixed
107105
*/
108106
public function fetchColumn($columnNo = 0) {
109-
return $this->exceptionHandler(function() use ($columnNo) {
107+
return $this->exceptionHandler($this->query, function() use ($columnNo) {
110108
return $this->statement->fetchColumn($columnNo);
111109
});
112110
}
@@ -115,7 +113,7 @@ public function fetchColumn($columnNo = 0) {
115113
* @return bool
116114
*/
117115
public function closeCursor(): bool {
118-
return $this->exceptionHandler(function() {
116+
return $this->exceptionHandler($this->query, function() {
119117
return $this->statement->closeCursor();
120118
});
121119
}
@@ -124,7 +122,7 @@ public function closeCursor(): bool {
124122
* @return int
125123
*/
126124
public function columnCount(): int {
127-
return $this->exceptionHandler(function() {
125+
return $this->exceptionHandler($this->query, function() {
128126
return $this->statement->columnCount();
129127
});
130128
}
@@ -134,7 +132,7 @@ public function columnCount(): int {
134132
* @return null|array<string, mixed>
135133
*/
136134
public function getColumnMeta(int $columnNo): ?array {
137-
return $this->exceptionHandler(function() use ($columnNo) {
135+
return $this->exceptionHandler($this->query, function() use ($columnNo) {
138136
$columnMeta = $this->statement->getColumnMeta($columnNo);
139137
if($columnMeta === false) {
140138
return null;
@@ -144,15 +142,17 @@ public function getColumnMeta(int $columnNo): ?array {
144142
}
145143

146144
/**
147-
* @param callable $fn
148-
* @return mixed
145+
* @template T
146+
* @param callable(): T $fn
147+
* @return T
149148
*/
150-
private function exceptionHandler(callable $fn) {
151-
try {
152-
return $fn();
153-
} catch (PDOException $e) {
154-
$this->exceptionInterpreter->throwMoreConcreteException($e);
155-
}
156-
return null;
149+
private function exceptionHandler(string $query, callable $fn) {
150+
return $this->queryLoggers->logRegion($query, function () use ($fn) {
151+
try {
152+
return $fn();
153+
} catch (PDOException $exception) {
154+
return $this->exceptionInterpreter->getMoreConcreteException($exception);
155+
}
156+
});
157157
}
158158
}

src/Databases/MySQL.php

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,10 @@ public function getVirtualTables(): VirtualTables {
9393
* @return QueryStatement
9494
*/
9595
public function query(string $query) {
96-
return $this->buildQueryStatement($query, function ($query) {
97-
return $this->pdo->query($query);
96+
return $this->getQueryLoggers()->logRegion($query, function() use ($query) {
97+
return $this->buildQueryStatement($query, function ($query) {
98+
return $this->pdo->query($query);
99+
});
98100
});
99101
}
100102

@@ -114,14 +116,16 @@ public function prepare(string $query) {
114116
* @return int
115117
*/
116118
public function exec(string $query, array $params = []): int {
117-
return $this->exceptionHandler(function () use ($query, $params) {
118-
$stmt = $this->pdo->prepare($query);
119-
$timer = microtime(true);
120-
$stmt->execute($params);
121-
$this->queryLoggers->log($query, microtime(true) - $timer);
122-
$result = $stmt->rowCount();
123-
$stmt->closeCursor();
124-
return $result;
119+
return $this->getQueryLoggers()->logRegion($query, function() use ($query, $params) {
120+
return $this->exceptionHandler(function () use ($query, $params) {
121+
$stmt = $this->pdo->prepare($query);
122+
$timer = microtime(true);
123+
$stmt->execute($params);
124+
$this->queryLoggers->log($query, microtime(true) - $timer);
125+
$result = $stmt->rowCount();
126+
$stmt->closeCursor();
127+
return $result;
128+
});
125129
});
126130
}
127131

@@ -138,18 +142,28 @@ public function getLastInsertId(?string $name = null): string {
138142
* @return array<int, string>
139143
*/
140144
public function getTableFields(string $table): array {
141-
$table = $this->select()->aliasReplacer()->replace($table);
142-
if(array_key_exists($table, $this->tableFields)) {
143-
return $this->tableFields[$table];
145+
$fqTable = $this->select()->aliasReplacer()->replace($table);
146+
if(array_key_exists($fqTable, $this->tableFields)) {
147+
return $this->tableFields[$fqTable];
144148
}
145-
$stmt = $this->pdo->query("DESCRIBE {$table}");
146-
if($stmt === false) {
147-
throw new RuntimeException('Invalid return type');
148-
}
149-
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
150-
$this->tableFields[$table] = array_map(static function ($row) { return $row['Field']; }, $rows ?: []);
151-
$stmt->closeCursor();
152-
return $this->tableFields[$table];
149+
$query = "DESCRIBE {$fqTable}";
150+
return $this->getQueryLoggers()->logRegion($query, function() use ($query, $fqTable) {
151+
return $this->exceptionHandler(function () use ($query, $fqTable) {
152+
$stmt = $this->pdo->query($query);
153+
try {
154+
if($stmt === false) {
155+
throw new RuntimeException('Invalid return type');
156+
}
157+
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
158+
$this->tableFields[$fqTable] = array_map(static function ($row) { return $row['Field']; }, $rows ?: []);
159+
return $this->tableFields[$fqTable];
160+
} finally {
161+
try {
162+
$stmt->closeCursor();
163+
} catch (Throwable $e) {}
164+
}
165+
});
166+
});
153167
}
154168

155169
/**
@@ -351,15 +365,15 @@ private function buildQueryStatement(string $query, callable $fn): QueryStatemen
351365
}
352366

353367
/**
354-
* @param callable $fn
355-
* @return mixed
368+
* @template T
369+
* @param callable(): T $fn
370+
* @return T
356371
*/
357372
private function exceptionHandler(callable $fn) {
358373
try {
359374
return $fn();
360-
} catch (PDOException $e) {
361-
$this->exceptionInterpreter->throwMoreConcreteException($e);
375+
} catch (PDOException $exception) {
376+
throw $this->exceptionInterpreter->getMoreConcreteException($exception);
362377
}
363-
return null;
364378
}
365379
}

src/Databases/MySQL/MySQLExceptionInterpreter.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,25 @@
1212
class MySQLExceptionInterpreter {
1313
/**
1414
* @param PDOException $exception
15-
* @throw PDOException
15+
* @return SqlException
1616
*/
17-
public function throwMoreConcreteException(PDOException $exception): void {
17+
public function getMoreConcreteException(PDOException $exception): SqlException {
1818
$errorInfo = $exception->errorInfo;
1919
/** @link http://php.net/manual/en/class.exception.php#Hcom115813 (cHao's comment) */
2020
$code = is_array($errorInfo) && isset($errorInfo[1]) ? ((int) $errorInfo[1]) : ((int) $exception->getCode());
2121
$message = $exception->getMessage();
2222
switch($code) {
23-
case 2006: throw new DatabaseHasGoneAwayException($message, $code, $exception);
24-
case 1213: throw new SqlDeadLockException($message, $code, $exception);
25-
case 1205: throw new LockWaitTimeoutExceededException($message, $code, $exception);
23+
case 2006: return new DatabaseHasGoneAwayException($message, $code, $exception);
24+
case 1213: return new SqlDeadLockException($message, $code, $exception);
25+
case 1205: return new LockWaitTimeoutExceededException($message, $code, $exception);
2626
case 1022:
2727
case 1062:
2828
case 1169:
29-
case 1586: throw new DuplicateUniqueKeyException($message, $code, $exception);
29+
case 1586: return new DuplicateUniqueKeyException($message, $code, $exception);
3030
case 1216:
3131
case 1217:
32-
case 1452: throw new IntegrityConstraintViolationException($message, $code, $exception);
32+
case 1452: return new IntegrityConstraintViolationException($message, $code, $exception);
3333
}
34-
throw new SqlException($message, $code, $exception);
34+
return new SqlException($message, $code, $exception);
3535
}
3636
}
Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,30 @@
11
<?php
22
namespace Kir\MySQL\QueryLogger;
33

4+
use Throwable;
5+
46
class ClosureQueryLogger implements QueryLogger {
5-
/** @var callable */
7+
/** @var callable(string, float, string, Throwable|null): void */
68
private $fn;
79

810
/**
9-
* @param callable $fn
11+
* @param callable(string, float, string, Throwable|null): void $fn
1012
*/
1113
public function __construct(callable $fn) {
1214
$this->fn = $fn;
1315
}
1416

1517
/**
16-
* @param string $query
17-
* @param float $duration Duration in seconds
18-
* @return void
18+
* @inheritDoc
1919
*/
2020
public function log(string $query, float $duration): void {
21-
call_user_func($this->fn, $query, $duration);
21+
call_user_func($this->fn, $query, $duration, 'INFO', null);
22+
}
23+
24+
/**
25+
* @inheritDoc
26+
*/
27+
public function logError(string $query, Throwable $exception, float $duration): void {
28+
call_user_func($this->fn, $query, $duration, 'ERROR', $exception);
2229
}
2330
}

src/QueryLogger/LoggerInterfaceQueryLogger.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
namespace Kir\MySQL\QueryLogger;
33

44
use Psr\Log\LoggerInterface;
5+
use Throwable;
56

67
class LoggerInterfaceQueryLogger implements QueryLogger {
78
/** @var LoggerInterface */
@@ -15,11 +16,16 @@ public function __construct(LoggerInterface $logger) {
1516
}
1617

1718
/**
18-
* @param string $query
19-
* @param float $duration Duration in seconds
20-
* @return void
19+
* @inheritDoc
2120
*/
2221
public function log(string $query, float $duration): void {
2322
$this->logger->info(sprintf("Query %s took %0.4f seconds", $query, $duration), ['query' => $query, 'duration' => $duration]);
2423
}
24+
25+
/**
26+
* @inheritDoc
27+
*/
28+
public function logError(string $query, Throwable $exception, float $duration): void {
29+
$this->logger->error(sprintf("Error'd query %s took %0.4f seconds", $query, $duration), ['query' => $query, 'duration' => $duration, 'exception' => $exception]);
30+
}
2531
}

src/QueryLogger/QueryLogger.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
11
<?php
22
namespace Kir\MySQL\QueryLogger;
33

4+
use Throwable;
5+
46
interface QueryLogger {
57
/**
68
* @param string $query
79
* @param float $duration Duration in seconds
810
* @return void
911
*/
1012
public function log(string $query, float $duration): void;
13+
14+
/**
15+
* @param string $query
16+
* @param Throwable $exception
17+
* @param float $duration Duration in seconds
18+
* @return void
19+
*/
20+
public function logError(string $query, Throwable $exception, float $duration): void;
1121
}

src/QueryLogger/QueryLoggers.php

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<?php
22
namespace Kir\MySQL\QueryLogger;
33

4+
use Throwable;
5+
46
class QueryLoggers {
57
/** @var QueryLogger[] */
68
private $queryLoggers = [];
@@ -12,13 +14,52 @@ public function add(QueryLogger $queryLogger): void {
1214
$this->queryLoggers[] = $queryLogger;
1315
}
1416

17+
/**
18+
* @template T
19+
* @param string $query
20+
* @param callable(): T $fn
21+
* @return T
22+
*/
23+
public function logRegion(string $query, $fn) {
24+
$exception = null;
25+
$timer = microtime(true);
26+
try {
27+
return $fn();
28+
} catch(Throwable $e) {
29+
$exception = $e;
30+
throw $e;
31+
} finally {
32+
$finalTimer = microtime(true) - $timer;
33+
if($exception === null) {
34+
$this->log($query, $finalTimer);
35+
} else {
36+
$this->logError($query, $exception, $finalTimer);
37+
}
38+
}
39+
}
40+
1541
/**
1642
* @param string $query
1743
* @param float $duration
1844
*/
1945
public function log(string $query, float $duration): void {
2046
foreach ($this->queryLoggers as $queryLogger) {
21-
$queryLogger->log($query, $duration);
47+
try {
48+
$queryLogger->log($query, $duration);
49+
} catch (Throwable $e) {}
50+
}
51+
}
52+
53+
/**
54+
* @param string $query
55+
* @param Throwable $exception
56+
* @param float $duration
57+
*/
58+
public function logError(string $query, Throwable $exception, float $duration): void {
59+
foreach ($this->queryLoggers as $queryLogger) {
60+
try {
61+
$queryLogger->logError($query, $exception, $duration);
62+
} catch (Throwable $e) {}
2263
}
2364
}
2465
}

0 commit comments

Comments
 (0)