顺利记录错误 [英] Error logging, in a smooth way

查看:40
本文介绍了顺利记录错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在阅读错误日志记录",并且想出了"error_log"函数,它似乎是处理错误日志记录的好工具.但是,最流畅和最佳的使用方式是什么?

I've been reading on in particular 'error logging' And I have come up with the function 'error_log' which seem to be a good tool to use to handle the error logging. But how is the smoothest and best way to use it?

如果我有

try {
     //try a database connection...

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

}

这会将错误记录在my-errors.log文件中.但是,如果我有时需要更改文件位置,新文件夹或其他内容的位置,该怎么办?如果我有大量文件,则需要全部更改.

This would log the error in the my-errors.log file. But what If I sometime need to change the position of where the file is, a new folder, or something. If I have tons of files I need to change them all.

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

Now I started of thinking to use a variable to set the path to the error log. Sure that could work, but what If I want to use the error_log in a function or class method? Then I would need to set the variable as global, but that is considered bad practise! But what If I shouldn't use the function deep in a class, wouldn't that also be considered bad practise? What is a good solution here?

<?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
}

这是我在上面谈论的一个示例(没有在类/函数中进行深入日志记录.这是一个好方法吗?)

This is an example of what I was talking about above (Not having the logging deep in a class/function... Is it a good way?)

Furtheron:

Furtheron:

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

I am not quite sure how I should use the Exceptions in general. Let's say I want to do a INSERT to a database with SQL inside a method, would I use a try/catch and then rethrow the exception if it fails? Is that considered good practise? Examples please.

推荐答案

首先,我谨向您推荐一下PHP中的标准错误方法.不幸的是,您发现error_log有一些限制.

Firstly, I'd like to commend you for looking at the standard error methods within PHP. Unfortunately error_log has some limitations as you found out.

这是一个很长的答案,请继续阅读以下内容:

This is a long answer, read on to find out about:

  1. 错误
    • 直接记录错误与trigger_errorset_error_handler
    • 好的错误会变坏的地方-致命错误.
  1. Errors
    • Logging the error directly vs trigger_error and set_error_handler
    • Where good errors go bad - Fatal Errors.
  • SPL
  • 该怎么办?
  • 设置
  • 用法

TL; DR 使用trigger_error引发错误,使用set_error_handler记录错误.

TL;DR Use trigger_error for raising errors and set_error_handler for logging them.

当程序中的运行不正常时,您通常会引发错误,以便通知某人或某物.错误是指程序可以继续运行,但发生了一些值得注意的,可能有害或错误的情况.此时,许多人希望使用所选的日志记录包立即记录该错误.我相信这完全是错误的事情.我建议使用trigger_error引发错误,以便可以使用set_error_handler设置的回调来处理该错误.让我们比较一下这些选项:

When things don't go as expected in your program, you will often want to raise an error so that someone or something is notified. An error is for a situation where the program may continue, but something noteworthy, possibly harmful or erroneous has occurred. At this point many people want to log the error immediately with their logging package of choice. I believe this is exactly the wrong thing to do. I recommend using trigger_error to raise the error so that it can be handled with a callback set by set_error_handler. Lets compare these options:

直接记录错误

因此,您已经选择了日志记录包.现在,您可以将代码中发生错误的任何地方分散到记录器中.让我们来看一个您可能会打的电话(我将使用与Jack回答中类似的记录器):

So, you have chosen your logging package. Now you are ready to spread the calls to your logger wherever an error occurs in your code. Lets look at a single call that you might make (I'll use a similar logger to the one in Jack's answer):

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

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

What do you need in place to run this code?


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

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

These are the dependencies that are required to use this code. Everyone who wants to re-use this code will have to provide these dependencies. This means that a standard PHP configuration will no longer be sufficient to re-use your code. With the best case, using Dependency Injection you still require a logger object to be passed into all of your code that can emit an error.

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

Also, in addition to whatever the code is responsible for, it also has responsibility for logging the error. This goes against the Single Responsibility Principle.

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

触发错误进行救援

PHP具有一个名为 trigger_error 的函数,可以使用就像标准函数一样引发错误.与它一起使用的错误级别在错误级别常量中定义.作为用户,您必须使用以下用户错误之一:E_USER_ERRORE_USER_WARNING或默认值E_USER_NOTICE(其他错误级别保留用于标准功能等).使用标准的PHP函数引发错误,可使代码在任何标准的PHP安装中重复使用!我们的代码不再负责记录错误(仅确保已引发错误).

PHP has a function called trigger_error which can be used to raise an error just like the standard functions do. The error levels that you use with it are defined in the error level constants. As a user you must use one of the user errors: E_USER_ERROR, E_USER_WARNING or the default value E_USER_NOTICE (other error levels are reserved for the standard functions etc.). Using a standard PHP function to raise the error allows the code to be re-used with any standard PHP installation! Our code is no longer responsible for logging the error (only making sure that it is raised).

