Search code examples
symfonymailersubscriber

Symfony: Logging all Mails created by Mailer


For various reasons I want/need to log all emails sent through my website which runs on Symfony 5.

What I have so far is a subscriber that creates an Entity of type EmailLogEntry when a MessageEvent class is created (at least that's what I understand from (MessageEvent::class) - correct me if I'm wrong). I also use this subscriber to fill in missing emailadresses with the default system address.

Now, after sending the email, I'd like to adjust my entity and call $email->setSent(true);, but I can't figure out how to subscribe to the event that tries to send the email. And for the reusability of the code I don't want to do that in the Services (yes, it's multiple since there's multiple sources that generate mails) where I actually call $this->mailer->send($email);.

My questions now are:

  • Can someone tell me how I can subscribe to the Mailers send event?
  • How, in general, do I figure out what events I can subscribe to? The kernel events are listed in the documentation, but what about all the other events that are fired?

Btw, my subscriber code at the moment:

class SendMailSubscriber implements EventSubscriberInterface
    {
        public static function getSubscribedEvents()
        {
            return [
                MessageEvent::class => [
                    ['onMessage', 255],
                    ['logMessage', 0],
                ],
            ];
        }

        public function logMessage(MessageEvent $event) {
            $email = new EmailLogEntry();
            [...]
        }
    }

Thanks.


Solution

  • The answer to my question is: at the moment you can not subscribe to the send() event of Mailer.

    As a workaround, that's my code for now: It's an extract from my Mailer service.

    // send email and log the whole message
    private function logSendEmail($email) {
        $log = new EmailLog();
    
        // sender
        $log->setSender(($email->getFrom()[0]->getName() ?:"")." <".$email->getFrom()[0]->getAddress().">");
    
        // get recipient list
        foreach($email->getTo() AS $to) {
            $recipient[] = "To: ".$to->getName()." <".$to->getAddress().">";
        }
        foreach($email->getCc() AS $cc) {
            $recipient[] = "CC: ".$cc->getName()." <".$cc->getAddress().">";
        }
        foreach($email->getBcc() AS $bcc) {
            $recipient[] = "Bcc: ".$bcc->getName()." <".$bcc->getAddress().">";
        }
        $log->setRecipient(implode(";",$recipient));
    
        // other message data
        $log->setSubject($email->getSubject());
        $log->setMessage(serialize($email->__serialize()));
        $log->setMessageTime(new \DateTime("now"));
        $log->setSent(0); // set Sent to 0 since mail has not been sent yet
    
        // try to send email
        try {
            $this->mailer->send($email);
            $log->setSent(1);   // set sent to 1 if mail was sent successfully
        }
    
    //      catch(Exception $e) {
    //          to be determined
    //      }
    
        // and finally persist entity to database
        finally {
            $this->em->persist($log);
            $this->em->flush();
        }
    }
    

    EmailLog is an Entity I created. There's a slight overhang as I save the sender, recipients and subject seperatly. However, in compliance with consumer data protection, I plan to clear the message field automatically after 30 days while holding on to the other fields for 6 months.

    I'm also not using a subscriber as of now, mostly because website visitors can request a message copy and I'm not interested in logging that as well. Also, since I want to produce reusable code, I might at some point face the problem that mails could contain personal messages and I certainly will not want to log this mails.