Skip to content

Commit

Permalink
feature #36621 Log deprecations on a dedicated Monolog channel (l-vo)
Browse files Browse the repository at this point in the history
This PR was squashed before being merged into the 5.1-dev branch.

Discussion
----------

Log deprecations on a dedicated Monolog channel

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Tickets       | https://github.com/orgs/symfony/projects/1#card-35233930
| License       | MIT
| Doc PR        |

This PR allows to activate a specific channel for deprecations.

## Base configuration

```yaml
monolog:
    handlers:
        #...
        deprecation:
            type: stream
            path: "%kernel.logs_dir%/%kernel.environment%.deprecations.log"
        deprecation_filter:
            type: filter
            handler: deprecation
            max_level: info
            channels: ["php"]
```

## Deprecation specific channel enabled:

```yaml
monolog:
    channels: ['deprecation']
    handlers:
        #...
        deprecation:
            type: stream
            channels: ["deprecation"]
            path: "%kernel.logs_dir%/%kernel.environment%.deprecations.log"
```

Commits
-------

3d415cb Log deprecations on a dedicated Monolog channel
  • Loading branch information
fabpot committed May 5, 2020
2 parents 09d78cf + 3d415cb commit 55706f7
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 26 deletions.
Expand Up @@ -15,12 +15,13 @@
<tag name="kernel.event_subscriber" />
<tag name="monolog.logger" channel="php" />
<argument>null</argument><!-- Exception handler -->
<argument type="service" id="logger" on-invalid="null" />
<argument type="service" id="monolog.logger.php" on-invalid="null" />
<argument>null</argument><!-- Log levels map for enabled error levels -->
<argument>%debug.error_handler.throw_at%</argument>
<argument>%kernel.debug%</argument>
<argument type="service" id="debug.file_link_formatter" />
<argument>%kernel.debug%</argument>
<argument type="service" id="monolog.logger.deprecation" on-invalid="null" />
</service>

<service id="debug.file_link_formatter" class="Symfony\Component\HttpKernel\Debug\FileLinkFormatter">
Expand Down
Expand Up @@ -425,7 +425,7 @@ public function testEnabledPhpErrorsConfig()
$container = $this->createContainerFromFile('php_errors_enabled');

$definition = $container->getDefinition('debug.debug_handlers_listener');
$this->assertEquals(new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE), $definition->getArgument(1));
$this->assertEquals(new Reference('monolog.logger.php', ContainerInterface::NULL_ON_INVALID_REFERENCE), $definition->getArgument(1));
$this->assertNull($definition->getArgument(2));
$this->assertSame(-1, $container->getParameter('debug.error_handler.throw_at'));
}
Expand All @@ -445,7 +445,7 @@ public function testPhpErrorsWithLogLevel()
$container = $this->createContainerFromFile('php_errors_log_level');

$definition = $container->getDefinition('debug.debug_handlers_listener');
$this->assertEquals(new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE), $definition->getArgument(1));
$this->assertEquals(new Reference('monolog.logger.php', ContainerInterface::NULL_ON_INVALID_REFERENCE), $definition->getArgument(1));
$this->assertSame(8, $definition->getArgument(2));
}

Expand Down
5 changes: 5 additions & 0 deletions src/Symfony/Component/HttpKernel/CHANGELOG.md
@@ -1,6 +1,11 @@
CHANGELOG
=========

5.2.0
-----

* allowed to use a specific logger channel for deprecations

5.1.0
-----

