在symfony2中记录swiftmailer send()活动


Logging swiftmailer send() activity in symfony2

我使用swiftmailer从我的symfony2.2项目发送邮件。有没有一种方法可以在全球范围内记录所有电子邮件信息并发送结果?

如果mailer-send()方法有触发somÉ事件,那就太好了,但我看不出它有。

这个问题已经回答了,这个解决方案更适合Symfony 4与Monolog的结合。这是基于他的回答。但是没有自定义文件记录器的开销。

注意:日志将放置在中/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;
        }
    }
}

服务.yaml

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

如果你想让邮件日志在另一个频道添加这个:

dev/nolog.yaml(可选)

monolog:
    handlers:
        mailer:
            level:    debug
            type:     stream
            path:     '%kernel.logs_dir%/mailer.%kernel.environment%.log'
            channels: [mailer]

服务:

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));
    }
}

配置:

services:
    umpirsky.mailer.message_file_logger:
        class: MessageFileLogger
        arguments:
            - %kernel.logs_dir%/mailer.log
        tags:
            - { name: swiftmailer.plugin }

我是这样做的:

1.我的服务配置

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

2.邮件包装服务

它扩展了Swift_Mailer,因为它被传递到不同的类,期望mailer是Swift_Mailer的实例。它将Swift_Mailer实例创建为字段,因为。。。$transport'Swith_Mailer(链路)中的private。如果$transportprotected,代码会更好。。。

// /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.束生成器

// /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.和OverrideServiceSwiftMailer

// /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 */
    }
}

将以下内容添加到配置的"services"部分将打印与stdout传输的交互(如果您使用控制台命令进行调试,例如"swiftmailer:email:send"或"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]

示例输出,使用SMTP传输到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

您可以使用自己的自定义mailer类包装SwiftMailer。比如

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
    }
}