Logging swiftmailer send() activity in symfony2

前端 未结 5 1134
轻奢々
轻奢々 2020-12-16 14:53

Im using swiftmailer for sending mails from my symfony2.2 project. Is there a way to log globally all email info and send results?

It would be great if mailer send

相关标签:
5条回答
  • 2020-12-16 15:13

    Service:

    class MessageFileLogger implements Swift_Events_SendListener
    {
        private $filename;
    
        public function __construct($filename)
        {
            $this->filename = $filename;
        }
    
        public function getMessages()
        {
            return $this->read();
        }
    
        public function clear()
        {
            $this->write(array());
        }
    
        public function beforeSendPerformed(Swift_Events_SendEvent $evt)
        {
            $messages = $this->read();
            $messages[] = clone $evt->getMessage();
    
            $this->write($messages);
        }
    
        public function sendPerformed(Swift_Events_SendEvent $evt)
        {
        }
    
        private function read()
        {
            if (!file_exists($this->filename)) {
                return array();
            }
    
            return (array) unserialize(file_get_contents($this->filename));
        }
    
        private function write(array $messages)
        {
            file_put_contents($this->filename, serialize($messages));
        }
    }
    

    Config:

    services:
        umpirsky.mailer.message_file_logger:
            class: MessageFileLogger
            arguments:
                - %kernel.logs_dir%/mailer.log
            tags:
                - { name: swiftmailer.plugin }
    
    0 讨论(0)
  • 2020-12-16 15:24

    Adding the following to the 'services' section of your configuration will print interaction with the transport to stdout (which can be useful if you're debugging by using the console commands, e.g. 'swiftmailer:email:send' or 'swiftmailer:spool:send'):

    services:
    
        # (...)
    
        swiftmailer.plugins.loggerplugin:
            class: 'Swift_Plugins_LoggerPlugin'
            arguments: ['@swiftmailer.plugins.loggerplugin.logger']
            tags: [{ name: 'swiftmailer.default.plugin' }]
    
        swiftmailer.plugins.loggerplugin.logger:
            class: 'Swift_Plugins_Loggers_EchoLogger'
            arguments: [false]
    

    Example output, using SMTP transport to localhost:

    $ app/console swiftmailer:email:send --subject="Test" --body="Yo! :)" --from="user@example.com" --to="user@example.com"
    
    ++ Starting Swift_Transport_EsmtpTransport
    << 220 example.com ESMTP Exim 4.86 Thu, 07 Jan 2016 13:57:43 +0000
    
    >> EHLO [127.0.0.1]
    
    << 250-example.com Hello localhost [127.0.0.1]
    250-SIZE 52428800
    250-8BITMIME
    250-PIPELINING
    250-AUTH PLAIN LOGIN
    250-STARTTLS
    250 HELP
    
    ++ Swift_Transport_EsmtpTransport started
    >> MAIL FROM: <user@example.com>
    
    << 250 OK
    
    >> RCPT TO: <user@example.com>
    
    << 451 Temporary local problem - please try later
    
    !! Expected response code 250/251/252 but got code "451", with message "451 Temporary local problem - please try later"
    >> RSET
    
    << 250 Reset OK
    
    Sent 0 emails
    
    ++ Stopping Swift_Transport_EsmtpTransport
    >> QUIT
    
    << 221 example.com closing connection
    
    ++ Swift_Transport_EsmtpTransport stopped
    
    0 讨论(0)
  • 2020-12-16 15:27

    You can wrap SwiftMailer with your own custom mailer class. Like,

    class MyMailer
    {
        /**
         * @var \Swift_Mailer
         */
        private $mailer;
    
        /**
         * Mail the specified mailable using swift mailer.
         * 
         * @param SwiftMessage $swiftMessage
         */
        public function mail(\SwiftMessage $swiftMessage)
        {
            // PRESEND ACTIONS
    
            $sent = $this->mailer->send($swiftMessage);
    
            // POST SEND ACTIONS
        }
    }
    
    0 讨论(0)
  • 2020-12-16 15:29

    I did it this way:

    1. My service configuration

    # /src/Tiriana/MyBundle/Resources/config/services.yml
    parameters:
         swiftmailer.class:  Tiriana\MyBundle\Util\MailerWrapper
    

    2. Mailer wraper service

    It extends Swift_Mailer, because it is passed to different classes expecting mailer to be instance of Swift_Mailer. And it creates Swift_Mailer instance as a field, because... $transport is private in \Swith_Mailer (link). Code would be so much better if $transport was protected...

    // /src/Tiriana/MyBundle/Util/MailerWrapper.php
    namespace Tiriana\MyBundle\Util;
    
    use Monolog\Logger;
    use Monolog\Handler\StreamHandler;
    
    class MailerWrapper extends \Swift_Mailer
    {
        private $_logger;
    
        /** @var \Swift_Mailer */
        private $_mailer;
    
        public function send(\Swift_Mime_Message $message, &$failedRecipients = null)
        {
            $this->_log('BEFORE SEND');  // <-- add your logic here
            $ret = $this->_mailer->send($message, $failedRecipients);
            $this->_log('AFTER SEND');  // <-- add your logic here
    
            return $ret;
        }
    
        /** @return Logger */
        public function getLogger()
        {
            return $this->_logger;
        }
    
        protected function _log($msg)
        {
            $this->getLogger()->debug(__CLASS__ . ": " . $msg);
        }
    
        public function __construct(\Swift_Transport $transport, Logger $logger)
        {
            /* we need _mailer because _transport is private
               (not protected) in Swift_Mailer, unfortunately... */
            $this->_mailer = parent::newInstance($transport);
            $this->_logger = $logger;
        }
    
        public static function newInstance(\Swift_Transport $transport)
        {
            return new self($transport);
        }
    
        public function getTransport()
        {
            return $this->_mailer->getTransport();
        }
    
        public function registerPlugin(Swift_Events_EventListener $plugin)
        {
            $this->getTransport()->registerPlugin($plugin);
        }
    }
    

    3. Bundle builder

    // /src/Tiriana/MyBundle/TirianaMyBundle.php
    namespace Tiriana\MyBundle;
    
    use Symfony\Component\HttpKernel\Bundle\Bundle;
    use Symfony\Component\DependencyInjection\ContainerBuilder;
    
    use Tiriana\MyBundle\DependencyInjection\Compiler\OverrideServiceSwiftMailer;
    
    class TirianaMyBundle extends Bundle
    {
        public function build(ContainerBuilder $container)
        {
            parent::build($container);
            $container->addCompilerPass(new OverrideServiceSwiftMailer());  // <-- ADD THIS LINE
        }
    }
    

    4. And OverrideServiceSwiftMailer class

    // /src/Tiriana/MyBundle/DependencyInjection/Compiler/OverrideServiceSwiftMailer.php
    namespace Tiriana\MyBundle\DependencyInjection\Compiler;
    use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
    use Symfony\Component\DependencyInjection\ContainerBuilder;
    use Symfony\Component\DependencyInjection\Reference;
    
    class OverrideServiceSwiftMailer implements CompilerPassInterface
    {
        public function process(ContainerBuilder $container)
        {
            /* @var $definition \Symfony\Component\DependencyInjection\DefinitionDecorator */
            $definition = $container->findDefinition('mailer');
            $definition->addArgument(new Reference('logger'));
            /* add more dependencies if you need - i.e. event_dispatcher */
        }
    }
    
    0 讨论(0)
  • 2020-12-16 15:34

    This question was answered already, This solution is better for Symfony 4 combined with Monolog. It is based on umpirsky his Answer. But without the overhead of a custom file logger.

    Note: Logs will be placed in ./var/logs/...

    App\Util\MailLoggerUtil.php

    <?php
    
    namespace App\Util;
    
    use Psr\Log\LoggerInterface;
    use Psr\Log\LogLevel;
    use Swift_Events_SendEvent;
    use Swift_Events_SendListener;
    
    class MailerLoggerUtil implements Swift_Events_SendListener
    {
        protected $logger;
    
        /**
         * MailerLoggerUtil constructor.
         *
         * @param LoggerInterface $logger
         */
        public function __construct(LoggerInterface $logger)
        {
            $this->logger = $logger;
        }
    
        /**
         * @param Swift_Events_SendEvent $evt
         */
        public function beforeSendPerformed(Swift_Events_SendEvent $evt)
        : void
        {
            // ...
        }
    
        /**
         * @param Swift_Events_SendEvent $evt
         */
        public function sendPerformed(Swift_Events_SendEvent $evt)
        : void
        {
            $level   = $this->getLogLevel($evt);
            $message = $evt->getMessage();
    
            $this->logger->log(
                $level,
                $message->getSubject().' - '.$message->getId(),
                [
                    'result'  => $evt->getResult(),
                    'subject' => $message->getSubject(),
                    'to'      => $message->getTo(),
                    'cc'      => $message->getCc(),
                    'bcc'     => $message->getBcc(),
                ]
            );
        }
    
        /**
         * @param Swift_Events_SendEvent $evt
         *
         * @return string
         */
        private function getLogLevel(Swift_Events_SendEvent $evt)
        : string
        {
            switch ($evt->getResult()) {
                // Sending has yet to occur
                case Swift_Events_SendEvent::RESULT_PENDING:
                    return LogLevel::DEBUG;
    
                // Email is spooled, ready to be sent
                case Swift_Events_SendEvent::RESULT_SPOOLED:
                    return LogLevel::DEBUG;
    
                // Sending failed
                default:
                case Swift_Events_SendEvent::RESULT_FAILED:
                    return LogLevel::CRITICAL;
    
                // Sending worked, but there were some failures
                case Swift_Events_SendEvent::RESULT_TENTATIVE:
                    return LogLevel::ERROR;
    
                // Sending was successful
                case Swift_Events_SendEvent::RESULT_SUCCESS:
                    return LogLevel::INFO;
            }
        }
    }
    

    services.yaml

    App\Util\MailLoggerUtil:
        arguments: ["@logger"]
        tags:
          - { name: monolog.logger, channel: mailer }
          - { name: "swiftmailer.default.plugin" }
    

    If you want the mailer logs to be in another channel add this:

    dev/monolog.yaml (optional)

    monolog:
        handlers:
            mailer:
                level:    debug
                type:     stream
                path:     '%kernel.logs_dir%/mailer.%kernel.environment%.log'
                channels: [mailer]
    
    0 讨论(0)
提交回复
热议问题