8000 Split into two listeners · symfony/symfony@87ac731 · GitHub
[go: up one dir, main page]

Skip to content

Commit 87ac731

Browse files
committed
Split into two listeners
1 parent 2a2c0f3 commit 87ac731

File tree

9 files changed

+231
-63
lines changed

9 files changed

+231
-63
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ public function getConfigTreeBuilder()
100100
$this->addPhpErrorsSection($rootNode);
101101
$this->addWebLinkSection($rootNode);
102102
$this->addLockSection($rootNode);
103+
$this->addHttpExceptionLogLevels($rootNode);
103104

104105
return $treeBuilder;
105106
}
@@ -894,4 +895,43 @@ private function addWebLinkSection(ArrayNodeDefinition $rootNode)
894895
->end()
895896
;
896897
}
898+
899+
private function addHttpExceptionLogLevels(ArrayNodeDefinition $rootNode)
900+
{
901+
$rootNode
902+
->children()
903+
->arrayNode('http_exception_log_levels')
904+
->info('The override log levels for http exceptions')
905+
->example(array('403' => 'NOTICE', '404' => 'INFO'))
906+
->useAttributeAsKey('http_exception_log_levels')
907+
->prototype('variable')->end()
908+
->validate()
909+
->always(function ($v) {
910+
$map = array();
911+
foreach ($v as $status => $level) {
912+
if (!(is_int($status) && $status >= 100 && $status <= 599)) {
913+
throw new InvalidConfigurationException(sprintf(
914+
'The configured status code "%s" in framework.http_exception_log_levels is not a valid http status code.',
915+
$status
916+
));
917+
}
918+
919+
$levelConstant = 'Psr\Log\LogLevel::'.$level;
920+
if (!defined($levelConstant)) {
921+
throw new InvalidConfigurationException(sprintf(
922+
'The configured log level "%s" in framework.http_exception_log_levels is invalid as it is not defined in Psr\\Log\\LogLevel.',
923+
$level
924+
));
925+
}
926+
927+
$map[$status] = constant($levelConstant);
928+
}
929+
930+
return $map;
931+
})
932+
->end()
933+
->end()
934+
->end()
935+
;
936+
}
897937
}

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ public function load(array $configs, ContainerBuilder $container)
162162
$container->setParameter('kernel.trusted_hosts', $config['trusted_hosts']);
163163
$container->setParameter('kernel.default_locale', $config['default_locale']);
164164

165+
$container->setParameter('framework.exception_listener.http_log_levels', $config['http_exception_log_levels']);
166+
165167
if (!$container->hasParameter('debug.file_link_format')) {
166168
if (!$container->hasParameter('templating.helper.code.file_link_format')) {
167169
$links = array(

src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,13 @@
7171
</service>
7272

7373
<service id="services_resetter" class="Symfony\Component\HttpKernel\DependencyInjection\ServicesResetter" public="true" />
74+
75+
<service id="exception_logger" class="Symfony\Component\HttpKernel\EventListener\LoggingExceptionListener">
76+
<tag name="kernel.event_subscriber" />
77+
<tag name="monolog.logger" channel="request" />
78+
<argument type="service" id="logger" on-invalid="null" />
79+
<argument>%framework.exception_listener.http_log_levels%</argument>
80+
</service>
81+
7482
</services>
7583
</container>

src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ public function getConfigTreeBuilder()
4343
$this->addGlobalsSection($rootNode);
4444
$this->addTwigOptions($rootNode);
4545
$this->addTwigFormatOptions($rootNode);
46-
$this->addHttpExceptionLogLevels($rootNode);
4746

4847
return $treeBuilder;
4948
}
@@ -196,43 +195,4 @@ private function addTwigFormatOptions(ArrayNodeDefinition $rootNode)
196195
->end()
197196
;
198197
}
199-
200-
private function addHttpExceptionLogLevels(ArrayNodeDefinition $rootNode)
201-
{
202-
$rootNode
203-
->children()
204-
->arrayNode('http_exception_log_levels')
205-
->info('The override log levels for http exceptions')
206-
->example(array('403' => 'NOTICE', '404' => 'INFO'))
207-
->useAttributeAsKey('http_exception_log_levels')
208-
->prototype('variable')->end()
209-
->validate()
210-
->always(function ($v) {
211-
$map = array();
212-
foreach ($v as $status => $level) {
213-
if (!(is_int($status) && $status >= 100 && $status <= 599)) {
214-
throw new InvalidConfigurationException(sprintf(
215-
'The configured status code "%s" in twig.http_exception_log_levels is not a valid http status code.',
216-
$status
217-
));
218-
}
219-
220-
$levelConstant = 'Psr\Log\LogLevel::'.$level;
221-
if (!defined($levelConstant)) {
222-
throw new InvalidConfigurationException(sprintf(
223-
'The configured log level "%s" in twig.http_exception_log_levels is invalid as it is not defined in Psr\\Log\\LogLevel.',
224-
$level
225-
));
226-
}
227-
228-
$map[$status] = constant($levelConstant);
229-
}
230-
231-
return $map;
232-
})
233-
->end()
234-
->end()
235-
->end()
236-
;
237-
}
238198
}

