diff --git a/src/AppConfig.php b/src/AppConfig.php index ed1ef732..486198bb 100644 --- a/src/AppConfig.php +++ b/src/AppConfig.php @@ -16,6 +16,7 @@ use craft\helpers\App; use craft\log\MonologTarget; use craft\queue\Queue as CraftQueue; +use Monolog\Handler\FormattableHandlerInterface; use yii\caching\ArrayCache; use yii\redis\Cache as RedisCache; @@ -161,9 +162,17 @@ private function getDefinitions(): array return [ Temp::class => TmpFs::class, MonologTarget::class => function($container, $params, $config) { - return new MonologTarget([ + $target = new MonologTarget([ 'logContext' => false, ] + $config); + + foreach ($target->getLogger()->getHandlers() as $handler) { + if ($handler instanceof FormattableHandlerInterface) { + $handler->setFormatter(new TruncatingFormatter($handler->getFormatter())); + } + } + + return $target; }, /** diff --git a/src/TruncatingFormatter.php b/src/TruncatingFormatter.php new file mode 100644 index 00000000..6c19631c --- /dev/null +++ b/src/TruncatingFormatter.php @@ -0,0 +1,49 @@ +truncate($this->formatter->format($record)); + } + + public function formatBatch(array $records) + { + return $this->truncate($this->formatter->formatBatch($records)); + } + + private function truncate(mixed $formatted): mixed + { + if (is_array($formatted)) { + return array_map($this->truncate(...), $formatted); + } + + if (!is_string($formatted) || strlen($formatted) <= self::MAX_BYTES) { + return $formatted; + } + + $lineEnding = preg_match('/\R\z/', $formatted, $matches) ? $matches[0] : ''; + $bytes = self::MAX_BYTES - strlen(self::SUFFIX . $lineEnding); + + return mb_strcut($formatted, 0, $bytes, 'UTF-8') . self::SUFFIX . $lineEnding; + } +} diff --git a/tests/unit/TruncatingFormatterTest.php b/tests/unit/TruncatingFormatterTest.php new file mode 100644 index 00000000..f291cffd --- /dev/null +++ b/tests/unit/TruncatingFormatterTest.php @@ -0,0 +1,38 @@ +formatted; + } + + public function formatBatch(array $records): string + { + return $this->formatted; + } + }; + $truncatingFormatter = new TruncatingFormatter($formatter); + + $formatter->formatted = "short\r\n"; + $this->assertSame($formatter->formatted, $truncatingFormatter->format(null)); + + $formatter->formatted = str_repeat('é', 240 * 1024) . "\r\n"; + $formatted = $truncatingFormatter->format(null); + + $this->assertLessThanOrEqual(240 * 1024, strlen($formatted)); + $this->assertTrue(mb_check_encoding($formatted, 'UTF-8')); + $this->assertStringEndsWith("... [truncated]\r\n", $formatted); + } +}