Skip to content

Commit 7074c49

Browse files
fix: Use sub-second timestamps in logs (#2018)
1 parent c34bdc2 commit 7074c49

3 files changed

Lines changed: 42 additions & 13 deletions

File tree

‎src/Monolog/BreadcrumbHandler.php‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,18 @@ public function __construct(HubInterface $hub, $level = Logger::DEBUG, bool $bub
5454
*/
5555
protected function write($record): void
5656
{
57+
$datetime = $record['datetime'] ?? null;
58+
$timestamp = $datetime instanceof \DateTimeInterface
59+
? $datetime->getTimestamp() + (int) $datetime->format('u') / 1000000
60+
: null;
61+
5762
$breadcrumb = new Breadcrumb(
5863
$this->getBreadcrumbLevel($record['level']),
5964
$this->getBreadcrumbType($record['level']),
6065
$record['channel'],
6166
$record['message'],
6267
($record['context'] ?? []) + ($record['extra'] ?? []),
63-
$record['datetime']->getTimestamp()
68+
$timestamp
6469
);
6570

6671
$this->hub->addBreadcrumb($breadcrumb);

‎tests/Monolog/BreadcrumbHandlerTest.php‎

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,19 @@ final class BreadcrumbHandlerTest extends TestCase
1515
{
1616
/**
1717
* @dataProvider handleDataProvider
18+
*
19+
* @param LogRecord|array<string, mixed> $record
1820
*/
1921
public function testHandle($record, Breadcrumb $expectedBreadcrumb): void
2022
{
2123
$hub = $this->createMock(HubInterface::class);
2224
$hub->expects($this->once())
2325
->method('addBreadcrumb')
24-
->with($this->callback(function (Breadcrumb $breadcrumb) use ($expectedBreadcrumb, $record): bool {
26+
->with($this->callback(function (Breadcrumb $breadcrumb) use ($expectedBreadcrumb): bool {
2527
$this->assertSame($expectedBreadcrumb->getMessage(), $breadcrumb->getMessage());
2628
$this->assertSame($expectedBreadcrumb->getLevel(), $breadcrumb->getLevel());
2729
$this->assertSame($expectedBreadcrumb->getType(), $breadcrumb->getType());
28-
$this->assertEquals($record['datetime']->getTimestamp(), $breadcrumb->getTimestamp());
30+
$this->assertEquals($expectedBreadcrumb->getTimestamp(), $breadcrumb->getTimestamp());
2931
$this->assertSame($expectedBreadcrumb->getCategory(), $breadcrumb->getCategory());
3032
$this->assertEquals($expectedBreadcrumb->getMetadata(), $breadcrumb->getMetadata());
3133

@@ -37,16 +39,19 @@ public function testHandle($record, Breadcrumb $expectedBreadcrumb): void
3739
}
3840

3941
/**
40-
* @return iterable<LogRecord|array{array<string, mixed>, Breadcrumb}>
42+
* @return iterable<array{LogRecord|array<string, mixed>, Breadcrumb}>
4143
*/
4244
public static function handleDataProvider(): iterable
4345
{
46+
$now = new \DateTimeImmutable();
47+
4448
$defaultBreadcrumb = new Breadcrumb(
4549
Breadcrumb::LEVEL_DEBUG,
4650
Breadcrumb::TYPE_DEFAULT,
4751
'channel.foo',
4852
'foo bar',
49-
[]
53+
[],
54+
(float) $now->format('U.u')
5055
);
5156

5257
$levelsToBeTested = [
@@ -58,31 +63,46 @@ public static function handleDataProvider(): iterable
5863

5964
foreach ($levelsToBeTested as $loggerLevel => $breadcrumbLevel) {
6065
yield 'with level ' . Logger::getLevelName($loggerLevel) => [
61-
RecordFactory::create('foo bar', $loggerLevel, 'channel.foo', [], []),
66+
RecordFactory::create('foo bar', $loggerLevel, 'channel.foo', [], [], $now),
6267
$defaultBreadcrumb->withLevel($breadcrumbLevel),
6368
];
6469
}
6570

6671
yield 'with level ERROR' => [
67-
RecordFactory::create('foo bar', Logger::ERROR, 'channel.foo', [], []),
72+
RecordFactory::create('foo bar', Logger::ERROR, 'channel.foo', [], [], $now),
6873
$defaultBreadcrumb->withLevel(Breadcrumb::LEVEL_ERROR)
6974
->withType(Breadcrumb::TYPE_ERROR),
7075
];
7176

7277
yield 'with level ALERT' => [
73-
RecordFactory::create('foo bar', Logger::ALERT, 'channel.foo', [], []),
78+
RecordFactory::create('foo bar', Logger::ALERT, 'channel.foo', [], [], $now),
7479
$defaultBreadcrumb->withLevel(Breadcrumb::LEVEL_FATAL)
7580
->withType(Breadcrumb::TYPE_ERROR),
7681
];
7782

7883
yield 'with context' => [
79-
RecordFactory::create('foo bar', Logger::DEBUG, 'channel.foo', ['context' => ['foo' => 'bar']], []),
84+
RecordFactory::create('foo bar', Logger::DEBUG, 'channel.foo', ['context' => ['foo' => 'bar']], [], $now),
8085
$defaultBreadcrumb->withMetadata('context', ['foo' => 'bar']),
8186
];
8287

8388
yield 'with extra' => [
84-
RecordFactory::create('foo bar', Logger::DEBUG, 'channel.foo', [], ['extra' => ['foo' => 'bar']]),
89+
RecordFactory::create('foo bar', Logger::DEBUG, 'channel.foo', [], ['extra' => ['foo' => 'bar']], $now),
8590
$defaultBreadcrumb->withMetadata('extra', ['foo' => 'bar']),
8691
];
92+
93+
yield 'with timestamp' => [
94+
RecordFactory::create('foo bar', Logger::DEBUG, 'channel.foo', [], [], new \DateTimeImmutable('1970-01-01 00:00:42.1337 UTC')),
95+
$defaultBreadcrumb->withTimestamp(42.1337),
96+
];
97+
98+
yield 'with zero timestamp' => [
99+
RecordFactory::create('foo bar', Logger::DEBUG, 'channel.foo', [], [], new \DateTimeImmutable('1970-01-01 00:00:00.000 UTC')),
100+
$defaultBreadcrumb->withTimestamp(0.0),
101+
];
102+
103+
yield 'with negative timestamp' => [
104+
RecordFactory::create('foo bar', Logger::DEBUG, 'channel.foo', [], [], new \DateTimeImmutable('1969-12-31 23:59:56.859 UTC')),
105+
$defaultBreadcrumb->withTimestamp(-3.141),
106+
];
87107
}
88108
}

‎tests/Monolog/RecordFactory.php‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@ final class RecordFactory
1919
*
2020
* @return array<string, mixed>|LogRecord
2121
*/
22-
public static function create(string $message, int $level, string $channel, array $context = [], array $extra = [])
22+
public static function create(string $message, int $level, string $channel, array $context = [], array $extra = [], ?\DateTimeImmutable $datetime = null)
2323
{
24+
if ($datetime === null) {
25+
$datetime = new \DateTimeImmutable();
26+
}
27+
2428
if (Logger::API >= 3) {
2529
return new LogRecord(
26-
new \DateTimeImmutable(),
30+
$datetime,
2731
$channel,
2832
Logger::toMonologLevel($level),
2933
$message,
@@ -39,7 +43,7 @@ public static function create(string $message, int $level, string $channel, arra
3943
'level_name' => Logger::getLevelName($level),
4044
'channel' => $channel,
4145
'extra' => $extra,
42-
'datetime' => new \DateTimeImmutable(),
46+
'datetime' => $datetime,
4347
];
4448
}
4549
}

0 commit comments

Comments
 (0)