Skip to content

Make the list of commands for which distributed tracing is active configurable #515

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- Make the transport factory configurable in the bundle's config (#504)
- Add the `sentry_trace_meta()` Twig function to print the `sentry-trace` HTML meta tag (#510)
- Make the list of commands for which distributed tracing is active configurable (#515)

## 4.1.0 (2021-04-19)

Expand Down
10 changes: 10 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,16 @@ private function addDistributedTracingSection(ArrayNodeDefinition $rootNode): vo
->arrayNode('cache')
->{interface_exists(CacheInterface::class) ? 'canBeDisabled' : 'canBeEnabled'}()
->end()
->arrayNode('console')
->addDefaultsIfNotSet()
->fixXmlConfig('excluded_command')
->children()
->arrayNode('excluded_commands')
->scalarPrototype()->end()
->defaultValue(['messenger:consume'])
->end()
->end()
->end()
->end()
->end()
->end();
Expand Down
4 changes: 4 additions & 0 deletions src/DependencyInjection/SentryExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,11 @@ private function registerTracingConfiguration(ContainerBuilder $container, array
$container->removeDefinition(TracingRequestListener::class);
$container->removeDefinition(TracingSubRequestListener::class);
$container->removeDefinition(TracingConsoleListener::class);

return;
}

$container->getDefinition(TracingConsoleListener::class)->replaceArgument(1, $config['console']['excluded_commands']);
}

/**
Expand Down
34 changes: 30 additions & 4 deletions src/EventListener/TracingConsoleListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,21 @@ final class TracingConsoleListener
*/
private $hub;

/**
* @var string[] The list of commands for which distributed tracing must be skipped
*/
private $excludedCommands;

/**
* Constructor.
*
* @param HubInterface $hub The current hub
* @param HubInterface $hub The current hub
* @param string[] $excludedCommands The list of commands for which distributed tracing must be skipped
*/
public function __construct(HubInterface $hub)
public function __construct(HubInterface $hub, array $excludedCommands = [])
{
$this->hub = $hub;
$this->excludedCommands = $excludedCommands;
}

/**
Expand All @@ -42,18 +49,24 @@ public function __construct(HubInterface $hub)
*/
public function handleConsoleCommandEvent(ConsoleCommandEvent $event): void
{
$command = $event->getCommand();

if ($this->isCommandExcluded($command)) {
return;
}

$currentSpan = $this->hub->getSpan();

if (null === $currentSpan) {
$transactionContext = new TransactionContext();
$transactionContext->setOp('console.command');
$transactionContext->setName($this->getSpanName($event->getCommand()));
$transactionContext->setName($this->getSpanName($command));

$span = $this->hub->startTransaction($transactionContext);
} else {
$spanContext = new SpanContext();
$spanContext->setOp('console.command');
$spanContext->setDescription($this->getSpanName($event->getCommand()));
$spanContext->setDescription($this->getSpanName($command));

$span = $currentSpan->startChild($spanContext);
}
Expand All @@ -69,6 +82,10 @@ public function handleConsoleCommandEvent(ConsoleCommandEvent $event): void
*/
public function handleConsoleTerminateEvent(ConsoleTerminateEvent $event): void
{
if ($this->isCommandExcluded($event->getCommand())) {
return;
}

$span = $this->hub->getSpan();

if (null !== $span) {
Expand All @@ -84,4 +101,13 @@ private function getSpanName(?Command $command): string

return $command->getName();
}

private function isCommandExcluded(?Command $command): bool
{
if (null === $command) {
return true;
}

return \in_array($command->getName(), $this->excludedCommands, true);
}
}
7 changes: 7 additions & 0 deletions src/Resources/config/schema/sentry-1.0.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
<xsd:element name="dbal" type="tracing-dbal" minOccurs="0" maxOccurs="1" />
<xsd:element name="twig" type="tracing-twig" minOccurs="0" maxOccurs="1" />
<xsd:element name="cache" type="tracing-cache" minOccurs="0" maxOccurs="1" />
<xsd:element name="console" type="tracing-console" minOccurs="0" maxOccurs="1" />
</xsd:choice>

<xsd:attribute name="enabled" type="xsd:boolean" default="true"/>
Expand All @@ -107,4 +108,10 @@
<xsd:complexType name="tracing-cache">
<xsd:attribute name="enabled" type="xsd:boolean" />
</xsd:complexType>

<xsd:complexType name="tracing-console">
<xsd:sequence maxOccurs="unbounded">
<xsd:element name="excluded-command" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
1 change: 1 addition & 0 deletions src/Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@

<service id="Sentry\SentryBundle\EventListener\TracingConsoleListener" class="Sentry\SentryBundle\EventListener\TracingConsoleListener">
<argument type="service" id="Sentry\State\HubInterface" />
<argument /> <!-- $excludedCommands -->

<tag name="kernel.event_listener" event="console.command" method="handleConsoleCommandEvent" priority="118" />
<tag name="kernel.event_listener" event="console.terminate" method="handleConsoleTerminateEvent" priority="-54" />
Expand Down
3 changes: 3 additions & 0 deletions tests/DependencyInjection/ConfigurationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ public function testProcessConfigurationWithDefaultConfiguration(): void
'cache' => [
'enabled' => interface_exists(CacheInterface::class),
],
'console' => [
'excluded_commands' => ['messenger:consume'],
],
],
];

