merged branch stof/doctrine_profiling (PR #2895)

Commits
-------

8713c2d [DoctrineBridge][DoctrineBundle] Refactored the DBAL logging

Discussion
----------

[DoctrineBridge][DoctrineBundle] Refactored the DBAL logging

This allows enabling the logging and the profiling separately. This is useful for instance when doing batch processing leading to memory issue because of the profiling. In such case, the only solution currently is to disable the logging totally whereas disabling only the use of the profiler would allow seeing the queries in the logs (and the profiles are not collected in the CLI anyway).

I'm not sure about the place where the ``Stopwatch`` should be used. Keeping it in the ``DbalLogger`` with Monolog was the easy way as it allows using the DBAL class directly to collect queries for the profiler but technically the ``Stopwatch`` is used by the profiler.

the bundle changes are part of the PR to avoid letting the bundle in a broken state. I will also submit them to the doctrine/DoctrineBundle repo

---------------------------------------------------------------------------

by fabpot at 2011/12/16 02:33:05 -0800

Tests do not pass for me (even after upgrading the Doctrine deps to their latest versions):

    There was 1 error:

    1) Symfony\Bundle\DoctrineBundle\Tests\ContainerTest::testContainer
    Argument 1 passed to Doctrine\DBAL\Logging\LoggerChain::addLogger() must implement interface Doctrine\DBAL\Logging\SQLLogger, instance of Doctrine\Dbal\Logging\DebugStack given

    vendor/doctrine-dbal/lib/Doctrine/DBAL/Logging/LoggerChain.php:39
    src/Symfony/Component/DependencyInjection/ContainerBuilder.php:777
    src/Symfony/Component/DependencyInjection/ContainerBuilder.php:349
    src/Symfony/Bundle/DoctrineBundle/Tests/ContainerTest.php:22

---------------------------------------------------------------------------

by stof at 2011/12/16 04:24:46 -0800

this is weird. DebugStack implements the interface, and the test passes for me

---------------------------------------------------------------------------

by fabpot at 2011/12/16 05:30:11 -0800

actually, the test pass when I run the `ContainerTest.php` file alone, but fail when I'm running the whole test suite.

---------------------------------------------------------------------------

by stof at 2011/12/16 05:39:15 -0800

I'm not able to run the whole testsuite. With intl enabled, it fails before the first test somewhere in the Locale component tests (Intl seems to be broken on 5.3.8 on windows as using the constructor of the intl classes gives me ``null`` in the variable). And after disabling intl, I got an error about allowed memory size exhausted during the EntityType tests

---------------------------------------------------------------------------

by stloyd at 2011/12/16 05:42:09 -0800

@stof And here goes Travis with help! ;-)

Just log in there, enable hook for your symfony repo, force an push, and watch result at: http://travis-ci.org/#!/stof/symfony

---------------------------------------------------------------------------

by stof at 2011/12/16 05:47:57 -0800

Note: when running only the testsuite for bundles, I also get such an error after about 450 of the 500 tests. It seems like the garbage collector does not clean the memory between tests...

---------------------------------------------------------------------------

by stof at 2011/12/16 05:52:08 -0800

anyway, the error seems really weird as the class implements the interface. I don't see how it could be passed without implementing it

---------------------------------------------------------------------------

by stof at 2011/12/16 06:10:43 -0800

@stloyd Travis allows me to see that this issue is not specific to @fabpot's computer. But it does not allow me to debug the test as I only get the result of the test, which does not make any sense
This commit is contained in:
Fabien Potencier 2011-12-17 10:46:03 +01:00
commit 4a88287b29
11 changed files with 98 additions and 19 deletions

View File

