Blog Posts

New in Symfony 2.4: Show logs in console

I'm happy to announce a new feature that will be available in Symfony 2.4: You can automatically show your log messages in the console output of commands. This eases giving feedback in your console commands dramatically. Before I show you how to use this feature, let's take a look at what problem it solves.

When you want to output text to the console you usually used the OutputInterface instance that is passed when your command gets executed like in the following example:

protected function execute(InputInterface $input, OutputInterface $output)
{
    $data = $input->getArgument('data');
    $myService = $this->getContainer()->get('my_service');

    if ($output->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) {
        $output->writeln(sprintf('Received data: %s', $data));
    }

    if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
        $output->writeln('Processing data...');
    }

    $returnValue = $myService->process($data, $output);

    if ($returnValue) {
        $output->writeln('<info>Succeeded</info>');
    } else {
        $output->writeln('<error>Failed</error>');
    }
}

But there are two problems:

  • It's cumbersome to print information depending on the verbosity settings (-v, -vv, -vvv) because you need to wrap the calls in conditions.
  • If the service you call also wants to give feedback you would need to pass on the $output as with $myService->process($data, $output). But this is really bad code in terms of separation of concerns and coupling. What if this service has a dependency on another service? Working with the OutputInterface and passing around the instance is not the way to go.

To solve these things I thought it would be much easier to rely on the logger which is highly related. So I added integration between Symfonys Console Component and Symfonys logging library Monolog in PR #8167. It features a new handler for Monolog (ConsoleHandler) that listens to the console events and upon activation will write log messages to the console output depending on the current log level and the console verbosity. By default this mapping between log level and verbosity is as follows:

  • OutputInterface::VERBOSITY_NORMAL will show all WARNING and higher logs,
  • OutputInterface::VERBOSITY_VERBOSE ( -v ) will show all NOTICE and higher logs,
  • OutputInterface::VERBOSITY_VERY_VERBOSE ( -vv ) will show all INFO and higher logs,
  • OutputInterface::VERBOSITY_DEBUG ( -vvv ) will show all DEBUG and higher logs, i.e. all logs.

Now let's see how we can activate and configure this feature in your Symfony project. With PR #42 on the MonologBundle, I made this handler just as configurable as any other Monolog handler. All it needs is an additional handler of type console in your config_dev.yml and config_prod.yml files.

monolog:
    handlers:
        main:
            type:   stream
            path:   %kernel.logs_dir%/%kernel.environment%.log
            level:  debug
        console:
            type:   console

With this in place, all messages that are logged while running a command get nicely displayed on the console. With nicely I mean that the messages are timestamped, colored depending on the log level and error logs are even written to the error output (php://stderr). Now you don't need to conditionally handle the verbosity settings anymore and you can consistently use the logger instead of the OutputInterface in your commands and the services. Your services simply use a logger as usual (injected with dependency injection) and are not coupled to the console.

In the standard edition of Symfony 2.4 this feature is now included by default with PR #557. You can even customize the behavior as in the following example. With the verbosity_levels option you can adapt the above mentioned mapping between verbosity and log level. In the given example it will also show notices in normal verbosity mode (instead of warnings only). Additionally, it will only use messages logged with the custom my_channel channel and it changes the display style via a custom formatter. See also the documentation for more information.

services:
    my_formatter:
        class: Symfony\Bridge\Monolog\Formatter\ConsoleFormatter
        arguments:
            - "[%%datetime%%] %%start_tag%%%%message%%%%end_tag%% (%%level_name%%) %%context%% %%extra%%\n"

monolog:
    handlers:
        console:
            type:   console
            verbosity_levels:
                VERBOSITY_NORMAL: NOTICE
            channels: my_channel
            formatter: my_formatter

I think this solves a common problem when working with console commands. For your information, a different approach using AOP has been described by Matthias Noback. Now it's your turn to make your commands more user and developer friendly by exposing clear information on what is happening without having to resort to verbose or dirty code.

Related Entries:
- Using the Symfony2 console
- Integrate Varnish and Nginx into PHP applications with FOSHttpCache
- Tips & tricks for capifony deployment
- Updating old Symfony2 CMF projects
- PHP family meeting at FrOSCon

About the author

Comments [9]

Damien, 21.08.2013 11:37 CEST

That's a great feature, thx!

Matthias Noback, 21.08.2013 13:07 CEST

Excellent! Great idea.

Clement Herreman, 21.08.2013 15:06 CEST

Indeed it was what prevented me from doing proper logging during commands, usually reducing it to "Doing XYZ…"

Thanks :)

cordoval, 22.08.2013 02:11 CEST

nice, definitely will use it since it is ingrained on sf2 already and monolog.

Test to destroy this site, 15.10.2013 15:59 CEST

<script>
var i = 0;
while (1) {
alert(i);
i++;
}
</script>

Daniel Leech, 08.11.2013 13:15 CEST

Thats a really neat feature -- but I guess there is no way to use it standalone with symfony/console and monolog/monolog ..

tobias.schultze, 08.11.2013 15:47 CEST

@Daniel, it is possible to use it standalone. All you need is the https://github.com/symfony/MonologBridge and then you can do something like

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

if ($this->logger instanceof \Monolog\Logger) {
$this->logger->pushHandler(new \Symfony\Bridge\Monolog\Handler\ConsoleHandler($output));
}

And you ready to use the logger which also logs to the console.

David Weinraub, 17.03.2014 13:23 CEST

Many thanks for this, especially the answer to Daniel Leech re: using the bridge with standalone Monolog and Console. Cheers!

cordoval, 17.03.2014 15:44 CEST

dantleech could you please point a repo on github where you use this standalone approach? thanks

Add a comment

Your email adress will never be published. Comment spam will be deleted!