
Yii 1.1 中 Yii::log() 调用看似执行成功,但日志文件仅记录首条消息,后续日志丢失——这通常并非日志配置错误,而是因未完成的请求生命周期(如死循环、超时、异常中断)导致日志缓冲未刷新或应用提前终止。
yii 1.1 中 `yii::log()` 调用看似执行成功,但日志文件仅记录首条消息,后续日志丢失——这通常并非日志配置错误,而是因未完成的请求生命周期(如死循环、超时、异常中断)导致日志缓冲未刷新或应用提前终止。
在 Yii 1.1 中,Yii::log() 默认采用异步缓冲写入机制:日志消息先存入内存缓冲区,直到请求结束(即 CApplication::end() 被调用)或显式触发 Yii::app()->log->flush() 时,才批量写入文件。这意味着:若请求中途崩溃、超时、陷入无限循环或被强制终止,缓冲区中的日志将永远丢失。
你观察到的现象——'Entered foo()' 成功写入,但后续 log() 调用无痕迹,而数据库却已更新——正是典型“部分执行 + 非正常终止”的信号。正如你最终定位的:某处存在递归查询 27 万行数据的逻辑,引发长时间阻塞甚至 PHP 超时(如 max_execution_time 触发),导致脚本在 bar() 中途卡死,Yii::log() 虽被调用,但缓冲区从未 flush,进程即被终止。
✅ 验证与诊断建议:
- 检查
protected/config/main.php中日志组件配置,确认CFileLogRoute已启用且levels包含'dev':'log'=>array( 'class'=>'CLogRouter', 'routes'=>array( array( 'class'=>'CFileLogRoute', 'levels'=>'dev, error, warning, info', // 确保包含 'dev' 'categories'=>'My.Code.Example', // 可选:按分类过滤 'logFile'=>'application.log', ), ), ), -
强制立即刷日志(临时调试用):在关键
log()后添加Yii::log('bar(): got past first if', 'dev', 'My.Code.Example'); Yii::app()->log->flush(); // 强制立即写入磁盘 - 启用
CProfileLogRoute或在index.php开头添加ini_set('log_errors', 1); ini_set('error_log', '/tmp/php-errors.log');捕获底层致命错误。
⚠️ 根本规避策略:
-
避免长耗时同步操作:将大数据量处理拆分为分页任务(如
LIMIT 1000 OFFSET ...),或移交至 CLI 脚本/队列; -
设置合理超时:在
php.ini中调高max_execution_time(仅临时),并用set_time_limit(300)在关键方法内动态延长; -
添加防御性日志与监控:在循环体中加入计数器和超时检查,例如:
$startTime = microtime(true); for ($i = 0; $i 240) { // 4分钟保护 Yii::log("Loop timeout at {$i}/{$totalRows}", 'error', 'My.Code.Example'); break; } // 处理单行... }
? 总结:Yii 1.1 的日志“消失”极少是 log() 函数本身失效,而多源于请求未优雅结束。优先排查性能瓶颈、无限循环、未捕获异常及 PHP 资源限制,而非修改日志配置。通过 flush() 临时验证 + 分层超时控制 + 异步化重载,可系统性解决此类静默失败问题。











