PHP异常未写入日志需检查:log_errors必须为On,error_log须用绝对路径;CLI与FPM加载不同php.ini;catch中须手动调用error_log等记录,不可静默吞掉;日志需含上下文、脱敏、时间戳;并确认文件权限、磁盘空间及轮转配置。

PHP 异常没写进日志?先检查 error_log 配置是否生效
很多 PHP 脚本抛出异常后,error_log 文件里空空如也,不是代码没写对,而是 PHP 根本没把错误路由到文件。关键看两个地方:log_errors 必须为 On,且 error_log 的值得是绝对路径(相对路径在 CLI 和 Web 环境下行为不一致,极易失效)。
-
log_errors = On—— 不开这个,所有错误(包括throw new Exception()未捕获时触发的Fatal error)都不会落盘 -
error_log = /var/log/php_errors.log—— 推荐用绝对路径;如果写成./php_errors.log,CLI 下可能写到当前执行目录,Web 下往往写到 webserver 用户主目录或直接失败 - CLI 模式和 FPM 模式读的是不同 php.ini,
php -i | grep "Loaded Configuration File"确认你改的是正在用的那个
try-catch 里手动记录异常,别只靠 set_exception_handler
set_exception_handler 只能兜底未被捕获的异常,而业务中大量异常是主动 throw 后被 catch 处理的——这时候如果不显式调用日志函数,异常就彻底消失了。
- 捕获后必须自己写日志,比如:
error_log("API failed: " . $e->getMessage(), 3, "/var/log/myapp.log"); -
error_log第二个参数用3表示写入文件,用4是 syslog(需系统支持),别用0(发送到 SAPI 错误日志,不可控) - 别在
catch里只写echo $e->getMessage()或静默吞掉,这是日志丢失最常见原因 - 如果用了 Monolog 等库,确保 handler 已 attach,且 level 设置合理(
Logger::ERROR不会记录Warning级别)
日志内容要带上下文,光记 $e->getMessage() 基本没用
线上出问题时,单看“SQLSTATE[HY000]: General error”这种信息,连哪条 SQL、哪个用户、什么时间触发的都不知道,等于没记。
- 至少拼上:
$e->getMessage()+$e->getTraceAsString()(或精简后的前几帧) - 加上请求标识:如果是 Web 请求,记录
$_SERVER['REQUEST_URI']和$_SERVER['REQUEST_METHOD'];CLI 脚本记录$argv - 敏感信息过滤:别直接打
$_POST全量,用array_diff_key($_POST, array_flip(['password', 'token']))这类方式脱敏 - 时间戳用
date('c')而非microtime(true),避免解析困难
文件权限和磁盘空间是日志写不进去的隐形杀手
PHP 进程以什么用户身份运行,就决定了它能不能往目标路径写文件。FPM 下通常是 www-data 或 nginx,CLI 下是你当前登录用户——这两者对同一路径的权限可能完全不同。
立即学习“PHP免费学习笔记(深入)”;
- 检查目标日志目录归属:
ls -ld /var/log/myapp/,确保运行 PHP 的用户有w权限 - 用
touch /var/log/myapp/test.log && rm /var/log/myapp/test.log手动验证写入能力 - 磁盘满(
df -h)、inode 耗尽(df -i)会导致error_log静默失败,不会报错,但日志停更 - 日志轮转没配的话,单个文件可能涨到几个 GB,既难查又拖慢写入,建议用
rotatelogs或 logrotate
真正卡住排查的,往往是配置看似对了,但权限或磁盘状态不对;或者异常被 catch 住了却忘了记日志——这两处一漏,等于没做异常记录。











