Skip to content

Commit 333afb1

Browse files
committed
added possibility show log messages in console commands
1 parent 03ed73b commit 333afb1

File tree

5 files changed

+275
-0
lines changed

5 files changed

+275
-0
lines changed

DependencyInjection/MonologExtension.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
class MonologExtension extends Extension
2929
{
3030
private $nestedHandlers = array();
31+
private $consoleHandlers = array();
3132

3233
/**
3334
* Loads the Monolog configuration.
@@ -54,6 +55,12 @@ public function load(array $configs, ContainerBuilder $container)
5455
);
5556
}
5657

58+
if ($this->consoleHandlers) {
59+
$consoleListener = $container->getDefinition('monolog.listener.console');
60+
$consoleListener->replaceArgument(0, $this->consoleHandlers);
61+
$consoleListener->setAbstract(false);
62+
}
63+
5764
ksort($handlers);
5865
$sortedHandlers = array();
5966
foreach ($handlers as $priorityHandlers) {
@@ -123,6 +130,13 @@ private function buildHandler(ContainerBuilder $container, $name, array $handler
123130
));
124131
break;
125132

133+
case 'console':
134+
$definition->setArguments(array(
135+
$handler['bubble'],
136+
));
137+
$this->consoleHandlers[] = new Reference($handlerId);
138+
break;
139+
126140
case 'firephp':
127141
$definition->setArguments(array(
128142
$handler['level'],
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
namespace Symfony\Bundle\MonologBundle\EventListener;
4+
5+
use Symfony\Bundle\MonologBundle\Handler\ConsoleHandler;
6+
use Symfony\Component\Console\ConsoleEvents;
7+
use Symfony\Component\Console\Event\ConsoleCommandEvent;
8+
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
9+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
10+
11+
/**
12+
* A listener, executed before command execution, that sets the console output
13+
* on the console handlers so they know where to write the logs.
14+
*
15+
* @author Tobias Schultze <http://tobion.de>
16+
*/
17+
class LogToConsoleListener implements EventSubscriberInterface
18+
{
19+
private $consoleHandlers;
20+
21+
/**
22+
* Constructor.
23+
*
24+
* @param ConsoleHandler[] $consoleHandlers Array of ConsoleHandler instances
25+
*/
26+
public function __construct(array $consoleHandlers)
27+
{
28+
$this->consoleHandlers = $consoleHandlers;
29+
}
30+
31+
/**
32+
* Enables the handlers by setting the console output on them before a command is executed.
33+
*
34+
* @param ConsoleCommandEvent $event
35+
*/
36+
public function onCommand(ConsoleCommandEvent $event)
37+
{
38+
foreach ($this->consoleHandlers as $handler) {
39+
/** @var $handler ConsoleHandler */
40+
$handler->setOutput($event->getOutput());
41+
}
42+
}
43+
44+
/**
45+
* Disables the handlers after a command has been executed.
46+
*
47+
* @param ConsoleTerminateEvent $event
48+
*/
49+
public function onTerminate(ConsoleTerminateEvent $event)
50+
{
51+
foreach ($this->consoleHandlers as $handler) {
52+
/** @var $handler ConsoleHandler */
53+
$handler->close();
54+
}
55+
}
56+
57+
/**
58+
* {@inheritdoc}
59+
*/
60+
public static function getSubscribedEvents()
61+
{
62+
return array(
63+
ConsoleEvents::COMMAND => 'onCommand',
64+
ConsoleEvents::TERMINATE => 'onTerminate'
65+
);
66+
}
67+
}

Handler/ConsoleHandler.php

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Monolog package.
5+
*
6+
* (c) Jordi Boggiano <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\MonologBundle\Handler;
13+
14+
use Monolog\Handler\AbstractProcessingHandler;
15+
use Monolog\Logger;
16+
use Symfony\Component\Console\Output\ConsoleOutputInterface;
17+
use Symfony\Component\Console\Output\OutputInterface;
18+
use Symfony\Component\Console\Output\StreamOutput;
19+
20+
/**
21+
* Writes to the console output depending on its verbosity setting.
22+
*
23+
* It is disabled by default and gets activated when setOutput is called.
24+
*
25+
* @author Tobias Schultze <http://tobion.de>
26+
*/
27+
class ConsoleHandler extends AbstractProcessingHandler
28+
{
29+
/**
30+
* The stream to use when the console OutputInterface is not a StreamOutput.
31+
*/
32+
const FALLBACK_STREAM = 'php://output';
33+
34+
/**
35+
* The error stream to use when the console OutputInterface is not a StreamOutput.
36+
*/
37+
const FALLBACK_ERROR_STREAM = 'php://stderr';
38+
39+
private $enabled = false;
40+
private $stream;
41+
private $streamCreated = false;
42+
private $errorStream;
43+
private $errorStreamCreated = false;
44+
45+
/**
46+
* Constructor.
47+
*
48+
* The minimum logging level it is set based on the output verbosity in setOutput.
49+
*
50+
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
51+
*/
52+
public function __construct($bubble = true)
53+
{
54+
parent::__construct(Logger::DEBUG, $bubble);
55+
}
56+
57+
/**
58+
* {@inheritdoc}
59+
*/
60+
public function isHandling(array $record)
61+
{
62+
return $this->enabled && parent::isHandling($record);
63+
}
64+
65+
/**
66+
* {@inheritdoc}
67+
*/
68+
public function handle(array $record)
69+
{
70+
if (!$this->enabled) {
71+
return false;
72+
}
73+
74+
return parent::handle($record);
75+
}
76+
77+
/**
78+
* Sets the console output to use for printing logs.
79+
*
80+
* It enabled the writing unless the output verbosity is set to quiet.
81+
*
82+
* @param OutputInterface $output The console output to use
83+
*/
84+
public function setOutput(OutputInterface $output)
85+
{
86+
// close streams that might have been created before
87+
$this->close();
88+
89+
if (OutputInterface::VERBOSITY_QUIET === $output->getVerbosity()) {
90+
return; // the handler remains disabled
91+
}
92+
93+
if ($output instanceof StreamOutput) {
94+
$this->stream = $output->getStream();
95+
}
96+
97+
if ($output instanceof ConsoleOutputInterface && $output->getErrorOutput() instanceof StreamOutput) {
98+
$this->errorStream = $output->getErrorOutput()->getStream();
99+
}
100+
101+
switch ($output->getVerbosity()) {
102+
case OutputInterface::VERBOSITY_NORMAL:
103+
$this->setLevel(Logger::WARNING);
104+
break;
105+
case OutputInterface::VERBOSITY_VERBOSE:
106+
$this->setLevel(Logger::NOTICE);
107+
break;
108+
case OutputInterface::VERBOSITY_VERY_VERBOSE:
109+
$this->setLevel(Logger::INFO);
110+
break;
111+
default:
112+
$this->setLevel(Logger::DEBUG);
113+
break;
114+
}
115+
116+
$this->enabled = true;
117+
}
118+
119+
/**
120+
* Disables the output and closes newly created streams.
121+
*
122+
* It does not close streams coming from StreamOutput because they belong to the console.
123+
*/
124+
public function close()
125+
{
126+
if ($this->streamCreated && is_resource($this->stream)) {
127+
fclose($this->stream);
128+
}
129+
$this->stream = null;
130+
$this->streamCreated = false;
131+
132+
if ($this->errorStreamCreated && is_resource($this->errorStream)) {
133+
fclose($this->errorStream);
134+
}
135+
$this->errorStream = null;
136+
$this->errorStreamCreated = false;
137+
138+
$this->enabled = false;
139+
140+
parent::close();
141+
}
142+
143+
/**
144+
* {@inheritdoc}
145+
*/
146+
protected function write(array $record)
147+
{
148+
if (null === $this->stream) {
149+
$errorMessage = null;
150+
set_error_handler(function ($code, $msg) use (&$errorMessage) {
151+
$errorMessage = preg_replace('{^fopen\(.*?\): }', '', $msg);
152+
});
153+
$this->stream = fopen(self::FALLBACK_STREAM, 'a');
154+
restore_error_handler();
155+
if (!is_resource($this->stream)) {
156+
$this->stream = null;
157+
$this->streamCreated = false;
158+
throw new \UnexpectedValueException(sprintf('The stream "%s" could not be opened: '.$errorMessage, self::FALLBACK_STREAM));
159+
}
160+
$this->streamCreated = true;
161+
}
162+
163+
if (null === $this->errorStream) {
164+
$errorMessage = null;
165+
set_error_handler(function ($code, $msg) use (&$errorMessage) {
166+
$errorMessage = preg_replace('{^fopen\(.*?\): }', '', $msg);
167+
});
168+
$this->errorStream = fopen(self::FALLBACK_ERROR_STREAM, 'a');
169+
restore_error_handler();
170+
if (!is_resource($this->errorStream)) {
171+
$this->errorStream = null;
172+
$this->errorStreamCreated = false;
173+
throw new \UnexpectedValueException(sprintf('The stream "%s" could not be opened: '.$errorMessage, self::FALLBACK_STREAM));
174+
}
175+
$this->errorStreamCreated = true;
176+
}
177+
178+
if ($record['level'] >= Logger::ERROR) {
179+
fwrite($this->errorStream, (string) $record['formatted']);
180+
} else {
181+
fwrite($this->stream, (string) $record['formatted']);
182+
}
183+
}
184+
}

Resources/config/monolog.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66

77
<parameters>
88
<parameter key="monolog.logger.class">Symfony\Bridge\Monolog\Logger</parameter>
9+
<parameter key="monolog.listener.console.class">Symfony\Bundle\MonologBundle\EventListener\LogToConsoleListener</parameter>
910
<parameter key="monolog.gelf.publisher.class">Gelf\MessagePublisher</parameter>
1011
<parameter key="monolog.handler.stream.class">Monolog\Handler\StreamHandler</parameter>
12+
<parameter key="monolog.handler.console.class">Symfony\Bundle\MonologBundle\Handler\ConsoleHandler</parameter>
1113
<parameter key="monolog.handler.group.class">Monolog\Handler\GroupHandler</parameter>
1214
<parameter key="monolog.handler.buffer.class">Monolog\Handler\BufferHandler</parameter>
1315
<parameter key="monolog.handler.rotating_file.class">Monolog\Handler\RotatingFileHandler</parameter>
@@ -38,5 +40,10 @@
3840
<service id="monolog.logger_prototype" class="%monolog.logger.class%" abstract="true">
3941
<argument /><!-- Channel -->
4042
</service>
43+
44+
<service id="monolog.listener.console" class="%monolog.listener.console.class%" abstract="true">
45+
<tag name="kernel.event_subscriber" />
46+
<argument /><!-- Array of ConsoleHandlers -->
47+
</service>
4148
</services>
4249
</container>

composer.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
"require-dev": {
2626
"symfony/yaml": "~2.2-beta2"
2727
},
28+
"suggest": {
29+
"event-dispatcher": "For the possibility to show log messages in console commands depending on verbosity settings."
30+
},
2831
"autoload": {
2932
"psr-0": { "Symfony\\Bundle\\MonologBundle": "" }
3033
},

0 commit comments

Comments
 (0)