From db1e79c3b12508d242c02427497934b600b96d8e Mon Sep 17 00:00:00 2001 From: Sam Minnee Date: Mon, 11 Aug 2008 04:02:32 +0000 Subject: [PATCH] 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 --- dev/CliDebugView.php | 57 +++++++++++++++++++++++++++ dev/Debug.php | 91 ++++++++++++++++++++++++++++++++++---------- dev/DebugView.php | 37 ++++++++++++++++++ 3 files changed, 165 insertions(+), 20 deletions(-) create mode 100644 dev/CliDebugView.php diff --git a/dev/CliDebugView.php b/dev/CliDebugView.php new file mode 100644 index 000000000..301ae220d --- /dev/null +++ b/dev/CliDebugView.php @@ -0,0 +1,57 @@ + $line) { + echo ($offset == $errline) ? "* " : " "; + echo str_pad("$offset:",5); + echo wordwrap($line, 100, "\n "); + } + echo "\n"; + } + + /** + * Write a backtrace + */ + function writeTrace() { + Debug::backtrace(); + } + +} + +?> \ No newline at end of file diff --git a/dev/Debug.php b/dev/Debug.php index 4f5979208..73361ac39 100644 --- a/dev/Debug.php +++ b/dev/Debug.php @@ -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"); - - if(Director::isDev()) { + self::log_error_if_necessary( $errno, $errstr, $errfile, $errline, $errcontext, "Error"); + + if(Director::isDev() || Director::is_cli()) { Debug::showError($errno, $errstr, $errfile, $errline, $errcontext); } else { Debug::friendlyError($errno, $errstr, $errfile, $errline, $errcontext); } - die(); + exit(1); } /** @@ -267,6 +278,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 @@ -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 $errline in $errfile"); - echo '

Source

'; - Debug::showLines($errfile, $errline); - echo '

Trace

'; - Debug::backtrace(); - echo '
'; + $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 */ @@ -396,6 +441,14 @@ class Debug { static function get_send_warnings_to() { 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. @@ -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 = "