PHP错误处理的宏大统一理论

时间:2011-06-06 02:09:00

标签: php error-handling

aka,寻求通用错误处理程序(商业上使用的ΚΚ)

我怀疑我是最好的PHP程序员,所以,虽然我有set_error_handler()我自己的通用错误处理程序,但我想知道其他人做了什么以及是否有“最好”(抱歉,如果这听起来很主观 - 我只想提出一般方法(但即使是'最佳实践'标签也已从SO中移除)。)

为了客观,这就是我认为需要的。如果我错了,请纠正我。如果你同意,请指出一些好的代码。

  • 我想尽可能多地捕获信息 - 不知道错误是什么。

  • 因此,例如,转储调用堆栈是有意义的。

  • $_GET$_POST$_SESSION

  • 我想要调用堆栈&全球印刷得很漂亮

  • 我想要一些“纯文本”布局,而不是CSS&花哨的JS扩展/崩溃信息。我的用户可能需要剪切/粘贴到电子邮件中,甚至可以打印出来。传真。

  • 我希望能够添加我自己设计的标题,最好是作为参数,但如果需要,我可以破解代码。标题可能包括程序版本,时间戳等(在我的情况下,我有一个审计跟踪,因此我可以包括用户的最后几个操作,这导致崩溃)。

  • 某些用户可能会允许我的代码自动通过电子邮件发送报告,有些用户可能希望将其预览为forst&他们通过电子邮件发送,有些人可能不希望我发送电子邮件。

3 个答案:

答案 0 :(得分:28)

我建议采用“例外”的方式。

当出现用户错误时抛出异常,您可以将php错误转换为异常,如下所示:

function exception_error_handler($errno, $errstr, $errfile, $errline ) {
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
set_error_handler("exception_error_handler");

虽然这种行为在OOP环境中效果最好。如果你没有单一的入口点(比如前控制器),你也可能会遇到松散的异常:

function myException($exception)
{
    echo "<b>Exception:</b> " , $exception->getMessage();
}

set_exception_handler('myException');

使用异常进行简单调试会有点像这样:

function parseException($e) {
    $result = 'Exception: "';
    $result .= $e->getMessage();
    $trace = $e->getTrace();
    foreach (range(0, 10) as $i) {
        $result .= '" @ ';
        if (!isset($trace[$i])) {
            break;
        }
        if (isset($trace[$i]['class'])) {
            $result .= $trace[$i]['class'];
            $result .= '->';
        }
        $result .= $trace[$i]['function'];
        $result .= '(); ';
        $result .= $e->getFile() . ':' . $e->getLine() . "\n\n";
    }

    return $result;
}

从那里评估全球化等是在公园散步。您可能会从Symfony Framework调试工具栏中寻找灵感,它提供了许多这些请求。

答案 1 :(得分:13)

我无法相信这还没有被建议。

在我公司,我们只使用Exceptions和自定义错误处理程序。错误处理程序将使用以下命令编译调试消息:

  • GET,POST,COOKIES,SESSION,GLOBALS
  • 追踪
  • 错误消息(异常中的消息或警告,是的,您还应该捕获警告,甚至严重错误)。

然后该邮件会发送到监控服务器,如果失败,它会尝试向我们发送电子邮件,它会失败,它会尝试< strong>登录到数据库(如果失败则会记录到文件)。如果错误是“致命”错误,因为无法保证您的输出,您可以选择抛出500标题并打印默认的“oops”消息。

我建议您始终自动报告所有错误。您不想知道的唯一错误是由用户错误输入引起的错误。在这种情况下,错误应以某种方式呈现给用户。我发现,对于每个异常,您都可以确定这是系统中的错误还是用户的错误。例如:链接到页面,然后删除页面(这将导致404)。您不想了解404,但您的客户确实知道。

您始终想知道所有错误的原因很简单。如果您的系统有一个错误,除非您遇到自己,或者您的客户报告(他们几乎从不这样做),否则您将无法了解它。 我们曾经有一个擅长隐藏所有错误的系统,而且它是超级越野车。我们开始暴露所有错误,两年后,这是一个非常稳定的应用程序。

此外。有一个技巧可以用来捕捉讨厌的 Fatal Errors 。您可以使用register_shutdown_handler注册一个在PHP脚本完成后始终运行的函数。然后,您可以使用error_get_last来检查致命错误。然后,您可以重复上述步骤以使您知道错误。这是有效的,我一直都在使用它。

将其四舍五入。无论您选择何种错误报告,都与应用程序的用户将看到的内容无关。你可以选择给他一个错误报告,你甚至可以在那时向他询问反馈。但大多数情况下,系统中只有一个错误,因此用户无法真正做到这一点。

答案 2 :(得分:3)

将所有错误处理公开给最终用户并不是一个好主意。

1)它暴露了代码的内部结构 - 好吧所以即使潜在的攻击者拥有完整的源代码也应该是安全的 - 但是让他们的生活变得更轻松没有意义。

2)您是否真的相信最终用户会认真地复制所有信息并将其发回给您?

3)你用他们不关心的大量信息压倒了用户。

我处理这些的方法是捕获与实际服务器端一样多的信息(并在发生错误时将其写入平面文件)然后向用户提供有意义的错误消息,包括我可以在哪里找到错误的简单参考日志。对于大规模系统,我还建议捕获错误的指纹(例如堆栈跟踪的md5散列的最后6位数),以允许帮助台管理和分类同一潜在故障的多个报告事件。

请记住,使用PHP时,脚本完成时会清除所有数据。如果您使用C语言编写Java应用程序或程序,那么您真的不希望不断地累积数据 - 因此唯一的选择是将调试/信息条目写入日志,然后一旦发生错误就写入堆栈跟踪到日志。但是对于网页的PHP,我通常在PHP变量中保存这些日志,然后在发生错误时将其写入文件 以及堆栈跟踪(或者当脚本完成时我已经在代码中设置了一个标志,例如通过会话,用于分析它在非错误情况下的行为方式。)

您记录的详细信息取决于您 - 我通常记录所有fn入口和出口点以及任何SQL查询。

e.g。

function logit($msg)
{
   global $runlog;
   static $basetime;
   if (!$basetime) $basetime=time();
   $runlog.="+" . time()-$basetime . "s : " . $msg . "\n";
}