diff --git a/CHANGELOG.md b/CHANGELOG.md index f3a400ef..a3ea01fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Add support for distributed tracing of SQL queries while using Doctrine DBAL (#426) - Add support for distributed tracing when running a console command (#455) - Add support for distributed tracing of cache pools (#) +- Add `Full command` to extras for CLI commands, which includes command with all arguments - Deprecate the `Sentry\SentryBundle\EventListener\ConsoleCommandListener` class in favor of its parent class `Sentry\SentryBundle\EventListener\ConsoleListener` (#429) - Lower the required version of `symfony/psr-http-message-bridge` to allow installing it on a project that uses Symfony `3.4.x` components only (#480) diff --git a/src/EventListener/ConsoleListener.php b/src/EventListener/ConsoleListener.php index d973ff90..46c57e1a 100644 --- a/src/EventListener/ConsoleListener.php +++ b/src/EventListener/ConsoleListener.php @@ -9,6 +9,7 @@ use Symfony\Component\Console\Event\ConsoleCommandEvent; use Symfony\Component\Console\Event\ConsoleErrorEvent; use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\Console\Input\ArgvInput; /** * This listener handles all errors thrown while running a console command and @@ -49,10 +50,15 @@ public function handleConsoleCommandEvent(ConsoleCommandEvent $event): void { $scope = $this->hub->pushScope(); $command = $event->getCommand(); + $input = $event->getInput(); if (null !== $command && null !== $command->getName()) { $scope->setTag('console.command', $command->getName()); } + + if ($input instanceof ArgvInput) { + $scope->setExtra('Full command', (string) $input); + } } /** diff --git a/tests/End2End/App/Command/MainCommand.php b/tests/End2End/App/Command/MainCommand.php new file mode 100644 index 00000000..5f5ad4fe --- /dev/null +++ b/tests/End2End/App/Command/MainCommand.php @@ -0,0 +1,27 @@ +addOption('option1', null, InputOption::VALUE_NONE) + ->addOption('option2', 'o2', InputOption::VALUE_OPTIONAL) + ->addArgument('id') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + throw new \RuntimeException('This is an intentional error'); + } +} diff --git a/tests/End2End/App/config.yml b/tests/End2End/App/config.yml index 315ad1e1..ee0e6bdf 100644 --- a/tests/End2End/App/config.yml +++ b/tests/End2End/App/config.yml @@ -23,6 +23,9 @@ services: tags: - controller.service_arguments + Sentry\SentryBundle\Tests\End2End\App\Command\MainCommand: + tags: [{ name: 'console.command', command: 'main-command' }] + monolog: handlers: main: diff --git a/tests/End2End/End2EndTest.php b/tests/End2End/End2EndTest.php index 6bdb6490..c19cecc3 100644 --- a/tests/End2End/End2EndTest.php +++ b/tests/End2End/End2EndTest.php @@ -10,6 +10,8 @@ use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\FrameworkBundle\KernelBrowser; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Output\NullOutput; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -171,6 +173,25 @@ public function testNotice(): void $this->assertEventCount(1); } + public function testCommand(): void + { + self::bootKernel(); + $application = new Application(self::$kernel); + + try { + $application->doRun(new ArgvInput(['bin/console', 'main-command', '--option1', '--option2=foo', 'bar']), new NullOutput()); + } catch (\RuntimeException $e) { + $this->assertSame('This is an intentional error', $e->getMessage()); + } + + $this->assertEventCount(1); + $this->assertCount(1, StubTransportFactory::$events); + $this->assertSame( + ['Full command' => 'main-command --option1 --option2=foo bar'], + StubTransportFactory::$events[0]->getExtra() + ); + } + public function testMessengerCaptureHardFailure(): void { $this->skipIfMessengerIsMissing(); diff --git a/tests/End2End/StubTransportFactory.php b/tests/End2End/StubTransportFactory.php index 3344ea3b..119e0dfd 100644 --- a/tests/End2End/StubTransportFactory.php +++ b/tests/End2End/StubTransportFactory.php @@ -17,11 +17,17 @@ class StubTransportFactory implements TransportFactoryInterface { public const SEPARATOR = '###'; + /** + * @var Event[] + */ + public static $events = []; + public function create(Options $options): TransportInterface { return new class() implements TransportInterface { public function send(Event $event): PromiseInterface { + StubTransportFactory::$events[] = $event; touch(End2EndTest::SENT_EVENTS_LOG); if ($event->getMessage()) { diff --git a/tests/EventListener/AbstractConsoleListenerTest.php b/tests/EventListener/AbstractConsoleListenerTest.php index 75a5b6b8..48711d47 100644 --- a/tests/EventListener/AbstractConsoleListenerTest.php +++ b/tests/EventListener/AbstractConsoleListenerTest.php @@ -14,6 +14,7 @@ use Symfony\Component\Console\Event\ConsoleCommandEvent; use Symfony\Component\Console\Event\ConsoleErrorEvent; use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -33,8 +34,9 @@ protected function setUp(): void * @dataProvider handleConsoleCommmandEventDataProvider * * @param array $expectedTags + * @param array $expectedExtra */ - public function testHandleConsoleCommandEvent(ConsoleCommandEvent $consoleEvent, array $expectedTags): void + public function testHandleConsoleCommandEvent(ConsoleCommandEvent $consoleEvent, array $expectedTags, array $expectedExtra): void { $listenerClass = static::getListenerClass(); $scope = new Scope(); @@ -49,6 +51,7 @@ public function testHandleConsoleCommandEvent(ConsoleCommandEvent $consoleEvent, $event = $scope->applyToEvent(Event::createEvent()); $this->assertSame($expectedTags, $event->getTags()); + $this->assertSame($expectedExtra, $event->getExtra()); } /** @@ -59,16 +62,25 @@ public function handleConsoleCommmandEventDataProvider(): \Generator yield [ new ConsoleCommandEvent(null, $this->createMock(InputInterface::class), $this->createMock(OutputInterface::class)), [], + [], ]; yield [ new ConsoleCommandEvent(new Command(), $this->createMock(InputInterface::class), $this->createMock(OutputInterface::class)), [], + [], ]; yield [ new ConsoleCommandEvent(new Command('foo:bar'), $this->createMock(InputInterface::class), $this->createMock(OutputInterface::class)), ['console.command' => 'foo:bar'], + [], + ]; + + yield [ + new ConsoleCommandEvent(new Command('foo:bar'), new ArgvInput(['bin/console', 'foo:bar', '--foo=bar']), $this->createMock(OutputInterface::class)), + ['console.command' => 'foo:bar'], + ['Full command' => "'foo:bar' --foo=bar"], ]; }