diff --git a/analysis-baseline.toml b/analysis-baseline.toml index 839354d6a..48da55acb 100644 --- a/analysis-baseline.toml +++ b/analysis-baseline.toml @@ -630,6 +630,12 @@ code = "possibly-invalid-argument" message = 'Possible argument type mismatch for argument #1 of `Monolog\Handler\AbstractHandler::isHandling`: expected `Monolog\LogRecord`, but possibly received `Monolog\LogRecord|array`.' count = 1 +[[issues]] +file = "src/Monolog/ExceptionToSentryIssueHandler.php" +code = "possibly-invalid-argument" +message = 'Possible argument type mismatch for argument #1 of `Monolog\Handler\AbstractProcessingHandler::handle`: expected `Monolog\LogRecord`, but possibly received `Monolog\LogRecord|array`.' +count = 1 + [[issues]] file = "src/Monolog/Handler.php" code = "mixed-argument" diff --git a/src/Monolog/ExceptionToSentryIssueHandler.php b/src/Monolog/ExceptionToSentryIssueHandler.php index b2e7abeb6..6f1d7cde5 100644 --- a/src/Monolog/ExceptionToSentryIssueHandler.php +++ b/src/Monolog/ExceptionToSentryIssueHandler.php @@ -4,7 +4,7 @@ namespace Sentry\Monolog; -use Monolog\Handler\AbstractHandler; +use Monolog\Handler\AbstractProcessingHandler; use Monolog\Level; use Monolog\Logger; use Monolog\LogRecord; @@ -15,8 +15,10 @@ /** * This Monolog handler will collect monolog events and send them to sentry. */ -class ExceptionToSentryIssueHandler extends AbstractHandler +class ExceptionToSentryIssueHandler extends AbstractProcessingHandler { + use CompatibilityProcessingHandlerTrait; + /** * @var HubInterface */ @@ -44,6 +46,21 @@ public function handle($record): bool return false; } + /** @phpstan-ignore argument.type */ + return parent::handle($record); + } + + /** + * @param array|LogRecord $record + */ + protected function doWrite($record): void + { + // A processor may have removed the exception from the context + $exception = $this->getExceptionFromRecord($record); + if ($exception === null) { + return; + } + $this->hub->withScope(function (Scope $scope) use ($record, $exception): void { $scope->setExtra('monolog.channel', $record['channel']); $scope->setExtra('monolog.level', $record['level_name']); @@ -63,8 +80,6 @@ public function handle($record): bool $this->hub->captureException($exception); }); - - return $this->bubble === false; } /** diff --git a/tests/Monolog/ExceptionToSentryIssueHandlerTest.php b/tests/Monolog/ExceptionToSentryIssueHandlerTest.php index 20dd68139..89151b340 100644 --- a/tests/Monolog/ExceptionToSentryIssueHandlerTest.php +++ b/tests/Monolog/ExceptionToSentryIssueHandlerTest.php @@ -6,6 +6,7 @@ use Monolog\Logger; use Monolog\LogRecord; +use Monolog\Processor\PsrLogMessageProcessor; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Sentry\ClientInterface; @@ -97,6 +98,42 @@ public function testHandleReturnsTrueWhenBubblingDisabled(): void $this->assertTrue($handler->handle($record)); } + public function testHandleAppliesProcessors(): void + { + $exception = new \RuntimeException('boom'); + + /** @var ClientInterface&MockObject $client */ + $client = $this->createMock(ClientInterface::class); + $client->expects($this->once()) + ->method('captureException') + ->with( + $this->identicalTo($exception), + $this->callback(function (Scope $scopeArg): bool { + $event = $scopeArg->applyToEvent(Event::createEvent()); + + $this->assertNotNull($event); + $this->assertSame('Failed to sync user john', $event->getExtra()['monolog.message']); + + return true; + }), + null + ); + + $handler = new ExceptionToSentryIssueHandler(new Hub($client, new Scope())); + $handler->pushProcessor(new PsrLogMessageProcessor()); + + $handler->handle(RecordFactory::create( + 'Failed to sync user {username}', + Logger::ERROR, + 'channel.foo', + [ + 'username' => 'john', + 'exception' => $exception, + ], + [] + )); + } + /** * @dataProvider ignoredRecordsDataProvider *