diff --git a/src/Monolog/DateTimeImmutable.php b/src/Monolog/DateTimeImmutable.php new file mode 100644 index 00000000..6187c5a6 --- /dev/null +++ b/src/Monolog/DateTimeImmutable.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +/** + * Overrides default json encoding of date time objects + * + * @author Menno Holtkamp + * @author Jordi Boggiano + */ +class DateTimeImmutable extends \DateTimeImmutable implements \JsonSerializable +{ + private $useMicroseconds; + + public function __construct($useMicroseconds, \DateTimeZone $timezone = null) + { + $date = null; + if ($useMicroseconds) { + $timestamp = microtime(true); + $microseconds = sprintf("%06d", ($timestamp - floor($timestamp)) * 1000000); + $date = date('Y-m-d H:i:s.' . $microseconds, $timestamp); + } + parent::__construct($date, $timezone); + + $this->useMicroseconds = $useMicroseconds; + } + + /** + * @return string + */ + public function jsonSerialize() + { + if ($this->useMicroseconds) { + return $this->format('Y-m-d\TH:i:s.uP'); + } + + return $this->format('Y-m-d\TH:i:sP'); + } +} diff --git a/src/Monolog/Logger.php b/src/Monolog/Logger.php index 74ab2857..f81a6275 100644 --- a/src/Monolog/Logger.php +++ b/src/Monolog/Logger.php @@ -315,12 +315,7 @@ class Logger implements LoggerInterface return false; } - if ($this->microsecondTimestamps) { - $ts = \DateTimeImmutable::createFromFormat('U.u', sprintf('%.6F', microtime(true)), $this->timezone); - } else { - $ts = new \DateTimeImmutable('', $this->timezone); - } - $ts->setTimezone($this->timezone); + $ts = $this->createDateTime(); $record = array( 'message' => $message, @@ -560,4 +555,17 @@ class Logger implements LoggerInterface { return $this->timezone; } + + /** + * Circumvent DateTimeImmutable::createFromFormat() which always returns the native DateTime instead of the specialized one + * + * @link https://bugs.php.net/bug.php?id=60302 + */ + private function createDateTime(): DateTimeImmutable + { + $dateTime = new DateTimeImmutable($this->microsecondTimestamps, $this->timezone); + $dateTime->setTimezone($this->timezone); + + return $dateTime; + } } diff --git a/src/Monolog/Test/TestCase.php b/src/Monolog/Test/TestCase.php index 40b387c6..3fe7550c 100644 --- a/src/Monolog/Test/TestCase.php +++ b/src/Monolog/Test/TestCase.php @@ -12,6 +12,7 @@ namespace Monolog\Test; use Monolog\Logger; +use Monolog\DateTimeImmutable; /** * Lets you easily generate log records and a dummy formatter for testing purposes @@ -31,7 +32,7 @@ class TestCase extends \PHPUnit_Framework_TestCase 'level' => $level, 'level_name' => Logger::getLevelName($level), 'channel' => 'test', - 'datetime' => \DateTimeImmutable::createFromFormat('U.u', sprintf('%.6F', microtime(true))), + 'datetime' => new DateTimeImmutable(true), 'extra' => array(), ); } diff --git a/tests/Monolog/Formatter/JsonFormatterTest.php b/tests/Monolog/Formatter/JsonFormatterTest.php index eb717567..2b6d1650 100644 --- a/tests/Monolog/Formatter/JsonFormatterTest.php +++ b/tests/Monolog/Formatter/JsonFormatterTest.php @@ -42,7 +42,7 @@ class JsonFormatterTest extends TestCase $formatter = new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false); $record = $this->getRecord(); - $this->assertEquals(json_encode($record), $formatter->format($record)); + $this->assertEquals('{"message":"test","context":[],"level":300,"level_name":"WARNING","channel":"test","datetime":"'.$record['datetime']->format('Y-m-d\TH:i:s.uP').'","extra":[]}', $formatter->format($record)); } /**