vendor/sentry/sentry/src/State/Hub.php line 150

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Sentry\State;
  4. use Sentry\Breadcrumb;
  5. use Sentry\CheckIn;
  6. use Sentry\CheckInStatus;
  7. use Sentry\ClientInterface;
  8. use Sentry\Event;
  9. use Sentry\EventHint;
  10. use Sentry\EventId;
  11. use Sentry\Integration\IntegrationInterface;
  12. use Sentry\MonitorConfig;
  13. use Sentry\Severity;
  14. use Sentry\Tracing\SamplingContext;
  15. use Sentry\Tracing\Span;
  16. use Sentry\Tracing\Transaction;
  17. use Sentry\Tracing\TransactionContext;
  18. /**
  19.  * This class is a basic implementation of the {@see HubInterface} interface.
  20.  */
  21. final class Hub implements HubInterface
  22. {
  23.     /**
  24.      * @var Layer[] The stack of client/scope pairs
  25.      */
  26.     private $stack = [];
  27.     /**
  28.      * @var EventId|null The ID of the last captured event
  29.      */
  30.     private $lastEventId;
  31.     /**
  32.      * Hub constructor.
  33.      *
  34.      * @param ClientInterface|null $client The client bound to the hub
  35.      * @param Scope|null           $scope  The scope bound to the hub
  36.      */
  37.     public function __construct(?ClientInterface $client null, ?Scope $scope null)
  38.     {
  39.         $this->stack[] = new Layer($client$scope ?? new Scope());
  40.     }
  41.     /**
  42.      * {@inheritdoc}
  43.      */
  44.     public function getClient(): ?ClientInterface
  45.     {
  46.         return $this->getStackTop()->getClient();
  47.     }
  48.     /**
  49.      * {@inheritdoc}
  50.      */
  51.     public function getLastEventId(): ?EventId
  52.     {
  53.         return $this->lastEventId;
  54.     }
  55.     /**
  56.      * {@inheritdoc}
  57.      */
  58.     public function pushScope(): Scope
  59.     {
  60.         $clonedScope = clone $this->getScope();
  61.         $this->stack[] = new Layer($this->getClient(), $clonedScope);
  62.         return $clonedScope;
  63.     }
  64.     /**
  65.      * {@inheritdoc}
  66.      */
  67.     public function popScope(): bool
  68.     {
  69.         if (=== \count($this->stack)) {
  70.             return false;
  71.         }
  72.         return null !== array_pop($this->stack);
  73.     }
  74.     /**
  75.      * {@inheritdoc}
  76.      */
  77.     public function withScope(callable $callback)
  78.     {
  79.         $scope $this->pushScope();
  80.         try {
  81.             return $callback($scope);
  82.         } finally {
  83.             $this->popScope();
  84.         }
  85.     }
  86.     /**
  87.      * {@inheritdoc}
  88.      */
  89.     public function configureScope(callable $callback): void
  90.     {
  91.         $callback($this->getScope());
  92.     }
  93.     /**
  94.      * {@inheritdoc}
  95.      */
  96.     public function bindClient(ClientInterface $client): void
  97.     {
  98.         $layer $this->getStackTop();
  99.         $layer->setClient($client);
  100.     }
  101.     /**
  102.      * {@inheritdoc}
  103.      */
  104.     public function captureMessage(string $message, ?Severity $level null, ?EventHint $hint null): ?EventId
  105.     {
  106.         $client $this->getClient();
  107.         if (null !== $client) {
  108.             return $this->lastEventId $client->captureMessage($message$level$this->getScope(), $hint);
  109.         }
  110.         return null;
  111.     }
  112.     /**
  113.      * {@inheritdoc}
  114.      */
  115.     public function captureException(\Throwable $exception, ?EventHint $hint null): ?EventId
  116.     {
  117.         $client $this->getClient();
  118.         if (null !== $client) {
  119.             return $this->lastEventId $client->captureException($exception$this->getScope(), $hint);
  120.         }
  121.         return null;
  122.     }
  123.     /**
  124.      * {@inheritdoc}
  125.      */
  126.     public function captureEvent(Event $event, ?EventHint $hint null): ?EventId
  127.     {
  128.         $client $this->getClient();
  129.         if (null !== $client) {
  130.             return $this->lastEventId $client->captureEvent($event$hint$this->getScope());
  131.         }
  132.         return null;
  133.     }
  134.     /**
  135.      * {@inheritdoc}
  136.      */
  137.     public function captureLastError(?EventHint $hint null): ?EventId
  138.     {
  139.         $client $this->getClient();
  140.         if (null !== $client) {
  141.             return $this->lastEventId $client->captureLastError($this->getScope(), $hint);
  142.         }
  143.         return null;
  144.     }
  145.     /**
  146.      * {@inheritdoc}
  147.      *
  148.      * @param int|float|null $duration
  149.      */
  150.     public function captureCheckIn(string $slugCheckInStatus $status$duration null, ?MonitorConfig $monitorConfig null, ?string $checkInId null): ?string
  151.     {
  152.         $client $this->getClient();
  153.         if (null === $client) {
  154.             return null;
  155.         }
  156.         $options $client->getOptions();
  157.         $event Event::createCheckIn();
  158.         $checkIn = new CheckIn(
  159.             $slug,
  160.             $status,
  161.             $checkInId,
  162.             $options->getRelease(),
  163.             $options->getEnvironment(),
  164.             $duration,
  165.             $monitorConfig
  166.         );
  167.         $event->setCheckIn($checkIn);
  168.         $this->captureEvent($event);
  169.         return $checkIn->getId();
  170.     }
  171.     /**
  172.      * {@inheritdoc}
  173.      */
  174.     public function addBreadcrumb(Breadcrumb $breadcrumb): bool
  175.     {
  176.         $client $this->getClient();
  177.         if (null === $client) {
  178.             return false;
  179.         }
  180.         $options $client->getOptions();
  181.         $beforeBreadcrumbCallback $options->getBeforeBreadcrumbCallback();
  182.         $maxBreadcrumbs $options->getMaxBreadcrumbs();
  183.         if ($maxBreadcrumbs <= 0) {
  184.             return false;
  185.         }
  186.         $breadcrumb $beforeBreadcrumbCallback($breadcrumb);
  187.         if (null !== $breadcrumb) {
  188.             $this->getScope()->addBreadcrumb($breadcrumb$maxBreadcrumbs);
  189.         }
  190.         return null !== $breadcrumb;
  191.     }
  192.     /**
  193.      * {@inheritdoc}
  194.      */
  195.     public function getIntegration(string $className): ?IntegrationInterface
  196.     {
  197.         $client $this->getClient();
  198.         if (null !== $client) {
  199.             return $client->getIntegration($className);
  200.         }
  201.         return null;
  202.     }
  203.     /**
  204.      * {@inheritdoc}
  205.      *
  206.      * @param array<string, mixed> $customSamplingContext Additional context that will be passed to the {@see SamplingContext}
  207.      */
  208.     public function startTransaction(TransactionContext $context, array $customSamplingContext = []): Transaction
  209.     {
  210.         $transaction = new Transaction($context$this);
  211.         $client $this->getClient();
  212.         $options null !== $client $client->getOptions() : null;
  213.         if (null === $options || !$options->isTracingEnabled()) {
  214.             $transaction->setSampled(false);
  215.             return $transaction;
  216.         }
  217.         $samplingContext SamplingContext::getDefault($context);
  218.         $samplingContext->setAdditionalContext($customSamplingContext);
  219.         $tracesSampler $options->getTracesSampler();
  220.         if (null === $transaction->getSampled()) {
  221.             if (null !== $tracesSampler) {
  222.                 $sampleRate $tracesSampler($samplingContext);
  223.             } else {
  224.                 $sampleRate $this->getSampleRate(
  225.                     $samplingContext->getParentSampled(),
  226.                     $options->getTracesSampleRate() ?? 0
  227.                 );
  228.             }
  229.             if (!$this->isValidSampleRate($sampleRate)) {
  230.                 $transaction->setSampled(false);
  231.                 return $transaction;
  232.             }
  233.             $transaction->getMetadata()->setSamplingRate($sampleRate);
  234.             if (0.0 === $sampleRate) {
  235.                 $transaction->setSampled(false);
  236.                 return $transaction;
  237.             }
  238.             $transaction->setSampled($this->sample($sampleRate));
  239.         }
  240.         if (!$transaction->getSampled()) {
  241.             return $transaction;
  242.         }
  243.         $transaction->initSpanRecorder();
  244.         $profilesSampleRate $options->getProfilesSampleRate();
  245.         if ($this->sample($profilesSampleRate)) {
  246.             $transaction->initProfiler();
  247.             $profiler $transaction->getProfiler();
  248.             if (null !== $profiler) {
  249.                 $profiler->start();
  250.             }
  251.         }
  252.         return $transaction;
  253.     }
  254.     /**
  255.      * {@inheritdoc}
  256.      */
  257.     public function getTransaction(): ?Transaction
  258.     {
  259.         return $this->getScope()->getTransaction();
  260.     }
  261.     /**
  262.      * {@inheritdoc}
  263.      */
  264.     public function setSpan(?Span $span): HubInterface
  265.     {
  266.         $this->getScope()->setSpan($span);
  267.         return $this;
  268.     }
  269.     /**
  270.      * {@inheritdoc}
  271.      */
  272.     public function getSpan(): ?Span
  273.     {
  274.         return $this->getScope()->getSpan();
  275.     }
  276.     /**
  277.      * Gets the scope bound to the top of the stack.
  278.      */
  279.     private function getScope(): Scope
  280.     {
  281.         return $this->getStackTop()->getScope();
  282.     }
  283.     /**
  284.      * Gets the topmost client/layer pair in the stack.
  285.      */
  286.     private function getStackTop(): Layer
  287.     {
  288.         return $this->stack[\count($this->stack) - 1];
  289.     }
  290.     private function getSampleRate(?bool $hasParentBeenSampledfloat $fallbackSampleRate): float
  291.     {
  292.         if (true === $hasParentBeenSampled) {
  293.             return 1;
  294.         }
  295.         if (false === $hasParentBeenSampled) {
  296.             return 0;
  297.         }
  298.         return $fallbackSampleRate;
  299.     }
  300.     /**
  301.      * @param mixed $sampleRate
  302.      */
  303.     private function sample($sampleRate): bool
  304.     {
  305.         if (0.0 === $sampleRate) {
  306.             return false;
  307.         }
  308.         if (1.0 === $sampleRate) {
  309.             return true;
  310.         }
  311.         return mt_rand(0mt_getrandmax() - 1) / mt_getrandmax() < $sampleRate;
  312.     }
  313.     /**
  314.      * @param mixed $sampleRate
  315.      */
  316.     private function isValidSampleRate($sampleRate): bool
  317.     {
  318.         if (!\is_float($sampleRate) && !\is_int($sampleRate)) {
  319.             return false;
  320.         }
  321.         if ($sampleRate || $sampleRate 1) {
  322.             return false;
  323.         }
  324.         return true;
  325.     }
  326. }