8000 [Debug] Add BootstrappingLogger to buffer errors that happen before a… · symfony/symfony@e944b5e · GitHub
[go: up one dir, main page]

Skip to content

Commit e944b5e

Browse files
[Debug] Add BootstrappingLogger to buffer errors that happen before a proper logger is configured
1 parent 7f745d7 commit e944b5e

File tree

6 files changed

+204
-4
lines changed

6 files changed

+204
-4
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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\Debug;
13+
14+
use Psr\Log\LoggerInterface;
15+
16+
/**
17+
* A bootstrapping logger that can stack logs and flush them when required to another logger.
18+
*
19+
* @author Nicolas Grekas <p@tchwork.com>
20+
*/
21+
interface BootstrappingLoggerInterface extends LoggerInterface
22+
{
23+
public function flush(LoggerInterface $logger);
24+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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\Debug;
13+
14+
use Psr\Log\AbstractLogger;
15+
use Psr\Log\LoggerInterface;
16+
17+
/**
18+
* A buffering lo 8000 gger that stacks logs and flushes them when required to another logger.
19+
*
20+
* @author Nicolas Grekas <p@tchwork.com>
21+
*/
22+
class BufferingLogger extends AbstractLogger implements BootstrappingLoggerInterface
23+
{
24+
private $stripScopeVars;
25+
private $logs = array();
26+
27+
public function __construct($stripScopeVars = true)
28+
{
29+
$this->stripScopeVars = $stripScopeVars;
30+
}
31+
32+
public function log($level, $message, array $context = array())
33+
{
34+
if ($this->stripScopeVars) {
35+
if (isset($context['stack']) && is_array($context['stack'])) {
36+
foreach ($context['stack'] as &$frame) {
37+
unset($frame['args'], $frame['object'], $frame);
38+
}
39+
}
40+
unset($context['scope_vars']);
41+
}
42+
43+
$this->logs[] = array($level, $message, $context);
44+
}
45+
46+
public function flush(LoggerInterface $logger)
47+
{
48+
$logs = $this->logs;
49+
$this->logs = array();
50+
51+
foreach ($logs as $log) {
52+
$logger->log($log[0], $log[1], $log[2]);
53+
}
54+
}
55+
}

src/Symfony/Component/Debug/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
CHANGELOG
22
=========
33

4+
2.8.0
5+
-----
6+
7+
* added BufferingLogger and BootstrappingLoggerInterface for errors that happen before a proper logger is configured
8+
* allow throwing from `__toString()` with `return trigger_error($e, E_USER_ERROR);`
9+
* deprecate ExceptionHandler::createResponse
10+
411
2.7.0
512
-----
613

src/Symfony/Component/Debug/Debug.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ public static function enable($errorReportingLevel = null, $displayErrors = true
5353
ini_set('display_errors', 1);
5454
}
5555
$handler = ErrorHandler::register();
56+
$loggers = $handler->setLoggers(array());
57+
foreach ($loggers as &$level) {
58+
$level[0] = new BufferingLogger();
59+
}
60+
unset($level);
61+
$handler->setLoggers($loggers);
5662
if (!$displayErrors) {
5763
$handler->throwAt(0, true);
5864
}

src/Symfony/Component/Debug/ErrorHandler.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ public function setDefaultLogger(LoggerInterface $logger, $levels = null, $repla
165165

166166
if (is_array($levels)) {
167167
foreach ($levels as $type => $logLevel) {
168-
if (empty($this->loggers[$type][0]) || $replace) {
168+
if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] instanceof BootstrappingLoggerInterface) {
169169
$loggers[$type] = array($logger, $logLevel);
170170
}
171171
}
@@ -174,7 +174,7 @@ public function setDefaultLogger(LoggerInterface $logger, $levels = null, $repla
174174
$levels = E_ALL | E_STRICT;
175175
}
176176
foreach ($this->loggers as $type => $log) {
177-
if (($type & $levels) && (empty($log[0]) || $replace)) {
177+
if (($type & $levels) && (empty($log[0]) || $replace || $log[0] instanceof BootstrappingLoggerInterface)) {
178178
$log[0] = $logger;
179179
$loggers[$type] = $log;
180180
}
@@ -215,6 +215,10 @@ public function setLoggers(array $loggers)
215215
throw new \InvalidArgumentException('Invalid logger provided');
216216
}
217217
$this->loggers[$type] = $log + $prev[$type];
218+
219+
if (($this->loggedErrors & $type) && $prev[$type][0] instanceof BootstrappingLoggerInterface) {
220+
$prev[$type][0]->flush($this->loggers[$type][0]);
221+
}
218222
}
219223
$this->reRegister($prevLogged | $this->thrownErrors);
220224

@@ -252,7 +256,7 @@ public function setExceptionHandler($handler)
252256
public function throwAt($levels, $replace = false)
253257
{
254258
$prev = $this->thrownErrors;
255-
$this->thrownErrors = ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED;
259+
$this->thrownErrors = (E_ALL | E_STRICT) & ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED;
256260
if (!$replace) {
257261
$this->thrownErrors |= $prev;
258262
}
@@ -490,7 +494,7 @@ public function handleError($type, $message, $file, $line, array $context, array
490494
}
491495

492496
/**
493-
* Handles an exception by logging then forwarding it to an other handler.
497+
* Handles an exception by logging then forwarding it to another handler.
494498
*
495499
* @param \Exception|\Throwable $exception An exception to handle
496500
* @param array $error An array as returned by error_get_last()
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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\Debug\Tests;
13+
14+
use Psr\Log\LogLevel;
15+
use Symfony\Component\Debug\BufferingLogger;
16+
use Symfony\Component\Debug\ErrorHandler;
17+
18+
/**
19+
* BufferingLoggerTest.
20+
*
21+
* @author Nicolas Grekas <p@tchwork.com>
22+
*/
23+
class BufferingLoggerTest extends \PHPUnit_Framework_TestCase
24+
{
25+
/**
26+
* @dataProvider provideFlush
27+
*/
28+
public function testFlush($strip, $expectedContext, $context)
29+
{
30+
$logger = new BufferingLogger($strip);
31+
32+
$logger->log(LogLevel::INFO, 'Foo message', $context);
33+
34+
$mockLogger = $this->getMock('Psr\Log\LoggerInterface');
35+
$mockLogger->expects($this->once())
36+
->method('log')
37+
->with(LogLevel::INFO, 'Foo message', $expectedContext);
38+
39+
$logger->flush($mockLogger);
40+
}
41+
42+
public function provideFlush()
43+
{
44+
$context = array(
45+
'level' => 0,
46+
'type' => E_USER_NOTICE,
47+
'file' => __FILE__,
48+
'line' => 123,
49+
'scope_vars' => 234,
50+
'stack' => array(
51+
array(
52+
'file' => __FILE__,
53+
'args' => 345,
54+
'object' => 456,
55+
),
56+
),
57+
);
58+
59+
$strippedContext = $context;
60+
unset($strippedContext['scope_vars'], $strippedContext['stack'][0]['args'], $strippedContext['stack'][0]['object']);
61+
62+
return array(
63+
array(false, $context, $context),
64+
array(true, $strippedContext, $context),
65+
);
66+
}
67+
68+
public function testLifecycle()
69+
{
70+
$logger = new BufferingLogger();
71+
72+
$handler = new ErrorHandler();
73+
$handler->setDefaultLogger($logger);
74+
75+
$handler->handleError(E_DEPRECATED, 'Foo message', __FILE__, 123, array());
76+
77+
$mockLogger = $this->getMock('Psr\Log\LoggerInterface');
78+
$mockLogger->expects($this->once())
79+
->method('log')
80+
->with(LogLevel::INFO, 'Foo message', $this->isType('array'));
81+
82+
$handler->setDefaultLogger($mockLogger);
83+
84+
$loggers = array(
85+
E_DEPRECATED => array($mockLogger, LogLevel::INFO),
86+
E_USER_DEPRECATED => array($mockLogger, LogLevel::INFO),
87+
E_NOTICE => array($mockLogger, LogLevel::WARNING),
88+
E_USER_NOTICE => array($mockLogger, LogLevel::WARNING),
89+
E_STRICT => array($mockLogger, LogLevel::WARNING),
90+
E_WARNING => array($mockLogger, LogLevel::WARNING),
91+
E_USER_WARNING => array($mockLogger, LogLevel::WARNING),
92+
E_COMPILE_WARNING => array($mockLogger, LogLevel::WARNING),
93+
E_CORE_WARNING => array($mockLogger, LogLevel::WARNING),
94+
E_USER_ERROR => array($mockLogger, LogLevel::CRITICAL),
95+
E_RECOVERABLE_ERROR => array($mockLogger, LogLevel::CRITICAL),
96+
E_COMPILE_ERROR => array($mockLogger, LogLevel::CRITICAL),
97+
E_PARSE => array($mockLogger, LogLevel::CRITICAL),
98+
E_ERROR => array($mockLogger, LogLevel::CRITICAL),
99+
E_CORE_ERROR => array($mockLogger, LogLevel::CRITICAL),
100+
);
101+
102+
$this->assertSame($loggers, $handler->setLoggers(array()));
103+
}
104+
}

0 commit comments

Comments
 (0)
0