Send email when error occurs in console command of Symfony2 app

折月煮酒 提交于 2019-12-07 12:08:27

问题


How is it possible to send email with log when something wrong in console command? Currently I've configured my app to send emails from web interface and it works correctly. Swiftmailer spool is disabled. My config is:

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

When I try to execute php app/console test:exception-command -e prod which throws exception no email sends.


回答1:


I had the same problem, and after a couple of attempts I got a solution. The problem is the spool in swiftmailer, it was configured as memory so I changed it to file

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

Then I called the logger in the command

$this->logger = $this->getContainer()->get('logger');
$this->logger->critical('commands');

And after the command I called the swiftmailer command to send pending emails

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



回答2:


It should send emails with the spool disabled in your swiftmailer config, this should cause emails to be sent out immediately rather than be spooled and your command uses the logger.

If you want to use the memory spool you can flush the queue in your command's execute methods.

/**
 * 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);
}

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

If you are wanting to log Uncaught Exceptions you will have to configure a console event to work with the logger.

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




回答3:


For Symfony < 2.3

There is another possibility, overwrite the Application class the following class could help, this application class activates logger for the console and logs autoexists

//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);
        }

    }

Then use it in app/console

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

At least remove app/cache/* directories app/console cache:clear was not enough



来源:https://stackoverflow.com/questions/19911709/send-email-when-error-occurs-in-console-command-of-symfony2-app

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!