使用trigger_error我们仅执行错误记录过程的一半(引发错误),并为错误处理程序节省了对错误做出响应的责任.

Using trigger_error we only perform half of the error logging process (raising the error) and save the responsibility of responding to the error for the error handler which will be covered next.

错误处理程序

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

We set a custom error handler with the set_error_handler function (see the code setup). This custom error handler replaces the standard PHP error handler that normally logs messages in the web server error log depending on the PHP configuration settings. We can still use this standard error handler by returning false within our custom error handler.

自定义错误处理程序有一个职责:响应错误(包括您要执行的任何日志记录).在自定义错误处理程序中,您具有对系统的完全访问权限,并且可以运行所需的任何类型的日志记录.几乎任何使用Observer设计模式的记录器都是可以的(我不打算讨论它,因为我认为它是次要的).这应该允许您挂接新的日志观察器,以将输出发送到所需的地方.

The custom error handler has a single responsibility: to respond to the error (including any logging that you want to do). Within the custom error handler you have full access to the system and can run any sort of logging that you want. Virtually any logger that uses the Observer design pattern will be ok (I'm not going to go into that as I believe it is of secondary importance). This should allow you to hook in new log observers to send the output to where you need it.

您具有完全的控制权,可以在代码的单个可维护部分中对错误进行处理.现在,可以在项目之间快速或轻松地更改错误日志记录,也可以在页面之间的单个项目中快速轻松地更改错误日志记录.有趣的是,甚至@抑制的错误也使它成为errno为0的自定义错误处理程序,如果 error_reporting 掩码未得到遵守.

You have complete control to do what you like with the errors in a single maintainable part of your code. The error logging can now be changed quickly and easily from project to project or within a single project from page to page. Interestingly even @ suppressed errors make it to the custom error handler with an errno of 0 which if the error_reporting mask is respected should not be reported.

好的错误变为严重的错误-致命错误

无法从某些错误继续.自定义错误处理程序无法处理以下错误级别:E_ERRORE_PARSEE_CORE_ERRORE_CORE_WARNINGE_COMPILE_ERRORE_COMPILE_WARNING.当标准函数调用触发了这类错误时,自定义错误处理程序将被跳过,系统将关闭.可以通过以下方式生成:

It is not possible to continue from certain errors. The following error levels can not be handled from a custom error handler: E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING. When these sorts of errors are triggered by a standard function call the custom error handler is skipped and the system shuts down. This can be generated by:

call_this_function_that_obviously_does_not_exist_or_was_misspelt();

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

This is a serious mistake! It is impossible to recover from, and the system is about to shut down. Our only choice is to have a register_shutdown_function deal with the shutdown. However this function is executed whenever a script completes (successful, as well as unsuccessful). Using this and error_get_last some basic information can be logged (the system is almost shutdown at this point) when the last error was a fatal error. It can also be useful to send the correct status code and show an Internal Server Error type page of your choosing.

可以用与基本错误非常相似的方式处理异常.代替trigger_error的是,您的代码将引发异常(通常使用throw new Exception或通过标准函数调用).使用 set_exception_handler 定义要用于的回调处理异常.

Exceptions can be dealt with in a very similar way to basic errors. Instead of trigger_error an exception will be thrown by your code (manually with throw new Exception or from a standard function call). Use set_exception_handler to define the callback you want to use to handle the exception with.

SPL

标准PHP库(SPL)提供了例外.它们是引发异常的首选方式,因为像trigger_error一样,它们是PHP的标准部分,不会为您的代码引入额外的依赖关系.

The Standard PHP Library (SPL) provides exceptions. They are my preferred way of raising exceptions because like trigger_error they are a standard part of PHP which does not introduce extra dependencies to your code.

该如何处理?

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

When an exception is thrown there are three choices that can be made:

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

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

At each level of the stack these choices are made. Eventually once it bubbles up to the highest level the callback you set with set_exception_handler will be executed. This is where your logging code belongs (for the same reasons as the error handling) rather than spread throughout catch statements in your code.

错误处理程序

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');

异常处理程序

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

$previousExceptionHandler = set_exception_handler('exceptionHandler');

关闭功能

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');

用法

错误

// 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.

例外

以前列出的三个选择中的每一个都以通用的方式列出,对其进行修复,追加,使其冒泡.

Each of the three choices from before are listed here in a generic way, fix it, append to it and let it bubble up.

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.

2追加:

在下面观察code_that_can_generate_exception()如何不了解$context.此级别的catch块具有更多信息,如果可以通过将其重新抛出有用,则可以将其附加到异常之外.

Observe below how the code_that_can_generate_exception() does not know about $context. The catch block at this level has more information which it can append to the exception if it is useful by rethrowing it.

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);
}

3让它冒出来:

// Don't catch it.

这篇关于顺利记录错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