vendor/sentry/sentry/src/Transport/HttpTransport.php line 96

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Sentry\Transport;
  4. use GuzzleHttp\Promise\FulfilledPromise;
  5. use GuzzleHttp\Promise\PromiseInterface;
  6. use GuzzleHttp\Promise\RejectedPromise;
  7. use Http\Client\HttpAsyncClient as HttpAsyncClientInterface;
  8. use Psr\Http\Message\RequestFactoryInterface;
  9. use Psr\Http\Message\ResponseInterface;
  10. use Psr\Http\Message\StreamFactoryInterface;
  11. use Psr\Log\LoggerInterface;
  12. use Psr\Log\NullLogger;
  13. use Sentry\Event;
  14. use Sentry\EventType;
  15. use Sentry\Options;
  16. use Sentry\Response;
  17. use Sentry\ResponseStatus;
  18. use Sentry\Serializer\PayloadSerializerInterface;
  19. /**
  20.  * This transport sends the events using a syncronous HTTP client that will
  21.  * delay sending of the requests until the shutdown of the application.
  22.  *
  23.  * @author Stefano Arlandini <sarlandini@alice.it>
  24.  */
  25. final class HttpTransport implements TransportInterface
  26. {
  27.     /**
  28.      * @var Options The Sentry client options
  29.      */
  30.     private $options;
  31.     /**
  32.      * @var HttpAsyncClientInterface The HTTP client
  33.      */
  34.     private $httpClient;
  35.     /**
  36.      * @var StreamFactoryInterface The PSR-7 stream factory
  37.      */
  38.     private $streamFactory;
  39.     /**
  40.      * @var RequestFactoryInterface The PSR-7 request factory
  41.      */
  42.     private $requestFactory;
  43.     /**
  44.      * @var PayloadSerializerInterface The event serializer
  45.      */
  46.     private $payloadSerializer;
  47.     /**
  48.      * @var LoggerInterface A PSR-3 logger
  49.      */
  50.     private $logger;
  51.     /**
  52.      * @var RateLimiter The rate limiter
  53.      */
  54.     private $rateLimiter;
  55.     /**
  56.      * Constructor.
  57.      *
  58.      * @param Options                    $options           The Sentry client configuration
  59.      * @param HttpAsyncClientInterface   $httpClient        The HTTP client
  60.      * @param StreamFactoryInterface     $streamFactory     The PSR-7 stream factory
  61.      * @param RequestFactoryInterface    $requestFactory    The PSR-7 request factory
  62.      * @param PayloadSerializerInterface $payloadSerializer The event serializer
  63.      * @param LoggerInterface|null       $logger            An instance of a PSR-3 logger
  64.      */
  65.     public function __construct(
  66.         Options $options,
  67.         HttpAsyncClientInterface $httpClient,
  68.         StreamFactoryInterface $streamFactory,
  69.         RequestFactoryInterface $requestFactory,
  70.         PayloadSerializerInterface $payloadSerializer,
  71.         ?LoggerInterface $logger null
  72.     ) {
  73.         $this->options $options;
  74.         $this->httpClient $httpClient;
  75.         $this->streamFactory $streamFactory;
  76.         $this->requestFactory $requestFactory;
  77.         $this->payloadSerializer $payloadSerializer;
  78.         $this->logger $logger ?? new NullLogger();
  79.         $this->rateLimiter = new RateLimiter($this->logger);
  80.     }
  81.     /**
  82.      * {@inheritdoc}
  83.      */
  84.     public function send(Event $event): PromiseInterface
  85.     {
  86.         $dsn $this->options->getDsn();
  87.         if (null === $dsn) {
  88.             throw new \RuntimeException(sprintf('The DSN option must be set to use the "%s" transport.'self::class));
  89.         }
  90.         $eventType $event->getType();
  91.         if ($this->rateLimiter->isRateLimited($eventType)) {
  92.             $this->logger->warning(
  93.                 sprintf('Rate limit exceeded for sending requests of type "%s".', (string) $eventType),
  94.                 ['event' => $event]
  95.             );
  96.             return new RejectedPromise(new Response(ResponseStatus::rateLimit(), $event));
  97.         }
  98.         if (
  99.             EventType::transaction() === $eventType ||
  100.             EventType::checkIn() === $eventType
  101.         ) {
  102.             $request $this->requestFactory->createRequest('POST'$dsn->getEnvelopeApiEndpointUrl())
  103.                 ->withHeader('Content-Type''application/x-sentry-envelope')
  104.                 ->withBody($this->streamFactory->createStream($this->payloadSerializer->serialize($event)));
  105.         } else {
  106.             $request $this->requestFactory->createRequest('POST'$dsn->getStoreApiEndpointUrl())
  107.                 ->withHeader('Content-Type''application/json')
  108.                 ->withBody($this->streamFactory->createStream($this->payloadSerializer->serialize($event)));
  109.         }
  110.         try {
  111.             /** @var ResponseInterface $response */
  112.             $response $this->httpClient->sendAsyncRequest($request)->wait();
  113.         } catch (\Throwable $exception) {
  114.             $this->logger->error(
  115.                 sprintf('Failed to send the event to Sentry. Reason: "%s".'$exception->getMessage()),
  116.                 ['exception' => $exception'event' => $event]
  117.             );
  118.             return new RejectedPromise(new Response(ResponseStatus::failed(), $event));
  119.         }
  120.         $sendResponse $this->rateLimiter->handleResponse($event$response);
  121.         if (ResponseStatus::success() === $sendResponse->getStatus()) {
  122.             return new FulfilledPromise($sendResponse);
  123.         }
  124.         return new RejectedPromise($sendResponse);
  125.     }
  126.     /**
  127.      * {@inheritdoc}
  128.      */
  129.     public function close(?int $timeout null): PromiseInterface
  130.     {
  131.         return new FulfilledPromise(true);
  132.     }
  133. }