<?php
declare(strict_types=1);
namespace Sentry\SentryBundle\EventListener;
use Sentry\State\HubInterface;
use Sentry\Tracing\Span;
use Sentry\Tracing\SpanContext;
use Sentry\Tracing\Transaction;
use Sentry\Tracing\TransactionContext;
use Sentry\Tracing\TransactionSource;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
/**
* This listener either starts a {@see Transaction} or a child {@see Span} when
* a console command is executed to allow measuring the application performances.
*/
final class TracingConsoleListener
{
/**
* @var HubInterface The current hub
*/
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 string[] $excludedCommands The list of commands for which distributed tracing must be skipped
*/
public function __construct(HubInterface $hub, array $excludedCommands = [])
{
$this->hub = $hub;
$this->excludedCommands = $excludedCommands;
}
/**
* Handles the execution of a console command by starting a new {@see Transaction}
* if it doesn't exists, or a child {@see Span} if it does.
*
* @param ConsoleCommandEvent $event The event
*/
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($command));
$transactionContext->setSource(TransactionSource::task());
$span = $this->hub->startTransaction($transactionContext);
} else {
$spanContext = new SpanContext();
$spanContext->setOp('console.command');
$spanContext->setDescription($this->getSpanName($command));
$span = $currentSpan->startChild($spanContext);
}
$this->hub->setSpan($span);
}
/**
* Handles the termination of a console command by stopping the active {@see Span}
* or {@see Transaction}.
*
* @param ConsoleTerminateEvent $event The event
*/
public function handleConsoleTerminateEvent(ConsoleTerminateEvent $event): void
{
if ($this->isCommandExcluded($event->getCommand())) {
return;
}
$span = $this->hub->getSpan();
if (null !== $span) {
$span->finish();
}
}
private function getSpanName(?Command $command): string
{
if (null === $command || null === $command->getName()) {
return '<unnamed command>';
}
return $command->getName();
}
private function isCommandExcluded(?Command $command): bool
{
if (null === $command) {
return true;
}
return \in_array($command->getName(), $this->excludedCommands, true);
}
}