Skip to content

[Console] Update the doc for automatic console logging #7373

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 3, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
262 changes: 5 additions & 257 deletions console/logging.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,261 +4,9 @@
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.
In Symfony versions prior to 3.3, the Console component didn't provide any
logging capabilities out of the box and you had to implement your own exception
lister for the console.

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-stack
framework as described in ":doc:`/console`", your command extends
:class:`Symfony\\Bundle\\FrameworkBundle\\Command\\ContainerAwareCommand`.
This means that you can simply access the standard logger service through the
container and use it to do the logging::

// src/AppBundle/Command/GreetCommand.php
namespace AppBundle\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 Psr\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->warning('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 ``var/logs/dev.log`` or ``var/logs/prod.log``.

Enabling automatic Exceptions Logging
-------------------------------------

To get your console application to automatically log uncaught exceptions for
all of your commands, you can use :doc:`console events</components/console/events>`.

First configure a listener for console exception events in the service container:

.. configuration-block::

.. code-block:: yaml

# app/config/services.yml
services:
app.listener.command_exception:
class: AppBundle\EventListener\ConsoleExceptionListener
arguments: ['@logger']
tags:
- { name: kernel.event_listener, event: console.exception }

.. code-block:: xml

<!-- app/config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<services>
<service id="app.listener.command_exception" class="AppBundle\EventListener\ConsoleExceptionListener">
<argument type="service" id="logger"/>
<tag name="kernel.event_listener" event="console.exception" />
</service>
</services>
</container>

.. code-block:: php

// app/config/services.php
use AppBundle\EventListener\ConsoleExceptionListener;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

$definitionConsoleExceptionListener = new Definition(
ConsoleExceptionListener::class,
array(new Reference('logger'))
);
$definitionConsoleExceptionListener->addTag(
'kernel.event_listener',
array('event' => 'console.exception')
);
$container->setDefinition(
'app.listener.command_exception',
$definitionConsoleExceptionListener
);

Then implement the actual listener::

// src/AppBundle/EventListener/ConsoleExceptionListener.php
namespace AppBundle\EventListener;

use Symfony\Component\Console\Event\ConsoleExceptionEvent;
use Psr\Log\LoggerInterface;

class ConsoleExceptionListener
{
private $logger;

public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}

public function onConsoleException(ConsoleExceptionEvent $event)
{
$command = $event->getCommand();
$exception = $event->getException();

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

$this->logger->error($message, array('exception' => $exception));
}
}

In the code above, when any command throws an exception, the listener will
receive an event. You can simply log it by passing the logger service via the
service configuration. Your method receives a
:class:`Symfony\\Component\\Console\\Event\\ConsoleExceptionEvent` object,
which has methods to get information about the event and the exception.

.. _logging-non-0-exit-statuses:

Logging Error Exit Statuses
---------------------------

The logging capabilities of the console can be further extended by logging
commands that return error exit statuses, which are any number different than
zero. This way you will know if a command had any errors, even if no exceptions
were thrown.

First configure a listener for console terminate events in the service container:

.. configuration-block::

.. code-block:: yaml

# app/config/services.yml
services:
app.listener.command_error:
class: AppBundle\EventListener\ErrorLoggerListener
arguments: ['@logger']
tags:
- { name: kernel.event_listener, event: console.terminate }

.. code-block:: xml

<!-- app/config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<services>
<service id="app.listener.command_error" class="AppBundle\EventListener\ErrorLoggerListener">
<argument type="service" id="logger"/>
<tag name="kernel.event_listener" event="console.terminate" />
</service>
</services>
</container>

.. code-block:: php

// app/config/services.php
use AppBundle\EventListener\ErrorLoggerListener;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

$definitionErrorLoggerListener = new Definition(
ErrorLoggerListener::class,
array(new Reference('logger'))
);
$definitionErrorLoggerListener->addTag(
'kernel.event_listener',
array('event' => 'console.terminate')
);
$container->setDefinition(
'app.listener.command_error',
$definitionErrorLoggerListener
);

Then implement the actual listener::

// src/AppBundle/EventListener/ErrorLoggerListener.php
namespace AppBundle\EventListener;

use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Psr\Log\LoggerInterface;

class ErrorLoggerListener
{
private $logger;

public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}

public function onConsoleTerminate(ConsoleTerminateEvent $event)
{
$statusCode = $event->getExitCode();
$command = $event->getCommand();

if ($statusCode === 0) {
return;
}

if ($statusCode > 255) {
$statusCode = 255;
$event->setExitCode($statusCode);
}

$this->logger->warning(sprintf(
'Command `%s` exited with status code %d',
$command->getName(),
$statusCode
));
}
}
Starting from Symfony 3.3, the Console component provides automatic error and
exception logging.