app/Service/EventSubscriber/ApmErrorSubscriber.php line 45

Open in your IDE?
  1. <?php
  2. namespace Sq\Service\EventSubscriber;
  3. use Sq\Entity\Apm\WarningError;
  4. use Sq\Event\Kernel\InitialiseContainerEvent;
  5. use Sq\Exception\Controller\AdminAccessDeniedException;
  6. use Sq\Exception\Controller\MemberAccessDeniedException;
  7. use Sq\Exception\Controller\MFARequiredException;
  8. use Sq\Exception\Controller\MFAVerificationException;
  9. use Sq\Service\Apm\ApmInterface;
  10. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  11. use Symfony\Component\HttpKernel\Event\ExceptionEvent;
  12. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  13. use Symfony\Component\HttpKernel\KernelEvents;
  14. class ApmErrorSubscriber implements EventSubscriberInterface
  15. {
  16.     private ApmInterface $apm;
  17.     protected array $ignoredExceptionClasses = [
  18.         MFARequiredException::class,
  19.         MFAVerificationException::class,
  20.         AdminAccessDeniedException::class,
  21.         MemberAccessDeniedException::class,
  22.         NotFoundHttpException::class
  23.     ];
  24.     protected array $trackedErrors = [];
  25.     public function __construct(ApmInterface $apm)
  26.     {
  27.         $this->apm $apm;
  28.     }
  29.     /** @inheritDoc */
  30.     public static function getSubscribedEvents()
  31.     {
  32.         return [
  33.             KernelEvents::EXCEPTION => ['onKernelException'],
  34.             InitialiseContainerEvent::class => ['onInitializeContainer'],
  35.         ];
  36.     }
  37.     public function onInitializeContainer()
  38.     {
  39.         if (!$this->apm->isTracking())
  40.         {
  41.             return;
  42.         }
  43.         $this->registerErrorHandler();
  44.         $this->registerExceptionHandler();
  45.     }
  46.     public function onKernelException(ExceptionEvent $event)
  47.     {
  48.         if (!$this->apm->isTracking())
  49.         {
  50.             return;
  51.         }
  52.         $this->trackApmError($event->getThrowable());
  53.     }
  54.     protected function registerErrorHandler()
  55.     {
  56.         // TODO: Remove $error_context once we migrate to PHP 8
  57.         $prevErrorHandler \set_error_handler(function ($phpErrorLevel$message$filename$line$errorContext = []) use (&$prevErrorHandler)
  58.         {
  59.             switch ($phpErrorLevel)
  60.             {
  61.                 case E_WARNING:
  62.                 case E_USER_WARNING:
  63.                     $this->trackApmError(new WarningError($message0$phpErrorLevel$filename$line));
  64.             }
  65.             return $prevErrorHandler $prevErrorHandler($phpErrorLevel$message$filename$line) : null;
  66.         }, E_ALL);
  67.     }
  68.     protected function registerExceptionHandler()
  69.     {
  70.         $prevExceptionHandler \set_exception_handler(function ($exception) use (&$prevExceptionHandler): void
  71.         {
  72.             $this->trackApmError($exception);
  73.             if ($prevExceptionHandler)
  74.             {
  75.                 $prevExceptionHandler($exception);
  76.             }
  77.         });
  78.     }
  79.     protected function trackApmError(\Throwable $throwable)
  80.     {
  81.         if (
  82.             in_array(get_class($throwable), $this->ignoredExceptionClasses) ||
  83.             isset($this->trackedErrors[spl_object_hash($throwable)])
  84.         ) {
  85.             return;
  86.         }
  87.         $this->trackedErrors[spl_object_hash($throwable)] = $throwable;
  88.         $this->apm->noticeErrorFromThrowable($throwable);
  89.     }
  90. }