vendor/sentry/sentry-symfony/src/EventListener/ExceptionListener.php line 142

Open in your IDE?
  1. <?php
  2. namespace Sentry\SentryBundle\EventListener;
  3. use Sentry\SentryBundle\Event\SentryUserContextEvent;
  4. use Sentry\SentryBundle\SentrySymfonyEvents;
  5. use Symfony\Component\Console\Event\ConsoleCommandEvent;
  6. use Symfony\Component\Console\Event\ConsoleErrorEvent;
  7. use Symfony\Component\Console\Event\ConsoleEvent;
  8. use Symfony\Component\Console\Event\ConsoleExceptionEvent;
  9. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  10. use Symfony\Component\HttpFoundation\Request;
  11. use Symfony\Component\HttpFoundation\RequestStack;
  12. use Symfony\Component\HttpKernel\Event\GetResponseEvent;
  13. use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
  14. use Symfony\Component\HttpKernel\HttpKernelInterface;
  15. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  16. use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
  17. use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter;
  18. use Symfony\Component\Security\Core\User\UserInterface;
  19. /**
  20.  * Class ExceptionListener
  21.  * @package Sentry\SentryBundle\EventListener
  22.  */
  23. class ExceptionListener implements SentryExceptionListenerInterface
  24. {
  25.     /** @var TokenStorageInterface|null */
  26.     protected $tokenStorage;
  27.     /** @var AuthorizationCheckerInterface|null */
  28.     protected $authorizationChecker;
  29.     /** @var \Raven_Client */
  30.     protected $client;
  31.     /** @var EventDispatcherInterface */
  32.     protected $eventDispatcher;
  33.     /** @var RequestStack */
  34.     protected $requestStack;
  35.     /** @var string[] */
  36.     protected $skipCapture;
  37.     /**
  38.      * ExceptionListener constructor.
  39.      * @param \Raven_Client $client
  40.      * @param EventDispatcherInterface $dispatcher
  41.      * @param array $skipCapture
  42.      * @param TokenStorageInterface|null $tokenStorage
  43.      * @param AuthorizationCheckerInterface|null $authorizationChecker
  44.      */
  45.     public function __construct(
  46.         \Raven_Client $client,
  47.         EventDispatcherInterface $dispatcher,
  48.         RequestStack $requestStack,
  49.         array $skipCapture,
  50.         TokenStorageInterface $tokenStorage null,
  51.         AuthorizationCheckerInterface $authorizationChecker null
  52.     ) {
  53.         $this->client $client;
  54.         $this->eventDispatcher $dispatcher;
  55.         $this->requestStack $requestStack;
  56.         $this->skipCapture $skipCapture;
  57.         $this->tokenStorage $tokenStorage;
  58.         $this->authorizationChecker $authorizationChecker;
  59.     }
  60.     /**
  61.      * @param \Raven_Client $client
  62.      */
  63.     public function setClient(\Raven_Client $client)
  64.     {
  65.         $this->client $client;
  66.     }
  67.     /**
  68.      * Set the username from the security context by listening on core.request
  69.      *
  70.      * @param GetResponseEvent $event
  71.      */
  72.     public function onKernelRequest(GetResponseEvent $event): void
  73.     {
  74.         if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
  75.             return;
  76.         }
  77.         if (null === $this->tokenStorage || null === $this->authorizationChecker) {
  78.             return;
  79.         }
  80.         $token $this->tokenStorage->getToken();
  81.         if (null !== $token && $token->isAuthenticated() && $this->authorizationChecker->isGranted(AuthenticatedVoter::IS_AUTHENTICATED_REMEMBERED)) {
  82.             $this->setUserValue($token->getUser());
  83.             $contextEvent = new SentryUserContextEvent($token);
  84.             $this->eventDispatcher->dispatch(SentrySymfonyEvents::SET_USER_CONTEXT$contextEvent);
  85.         }
  86.     }
  87.     /**
  88.      * @param GetResponseForExceptionEvent $event
  89.      */
  90.     public function onKernelException(GetResponseForExceptionEvent $event): void
  91.     {
  92.         $exception $event->getException();
  93.         if ($this->shouldExceptionCaptureBeSkipped($exception)) {
  94.             return;
  95.         }
  96.         $this->eventDispatcher->dispatch(SentrySymfonyEvents::PRE_CAPTURE$event);
  97.         $this->client->captureException($exception$this->getExceptionData());
  98.     }
  99.     /**
  100.      * Additional attributes to pass with this event (see Sentry docs).
  101.      *
  102.      * @return array
  103.      */
  104.     protected function getExceptionData()
  105.     {
  106.         $data = ['tags' => []];
  107.         $request $this->requestStack->getCurrentRequest();
  108.         if ($request instanceof Request) {
  109.             $data['tags']['route'] = $request->attributes->get('_route');
  110.         }
  111.         return $data;
  112.     }
  113.     /**
  114.      * This method only ensures that the client and error handlers are registered at the start of the command
  115.      * execution cycle, and not only on exceptions
  116.      *
  117.      * @param ConsoleCommandEvent $event
  118.      *
  119.      * @return void
  120.      */
  121.     public function onConsoleCommand(ConsoleCommandEvent $event): void
  122.     {
  123.         // only triggers loading of client, does not need to do anything.
  124.     }
  125.     public function onConsoleError(ConsoleErrorEvent $event): void
  126.     {
  127.         $this->handleConsoleError($event);
  128.     }
  129.     public function onConsoleException(ConsoleExceptionEvent $event): void
  130.     {
  131.         $this->handleConsoleError($event);
  132.     }
  133.     /**
  134.      * @param ConsoleExceptionEvent|ConsoleErrorEvent $event
  135.      */
  136.     protected function handleConsoleError(ConsoleEvent $event): void
  137.     {
  138.         $command $event->getCommand();
  139.         switch (true) {
  140.             case $event instanceof ConsoleErrorEvent:
  141.                 $exception $event->getError();
  142.                 break;
  143.             case $event instanceof ConsoleExceptionEvent:
  144.                 $exception $event->getException();
  145.                 break;
  146.             default:
  147.                 throw new \InvalidArgumentException('Event not recognized: ' . \get_class($event));
  148.         }
  149.         if ($this->shouldExceptionCaptureBeSkipped($exception)) {
  150.             return;
  151.         }
  152.         $data = [
  153.             'tags' => [
  154.                 'command' => $command $command->getName() : 'N/A',
  155.                 'status_code' => $event->getExitCode(),
  156.             ],
  157.         ];
  158.         $this->eventDispatcher->dispatch(SentrySymfonyEvents::PRE_CAPTURE$event);
  159.         $this->client->captureException($exception$data);
  160.     }
  161.     protected function shouldExceptionCaptureBeSkipped(\Throwable $exception): bool
  162.     {
  163.         foreach ($this->skipCapture as $className) {
  164.             if ($exception instanceof $className) {
  165.                 return true;
  166.             }
  167.         }
  168.         return false;
  169.     }
  170.     /**
  171.      * Additional user data
  172.      *
  173.      * @return array
  174.      */
  175.     protected function getUserData()
  176.     {
  177.         $data = [];
  178.         $request $this->requestStack->getCurrentRequest();
  179.         if ($request instanceof Request) {
  180.             $data['ip_address'] = $request->getClientIp();
  181.         }
  182.         return $data;
  183.     }
  184.     /**
  185.      * @param UserInterface | object | string $user
  186.      */
  187.     protected function setUserValue($user)
  188.     {
  189.         if ($user instanceof UserInterface) {
  190.             $this->client->set_user_data($user->getUsername(), null$this->getUserData());
  191.             return;
  192.         }
  193.         if (is_string($user)) {
  194.             $this->client->set_user_data($usernull$this->getUserData());
  195.             return;
  196.         }
  197.         if (is_object($user) && method_exists($user'__toString')) {
  198.             $this->client->set_user_data((string)$usernull$this->getUserData());
  199.         }
  200.     }
  201. }