[MonologBridge] added integration with the console component
This commit is contained in:
parent
e160ddb286
commit
c0b59963b3
@ -1,6 +1,12 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
2.4.0
|
||||
-----
|
||||
|
||||
* added ConsoleHandler and ConsoleFormatter which can be used to show log messages
|
||||
in the console output depending on the verbosity settings
|
||||
|
||||
2.1.0
|
||||
-----
|
||||
|
||||
|
47
src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php
Normal file
47
src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\Monolog\Formatter;
|
||||
|
||||
use Monolog\Formatter\LineFormatter;
|
||||
use Monolog\Logger;
|
||||
|
||||
/**
|
||||
* Formats incoming records for console output by coloring them depending on log level.
|
||||
*
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
*/
|
||||
class ConsoleFormatter extends LineFormatter
|
||||
{
|
||||
const SIMPLE_FORMAT = "%start_tag%[%datetime%] %channel%.%level_name%:%end_tag% %message% %context% %extra%\n";
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function format(array $record)
|
||||
{
|
||||
if ($record['level'] >= Logger::ERROR) {
|
||||
$record['start_tag'] = '<error>';
|
||||
$record['end_tag'] = '</error>';
|
||||
} elseif ($record['level'] >= Logger::NOTICE) {
|
||||
$record['start_tag'] = '<comment>';
|
||||
$record['end_tag'] = '</comment>';
|
||||
} elseif ($record['level'] >= Logger::INFO) {
|
||||
$record['start_tag'] = '<info>';
|
||||
$record['end_tag'] = '</info>';
|
||||
} else {
|
||||
$record['start_tag'] = '';
|
||||
$record['end_tag'] = '';
|
||||
}
|
||||
|
||||
return parent::format($record);
|
||||
}
|
||||
}
|
186
src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php
Normal file
186
src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php
Normal file
@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\Monolog\Handler;
|
||||
|
||||
use Monolog\Handler\AbstractProcessingHandler;
|
||||
use Monolog\Logger;
|
||||
use Symfony\Bridge\Monolog\Formatter\ConsoleFormatter;
|
||||
use Symfony\Component\Console\ConsoleEvents;
|
||||
use Symfony\Component\Console\Event\ConsoleCommandEvent;
|
||||
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
|
||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* Writes logs to the console output depending on its verbosity setting.
|
||||
*
|
||||
* It is disabled by default and gets activated as soon as a command is executed.
|
||||
* Instead of listening to the console events, the output can also be set manually.
|
||||
*
|
||||
* The minimum logging level at which this handler will be triggered depends on the
|
||||
* verbosity setting of the console output. The default mapping is:
|
||||
* - 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
|
||||
*
|
||||
* This mapping can be customized with the $verbosityLevelMap constructor parameter.
|
||||
*
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
*/
|
||||
class ConsoleHandler extends AbstractProcessingHandler implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var OutputInterface|null
|
||||
*/
|
||||
private $output;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $verbosityLevelMap = array(
|
||||
OutputInterface::VERBOSITY_NORMAL => Logger::WARNING,
|
||||
OutputInterface::VERBOSITY_VERBOSE => Logger::NOTICE,
|
||||
OutputInterface::VERBOSITY_VERY_VERBOSE => Logger::INFO,
|
||||
OutputInterface::VERBOSITY_DEBUG => Logger::DEBUG
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param OutputInterface|null $output The console output to use (the handler remains disabled when passing null
|
||||
* until the output is set, e.g. by using console events)
|
||||
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack
|
||||
* @param array $verbosityLevelMap Array that maps the OutputInterface verbosity to a minimum logging
|
||||
* level (leave empty to use the default mapping)
|
||||
*/
|
||||
public function __construct(OutputInterface $output = null, $bubble = true, array $verbosityLevelMap = array())
|
||||
{
|
||||
parent::__construct(Logger::DEBUG, $bubble);
|
||||
$this->output = $output;
|
||||
|
||||
if ($verbosityLevelMap) {
|
||||
$this->verbosityLevelMap = $verbosityLevelMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isHandling(array $record)
|
||||
{
|
||||
return $this->updateLevel() && parent::isHandling($record);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handle(array $record)
|
||||
{
|
||||
// we have to update the logging level each time because the verbosity of the
|
||||
// console output might have changed in the meantime (it is not immutable)
|
||||
return $this->updateLevel() && parent::handle($record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the console output to use for printing logs.
|
||||
*
|
||||
* @param OutputInterface $output The console output to use
|
||||
*/
|
||||
public function setOutput(OutputInterface $output)
|
||||
{
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the output.
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$this->output = null;
|
||||
|
||||
parent::close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Before a command is executed, the handler gets activated and the console output
|
||||
* is set in order to know where to write the logs.
|
||||
*
|
||||
* @param ConsoleCommandEvent $event
|
||||
*/
|
||||
public function onCommand(ConsoleCommandEvent $event)
|
||||
{
|
||||
$this->setOutput($event->getOutput());
|
||||
}
|
||||
|
||||
/**
|
||||
* After a command has been executed, it disables the output.
|
||||
*
|
||||
* @param ConsoleTerminateEvent $event
|
||||
*/
|
||||
public function onTerminate(ConsoleTerminateEvent $event)
|
||||
{
|
||||
$this->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
ConsoleEvents::COMMAND => 'onCommand',
|
||||
ConsoleEvents::TERMINATE => 'onTerminate'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function write(array $record)
|
||||
{
|
||||
if ($record['level'] >= Logger::ERROR && $this->output instanceof ConsoleOutputInterface) {
|
||||
$this->output->getErrorOutput()->write((string) $record['formatted']);
|
||||
} else {
|
||||
$this->output->write((string) $record['formatted']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDefaultFormatter()
|
||||
{
|
||||
return new ConsoleFormatter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the logging level based on the verbosity setting of the console output.
|
||||
*
|
||||
* @return Boolean Whether the handler is enabled and verbosity is not set to quiet.
|
||||
*/
|
||||
private function updateLevel()
|
||||
{
|
||||
if (null === $this->output || OutputInterface::VERBOSITY_QUIET === $verbosity = $this->output->getVerbosity()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($this->verbosityLevelMap[$verbosity])) {
|
||||
$this->setLevel($this->verbosityLevelMap[$verbosity]);
|
||||
} else {
|
||||
$this->setLevel(Logger::DEBUG);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
166
src/Symfony/Bridge/Monolog/Tests/Handler/ConsoleHandlerTest.php
Normal file
166
src/Symfony/Bridge/Monolog/Tests/Handler/ConsoleHandlerTest.php
Normal file
@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\Monolog\Tests\Handler;
|
||||
|
||||
use Monolog\Logger;
|
||||
use Symfony\Bridge\Monolog\Handler\ConsoleHandler;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Tests the ConsoleHandler and also the ConsoleFormatter.
|
||||
*
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
*/
|
||||
class ConsoleHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
if (!class_exists('Monolog\\Logger')) {
|
||||
$this->markTestSkipped('Monolog is not available.');
|
||||
}
|
||||
}
|
||||
|
||||
public function testConstructor()
|
||||
{
|
||||
$handler = new ConsoleHandler(null, false);
|
||||
$this->assertFalse($handler->getBubble(), 'the bubble parameter gets propagated');
|
||||
}
|
||||
|
||||
public function testIsHandling()
|
||||
{
|
||||
$handler = new ConsoleHandler();
|
||||
$this->assertFalse($handler->isHandling(array()), '->isHandling returns false when no output is set');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideVerbosityMappingTests
|
||||
*/
|
||||
public function testVerbosityMapping($verbosity, $level, $isHandling, array $map = array())
|
||||
{
|
||||
$output = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
|
||||
$output
|
||||
->expects($this->atLeastOnce())
|
||||
->method('getVerbosity')
|
||||
->will($this->returnValue($verbosity))
|
||||
;
|
||||
$handler = new ConsoleHandler($output, true, $map);
|
||||
$this->assertSame($isHandling, $handler->isHandling(array('level' => $level)),
|
||||
'->isHandling returns correct value depending on console verbosity and log level'
|
||||
);
|
||||
}
|
||||
|
||||
public function provideVerbosityMappingTests()
|
||||
{
|
||||
return array(
|
||||
array(OutputInterface::VERBOSITY_QUIET, Logger::ERROR, false),
|
||||
array(OutputInterface::VERBOSITY_NORMAL, Logger::WARNING, true),
|
||||
array(OutputInterface::VERBOSITY_NORMAL, Logger::NOTICE, false),
|
||||
array(OutputInterface::VERBOSITY_VERBOSE, Logger::NOTICE, true),
|
||||
array(OutputInterface::VERBOSITY_VERBOSE, Logger::INFO, false),
|
||||
array(OutputInterface::VERBOSITY_VERY_VERBOSE, Logger::INFO, true),
|
||||
array(OutputInterface::VERBOSITY_VERY_VERBOSE, Logger::DEBUG, false),
|
||||
array(OutputInterface::VERBOSITY_DEBUG, Logger::DEBUG, true),
|
||||
array(OutputInterface::VERBOSITY_DEBUG, Logger::EMERGENCY, true),
|
||||
array(OutputInterface::VERBOSITY_NORMAL, Logger::NOTICE, true, array(
|
||||
OutputInterface::VERBOSITY_NORMAL => Logger::NOTICE
|
||||
)),
|
||||
array(OutputInterface::VERBOSITY_DEBUG, Logger::NOTICE, true, array(
|
||||
OutputInterface::VERBOSITY_NORMAL => Logger::NOTICE
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
public function testVerbosityChanged()
|
||||
{
|
||||
$output = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
|
||||
$output
|
||||
->expects($this->at(0))
|
||||
->method('getVerbosity')
|
||||
->will($this->returnValue(OutputInterface::VERBOSITY_QUIET))
|
||||
;
|
||||
$output
|
||||
->expects($this->at(1))
|
||||
->method('getVerbosity')
|
||||
->will($this->returnValue(OutputInterface::VERBOSITY_DEBUG))
|
||||
;
|
||||
$handler = new ConsoleHandler($output);
|
||||
$this->assertFalse($handler->isHandling(array('level' => Logger::NOTICE)),
|
||||
'when verbosity is set to quiet, the handler does not handle the log'
|
||||
);
|
||||
$this->assertTrue($handler->isHandling(array('level' => Logger::NOTICE)),
|
||||
'since the verbosity of the output increased externally, the handler is now handling the log'
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetFormatter()
|
||||
{
|
||||
$handler = new ConsoleHandler();
|
||||
$this->assertInstanceOf('Symfony\Bridge\Monolog\Formatter\ConsoleFormatter', $handler->getFormatter(),
|
||||
'-getFormatter returns ConsoleFormatter by default'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWritingAndFormatting()
|
||||
{
|
||||
$output = $this->getMock('Symfony\Component\Console\Output\ConsoleOutputInterface');
|
||||
$output
|
||||
->expects($this->any())
|
||||
->method('getVerbosity')
|
||||
->will($this->returnValue(OutputInterface::VERBOSITY_DEBUG))
|
||||
;
|
||||
$output
|
||||
->expects($this->once())
|
||||
->method('write')
|
||||
->with('<info>[2013-05-29 16:21:54] app.INFO:</info> My info message [] []'."\n")
|
||||
;
|
||||
|
||||
$errorOutput = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
|
||||
$errorOutput
|
||||
->expects($this->once())
|
||||
->method('write')
|
||||
->with('<error>[2013-05-29 16:21:54] app.ERROR:</error> My error message [] []'."\n")
|
||||
;
|
||||
|
||||
$output
|
||||
->expects($this->any())
|
||||
->method('getErrorOutput')
|
||||
->will($this->returnValue($errorOutput))
|
||||
;
|
||||
|
||||
$handler = new ConsoleHandler(null, false);
|
||||
$handler->setOutput($output);
|
||||
|
||||
$infoRecord = array(
|
||||
'message' => 'My info message',
|
||||
'context' => array(),
|
||||
'level' => Logger::INFO,
|
||||
'level_name' => Logger::getLevelName(Logger::INFO),
|
||||
'channel' => 'app',
|
||||
'datetime' => new \DateTime('2013-05-29 16:21:54'),
|
||||
'extra' => array(),
|
||||
);
|
||||
|
||||
$this->assertTrue($handler->handle($infoRecord), 'The handler finished handling the log as bubble is false.');
|
||||
|
||||
$errorRecord = array(
|
||||
'message' => 'My error message',
|
||||
'context' => array(),
|
||||
'level' => Logger::ERROR,
|
||||
'level_name' => Logger::getLevelName(Logger::ERROR),
|
||||
'channel' => 'app',
|
||||
'datetime' => new \DateTime('2013-05-29 16:21:54'),
|
||||
'extra' => array(),
|
||||
);
|
||||
|
||||
$this->assertTrue($handler->handle($errorRecord), 'The handler finished handling the log as bubble is false.');
|
||||
}
|
||||
}
|
@ -17,9 +17,17 @@
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.3",
|
||||
"symfony/http-kernel": "~2.2",
|
||||
"monolog/monolog": "~1.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/http-kernel": "~2.2",
|
||||
"symfony/console": "~2.3",
|
||||
"symfony/event-dispatcher": "~2.2"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/http-kernel": "For using the debugging handlers together with the response life cycle of the HTTP kernel.",
|
||||
"symfony/console": "For the possibility to show log messages in console commands depending on verbosity settings. You need version ~2.3 of the console for it."
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": { "Symfony\\Bridge\\Monolog\\": "" }
|
||||
},
|
||||
|
Reference in New Issue
Block a user