直接清理被PHP进程写入的日志文件会导致写入中断、磁盘空间不释放等问题;应使用logrotate配合copytruncate或SIGUSR1信号安全轮转,PHP CLI脚本宜长期持有文件句柄并交由logrotate处理。

直接清理正在被 PHP 进程写入的 logs 文件(比如用 rm 或清空 > app.log)大概率会中断日志写入,甚至导致部分服务卡住或报错——不是因为 PHP 崩了,而是日志文件句柄还被进程持有着。
PHP 进程没释放 log 文件句柄怎么办
Linux 下,即使你删掉了 app.log,只要 PHP 进程(如 php-fpm 子进程或 CLI 脚本)还在往它写,系统实际是往一个“已删除但未关闭”的 inode 写数据。磁盘空间不会释放,新日志也看不到,还可能因满盘触发异常。
- 用
lsof -p查看哪些进程还占着旧日志文件| grep log - 不要直接
rm正在写的日志;改用logrotate配合copytruncate或发送SIGUSR1信号(需程序支持) - 对
php-fpm,可配置access.log和error_log路径,并启用logrotate的create+sharedscripts模式
logrotate 配合 PHP 安全轮转的关键配置
logrotate 是最稳的方案,但默认不通知 PHP 进程重开文件,必须显式处理。PHP 自身不监听日志轮转信号,所以得靠外部机制。
- 在
/etc/logrotate.d/php-app中写:/var/log/myapp/*.log { daily missingok rotate 30 compress delaycompress copytruncate } -
copytruncate是关键:先复制再清空原文件,避免丢失正在写入的最后几行,且无需重启进程 - 若用
create替代copytruncate,必须配合postrotate发送信号(例如kill -USR1 `cat /var/run/php-fpm.pid`),但仅对支持该信号的程序有效(php-fpm支持,普通 CLI 脚本不支持)
PHP CLI 脚本自己管理日志时怎么避免锁死
如果你用 file_put_contents($log, $msg, FILE_APPEND | LOCK_EX) 写日志,每次写都加锁,看似安全,但频繁写+锁会导致性能抖动,且无法解决轮转问题。
立即学习“PHP免费学习笔记(深入)”;
- 改用单次打开、长期持有句柄的方式:
$fp = fopen($log, 'a');,并在脚本生命周期内复用$fp - 轮转时,不要在 PHP 里判断时间/大小并
fclose+fopen——容易竞态;交给logrotate处理,PHP 只需容忍文件被截断(copytruncate后继续写没问题) - 若必须 PHP 自己轮转(比如无 root 权限装不了
logrotate),务必用rename()替代unlink(),再fopen(..., 'a')新文件,并检查flock()返回值是否成功
真正麻烦的不是“怎么删”,而是“删完谁还在写、写到哪去了”。线上稳定运行的核心,是让日志操作和业务逻辑彻底解耦——别让清理动作碰触任何正在活跃的文件描述符。











