错误记录,顺利进行

Joh*_*ohn 37 php pdo

我一直在阅读特别是'错误记录'我已经提出了函数'error_log',它似乎是一个用来处理错误记录的好工具.但最流畅和最好的使用方法是怎样的呢?

如果我有

try {
     //try a database connection...

} catch (PDOException $e) {
    error_log($e->getMessage(), 3, "/var/tmp/my-errors.log");

}
Run Code Online (Sandbox Code Playgroud)

这将记录my-errors.log文件中的错误.但是,如果我有时需要更改文件所在位置,新文件夹或其他内容.如果我有大量文件,我需要更改它们.

现在我开始考虑使用变量来设置错误日志的路径.当然可以工作,但是如果我想在函数或类方法中使用error_log呢?然后我需要将变量设置为全局,但这被认为是不好的做法!但是,如果我不应该在课堂上深入使用这个功能,那么这也不会被认为是不好的做法吗?这里有什么好的解决方案?

<?php

function legit() {
    try {
        if (1 == 1) {
            throw new Exception('There was an error here');
        }
    } catch (Exception $e) {
        throw new Exception('throw the error to the try-catch outside the function...');
    }

}

try {
    legit();
} catch (Exception $e) {
    echo 'error here' . $e->getMessage();

    //log it
}
Run Code Online (Sandbox Code Playgroud)

这是我上面讨论的一个例子(没有在类/函数中深入记录......这是一个好方法吗?)

进一步:

我不太确定我应该如何使用一般的例外.假设我想在方法中使用SQL对数据库执行INSERT,我是否会使用try/catch然后在失败时重新抛出异常?这被认为是好习惯吗?请举例.

Pau*_*aul 83

首先,我想赞扬你在PHP中查看标准错误方法.不幸的是error_log,你发现它有一些局限性.

这是一个很长的答案,请继续阅读以了解:

  1. 错误
    • 直接记录错误vs trigger_errorset_error_handler
    • 好错误哪里坏 - 致命错误.
  2. 例外
    • SPL
    • 他们怎么办?
    • 建立
    • 用法

TL; DR使用trigger_error筹集的错误和set_error_handler用于记录他们.

1.错误

如果程序中的内容没有按预期运行,您通常会提出错误,以便通知某人或某事.错误是针对程序可能继续的情况,但发生了一些值得注意的,可能有害的或错误的.此时,许多人希望立即使用他们选择的日志包来记录错误.我认为这是错误的做法.我建议使用trigger_error来引发错误,以便可以使用回调设置来处理它set_error_handler.让我们比较这些选项:

直接记录错误

所以,您选择了日志包.现在,您已准备好在代码中发生错误的地方将调用传播到记录器.让我们看一下你可能做的一个电话(我将使用类似的记录器来回答Jack的回答):

Logger::getLogger('standard')->error('Ouch, this hurts');

您需要什么来运行此代码?

    Class:  Logger
    Method: getLogger
    Return: Object with method 'error'

这些是使用此代码所需的依赖项.每个想要重用此代码的人都必须提供这些依赖关系.这意味着标准的PHP配置将不再足以重用您的代码.在最好的情况下,使用依赖注入仍然需要将记录器对象传递到可能发出错误的所有代码中.

此外,除了代码负责的任何内容之外,它还负责记录错误.这违反了单一责任原则.

我们可以看到直接记录错误是不好的.

trigger_error来救援

PHP有一个函数被调用trigger_error,它可以像标准函数一样用于引发错误.您使用它的错误级别错误级别常量中定义.当你必须使用用户错误的一个用户:E_USER_ERROR,E_USER_WARNING或默认值E_USER_NOTICE(其他错误级别被保留用于标准的功能等).使用标准的PHP函数来引发错误允许代码重复使用任何标准的PHP安装!我们的代码不再负责记录错误(只确保它被引发).

