【发布时间】:2010-11-12 15:40:09
【问题描述】:
尝试使用默认的仅当前行错误消息来调试 PHP 是可怕的。产生错误时如何让 PHP 产生回溯(堆栈跟踪)?
【问题讨论】:
标签: php debugging error-handling stack-trace backtrace
尝试使用默认的仅当前行错误消息来调试 PHP 是可怕的。产生错误时如何让 PHP 产生回溯(堆栈跟踪)?
【问题讨论】:
标签: php debugging error-handling stack-trace backtrace
我用于安装产生回溯的错误处理程序的脚本:
<?php
function process_error_backtrace($errno, $errstr, $errfile, $errline, $errcontext) {
if(!(error_reporting() & $errno))
return;
switch($errno) {
case E_WARNING :
case E_USER_WARNING :
case E_STRICT :
case E_NOTICE :
case E_USER_NOTICE :
$type = 'warning';
$fatal = false;
break;
default :
$type = 'fatal error';
$fatal = true;
break;
}
$trace = array_reverse(debug_backtrace());
array_pop($trace);
if(php_sapi_name() == 'cli') {
echo 'Backtrace from ' . $type . ' \'' . $errstr . '\' at ' . $errfile . ' ' . $errline . ':' . "\n";
foreach($trace as $item)
echo ' ' . (isset($item['file']) ? $item['file'] : '<unknown file>') . ' ' . (isset($item['line']) ? $item['line'] : '<unknown line>') . ' calling ' . $item['function'] . '()' . "\n";
} else {
echo '<p class="error_backtrace">' . "\n";
echo ' Backtrace from ' . $type . ' \'' . $errstr . '\' at ' . $errfile . ' ' . $errline . ':' . "\n";
echo ' <ol>' . "\n";
foreach($trace as $item)
echo ' <li>' . (isset($item['file']) ? $item['file'] : '<unknown file>') . ' ' . (isset($item['line']) ? $item['line'] : '<unknown line>') . ' calling ' . $item['function'] . '()</li>' . "\n";
echo ' </ol>' . "\n";
echo '</p>' . "\n";
}
if(ini_get('log_errors')) {
$items = array();
foreach($trace as $item)
$items[] = (isset($item['file']) ? $item['file'] : '<unknown file>') . ' ' . (isset($item['line']) ? $item['line'] : '<unknown line>') . ' calling ' . $item['function'] . '()';
$message = 'Backtrace from ' . $type . ' \'' . $errstr . '\' at ' . $errfile . ' ' . $errline . ': ' . join(' | ', $items);
error_log($message);
}
if($fatal)
exit(1);
}
set_error_handler('process_error_backtrace');
?>
警告:影响各种'PHP Fatal Errors' 是无能为力的,因为 Zend 在他们的智慧中决定这些会忽略set_error_handler()。因此,您仍然会遇到无用的 final-location-only 错误。
【讨论】:
debug_backtrace 没有超出关闭功能(堆栈已经弹出),您可能想知道您仍然可以在关闭功能中访问全局范围的变量。因此,找到发生E_ERROR 的行并设置一个全局范围的变量,该变量等于上一行debug_backtrace 的内容。中提琴,你有一个完整的回溯,从你的致命错误之前的行开始,可以从 register_shutdown_function 定义的函数访问!
Xdebug 打印错误的回溯表,您不必编写任何 PHP 代码来实现它。
缺点是您必须将其安装为 PHP 扩展。
【讨论】:
sudo apt-get install php5-xdebug && sudo service php5-fpm restart 对我来说效果很好。谢谢!
这对于用 PHP 编写的 PHP 来说是更好的错误报告。不需要额外的扩展!
对于正常的 AJAXy 请求(处于暂停状态),在浏览器中显示所有错误的位置很简单。然后,所有错误都会为您提供整个堆栈跟踪的回溯和代码上下文,包括函数参数、服务器变量。
您需要做的就是包含一个文件并调用函数(在代码的开头),例如
require('php_error.php');
\php_error\reportErrors();
查看截图:
GitHub:https://github.com/JosephLenton/PHP-Error
我的叉子(有额外的修复):https://github.com/kenorb-contrib/PHP-Error
一个完整的 PHP 调试器类,支持异常、错误、警报(来自用户)、代码行和高亮标志。
示例用法:
<?php
include( dirname(dirname(__FILE__)) . '/src/Debug.php' );
//Catch all
Debug::register();
//Generate an errors
if( this_function_does_not_exists( ) )
{
return false;
}
?>
下面的例子展示了通过触发错误并使用用户定义的函数来处理内部异常:
<?php
function e($number, $msg, $file, $line, $vars) {
print_r(debug_backtrace());
die();
}
set_error_handler('e');
// set to the user defined error handler
$old_error_handler = set_error_handler("myErrorHandler");
// error handler function
function myErrorHandler($errno, $errstr, $errfile, $errline)
{
if (!(error_reporting() & $errno)) {
// This error code is not included in error_reporting
return;
}
switch ($errno) {
case E_USER_ERROR:
echo "<b>My ERROR</b> [$errno] $errstr<br />\n";
echo " Fatal error on line $errline in file $errfile";
echo ", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\n";
echo "Aborting...<br />\n";
var_dump(debug_backtrace());
exit(1);
break;
case E_USER_WARNING:
echo "<b>My WARNING</b> [$errno] $errstr<br />\n";
break;
case E_USER_NOTICE:
echo "<b>My NOTICE</b> [$errno] $errstr<br />\n";
break;
default:
echo "Unknown error type: [$errno] $errstr<br />\n";
break;
}
/* Don't execute PHP internal error handler */
return true;
}
见:http://www.php.net/manual/en/function.set-error-handler.php
注意:一次只能有一个错误异常。当您调用 set_error_handler() 函数时,它将返回旧错误处理程序的名称。您可以存储它并从您的错误处理程序中自己调用它 - 从而允许您拥有多个错误处理程序。
对于更高级的解决方案,您可以使用 PHP 的 XDebug 扩展。
默认情况下,当 XDebug 被加载时,它应该自动显示回溯,以防出现任何致命错误。或者您跟踪文件 (xdebug.auto_trace) 以对整个请求进行非常大的回溯,或者进行分析 (xdebug.profiler_enable) 或 other settings。如果跟踪文件太大,您可以使用 xdebug_start_trace() 和 xdebug_stop_trace() 转储部分跟踪。
安装
使用 PECL:
pecl install xdebug
在 Linux 上:
sudo apt-get install php5-xdebug
在 Mac 上(使用 Homebrew):
brew tap josegonzalez/php
brew search xdebug
php53-xdebug
我的配置示例:
[xdebug]
; Extensions
extension=xdebug.so
; zend_extension="/YOUR_PATH/php/extensions/no-debug-non-zts-20090626/xdebug.so"
; zend_extension="/Applications/MAMP/bin/php/php5.3.20/lib/php/extensions/no-debug-non-zts-20090626/xdebug.so" ; MAMP
; Data
xdebug.show_exception_trace=1 ; bool: Show a stack trace whenever an exception is raised.
xdebug.collect_vars = 1 ; bool: Gather information about which variables are used in a certain scope.
xdebug.show_local_vars=1 ; int: Generate stack dumps in error situations.
xdebug.collect_assignments=1 ; bool: Controls whether Xdebug should add variable assignments to function traces.
xdebug.collect_params=4 ; int1-4: Collect the parameters passed to functions when a function call is recorded.
xdebug.collect_return=1 ; bool: Write the return value of function calls to the trace files.
xdebug.var_display_max_children=256 ; int: Amount of array children and object's properties are shown.
xdebug.var_display_max_data=1024 ; int: Max string length that is shown when variables are displayed.
xdebug.var_display_max_depth=3 ; int: How many nested levels of array/object elements are displayed.
xdebug.show_mem_delta=0 ; int: Show the difference in memory usage between function calls.
; Trace
xdebug.auto_trace=0 ; bool: The tracing of function calls will be enabled just before the script is run.
xdebug.trace_output_dir="/var/log/xdebug" ; string: Directory where the tracing files will be written to.
xdebug.trace_output_name="%H%R-%s-%t" ; string: Name of the file that is used to dump traces into.
; Profiler
xdebug.profiler_enable=0 ; bool: Profiler which creates files read by KCacheGrind.
xdebug.profiler_output_dir="/var/log/xdebug" ; string: Directory where the profiler output will be written to.
xdebug.profiler_output_name="%H%R-%s-%t" ; string: Name of the file that is used to dump traces into.
xdebug.profiler_append=0 ; bool: Files will not be overwritten when a new request would map to the same file.
; CLI
xdebug.cli_color=1 ; bool: Color var_dumps and stack traces output when in CLI mode.
; Remote debugging
xdebug.remote_enable=off ; bool: Try to contact a debug client which is listening on the host and port.
xdebug.remote_autostart=off ; bool: Start a remote debugging session even GET/POST/COOKIE variable is not present.
xdebug.remote_handler=dbgp ; select: php3/gdb/dbgp: The DBGp protocol is the only supported protocol.
xdebug.remote_host=localhost ; string: Host/ip where the debug client is running.
xdebug.remote_port=9000 ; integer: The port to which Xdebug tries to connect on the remote host.
xdebug.remote_mode=req ; select(req,jit): Selects when a debug connection is initiated.
xdebug.idekey="xdebug-cli" ; string: IDE Key Xdebug which should pass on to the DBGp debugger handler.
xdebug.remote_log="/var/log/xdebug.log" ; string: Filename to a file to which all remote debugger communications are logged.
启用开发:
/**
* Implements hook_watchdog().
*/
function foo_watchdog($log_entry) {
if ($log_entry['type'] == 'php' && $log_entry['severity'] <= WATCHDOG_WARNING) {
function_exists('dd') && dd(debug_backtrace());
}
}
上述函数会将每个错误的回溯记录到临时文件中(默认为/tmp/drupal_debug.txt)。
或者通过drush eval "echo file_directory_temp() . '/drupal_debug.txt'找到文件。
在未启用 Devel 的情况下,使用老式方法:var_dump(debug_backtrace()); 而不是 dd()。
【讨论】:
var_dump(debug_backtrace());exit; 在出现错误之前在线(并在您的网络浏览器中查看源代码以获得更好的格式)。其他调试工具可能包括strace、dtrace等。正确使用,可以找到问题的根源。
我只是尝试在违规行设置一个包含 debug_backtrace() 内容的会话变量,然后使用 register_shutdown_function() 打印它。像魅力一样工作。
【讨论】:
您可以使用debug_backtrace
【讨论】:
这就是你的做法:
set_error_handler(function($errorType){
if(error_reporting() & $errorType){
?><pre><?
debug_print_backtrace();
?></pre><?
}
}) ;
它需要 PHP 5.3+,因为它使用闭包。如果您需要较低的 PHP 支持,只需将闭包转换为普通函数即可。
【讨论】:
<?= ?> 是允许的
$backtrace = debug_backtrace();
不久前我写了一个little article about backtracing
【讨论】:
set_error_handler() + debug_backtrace() + debug_print_backtrace() 在 PHP5 中
【讨论】:
如果您无法安装调试器,则使用此函数围绕致命错误来获取“致命堆栈”。检查下面的代码和示例,以更好地解释如何使用它:
// Give an extra parameter to the filename
// to save multiple log files
function _fatalog_($extra = false)
{
static $last_extra;
// CHANGE THIS TO: A writeable filepath in your system...
$filepath = '/var/www/html/sites/default/files/fatal-'.($extra === false ? $last_extra : $extra).'.log';
if ($extra===false) {
unlink($filepath);
} else {
// we write a log file with the debug info
file_put_contents($filepath, json_encode(debug_backtrace()));
// saving last extra parameter for future unlink... if possible...
$last_extra = $extra;
}
}
这是一个如何使用它的示例:
// A function which will produce a fatal error
function fatal_example()
{
_fatalog_(time()); // writing the log
$some_fatal_code = array()/3; // fatality!
_fatalog_(); // if we get here then delete last file log
}
终于读到了日志的内容……
var_dump(json_decode(file_get_contents('/path/to-the-fatal.log')));
希望有帮助!
【讨论】:
PHP DeBugger 还可以进行类似于 PHP 错误的回溯,并提供更多选项。
如果您愿意,可以使用 set_error_handler 和 debug_backtrace 轻松制作自己的
set_error_handler ($error_handler, error_reporting);
/**
* @var int $errno the error number
* @var string $errstr the error message
* @var string $errfile the error file
* @var int $errline the line of the error
*/
$error_handler = function($errno, $errstr, $errfile, $errline){
$trace = debug_backtrace();
array_shift($backtrace);//remove the stack about this handler
foreach($trace as $k => $v){
//parse your backtrace
}
}
另外请注意,对于回溯中的内部堆栈,某些键不会被设置。如果您有所有错误,请务必在使用它之前检查密钥是否存在:)
【讨论】:
E_WARNING but not not E_ERROR 所以你必须为致命错误做点别的事情 register_shutdown_function 是个不错的选择。