Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony Messenger: Send logged messenger errors per email (via swift_mailer)?

I've configured monolog to send errors via email as described in the symfony docs here: https://symfony.com/doc/4.3/logging/monolog_email.html

Works well with all errors happing during a request, as well as console command errors. But it does not send emails for errors which occurred during the handling of a messenger message.

Errors are shown when running the consumer bin/console messenger:consume async -vv and they also show up in prod.log like this: [2020-01-10 12:52:38] messenger.CRITICAL: Error thrown while handling message...

Thanks for any hints on how to set up monolog to get messenger errors emailed too.

like image 495
virtualize Avatar asked Nov 07 '22 10:11

virtualize


1 Answers

In fact monolog swift_mailer type use SwiftMailerHandler wish also implements reset interface and use memory spool by default wish keep all emails in buffer until it is destructed, so till the end of request :

  • onKernelTerminate
  • onCliTerminate
  • OR till reset method is called, which means that for messenger worker no emails will be send ever because ther's no instant flush - all of them will be kept in in-memory buffer, and probably lost if the process will be killed.

To solve this, you can just disable the default spool memory setting for swiftmailer. Another solution is to flush your emails after WorkerMessageFailedEvent event gets fired, you can implement an event subscriber to do it for this.

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
use Symfony\Component\Messenger\Event\WorkerMessageHandledEvent;
use Symfony\Contracts\Service\ResetInterface;

/**
 * Class ServiceResetterSubscriber.
 */
class ServiceResetterSubscriber implements EventSubscriberInterface
{

     protected ResetInterface $servicesResetter;
     public function __construct(ResetInterface $servicesResetter)
     {
         $this->servicesResetter = $servicesResetter;
     }
     public function resetServices(): void
     {
         $this->servicesResetter->reset();
     }
     public static function getSubscribedEvents(): array
     {
         return [
             WorkerMessageFailedEvent::class => ['resetServices', 10],
         ];
     }

 }

Register your service with the right argument:

App\EventSubscriber\ServiceResetterSubscriber:
    arguments: ['@services_resetter']

By the way without this (and without buffer limit) your app will leak and no emails will be sent ever.

Another trick: Make sure that your message implements \JsonSerializable to get the message content in your logs, because messenger uses his monolog directly and its context serializer wish use json_encode for seriliazation. That's why we need to customize their JSON representation when encoded is done with json_encode.

like image 71
famas23 Avatar answered Nov 14 '22 21:11

famas23