From d79edcef53df0d0c4d8f684bfaeca167bd81c4a8 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Wed, 17 Jun 2026 14:30:50 +0800 Subject: [PATCH] fix: add recursion guard to prevent 100% CPU when STDOUT/STDERR is broken When the terminal (IDE) is closed without stopping the framework first, the PTY is destroyed and STDOUT/STDERR become broken file descriptors. Any subsequent log call via echo/fwrite fails with E_WARNING, the error handler catches it and calls the logger again, creating a recursive loop that consumes 100% CPU. Changes: - Add static $in_log recursion guard flag to ConsoleLogger - Wrap log output (echo/fwrite/fflush) with the guard and @ suppression - Bump version from 1.1.6 to 1.1.7 --- src/ZM/Logger/ConsoleLogger.php | 37 ++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/ZM/Logger/ConsoleLogger.php b/src/ZM/Logger/ConsoleLogger.php index 0a173be..705e251 100644 --- a/src/ZM/Logger/ConsoleLogger.php +++ b/src/ZM/Logger/ConsoleLogger.php @@ -10,7 +10,7 @@ use Psr\Log\LogLevel; class ConsoleLogger extends AbstractLogger { - public const VERSION = '1.1.6'; + public const VERSION = '1.1.7'; /** * 日志输出格式 @@ -86,6 +86,13 @@ class ConsoleLogger extends AbstractLogger */ protected $stream; + /** + * 递归保护标志 — 防止日志写入失败时错误处理器再次调用日志导致死循环 CPU 100% + * + * @var bool + */ + protected static $in_log = false; + /** * 是否带颜色 * @@ -242,17 +249,27 @@ class ConsoleLogger extends AbstractLogger } else { $output = $output . PHP_EOL; } - // use stream - if ($this->stream) { - fwrite($this->stream, $output); - fflush($this->stream); - } else { - if ($level <= 4 && $this->use_stderr) { - fwrite(STDERR, $output); + // 递归保护:如果上一次日志写入触发了错误(如终端关闭后 STDOUT/STDERR 破损), + // 跳过本次输出,防止错误处理器递归调用导致 CPU 100% 空耗 + if (self::$in_log) { + return; + } + self::$in_log = true; + try { + // use stream + if ($this->stream) { + @fwrite($this->stream, $output); + @fflush($this->stream); } else { - // use plain text output - echo $output; + if ($level <= 4 && $this->use_stderr) { + @fwrite(STDERR, $output); + } else { + // use plain text output + @echo $output; + } } + } finally { + self::$in_log = false; } }