Expand Down
17 changes: 17 additions & 0 deletions tests/DependencyInjection/Fixtures/php/console_tracing_enabled.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

use Symfony\Component\DependencyInjection\ContainerBuilder;

/** @var ContainerBuilder $container */
$container->loadFromExtension('sentry', [
'tracing' => [
'console' => [
'excluded_commands' => [
'foo:bar',
'bar:foo',
],
],
],
]);
3 changes: 3 additions & 0 deletions tests/DependencyInjection/Fixtures/php/full.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,8 @@
'cache' => [
'enabled' => false,
],
'console' => [
'excluded_commands' => ['app:command'],
],
],
]);
17 changes: 17 additions & 0 deletions tests/DependencyInjection/Fixtures/xml/console_tracing_enabled.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sentry="https://sentry.io/schema/dic/sentry-symfony"
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
https://sentry.io/schema/dic/sentry-symfony https://sentry.io/schema/dic/sentry-symfony/sentry-1.0.xsd">

<sentry:config>
<sentry:tracing>
<sentry:console>
<sentry:excluded-command>foo:bar</sentry:excluded-command>
<sentry:excluded-command>bar:foo</sentry:excluded-command>
</sentry:console>
</sentry:tracing>
</sentry:config>
</container>
3 changes: 3 additions & 0 deletions tests/DependencyInjection/Fixtures/xml/full.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
</sentry:dbal>
<sentry:twig enabled="false" />
<sentry:cache enabled="false" />
<sentry:console>
<sentry:excluded-command>app:command</sentry:excluded-command>
</sentry:console>
</sentry:tracing>
</sentry:config>
</container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
sentry:
tracing:
console:
excluded_commands:
- foo:bar
- bar:foo
3 changes: 3 additions & 0 deletions tests/DependencyInjection/Fixtures/yml/full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,6 @@ sentry:
enabled: false
cache:
enabled: false
console:
excluded_commands:
- app:command
8 changes: 8 additions & 0 deletions tests/DependencyInjection/SentryExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,14 @@ public function testTwigTracingExtensionIsRemovedWhenTwigTracingIsDisabled(): vo
$this->assertFalse($container->hasDefinition(TwigTracingExtension::class));
}

public function testConsoleTracingListenerIsConfiguredWhenTracingIsEnabled(): void
{
$container = $this->createContainerFromFixture('console_tracing_enabled');

$this->assertTrue($container->hasDefinition(TracingConsoleListener::class));
$this->assertSame(['foo:bar', 'bar:foo'], $container->getDefinition(TracingConsoleListener::class)->getArgument(1));
}

