Yii2 CLI 模式日志最佳实践指南

yii2 Yii2 · best · 于 1天前 发布 · 21 次阅读

Yii2 CLI 模式日志最佳实践指南

CLI (命令行) 模式通常用于运行定时任务 (Cron)、处理队列 (Queue Workers) 或执行守护进程。本指南涵盖了从基础的实时写入配置到高级的 Supervisor 托管架构的完整日志策略。

1. 基础原则 (通用)

无论是否使用进程管理工具,CLI 环境下有两个核心参数必须调整,以防止进程异常退出导致内存中的日志丢失。

1.1 强制实时写入 (Flush Interval)

现象:脚本正在跑,tail -f 看不到日志。脚本报错崩溃后,最后几十条日志“失踪了”。

原因:Yii2 默认 flushIntervalexportInterval 均为 1000。这意味着积攒 1000 条日志才写盘。在 CLI 中,只要脚本崩溃或被 Kill,这 1000 条日志就会凭空消失。

最佳实践: 在 config/console.php 中,将这两个值设为 1(实时写入)。

修改 config/console.php:

'components' => [
    'log' => [
        'flushInterval' => 1, // Logger -> Target: 立即
        'targets' => [
            [
                'class' => 'yii\log\FileTarget',
                'exportInterval' => 1, // Target -> Medium: 立即
                'logVars' => [], // CLI 没必要记录 $_SERVER 等
                // ... 其他配置
            ],
        ],
    ],
],

2. 方案 A:独立脚本 / Cron 模式

适用于简单的定时任务 (yii hello/index) 或临时手动执行的脚本。

2.1 写文件

使用 FileTarget 记录到 @runtime/logs/console.log

关键:记录 PID

多进程并发时(如每分钟启动一个同步脚本),没有 PID 很难区分日志来源。

'prefix' => function ($message) {
    return "[" . getmypid() . "]"; 
},

2.2 看屏幕(可选)

现象:Yii::info() 只写文件,控制台一片黑,调试困难;echo 只上屏,不留档。 最佳实践:

最佳实践:

  • 生产环境:只写文件(FileTarget)。
  • 开发/调试:在开发环境额外增加一个指向 php://stdout 的 Target,方便调试。
// config/console.php (可以仅在 dev 环境开启)
'targets' => [
    // ... FileTarget ...
    [
        'class' => 'yii\log\FileTarget',
        'logFile' => 'php://stdout', // 直接写到标准输出
        //'categories' => ['application'], // 过滤杂音
        'logVars' => [],
        'exportInterval' => 1,
        // 自定义格式,去掉时间戳(因为屏幕左边通常不需要太长的前缀)
        'prefix' => function ($message) {
            return ""; 
        },
    ],
],

3. 方案 B:Supervisor 托管 / 守护进程 (推荐)

这是现代微服务和 12-Factor App 推荐的架构。适用于 yii queue/listen 等常驻进程。

3.1 核心思想

PHP 只管“说” (Stdout),Supervisor 负责“记” (File)

彻底解决 PHP 多进程写文件的锁争用、权限问题以及日志轮转问题。

3.2 PHP 配置 (config/console.php)

放弃写 @runtime 目录,全部吐给标准输出。

'targets' => [
    // 将所有 INFO 级别以上的日志输出标准输出
    [
        'class' => 'yii\log\FileTarget',
        'logFile' => 'php://stdout', 
        'exportInterval' => 1,
        'levels' => ['info', 'warning', 'error'],
        'logVars' => [],
        // 建议:输出 JSON 格式,方便 Supervisor 收集后被 ELK 解析
        'formatMessage' => function ($message) {
            return json_encode([
                'time' => date('Y-m-d H:i:s'),
                'level' => yii\log\Logger::getLevelName($message[1]),
                'category' => $message[2],
                'pid' => getmypid(),
                'msg' => $message[0],
                // 自动携带 Request ID (如果实现了该组件)
                'trace_id' => Yii::$app->params['request_id'] ?? null,
            ], JSON_UNESCAPED_UNICODE);
        }
    ],
    // (可选) 错误单独走 stderr,但这在 Supervisor 中通常会被 redirect_stderr=true 合并
],

3.3 Supervisor 配置 (yii-worker.conf)

让 Supervisor 接管日志的写入、轮转和生命周期。

[program:yii-worker]
command=php /path/to/yii queue/listen
process_name=%(program_name)s_%(process_num)02d
numprocs=4
autostart=true
autorestart=true
user=www-data

; --- 日志托管 ---
redirect_stderr=true  ; 合并 stderr 到 stdout
stdout_logfile=/var/log/supervisor/yii-worker.log ; 日志落盘位置
stdout_logfile_maxbytes=50MB ; 自动轮转:50MB 切分
stdout_logfile_backups=10    ; 保留最近 10 个文件

4. 全链路追踪 (Traceability)

CLI 任务通常由 Web 请求触发(如异步发邮件)。为了串联日志:

  1. Web 端 (Producer): 生成 Request ID 并放入 Job 载荷。

    Yii::$app->queue->push(new Job([
        'trace_id' => Yii::$app->params['request_id'], 
        'data' => $data
    ]));
    
  2. CLI 端 (Consumer): 取出 trace_id 并设为当前上下文。

    public function execute($queue) {
        Yii::$app->params['request_id'] = $this->trace_id; // 注入上下文
        Yii::info("Job started"); // 日志组件会自动读取 params['request_id']
    }
    

5. 异常捕获

Web 模式有 ErrorHandler 显示错误页,CLI 模式异常直接导致进程退出。 务必确保 console/config/main.php 正确配置了 errorHandler,配合 flushInterval=1,确保死前最后一声惨叫能被记录。

'components' => [
    'errorHandler' => [
        'class' => 'yii\console\ErrorHandler',
    ],
],

6. 总结清单

场景写入目标轮转管理权限管理
独立脚本FileTarget -> .log 文件Yii 自带 (maxLogFiles)需注意 chmod,避免 root 执行后 www-data 无法写
SupervisorFileTarget -> stdoutSupervisor 自带完美 (由 Supervisor 父进程管理)

黄金法则:CLI 模式下,永远设置 flushIntervalexportInterval1

本文由 best 创作,采用 知识共享署名 3.0 中国大陆许可协议 进行许可。 可自由转载、引用,但需署名作者且注明文章出处。

共收到 0 条回复
没有找到数据。
添加回复 (需要登录)
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册