Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add doctrine dbal tracing support that is optional to enable #426

Merged
merged 16 commits into from
Feb 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/static-analysis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ jobs:

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '7.4'

- name: Install dependencies
run: composer update --no-progress --no-interaction --prefer-dist
Expand Down
56 changes: 56 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,59 @@ jobs:
with:
file: './coverage.xml'
fail_ci_if_error: true

missing-optional-packages-tests:
name: Tests without optional packages
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php:
- '7.2'
- '8.0'
dependencies:
- lowest
- highest

steps:
- name: Checkout
uses: actions/checkout@v2

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: xdebug

- name: Setup Problem Matchers for PHPUnit
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"

- name: Determine Composer cache directory
id: composer-cache
run: echo "::set-output name=directory::$(composer config cache-dir)"

- name: Cache Composer dependencies
uses: actions/cache@v2
with:
path: ${{ steps.composer-cache.outputs.directory }}
key: ${{ runner.os }}-${{ matrix.php }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-${{ matrix.php }}-composer-${{ matrix.dependencies }}-

- name: Remove optional packages
run: composer remove doctrine/dbal doctrine/doctrine-bundle symfony/messenger --dev --no-update

- name: Install highest dependencies
run: composer update --no-progress --no-interaction --prefer-dist
if: ${{ matrix.dependencies == 'highest' }}

- name: Install lowest dependencies
run: composer update --no-progress --no-interaction --prefer-dist --prefer-lowest
if: ${{ matrix.dependencies == 'lowest' }}

- name: Run tests
run: vendor/bin/phpunit --coverage-clover=build/coverage-report.xml

- name: Upload code coverage
uses: codecov/codecov-action@v1
with:
file: build/coverage-report.xml
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

- Add support for distributed tracing of SQL queries while using Doctrine DBAL (#426)
- Added missing `capture-soft-fails` config schema option (#417)
- Deprecate the `Sentry\SentryBundle\EventListener\ConsoleCommandListener` class in favor of its parent class `Sentry\SentryBundle\EventListener\ConsoleListener` (#429)

Expand Down
26 changes: 15 additions & 11 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,18 @@
"jean85/pretty-package-versions": "^1.5 || ^2.0",
"php-http/discovery": "^1.11",
"sentry/sdk": "^3.1",
"symfony/config": "^3.4.43||^4.4.11||^5.0.11",
"symfony/console": "^3.4.43||^4.4.11||^5.0.11",
"symfony/dependency-injection": "^3.4.43||^4.4.11||^5.0.11",
"symfony/event-dispatcher": "^3.4.43||^4.4.11||^5.0.11",
"symfony/http-kernel": "^3.4.43||^4.4.11||^5.0.11",
"symfony/config": "^3.4.44||^4.4.11||^5.0.11",
"symfony/console": "^3.4.44||^4.4.11||^5.0.11",
"symfony/dependency-injection": "^3.4.44||^4.4.11||^5.0.11",
"symfony/event-dispatcher": "^3.4.44||^4.4.11||^5.0.11",
"symfony/http-kernel": "^3.4.44||^4.4.11||^5.0.11",
"symfony/psr-http-message-bridge": "^2.0",
"symfony/security-core": "^3.4.43||^4.4.11||^5.0.11"
"symfony/security-core": "^3.4.44||^4.4.11||^5.0.11"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.17",
"doctrine/dbal": "^2.10||^3.0",
"doctrine/doctrine-bundle": "^1.12||^2.0",
"friendsofphp/php-cs-fixer": "^2.18",
"jangregor/phpstan-prophecy": "^0.8",
"monolog/monolog": "^1.3||^2.0",
"phpspec/prophecy": "!=1.11.0",
Expand All @@ -41,17 +43,19 @@
"phpstan/phpstan": "^0.12",
"phpstan/phpstan-phpunit": "^0.12",
"phpunit/phpunit": "^8.5||^9.0",
"symfony/browser-kit": "^3.4.43||^4.4.11||^5.0.11",
"symfony/framework-bundle": "^3.4.43||^4.4.11||^5.0.11",
"symfony/browser-kit": "^3.4.44||^4.4.12||^5.0.11",
"symfony/dom-crawler": "^3.4.44||^4.4.12||^5.0.11",
"symfony/framework-bundle": "^3.4.44||^4.4.12||^5.0.11",
"symfony/messenger": "^4.4.11||^5.0.11",
"symfony/monolog-bundle": "^3.4",
"symfony/phpunit-bridge": "^5.0",
"symfony/polyfill-php80": "^1.22",
"symfony/yaml": "^3.4.43||^4.4.11||^5.0.11",
"symfony/yaml": "^3.4.44||^4.4.11||^5.0.11",
"vimeo/psalm": "^4.3"
},
"suggest": {
"monolog/monolog": "Allow sending log messages to Sentry by using the included Monolog handler."
"monolog/monolog": "Allow sending log messages to Sentry by using the included Monolog handler.",
"doctrine/doctrine-bundle": "Allow distributed tracing of database queries using Sentry."
},
"autoload": {
"files": [
Expand Down
105 changes: 105 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,91 @@ parameters:
count: 1
path: src/DependencyInjection/Configuration.php

-
message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface\\:\\:end\\(\\)\\.$#"
count: 1
path: src/DependencyInjection/Configuration.php

-
message: "#^Else branch is unreachable because previous condition is always true\\.$#"
count: 1
path: src/EventListener/ErrorListener.php

-
message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriver\\:\\:connect\\(\\) has parameter \\$driverOptions with no value type specified in iterable type array\\.$#"
count: 1
path: src/Tracing/Doctrine/DBAL/TracingDriver.php

-
message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriver\\:\\:connect\\(\\) has parameter \\$password with no typehint specified\\.$#"
count: 1
path: src/Tracing/Doctrine/DBAL/TracingDriver.php

-
message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriver\\:\\:connect\\(\\) has parameter \\$username with no typehint specified\\.$#"
count: 1
path: src/Tracing/Doctrine/DBAL/TracingDriver.php

-
message: "#^Call to an undefined method Doctrine\\\\DBAL\\\\Driver\\|Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\Compatibility\\\\ExceptionConverterDriverInterface\\:\\:connect\\(\\)\\.$#"
count: 1
path: src/Tracing/Doctrine/DBAL/TracingDriver.php

-
message: "#^Call to an undefined method Doctrine\\\\DBAL\\\\Driver\\|Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\Compatibility\\\\ExceptionConverterDriverInterface\\:\\:getDatabasePlatform\\(\\)\\.$#"
count: 2
path: src/Tracing/Doctrine/DBAL/TracingDriver.php

-
message: "#^Call to an undefined method Doctrine\\\\DBAL\\\\Driver\\|Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\Compatibility\\\\ExceptionConverterDriverInterface\\:\\:getSchemaManager\\(\\)\\.$#"
count: 1
path: src/Tracing/Doctrine/DBAL/TracingDriver.php

-
message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriver\\:\\:convertException\\(\\) has parameter \\$message with no typehint specified\\.$#"
count: 1
path: src/Tracing/Doctrine/DBAL/TracingDriver.php

-
message: "#^Call to an undefined method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\Compatibility\\\\ExceptionConverterDriverInterface\\:\\:convertException\\(\\)\\.$#"
count: 1
path: src/Tracing/Doctrine/DBAL/TracingDriver.php

-
message: "#^Parameter \\#2 \\$query of class Doctrine\\\\DBAL\\\\Exception\\\\DriverException constructor expects Doctrine\\\\DBAL\\\\Query\\|null, Doctrine\\\\DBAL\\\\Driver\\\\Exception given\\.$#"
count: 1
path: src/Tracing/Doctrine/DBAL/TracingDriver.php

-
message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:prepare\\(\\) has parameter \\$sql with no typehint specified\\.$#"
count: 1
path: src/Tracing/Doctrine/DBAL/TracingDriverConnection.php

-
message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:query\\(\\) has parameter \\$args with no typehint specified\\.$#"
count: 1
path: src/Tracing/Doctrine/DBAL/TracingDriverConnection.php

-
message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:exec\\(\\) has parameter \\$sql with no typehint specified\\.$#"
count: 1
path: src/Tracing/Doctrine/DBAL/TracingDriverConnection.php

-
message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:errorCode\\(\\) has no return typehint specified\\.$#"
count: 1
path: src/Tracing/Doctrine/DBAL/TracingDriverConnection.php

-
message: "#^Call to an undefined method Doctrine\\\\DBAL\\\\Driver\\\\Connection\\:\\:errorCode\\(\\)\\.$#"
count: 1
path: src/Tracing/Doctrine/DBAL/TracingDriverConnection.php

-
message: "#^Method Sentry\\\\SentryBundle\\\\Tracing\\\\Doctrine\\\\DBAL\\\\TracingDriverConnection\\:\\:errorInfo\\(\\) has no return typehint specified\\.$#"
count: 1
path: src/Tracing/Doctrine/DBAL/TracingDriverConnection.php

-
message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseForExceptionEvent not found\\.$#"
count: 1
Expand Down Expand Up @@ -115,3 +195,28 @@ parameters:
count: 2
path: tests/EventListener/SubRequestListenerTest.php

-
message: "#^Trying to mock an undefined method getName\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\.$#"
count: 1
path: tests/Tracing/Doctrine/DBAL/TracingDriverTest.php

-
message: "#^Trying to mock an undefined method getDatabase\\(\\) on class Doctrine\\\\DBAL\\\\Driver\\.$#"
count: 1
path: tests/Tracing/Doctrine/DBAL/TracingDriverTest.php

-
message: "#^Parameter \\#1 \\$driverException of class Doctrine\\\\DBAL\\\\Exception\\\\DriverException constructor expects Doctrine\\\\DBAL\\\\Driver\\\\Exception, string given\\.$#"
count: 1
path: tests/Tracing/Doctrine/DBAL/TracingDriverTest.php

-
message: "#^Parameter \\#2 \\$query of class Doctrine\\\\DBAL\\\\Exception\\\\DriverException constructor expects Doctrine\\\\DBAL\\\\Query\\|null, Doctrine\\\\DBAL\\\\Driver\\\\Exception&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#"
count: 1
path: tests/Tracing/Doctrine/DBAL/TracingDriverTest.php

-
message: "#^Trying to mock an undefined method convertException\\(\\) on class Sentry\\\\SentryBundle\\\\Tests\\\\Tracing\\\\Doctrine\\\\DBAL\\\\StubExceptionConverterDriverInterface\\.$#"
count: 1
path: tests/Tracing/Doctrine/DBAL/TracingDriverTest.php

1 change: 1 addition & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ parameters:
- tests/End2End/App
dynamicConstantNames:
- Symfony\Component\HttpKernel\Kernel::VERSION
- Doctrine\DBAL\Version::VERSION
10 changes: 9 additions & 1 deletion psalm-baseline.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="4.4.1@9fd7a7d885b3a216cff8dec9d8c21a132f275224">
<files psalm-version="4.3.0@6f916553a8de3c5c2483162b6cc01b21b24e4d1d">
<file src="src/EventListener/ConsoleCommandListener.php">
<InvalidExtendClass occurrences="1">
<code>ConsoleListener</code>
Expand All @@ -8,4 +8,12 @@
<code>public function __construct(HubInterface $hub)</code>
</MethodSignatureMismatch>
</file>
<file src="src/Tracing/Doctrine/DBAL/TracingDriver.php">
<TooManyArguments occurrences="1">
<code>getSchemaManager</code>
</TooManyArguments>
<UndefinedClass occurrences="1">
<code>ExceptionConverter</code>
</UndefinedClass>
</file>
</files>
87 changes: 87 additions & 0 deletions src/DependencyInjection/Compiler/DbalTracingPass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

declare(strict_types=1);

namespace Sentry\SentryBundle\DependencyInjection\Compiler;

use Doctrine\DBAL\Driver\ResultStatement;
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\ConnectionConfigurator;
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverMiddleware;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

final class DbalTracingPass implements CompilerPassInterface
{
/**
* This is the format used by the DoctrineBundle bundle to register the
* services for each connection.
*/
private const CONNECTION_SERVICE_NAME_FORMAT = 'doctrine.dbal.%s_connection';

/**
* This is the format used by the DoctrineBundle bundle to register the
* services for each connection's configuration.
*/
private const CONNECTION_CONFIG_SERVICE_NAME_FORMAT = 'doctrine.dbal.%s_connection.configuration';

/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container): void
{
if (!$container->hasParameter('doctrine.connections')) {
return;
}

/** @var string[] $connections */
$connections = $container->getParameter('doctrine.connections');

/** @var string[] $connectionsToTrace */
$connectionsToTrace = $container->getParameter('sentry.tracing.dbal.connections');

foreach ($connectionsToTrace as $connectionName) {
if (!\in_array(sprintf(self::CONNECTION_SERVICE_NAME_FORMAT, $connectionName), $connections, true)) {
continue;
}

if (!interface_exists(ResultStatement::class)) {
$this->configureConnectionForDoctrineDBALVersion3($container, $connectionName);
} else {
$this->configureConnectionForDoctrineDBALVersion2($container, $connectionName);
}
}
}

private function configureConnectionForDoctrineDBALVersion3(ContainerBuilder $container, string $connectionName): void
{
$configurationDefinition = $container->getDefinition(sprintf(self::CONNECTION_CONFIG_SERVICE_NAME_FORMAT, $connectionName));
$setMiddlewaresMethodCallArguments = $this->getSetMiddlewaresMethodCallArguments($configurationDefinition);
$setMiddlewaresMethodCallArguments[0] = array_merge($setMiddlewaresMethodCallArguments[0] ?? [], [new Reference(TracingDriverMiddleware::class)]);

$configurationDefinition
->removeMethodCall('setMiddlewares')
->addMethodCall('setMiddlewares', $setMiddlewaresMethodCallArguments);
}

private function configureConnectionForDoctrineDBALVersion2(ContainerBuilder $container, string $connectionName): void
{
$connectionDefinition = $container->getDefinition(sprintf(self::CONNECTION_SERVICE_NAME_FORMAT, $connectionName));
$connectionDefinition->setConfigurator([new Reference(ConnectionConfigurator::class), 'configure']);
}

/**
* @return mixed[]
*/
private function getSetMiddlewaresMethodCallArguments(Definition $definition): array
{
foreach ($definition->getMethodCalls() as $methodCall) {
if ('setMiddlewares' === $methodCall[0]) {
return $methodCall[1];
}
}

return [];
}
}
Loading