private function createContainerFromFixture(string $fixtureFile): ContainerBuilder
{
$container = new ContainerBuilder(new EnvPlaceholderParameterBag([
Expand Down
63 changes: 37 additions & 26 deletions tests/EventListener/TracingConsoleListenerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,15 @@ final class TracingConsoleListenerTest extends TestCase
*/
private $hub;

/**
* @var TracingConsoleListener
*/
private $listener;

protected function setUp(): void
{
$this->hub = $this->createMock(HubInterface::class);
$this->listener = new TracingConsoleListener($this->hub);
}

/**
* @dataProvider handleConsoleCommandEventStartsTransactionIfNoSpanIsSetOnHubDataProvider
*/
public function testHandleConsoleCommandEventStartsTransactionIfNoSpanIsSetOnHub(?Command $command, TransactionContext $expectedTransactionContext): void
public function testHandleConsoleCommandEventStartsTransactionIfNoSpanIsSetOnHub(Command $command, TransactionContext $expectedTransactionContext): void
{
$transaction = new Transaction(new TransactionContext());

Expand All @@ -61,7 +55,8 @@ public function testHandleConsoleCommandEventStartsTransactionIfNoSpanIsSetOnHub
->with($transaction)
->willReturnSelf();

$this->listener->handleConsoleCommandEvent(new ConsoleCommandEvent(
$listener = new TracingConsoleListener($this->hub);
$listener->handleConsoleCommandEvent(new ConsoleCommandEvent(
$command,
$this->createMock(InputInterface::class),
$this->createMock(OutputInterface::class)
Expand All @@ -77,15 +72,6 @@ public function handleConsoleCommandEventStartsTransactionIfNoSpanIsSetOnHubData
$transactionContext->setOp('console.command');
$transactionContext->setName('<unnamed command>');

yield [
null,
$transactionContext,
];

$transactionContext = new TransactionContext();
$transactionContext->setOp('console.command');
$transactionContext->setName('<unnamed command>');

yield [
new Command(),
$transactionContext,
Expand All @@ -104,7 +90,7 @@ public function handleConsoleCommandEventStartsTransactionIfNoSpanIsSetOnHubData
/**
* @dataProvider handleConsoleCommandEventStartsChildSpanIfSpanIsSetOnHubDataProvider
*/
public function testHandleConsoleCommandEventStartsChildSpanIfSpanIsSetOnHub(?Command $command, string $expectedDescription): void
public function testHandleConsoleCommandEventStartsChildSpanIfSpanIsSetOnHub(Command $command, string $expectedDescription): void
{
$span = new Span();

Expand All @@ -123,7 +109,8 @@ public function testHandleConsoleCommandEventStartsChildSpanIfSpanIsSetOnHub(?Co
}))
->willReturnSelf();

$this->listener->handleConsoleCommandEvent(new ConsoleCommandEvent(
$listener = new TracingConsoleListener($this->hub);
$listener->handleConsoleCommandEvent(new ConsoleCommandEvent(
$command,
$this->createMock(InputInterface::class),
$this->createMock(OutputInterface::class)
Expand All @@ -135,11 +122,6 @@ public function testHandleConsoleCommandEventStartsChildSpanIfSpanIsSetOnHub(?Co
*/
public function handleConsoleCommandEventStartsChildSpanIfSpanIsSetOnHubDataProvider(): Generator
{
yield [
null,
'<unnamed command>',
];

yield [
new Command(),
'<unnamed command>',
Expand All @@ -151,6 +133,19 @@ public function handleConsoleCommandEventStartsChildSpanIfSpanIsSetOnHubDataProv
];
}

public function testHandleConsoleCommandEvent(): void
{
$this->hub->expects($this->never())
->method('getSpan');

$listener = new TracingConsoleListener($this->hub, ['foo:bar']);
$listener->handleConsoleCommandEvent(new ConsoleCommandEvent(
new Command('foo:bar'),
$this->createMock(InputInterface::class),
$this->createMock(OutputInterface::class)
));
}

public function testHandleConsoleTerminateEvent(): void
{
$span = new Span();
Expand All @@ -159,7 +154,8 @@ public function testHandleConsoleTerminateEvent(): void
->method('getSpan')
->willReturn($span);

$this->listener->handleConsoleTerminateEvent(new ConsoleTerminateEvent(
$listener = new TracingConsoleListener($this->hub);
$listener->handleConsoleTerminateEvent(new ConsoleTerminateEvent(
new Command(),
$this->createMock(InputInterface::class),
$this->createMock(OutputInterface::class),
Expand All @@ -175,11 +171,26 @@ public function testHandleConsoleTerminateEventDoesNothingIfNoSpanIsSetOnHub():
->method('getSpan')
->willReturn(null);

$this->listener->handleConsoleTerminateEvent(new ConsoleTerminateEvent(
$listener = new TracingConsoleListener($this->hub);
$listener->handleConsoleTerminateEvent(new ConsoleTerminateEvent(
new Command(),
$this->createMock(InputInterface::class),
$this->createMock(OutputInterface::class),
0
));
}

public function testHandleConsoleTerminateEventDoesNothingIfCommandIsExcluded(): void
{
$this->hub->expects($this->never())
->method('getSpan');

$listener = new TracingConsoleListener($this->hub, ['foo:bar']);
$listener->handleConsoleTerminateEvent(new ConsoleTerminateEvent(
new Command('foo:bar'),
$this->createMock(InputInterface::class),
$this->createMock(OutputInterface::class),
0
));
}
}