Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b516ef7

Browse files
committed
[Bridge/Monolog] Enhanced the Console Handler
Basically, The formatter now uses the VarDumper & uses more significant colors and has a more compact / readable format (IMHO).
1 parent 0e92e0a commit b516ef7

File tree

4 files changed

+182
-22
lines changed

4 files changed

+182
-22
lines changed

src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php

Lines changed: 164 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,45 +11,191 @@
1111

1212
namespace Symfony\Bridge\Monolog\Formatter;
1313

14-
use Monolog\Formatter\LineFormatter;
15-
use Monolog\Logger;
14+
use Monolog\Formatter\FormatterInterface;
15+
use Symfony\Component\VarDumper\Cloner\Data;
16+
use Symfony\Component\VarDumper\Cloner\Stub;
17+
use Symfony\Component\VarDumper\Cloner\VarCloner;
18+
use Symfony\Component\VarDumper\Dumper\CliDumper;
1619

1720
/**
1821
* Formats incoming records for console output by coloring them depending on log level.
1922
*
2023
* @author Tobias Schultze <http://tobion.de>
24+
* @author Grégoire Pineau <[email protected]>
2125
*/
22-
class ConsoleFormatter extends LineFormatter
26+
class ConsoleFormatter implements FormatterInterface
2327
{
24-
const SIMPLE_FORMAT = "%start_tag%[%datetime%] %channel%.%level_name%:%end_tag% %message% %context% %extra%\n";
28+
const SIMPLE_FORMAT = "%datetime% %start_tag%%level_name% [%channel%]%end_tag% %message%%context%%extra%\n";
29+
const SIMPLE_DATE = 'H:i:s';
30+
31+
private static $levelColorMap = array(
32+
100 => 'fg=white',
33+
200 => 'fg=green',
34+
250 => 'fg=blue',
35+
300 => 'fg=cyan',
36+
400 => 'fg=yellow',
37+
500 => 'fg=red',
38+
550 => 'fg=red',
39+
600 => 'fg=white;bg=red',
40+
);
41+
42+
private $options;
43+
private $cloner;
44+
private $outputBuffer;
45+
private $dumper;
46+
47+
/**
48+
* {@inheritdoc}
49+
*/
50+
public function __construct($options = array())
51+
{
52+
// BC Layer
53+
if (!is_array($options)) {
54+
@trigger_error(sprintf('The constructor arguments $format, $dateFormat, $allowInlineLineBreaks, $ignoreEmptyContextAndExtra of %s are deprecated and will be removed in 4.0. Use $options instead.', self::class), E_USER_DEPRECATED);
55+
$args = func_get_args();
56+
$options = array();
57+
if (isset($args[0])) {
58+
$options['format'] = $args[0];
59+
}
60+
if (isset($args[1])) {
61+
$options['date_format'] = $args[1];
62+
}
63+
}
64+
65+
$this->options = array_replace(array(
66+
'format' => self::SIMPLE_FORMAT,
67+
'date_format' => self::SIMPLE_DATE,
68+
'colors' => true,
69+
'multiline' => false,
70+
), $options);
71+
72+
if (!class_exists(VarCloner::class)) {
73+
throw new \RuntimeException('To use the ConsoleFormatter you must install the symfony/var-dumper component.');
74+
}
75+
76+
$this->cloner = new VarCloner();
77+
$this->cloner->addCasters(array(
78+
'*' => array($this, 'castObject'),
79+
));
80+
81+
$this->outputBuffer = fopen('php://memory', 'r+b');
82+
if ($this->options['multiline']) {
83+
$output = $this->outputBuffer;
84+
} else {
85+
$output = array($this, 'echoLine');
86+
}
87+
88+
$this->dumper = new CliDumper($output, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR);
89+
}
2590

2691
/**
2792
* {@inheritdoc}
2893
*/
29-
public function __construct($format = null, $dateFormat = null, $allowInlineLineBreaks = false, $ignoreEmptyContextAndExtra = true)
94+
public function formatBatch(array $records)
3095
{
31-
parent::__construct($format, $dateFormat, $allowInlineLineBreaks, $ignoreEmptyContextAndExtra);
96+
foreach ($records as $key => $record) {
97+
$records[$key] = $this->format($record);
98+
}
99+
100+
return $records;
32101
}
33102

34103
/**
35104
* {@inheritdoc}
36105
*/
37106
public function format(array $record)
38107
{
39-
if ($record['level'] >= Logger::ERROR) {
40-
$record['start_tag'] = '<error>';
41-
$record['end_tag'] = '</error>';
42-
} elseif ($record['level'] >= Logger::NOTICE) {
43-
$record['start_tag'] = '<comment>';
44-
$record['end_tag'] = '</comment>';
45-
} elseif ($record['level'] >= Logger::INFO) {
46-
$record['start_tag'] = '<info>';
47-
$record['end_tag'] = '</info>';
108+
$record = $this->replacePlaceHolder($record);
109+
110+
$levelColor = self::$levelColorMap[$record['level']];
111+
112+
if ($this->options['multiline']) {
113+
$context = $extra = "\n";
114+
} else {
115+
$context = $extra = ' ';
116+
}
117+
$context .= $this->dumpData($record['context']);
118+
$extra .= $this->dumpData($record['extra']);
119+
120+
$formatted = strtr($this->options['format'], array(
121+
'%datetime%' => $record['datetime']->format($this->options['date_format']),
122+
'%start_tag%' => sprintf('<%s>', $levelColor),
123+
'%level_name%' => sprintf('%-9s', $record['level_name']),
124+
'%end_tag%' => '</>',
125+
'%channel%' => $record['channel'],
126+
'%message%' => $this->replacePlaceHolder($record)['message'],
127+
'%context%' => $context,
128+
'%extra%' => $extra,
129+
));
130+
131+
return $formatted;
132+
}
133+
134+
/**
135+
* @internal
136+
*/
137+
public function echoLine($line, $depth, $indentPad)
138+
{
139+
if (-1 !== $depth) {
140+
fwrite($this->outputBuffer, $line);
141+
}
142+
}
143+
144+
/**
145+
* @internal
146+
*/
147+
public function castObject($v, array $a, Stub $s, $isNested)
148+
{
149+
if ($this->options['multiline']) {
150+
return $a;
151+
}
152+
153+
if ($isNested && !$v instanceof \DateTimeInterface) {
154+
$s->cut = -1;
155+
$a = array();
156+
}
157+
158+
return $a;
159+
}
160+
161+
private function replacePlaceHolder(array $record)
162+
{
163+
$message = $record['message'];
164+
165+
if (false === strpos($message, '{')) {
166+
return $record;
167+
}
168+
169+
$context = $record['context'];
170+
171+
$replacements = array();
172+
foreach ($context as $k => $v) {
173+
$replacements['{'.$k.'}'] = sprintf('<comment>%s</>', $this->dumpData($v, false));
174+
}
175+
176+
$record['message'] = strtr($message, $replacements);
177+
178+
return $record;
179+
}
180+
181+
private function dumpData($data, $colors = null)
182+
{
183+
if (null === $colors) {
184+
$this->dumper->setColors($this->options['colors']);
48185
} else {
49-
$record['start_tag'] = '';
50-
$record['end_tag'] = '';
186+
$this->dumper->setColors($colors);
51187
}
52188

53-
return parent::format($record);
189+
if (!$data instanceof Data) {
190+
$data = $this->cloner->cloneVar($data);
191+
}
192+
$data = $data->withRefHandles(false);
193+
$this->dumper->dump($data);
194+
195+
$dump = stream_get_contents($this->outputBuffer, -1, 0);
196+
rewind($this->outputBuffer);
197+
ftruncate($this->outputBuffer, 0);
198+
199+
return rtrim($dump);
54200
}
55201
}

src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,14 @@ protected function write(array $record)
164164
*/
165165
protected function getDefaultFormatter()
166166
{
167-
return new ConsoleFormatter();
167+
if (!$this->output) {
168+
return new ConsoleFormatter();
169+
}
170+
171+
return new ConsoleFormatter(array(
172+
'colors' => $this->output->isDecorated(),
173+
'multiline' => OutputInterface::VERBOSITY_DEBUG <= $this->output->getVerbosity(),
174+
));
168175
}
169176

170177
/**

src/Symfony/Bridge/Monolog/Tests/Handler/ConsoleHandlerTest.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,19 @@ public function testVerbosityMapping($verbosity, $level, $isHandling, array $map
5959

6060
// check that the handler actually outputs the record if it handles it
6161
$levelName = Logger::getLevelName($level);
62+
$levelName = sprintf('%-9s', $levelName);
6263

6364
$realOutput = $this->getMockBuilder('Symfony\Component\Console\Output\Output')->setMethods(array('doWrite'))->getMock();
6465
$realOutput->setVerbosity($verbosity);
66+
if ($realOutput->isDebug()) {
67+
$log = "16:21:54 $levelName [app] My info message\n[]\n[]\n";
68+
} else {
69+
$log = "16:21:54 $levelName [app] My info message [] []\n";
70+
}
6571
$realOutput
6672
->expects($isHandling ? $this->once() : $this->never())
6773
->method('doWrite')
68-
->with("[2013-05-29 16:21:54] app.$levelName: My info message \n", false);
74+
->with($log, false);
6975
$handler = new ConsoleHandler($realOutput, true, $map);
7076

7177
$infoRecord = array(
@@ -143,7 +149,7 @@ public function testWritingAndFormatting()
143149
$output
144150
->expects($this->once())
145151
->method('write')
146-
->with('<info>[2013-05-29 16:21:54] app.INFO:</info> My info message '."\n")
152+
->with("16:21:54 <fg=green>INFO </> <comment>[app]</comment> My info message\n[]\n[]\n")
147153
;
148154

149155
$handler = new ConsoleHandler(null, false);

src/Symfony/Bridge/Monolog/composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
},
2323
"require-dev": {
2424
"symfony/console": "~2.8|~3.0",
25-
"symfony/event-dispatcher": "~2.8|~3.0"
25+
"symfony/event-dispatcher": "~2.8|~3.0",
26+
"symfony/var-dumper": "~3.3"
2627
},
2728
"suggest": {
2829
"symfony/http-kernel": "For using the debugging handlers together with the response life cycle of the HTTP kernel.",

0 commit comments

Comments
 (0)