diff --git a/src/Symfony/Bridge/Monolog/Processor/DebugProcessor.php b/src/Symfony/Bridge/Monolog/Processor/DebugProcessor.php index 22a4faac5cc9a..dbd8f3fb31b97 100644 --- a/src/Symfony/Bridge/Monolog/Processor/DebugProcessor.php +++ b/src/Symfony/Bridge/Monolog/Processor/DebugProcessor.php @@ -16,11 +16,27 @@ class DebugProcessor implements DebugLoggerInterface { + private $channels = array(); + private $channelsExclusive = true; private $records = array(); private $errorCount = 0; + /** + * @param array $channels + * @param bool $exclude + */ + public function setFilterChannels(array $channels, $exclude = true) + { + $this->channels = $channels; + $this->channelsExclusive = (bool) $exclude; + } + public function __invoke(array $record) { + if ($this->isFiltered($record)) { + return $record; + } + $this->records[] = array( 'timestamp' => $record['datetime']->getTimestamp(), 'message' => $record['message'], @@ -40,6 +56,17 @@ public function __invoke(array $record) return $record; } + private function isFiltered(array $record) + { + if ($this->channelsExclusive && !in_array($record['channel'], $this->channels)) { + return false; + } elseif (!$this->channelsExclusive && in_array($record['channel'], $this->channels)) { + return false; + } + + return true; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Bridge/Monolog/Tests/Processor/DebugProcessorTest.php b/src/Symfony/Bridge/Monolog/Tests/Processor/DebugProcessorTest.php new file mode 100644 index 0000000000000..c0cce6727f516 --- /dev/null +++ b/src/Symfony/Bridge/Monolog/Tests/Processor/DebugProcessorTest.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Tests\Processor; + +use Monolog\Handler\TestHandler; +use Monolog\Logger; +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\Monolog\Processor\DebugProcessor; + +class DebugProcessorTest extends TestCase +{ + public function testNoChannelsAreFilteredByDefault() + { + $handler = new TestHandler(); + $processor = new DebugProcessor(); + + $aLogger = new Logger('a', array($handler), array($processor)); + $bLogger = new Logger('b', array($handler), array($processor)); + + $aLogger->info('test A'); + $bLogger->info('test B'); + + $logs = $processor->getLogs(); + $this->assertCount(2, $logs); + + $this->assertEquals('a', $logs[0]['channel']); + $this->assertEquals('test A', $logs[0]['message']); + + $this->assertEquals('b', $logs[1]['channel']); + $this->assertEquals('test B', $logs[1]['message']); + } + + public function testExcludedChannels() + { + $handler = new TestHandler(); + + $processor = new DebugProcessor(); + $processor->setFilterChannels(array('a'), true); + + $aLogger = new Logger('a', array($handler), array($processor)); + $bLogger = new Logger('b', array($handler), array($processor)); + + $aLogger->info('test A'); + $bLogger->info('test B'); + + $logs = $processor->getLogs(); + $this->assertCount(1, $logs); + + $this->assertEquals('b', $logs[0]['channel']); + $this->assertEquals('test B', $logs[0]['message']); + } + + public function testIncludedChannels() + { + $handler = new TestHandler(); + + $processor = new DebugProcessor(); + $processor->setFilterChannels(array('a'), false); + + $aLogger = new Logger('a', array($handler), array($processor)); + $bLogger = new Logger('b', array($handler), array($processor)); + + $aLogger->info('test A'); + $bLogger->info('test B'); + + $logs = $processor->getLogs(); + $this->assertCount(1, $logs); + + $this->assertEquals('a', $logs[0]['channel']); + $this->assertEquals('test A', $logs[0]['message']); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index c089a8b40a743..de4d479909722 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG require symfony/stopwatch` in your `dev` environment. * Deprecated using the `KERNEL_DIR` environment variable with `KernelTestCase::getKernelClass()`. * Deprecated the `KernelTestCase::getPhpUnitXmlDir()` and `KernelTestCase::getPhpUnitCliConfigArgument()` methods. + * Added a configuration to exclude logging channels from the profiler by setting `framework.profiler.log_channels`. 3.3.0 ----- diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 3f9c4eee02421..3d19fa9e1adcd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -17,6 +17,7 @@ use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Form\Form; use Symfony\Component\Serializer\Serializer; use Symfony\Component\Translation\Translator; @@ -219,6 +220,64 @@ private function addProfilerSection(ArrayNodeDefinition $rootNode) ->booleanNode('only_exceptions')->defaultFalse()->end() ->booleanNode('only_master_requests')->defaultFalse()->end() ->scalarNode('dsn')->defaultValue('file:%kernel.cache_dir%/profiler')->end() + ->arrayNode('log_channels') + ->info('list of log channels to handle in profiler') + ->canBeUnset() + ->beforeNormalization() + ->ifString() + ->then(function ($v) { return array('channels' => array($v)); }) + ->end() + ->beforeNormalization() + ->ifTrue(function ($v) { return is_array($v) && is_numeric(key($v)); }) + ->then(function ($v) { return array('channels' => $v); }) + ->end() + ->validate() + ->ifTrue(function ($v) { return empty($v); }) + ->thenUnset() + ->end() + ->validate() + ->always(function ($v) { + $isExclusive = null; + if (isset($v['type'])) { + $isExclusive = 'exclusive' === $v['type']; + } + + $channels = array(); + foreach ($v['channels'] as $channel) { + if (0 === strpos($channel, '!')) { + if (false === $isExclusive) { + throw new InvalidConfigurationException('Cannot combine exclusive/inclusive definitions in profiler log_channels list.'); + } + $channels[] = substr($channel, 1); + $isExclusive = true; + } else { + if (true === $isExclusive) { + throw new InvalidConfigurationException('Cannot combine exclusive/inclusive definitions in profiler log_channels list'); + } + $channels[] = $channel; + $isExclusive = false; + } + } + + if (!count($channels)) { + return null; + } + + return array('type' => $isExclusive ? 'exclusive' : 'inclusive', 'channels' => $channels); + }) + ->end() + ->children() + ->scalarNode('type') + ->validate() + ->ifNotInArray(array('inclusive', 'exclusive')) + ->thenInvalid('The type of log_channels has to be inclusive or exclusive') + ->end() + ->end() + ->arrayNode('channels') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() ->arrayNode('matcher') ->canBeEnabled() ->performNoDeepMerging() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 1769258215475..431885b5252a1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -231,6 +231,7 @@ public function load(array $configs, ContainerBuilder $container) $this->registerCacheConfiguration($config['cache'], $container); $this->registerWorkflowConfiguration($config['workflows'], $container, $loader); $this->registerDebugConfiguration($config['php_errors'], $container, $loader); + $this->registerDebugLoggerConfiguration($config['profiler'], $container); if ($this->isConfigEnabled($container, $config['router'])) { $this->registerRouterConfiguration($config['router'], $container, $loader); @@ -672,10 +673,29 @@ private function registerDebugConfiguration(array $config, ContainerBuilder $con $definition->replaceArgument(4, $debug); $definition->replaceArgument(6, $debug); + } + + /** + * Registers the debug log processor if debug is enabled. + * + * @param array $config + * @param ContainerBuilder $container + */ + private function registerDebugLoggerConfiguration(array $config, ContainerBuilder $container) + { + $debug = $container->getParameter('kernel.debug'); if ($debug && class_exists(DebugProcessor::class)) { $definition = new Definition(DebugProcessor::class); $definition->setPublic(false); + + if (isset($config['log_channels'])) { + $definition->addMethodCall('setChannels', array( + $config['log_channels']['channels'], + $config['log_channels']['type'] === 'exclusive', + )); + } + $container->setDefinition('debug.log_processor', $definition); } }