Skip to content

Commit 8ea6769

Browse files
ste93cryJean85
andauthored
Fix missing instrumentation of the Statement::execute() method of Doctrine DBAL (#548)
Co-authored-by: Alessandro Lai <[email protected]>
1 parent a05a849 commit 8ea6769

13 files changed

+672
-59
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Unreleased
44

5+
- Fix missing instrumentation of the `Statement::execute()` method of Doctrine DBAL (#548)
6+
57
## 4.2.1 (2021-08-24)
68

79
- Fix return type for `TracingDriver::getDatabase()` method (#541)

phpstan-baseline.neon

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -95,31 +95,6 @@ parameters:
9595
count: 1
9696
path: src/Tracing/Doctrine/DBAL/TracingDriverConnection.php
9797

98-
-
99-
message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\FilterControllerEvent not found\\.$#"
100-
count: 1
101-
path: src/aliases.php
102-
103-
-
104-
message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\FilterResponseEvent not found\\.$#"
105-
count: 1
106-
path: src/aliases.php
107-
108-
-
109-
message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseEvent not found\\.$#"
110-
count: 2
111-
path: src/aliases.php
112-
113-
-
114-
message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseForExceptionEvent not found\\.$#"
115-
count: 1
116-
path: src/aliases.php
117-
118-
-
119-
message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\PostResponseEvent not found\\.$#"
120-
count: 1
121-
path: src/aliases.php
122-
12398
-
12499
message: "#^Class Symfony\\\\Bundle\\\\FrameworkBundle\\\\Client not found\\.$#"
125100
count: 1
@@ -280,3 +255,48 @@ parameters:
280255
count: 1
281256
path: tests/Tracing/Doctrine/DBAL/TracingDriverTest.php
282257

258+
-
259+
message: "#^Trying to mock an undefined method closeCursor\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#"
260+
count: 1
261+
path: tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
262+
263+
-
264+
message: "#^Trying to mock an undefined method columnCount\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#"
265+
count: 1
266+
path: tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
267+
268+
-
269+
message: "#^Trying to mock an undefined method errorCode\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#"
270+
count: 1
271+
path: tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
272+
273+
-
274+
message: "#^Trying to mock an undefined method errorInfo\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#"
275+
count: 1
276+
path: tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
277+
278+
-
279+
message: "#^Trying to mock an undefined method fetch\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#"
280+
count: 1
281+
path: tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
282+
283+
-
284+
message: "#^Trying to mock an undefined method fetchAll\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#"
285+
count: 1
286+
path: tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
287+
288+
-
289+
message: "#^Trying to mock an undefined method fetchColumn\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#"
290+
count: 1
291+
path: tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
292+
293+
-
294+
message: "#^Trying to mock an undefined method rowCount\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#"
295+
count: 1
296+
path: tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
297+
298+
-
299+
message: "#^Trying to mock an undefined method setFetchMode\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#"
300+
count: 1
301+
path: tests/Tracing/Doctrine/DBAL/TracingStatementForV2Test.php
302+

phpstan.neon

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@ parameters:
66
paths:
77
- src
88
- tests
9-
excludes_analyse:
9+
bootstrapFiles:
10+
- src/aliases.php
11+
excludePaths:
12+
- src/aliases.php
13+
- src/Tracing/Doctrine/DBAL/TracingStatementForV2.php
1014
- tests/End2End/App
1115
dynamicConstantNames:
1216
- Symfony\Component\HttpKernel\Kernel::VERSION
1317
- Symfony\Component\HttpKernel\Kernel::VERSION_ID
14-
- Doctrine\DBAL\Version::VERSION
1518
stubFiles:
1619
- tests/Stubs/Profile.phpstub
20+
featureToggles:
21+
disableRuntimeReflectionProvider: true

psalm-baseline.xml

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<files psalm-version="4.8.1@f73f2299dbc59a3e6c4d66cff4605176e728ee69">
2+
<files psalm-version="4.9.3@4c262932602b9bbab5020863d1eb22d49de0dbf4">
33
<file src="src/EventListener/ConsoleCommandListener.php">
44
<InvalidExtendClass occurrences="1">
55
<code>ConsoleListener</code>
@@ -8,4 +8,38 @@
88
<code>public function __construct(HubInterface $hub, bool $captureErrors = true)</code>
99
</MethodSignatureMismatch>
1010
</file>
11+
<file src="src/Tracing/Doctrine/DBAL/TracingStatementForV2.php">
12+
<InvalidReturnStatement occurrences="2">
13+
<code>$this-&gt;decoratedStatement</code>
14+
<code>$this-&gt;traceFunction($spanContext, [$this-&gt;decoratedStatement, 'execute'], $params)</code>
15+
</InvalidReturnStatement>
16+
<InvalidReturnType occurrences="2">
17+
<code>\Traversable</code>
18+
<code>bool</code>
19+
</InvalidReturnType>
20+
<MethodSignatureMismatch occurrences="1">
21+
<code>TracingStatementForV2</code>
22+
</MethodSignatureMismatch>
23+
<UndefinedInterfaceMethod occurrences="9">
24+
<code>closeCursor</code>
25+
<code>columnCount</code>
26+
<code>errorCode</code>
27+
<code>errorInfo</code>
28+
<code>fetch</code>
29+
<code>fetchAll</code>
30+
<code>fetchColumn</code>
31+
<code>rowCount</code>
32+
<code>setFetchMode</code>
33+
</UndefinedInterfaceMethod>
34+
</file>
35+
<file src="src/aliases.php">
36+
<UndefinedClass occurrences="6">
37+
<code>FilterControllerEvent</code>
38+
<code>FilterResponseEvent</code>
39+
<code>GetResponseEvent</code>
40+
<code>GetResponseEvent</code>
41+
<code>GetResponseForExceptionEvent</code>
42+
<code>PostResponseEvent</code>
43+
</UndefinedClass>
44+
</file>
1145
</files>

src/DependencyInjection/Compiler/DbalTracingPass.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
namespace Sentry\SentryBundle\DependencyInjection\Compiler;
66

7-
use Doctrine\DBAL\Driver\ResultStatement;
87
use Doctrine\DBAL\Result;
98
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\ConnectionConfigurator;
109
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverMiddleware;
@@ -57,7 +56,7 @@ public function process(ContainerBuilder $container): void
5756
throw new \InvalidArgumentException(sprintf('The Doctrine connection "%s" does not exists and cannot be instrumented.', $connectionName));
5857
}
5958

60-
if (!interface_exists(ResultStatement::class)) {
59+
if (class_exists(Result::class)) {
6160
$this->configureConnectionForDoctrineDBALVersion3($container, $connectionName);
6261
} else {
6362
$this->configureConnectionForDoctrineDBALVersion2($container, $connectionName);
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sentry\SentryBundle\Tracing\Doctrine\DBAL;
6+
7+
use Doctrine\DBAL\Driver\Statement;
8+
use Sentry\State\HubInterface;
9+
use Sentry\Tracing\Span;
10+
use Sentry\Tracing\SpanContext;
11+
12+
abstract class AbstractTracingStatement
13+
{
14+
/**
15+
* @internal
16+
*/
17+
public const SPAN_OP_STMT_EXECUTE = 'sql.stmt.execute';
18+
19+
/**
20+
* @var HubInterface The current hub
21+
*/
22+
protected $hub;
23+
24+
/**
25+
* @var Statement The decorated statement
26+
*/
27+
protected $decoratedStatement;
28+
29+
/**
30+
* @var string The SQL query executed by the decorated statement
31+
*/
32+
protected $sqlQuery;
33+
34+
/**
35+
* @var array<string, string> The span tags
36+
*/
37+
protected $spanTags;
38+
39+
/**
40+
* Constructor.
41+
*
42+
* @param HubInterface $hub The current hub
43+
* @param Statement $decoratedStatement The decorated statement
44+
* @param string $sqlQuery The SQL query executed by the decorated statement
45+
* @param array<string, string> $spanTags The span tags
46+
*/
47+
public function __construct(HubInterface $hub, Statement $decoratedStatement, string $sqlQuery, array $spanTags)
48+
{
49+
$this->hub = $hub;
50+
$this->decoratedStatement = $decoratedStatement;
51+
$this->sqlQuery = $sqlQuery;
52+
$this->spanTags = $spanTags;
53+
}
54+
55+
/**
56+
* Calls the given callback by passing to it the specified arguments and
57+
* wrapping its execution into a child {@see Span} of the current one.
58+
*
59+
* @param callable $callback The function to call
60+
* @param mixed ...$args The arguments to pass to the callback
61+
*
62+
* @phpstan-template T
63+
*
64+
* @phpstan-param callable(mixed...): T $callback
65+
*
66+
* @phpstan-return T
67+
*/
68+
protected function traceFunction(SpanContext $spanContext, callable $callback, ...$args)
69+
{
70+
$span = $this->hub->getSpan();
71+
72+
if (null !== $span) {
73+
$span = $span->startChild($spanContext);
74+
}
75+
76+
try {
77+
return $callback(...$args);
78+
} finally {
79+
if (null !== $span) {
80+
$span->finish();
81+
}
82+
}
83+
}
84+
}

src/Tracing/Doctrine/DBAL/TracingDriverConnection.php

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,6 @@ final class TracingDriverConnection implements DriverConnectionInterface
5858
*/
5959
private $decoratedConnection;
6060

61-
/**
62-
* @var string The name of the database platform
63-
*/
64-
private $databasePlatform;
65-
6661
/**
6762
* @var array<string, string> The tags to attach to the span
6863
*/
@@ -84,18 +79,19 @@ public function __construct(
8479
) {
8580
$this->hub = $hub;
8681
$this->decoratedConnection = $decoratedConnection;
87-
$this->databasePlatform = $databasePlatform;
88-
$this->spanTags = $this->getSpanTags($params);
82+
$this->spanTags = $this->getSpanTags($databasePlatform, $params);
8983
}
9084

9185
/**
9286
* {@inheritdoc}
9387
*/
9488
public function prepare($sql): Statement
9589
{
96-
return $this->traceFunction(self::SPAN_OP_CONN_PREPARE, $sql, function () use ($sql): Statement {
90+
$statement = $this->traceFunction(self::SPAN_OP_CONN_PREPARE, $sql, function () use ($sql): Statement {
9791
return $this->decoratedConnection->prepare($sql);
9892
});
93+
94+
return new TracingStatement($this->hub, $statement, $sql, $this->spanTags);
9995
}
10096

10197
/**
@@ -225,15 +221,16 @@ private function traceFunction(string $spanOperation, string $spanDescription, \
225221
/**
226222
* Gets a map of key-value pairs that will be set as tags of the span.
227223
*
228-
* @param array<string, mixed> $params The connection params
224+
* @param string $databasePlatform The database platform
225+
* @param array<string, mixed> $params The connection params
229226
*
230227
* @return array<string, string>
231228
*
232229
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md
233230
*/
234-
private function getSpanTags(array $params): array
231+
private function getSpanTags(string $databasePlatform, array $params): array
235232
{
236-
$tags = ['db.system' => $this->databasePlatform];
233+
$tags = ['db.system' => $databasePlatform];
237234

238235
if (isset($params['user'])) {
239236
$tags['db.user'] = $params['user'];

0 commit comments

Comments
 (0)