使用trigger_error我们只执行错误记录过程的一半(引发错误)并保留响应错误处理程序错误的责任,这将在下面介绍.

错误处理程序

我们使用该set_error_handler函数设置了一个自定义错误处理程序(请参阅代码设置).此自定义错误处理程序替换标准PHP错误处理程序,该处理程序通常在Web服务器错误日志中记录消息,具体取决于PHP配置设置.我们仍然可以通过false在自定义错误处理程序中返回来使用此标准错误处理程

自定义错误处理程序只有一个责任:响应错误(包括您要执行的任何日志记录).在自定义错误处理程序中,您可以完全访问系统,并可以运行所需的任何类型的日志记录.实际上任何使用Observer设计模式的记录器都可以(我不打算进入那个,因为我认为它是次要的).这应该允许您挂钩新的日志观察器以将输出发送到您需要的位置.

您可以完全控制在代码的单个可维护部分中执行您喜欢的错误.现在可以在项目之间快速轻松地更改错误日志记录,也可以在页面之间的单个项目中更改错误日志记录.有趣的是,即使是@被抑制的错误也会使其成为errno0 的自定义错误处理程序,如果error_reporting不遵守该掩码,则应该使用0 .

当好错误变坏 - 致命错误

不可能继续某些错误.以下错误级别不能从自定义错误处理程序处理:E_ERROR,E_PARSE,E_CORE_ERROR,E_CORE_WARNING,E_COMPILE_ERROR,E_COMPILE_WARNING.当标准函数调用触发这些类型的错误时,将跳过自定义错误处理程序并关闭系统.这可以通过以下方式生成:

call_this_function_that_obviously_does_not_exist_or_was_misspelt();

这是一个严重的错误!无法恢复,系统即将关闭.我们唯一的选择是register_shutdown_function达成停工协议.但是,只要脚本完成(成功,不成功),就会执行此功能.error_get_last当最后一个错误是致命错误时,可以使用此信息记录一些基本信息(此时系统几乎已关闭).发送正确的状态代码并显示您选择的内部服务器错误类型页面也很有用.

2.例外情况

可以通过与基本错误非常相似的方式处理异常.trigger_error您的代码将抛出异常而不是异常(手动使用throw new Exception或来自标准函数调用).使用set_exception_handler定义要用来处理与异常的回调.

SPL

标准PHP库(SPL)提供了例外.它们是我提出异常的首选方式,因为trigger_error它们是PHP的标准部分,不会为代码引入额外的依赖性.

他们怎么办?

抛出异常时,可以做出三种选择:

  1. 抓住并修复它(代码然后继续,就好像没有发生任何不好的事情).
  2. 抓住它,附加有用的信息并重新抛出它.
  3. 让它泡到更高的水平.

在堆栈的每个级别进行这些选择.最终一旦它冒泡到最高级别,您将设置的回调set_exception_handler将被执行.这是您的日志记录代码所属的位置(出于与错误处理相同的原因),而不是遍布catch代码中的语句.

3.代码

建立

错误处理程序

function errorHandler($errno , $errstr, $errfile, $errline, $errcontext)
{
    // Perform your error handling here, respecting error_reporting() and
    // $errno.  This is where you can log the errors.  The choice of logger
    // that you use is based on your preference.  So long as it implements
    // the observer pattern you will be able to easily add logging for any
    // type of output you desire.
}

$previousErrorHandler = set_error_handler('errorHandler');
Run Code Online (Sandbox Code Playgroud)

异常处理程序

function exceptionHandler($e)
{
    // Perform your exception handling here.
}

$previousExceptionHandler = set_exception_handler('exceptionHandler');
Run Code Online (Sandbox Code Playgroud)

关机功能

