thinkphp在workerman模式下日志不按天切分,根本原因是rotatingfilehandler仅按文件大小轮转且tp无日期感知机制;其daily配置仅影响文件名后缀,不触发每日新建,而workerman常驻进程导致日志句柄长期持有,必须依赖logrotate+copytruncate系统级切割。

ThinkPHP 在 Workerman 模式下日志不会自动按天切分,根本原因是:RotatingFileHandler 默认按文件大小轮转,且 ThinkPHP 的日志通道不感知日期变化——它不会主动检测「今天是不是新一天」并重建 handler。
RotatingFileHandler 本身不支持日期驱动轮转
ThinkPHP 6 默认用 RotatingFileHandler(来自 Monolog),它的轮转触发条件只有两个:maxFileSize 和 maxFiles。即使你配置了 'daily' => true,那也只是影响文件名后缀(如 2026-05-25.log),但不会强制每天新建文件——只有当前日志写满 maxFileSize 时才会滚动,否则所有日志仍持续追加到同一个文件里。
- 比如设了
maxFileSize = 10 * 1024 * 1024(10MB),而某天只写了 2MB,那2026-05-25.log就会一直被复用,第二天的新日志也继续往里写 -
maxFiles = 30控制的是最多保留 30 个历史文件,不是「保留最近 30 天」——如果一周只生成 1 个文件,30 个文件能存 30 周 - Webman 那套靠定时器 + 重建 handler 的方案,在 ThinkPHP 里不直接适用,因为 TP 没暴露 logger 实例的动态替换入口
Workerman 常驻进程导致日志句柄长期持有
Workerman 启动后,ThinkPHP 的日志 handler 一旦初始化,就会打开一个文件描述符并长期持有。它不会像 CLI 请求那样每次执行完就释放——所以没有外部干预,文件就不会被重开、也不会因日期变化自动切换路径。
- 即使你在
config/log.php中把日志路径写成runtime_path().'/logs/'.date('Y-m-d').'.log',这个date()只在应用启动时执行一次,后续所有请求都写进那个固定路径的文件 - Workerman 不支持 SIGHUP 信号重载日志配置,也无法通过框架层触发 handler 重建
- 试图在中间件里每次请求都 new 一个
RotatingFileHandler会导致频繁 open/close,fd 耗尽风险高,且违反日志写入原子性
真正可行的解法只有系统级 logrotate + copytruncate
生产环境唯一稳定、低侵入、不改框架源码的方案,是绕过 PHP 层,交给 logrotate 在系统层面切割,并强制用 copytruncate 保活文件描述符。
- 必须配
copytruncate:Workerman 不重开 fd,不用这个选项,logrotate移走文件后,新日志仍写向旧 inode,结果就是日志“消失” - 别配
create:ThinkPHP 进程自己创建的日志文件权限/属主可能被覆盖,导致后续写入失败且无报错 - 推荐配置片段:
/path/to/your/runtime/logs/*.log { daily missingok rotate 30 compress delaycompress copytruncate notifempty } - 测试命令用
sudo logrotate -f /etc/logrotate.d/your-app,不要只等 cron 触发
最易被忽略的一点:logrotate 配置里的路径必须和 ThinkPHP 实际写入的路径完全一致(包括软链接展开后的绝对路径),否则规则不匹配,切割就失效。运行 ls -l /proc/$(pgrep -f 'WorkerServer')/fd/ | grep log 可确认 Workerman 当前持有的日志文件真实路径。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











