Php 当邮件传递失败时,SwiftMailer会自动忽略错误

Php 当邮件传递失败时,SwiftMailer会自动忽略错误,php,email,symfony,smtp,swiftmailer,Php,Email,Symfony,Smtp,Swiftmailer,当从控制器内部运行以及通过spool:{type:memory}swiftmailer配置内存假脱机时,swiftmailer的工作方式如下: 每当从控制器内调用mailer->send($message)时->将消息保存在内存中 当控制器完成工作并且symfony内核即将关闭时(在事件内核中出现somwehere.terminate或smth)-检查保存在内存中的消息,并实际将它们提交到SMTP服务器 然而,这最后一步似乎悄悄地忽略了在向SMTP服务器提交邮件时可能抛出的任何错误 我发现,

当从控制器内部运行以及通过
spool:{type:memory}
swiftmailer配置内存假脱机时,swiftmailer的工作方式如下:

  • 每当从控制器内调用
    mailer->send($message)
    时->将消息保存在内存中
  • 当控制器完成工作并且symfony内核即将关闭时(在事件
    内核中出现somwehere.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\u TransportException
,并且错误不会被自动忽略。(根据我的观察,symfony命令似乎不使用内存假脱机,而是尝试立即发送消息)

下面是命令的示例(所以您肯定我做得对)

下面是引发异常
Swift\u 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留下失败传递尝试的痕迹?

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

虽然这个示例最初是在控制台环境中使用的,但我看不出为什么它在控制器中无效

编辑:

记录异常的另一种方法是利用Swiftmailer事件系统。这涉及到创建一个实现
\Swift\u Events\u 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的标准日志区域中记录异常消息。但是,如果超时需要很长时间,我认为如果用户不点击或关闭浏览器/选项卡,可能仍然无法正确记录,等等。可能是上面的方法,以及通过
mailer\u timeout
参数为Swiftmailer设置一个较低的超时值。

我刚刚尝试过,该方法在控制器中确实有效,正如预期的那样,它将在尝试传递失败时引发异常。因此,它解决了我一半的问题,但引入了恼人的“副作用”:控制器阻止传递,在最坏的情况下:当SMTP服务器关闭时,这将阻止30秒。。。让用户放弃我的应用程序。“内存中”假脱机允许控制器在尝试“长时间运行”传递之前返回页面。。。我想知道是否还有任何方法可以将这两者结合起来:延迟交付和日志中仍然有错误。@DimitryK我已经用另一种方法更新了我的答案,记录异常,这可能会更好一些。宾果!添加Swift异常侦听器可拦截
Swift\u TransportException
以下是我的
dev.log
[2014-12-02 09:02:15]应用程序。错误:Swiftmailer异常:无法与主机建立连接无效\u email-smtp.us-east-1.amazonaws.com[php_network_getaddresses:getaddrinfo失败:名称或服务未知#0][[]]
。首先,我用
swiftmailer.default_plugin
错误地标记了我的
swiftmailer.default.plugin
,而不是用正确的标记
swiftmailer.default.plugin
(参见.dot?)我还检查了配置错误的端口(使用465,它在SES上是打开的,但会超时)是否会触发超时异常。果然如此:
[2014-12-02 09:19:56]app.ERROR:Swiftmailer异常:连接到tcp://email-smtp.us-east-1.amazonaws.com:465 超时[][]
。我的用户立即收到页面的HTML内容,但PHP脚本在服务器上继续运行10秒,等待超时,然后抛出异常并记录。
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));
    }
}
<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>