PHP框架日志不能直接用rm -rf logs/*,因当前日志文件被进程占用,删除会导致“Text file busy”、写入失败或权限异常;应使用logrotate等系统级工具安全轮转。

PHP框架日志文件为什么不能直接用 rm -rf logs/*?
多数 PHP 框架(Laravel、ThinkPHP、Symfony)默认使用轮转日志(如 laravel.log + laravel-2024-06-15.log),且当前日志文件正被 file_put_contents 或 Monolog 持有写入句柄。直接 rm 可能触发「Text file busy」错误,或导致新日志写入失败、丢失。
- Linux 下删除正在被进程打开的文件,inode 未立即释放,磁盘空间不回收
- 部分框架(如 Laravel)在日志轮转时依赖文件存在性判断,删空目录可能干扰
RotatingFileHandler行为 - 误删正在写的
laravel.log后,框架可能静默创建新文件但权限异常(尤其配合www-data用户时)
Laravel 日志按天清理:用 logrotate 而非脚本遍历
硬编码循环 find logs/ -name "*.log" -mtime +7 -delete 风险高——它不区分主日志和归档日志,且无法保证轮转原子性。推荐系统级 logrotate,与框架解耦、更可靠。
- 配置示例:
/var/www/myapp/storage/logs/*.log { daily missingok rotate 30 compress delaycompress notifempty create 644 www-data www-data sharedscripts postrotate if [ -f /var/run/php/php8.1-fpm.pid ]; then kill -USR1 `cat /var/run/php/php8.1-fpm.pid` fi endscript } -
sharedscripts+postrotate确保所有日志轮转完成后才通知 FPM 重载句柄 - 避免在 Laravel 的
Kernel.php中写Artisan::call('log:clear')—— 该命令只清空当前laravel.log,不处理历史归档
ThinkPHP 日志路径与清理边界要手动对齐
ThinkPHP 默认日志路径是 runtime/log/,但具体子目录由 app_debug 和 log.level 决定(如 runtime/log/202406/ 或 runtime/log/error/)。盲目清理整个 log/ 可能误删正在使用的会话或缓存临时文件。
- 确认实际日志位置:
config('log.path')或检查think log:list命令输出 - 只清理带日期后缀的子目录(如
202405*),保留最新 3 个:find runtime/log/ -maxdepth 1 -type d -name "202[0-9][0-9]*" | sort -r | tail -n +4 | xargs rm -rf - 禁用
log.single模式前不要删runtime/log/log.php,否则下次请求会因文件不存在报Warning: file_put_contents(): failed to open stream
自定义日志清理命令必须重载 Monolog Handler
如果业务要求「清理 30 天前的 ERROR 级别日志」,不能靠字符串匹配 .log 文件名——因为 Monolog 默认把所有级别写进同一文件。得从 Handler 层面介入。
立即学习“PHP免费学习笔记(深入)”;
- 在 Laravel 中,扩展
RotatingFileHandler,重写getFiles()方法,按内容正则提取时间戳和 level - 或改用
StreamHandler+ 自定义 formatter,让每行日志以[2024-06-15 14:22:01] error开头,再用awk定向清理 - 切记:任何清理逻辑执行前,先调用
$logger->close()(或Log::channel('xxx')->getLogger()->close()),释放文件锁











