现代的php开发,通常的习惯是将错误信息对用户隐藏,一来提高体验,二来隐藏系统内部细节。
但是错误日志需要保留,便于开发人员解决BUG。
现在web端最常用的组合是nginx+php-fpm,php以fastcgi方式运行,此时php会有两个配置文件:php.ini 和 php-fpm.conf,两个配置文件各自负责不同的设置,这里只重点说一下日志相关的配置,以下内容基于 php7.3.6。
错误日志
php.ini 中与日志有关的配置项有:
# 是否将错误信息作为输出的一部分显示到屏幕,出于前面提到的习惯,这个配置项应该设置为 Off
display_errors = Off
# 是否将脚本运行的错误信息记录到服务器错误日志或者error_log之中,出于前面提到的习惯,这个配置项应该设置为 On
log_errors = On
# 设置 log_errors 的最大字节数
log_errors_max_len = 1024
# 不记录重复的信息,设置为 Off,即重复的信息也要记录下来
ignore_repeated_errors = Off
# 忽略重复消息时,也忽略消息的来源,同上,这里也设置为 Off,这样,所有的错误消息都会被记录
ignore_repeated_source = Off
# 如果开启,最后的一个错误将永远存在于变量 $php_errormsg 中。从 PHP 7.2.0 起 $php_errormsg 已被废弃,track_errors 设置为 Off 即可
track_errors = Off
# 设置脚本错误将被记录到的文件
error_log = /var/log/php/error_log
# 设置错误报告的级别,E_ALL&~E_NOTICE 的意思是记录除 E_NOTICE 之外的所有错误信息
error_reporting = E_ALL&~E_NOTICE
如果按照上述配置,达到的效果是,页面上不会显示错误信息,除 E_NOTICE 之外的所有错误信息都会被记录到文件 /var/log/php/error_log 中,这是一个可以用在生产环境上的配置,也比较严格,由于php可以通过 ini_set() 函数在运行时动态调整配置,因此php.ini中最好直接做成生产可用的,其它环境下的个别需求可以通过ini_set来调整,比如开发环境下,error_reporting最好是设置为 E_ALL,这样便于在开发阶段找出程序中的隐患并解决,而在生产环境关闭 E_NOTICE 的用意是为了更好的突出比较严重的错误,使之不会淹没在大量notice中。
php-fpm.conf 中于日志有关的配置项有:
[global]
; 设置脚本错误将被记录到的文件
error_log = /var/log/php/php-fpm.log
; 错误级别。可用级别为:alert(必须立即处理),error(错误情况),warning(警告情况),notice(一般重要信息),debug(调试信息)。默认:notice
log_level = notice
; 错误长度
log_limit = 8192
[www]
; 设置access log的位置,通常不用设置,/proc/self/fd/2 这个值是php的官方docker容器中的配置,目的是为了通过 docker log 命令可以看到容器的访问记录
access.log = /proc/self/fd/2
; 设置 是否将 stdout 和 stderr 输出到 error log,如果不设置,信息会被输出到 /dev/null ,因此这里应设置为 yes
catch_workers_output = yes
; 是否修饰输出,默认为 yes
decorate_workers_output = yes
需要注意的是,php.ini 的优先级高于 php-fpm.conf,如果 php.ini 中设置了 error_log ,那么 php-fpm.conf 的设置将会失效,通常来说,对错误日志的配置比较适合放在 php.ini 中
慢日志
php-fpm.conf 中可以设置是否开启慢日志,这个非常有用,原则上,来自web的访问都应该很快结束,哪怕是出错了,如果有进程被长时间占用,那么很有可能会导致php-fpm子进程池被消耗光,从而导致新的访问无法被执行,nginx就会报504错误,所以记录下慢日志,可以方便查找错误,开启方法很简单,增加两行配置:
[www]
; 设置记录慢日志的超时时间
request_slowlog_timeout = 5s
; 设置日志文件位置
slowlog = /var/log/php/slow_log
需要注意的是,在Linux系统中,php-fpm使用 SYS_PTRACE 跟踪 worker 进程,但是docker容器默认是不启用这个功能的,因此,当php运行在docker容器中时,慢日志无法被记录到文件中,可以这样解决,如果是用命令行启动的容器,在命令上加上:
--cap-add=SYS_PTRACE
如果用了compose,在docker-compose.yaml文件中加上:
php:
#...
cap_add:
- SYS_PTRACE
#...
重启容器即可。
错误/异常捕获
摘录php官方文档中的一段话:
PHP 7 改变了大多数错误的报告方式。不同于传统(PHP 5)的错误报告机制,现在大多数错误被作为 Error 异常抛出。
这种 Error 异常可以像 Exception 异常一样被第一个匹配的 try / catch 块所捕获。如果没有匹配的 catch 块,则调用异常处理函数(事先通过 set_exception_handler() 注册)进行处理。 如果尚未注册异常处理函数,则按照传统方式处理:被报告为一个致命错误(Fatal Error)。
当选择不在页面上显示错误,并将错误全部写入日志文件时,想要跟踪或者发现系统运行中的问题,都要依赖日志文件,这种方式并不方便。
现代的web框架基本都提供捕获错误/异常的功能,这样一来,开发人员就可以比较自由的来选择如何处理这些错误,比如是否显示在页面上,是否写入日志文件,也便于对代码做更严格的校验,比如遇到任何级别的错误都让服务器报500,降低代码隐患。
php7里面有三个函数可以用来辅助这件事:
set_exception_handler
设置默认的异常处理程序,用于没有用 try/catch 块来捕获的异常。
set_error_handler
设置用户的函数 (error_handler) 来处理脚本中出现的错误。
register_shutdown_function
注册一个 callback ,它会在脚本执行完成或者 exit() 后被调用。
通过这三个函数的组合运用,基本可以把程序运行时所产生的大部分错误捕获到。
每一个web框架,对于捕获到的错误都有其默认的处理规则,具体的可以去各家源代码中一探究竟。