src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ public function load(array $configs, ContainerBuilder $container)
7878
$config = $this->processConfiguration($configuration, $configs);
7979

8080
$container->setParameter('twig.exception_listener.controller', $config['exception_controller']);
81-
$container->setParameter('twig.exception_listener.http_log_levels', $config['http_exception_log_levels']);
8281

8382
$container->setParameter('twig.form.resources', $config['form_themes']);
8483
$container->setParameter('twig.default_path', $config['default_path']);

src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,11 @@
123123
<argument type="service" id="workflow.registry" />
124124
</service>
125125

126-
<service id="twig.exception_listener" class="Symfony\Component\HttpKernel\EventListener\ExceptionListener">
126+
<service id="twig.exception_listener" class="Symfony\Component\HttpKernel\EventListener\RenderControllerExceptionListener">
127127
<tag name="kernel.event_subscriber" />
128128
<tag name="monolog.logger" channel="request" />
129129
<argument>%twig.exception_listener.controller%</argument>
130130
<argument type="service" id="logger" on-invalid="null" />
131-
<argument>%twig.exception_listener.http_log_levels%</argument>
132131
</service>
133132

134133
<service id="twig.controller.exception" class="Symfony\Bundle\TwigBundle\Controller\ExceptionController" public="true">

src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
namespace Symfony\Component\HttpKernel\EventListener;
1313

1414
use Psr\Log\LoggerInterface;
15-
use Psr\Log\LogLevel;
1615
use Symfony\Component\Debug\Exception\FlattenException;
1716
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
1817
use Symfony\Component\HttpFoundation\Request;
@@ -24,22 +23,23 @@
2423
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
2524
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
2625

26+
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.2 and will be removed in 5.0, use "%s" or "%s" instead.', ExceptionListener::class, RenderControllerExceptionListener::class, LoggingExceptionListener::class), E_USER_DEPRECATED);
27+
2728
/**
2829
* ExceptionListener.
2930
*
3031
* @author Fabien Potencier <fabien@symfony.com>
32+
* @deprecated since Symfony 4.2, use RenderControllerExceptionListener or LoggingExceptionListener instead
3133
*/
3234
class ExceptionListener implements EventSubscriberInterface
3335
{
3436
protected $controller;
3537
protected $logger;
36-
protected $httpStatusCodeLogLevel;
3738

38-
public function __construct($controller, LoggerInterface $logger = null, array $httpStatusCodeLogLevel = array())
39+
public function __construct($controller, LoggerInterface $logger = null)
3940
{
4041
$this->controller = $controller;
4142
$this->logger = $logger;
42-
$this->httpStatusCodeLogLevel = $httpStatusCodeLogLevel;
4343
}
4444

4545
public function logKernelException(GetResponseForExceptionEvent $event)
@@ -97,21 +97,6 @@ public static function getSubscribedEvents()
9797
);
9898
}
9999

