8000 Migrate server:log command away from WebServerBundle · symfony/symfony@07ac962 · GitHub
[go: up one dir, main page]

Skip to content

Commit 07ac962

Browse files
committed
Migrate server:log command away from WebServerBundle
1 parent c79a498 commit 07ac962

File tree

6 files changed

+204
-2
lines changed

6 files changed

+204
-2
lines changed
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Bridge\Monolog\Command;
13+
14+
use Monolog\Formatter\FormatterInterface;
15+
use Monolog\Logger;
16+
use Symfony\Bridge\Monolog\Formatter\ConsoleFormatter;
17+
use Symfony\Bridge\Monolog\Handler\ConsoleHandler;
18+
use Symfony\Component\Console\Command\Command;
19+
use Symfony\Component\Console\Exception\LogicException;
20+
use Symfony\Component\Console\Exception\RuntimeException;
21+
use Symfony\Component\Console\Input\InputInterface;
22+
use Symfony\Component\Console\Input\InputOption;
23+
use Symfony\Component\Console\Output\OutputInterface;
24+
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
25+
26+
/**
27+
* @author Grégoire Pineau <lyrixx@lyrixx.info>
28+
*/
29+
class ServerLogCommand extends Command
30+
{
31+
private static $bgColor = ['black', 'blue', 'cyan', 'green', 'magenta', 'red', 'white', 'yellow'];
32+
33+
private $el;
34+
private $handler;
35+
36+
protected static $defaultName = 'server:log';
37+
38+
public function isEnabled()
39+
{
40+
if (!class_exists(ConsoleFormatter::class)) {
41+
return false;
42+
}
43+
44+
// based on a symfony/symfony package, it crashes due a missing FormatterInterface from monolog/monolog
45+
if (!interface_exists(FormatterInterface::class)) {
46+
return false;
47+
}
48+
49+
return parent::isEnabled();
50+
}
51+
52+
protected function configure()
53+
{
54+
if (!class_exists(ConsoleFormatter::class)) {
55+
return;
56+
}
57+
58+
$this
59+
->addOption('host', null, InputOption::VALUE_REQUIRED, 'The server host', '0.0.0 A93C .0:9911')
60+
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The line format', ConsoleFormatter::SIMPLE_FORMAT)
61+
->addOption('date-format', null, InputOption::VALUE_REQUIRED, 'The date format', ConsoleFormatter::SIMPLE_DATE)
62+
->addOption('filter', null, InputOption::VALUE_REQUIRED, 'An expression to filter log. Example: "level > 200 or channel in [\'app\', \'doctrine\']"')
63+
->setDescription('Starts a log server that displays logs in real time')
64+
->setHelp(<<<'EOF'
65+
<info>%command.name%</info> starts a log server to display in real time the log
66+
messages generated by your application:
67+
68+
<info>php %command.full_name%</info>
69+
70+
To get the information as a machine readable format, use the
71+
<comment>--filter</> option:
72+
73+
<info>php %command.full_name% --filter=port</info>
74+
EOF
75+
)
76+
;
77+
}
78+
79+
protected function execute(InputInterface $input, OutputInterface $output)
80+
{
81+
$filter = $input->getOption('filter');
82+
if ($filter) {
83+
if (!class_exists(ExpressionLanguage::class)) {
84+
throw new LogicException('Package "symfony/expression-language" is required to use the "filter" option.');
85+
}
86+
$this->el = new ExpressionLanguage();
87+
}
88+
89+
$this->handler = new ConsoleHandler($output, true, [
90+
OutputInterface::VERBOSITY_NORMAL => Logger::DEBUG,
91+
]);
92+
93+
$this->handler->setFormatter(new ConsoleFormatter([
94+
'format' => str_replace('\n', "\n", $input->getOption('format')),
95+
'date_format' => $input->getOption('date-format'),
96+
'colors' => $output->isDecorated(),
97+
'multiline' => OutputInterface::VERBOSITY_DEBUG <= $output->getVerbosity(),
98+
]));
99+
100+
if (false === strpos($host = $input->getOption('host'), '://')) {
101+
$host = 'tcp://'.$host;
102+
}
103+
104+
if (!$socket = stream_socket_server($host, $errno, $errstr)) {
105+
throw new RuntimeException(sprintf('Server start failed on "%s": %s %s.', $host, $errstr, $errno));
106+
}
107+
108+
foreach ($this->getLogs($socket) as $clientId => $message) {
109+
$record = unserialize(base64_decode($message));
110+
111+
// Impossible to decode the message, give up.
112+
if (false === $record) {
113+
continue;
114+
}
115+
116+
if ($filter && !$this->el->evaluate($filter, $record)) {
117+
continue;
118+
}
119+
120+
$this->displayLog($output, $clientId, $record);
121+
}
122+
123+
return 0;
124+
}
125+
126+
private function getLogs($socket): iterable
127+
{
128+
$sockets = [(int) $socket => $socket];
129+
$write = [];
130+
131+
while (true) {
132+
$read = $sockets;
133+
stream_select($read, $write, $write, null);
134+
135+
foreach ($read as $stream) {
136+
if ($socket === $stream) {
137+
$stream = stream_socket_accept($socket);
138+
$sockets[(int) $stream] = $stream;
139+
} elseif (feof($stream)) {
140+
unset($sockets[(int) $stream]);
141+
fclose($stream);
142+
} else {
143+
yield (int) $stream => fgets($stream);
144+
}
145+
}
146+
}
147+
}
148+
149+
private function displayLog(OutputInterface $output, int $clientId, array $record)
150+
{
151+
if (isset($record['log_id'])) {
152+
$clientId = unpack('H*', $record['log_id'])[1];
153+
}
154+
$logBlock = sprintf('<bg=%s> </>', self::$bgColor[$clientId % 8]);
155+
$output->write($logBlock);
156+
157+
$this->handler->handle($record);
158+
}
159+
}

