当消息传递失败时,SwiftMailer会默默地忽略错误

时间:2014-11-22 21:13:24

标签: php email symfony smtp swiftmailer

当从内部控制器运行并且通过spool: { type: memory }配置内存中假脱机时,swiftmailer似乎是这样的:

  • 无论何时从控制器mailer->send($message)内调用 - >>将消息保存在内存中
  • 当控制器完成工作并且symfony内核即将关闭时(在事件kernel.terminate或smth中发生故障) - 检查保存在内存中的消息并_实际将它们提交给SMTP服务器

然而,最后一步似乎默默地忽略了在将邮件提交到SMTP服务器时可能引发的任何错误。

我发现当我从Amazon SES设置SMTP并且配置错误时,会无意中吞下错误:

mailer_transport: smtp
# For illustration I put WRONG port 9999, which means that this should trigger
# error (correct port would be 587)
mailer_port: 9999
mailer_encryption: tls
mailer_host: email-smtp.us-east-1.amazonaws.com
mailer_user: SES_USER_KEY
mailer_password: SES_USER_SECRET 

现在,如果我尝试使用symfony命令中的错误配置发送电子邮件,正如预期的那样我得到Swift_TransportException ,并且不会无提示地忽略错误。 (根据我的观察,似乎symfony命令不使用内存假脱机并尝试立即发送消息)

下面是命令的示例(所以你确定我做得对。)

protected function execute(InputInterface $input, OutputInterface $output) {
     $email = $input->getArgument('email');
     $content = $this->getHelper('dialog')->ask($output, 'Please input content:');

     $emsg = \Swift_Message::newInstance();
     $emsg->setTo($email);
     $emsg->setFrom('d@my-ses-verified-domain.com');
     $emsg->setSubject('This is subject');
     $emsg->setBody($content);

     $this->getContainer()->get('mailer')->send($emsg);
}

当抛出异常Swift_TransportException时,此处的命令输出:

ubuntu@localhost:~/my-app$ console acme:email:send existing@email.com
We are going to send email to :existing@email.com

Please input content:asdf adf


[Swift_TransportException]                                                                                    
Connection could not be established with host email-smtp.us-east-1.amazonaws.com [Connection timed out #110]  

Hovewer如果我尝试从控制器发送电子邮件,那么我看不到任何错误消息。基本上这意味着如果出现错误(配置错误或网络错误或SMTP服务器关闭),我发送的所有电子邮件都将默默消失而没有任何跟踪(没有异常抛出,dev.log中没有错误记录在prod.log)。

如何强制Swiftmailer留下未通过递送尝试的痕迹?

1 个答案:

答案 0 :(得分:3)

您可以通过手动刷新假脱机程序来强制发送电子邮件,这样您就可以捕获异常并根据需要进行记录。我在这里的文档中找到了一个示例(针对控制器的上下文进行了修改)......

http://symfony.com/doc/current/cookbook/console/sending_emails.html

$message = new \Swift_Message();

// ... prepare the message

$mailer = $this->get('mailer');

$mailer->send($message);

// now manually flush the queue
$spool = $mailer->getTransport()->getSpool();
$transport = $this->get('swiftmailer.transport.real');

$spool->flushQueue($transport);

虽然该示例最初是在控制台环境中使用的,但我认为没有理由说它在控制器中无效。

修改

记录异常的另一种方法是使用Swiftmailer事件系统。这涉及创建一个自定义插件(作为服务最好),实现\Swift_Events_TransportExceptionListener,然后将其注册到邮件程序。

例如,自定义插件类:

namespace Acme\DemoBundle\SwiftPlugin;

use Symfony\Bridge\Monolog\Logger;

class SwiftExceptionLoggerPlugin implements \Swift_Events_TransportExceptionListener
{
    private $logger;

    public function __construct(Logger $logger)
    {
        $this->logger = $logger;
    }

    /**
     * Invoked as a TransportException is thrown in the Transport system.
     *
     * @param Swift_Events_TransportExceptionEvent $evt
     */
    public function exceptionThrown(\Swift_Events_TransportExceptionEvent $evt)
    {
        $e = $evt->getException();
        $message = $e->getMessage();
        $this->logger->err(sprintf("Swiftmailer Exception: %s", $message));
    }
}

然后将此作为服务与swiftmailer.default.plugin标记一起添加。这将自动将其注册为Swiftmailer插件......

<service id="acme.demo.swift_plugin.swift_exception_logger_plugin" class="Acme\DemoBundle\SwiftPlugin\SwiftExceptionLoggerPlugin">
    <tag name="swiftmailer.default.plugin" />
    <argument type="service" id="logger" />
</service>

这将在dev / prod的标准日志区域中记录异常消息。但是,如果超时需要很长时间才会发生,我认为如果用户点击或关闭其浏览器/选项卡等,它仍然可能无法正确记录。也许以上内容以及通过{{1}为Swiftmailer设置较低的超时值参数。