100-
protected function getExceptionLogLevel(\Exception $exception): string
101-
{
102-
$logLevel = LogLevel::CRITICAL;
103-
if ($exception instanceof HttpExceptionInterface) {
104-
$statusCode = $exception->getStatusCode();
105-
if (isset($this->httpStatusCodeLogLevel[$statusCode])) {
106-
$logLevel = $this->httpStatusCodeLogLevel[$statusCode];
107-
} elseif ($statusCode >= 400 && $statusCode < 500) {
108-
$logLevel = LogLevel::WARNING;
109-
}
110-
}
111-
112-
return $logLevel;
113-
}
114-
115100
/**
116101
* Logs an exception.
117102
*
@@ -121,7 +106,11 @@ protected function getExceptionLogLevel(\Exception $exception): string
121106
protected function logException(\Exception $exception, $message)
122107
{
123108
if (null !== $this->logger) {
124-
$this->logger->log($this->getExceptionLogLevel($exception), $message, array('exception' => $exception));
109+
if (!$exception instanceof HttpExceptionInterface || $exception->getStatusCode() >= 500) {
110+
$this->logger->critical($message, array('exception' => $exception));
111+
} else {
112+
$this->logger->error($message, array('exception' => $exception));
113+
}
125114
}
126115
}
127116

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpKernel\EventListener;
13+
14+
use Psr\Log\LoggerInterface;
15+
use Psr\Log\LogLevel;
16+
use Psr\Log\NullLogger;
17+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
18+
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
19+
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
20+
use Symfony\Component\HttpKernel\KernelEvents;
21+
22+
class LoggingExceptionListener implements EventSubscriberInterface
23+
{
24+
protected $logger;
25+
protected $httpStatusCodeLogLevel;
26+
27+
public function __construct(LoggerInterface $logger = null, array $httpStatusCodeLogLevel = array())
28+
{
29+
$this->logger = $logger ?: new NullLogger();
30+
$this->httpStatusCodeLogLevel = $httpStatusCodeLogLevel;
31+
}
32+
33+
public function logKernelException(GetResponseForExceptionEvent $event)
34+
{
35+
$exception = $event->getException();
36+
$level = $this->getExceptionLogLevel($exception);
37+
$message = sprintf('Uncaught PHP Exception %s: "%s" at %s line %s', get_class($exception), $exception->getMessage(), $exception->getFile(), $exception->getLine());
38+
39+
$this->logger->log($level, $message, array('exception' => $exception));
40+
}
41+
42+
public static function getSubscribedEvents(): array
43+
{
44+
return array(
45+
KernelEvents::EXCEPTION => array(
46+
array('logKernelException', 2048),
47+
),
48+
);
49+
}
50+
51+
protected function getExceptionLogLevel(\Exception $exception): string
52+
{
53+
$logLevel = LogLevel::CRITICAL;
54+
if ($exception instanceof HttpExceptionInterface) {
55+
$statusCode = $exception->getStatusCode();
56+
if (isset($this->httpStatusCodeLogLevel[$statusCode])) {
57+
$logLevel = $this->httpStatusCodeLogLevel[$statusCode];
58+
} elseif ($statusCode >= 400 && $statusCode < 500) {
59+
$logLevel = LogLevel::WARNING;
60+
}
61+
}
62+
63+
return $logLevel;
64+
}
65+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpKernel\EventListener;
13+
14+
use Psr\Log\LoggerInterface;
15+
use Psr\Log\NullLogger;
16+
use Symfony\Component\Debug\Exception\FlattenException;
17+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
18+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
19+
use Symfony\Component\HttpFoundation\Request;
20+
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
21+
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
22+
use Symfony\Component\HttpKernel\HttpKernelInterface;
23+
use Symfony\Component\HttpKernel\KernelEvents;
24+
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
25+
26+
/**
27+
* RenderControllerExceptionListener renders an exception using the given controller
28+
*/
29+
class RenderControllerExceptionListener implements EventSubscriberInterface
30+
{
31+
protected $controller;
32+
protected $logger;
33+
34+
public function __construct($controller, LoggerInterface $logger = null)
35+
{
36+
$this->controller = $controller;
37+
$this->logger = $logger ?: new NullLogger();
38+
}
39+
40+
public static function getSubscribedEvents()
41+
{
42+
return array(
43+
KernelEvents::EXCEPTION => array(
44+
array('onKernelException', -128),
45+
),
46+
);
47+
}
48+
49+
public function onKernelException(GetResponseForExceptionEvent $event, string $eventName, EventDispatcherInterface $eventDispatcher)
50+
{
51+
$exception = $event->getException();
52+
$request = $this->duplicateRequest($exception, $event->getRequest());
53+
$response = $this->handleRequest($event->getKernel(), $request, $exception);
54+
$this->addListenerToRemoveContentSecurityPolicyHeader($eventDispatcher);
55+
$event->setResponse($response);
56+
}
57 C2EE +
58+
private function duplicateRequest(\Exception $exception, Request $request): Request
59+
{
60+
$attributes = array(
61+
'_controller' => $this->controller,
62+
'exception' => FlattenException::create($exception),
63+
'logger' => $this->logger instanceof DebugLoggerInterface ? $this->logger : null,
64+
);
65+
$request = $request->duplicate(null, null, $attributes);
66+
$request->setMethod('GET');
67+
68+
return $request;
69+
}
70+
71+
private function handleRequest($kernel, $request, $exception)
72+
{
73+
try {
74+
return $kernel->handle($request, HttpKernelInterface::SUB_REQUEST, false);
75+
} catch (\Exception $e) {
76+
$this->logger->critical(sprintf('Exception thrown when handling an exception (%s: %s at %s line %s)', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()), array('exception' => $e));
77+
throw $this->pushOriginalExceptionIntoNewException($exception, $e);
78+
}
79+
}
80+
81+
private function addListenerToRemoveContentSecurityPolicyHeader(EventDispatcherInterface $eventDispatcher)
82+
{
83+
$cspRemovalListener = function (FilterResponseEvent $event) use (&$cspRemovalListener, $eventDispatcher) {
84+
$event->getResponse()->headers->remove('Content-Security-Policy');
85+
$eventDispatcher->removeListener(KernelEvents::RESPONSE, $cspRemovalListener);
86+
};
87+
$eventDispatcher->addListener(KernelEvents::RESPONSE, $cspRemovalListener, -128);
88+
}
89+
90+
private function pushOriginalExceptionIntoNewException(\Exception $originalException, \Exception $newException): \Exception
91+
{
92+
$wrapper = $newException;
93+
94+
while ($prev = $wrapper->getPrevious()) {
95+
if ($originalException === $wrapper = $prev) {
96+
return $newException;
97+
}
98+
}
99+
100+
$prev = new \ReflectionProperty('Exception', 'previous');
101+
$prev->setAccessible(true);
102+
$prev->setValue($wrapper, $originalException);
103+
104+
return $newException;
105+
}
106+
}

0 commit comments

Comments
 (0)
0