merged branch Koc/critical-errors-logging (PR #5863)
This PR was merged into the master branch. Commits -------acfc750
#2042 initial implementation of fatal error handler Discussion ---------- Display traces for fatal errors Bug fix: no Feature addition: yes Backwards compatibility break: no Symfony2 tests pass: looks like yes Fixes the following tickets: #2042 (partly) License of the code: MIT Output looks like on screen http://easycaptures.com/fs/uploaded/737/1191436899.png . I've added one line to css to prevent displaying standard xdebug trace http://easycaptures.com/fs/uploaded/737/5939488074.png --------------------------------------------------------------------------- by Koc at 2012-11-08T21:55:41Z So, community please advice me, how can I trigger `KernelEvents::EXCEPTION` event in `ErrorHandler` or `ExceptionHandler`? Or should I provide other event for this? --------------------------------------------------------------------------- by stof at 2012-11-08T22:03:23Z @Koc Don't. the exception handler is there to be the safe guard when developing, and does not depend on the kernel (which would be required to trigger the event). If you were triggering the listener again, it would mean that any exception thrown in a listener would lead to a loop. And if it is for the fatal error handling, you simply cannot be sure the kernel is still available (and even less in a wokring state) at this point. --------------------------------------------------------------------------- by Koc at 2012-11-08T22:06:31Z But how can I notify logger (which will send me mail or just log this situation)? --------------------------------------------------------------------------- by fabpot at 2012-11-09T07:33:41Z The error handler is only registered when in debug mode in the Kernel and can be triggered very early in the handling of a request (even before we have access to the dispatcher or anything else). So, the current PR looks fine to me (apart from the typo and the lack of unit tests). --------------------------------------------------------------------------- by Koc at 2012-11-09T09:13:03Z > The error handler is only registered when in debug mode Ooh! I haven't see that before. But the goal - be notified about errors by email or log-file. Like now exceptions with traces from site emails to me. --------------------------------------------------------------------------- by fabpot at 2012-11-09T09:20:54Z I think there are two goals. The first one being to have nice pages in the development environment when a fatal error occurs. And this PR addresses that feature quite nicely. The second can be addressed in another PR. --------------------------------------------------------------------------- by henrikbjorn at 2012-11-14T11:50:22Z I have some questions about the ErrorHandler. Is there a reason for it only to be registered in an debug environment (which prod is not). Would assume that if i enable the ErrorHandler in productions aswell Monolog would log thoose instead of them just vanishing? --------------------------------------------------------------------------- by Koc at 2012-11-14T12:01:50Z I am thinking about it too. But as Fabien says it will another PR --------------------------------------------------------------------------- by GromNaN at 2012-11-18T10:38:09Z You should add a memory reserve to be able to handle "Out of memory" errors. An example is here :513d628966/lib/Raven/ErrorHandler.php (L91)
513d628966/lib/Raven/ErrorHandler.php (L62)
--------------------------------------------------------------------------- by fabpot at 2012-11-28T11:35:21Z @Koc can you finish this PR (probably by integrating the memory reserve as explained by @GromNaN)? --------------------------------------------------------------------------- by Koc at 2012-11-28T11:46:12Z of course, on this weekend --------------------------------------------------------------------------- by Koc at 2012-12-02T17:44:44Z @fabpot done
This commit is contained in:
commit
559fa8c214
@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\HttpKernel\Debug;
|
namespace Symfony\Component\HttpKernel\Debug;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpKernel\Exception\FatalErrorException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ErrorHandler.
|
* ErrorHandler.
|
||||||
*
|
*
|
||||||
@ -28,10 +30,16 @@ class ErrorHandler
|
|||||||
E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
|
E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
|
||||||
E_DEPRECATED => 'Deprecated',
|
E_DEPRECATED => 'Deprecated',
|
||||||
E_USER_DEPRECATED => 'User Deprecated',
|
E_USER_DEPRECATED => 'User Deprecated',
|
||||||
|
E_ERROR => 'Error',
|
||||||
|
E_CORE_ERROR => 'Core Error',
|
||||||
|
E_COMPILE_ERROR => 'Compile Error',
|
||||||
|
E_PARSE => 'Parse',
|
||||||
);
|
);
|
||||||
|
|
||||||
private $level;
|
private $level;
|
||||||
|
|
||||||
|
private $reservedMemory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the error handler.
|
* Register the error handler.
|
||||||
*
|
*
|
||||||
@ -45,6 +53,8 @@ class ErrorHandler
|
|||||||
$handler->setLevel($level);
|
$handler->setLevel($level);
|
||||||
|
|
||||||
set_error_handler(array($handler, 'handle'));
|
set_error_handler(array($handler, 'handle'));
|
||||||
|
register_shutdown_function(array($handler, 'handleFatal'));
|
||||||
|
$handler->reservedMemory = str_repeat('x', 10240);
|
||||||
|
|
||||||
return $handler;
|
return $handler;
|
||||||
}
|
}
|
||||||
@ -69,4 +79,28 @@ class ErrorHandler
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function handleFatal()
|
||||||
|
{
|
||||||
|
if (null === $error = error_get_last()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($this->reservedMemory);
|
||||||
|
$type = $error['type'];
|
||||||
|
if (0 === $this->level || !in_array($type, array(E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get current exception handler
|
||||||
|
$exceptionHandler = set_exception_handler(function() {});
|
||||||
|
restore_exception_handler();
|
||||||
|
|
||||||
|
if (is_array($exceptionHandler) && $exceptionHandler[0] instanceof ExceptionHandler) {
|
||||||
|
$level = isset($this->levels[$type]) ? $this->levels[$type] : $type;
|
||||||
|
$message = sprintf('%s: %s in %s line %d', $level, $error['message'], $error['file'], $error['line']);
|
||||||
|
$exception = new FatalErrorException($message, 0, $type, $error['file'], $error['line']);
|
||||||
|
$exceptionHandler[0]->handle($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,6 +234,9 @@ EOF;
|
|||||||
img { border: 0; }
|
img { border: 0; }
|
||||||
#sf-resetcontent { width:970px; margin:0 auto; }
|
#sf-resetcontent { width:970px; margin:0 auto; }
|
||||||
$css
|
$css
|
||||||
|
.xdebug-error {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\HttpKernel\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fatal Error Exception.
|
||||||
|
*
|
||||||
|
* @author Konstanton Myakshin <koc-dp@yandex.ru>
|
||||||
|
*/
|
||||||
|
class FatalErrorException extends \ErrorException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@ -47,7 +47,7 @@ class FlattenException
|
|||||||
|
|
||||||
$e->setStatusCode($statusCode);
|
$e->setStatusCode($statusCode);
|
||||||
$e->setHeaders($headers);
|
$e->setHeaders($headers);
|
||||||
$e->setTrace($exception->getTrace(), $exception->getFile(), $exception->getLine());
|
$e->setTraceFromException($exception);
|
||||||
$e->setClass(get_class($exception));
|
$e->setClass(get_class($exception));
|
||||||
$e->setFile($exception->getFile());
|
$e->setFile($exception->getFile());
|
||||||
$e->setLine($exception->getLine());
|
$e->setLine($exception->getLine());
|
||||||
@ -168,6 +168,40 @@ class FlattenException
|
|||||||
return $this->trace;
|
return $this->trace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setTraceFromException(\Exception $exception)
|
||||||
|
{
|
||||||
|
$trace = $exception->getTrace();
|
||||||
|
|
||||||
|
if ($exception instanceof FatalErrorException) {
|
||||||
|
if (function_exists('xdebug_get_function_stack')) {
|
||||||
|
$trace = array_slice(array_reverse(xdebug_get_function_stack()), 4);
|
||||||
|
|
||||||
|
foreach ($trace as $i => $frame) {
|
||||||
|
// XDebug pre 2.1.1 doesn't currently set the call type key http://bugs.xdebug.org/view.php?id=695
|
||||||
|
if (!isset($frame['type'])) {
|
||||||
|
$trace[$i]['type'] = '??';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('dynamic' === $trace[$i]['type']) {
|
||||||
|
$trace[$i]['type'] = '->';
|
||||||
|
} elseif ('static' === $trace[$i]['type']) {
|
||||||
|
$trace[$i]['type'] = '::';
|
||||||
|
}
|
||||||
|
|
||||||
|
// XDebug also has a different name for the parameters array
|
||||||
|
if (isset($frame['params']) && !isset($frame['args'])) {
|
||||||
|
$trace[$i]['args'] = $frame['params'];
|
||||||
|
unset($trace[$i]['params']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$trace = array_slice(array_reverse($trace), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setTrace($trace, $exception->getFile(), $exception->getLine());
|
||||||
|
}
|
||||||
|
|
||||||
public function setTrace($trace, $file, $line)
|
public function setTrace($trace, $file, $line)
|
||||||
{
|
{
|
||||||
$this->trace = array();
|
$this->trace = array();
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
namespace Symfony\Component\HttpKernel\Tests\Debug;
|
namespace Symfony\Component\HttpKernel\Tests\Debug;
|
||||||
|
|
||||||
use Symfony\Component\HttpKernel\Debug\ErrorHandler;
|
use Symfony\Component\HttpKernel\Debug\ErrorHandler;
|
||||||
use Symfony\Component\HttpKernel\Debug\ErrorException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ErrorHandlerTest
|
* ErrorHandlerTest
|
||||||
@ -48,10 +47,10 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
$handler = ErrorHandler::register(3);
|
$handler = ErrorHandler::register(3);
|
||||||
try {
|
try {
|
||||||
$handler->handle(1, 'foo', 'foo.php', 12, 'foo');
|
$handler->handle(111, 'foo', 'foo.php', 12, 'foo');
|
||||||
} catch (\ErrorException $e) {
|
} catch (\ErrorException $e) {
|
||||||
$this->assertSame('1: foo in foo.php line 12', $e->getMessage());
|
$this->assertSame('111: foo in foo.php line 12', $e->getMessage());
|
||||||
$this->assertSame(1, $e->getSeverity());
|
$this->assertSame(111, $e->getSeverity());
|
||||||
$this->assertSame('foo.php', $e->getFile());
|
$this->assertSame('foo.php', $e->getFile());
|
||||||
$this->assertSame(12, $e->getLine());
|
$this->assertSame(12, $e->getLine());
|
||||||
}
|
}
|
||||||
|
@ -156,14 +156,14 @@ class FlattenExceptionTest extends \PHPUnit_Framework_TestCase
|
|||||||
public function testToArray(\Exception $exception, $statusCode)
|
public function testToArray(\Exception $exception, $statusCode)
|
||||||
{
|
{
|
||||||
$flattened = FlattenException::create($exception);
|
$flattened = FlattenException::create($exception);
|
||||||
$flattened->setTrace(array(),'foo.php',123);
|
$flattened->setTrace(array(), 'foo.php', 123);
|
||||||
|
|
||||||
$this->assertEquals(array(
|
$this->assertEquals(array(
|
||||||
array(
|
array(
|
||||||
'message'=> 'test',
|
'message'=> 'test',
|
||||||
'class'=>'Exception',
|
'class'=>'Exception',
|
||||||
'trace'=>array(array(
|
'trace'=>array(array(
|
||||||
'namespace' => '', 'short_class' => '', 'class' => '','type' => '','function' => '', 'file' => 'foo.php','line' => 123,
|
'namespace' => '', 'short_class' => '', 'class' => '','type' => '','function' => '', 'file' => 'foo.php', 'line' => 123,
|
||||||
'args' => array()
|
'args' => array()
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user