PHP错误日志需精准过滤、权限正确、兜底捕获及格式统一:设error_reporting=E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT,确保error_log路径存在且PHP进程有写权,用register_shutdown_function捕获Fatal Error,手动格式化日志避免grep失效,区分CLI/Web配置路径。

PHP错误日志不是“有就行”,关键在于能否快速定位真实问题。默认配置下,error_log 文件里混着 E_NOTICE、E_WARNING、甚至被 @ 抑制后漏掉的致命错误,不加筛选地扫日志等于盲查。
怎么让 error_log 只记录真正要关注的错误
默认 error_reporting = E_ALL 会把变量未定义、数组键不存在这类提示全记下来,干扰严重。生产环境必须过滤掉低风险级别:
-
error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT是实际可用的底线配置;E_DEPRECATED要留着——它预示 PHP 版本升级后功能将失效 - 别只改脚本里的
ini_set('error_reporting', ...),得确认它没被后续代码覆盖(比如框架启动时又重设了一次) - 如果用的是 PHP-FPM,注意
php-fpm.conf或 pool 配置里可能有php_admin_value[error_reporting],它会强制覆盖脚本内设置
为什么 error_log 文件一直为空或权限拒绝
常见现象是配置写了 error_log = /var/log/php/error.log,但文件始终没内容,或者 PHP 报错 “failed to open stream: Permission denied”。
- 路径必须存在且 PHP 进程用户(如
www-data或nginx)有写权限:mkdir -p /var/log/php && touch /var/log/php/error.log && chown www-data:www-data /var/log/php/error.log - 不要用
syslog模式除非你真在用 rsyslog/rsyslogd;否则error_log = syslog看似生效,实际日志进了系统日志,和预期不符 - 检查 SELinux(CentOS/RHEL)是否拦截:临时关闭测试
setenforce 0,若日志立刻出现,说明需加策略setsebool -P httpd_can_network_connect 1
set_error_handler 捕不到 Fatal Error?那就补 register_shutdown_function
set_error_handler 对 E_ERROR、E_PARSE、E_CORE_ERROR 完全无效——它们发生时 PHP 已放弃执行上下文。必须靠关机函数兜底:
立即学习“PHP免费学习笔记(深入)”;
- 在入口文件最开头注册:
register_shutdown_function(function () { $e = error_get_last(); if ($e && in_array($e['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) { error_log(sprintf("[FATAL] %s in %s on line %d", $e['message'], $e['file'], $e['line']), 3, '/var/log/php/fatal.log'); } }); - 注意
error_get_last()返回值可能为null(无错误),必须判空,否则报Notice: Trying to access array offset on value of type null - 不要在 shutdown 函数里调用
trigger_error()或抛异常——此时已无法捕获,只会静默失败
日志格式混乱导致 grep 失效?手动控制输出结构
直接用 error_log("msg") 写入,时间戳、错误级别、行号全靠 PHP 自带格式,但不同 PHP 版本格式不一致(如 PHP 8.2 加了进程 ID,8.3 又改了分隔符),grep 和日志分析工具很难稳定提取。
- 统一用
date('c')+ 显式级别:error_log("[" . date('c') . "] [ERROR] " . $message . " in " . $file . ":" . $line, 3, $path); - 避免在日志里拼接敏感数据(如密码、token),尤其当
error_log被同步到远程日志平台时 - 如果项目用了 Monolog,别再混用原生
error_log()——Monolog 的Logger::error()默认带上下文、支持处理器链,error_log()写进去的日志会脱离结构化管道
最常被忽略的一点:日志路径在 CLI 和 Web SAPI 下可能指向不同文件(php.ini 和 php-fpm.conf 分开配置),跑定时任务出错时去查 Apache 日志目录,根本找不到记录。



















