Skip to content

Commit ba72c29

Browse files
authored
ref(logs): allow different log level types (#1992)
1 parent 5c38fd2 commit ba72c29

3 files changed

Lines changed: 157 additions & 97 deletions

File tree

src/Monolog/LogsHandler.php

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ class LogsHandler implements HandlerInterface
1818
/**
1919
* The minimum logging level at which this handler will be triggered.
2020
*
21-
* @var LogLevel
21+
* @psalm-suppress UndefinedDocblockClass
22+
*
23+
* @var LogLevel|\Monolog\Level|int
2224
*/
2325
private $logLevel;
2426

@@ -32,21 +34,32 @@ class LogsHandler implements HandlerInterface
3234
/**
3335
* Creates a new Monolog handler that converts Monolog logs to Sentry logs.
3436
*
35-
* @param LogLevel|null $logLevel the minimum logging level at which this handler will be triggered and collects the logs
36-
* @param bool $bubble whether the messages that are handled can bubble up the stack or not
37+
* @psalm-suppress UndefinedDocblockClass
38+
*
39+
* @param LogLevel|\Monolog\Level|int|null $logLevel the minimum logging level at which this handler will be triggered and collects the logs
40+
* @param bool $bubble whether the messages that are handled can bubble up the stack or not
3741
*/
38-
public function __construct(?LogLevel $logLevel = null, bool $bubble = true)
42+
public function __construct($logLevel = null, bool $bubble = true)
3943
{
4044
$this->logLevel = $logLevel ?? LogLevel::debug();
4145
$this->bubble = $bubble;
4246
}
4347

4448
/**
49+
* @psalm-suppress UndefinedDocblockClass
50+
* @psalm-suppress UndefinedClass
51+
*
4552
* @param array<string, mixed>|LogRecord $record
4653
*/
4754
public function isHandling($record): bool
4855
{
49-
return self::getSentryLogLevelFromMonologLevel($record['level'])->getPriority() >= $this->logLevel->getPriority();
56+
if ($this->logLevel instanceof LogLevel) {
57+
return self::getSentryLogLevelFromMonologLevel($record['level'])->getPriority() >= $this->logLevel->getPriority();
58+
} elseif ($this->logLevel instanceof \Monolog\Level) {
59+
return $record['level'] >= $this->logLevel->value;
60+
} else {
61+
return $record['level'] >= $this->logLevel;
62+
}
5063
}
5164

5265
/**

tests/Monolog/LogsHandlerTest.php

Lines changed: 138 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,17 @@
44

55
namespace Sentry\Tests\Monolog;
66

7+
use Monolog\Level;
78
use Monolog\Logger;
89
use PHPUnit\Framework\TestCase;
910
use Sentry\ClientBuilder;
10-
use Sentry\Event;
1111
use Sentry\Logs\Log;
1212
use Sentry\Logs\LogLevel;
1313
use Sentry\Logs\Logs;
1414
use Sentry\Monolog\LogsHandler;
1515
use Sentry\SentrySdk;
1616
use Sentry\State\Hub;
17-
use Sentry\Transport\Result;
18-
use Sentry\Transport\ResultStatus;
19-
use Sentry\Transport\TransportInterface;
17+
use Sentry\Tests\StubTransport;
2018

2119
final class LogsHandlerTest extends TestCase
2220
{
@@ -64,42 +62,47 @@ static function (string $key) {
6462
}
6563

6664
/**
67-
* @dataProvider logLevelDataProvider
65+
* @dataProvider monologLegacyLevelDataProvider
6866
*/
69-
public function testLogLevels($record, int $countLogs): void
67+
public function testFiltersAndMapsUsingLegacyMonologThreshold(int $threshold, int $recordLevel, int $expectedCount, ?LogLevel $expectedMappedLevel): void
7068
{
71-
$handler = new LogsHandler(LogLevel::warn());
72-
$handler->handle($record);
69+
$handler = new LogsHandler($threshold);
70+
$handler->handle(RecordFactory::create('foo bar', $recordLevel, 'channel.foo', [], []));
7371

7472
$logs = Logs::getInstance()->aggregator()->all();
75-
$this->assertCount($countLogs, $logs);
73+
$this->assertCount($expectedCount, $logs);
74+
75+
if ($expectedMappedLevel !== null) {
76+
$this->assertEquals($expectedMappedLevel, $logs[0]->getLevel());
77+
}
78+
}
79+
80+
/**
81+
* @dataProvider monologLevelDataProvider
82+
*/
83+
public function testFiltersAndMapsUsingMonologEnumThreshold($threshold, $recordLevel, int $expectedCount, ?LogLevel $expectedMappedLevel): void
84+
{
85+
if (!class_exists(Level::class)) {
86+
$this->markTestSkipped('Test only works for Monolog >= 3');
87+
}
88+
89+
$this->assertInstanceOf(Level::class, $threshold);
90+
$this->assertInstanceOf(Level::class, $recordLevel);
91+
92+
$handler = new LogsHandler($threshold);
93+
$handler->handle(RecordFactory::create('foo bar', $recordLevel->value, 'channel.foo', [], []));
94+
95+
$logs = Logs::getInstance()->aggregator()->all();
96+
$this->assertCount($expectedCount, $logs);
97+
98+
if ($expectedMappedLevel !== null) {
99+
$this->assertEquals($expectedMappedLevel, $logs[0]->getLevel());
100+
}
76101
}
77102

78103
public function testLogsHandlerDestructor()
79104
{
80-
$transport = new class implements TransportInterface {
81-
private $events = [];
82-
83-
public function send(Event $event): Result
84-
{
85-
$this->events[] = $event;
86-
87-
return new Result(ResultStatus::success());
88-
}
89-
90-
public function close(?int $timeout = null): Result
91-
{
92-
return new Result(ResultStatus::success());
93-
}
94-
95-
/**
96-
* @return Event[]
97-
*/
98-
public function getEvents(): array
99-
{
100-
return $this->events;
101-
}
102-
};
105+
$transport = new StubTransport();
103106
$client = ClientBuilder::create([
104107
'enable_logs' => true,
105108
])->setTransport($transport)
@@ -110,8 +113,8 @@ public function getEvents(): array
110113

111114
$this->handleLogAndDrop();
112115

113-
$this->assertCount(1, $transport->getEvents());
114-
$this->assertSame('I was dropped :(', $transport->getEvents()[0]->getLogs()[0]->getBody());
116+
$this->assertCount(1, StubTransport::$events);
117+
$this->assertSame('I was dropped :(', StubTransport::$events[0]->getLogs()[0]->getBody());
115118
}
116119

117120
private function handleLogAndDrop(): void
@@ -283,83 +286,127 @@ public static function handleDataProvider(): iterable
283286
];
284287
}
285288

