diff --git a/src/Monolog/Formatter/JsonFormatter.php b/src/Monolog/Formatter/JsonFormatter.php index b59639e17..fa11d0ef0 100644 --- a/src/Monolog/Formatter/JsonFormatter.php +++ b/src/Monolog/Formatter/JsonFormatter.php @@ -195,7 +195,11 @@ protected function normalize(mixed $data, int $depth = 0): mixed } if ($data instanceof Stringable) { - return $data->__toString(); + try { + return $data->__toString(); + } catch (Throwable) { + return $data::class; + } } if (\get_class($data) === '__PHP_Incomplete_Class') { diff --git a/tests/Monolog/Formatter/JsonFormatterTest.php b/tests/Monolog/Formatter/JsonFormatterTest.php index 56cef2287..271881f06 100644 --- a/tests/Monolog/Formatter/JsonFormatterTest.php +++ b/tests/Monolog/Formatter/JsonFormatterTest.php @@ -11,6 +11,7 @@ namespace Monolog\Formatter; +use Exception; use Monolog\Level; use Monolog\LogRecord; use JsonSerializable; @@ -338,6 +339,24 @@ public function testFormatObjects() $record ); } + + public function testNormalizeHandleExceptionInToString(): void + { + $formatter = new JsonFormatter(); + + $res = $formatter->format($this->getRecord( + Level::Critical, + 'bar', + datetime: new \DateTimeImmutable('2025-05-19 00:00:00'), + channel: 'test', + context: ['object' => new TestJsonNormWithFailingToString], + )); + + $this->assertSame( + '{"message":"bar","context":{"object":"Monolog\\\\Formatter\\\\TestJsonNormWithFailingToString"},"level":500,"level_name":"CRITICAL","channel":"test","datetime":"2025-05-19T00:00:00+00:00","extra":{}}'."\n", + $res, + ); + } } class TestJsonNormPublic @@ -370,3 +389,11 @@ public function __toString() return 'stringified'; } } + +class TestJsonNormWithFailingToString +{ + public function __toString() + { + throw new Exception('Whatever'); + } +} \ No newline at end of file