Expand Down
Expand Up @@ -32,6 +32,7 @@ class DebugHandlersListener implements EventSubscriberInterface
{
private $exceptionHandler;
private $logger;
private $deprecationLogger;
private $levels;
private $throwAt;
private $scream;
Expand All @@ -48,7 +49,7 @@ class DebugHandlersListener implements EventSubscriberInterface
* @param string|FileLinkFormatter|null $fileLinkFormat The format for links to source files
* @param bool $scope Enables/disables scoping mode
*/
public function __construct(callable $exceptionHandler = null, LoggerInterface $logger = null, $levels = E_ALL, ?int $throwAt = E_ALL, bool $scream = true, $fileLinkFormat = null, bool $scope = true)
public function __construct(callable $exceptionHandler = null, LoggerInterface $logger = null, $levels = E_ALL, ?int $throwAt = E_ALL, bool $scream = true, $fileLinkFormat = null, bool $scope = true, LoggerInterface $deprecationLogger = null)
{
$this->exceptionHandler = $exceptionHandler;
$this->logger = $logger;
Expand All @@ -57,6 +58,7 @@ public function __construct(callable $exceptionHandler = null, LoggerInterface $
$this->scream = $scream;
$this->fileLinkFormat = $fileLinkFormat;
$this->scope = $scope;
$this->deprecationLogger = $deprecationLogger;
}

/**
Expand All @@ -76,31 +78,30 @@ public function configure(object $event = null)
$handler = \is_array($handler) ? $handler[0] : null;
restore_exception_handler();

if ($this->logger || null !== $this->throwAt) {
if ($handler instanceof ErrorHandler) {
if ($this->logger) {
$handler->setDefaultLogger($this->logger, $this->levels);
if (\is_array($this->levels)) {
$levels = 0;
foreach ($this->levels as $type => $log) {
$levels |= $type;
}
} else {
$levels = $this->levels;
}
if ($this->scream) {
$handler->screamAt($levels);
if ($handler instanceof ErrorHandler) {
if ($this->logger || $this->deprecationLogger) {
$this->setDefaultLoggers($handler);
if (\is_array($this->levels)) {
$levels = 0;
foreach ($this->levels as $type => $log) {
$levels |= $type;
}
if ($this->scope) {
$handler->scopeAt($levels & ~E_USER_DEPRECATED & ~E_DEPRECATED);
} else {
$handler->scopeAt(0, true);
}
$this->logger = $this->levels = null;
} else {
$levels = $this->levels;
}
if (null !== $this->throwAt) {
$handler->throwAt($this->throwAt, true);

if ($this->scream) {
$handler->screamAt($levels);
}
if ($this->scope) {
$handler->scopeAt($levels & ~E_USER_DEPRECATED & ~E_DEPRECATED);
} else {
$handler->scopeAt(0, true);
}
$this->logger = $this->deprecationLogger = $this->levels = null;
}
if (null !== $this->throwAt) {
$handler->throwAt($this->throwAt, true);
}
}
if (!$this->exceptionHandler) {
Expand Down Expand Up @@ -135,6 +136,34 @@ public function configure(object $event = null)
}
}

private function setDefaultLoggers(ErrorHandler $handler): void
{
if (\is_array($this->levels)) {
$levelsDeprecatedOnly = [];
$levelsWithoutDeprecated = [];
foreach ($this->levels as $type => $log) {
if (E_DEPRECATED == $type || E_USER_DEPRECATED == $type) {
$levelsDeprecatedOnly[$type] = $log;
} else {
$levelsWithoutDeprecated[$type] = $log;
}
}
} else {
$levelsDeprecatedOnly = $this->levels & (E_DEPRECATED | E_USER_DEPRECATED);
$levelsWithoutDeprecated = $this->levels & ~E_DEPRECATED & ~E_USER_DEPRECATED;
}

$defaultLoggerLevels = $this->levels;
if ($this->deprecationLogger && $levelsDeprecatedOnly) {
$handler->setDefaultLogger($this->deprecationLogger, $levelsDeprecatedOnly);
$defaultLoggerLevels = $levelsWithoutDeprecated;
}

if ($this->logger && $defaultLoggerLevels) {
$handler->setDefaultLogger($this->logger, $defaultLoggerLevels);
}
}

public static function getSubscribedEvents(): array
{
$events = [KernelEvents::REQUEST => ['configure', 2048]];
Expand Down
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\HttpKernel\Tests\EventListener;

use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
Expand Down Expand Up @@ -150,4 +151,89 @@ public function testReplaceExistingExceptionHandler()

$this->assertSame($userHandler, $eHandler->setExceptionHandler('var_dump'));
}

public function provideLevelsAssignedToLoggers(): array
{
return [
[false, false, '0', null, null],
[false, false, E_ALL, null, null],
[false, false, [], null, null],
[false, false, [E_WARNING => LogLevel::WARNING, E_USER_DEPRECATED => LogLevel::NOTICE], null, null],

[true, false, E_ALL, E_ALL, null],
[true, false, E_DEPRECATED, E_DEPRECATED, null],
[true, false, [], null, null],
[true, false, [E_WARNING => LogLevel::WARNING, E_DEPRECATED => LogLevel::NOTICE], [E_WARNING => LogLevel::WARNING, E_DEPRECATED => LogLevel::NOTICE], null],

[false, true, '0', null, null],
[false, true, E_ALL, null, E_DEPRECATED | E_USER_DEPRECATED],
[false, true, E_ERROR, null, null],
[false, true, [], null, null],
[false, true, [E_ERROR => LogLevel::ERROR, E_DEPRECATED => LogLevel::DEBUG], null, [E_DEPRECATED => LogLevel::DEBUG]],

[true, true, '0', null, null],
[true, true, E_ALL, E_ALL & ~(E_DEPRECATED | E_USER_DEPRECATED), E_DEPRECATED | E_USER_DEPRECATED],
[true, true, E_ERROR, E_ERROR, null],
[true, true, E_USER_DEPRECATED, null, E_USER_DEPRECATED],
[true, true, [E_ERROR => LogLevel::ERROR, E_DEPRECATED => LogLevel::DEBUG], [E_ERROR => LogLevel::ERROR], [E_DEPRECATED => LogLevel::DEBUG]],
[true, true, [E_ERROR => LogLevel::ALERT], [E_ERROR => LogLevel::ALERT], null],
[true, true, [E_USER_DEPRECATED => LogLevel::NOTICE], null, [E_USER_DEPRECATED => LogLevel::NOTICE]],
];
}

/**
* @dataProvider provideLevelsAssignedToLoggers
*
* @param array|string $levels
* @param array|string|null $expectedLoggerLevels
* @param array|string|null $expectedDeprecationLoggerLevels
*/
public function testLevelsAssignedToLoggers(bool $hasLogger, bool $hasDeprecationLogger, $levels, $expectedLoggerLevels, $expectedDeprecationLoggerLevels)
{
if (!class_exists(ErrorHandler::class)) {
$this->markTestSkipped('ErrorHandler component is required to run this test.');
}

$handler = $this->createMock(ErrorHandler::class);

$expectedCalls = [];
$logger = null;

$deprecationLogger = null;
if ($hasDeprecationLogger) {
$deprecationLogger = $this->createMock(LoggerInterface::class);
if (null !== $expectedDeprecationLoggerLevels) {
$expectedCalls[] = [$deprecationLogger, $expectedDeprecationLoggerLevels];
}
}

if ($hasLogger) {
$logger = $this->createMock(LoggerInterface::class);
if (null !== $expectedLoggerLevels) {
$expectedCalls[] = [$logger, $expectedLoggerLevels];
}
}

$handler
->expects($this->exactly(\count($expectedCalls)))
->method('setDefaultLogger')
->withConsecutive(...$expectedCalls);

$sut = new DebugHandlersListener(null, $logger, $levels, null, true, null, true, $deprecationLogger);
$prevHander = set_exception_handler([$handler, 'handleError']);

try {
$handler
->method('handleError')
->willReturnCallback(function () use ($prevHander) {
$prevHander(...\func_get_args());
});

$sut->configure();
set_exception_handler($prevHander);
} catch (\Exception $e) {
set_exception_handler($prevHander);
throw $e;
}
}
}

0 comments on commit 55706f7

Please sign in to comment.