vendor/bugsnag/bugsnag-symfony/EventListener/BugsnagListener.php line 98

Open in your IDE?
  1. <?php
  2. namespace Bugsnag\BugsnagBundle\EventListener;
  3. use Bugsnag\BugsnagBundle\Request\SymfonyResolver;
  4. use Bugsnag\Client;
  5. use Bugsnag\Report;
  6. use InvalidArgumentException;
  7. use Symfony\Component\Console\ConsoleEvents;
  8. use Symfony\Component\Console\Event\ConsoleErrorEvent;
  9. use Symfony\Component\Console\Event\ConsoleExceptionEvent;
  10. use Symfony\Component\Debug\Exception\OutOfMemoryException;
  11. use Symfony\Component\ErrorHandler\Error\OutOfMemoryError;
  12. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  13. use Symfony\Component\HttpKernel\Event\ExceptionEvent;
  14. use Symfony\Component\HttpKernel\Event\GetResponseEvent;
  15. use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
  16. use Symfony\Component\HttpKernel\Event\RequestEvent;
  17. use Symfony\Component\HttpKernel\HttpKernelInterface;
  18. use Symfony\Component\HttpKernel\KernelEvents;
  19. class BugsnagListener implements EventSubscriberInterface
  20. {
  21.     /**
  22.      * The bugsnag client instance.
  23.      *
  24.      * @var \Bugsnag\Client
  25.      */
  26.     protected $client;
  27.     /**
  28.      * The request resolver instance.
  29.      *
  30.      * @var \Bugsnag\BugsnagBundle\Request\SymfonyResolver
  31.      */
  32.     protected $resolver;
  33.     /**
  34.      * If auto notifying is enabled.
  35.      *
  36.      * @var bool
  37.      */
  38.     protected $auto;
  39.     /**
  40.      * A regex that matches Symfony's OOM errors.
  41.      *
  42.      * @var string
  43.      */
  44.     private $oomRegex '/Allowed memory size of (\d+) bytes exhausted \(tried to allocate \d+ bytes\)/';
  45.     /**
  46.      * Create a new bugsnag listener instance.
  47.      *
  48.      * @param \Bugsnag\Client                                $client
  49.      * @param \Bugsnag\BugsnagBundle\Request\SymfonyResolver $resolver
  50.      * @param bool                                           $auto
  51.      *
  52.      * @return void
  53.      */
  54.     public function __construct(Client $clientSymfonyResolver $resolver$auto)
  55.     {
  56.         $this->client $client;
  57.         $this->resolver $resolver;
  58.         $this->auto $auto;
  59.     }
  60.     /**
  61.      * Handle an incoming request.
  62.      *
  63.      * @param GetResponseEvent|RequestEvent $event
  64.      *
  65.      * @return void
  66.      */
  67.     public function onKernelRequest($event)
  68.     {
  69.         // Compatibility with Symfony < 5 and Symfony >=5
  70.         if (!$event instanceof GetResponseEvent && !$event instanceof RequestEvent) {
  71.             throw new InvalidArgumentException('onKernelRequest function only accepts GetResponseEvent and RequestEvent arguments');
  72.         }
  73.         if ($event->getRequestType() !== HttpKernelInterface::MASTER_REQUEST) {
  74.             return;
  75.         }
  76.         $this->client->setFallbackType('HTTP');
  77.         $this->resolver->set($event->getRequest());
  78.     }
  79.     /**
  80.      * Handle an http kernel exception.
  81.      *
  82.      * @param GetResponseForExceptionEvent|ExceptionEvent $event
  83.      *
  84.      * @return void
  85.      */
  86.     public function onKernelException($event)
  87.     {
  88.         $throwable $this->resolveThrowable($event);
  89.         if ($this->isOom($throwable)
  90.             && $this->client->getMemoryLimitIncrease() !== null
  91.             && preg_match($this->oomRegex$throwable->getMessage(), $matches) === 1
  92.         ) {
  93.             $currentMemoryLimit = (int) $matches[1];
  94.             ini_set('memory_limit'$currentMemoryLimit $this->client->getMemoryLimitIncrease());
  95.         }
  96.         $this->sendNotify($throwable, []);
  97.     }
  98.     /**
  99.      * Handle a console exception (used instead of ConsoleErrorEvent before
  100.      * Symfony 3.3 and kept for backwards compatibility).
  101.      *
  102.      * @param \Symfony\Component\Console\Event\ConsoleExceptionEvent $event
  103.      *
  104.      * @return void
  105.      */
  106.     public function onConsoleException(ConsoleExceptionEvent $event)
  107.     {
  108.         $meta = ['status' => $event->getExitCode()];
  109.         if ($event->getCommand()) {
  110.             $meta['name'] = $event->getCommand()->getName();
  111.         }
  112.         $this->sendNotify($event->getException(), ['command' => $meta]);
  113.     }
  114.     /**
  115.      * Handle a console error.
  116.      *
  117.      * @param \Symfony\Component\Console\Event\ConsoleErrorEvent $event
  118.      *
  119.      * @return void
  120.      */
  121.     public function onConsoleError(ConsoleErrorEvent $event)
  122.     {
  123.         $meta = ['status' => $event->getExitCode()];
  124.         if ($event->getCommand()) {
  125.             $meta['name'] = $event->getCommand()->getName();
  126.         }
  127.         $this->sendNotify($event->getError(), ['command' => $meta]);
  128.     }
  129.     /**
  130.      * @param \Throwable $throwable
  131.      * @param array      $meta
  132.      *
  133.      * @return void
  134.      */
  135.     private function sendNotify($throwable$meta)
  136.     {
  137.         if (!$this->auto) {
  138.             return;
  139.         }
  140.         $report Report::fromPHPThrowable(
  141.             $this->client->getConfig(),
  142.             $throwable
  143.         );
  144.         $report->setUnhandled(true);
  145.         $report->setSeverityReason([
  146.             'type' => 'unhandledExceptionMiddleware',
  147.             'attributes' => [
  148.                 'framework' => 'Symfony',
  149.             ],
  150.         ]);
  151.         $report->setMetaData($meta);
  152.         $this->client->notify($report);
  153.     }
  154.     /**
  155.      * @param GetResponseForExceptionEvent|ExceptionEvent $event
  156.      *
  157.      * @return \Throwable
  158.      */
  159.     private function resolveThrowable($event)
  160.     {
  161.         // Compatibility with Symfony < 5 and Symfony >=5
  162.         // The additional `method_exists` check is to prevent errors in Symfony 4.3
  163.         // where the ExceptionEvent exists and is used but doesn't implement
  164.         // the `getThrowable` method, which was introduced in Symfony 4.4
  165.         if ($event instanceof ExceptionEvent && method_exists($event'getThrowable')) {
  166.             return $event->getThrowable();
  167.         }
  168.         if ($event instanceof GetResponseForExceptionEvent) {
  169.             return $event->getException();
  170.         }
  171.         throw new InvalidArgumentException('onKernelException function only accepts GetResponseForExceptionEvent and ExceptionEvent arguments');
  172.     }
  173.     /**
  174.      * Check if this $throwable is an OOM.
  175.      *
  176.      * This will be represented by an "OutOfMemoryError" on Symfony 4.4+ or an
  177.      * "OutOfMemoryException" on earlier versions.
  178.      *
  179.      * @param \Throwable $throwable
  180.      *
  181.      * @return bool
  182.      */
  183.     private function isOom($throwable)
  184.     {
  185.         return $throwable instanceof OutOfMemoryError
  186.             || $throwable instanceof OutOfMemoryException;
  187.     }
  188.     public static function getSubscribedEvents()
  189.     {
  190.         $listeners = [
  191.             KernelEvents::REQUEST => ['onKernelRequest'256],
  192.             KernelEvents::EXCEPTION => ['onKernelException'128],
  193.         ];
  194.         // Added ConsoleEvents in Symfony 2.3
  195.         if (class_exists(ConsoleEvents::class)) {
  196.             // Added with ConsoleEvents::ERROR in Symfony 3.3 to deprecate ConsoleEvents::EXCEPTION
  197.             if (class_exists(ConsoleErrorEvent::class)) {
  198.                 $listeners[ConsoleEvents::ERROR] = ['onConsoleError'128];
  199.             } else {
  200.                 $listeners[ConsoleEvents::EXCEPTION] = ['onConsoleException'128];
  201.             }
  202.         }
  203.         return $listeners;
  204.     }
  205. }