在Symfony2应用程序的控制台命令中发生错误时发送电子邮件

时间:2013-11-11 16:54:04

标签: symfony command-line-interface monolog

当控制台命令出错时,如何用日志发送电子邮件?目前我已将我的应用配置为从网络界面发送电子邮件,它可以正常工作。 Swiftmailer假脱机被禁用。我的配置是:

monolog:
    handlers:
        main:
            type:         fingers_crossed
            action_level: critical
            handler:      grouped
        grouped:
            type:    group
            members: [streamed, buffered]
        streamed:
            type:  stream
            path:  "%kernel.logs_dir%/%kernel.environment%.log"
            level: debug
        buffered:
            type:    buffer
            handler: swift
        swift:
            type:       swift_mailer
            from_email: info@site.com
            to_email:   username@site.com
            subject:    An Error Occurred!
            level:      debug

当我尝试执行抛出异常的php app/console test:exception-command -e prod时,没有电子邮件发送。

3 个答案:

答案 0 :(得分:1)

我遇到了同样的问题,经过几次尝试后我得到了解决方案。 问题是swiftmailer中的假脱机,它被配置为内存,所以我将其更改为文件

# app/config/config.yml
swiftmailer:
    transport: %mailer_transport%
    username:  %mailer_user%
    password:  %mailer_password%
    spool:
        type: file
        path: "%kernel.root_dir%/spool"

然后我在命令

中调用了记录器
$this->logger = $this->getContainer()->get('logger');
$this->logger->critical('commands');

在命令之后我调用swiftmailer命令发送待处理的电子邮件

app/console swiftmailer:spool:send --env=prod

答案 1 :(得分:1)

它应该在您的swiftmailer配置中发送禁用假脱机的电子邮件,这应该会立即发送电子邮件而不是假脱机,并且您的命令使用记录器。

如果要使用内存假脱机,可以在命令的执行方法中刷新队列。

/**
 * force emails to be sent out at the end of execute
 */
protected function execute(InputInterface $input, OutputInterface $output)
{
    $container = $this->getContainer();
    $logger = $container->get('logger');
    $logger->critical('My Critical Error Message');

    $mailer = $container->get('mailer');
    $spool = $mailer->getTransport()->getSpool();
    $transport = $container->get('swiftmailer.transport.real');
    $spool->flushQueue($transport);
}

参考:http://symfony.com/doc/current/cookbook/console/sending_emails.html#using-memory-spooling

如果您想要记录未捕获的例外,则必须配置控制台事件以使用记录器。

http://symfony.com/doc/current/cookbook/console/logging.html#enabling-automatic-exceptions-logging

答案 2 :(得分:0)

对于Symfony< 2.3

还有另一种可能性,覆盖以下类可以帮助的Application类, 此应用程序类激活控制台的记录器并记录自动存在

//Acme/LoggingBundle/Console/Application.php
    <?php

    namespace Acme\LoggingBundle\Console;

    use Symfony\Bundle\FrameworkBundle\Console\Application as BaseApplication;
    use Symfony\Component\Console\Input\InputInterface;
    use Symfony\Component\Console\Output\OutputInterface;
    use Symfony\Component\Console\Output\ConsoleOutputInterface;
    use Symfony\Component\DependencyInjection\IntrospectableContainerInterface;
    use Symfony\Component\HttpKernel\Log\LoggerInterface;
    use Symfony\Component\HttpKernel\KernelInterface;
    use Symfony\Component\Console\Output\ConsoleOutput;
    use Symfony\Component\Console\Input\ArgvInput;

    class Application extends BaseApplication
    {
        private $originalAutoExit;

        public function __construct(KernelInterface $kernel)
        {
            parent::__construct($kernel);
            $this->originalAutoExit = true;
        }

        /**
        * Runs the current application.
        *
        * @param InputInterface  $input  An Input instance
        * @param OutputInterface $output An Output instance
        *
        * @return integer 0 if everything went fine, or an error code
        *
        * @throws \Exception When doRun returns Exception
        *
        * @api
        */
        public function run(InputInterface $input = null, OutputInterface $output = null)
        {
            // make the parent method throw exceptions, so you can log it
            $this->setCatchExceptions(false);

            // store the autoExit value before resetting it - you'll need it later
            $autoExit = $this->originalAutoExit;
            $this->setAutoExit(false);

            if (null === $input) {
                $input = new ArgvInput();
            }

            if (null === $output) {
                $output = new ConsoleOutput();
            }

            try {
                $statusCode = parent::run($input, $output);
            } catch (\Exception $e) {

                /** @var $logger LoggerInterface */
                $container = $this->getKernel()->getContainer();
                $logger = null;

                if($container instanceof IntrospectableContainerInterface){
                    $logger = $container->get('logger');
                }

                $message = sprintf(
                    '%s: %s (uncaught exception) at %s line %s while running console command `%s`',
                    get_class($e),
                    $e->getMessage(),
                    $e->getFile(),
                    $e->getLine(),
                    $this->getCommandName($input)
                );

                if($logger){
                    $logger->crit($message);
                }

                if ($output instanceof ConsoleOutputInterface) {
                    $this->renderException($e, $output->getErrorOutput());
                } else {
                    $this->renderException($e, $output);
                }
                $statusCode = $e->getCode();

                $statusCode = is_numeric($statusCode) && $statusCode ? $statusCode : 1;
            }

            if ($autoExit) {
                if ($statusCode > 255) {
                    $statusCode = 255;
                }

                // log non-0 exit codes along with command name
                if ($statusCode !== 0) {

                    /** @var $logger LoggerInterface */
                    $container = $this->getKernel()->getContainer();
                    $logger = null;

                    if($container instanceof IntrospectableContainerInterface){
                        $logger = $container->get('logger');
                    }

                    if($logger){
                        $logger->warn(sprintf('Command `%s` exited with status code %d', $this->getCommandName($input), $statusCode));
                    }
                }

                // @codeCoverageIgnoreStart
                exit($statusCode);
                // @codeCoverageIgnoreEnd
            }

            return $statusCode;
        }

        public function setAutoExit($bool)
        {
            // parent property is private, so we need to intercept it in a setter
            $this->originalAutoExit = (Boolean) $bool;
            parent::setAutoExit($bool);
        }

    }

然后在app / console中使用它

//use Symfony\Bundle\FrameworkBundle\Console\Application;
use Acme\UltimateLoggingBundle\Console\Application;

至少删除app / cache / *目录app / console cache:clear not enough