From 5cc817d1bdb1ca111329352ccf62a86cf7d2300e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 4 Feb 2014 21:08:27 +0100 Subject: [PATCH] [Debug] add a screaming mode to ErrorHandler --- .../Resources/config/debug.xml | 7 ++++ .../views/Collector/logger.html.twig | 20 ++++++---- .../views/Profiler/profiler.css.twig | 3 ++ src/Symfony/Component/Debug/ErrorHandler.php | 38 ++++++++++++++++--- .../Debug/Tests/ErrorHandlerTest.php | 22 +++++++++++ .../DataCollector/LoggerDataCollector.php | 29 +++++++++----- .../DataCollector/LoggerDataCollectorTest.php | 18 ++++++--- 7 files changed, 109 insertions(+), 28 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml index 85cced76d5..875c556574 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml @@ -32,5 +32,12 @@ deprecation + + + + + scream + + diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig index 8f88c4dfcb..07e10f8bc4 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig @@ -3,16 +3,16 @@ {% import _self as logger %} {% block toolbar %} - {% if collector.counterrors or collector.countdeprecations %} + {% if collector.counterrors or collector.countdeprecations or collector.countscreams %} {% set icon %} Logs {% if collector.counterrors %} {% set status_color = "red" %} - {% else %} + {% elseif collector.countdeprecations %} {% set status_color = "yellow" %} {% endif %} - {% set error_count = collector.counterrors + collector.countdeprecations %} - {{ error_count }} + {% set error_count = collector.counterrors + collector.countdeprecations + collector.countscreams %} + {{ error_count }} {% endset %} {% set text %} {% if collector.counterrors %} @@ -27,6 +27,12 @@ {{ collector.countdeprecations }} {% endif %} + {% if collector.countscreams %} +
+ Silenced Errors + {{ collector.countscreams }} +
+ {% endif %} {% endset %} {% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': profiler_url } %} {% endif %} @@ -36,8 +42,8 @@ Logger Logs - {% if collector.counterrors or collector.countdeprecations %} - {% set error_count = collector.counterrors + collector.countdeprecations %} + {% if collector.counterrors or collector.countdeprecations or collector.countscreams %} + {% set error_count = collector.counterrors + collector.countdeprecations + collector.countscreams %} {{ error_count }} @@ -74,7 +80,7 @@ {% if collector.logs %}
    {% for log in collector.logs if priority >= 0 and log.priority >= priority or priority < 0 and log.context.type|default(0) == priority %} -
  • +
  • {{ logger.display_message(loop.index, log) }}
  • {% else %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig index 5cc3ae149f..78e4e20cd6 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig @@ -275,6 +275,9 @@ ul.alt li.warning { background-color: #ffcc00; margin-bottom: 1px; } +ul.alt li.scream, ul.alt li.scream strong { + color: gray; +} ul.sf-call-stack li { text-size: small; padding: 0 0 0 20px; diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php index e72e249f7f..2351893d52 100644 --- a/src/Symfony/Component/Debug/ErrorHandler.php +++ b/src/Symfony/Component/Debug/ErrorHandler.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Debug; +use Psr\Log\LogLevel; use Psr\Log\LoggerInterface; use Symfony\Component\Debug\Exception\ContextErrorException; use Symfony\Component\Debug\Exception\FatalErrorException; @@ -44,7 +45,7 @@ class ErrorHandler E_ERROR => 'Error', E_CORE_ERROR => 'Core Error', E_COMPILE_ERROR => 'Compile Error', - E_PARSE => 'Parse', + E_PARSE => 'Parse Error', ); private $level; @@ -108,7 +109,7 @@ class ErrorHandler * Sets a logger for the given channel. * * @param LoggerInterface $logger A logger interface - * @param string $channel The channel associated with the logger (deprecation or emergency) + * @param string $channel The channel associated with the logger (deprecation, emergency or scream) */ public static function setLogger(LoggerInterface $logger, $channel = 'deprecation') { @@ -120,10 +121,6 @@ class ErrorHandler */ public function handle($level, $message, $file = 'unknown', $line = 0, $context = array()) { - if (0 === $this->level) { - return false; - } - if ($level & (E_USER_DEPRECATED | E_DEPRECATED)) { if (isset(self::$loggers['deprecation'])) { if (self::$stackedErrorLevels) { @@ -182,6 +179,35 @@ class ErrorHandler } } + if (isset(self::$loggers['scream']) && !(error_reporting() & $level)) { + if (self::$stackedErrorLevels) { + self::$stackedErrors[] = func_get_args(); + } else { + switch ($level) { + case E_USER_ERROR: + case E_RECOVERABLE_ERROR: + $logLevel = LogLevel::ERROR; + break; + + case E_WARNING: + case E_USER_WARNING: + $logLevel = LogLevel::WARNING; + break; + + default: + $logLevel = LogLevel::NOTICE; + break; + } + + self::$loggers['scream']->log($logLevel, $message, array( + 'type' => $level, + 'file' => $file, + 'line' => $line, + 'scream' => error_reporting(), + )); + } + } + return false; } diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php index 129797c15a..133dfa7af3 100644 --- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php +++ b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php @@ -174,6 +174,28 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase $handler->handle(E_USER_DEPRECATED, 'foo', 'foo.php', 12, 'foo'); restore_error_handler(); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $that = $this; + $logArgCheck = function ($level, $message, $context) use ($that) { + $that->assertEquals('Undefined variable: undefVar', $message); + $that->assertArrayHasKey('type', $context); + $that->assertEquals($context['type'], E_NOTICE); + }; + + $logger + ->expects($this->once()) + ->method('log') + ->will($this->returnCallback($logArgCheck)) + ; + + $handler = ErrorHandler::register(E_NOTICE); + $handler->setLogger($logger, 'scream'); + unset($undefVar); + @$undefVar++; + + restore_error_handler(); } catch (\Exception $e) { restore_error_handler(); diff --git a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php index ba2ea44c5e..f215a97030 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php @@ -46,11 +46,8 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte public function lateCollect() { if (null !== $this->logger) { - $this->data = array( - 'error_count' => $this->logger->countErrors(), - 'logs' => $this->sanitizeLogs($this->logger->getLogs()), - 'deprecation_count' => $this->computeDeprecationCount() - ); + $this->data = $this->computeErrorsCount(); + $this->data['logs'] = $this->sanitizeLogs($this->logger->getLogs()); } } @@ -81,6 +78,11 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte return isset($this->data['deprecation_count']) ? $this->data['deprecation_count'] : 0; } + public function countScreams() + { + return isset($this->data['scream_count']) ? $this->data['scream_count'] : 0; + } + /** * {@inheritdoc} */ @@ -119,12 +121,21 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte return $context; } - private function computeDeprecationCount() + private function computeErrorsCount() { - $count = 0; + $count = array( + 'error_count' => $this->logger->countErrors(), + 'deprecation_count' => 0, + 'scream_count' => 0, + ); + foreach ($this->logger->getLogs() as $log) { - if (isset($log['context']['type']) && ErrorHandler::TYPE_DEPRECATION === $log['context']['type']) { - $count++; + if (isset($log['context']['type'])) { + if (ErrorHandler::TYPE_DEPRECATION === $log['context']['type']) { + ++$count['deprecation_count']; + } elseif (isset($log['context']['scream'])) { + ++$count['scream_count']; + } } } diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php index 7cd4d06c7a..40c4c5ac60 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php @@ -19,7 +19,7 @@ class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase /** * @dataProvider getCollectTestData */ - public function testCollect($nb, $logs, $expectedLogs, $expectedDeprecationCount) + public function testCollect($nb, $logs, $expectedLogs, $expectedDeprecationCount, $expectedScreamCount) { $logger = $this->getMock('Symfony\Component\HttpKernel\Log\DebugLoggerInterface'); $logger->expects($this->once())->method('countErrors')->will($this->returnValue($nb)); @@ -32,6 +32,7 @@ class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase $this->assertSame($nb, $c->countErrors()); $this->assertSame($expectedLogs ? $expectedLogs : $logs, $c->getLogs()); $this->assertSame($expectedDeprecationCount, $c->countDeprecations()); + $this->assertSame($expectedScreamCount, $c->countScreams()); } public function getCollectTestData() @@ -41,28 +42,33 @@ class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase 1, array(array('message' => 'foo', 'context' => array())), null, - 0 + 0, + 0, ), array( 1, array(array('message' => 'foo', 'context' => array('foo' => fopen(__FILE__, 'r')))), array(array('message' => 'foo', 'context' => array('foo' => 'Resource(stream)'))), - 0 + 0, + 0, ), array( 1, array(array('message' => 'foo', 'context' => array('foo' => new \stdClass()))), array(array('message' => 'foo', 'context' => array('foo' => 'Object(stdClass)'))), - 0 + 0, + 0, ), array( 1, array( array('message' => 'foo', 'context' => array('type' => ErrorHandler::TYPE_DEPRECATION)), - array('message' => 'foo2', 'context' => array('type' => ErrorHandler::TYPE_DEPRECATION)) + array('message' => 'foo2', 'context' => array('type' => ErrorHandler::TYPE_DEPRECATION)), + array('message' => 'foo3', 'context' => array('type' => E_USER_WARNING, 'scream' => 0)), ), null, - 2 + 2, + 1, ), ); }