Yii日志在Swoole中不实时写入,需手动调用flush()或降低flushInterval;FileTarget须配置绝对logPath并确保Swoole进程用户有写权限;多目标日志应通过categories精确路由,避免漏配导致日志丢失。

Yii日志在 Swoole 异步服务中不实时写入?先调 flush()
Yii 默认不会立即把日志刷到文件,而是攒够 $flushInterval 条(默认 1000)或等到应用结束才调用 flush()。Swoole 的 Worker/Task 进程长驻内存,没有“应用结束”这个时机,所以日志一直卡在内存里,kill 进程才看到日志落盘是典型症状。
解决方法不是改日志目标,而是主动触发刷新:
- 在关键逻辑后手动调
Yii::getLogger()->flush(),比如 Task 执行完、异步回调返回前 - 降低
flushInterval(如设为 10 或 1),代价是轻微性能开销,但对调试和可观测性更友好 - 避免在循环内高频调
flush()—— 每次 flush 都涉及磁盘 I/O,可能拖慢吞吐
FileTarget 在 Swoole 下必须显式配置 logPath 和权限
Swoole 进程通常以非 Web 用户(如 www-data 或自定义用户)运行,而 Yii 默认的 @runtime/logs/ 路径可能被 Web 服务器用户独占写入权限,导致 FileTarget 静默失败 —— 没报错,也没日志,就是查不到文件。
务必确认两点:
-
logPath必须设为绝对路径,且该路径对 Swoole 进程用户可写:'logPath' => '/var/www/myapp/runtime/logs',别依赖别名解析(Swoole 环境下别名有时不可靠) - 执行
ls -ld /var/www/myapp/runtime/logs,确保属主/组匹配 Swoole 进程用户,必要时chown -R www-data:www-data /var/www/myapp/runtime - 若仍不写入,临时加一行
file_put_contents('/tmp/debug.log', "test\n", FILE_APPEND);验证进程是否有基础文件写权限
多个日志目标共存时,levels 和 categories 要分清用途
在 Swoole 场景下常需分离日志:比如把 Task 日志单独打到 task.log,错误仍发邮件,DB 慢查询另存一份。这时不能只靠 levels 过滤,得靠 categories 做路由控制。
示例配置片段:
'targets' => [
[
'class' => 'yii\log\FileTarget',
'levels' => ['error', 'warning'],
'logFile' => '@runtime/logs/error.log',
'categories' => ['application', 'yii\base\ErrorCatcher'],
],
[
'class' => 'yii\log\FileTarget',
'levels' => ['info'],
'logFile' => '@runtime/logs/task.log',
'categories' => ['swoole.task'], // 手动指定 category
],
]
对应代码中写日志:
Yii::info('task started', 'swoole.task'); → 只进 task.logYii::error('db timeout', 'yii\db.Command'); → 不进 task.log,除非你显式放开 categories
- category 是字符串匹配,支持通配符:
'yii\db.*'匹配所有数据库相关日志 - 漏配
categories会导致日志“消失”——它不是黑名单,而是白名单 - 别在
levels里混写大小写,如['Error']无效,必须小写:['error']
生产环境禁用 ChromePhpTarget,但开发调试别全关 trace
ChromePhpTarget 依赖浏览器扩展,在 Swoole CLI 环境下完全无效,还可能因尝试输出 headers 导致 warning;但它在 Web 请求中仍有价值。真正要警惕的是 traceLevel 设置过高(如 YII_DEBUG ? 3 : 0)却没配好 trace 日志目标 —— 大量 trace 消息堆积内存,不 flush 就不释放,最终 OOM。
建议做法:
- 开发时保留
traceLevel => 3,但只为trace单独配一个轻量FileTarget,且maxFileSize设小(如 512KB),防膨胀 - 生产环境设
traceLevel => 0,彻底关闭 trace 收集,避免无意义内存占用 - 永远不要在 Swoole 的 onReceive/onTask 回调里用
var_dump()或echo—— 它们不输出,还可能干扰协程调度
最易被忽略的一点:Swoole 的 Task 进程默认不继承主进程的 Logger 配置上下文,如果用了多 Worker + 多 Task,且每个 Task 启动时动态修改了 Yii::$app->log->targets,这些修改只在当前 Task 生命周期生效,且无法跨进程共享。想统一行为,所有 target 必须在应用启动时(config 层)一次性注册完成。



