function shutdownFunction()
{
    $err = error_get_last();

    if (!isset($err))
    {
        return;
    }

    $handledErrorTypes = array(
        E_USER_ERROR      => 'USER ERROR',
        E_ERROR           => 'ERROR',
        E_PARSE           => 'PARSE',
        E_CORE_ERROR      => 'CORE_ERROR',
        E_CORE_WARNING    => 'CORE_WARNING',
        E_COMPILE_ERROR   => 'COMPILE_ERROR',
        E_COMPILE_WARNING => 'COMPILE_WARNING');

    // If our last error wasn't fatal then this must be a normal shutdown.  
    if (!isset($handledErrorTypes[$err['type']]))
    {
        return;
    }

    if (!headers_sent())
    {
        header('HTTP/1.1 500 Internal Server Error');
    }

    // Perform simple logging here.
}

register_shutdown_function('shutdownFunction');
Run Code Online (Sandbox Code Playgroud)

用法

错误

// Notices.
trigger_error('Disk space is below 20%.', E_USER_NOTICE);
trigger_error('Disk space is below 20%.'); // Defaults to E_USER_NOTICE

// Warnings.
fopen('BAD_ARGS'); // E_WARNING fopen() expects at least 2 parameters, 1 given
trigger_error('Warning, this mode could be dangerous', E_USER_WARNING);

// Fatal Errors.    
// This function has not been defined and so a fatal error is generated that
// does not reach the custom error handler.
this_function_has_not_been_defined();
// Execution does not reach this point.

// The following will be received by the custom error handler but is fatal.
trigger_error('Error in the code, cannot continue.', E_USER_ERROR);
// Execution does not reach this point.
Run Code Online (Sandbox Code Playgroud)

例外

之前的三个选项中的每一个都以通用方式列出,修复它,附加到它并让它冒泡.

1可修复:

try
{
    $value = code_that_can_generate_exception();
}
catch (Exception $e)
{
    // We decide to emit a notice here (a warning could also be used).
    trigger_error('We had to use the default value instead of ' .
                  'code_that_can_generate_exception\'s', E_USER_NOTICE);
    // Fix the exception.
    $value = DEFAULT_VALUE;
}

// Code continues executing happily here.
Run Code Online (Sandbox Code Playgroud)

2附加:

请注意以下如何code_that_can_generate_exception()不知道$context.此级别的catch块具有更多信息,如果通过重新抛出它可以将其附加到异常中.

try
{
    $context = 'foo';
    $value = code_that_can_generate_exception();
}
catch (Exception $e)
{
    // Raise another exception, with extra information and the existing
    // exception set as the previous exception. 
    throw new Exception('Context: ' . $context, 0, $e);
}
Run Code Online (Sandbox Code Playgroud)

3让它冒出来:

// Don't catch it.
Run Code Online (Sandbox Code Playgroud)


Ja͢*_*͢ck 35

已经要求让这个答案更适用于更多的观众,所以这里有.

前言

在编写应用程序时,错误处理通常不是您想要考虑的第一件事; 作为间接的结果,它会在需要时被拴上.但是,利用PHP中的现有机制也不需要花费太多成本.

这是一篇相当冗长的文章,所以我把它分解为逻辑文本集.

触发错误

在PHP中,有两种不同的方法可以触发错误:

  1. PHP本身的错误(例如使用未定义的变量)或内部函数(例如imagecreatefromjpeg无法打开文件),
  2. 用户代码触发的错误trigger_error,

这些通常印在你的页面上(除非display_errors被关闭或error_reporting为零),这应该是生产机器的标准,除非你写出像我这样的完美代码......继续前进); 这些错误也可以被捕获,通过set_error_handler稍后解释,让您可以一瞥代码中的任何障碍.

抛出异常

例外与三种主要方式的错误不同:

  1. 处理它们的代码可能远离它们被抛出的地方.必须将原点的变量状态显式传递给Exception构造函数,否则只有堆栈跟踪.
  2. 异常和catch之间的代码被完全跳过,而在发生错误(并且它不是致命的)之后代码仍然继续.
  3. 它们可以从主Exception类扩展; 这允许您捕获和处理特定的异常,但让其他人在堆栈中冒泡,直到被其他代码捕获.另见:http://www.php.net/manual/en/language.exceptions.php

