Reputation: 2356
When run from inside controller and when in-memory spooling is configured via spool: { type: memory }
swiftmailer seems to work like this:
mailer->send($message)
is called -> save message in memorykernel.terminate
or smth) - check messages saved in memory and _actually submit them to SMTP-serverHowever this last step seems to silently ignore any errors which may be thrown when peforming submitting of the message to the SMTP server.
I discovered that errors are silently swallowed when I was setting up SMTP from Amazon SES, and have made wrong configuration:
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
Now, if I attempt to send email using wrong configuration from a symfony Command, just as expected I get Swift_TransportException
and error is NOT silently ignored. (From my observations it seems that symfony commands do NOT use memory-spooling and attempt to send messages immediately)
Below is sample of the command (so you're sure I am doing it right).
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('[email protected]');
$emsg->setSubject('This is subject');
$emsg->setBody($content);
$this->getContainer()->get('mailer')->send($emsg);
}
And here's command output when the exception Swift_TransportException
is thrown:
ubuntu@localhost:~/my-app$ console acme:email:send [email protected]
We are going to send email to :[email protected]
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 if I attempt to send email from controller, then I see no error messages. Basically this means that in case there's an error (misconfiguration or network error or SMTP server down), all the emails I sent will silently disappear without any trace (no exception thrown, no error loggged in dev.log
neither in prod.log
).
How can I force Swiftmailer
to leave trace of failed delivery attempt?
Upvotes: 2
Views: 3608
Reputation: 2869
You might be able to force the delivery of the emails by flushing the spooler manually, which should then allow you to catch the exception and log as needed. I found an example in the docs here (modified for the context of a controller)...
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);
While the example was originally for use in the console environment, I see no reason why it wouldn't be valid in a controller.
Edit:
Another way to log the exception would be to utilize the Swiftmailer event system. This involves creating a custom plugin (as a service would be best) that implements \Swift_Events_TransportExceptionListener
, then registering it with the mailer.
For example, the custom plugin class:
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));
}
}
Then add this as a service along with the swiftmailer.default.plugin
tag. That will automatically register it as a Swiftmailer plugin...
<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>
This will log the exception message in the standard log area for dev/prod. However, if the timeout takes very long to occur I think it still may not log correctly if the user clicks away or closes their browser/tab, etc. Perhaps the above along with setting a lower timeout value for Swiftmailer via the mailer_timeout
parameter.
Upvotes: 3