workerman 4 默认不自动写文件日志,所有 echo、var_dump 和 worker::log() 输出到终端;业务日志需通过设置 worker::$logfile(绝对路径)、monolog 分级写入或重定向 stdout/stderr 实现,须注意多进程冲突、权限、路径有效性及锁机制。

Workerman 4 默认不自动写文件日志,所有 echo、var_dump 和 Worker::log() 都输出到终端(stdout/stderr)。业务日志想独立写入指定文件,关键不是“加个配置”,而是明确区分日志来源、控制输出通道、避免多进程冲突。
设置 Worker::$logFile 指向业务日志绝对路径
这是最直接的方式,适用于框架级运行信息和简单业务日志合并记录的场景:
-
必须用绝对路径,例如
/data/www/myapp/runtime/logs/workerman.log;相对路径在多进程下易因工作目录不同而写错位置或失败 - 该设置影响
Worker::log()和框架内部日志(如启动/重启/异常退出),但不影响echo或var_dump() - 确保运行用户(如 www-data)对该路径有写权限,且父目录存在 —— 否则日志静默丢失,无报错提示
用 Monolog 实现业务日志分级独立写入
当需要把支付、订单、API 等不同模块日志分开保存(如 pay.log、order_error.log),Monolog 是标准解法,但需避开常见陷阱:
- 每个 channel 对应一个 FilterHandler + RotatingFileHandler 组合,而不是直接 new 多个 RotatingFileHandler。否则 ERROR 日志会同时出现在 info.log 和 error.log 中
- RotatingFileHandler 构造时第三个参数指定最低级别,例如
Logger::ERROR,这样它只接收 ERROR 及以上日志 - 务必启用
IntrospectionProcessor并排除 vendor 路径,否则行号%L%显示的是 Workerman 底层文件,而非你的控制器 - 格式字符串必须显式定义,例如
"%datetime% [%P%] %level_name% %logger%:%L% - %message%\n",空字符串会导致定位信息失效
重定向 stdout/stderr 到业务日志文件(适合调试期)
如果你只是想把 echo、var_dump() 这类原始输出也落到文件里,shell 层重定向最简单可靠:
- 启动命令加上:
php start.php start -d >> /data/logs/stdout.log 2>&1 - 注意:
>>是追加,>是覆盖;2>&1表示 stderr 合并到 stdout - 配合 logrotate 使用时,必须启用
copytruncate,否则 Workerman 持有旧 inode,日志会被写丢 - 不要依赖
create指令,Workerman 不重开 fd,logrotate 创建的新文件不会被自动接管
避免日志混写与覆盖的关键细节
多进程模型下,日志安全落地比单进程复杂得多:
- 不用
file_put_contents($file, $msg, FILE_APPEND)直接写业务日志 —— 它不加锁,高并发时内容会错位、截断 - Monolog 的
StreamHandler默认不加锁,生产环境必须换用RotatingFileHandler或自行封装 flock - 所有日志路径建议统一通过
runtime_path().'/logs/'动态生成,避免硬编码路径在不同环境出错 - PHP 错误日志(
Notice/Warning)走的是 php.ini 的error_log配置,和 Workerman 日志无关,需单独检查











