Skip to content

Commit

Permalink
feature #10921 [Debug] generic ErrorHandler (nicolas-grekas)
Browse files Browse the repository at this point in the history
This PR was merged into the 2.6-dev branch.

Discussion
----------

[Debug] generic ErrorHandler

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | minor, see updated tests
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | none
| License       | MIT
| Doc PR        | none

The proposed goal of this PR is to build a class that can serve as a foundation for a standard error handler, shared with other projects and not only used by the FrameworkBundle.

This is a merge of my previous work on the subject (https://github.com/nicolas-grekas/Patchwork-Error-Logger/blob/master/class/Patchwork/PHP/InDepthErrorHandler.php) and recent improvements of error handling in Symfony's Debug\ErrorHandler.

ExceptionHandler has a new AbstractExceptionHandler base class that factors out the handling of fatal errors casted to exceptions.

ErrorHandler is introduced, which provides five bit fields that control how errors are handled:

- thrownErrors: errors thrown as ContextErrorException
- loggedErrors: logged errors, when not @-silenced
- scopedErrors: errors thrown or logged with their local context
- tracedErrors: errors logged with their trace, only once for repeated errors
- screamedErrors: never @-silenced errors

Each error level can be logged by a dedicated PSR-3 logger object.
Screaming only applies to logging.
Throwing takes precedence over logging.
Uncaught exceptions are logged as E_ERROR.
E_DEPRECATED and E_USER_DEPRECATED levels never throw.
E_RECOVERABLE_ERROR and E_USER_ERROR levels always throw.
Non catchable errors that can be detected at shutdown time are logged when the scream bit field allows so.
As errors have a performance cost, repeated errors are all logged, so that the developer
can see them and weight them as more important to fix than others of the same level.

- [x] build a more generic ErrorHandler
- [x] update service/listeners definitions to take advantage of the new interface of ErrorHandler
- [x] add phpdocs
- [x] add tests

Commits
-------

1701447 [Debug] update FrameworkBundle and HttpKernel for new ErrorHandler
839e9ac [Debug] generalized ErrorHandler
  • Loading branch information
nicolas-grekas committed Jun 16, 2014
2 parents 4a93d7f + 1701447 commit d55fe84
Show file tree
Hide file tree
Showing 21 changed files with 863 additions and 261 deletions.
Expand Up @@ -61,6 +61,9 @@ public function load(array $configs, ContainerBuilder $container)
if ($container->getParameter('kernel.debug')) {
$loader->load('debug.xml');

$definition = $container->findDefinition('debug.debug_handlers_listener');
$definition->replaceArgument(0, array(new Reference('http_kernel', ContainerInterface::NULL_ON_INVALID_REFERENCE), 'terminateWithException'));

$definition = $container->findDefinition('http_kernel');
$definition->replaceArgument(2, new Reference('debug.controller_resolver'));

Expand All @@ -69,6 +72,9 @@ public function load(array $configs, ContainerBuilder $container)
$definition->setPublic(false);
$container->setDefinition('debug.event_dispatcher.parent', $definition);
$container->setAlias('event_dispatcher', 'debug.event_dispatcher');
} else {
$definition = $container->findDefinition('debug.debug_handlers_listener');
$definition->replaceArgument(2, E_COMPILE_ERROR | E_PARSE | E_ERROR | E_CORE_ERROR);
}

$configuration = $this->getConfiguration($configs, $container);
Expand Down
23 changes: 0 additions & 23 deletions src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml
Expand Up @@ -9,7 +9,6 @@
<parameter key="debug.stopwatch.class">Symfony\Component\Stopwatch\Stopwatch</parameter>
<parameter key="debug.container.dump">%kernel.cache_dir%/%kernel.container_class%.xml</parameter>
<parameter key="debug.controller_resolver.class">Symfony\Component\HttpKernel\Controller\TraceableControllerResolver</parameter>
<parameter key="debug.debug_handlers_listener.class">Symfony\Component\HttpKernel\EventListener\DebugHandlersListener</parameter>
</parameters>

<services>
Expand All @@ -26,27 +25,5 @@
<argument type="service" id="controller_resolver" />
<argument type="service" id="debug.stopwatch" />
</service>

<service id="debug.deprecation_logger_listener" class="%debug.errors_logger_listener.class%">
<tag name="kernel.event_subscriber" />
<tag name="monolog.logger" channel="deprecation" />
<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>

<service id="debug.debug_handlers_listener" class="%debug.debug_handlers_listener.class%">
<tag name="kernel.event_subscriber" />
<argument type="collection">
<argument type="service" id="http_kernel" on-invalid="null" />
<argument>terminateWithException</argument>
</argument>
</service>
</services>
</container>
Expand Up @@ -5,15 +5,17 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<parameters>
<parameter key="debug.errors_logger_listener.class">Symfony\Component\HttpKernel\EventListener\ErrorsLoggerListener</parameter>
<parameter key="debug.debug_handlers_listener.class">Symfony\Component\HttpKernel\EventListener\DebugHandlersListener</parameter>
</parameters>

<services>
<service id="debug.emergency_logger_listener" class="%debug.errors_logger_listener.class%">
<service id="debug.debug_handlers_listener" class="%debug.debug_handlers_listener.class%">
<tag name="kernel.event_subscriber" />
<tag name="monolog.logger" channel="emergency" />
<argument>emergency</argument>
<tag name="monolog.logger" channel="php" />
<argument /><!-- Exception handler -->
<argument type="service" id="logger" on-invalid="null" />
<argument /><!-- Log levels map for enabled error levels -->
<argument>%kernel.debug%</argument>
</service>
</services>
</container>
2 changes: 1 addition & 1 deletion src/Symfony/Bundle/FrameworkBundle/composer.json
Expand Up @@ -21,7 +21,7 @@
"symfony/config" : "~2.4",
"symfony/event-dispatcher": "~2.5",
"symfony/http-foundation": "~2.4",
"symfony/http-kernel": "~2.5",
"symfony/http-kernel": "~2.6",
"symfony/filesystem": "~2.3",
"symfony/routing": "~2.2",
"symfony/security-core": "~2.4",
Expand Down
Expand Up @@ -105,7 +105,7 @@


{% macro display_message(log_index, log) %}
{% if constant('Symfony\\Component\\HttpKernel\\Debug\\ErrorHandler::TYPE_DEPRECATION') == log.context.type|default(0) %}
{% if log.context.level is defined and log.context.type is defined and (constant('E_DEPRECATED') == log.context.type or constant('E_USER_DEPRECATED') == log.context.type) %}
DEPRECATION - {{ log.message }}
{% set id = 'sf-call-stack-' ~ log_index %}
<a href="#" onclick="Sfjs.toggle('{{ id }}', document.getElementById('{{ id }}-on'), document.getElementById('{{ id }}-off')); return false;">
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Component/Debug/CHANGELOG.md
Expand Up @@ -4,6 +4,8 @@ CHANGELOG
2.6.0
-----

* generalized ErrorHandler and ExceptionHandler,
with some new methods and others deprecated
* enhanced error messages for uncaught exceptions

2.5.0
Expand Down
14 changes: 11 additions & 3 deletions src/Symfony/Component/Debug/Debug.php
Expand Up @@ -39,15 +39,23 @@ public static function enable($errorReportingLevel = null, $displayErrors = true

static::$enabled = true;

error_reporting(-1);
if (null !== $errorReportingLevel) {
error_reporting($errorReportingLevel);
} else {
error_reporting(-1);
}

ErrorHandler::register($errorReportingLevel, $displayErrors);
if ('cli' !== php_sapi_name()) {
ini_set('display_errors', 0);
ExceptionHandler::register();
// CLI - display errors only if they're not already logged to STDERR
} elseif ($displayErrors && (!ini_get('log_errors') || ini_get('error_log'))) {
// CLI - display errors only if they're not already logged to STDERR
ini_set('display_errors', 1);
}
$handler = ErrorHandler::register();
if (!$displayErrors) {
$handler->throwAt(0, true);
}

DebugClassLoader::enable();
}
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Component/Debug/DebugClassLoader.php
Expand Up @@ -78,7 +78,7 @@ public function getClassLoader()
public static function enable()
{
// Ensures we don't hit https://bugs.php.net/42098
class_exists(__NAMESPACE__.'\ErrorHandler', true);
class_exists('Symfony\Component\Debug\ErrorHandler');

if (!is_array($functions = spl_autoload_functions())) {
return;
Expand Down

0 comments on commit d55fe84

Please sign in to comment.