Symfony
sponsored by SensioLabs
Menu
  • About
  • Documentation
  • Screencasts
  • Cloud
  • Certification
  • Community
  • Businesses
  • News
  • Download
  1. Home
  2. Documentation
  3. Cookbook
  4. Console
  5. How to enable logging in Console Commands
  • Documentation
  • Book
  • Reference
  • Bundles
  • Cloud
Search by Algolia

Table of Contents

  • Manually logging from a console Command
  • Enabling automatic Exceptions logging
  • Logging non-0 exit statuses

How to enable logging in Console Commands

Edit this page

Warning: You are browsing the documentation for Symfony 2.2, which is no longer maintained.

Read the updated version of this page for Symfony 6.2 (the current stable version).

How to enable logging in Console Commands

The Console component doesn't provide any logging capabilities out of the box. Normally, you run console commands manually and observe the output, which is why logging is not provided. However, there are cases when you might need logging. For example, if you are running console commands unattended, such as from cron jobs or deployment scripts, it may be easier to use Symfony's logging capabilities instead of configuring other tools to gather console output and process it. This can be especially handful if you already have some existing setup for aggregating and analyzing Symfony logs.

There are basically two logging cases you would need:
  • Manually logging some information from your command;
  • Logging uncaught Exceptions.

Manually logging from a console Command

This one is really simple. When you create a console command within the full framework as described in "How to create a Console Command", your command extends ContainerAwareCommand. This means that you can simply access the standard logger service through the container and use it to do the logging:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// src/Acme/DemoBundle/Command/GreetCommand.php
namespace Acme\DemoBundle\Command;

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpKernel\Log\LoggerInterface;

class GreetCommand extends ContainerAwareCommand
{
    // ...

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        /** @var $logger LoggerInterface */
        $logger = $this->getContainer()->get('logger');

        $name = $input->getArgument('name');
        if ($name) {
            $text = 'Hello '.$name;
        } else {
            $text = 'Hello';
        }

        if ($input->getOption('yell')) {
            $text = strtoupper($text);
            $logger->warn('Yelled: '.$text);
        } else {
            $logger->info('Greeted: '.$text);
        }

        $output->writeln($text);
    }
}

Depending on the environment in which you run your command (and your logging setup), you should see the logged entries in app/logs/dev.log or app/logs/prod.log.

Enabling automatic Exceptions logging

To get your console application to automatically log uncaught exceptions for all of your commands, you'll need to do a little bit more work.

First, create a new sub-class of Application and override its run() method, where exception handling should happen:

Caution

Due to the nature of the core Application class, much of the run method has to be duplicated and even a private property originalAutoExit re-implemented. This serves as an example of what you could do in your code, though there is a high risk that something may break when upgrading to future versions of Symfony.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// src/Acme/DemoBundle/Console/Application.php
namespace Acme\DemoBundle\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\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);

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

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

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

            /** @var $logger LoggerInterface */
            $logger = $this->getKernel()->getContainer()->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)
            );
            $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 ($this->originalAutoExit) {
            if ($statusCode > 255) {
                $statusCode = 255;
            }
            // @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);
    }

}

In the code above, you disable exception catching so the parent run method will throw all exceptions. When an exception is caught, you simply log it by accessing the logger service from the service container and then handle the rest of the logic in the same way that the parent run method does (specifically, since the parent run method will not handle exceptions rendering and status code handling when catchExceptions is set to false, it has to be done in the overridden method).

For the extended Application class to work properly with in console shell mode, you have to do a small trick to intercept the autoExit setter and store the setting in a different property, since the parent property is private.

Now to be able to use your extended Application class you need to adjust the app/console script to use the new class instead of the default:

1
2
3
4
5
6
7
8
// app/console

// ...
// replace the following line:
// use Symfony\Bundle\FrameworkBundle\Console\Application;
use Acme\DemoBundle\Console\Application;

// ...

That's it! Thanks to autoloader, your class will now be used instead of original one.

Logging non-0 exit statuses

The logging capabilities of the console can be further extended by logging non-0 exit statuses. This way you will know if a command had any errors, even if no exceptions were thrown.

In order to do that, you'd have to modify the run() method of your extended Application class in the following way:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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 ($autoExit) {
        if ($statusCode > 255) {
            $statusCode = 255;
        }

        // log non-0 exit codes along with command name
        if ($statusCode !== 0) {
            /** @var $logger LoggerInterface */
            $logger = $this->getKernel()->getContainer()->get('logger');
            $logger->warn(sprintf('Command `%s` exited with status code %d', $this->getCommandName($input), $statusCode));
        }

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

    return $statusCode;
}
This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
We stand with Ukraine.
Version:
Measure & Improve Symfony Code Performance

Measure & Improve Symfony Code Performance

Peruse our complete Symfony & PHP solutions catalog for your web development needs.

Peruse our complete Symfony & PHP solutions catalog for your web development needs.

↓ Our footer now uses the colors of the Ukrainian flag because Symfony stands with the people of Ukraine.

Avatar of Cătălin Dan, a Symfony contributor

Thanks Cătălin Dan (@dancatalin) for being a Symfony contributor

5 commits • 109 lines changed

View all contributors that help us make Symfony

Become a Symfony contributor

Be an active part of the community and contribute ideas, code and bug fixes. Both experts and newcomers are welcome.

Learn how to contribute

Symfony™ is a trademark of Symfony SAS. All rights reserved.

  • What is Symfony?
    • Symfony at a Glance
    • Symfony Components
    • Case Studies
    • Symfony Releases
    • Security Policy
    • Logo & Screenshots
    • Trademark & Licenses
    • symfony1 Legacy
  • Learn Symfony
    • Symfony Docs
    • Symfony Book
    • Reference
    • Bundles
    • Best Practices
    • Training
    • eLearning Platform
    • Certification
  • Screencasts
    • Learn Symfony
    • Learn PHP
    • Learn JavaScript
    • Learn Drupal
    • Learn RESTful APIs
  • Community
    • SymfonyConnect
    • Support
    • How to be Involved
    • Code of Conduct
    • Events & Meetups
    • Projects using Symfony
    • Downloads Stats
    • Contributors
    • Backers
  • Blog
    • Events & Meetups
    • A week of symfony
    • Case studies
    • Cloud
    • Community
    • Conferences
    • Diversity
    • Documentation
    • Living on the edge
    • Releases
    • Security Advisories
    • SymfonyInsight
    • Twig
    • SensioLabs
  • Services
    • SensioLabs services
    • Train developers
    • Manage your project quality
    • Improve your project performance
    • Host Symfony projects
    Deployed on
Follow Symfony
Search by Algolia