286-
public static function logLevelDataProvider(): iterable
289+
public static function monologLegacyLevelDataProvider(): iterable
287290
{
288-
yield [
289-
RecordFactory::create(
290-
'foo bar',
291-
Logger::DEBUG,
292-
'channel.foo',
293-
[],
294-
[]
295-
),
291+
yield 'NOTICE threshold drops INFO (both map to sentry info)' => [
292+
Logger::NOTICE,
293+
Logger::INFO,
296294
0,
295+
null,
297296
];
298297

299-
yield [
300-
RecordFactory::create(
301-
'foo bar',
302-
Logger::NOTICE,
303-
'channel.foo',
304-
[],
305-
[]
306-
),
298+
yield 'NOTICE threshold keeps NOTICE (mapped to sentry info)' => [
299+
Logger::NOTICE,
300+
Logger::NOTICE,
301+
1,
302+
LogLevel::info(),
303+
];
304+
305+
yield 'NOTICE threshold keeps WARNING (mapped to sentry warn)' => [
306+
Logger::NOTICE,
307+
Logger::WARNING,
308+
1,
309+
LogLevel::warn(),
310+
];
311+
312+
yield 'ALERT threshold drops CRITICAL (both map to sentry fatal)' => [
313+
Logger::ALERT,
314+
Logger::CRITICAL,
307315
0,
316+
null,
308317
];
309318

310-
yield [
311-
RecordFactory::create(
312-
'foo bar',
313-
Logger::INFO,
314-
'channel.foo',
315-
[],
316-
[]
317-
),
319+
yield 'ALERT threshold keeps ALERT (mapped to sentry fatal)' => [
320+
Logger::ALERT,
321+
Logger::ALERT,
322+
1,
323+
LogLevel::fatal(),
324+
];
325+
326+
yield 'ALERT threshold keeps EMERGENCY (mapped to sentry fatal)' => [
327+
Logger::ALERT,
328+
Logger::EMERGENCY,
329+
1,
330+
LogLevel::fatal(),
331+
];
332+
333+
yield 'EMERGENCY threshold drops ALERT (both map to sentry fatal)' => [
334+
Logger::EMERGENCY,
335+
Logger::ALERT,
318336
0,
337+
null,
319338
];
320339

321-
yield [
322-
RecordFactory::create(
323-
'foo bar',
324-
Logger::WARNING,
325-
'channel.foo',
326-
[],
327-
[]
328-
),
340+
yield 'EMERGENCY threshold keeps EMERGENCY (mapped to sentry fatal)' => [
341+
Logger::EMERGENCY,
342+
Logger::EMERGENCY,
329343
1,
344+
LogLevel::fatal(),
330345
];
346+
}
331347

332-
yield [
333-
RecordFactory::create(
334-
'foo bar',
335-
Logger::CRITICAL,
336-
'channel.foo',
337-
[],
338-
[]
339-
),
348+
public static function monologLevelDataProvider(): iterable
349+
{
350+
if (!class_exists(Level::class)) {
351+
yield 'Monolog < 3 (skipped)' => [null, null, 0, null];
352+
353+
return;
354+
}
355+
356+
yield 'NOTICE threshold drops INFO (both map to sentry info)' => [
357+
Level::Notice,
358+
Level::Info,
359+
0,
360+
null,
361+
];
362+
363+
yield 'NOTICE threshold keeps NOTICE (mapped to sentry info)' => [
364+
Level::Notice,
365+
Level::Notice,
340366
1,
367+
LogLevel::info(),
341368
];
342369

343-
yield [
344-
RecordFactory::create(
345-
'foo bar',
346-
Logger::ALERT,
347-
'channel.foo',
348-
[],
349-
[]
350-
),
370+
yield 'NOTICE threshold keeps WARNING (mapped to sentry warn)' => [
371+
Level::Notice,
372+
Level::Warning,
351373
1,
374+
LogLevel::warn(),
352375
];
353376

354-
yield [
355-
RecordFactory::create(
356-
'foo bar',
357-
Logger::EMERGENCY,
358-
'channel.foo',
359-
[],
360-
[]
361-
),
377+
yield 'ALERT threshold drops CRITICAL (both map to sentry fatal)' => [
378+
Level::Alert,
379+
Level::Critical,
380+
0,
381+
null,
382+
];
383+
384+
yield 'ALERT threshold keeps ALERT (mapped to sentry fatal)' => [
385+
Level::Alert,
386+
Level::Alert,
387+
1,
388+
LogLevel::fatal(),
389+
];
390+
391+
yield 'ALERT threshold keeps EMERGENCY (mapped to sentry fatal)' => [
392+
Level::Alert,
393+
Level::Emergency,
394+
1,
395+
LogLevel::fatal(),
396+
];
397+
398+
yield 'EMERGENCY threshold drops ALERT (both map to sentry fatal)' => [
399+
Level::Emergency,
400+
Level::Alert,
401+
0,
402+
null,
403+
];
404+
405+
yield 'EMERGENCY threshold keeps EMERGENCY (mapped to sentry fatal)' => [
406+
Level::Emergency,
407+
Level::Emergency,
362408
1,
409+
LogLevel::fatal(),
363410
];
364411
}
365412
}

tests/Monolog/RecordFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ 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 = [])
2323
{
2424
if (Logger::API >= 3) {
2525
return new LogRecord(

0 commit comments

Comments
 (0)