mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
FEATURE: Improved debugging view on CLI interface, by having a separate DebugView subclass that takes care of error output for this situation.
API CHANGE: Added Debug::log_errors_to(), to log errors to a file. git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@60368 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
010c91af0a
commit
db1e79c3b1
57
dev/CliDebugView.php
Normal file
57
dev/CliDebugView.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* A basic HTML wrapper for stylish rendering of a developement info view.
|
||||
* Used to output error messages, and test results.
|
||||
*
|
||||
* @package sapphire
|
||||
* @subpackage dev
|
||||
*
|
||||
* @todo Perhaps DebugView should be an interface / ABC, implemented by HTMLDebugView and CliDebugView?
|
||||
*/
|
||||
|
||||
class CliDebugView extends DebugView {
|
||||
|
||||
/**
|
||||
* Render HTML header for development views
|
||||
*/
|
||||
public function writeHeader($httpRequest) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Render HTML footer for development views
|
||||
*/
|
||||
public function writeFooter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Write information about the error to the screen
|
||||
*/
|
||||
public function writeError($httpRequest, $errno, $errstr, $errfile, $errline, $errcontext) {
|
||||
echo "ERROR: $errstr\nIN $httpRequest\n";
|
||||
echo "Line $errline in $errfile\n\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a fragment of the a source file
|
||||
* @param $lines An array of file lines; the keys should be the original line numbers
|
||||
*/
|
||||
function writeSourceFragment($lines, $errline) {
|
||||
echo "Source\n======\n";
|
||||
foreach($lines as $offset => $line) {
|
||||
echo ($offset == $errline) ? "* " : " ";
|
||||
echo str_pad("$offset:",5);
|
||||
echo wordwrap($line, 100, "\n ");
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a backtrace
|
||||
*/
|
||||
function writeTrace() {
|
||||
Debug::backtrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
@ -58,6 +58,15 @@ class Debug {
|
||||
*/
|
||||
protected static $send_warnings_to;
|
||||
|
||||
/**
|
||||
* String indicating the file where errors are logged.
|
||||
* Filename is relative to the site root.
|
||||
* The named file will have a terse log sent to it, and the full log (an
|
||||
* encoded file containing backtraces and things) will go to a file of a similar
|
||||
* name, but with the suffix ".full" added.
|
||||
*/
|
||||
protected static $log_errors_to = null;
|
||||
|
||||
/**
|
||||
* Show the contents of val in a debug-friendly way.
|
||||
* Debug::show() is intended to be equivalent to dprintr()
|
||||
@ -214,6 +223,7 @@ class Debug {
|
||||
static function warningHandler($errno, $errstr, $errfile, $errline, $errcontext) {
|
||||
if(error_reporting() == 0) return;
|
||||
if(self::$send_warnings_to) self::emailError(self::$send_warnings_to, $errno, $errstr, $errfile, $errline, $errcontext, "Warning");
|
||||
self::log_error_if_necessary( $errno, $errstr, $errfile, $errline, $errcontext, "Warning");
|
||||
|
||||
if(Director::isDev()) {
|
||||
self::showError($errno, $errstr, $errfile, $errline, $errcontext);
|
||||
@ -233,14 +243,15 @@ class Debug {
|
||||
*/
|
||||
static function fatalHandler($errno, $errstr, $errfile, $errline, $errcontext) {
|
||||
if(self::$send_errors_to) self::emailError(self::$send_errors_to, $errno, $errstr, $errfile, $errline, $errcontext, "Error");
|
||||
self::log_error_if_necessary( $errno, $errstr, $errfile, $errline, $errcontext, "Error");
|
||||
|
||||
if(Director::isDev()) {
|
||||
if(Director::isDev() || Director::is_cli()) {
|
||||
Debug::showError($errno, $errstr, $errfile, $errline, $errcontext);
|
||||
|
||||
} else {
|
||||
Debug::friendlyError($errno, $errstr, $errfile, $errline, $errcontext);
|
||||
}
|
||||
die();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -268,6 +279,14 @@ class Debug {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of an appropriate DebugView object.
|
||||
*/
|
||||
static function create_debug_view() {
|
||||
if(Director::is_cli()) return new CliDebugView();
|
||||
else return new DebugView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a developer facing error page, showing the stack trace and details
|
||||
* of the code where the error occured.
|
||||
@ -284,17 +303,23 @@ class Debug {
|
||||
echo "ERROR:Error $errno: $errstr\n At l$errline in $errfile\n";
|
||||
Debug::backtrace();
|
||||
} else {
|
||||
$reporter = new DebugView();
|
||||
$reporter->writeHeader();
|
||||
$reporter->writeInfo(strip_tags($errstr), $_SERVER['REQUEST_METHOD'] . " " .$_SERVER['REQUEST_URI'],
|
||||
"Line <strong>$errline</strong> in <strong>$errfile</strong>");
|
||||
echo '<div class="trace"><h3>Source</h3>';
|
||||
Debug::showLines($errfile, $errline);
|
||||
echo '<h3>Trace</h3>';
|
||||
Debug::backtrace();
|
||||
echo '</div>';
|
||||
$reporter = self::create_debug_view();
|
||||
|
||||
// Coupling alert: This relies on knowledge of how the director gets its URL, it could be improved.
|
||||
$httpRequest = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : $_REQUEST['url'];
|
||||
if(isset($_SERVER['REQUEST_METHOD'])) $httpRequest = $_SERVER['REQUEST_METHOD'] . ' ' . $httpRequest;
|
||||
|
||||
$reporter->writeHeader($httpRequest);
|
||||
$reporter->writeError($httpRequest, $errno, $errstr, $errfile, $errline, $errcontext);
|
||||
|
||||
$lines = file($errfile);
|
||||
$offset = $errline-10;
|
||||
$lines = array_slice($lines, $offset, 16, true);
|
||||
$reporter->writeSourceFragment($lines, $errline);
|
||||
|
||||
$reporter->writeTrace($lines);
|
||||
$reporter->writeFooter();
|
||||
die();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -357,6 +382,26 @@ class Debug {
|
||||
mail($emailAddress, "$errorType at $relfile line $errline (http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI])", $data, "Content-type: text/html\nFrom: errors@silverstripe.com");
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the given error, if self::$log_errors is set.
|
||||
*/
|
||||
protected static function log_error_if_necessary($errno, $errstr, $errfile, $errline, $errcontext, $errtype) {
|
||||
if(self::$log_errors_to) {
|
||||
$shortFile = "../" . self::$log_errors_to;
|
||||
$fullFile = $shortFile . '.full';
|
||||
|
||||
$relfile = Director::makeRelative($errfile);
|
||||
if($relfile[0] == '/') $relfile = substr($relfile,1);
|
||||
|
||||
$urlSuffix = "";
|
||||
if(isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST'] && isset($_SERVER['REQUEST_URI'])) {
|
||||
$urlSuffix = " (http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI])";
|
||||
}
|
||||
|
||||
error_log('[' . date('d-M-Y h:i:s') . "] $errtype at $relfile line $errline: $errstr$urlSuffix\n", 3, $shortFile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $server IP-Address or domain
|
||||
*/
|
||||
@ -397,6 +442,14 @@ class Debug {
|
||||
return self::$send_warnings_to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to enable logging of errors.
|
||||
*/
|
||||
static function log_errors_to($logFile = ".sserrors") {
|
||||
self::$log_errors_to = $logFile;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deprecated. Send live errors and warnings to the given address.
|
||||
* @deprecated Use send_errors_to() instead.
|
||||
@ -424,20 +477,20 @@ class Debug {
|
||||
* @return unknown
|
||||
*/
|
||||
static function backtrace($returnVal = false, $ignoreAjax = false) {
|
||||
|
||||
$bt = debug_backtrace();
|
||||
|
||||
// Ingore functions that are plumbing of the error handler
|
||||
$ignoredFunctions = array('Debug::emailError','Debug::warningHandler','Debug::fatalHandler','errorHandler','Debug::showError','Debug::backtrace', 'exceptionHandler');
|
||||
$ignoredFunctions = array('DebugView::writeTrace', 'CliDebugView::writeTrace', 'Debug::emailError','Debug::warningHandler','Debug::fatalHandler','errorHandler','Debug::showError','Debug::backtrace', 'exceptionHandler');
|
||||
while( $bt && in_array(self::full_func_name($bt[0]), $ignoredFunctions) ) {
|
||||
array_shift($bt);
|
||||
}
|
||||
|
||||
$result = "<ul>";
|
||||
foreach($bt as $item) {
|
||||
if(Director::is_ajax() && !$ignoreAjax) {
|
||||
if(Director::is_cli() || (Director::is_ajax() && !$ignoreAjax)) {
|
||||
$result .= self::full_func_name($item,true) . "\n";
|
||||
$result .= "line $item[line] of " . basename($item['file']) . "\n\n";
|
||||
if(isset($item['line']) && isset($item['file'])) $result .= "line $item[line] of " . basename($item['file']) . "\n";
|
||||
$result .= "\n";
|
||||
} else {
|
||||
if ($item['function'] == 'user_error') {
|
||||
$name = $item['args'][0];
|
||||
@ -573,5 +626,3 @@ function errorHandler($errno, $errstr, $errfile, $errline, $errcontext) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -81,6 +81,43 @@ class DebugView {
|
||||
echo "</body></html>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Write information about the error to the screen
|
||||
*/
|
||||
public function writeError($httpRequest, $errno, $errstr, $errfile, $errline, $errcontext) {
|
||||
echo '<div class="info">';
|
||||
echo "<h1>" . strip_tags($errstr) . "</h1>";
|
||||
echo "<h3>$httpRequest</h3>";
|
||||
echo "<p>Line <strong>$errline</strong> in <strong>$errfile</strong></p>";
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a fragment of the a source file
|
||||
* @param $lines An array of file lines; the keys should be the original line numbers
|
||||
*/
|
||||
function writeSourceFragment($lines, $errline) {
|
||||
echo '<div class="trace"><h3>Source</h3>';
|
||||
echo '<pre>';
|
||||
foreach($lines as $offset => $line) {
|
||||
$line = htmlentities($line);
|
||||
if ($offset == $errline) {
|
||||
echo "<span>$offset</span> <span class=\"error\">$line</span>";
|
||||
} else {
|
||||
echo "<span>$offset</span> $line";
|
||||
}
|
||||
}
|
||||
echo '</pre>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a backtrace
|
||||
*/
|
||||
function writeTrace() {
|
||||
echo '<h3>Trace</h3>';
|
||||
Debug::backtrace();
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
Loading…
Reference in New Issue
Block a user