使用 PHP 进行日志记录的基本指南

介绍

在本教程中,我们将研究 PHP 日志记录。具体来说,我们将学习如何配置日志、如何显示日志以及如何采用在应用程序故障排除期间派上用场的最佳日志记录实践。在本教程之后,您将能够在您的 PHP 应用程序中创建一个强大的错误处理和日志记录系统。

先决条件

  • Ubuntu 20.04 发行版,包括安装了 sudo 访问权限和 PHP 的非 root 用户。

第 1 步 — 配置日志

PHP 可以配置为显示和记录错误输出。建议您查看以下设置,因为您可能想要进行一些更改。默认情况下,PHP 配置文件位于目录 /etc/php/7.4/apache2/php.ini 中。

/7.4/ 目录在您的系统上可能有不同的名称,因为它代表您当前的 PHP 版本。php.ini 配置文件的完整路径也可能因您的操作系统和 Web 服务器而异。在本例中,我们使用的是 Ubuntu 20.04 和 Apache Web 服务器以及 PHP 7.4.3。

查看配置文件

要查看 php.ini 配置文件,请运行以下命令:

sudo nano /etc/php/7.4/apache2/php.ini

这些是最重要的配置指令。我们强烈建议更改其中一些的值,因为它使记录更容易。

display_errors:

定义错误是否将显示在输出中。默认值为 1,但对于面向 Web 的服务器,建议将其更改为 0。

display_startup_errors:

定义是否显示 PHP 启动顺序错误。默认值为 0。

error_log:

定义将输出错误的日志文件的路径(如果 log_errors 为 1)。

error_reporting:

设置最小错误报告级别。默认值为空。我们建议将其更改为 E_ALL。

log_errors:

切换错误记录。默认值为 0。推荐值为 1。

log_errors_max_length:

设置错误日志的最大长度。将 0 设置为无限制或保留默认值。

track_errors:

如果设置为 1,则最后一个错误存储在 $php_errormsg 变量中。

更改配置指令

更改配置指令的第一种方法是在文本编辑器中打开 php.ini 配置文件并手动更改它。

第二种方法是使用 PHP 更改它。以下示例显示了如何查看和更改特定指令的值。

/*
Viewing value of the configuration directive 
'log_errors' using ini_get()
*/
$log_errors = ini_get("log_errors");
echo "log_errors = " . $log_errors;

/*
Changing value of the configuration directive
'log_errors' using ini_set()
*/
ini_set("log_errors",true);

第 2 步 — 查找日志

只有将 log_errors 指令的值设置为 1 时,错误日志才会输出到日志文件。如果保留 error_log 的默认值,错误将输出到以下位置:

/var/log/apache2/error.log

适用于 Apache Web 服务器和 Ubuntu 操作系统。

/var/log/nginx/error.log

用于 Nginx Web 服务器和 Ubuntu 操作系统。

XAMPP_installation_directory/apache/logs/error.log

 适用于 XAMPP 开发者环境。

否则,错误将输出到自定义日志文件。

第 3 步 — 登录 PHP

现在,当我们掌握了必要的理论后,让我们看看一些使用 PHP 登录的示例和最佳实践。

PHP 附带了多种可用于日志记录的函数。以下示例显示了原生 PHP 附带的函数,但如果您正在寻找更高效的工具,您还可以查看一些 PHP 日志库,因为它们提供了更多功能。

记录到文件

error_log() 函数将消息发送到当前日志文件。您可以仅将其与一个参数一起使用,即您要发送的自定义消息,如下例所示:

这将在日志文件中创建一个新条目,其中包含您输入的文本消息作为函数的参数。条目将如下所示:

error_log("I've just added an entry to the log file!");

手动触发错误

在 PHP 中,您还可以使用 trigger_error() 函数手动触发自定义错误。这个函数有两个参数。

第一个参数是您要发送的实际错误消息,第二个参数是错误的类型(E_USER_ERROR、E_USER_WARNING、E_USER_NOTICE),其中 E_USER_NOTICE 是默认值。

trigger_error('My custom error', E_USER_WARNING);

上面的代码会将以下条目输出到当前配置的日志文件中。

Output:
[15-Apr-2021 10:33:29 Europe/Berlin] PHP Warning:  My custom error in /html/index.php on line 2

例如,您可以使用此功能来跟踪用户何时输入了错误的密码。

if($enteredPassword != $userPassword){
		/* Custom error */
    trigger_error("User $user entered wrong password",E_USER_ERROR);
    /* Code that handled wrong password ... */
}

使用 Syslog 记录

您还可以通过将错误直接发送到默认系统日志来记录错误。为此,您需要使用指定错误前缀、选项和工具的 openlog() 函数打开日志。然后使用 syslog() 函数发送消息,在该函数中指定日志和实际消息的优先级。然后使用 closelog() 函数关闭日志。

openlog("MyAppPrefix", LOG_PID | LOG_PERROR,LOG_USER);
syslog(LOG_ERR, "Custom error message");
closelog();

第 4 步 — 格式化日志

向日志发送消息时,强烈建议包含有助于后续调试的重要信息,例如发生错误的时间和时间、发生错误的 URL 以及任何其他可以提供帮助的附加数据确定问题。

错误类型

PHP 带有多种错误类型,用于标识错误的严重性。这些错误类型是整数值,但它们有预定义的常量。以下是其中的一些,完整列表请访问 官方 PHP 文档

E_ERROR:

导致脚本终止的致命错误(例如调用不存在的函数、内存不足)

E_WARNING:

不会终止脚本执行的运行时警告(例如,在表达式中使用未定义的变量)

E_PARSE:

编译时解析错误

E_NOTICE:

由于代码错误导致的运行时通知

E_USER_ERROR:

用户产生的错误

E_USER_WARNING:

用户生成的警告

E_USER_NOTICE:

用户生成的通知

E_STRICT:

运行时通知

第 5 步 — 使用 JSON 进行日志记录

记录用户操作对于应用程序开发非常有用且很有帮助,因为您可以跟踪用户最常使用的功能或遇到错误时采取的步骤。这些信息可用于改善用户体验并帮助追踪代码中的错误。

这需要收集和记录许多信息和值。这可以通过使用 JSON 来简化。它是 Java-script Object Notation 的首字母缩写词。使用 JSON,您可以将字符串(JSON 格式)转换为 stdClass 对象或数组,然后从对象或数组转换回字符串。这允许您将日志存储在 JSON 格式的字符串中,然后在需要时您可以将所有记录的值和信息转换为单个对象或数组。

要转换为 JSON 格式的字符串,请使用 json_encode()。要从 JSON 格式的字符串转换,请使用 json_decode()。

使用 JSON 进行日志记录的示例

//Data that will be logged
$dataToLog = array(
    "errMsg" => "Requested page does not exist",
    "errCode" => 400,
    "url" => "my-application.com/non-existent-page",
    "userLoggedIn" => true,
    "userName" => "alice",
    "time" => date("Y-m-d H:i:s")
);
//Conversion
$stringToLog = json_encode($dataToLog);
//Logging
error_log($stringToLog);

//Print the result
echo $stringToLog;

这将打印此字符串并将其存储在日志中:

Output:
{"errMsg":"Requested page does not exist","errCode":400,"url":"my-application.com\\/non-existent-page","userLoggedIn":true,"userName":"alice","time":"2021-04-15 12:30:00"}

读取 JSON 日志

//Logged data
$jsonLog = '{
    "errMsg":"Requested page does not exist",
    "errCode":400,
    "url":"my-application.com\\/non-existent-page",
    "userLoggedIn":true,
    "userName":"alice",
    "time":"2021-04-15 12:30:00"
}';

//conversion
$result = json_decode($jsonLog);

//Print result
echo $result->errMsg . "<br>" . $result->errCode . "<br>" .
     $result->url . "<br>" . $result->userLoggedIn . "<br>" .
     $result->userName . "<br>" . $result->time . "<br>";

这会将结果打印到屏幕上

Output:
Requested page does not exist
400
my-application.com/non-existent-page
1
alice
2021-04-15 12:30:00

第 6 步 — 创建例外

当 PHP 开始支持 OOP(面向对象编程)时,它引入了 Exception。它是处理代码层错误和后续日志记录的好工具。异常由代码中的 throw / catch / finally 块处理。

抛出异常

在 try 块中是可以在发生错误时抛出异常的代码。这个异常将在 catch 块中被捕获和处理。即使没有抛出异常,您也可以使用 finally 块。

try{
		/* Some code above */

    if(true) //Error has occurred here
        throw new Exception("Error has occurred");

		/* Some code below */
}
catch(Exception $e){
    //Catching and handling exception
    echo $e->getMessage();
    //Error logging might follow
}

这将在屏幕上打印一条错误消息:

Output:
Error has occurred

自定义异常

您还可以为应用程序中可能遇到的特定错误和问题创建自定义异常。在这个例子中,我们将创建一个处理除以零的异常。

带有构造函数的自定义异常类:

class DivisionByZeroException extends Exception{

    public function __construct($msg){
        parent::__construct($msg);
    }

}

引发此异常的代码:

try{
    $a = 4;
    $b = 0;

    if($b != 0){
        $c = $a / $b;
    }
    else{
        throw new DivisionByZeroException("You can't divide by zero!");
    }
}
catch(Exception $e){
    //Catching and handling exception
    echo $e->getMessage();
    //Error logging might follow
}

这将打印自定义错误消息:

Output:
You can't divide by zero!

说到自定义异常,PHP 有一个名为 SPL(标准 PHP 库)的内置库,它为我们可以在代码中使用的常见错误提供了许多预定义的异常类。这是 SPL 中可用的异常类的完整列表。

BadFunctionCallException:

如果回调引用未定义的函数或缺少某些参数,则抛出。

BadMethodCallException:

如果回调引用未定义的方法或缺少某些参数,则抛出。

DomainException:

如果值不符合定义的有效数据域,则抛出

InvalidArgumentException:

如果参数不是预期的类型,则抛出。

LengthException:

如果长度无效则抛出。

LogicException: 

表示程序逻辑中的错误。

OutOfBoundsException:

如果值不是有效的键,则抛出。

OutOfRangeException:

请求非法索引时抛出。

OverflowException

: 将元素添加到完整容器时抛出。

RangeException:

抛出以指示程序执行期间的范围错误。

RuntimeException:

如果发生只能在运行时发现的错误,则抛出该错误。

UnderflowException:

在空容器上执行无效操作时抛出,例如删除元素。

结论

您了解了日志的存储位置、如何更改配置指令以及如何使用 PHP 存储日志。您还学习了如何使用 JSON 记录用户活动并使用 Exception 处理错误。

结合以上所有知识,您可以在 PHP 应用程序中创建强大的错误处理和日志系统,这将帮助您跟踪代码中的错误并开发更好的应用程序。

想了解更多关于 PHP 日志的信息?阅读特定的日志库,如Monologlog4php

the-essential-guide-to-logging-with-php-u02x34f5