稍后将给出抛出异常的示例.

处理错误

通过注册错误处理程序,捕获和处理错误非常简单,例如:

function my_error_handler($errno, $errstr, $errfile = 'unknown', $errline = 0, array $errcontext = array())
{
    // $errcontext is very powerful, it gives you the variable state at the point of error; this can be a pretty big variable in certain cases, but it may be extremely valuable for debugging
    // if error_reporting() returns 0, it means the error control operator was used (@)
    printf("%s [%d] occurred in %s:%d\n%s\n", $errstr, $errno, $errfile, $errline, print_r($errcontext, true));

    // if necessary, you can retrieve the stack trace that led up to the error by calling debug_backtrace()

    // if you return false here, the standard PHP error reporting is performed
}

set_error_handler('my_error_handler');
Run Code Online (Sandbox Code Playgroud)

对于kick,您可以ErrorException通过注册以下错误处理程序(PHP> = 5.1)将所有错误转换为a:

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

set_error_handler("exception_error_handler");
Run Code Online (Sandbox Code Playgroud)

处理异常

在大多数情况下,您处理异常尽可能接近导致其允许备份计划的代码.例如,您尝试插入数据库记录并抛出主键约束异常; 您可以通过更新记录来恢复(设计为大多数数据库可以自己处理).有些例外无法在本地处理,因此您希望这些例外级联.例:

function insertRecord($user, $name)
{
    try {
        if (true) {
            throw new Exception('This exception should not be handled here');
        }
        // this code is not executed
        $this->db->insert('users', array('uid' => $user, 'name' => $name));
    } catch (PDOException $e) {
        // attempt to fix; an exception thrown here will cascade down
        throw $e; // rethrow exception

        // since PHP 5.3.0 you can also nest exceptions
        throw new Exception("Could not insert '$name'", -1, $e);
    } catch (WhatEverException $e) {
        // guess what, we can handle whatever too
    }
}
Run Code Online (Sandbox Code Playgroud)

滑溜溜的例外

那么当你没有在任何地方发现异常时会发生什么?您也可以通过使用来捕获它set_exception_handler.

function my_exception_handler(Exception $exception)
{
    // do your stuff here, just don't throw another exception here
}

set_exception_handler('my_exception_handler');
Run Code Online (Sandbox Code Playgroud)

除非您没有有意义的方法在代码中的任何位置处理异常,否则不鼓励这样做.

记录错误/异常

现在您正在处理错误,您必须将其记录到某处.在我的例子中,我使用Apache从Java移植到PHP的项目,称为LOG4PHP.还有其他一些,但它说明了灵活的测井设施的重要性.

它使用以下概念:

  1. 记录器 - 代表您执行记录的命名实体; 它们可以特定于项目中的类或作为通用记录器共享,
  2. Appenders - 每个日志请求可以根据预定义的条件(例如日志级别)发送到一个或多个目的地(电子邮件,数据库,文本文件),
  3. 级别 - 日志从调试消息分类为致命错误.

用于说明不同消息级别的基本用法:

Logger::getLogger('main')->info('We have lift off');
Logger::getLogger('main')->warn('Rocket is a bit hot');
Logger::getLogger('main')->error('Houston, we have a problem');
Run Code Online (Sandbox Code Playgroud)

使用这些概念,您可以建立一个非常强大的日志工具; 例如,在不更改上述代码的情况下,您可以实现以下设置:

  1. 收集数据库中的所有调试消息供开发人员查看; 您可以在生产服务器上禁用它,
  2. 将警告收集到您可能在一天结束时发送电子邮件的每日文件中,
  3. 立即发送致命错误的电子邮件.

  • 哈哈哈好休斯顿参考:P (4认同)