Skip to content

Commit

Permalink
[Debug] add a screaming mode to ErrorHandler
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolas-grekas committed Mar 17, 2014
1 parent 48c9985 commit 5cc817d
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 28 deletions.
7 changes: 7 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml
Expand Up @@ -32,5 +32,12 @@
<argument>deprecation</argument>
<argument type="service" id="logger" on-invalid="null" />
</service>

<service id="debug.scream_logger_listener" class="%debug.errors_logger_listener.class%">
<tag name="kernel.event_subscriber" />
<tag name="monolog.logger" channel="scream" />
<argument>scream</argument>
<argument type="service" id="logger" on-invalid="null" />
</service>
</services>
</container>
Expand Up @@ -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 %}
<img width="15" height="28" alt="Logs" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAcCAYAAABoMT8aAAAA4klEQVQ4y2P4//8/AyWYYXgYwOPp6Xnc3t7+P7EYpB6k7+zZs2ADNEjRjIwDAgKWgAywIUfz8+fPVzg7O/8AGeCATQEQnAfi/SAah/wcV1dXvAYUgORANA75ehcXl+/4DHAABRIe+ZrhbgAhTHsDiEgHBA0glA6GfSDiw5mZma+A+sphBlhVVFQ88vHx+Xfu3Ll7QP5haOjjwtuAuGHv3r3NIMNABqh8+/atsaur666vr+9XUlwSHx//AGQANxCbAnEWyGQicRMQ9wBxIQM0qjiBWAFqkB00/glhayBWHwb1AgB38EJsUtxtWwAAAABJRU5ErkJggg==" />
{% if collector.counterrors %}
{% set status_color = "red" %}
{% else %}
{% elseif collector.countdeprecations %}
{% set status_color = "yellow" %}
{% endif %}
{% set error_count = collector.counterrors + collector.countdeprecations %}
<span class="sf-toolbar-status sf-toolbar-status-{{ status_color }}">{{ error_count }}</span>
{% set error_count = collector.counterrors + collector.countdeprecations + collector.countscreams %}
<span class="sf-toolbar-status{% if status_color is defined %}} sf-toolbar-status-{{ status_color }}{% endif %}">{{ error_count }}</span>
{% endset %}
{% set text %}
{% if collector.counterrors %}
Expand All @@ -27,6 +27,12 @@
<span class="sf-toolbar-status sf-toolbar-status-yellow">{{ collector.countdeprecations }}</span>
</div>
{% endif %}
{% if collector.countscreams %}
<div class="sf-toolbar-info-piece">
<b>Silenced Errors</b>
<span class="sf-toolbar-status sf-toolbar-status">{{ collector.countscreams }}</span>
</div>
{% endif %}
{% endset %}
{% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': profiler_url } %}
{% endif %}
Expand All @@ -36,8 +42,8 @@
<span class="label">
<span class="icon"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAgCAYAAAAMq2gFAAABjElEQVRIx2MIDw+vd3R0/GFvb/+fGtjFxeVJSUmJ1f///5nv37/PAMMMzs7OVLMEhoODgy/k5+cHJCYmagAtZAJbRG1L0DEwxCYALeOgiUXbt2+/X1NT8xTEdnd3/wi0SI4mFgHBDCBeCLXoF5BtwkCEpvNAvB8JnydCTwgQR0It+g1kWxNjUQEQOyDhAiL0gNUiWWRDjEUOyMkUZsCoRaMWjVpEvEVkFkGjFmEUqgc+fvx4hVYWIReqzi9evKileaoDslnu3LkTNLQtGk3edLPIycnpL9Bge5pb1NXVdQNosDmGRcAm7F+QgKur6783b95cBQoeRGv1kII3QPOdAoZF8+fPP4PUqnx55syZVKCEI1rLh1hsAbWEZ8aMGaUoFoFcMG3atKdIjfSPISEhawICAlaQgwMDA1f6+/sfB5rzE2Sej4/PD3C7DkjoAHHVoUOHLpSVlX3w8vL6Sa34Alr6Z8WKFaCoMARZxAHEoFZ/HBD3A/FyIF4BxMvIxCC964F4G6hZDMTxQCwJAGWE8pur5kFDAAAAAElFTkSuQmCC" alt="Logger"></span>
<strong>Logs</strong>
{% 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 %}
<span class="count">
<span>{{ error_count }}</span>
</span>
Expand Down Expand Up @@ -74,7 +80,7 @@
{% if collector.logs %}
<ul class="alt">
{% for log in collector.logs if priority >= 0 and log.priority >= priority or priority < 0 and log.context.type|default(0) == priority %}
<li class="{{ cycle(['odd', 'even'], loop.index) }}{% if log.priority >= 400 %} error{% elseif log.priority >= 300 %} warning{% endif %}">
<li class="{{ cycle(['odd', 'even'], loop.index) }}{% if log.priority >= 400 %} error{% elseif log.priority >= 300 %} warning{% endif %}{% if log.context.scream is defined %} scream{% endif %}">
{{ logger.display_message(loop.index, log) }}
</li>
{% else %}
Expand Down
Expand Up @@ -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;
Expand Down
38 changes: 32 additions & 6 deletions src/Symfony/Component/Debug/ErrorHandler.php
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -108,7 +109,7 @@ public function setDisplayErrors($displayErrors)
* 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')
{
Expand All @@ -120,10 +121,6 @@ public static function setLogger(LoggerInterface $logger, $channel = 'deprecatio
*/
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) {
Expand Down Expand Up @@ -182,6 +179,35 @@ function ($row) {
}
}

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;
}

Expand Down
22 changes: 22 additions & 0 deletions src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php
Expand Up @@ -174,6 +174,28 @@ public function testHandle()
$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();

Expand Down
Expand Up @@ -46,11 +46,8 @@ public function collect(Request $request, Response $response, \Exception $except
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());
}
}

Expand Down Expand Up @@ -81,6 +78,11 @@ public function countDeprecations()
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}
*/
Expand Down Expand Up @@ -119,12 +121,21 @@ private function sanitizeContext($context)
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'];
}
}
}

Expand Down
Expand Up @@ -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));
Expand All @@ -32,6 +32,7 @@ public function testCollect($nb, $logs, $expectedLogs, $expectedDeprecationCount
$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()
Expand All @@ -41,28 +42,33 @@ public function getCollectTestData()
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,
),
);
}
Expand Down

0 comments on commit 5cc817d

Please sign in to comment.