@ -12,10 +12,10 @@
namespace Symfony\Bridge\Doctrine\DataCollector;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\DBAL\Logging\DebugStack;
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bridge\Doctrine\Logger\DbalLogger;
/**
* DoctrineDataCollector.
@ -28,7 +28,7 @@ class DoctrineDataCollector extends DataCollector
private $managers;
private $logger;
public function __construct(ManagerRegistry $registry, DbalLogger $logger = null)
public function __construct(ManagerRegistry $registry, DebugStack $logger = null)
{
$this->connections = $registry->getConnectionNames();
$this->managers = $registry->getManagerNames();

View File

@ -13,14 +13,14 @@ namespace Symfony\Bridge\Doctrine\Logger;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Debug\Stopwatch;
use Doctrine\DBAL\Logging\DebugStack;
use Doctrine\DBAL\Logging\SQLLogger;
/**
* DbalLogger.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DbalLogger extends DebugStack
class DbalLogger implements SQLLogger
{
protected $logger;
protected $stopwatch;
@ -42,8 +42,6 @@ class DbalLogger extends DebugStack
*/
public function startQuery($sql, array $params = null, array $types = null)
{
parent::startQuery($sql, $params, $types);
if (null !== $this->stopwatch) {
$this->stopwatch->start('doctrine', 'doctrine');
}
@ -58,8 +56,6 @@ class DbalLogger extends DebugStack
*/
public function stopQuery()
{
parent::stopQuery();
if (null !== $this->stopwatch) {
$this->stopwatch->stop('doctrine');
}

View File

@ -78,6 +78,7 @@ class Configuration implements ConfigurationInterface
'platform_service',
'charset',
'logging',
'profiling',
'mapping_types',
) as $key) {
if (array_key_exists($key, $v)) {
@ -130,6 +131,7 @@ class Configuration implements ConfigurationInterface
->scalarNode('platform_service')->end()
->scalarNode('charset')->end()
->booleanNode('logging')->defaultValue($this->debug)->end()
->booleanNode('profiling')->defaultValue($this->debug)->end()
->scalarNode('driver_class')->end()
->scalarNode('wrapper_class')->end()
->arrayNode('options')

View File

@ -92,9 +92,18 @@ class DoctrineExtension extends AbstractDoctrineExtension
{
// configuration
$configuration = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name), new DefinitionDecorator('doctrine.dbal.connection.configuration'));
if (isset($connection['logging']) && $connection['logging']) {
$configuration->addMethodCall('setSQLLogger', array(new Reference('doctrine.dbal.logger')));
unset ($connection['logging']);
$logger = null;
if ($connection['logging']) {
$logger = new Reference('doctrine.dbal.logger');
}
unset ($connection['logging']);
if ($connection['profiling']) {
$logger = $logger ? new Reference('doctrine.dbal.logger.chain') : new Reference('doctrine.dbal.logger.profiling');
}
unset($connection['profiling']);
if ($logger) {
$configuration->addMethodCall('setSQLLogger', array($logger));
}
// event manager

View File

@ -5,7 +5,8 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="doctrine.dbal.logger.debug.class">Doctrine\DBAL\Logging\DebugStack</parameter>
<parameter key="doctrine.dbal.logger.chain.class">Doctrine\DBAL\Logging\LoggerChain</parameter>
<parameter key="doctrine.dbal.logger.profiling.class">Doctrine\DBAL\Logging\DebugStack</parameter>
<parameter key="doctrine.dbal.logger.class">Symfony\Bridge\Doctrine\Logger\DbalLogger</parameter>
<parameter key="doctrine.dbal.configuration.class">Doctrine\DBAL\Configuration</parameter>
<parameter key="doctrine.data_collector.class">Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector</parameter>
@ -19,7 +20,16 @@
</parameters>
<services>
<service id="doctrine.dbal.logger.debug" class="%doctrine.dbal.logger.debug.class%" public="false" />
<service id="doctrine.dbal.logger.chain" class="%doctrine.dbal.logger.chain.class%" public="false">
<call method="addLogger">
<argument type="service" id="doctrine.dbal.logger" />
</call>
<call method="addLogger">
<argument type="service" id="doctrine.dbal.logger.profiling" />
</call>
</service>
<service id="doctrine.dbal.logger.profiling" class="%doctrine.dbal.logger.profiling.class%" public="false" />
<service id="doctrine.dbal.logger" class="%doctrine.dbal.logger.class%" public="false">
<tag name="monolog.logger" channel="doctrine" />
@ -30,7 +40,7 @@
<service id="data_collector.doctrine" class="%doctrine.data_collector.class%" public="false">
<tag name="data_collector" template="DoctrineBundle:Collector:db" id="db" />
<argument type="service" id="doctrine" />
<argument type="service" id="doctrine.dbal.logger" />
<argument type="service" id="doctrine.dbal.logger.profiling" />
</service>
<service id="doctrine.dbal.connection_factory" class="%doctrine.dbal.connection_factory.class%">

View File

@ -29,6 +29,7 @@
<xsd:attribute name="wrapper-class" type="xsd:string" />
<xsd:attribute name="platform-service" type="xsd:string" />
<xsd:attribute name="logging" type="xsd:string" default="false" />
<xsd:attribute name="profiling" type="xsd:string" default="false" />
</xsd:attributeGroup>
<xsd:complexType name="dbal">

View File

@ -18,8 +18,9 @@ class ContainerTest extends TestCase
public function testContainer()
{
$container = $this->createYamlBundleTestContainer();
$this->assertInstanceOf('Doctrine\DBAL\Logging\DebugStack', $container->get('doctrine.dbal.logger.debug'));
$this->assertInstanceOf('Doctrine\DBAL\Logging\DebugStack', $container->get('doctrine.dbal.logger'));
$this->assertInstanceOf('Doctrine\DBAL\Logging\DebugStack', $container->get('doctrine.dbal.logger.profiling'));
$this->assertInstanceOf('Doctrine\DBAL\Logging\LoggerChain', $container->get('doctrine.dbal.logger.chain'));
$this->assertInstanceOf('Symfony\Bridge\Doctrine\Logger\DbalLogger', $container->get('doctrine.dbal.logger'));
$this->assertInstanceOf('Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector', $container->get('data_collector.doctrine'));
$this->assertInstanceOf('Doctrine\DBAL\Configuration', $container->get('doctrine.dbal.default_connection.configuration'));
$this->assertInstanceOf('Doctrine\Common\EventManager', $container->get('doctrine.dbal.default_connection.event_manager'));

View File

@ -218,7 +218,6 @@ abstract class AbstractDoctrineExtensionTest extends TestCase
'user' => 'root',
'password' => null,
'driver' => 'pdo_mysql',
'logging' => false,
'driverOptions' => array(),
),
new Reference('doctrine.dbal.default_connection.configuration'),
@ -258,7 +257,6 @@ abstract class AbstractDoctrineExtensionTest extends TestCase
'password' => 'sqlite_s3cr3t',
'dbname' => 'sqlite_db',
'memory' => true,
'logging' => false,
),
new Reference('doctrine.dbal.default_connection.configuration'),
new Reference('doctrine.dbal.default_connection.event_manager'),
@ -337,6 +335,26 @@ abstract class AbstractDoctrineExtensionTest extends TestCase
$this->assertEquals('%doctrine.orm.cache.array.class%', $definition->getClass());
}
public function testLoadLogging()
{
$container = $this->getContainer();
$loader = new DoctrineExtension();
$container->registerExtension($loader);
$this->loadFromFile($container, 'dbal_logging');
$this->compileContainer($container);
$definition = $container->getDefinition('doctrine.dbal.log_connection.configuration');
$this->assertDICDefinitionMethodCallOnce($definition, 'setSQLLogger', array(new Reference('doctrine.dbal.logger')));
$definition = $container->getDefinition('doctrine.dbal.profile_connection.configuration');
$this->assertDICDefinitionMethodCallOnce($definition, 'setSQLLogger', array(new Reference('doctrine.dbal.logger.profiling')));
$definition = $container->getDefinition('doctrine.dbal.both_connection.configuration');
$this->assertDICDefinitionMethodCallOnce($definition, 'setSQLLogger', array(new Reference('doctrine.dbal.logger.chain')));
}
public function testBundleEntityAliases()
{
$container = $this->getContainer();

View File

@ -0,0 +1,29 @@
<?xml version="1.0" ?>
<srv:container xmlns="http://symfony.com/schema/dic/doctrine"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:srv="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/doctrine http://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd">
<config>
<dbal default-connection="mysql">
<connection
name="log"
logging="true"
profiling="false" />
<connection
name="profile"
logging="false"
profiling="true" />
<connection
name="both"
logging="true"
profiling="true" />
<connection
name="none"
logging="false"
profiling="false" />
</dbal>
</config>
</srv:container>

View File

@ -0,0 +1,13 @@
doctrine:
dbal:
default_connection: mysql
connections:
log:
logging: true
profiling: false
profile:
logging: false
profiling: true
both:
logging: true
profiling: true

View File

@ -123,7 +123,7 @@ class DoctrineDataCollectorTest extends \PHPUnit_Framework_TestCase
->method('getManagerNames')
->will($this->returnValue(array('default' => 'doctrine.orm.default_entity_manager')));
$logger = $this->getMock('Symfony\Bridge\Doctrine\Logger\DbalLogger');
$logger = $this->getMock('Doctrine\Dbal\Logging\DebugStack');
$logger->queries = $queries;
return new DoctrineDataCollector($registry, $logger);