【问题标题】:The grand, unified theory of PHP error handling宏大、统一的 PHP 错误处理理论
【发布时间】:2011-06-06 02:09:00
【问题描述】:

又名,寻求通用错误处理程序(ΟΚ 用于商业用途)

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

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

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

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

  • $_GET$_POST$_SESSION

  • 我希望调用堆栈和全局变量打印得漂亮

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

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

  • 有些用户可能允许我的代码自动通过电子邮件发送报告,有些用户可能希望先预览并通过电子邮件发送,有些用户可能不希望我发送电子邮件。

【问题讨论】:

  • 史诗般的问题标题,+1。
  • 不管是什么问题,我都喜欢那个闪亮的标题。
  • 从 PHP 7 开始,旧式 PHP 错误将被视为异常,允许 try ... catch 等。这应该会使 PHP 错误处理好上亿倍(并有望消除讨厌的 @操作员一劳永逸)。

标签: php error-handling


【解决方案1】:

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

用户出错时抛出异常,可以将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 框架调试工具栏寻找灵感,它提供了许多此类请求。

【讨论】:

  • 我喜欢这样 - 我并没有真正考虑异常 - 更像是数据库问题(但我想它们也可能是异常)或编码错误(默认:“未知开关项”)和类似。我们想出的是否应该包含所有这些。
  • 它的好处是,在您的开发中,您可以添加对不同类型错误的精细支持。例如,在我的 ajax 环境中,向用户传播任何类型的错误变得非常容易。它让我的生活轻松了很多。
  • +1 用于建议单点入口。您应该始终拥有尽可能少的入口点。
  • 小心exception_handlers。与常规错误处理函数不同,您的脚本将在异常处理函数结束时终止,因此所有日志记录、通知、屏幕输出等都必须在该标记之前完成。
  • 我来晚了,但有趣的是,Laravel 在他们的error handling class 中基本上使用了这种精确的方法。
【解决方案2】:

我不敢相信这还没有被建议。

在我的公司,我们只是将Exceptions 与自定义错误处理程序一起使用。错误处理程序将编译一条调试消息:

  • GET、POST、COOKIES、会话、全局
  • 痕迹
  • 错误消息(异常消息或警告中的消息,是的,您还应该捕获警告,甚至是STRICTness错误)。

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

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

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

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

把它四舍五入。无论您为错误报告选择什么,都与您的应用程序用户将看到的内容无关。你可以选择给他一个错误报告,你甚至可以在那个时候要求他提供反馈。但大多数情况下,您的系统中只有一个错误,因此用户无法真正使用它。

【讨论】:

  • +1 用于回退安全日志记录。如果我可能会问我一直在思考这样的系统,你是如何设计监控服务的?它是在 syslog 级别、消息传递服务还是用 php 编写的东西?
  • 它实际上只是通过 HTTP 请求调用 Web 服务。所以这是一个非常活跃的监控系统,效果很好。
  • +1 听起来不错。任何示例代码或指向某些代码的指针,因为您不能发布公司的代码?
  • 我认为 php.net 网站上的示例应该足以让您开始学习。当然,我不会发布任何有关监控网络服务的内容;)
  • Mawg:也许这会有所帮助:stackoverflow.com/questions/6897668/… ?
【解决方案3】:

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

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

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

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

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

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

您记录多少细节取决于您 - 我通常会记录所有 fn 入口和出口点,以及任何 SQL 查询。

例如

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

【讨论】:

  • 有什么理由将执行写入平面文件而不是数据库?
  • @jbarlow,当然这是通过邮件优雅地通知某人的好情况(您的应用程序将关闭),我只是发现存储在数据库表中更容易排序,例如......如果用户使用特定页面报告了问题,我会将 REQUEST_URI 与错误详细信息一起存储,然后我可以执行查询
  • 我的建议是根本不向用户提供此信息,或者选择性地提供。一些极其致命的错误(如无响应的数据库)可以以非常通用的“哎呀”方式处理,只需显示通用错误消息并在后台发送警报(电子邮件或其他协议,如 web 服务)。
  • 理想情况下,我只想通过电子邮件发送该信息,但在某些地方,客户无法访问互联网。在这种情况下,我希望他打印并传真给我。
  • @Jaitsu:错误处理必须非常强大 - 写入数据库有各种复杂的平面文件不存在 - 尽管使用日志 API 而不是更可取直接写入文件。请注意,您永远不应该尝试直接从 PHP 编写单个平面文件作为日志文件或数据库 - 我说的是为日志目录中的每个条目编写一个文件。
猜你喜欢
  • 2010-12-31
  • 2010-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多