Регистрация активности send() swiftmailer в symfony2

Я использую swiftmailer для отправки писем из моего проекта symfony2.2. Есть ли способ глобально регистрировать всю информацию об электронной почте и отправлять результаты?

Было бы здорово, если бы метод рассылки send() вызывал какое-то событие, но я не вижу, чтобы это происходило.


person tiriana    schedule 03.08.2013    source источник


Ответы (5)


На этот вопрос уже был дан ответ. Это решение лучше для Symfony 4 в сочетании с Monolog. Он основан на умпирском его ответе. Но без накладных расходов на собственный файловый регистратор.

Примечание. Журналы будут помещены в ./var/logs/...

Приложение\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" }

Если вы хотите, чтобы логи почтовой программы находились в другом канале, добавьте это:

dev/monolog.yaml (необязательно)

monolog:
    handlers:
        mailer:
            level:    debug
            type:     stream
            path:     '%kernel.logs_dir%/mailer.%kernel.environment%.log'
            channels: [mailer]
person Wesley Abbenhuis    schedule 22.03.2016
comment
Большое спасибо за это - это было действительно полезно! - person Patrick; 25.06.2018
comment
отличный ответ! Отлично работает и с Symfony 3 - person Roubi; 14.10.2019

Обслуживание:

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 }
person umpirsky    schedule 16.04.2014
comment
Так должно быть. - person Eduardo Casas; 20.01.2016

Я сделал это следующим образом:

1. Моя конфигурация службы

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

2. Сервис Mailer-обертки

Он расширяет Swift_Mailer, потому что он передается другим классам, ожидая, что почтовая программа будет экземпляром Swift_Mailer. И он создает экземпляр Swift_Mailer как поле, потому что... $transport это private в \Swith_Mailer (ссылка). Код был бы намного лучше, если бы $transport было 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. Конструктор пакетов

// /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 */
    }
}
person tiriana    schedule 08.08.2013

Добавление следующего в раздел «services» вашей конфигурации выведет взаимодействие с транспортом на стандартный вывод (что может быть полезно, если вы отлаживаете с помощью консольных команд, например «swiftmailer:email:send» или «swiftmailer:spool: Отправить'):

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 на локальный хост:

$ app/console swiftmailer:email:send --subject="Test" --body="Yo! :)" --from="[email protected]" --to="[email protected]"

++ 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: <[email protected]>

<< 250 OK

>> RCPT TO: <[email protected]>

<< 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
person Martin Lie    schedule 07.01.2016
comment
не хватает одной мелочи - если у вас более 1 почтовой программы (или имя почтовой программы не по умолчанию) tags: [{ name: 'swiftmailer.%mailer_name%.plugin' }]. Также вы должны добавить logging: true в конфигурацию почтовика - person klipach; 23.07.2019

Вы можете обернуть 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
    }
}
person Czar Pino    schedule 08.08.2013
comment
Спасибо за повтор. Это нормально, но мне нужно изменить все вызовы mailer -> send() в моем коде. Это то, чего я хочу избежать. Было бы идеально, если бы я мог перезаписать службу почтовой программы и... передать ей ранее определенную службу почтовой программы :) - В настоящее время я пытаюсь найти место, где определяется служба "почтовой программы", поэтому я теперь знаю, какие параметры и аргументы он получает, но я просто не могу его найти... - person tiriana; 08.08.2013