src/Symfony/Bundle/DebugBundle/DebugBundle.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Bundle\DebugBundle;
1313

1414
use Symfony\Bundle\DebugBundle\DependencyInjection\Compiler\DumpDataCollectorPass;
15+
use Symfony\Bundle\DebugBundle\DependencyInjection\Compiler\RemoveWebServerBundleLoggerPass;
1516
use Symfony\Component\Console\Application;
1617
use Symfony\Component\DependencyInjection\ContainerBuilder;
1718
use Symfony\Component\HttpKernel\Bundle\Bundle;
@@ -52,6 +53,7 @@ public function build(ContainerBuilder $container)
5253
parent::build($container);
5354

5455
$container->addCompilerPass(new DumpDataCollectorPass());
56+
$container->addCompilerPass(new RemoveWebServerBundleLoggerPass());
5557
}
5658

5759
public function registerCommands(Application $application)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\DebugBundle\DependencyInjection\Compiler;
13+
14+
use Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener;
15+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
16+
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
18+
/**
19+
* @author Jérémy Derussé <jeremy@derusse.com>
20+
*/
21+
class RemoveWebServerBundleLoggerPass implements CompilerPassInterface
22+
{
23+
/**
24+
* {@inheritdoc}
25+
*/
26+
public function process(ContainerBuilder $container)
27+
{
28+
if ($container->hasDefinition('web_server.command.server_log') && $container->hasDefinition('monolog.command.server_log')) {
29+
$container->removeDefinition('web_server.command.server_log');
30+
}
31+
}
32+
}

src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Bundle\DebugBundle\DependencyInjection;
1313

14+
use Symfony\Bridge\Monolog\Command\ServerLogCommand;
1415
use Symfony\Bundle\DebugBundle\Command\ServerDumpPlaceholderCommand;
1516
use Symfony\Component\Config\FileLocator;
1617
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -90,6 +91,10 @@ public function load(array $configs, ContainerBuilder $container)
9091
]])
9192
;
9293
}
94+
95+
if (!\class_exists(ServerLogCommand::class)) {
96+
$container->removeDefinition('monolog.command.server_log');
97+
}
9398
}
9499

95100
/**

src/Symfony/Bundle/DebugBundle/Resources/config/services.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,5 +107,9 @@
107107
</argument>
108108
<tag name="console.command" command="server:dump" />
109109
</service>
110+
111+
<service id="monolog.command.server_log" class="Symfony\Bridge\Monolog\Command\ServerLogCommand">
112+
<tag name="console.command" command="server:log" />
113+
</service>
110114
</services>
111115
</container>

src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
/**
2727
* @author Grégoire Pineau <lyrixx@lyrixx.info>
2828
*
29-
* @deprecated since Symfony 4.4, to be removed in 5.0; the new Symfony local server has more features, you can use it instead.
29+
* @deprecated since Symfony 4.4, to be removed in 5.0; use ServerLogCommand from symfony/monolog-bridge instead
3030
*/
3131
class ServerLogCommand extends Command
3232
{
@@ -80,7 +80,7 @@ protected function configure()
8080

8181
protected function execute(InputInterface $input, OutputInterface $output)
8282
{
83-
@trigger_error('Using the WebserverBundle is deprecated since Symfony 4.4. The new Symfony local server has more features, you can use it instead.', E_USER_DEPRECATED);
83+
@trigger_error('Using the WebserverBundle is deprecated since Symfony 4.4. Use the DebugBundle combined with MonologBridge instead.', E_USER_DEPRECATED);
8484

8585
$filter = $input->getOption('filter');
8686
if ($filter) {

0 commit comments

Comments
 (0)
0