当从控制器内部运行时,当通过spool: { type: memory }
swiftmailer 配置内存假脱机时,似乎工作如下:
- 每当从控制器内部调用
mailer->send($message)
时 ->将消息保存在内存中 - 当控制器完成工作并且symfony内核即将关闭时(在事件
kernel.terminate
或smth中somwehere) - 检查保存在内存中的消息并_actually将它们提交给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]
如果我尝试从控制器发送电子邮件,则看不到任何错误消息。基本上这意味着如果出现错误(配置错误或网络错误或SMTP服务器关闭),我发送的所有电子邮件都将无声地消失,没有任何痕迹(没有抛出异常,没有错误记录dev.log
也没有prod.log
)。
如何强制Swiftmailer
留下传递尝试失败的痕迹?
您可以通过手动刷新后台处理程序来强制传递电子邮件,然后您应该可以捕获异常并根据需要进行记录。我在这里的文档中找到了一个示例(针对控制器的上下文进行了修改)......
https://symfony.com/doc/2.6/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 AcmeDemoBundleSwiftPlugin;
use SymfonyBridgeMonologLogger;
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="AcmeDemoBundleSwiftPluginSwiftExceptionLoggerPlugin">
<tag name="swiftmailer.default.plugin" />
<argument type="service" id="logger" />
</service>
这会在 dev/prod 的标准日志区域中记录异常消息。但是,如果超时需要很长时间才能发生,我认为如果用户单击离开或关闭浏览器/选项卡等,它仍然可能无法正确登录。也许以上以及通过 mailer_timeout
参数为 Swiftmailer 设置较低的超时值。