10000 feature #21502 Persist app bootstrapping logs for logger datacollecto… · symfony/symfony@ad86e2d · GitHub
[go: up one dir, main page]

Skip to content

Commit ad86e2d

Browse files
committed
feature #21502 Persist app bootstrapping logs for logger datacollector (ScullWM, nicolas-grekas)
This PR was merged into the 3.3-dev branch. Discussion ---------- Persist app bootstrapping logs for logger datacollector | Q | A | ------------- | --- | Branch? | 3.3 | Bug fix? | no | New feature? | yes | BC breaks? | ? | Deprecations? | no | Tests pass? | yes | Fixed tickets | #21405 | License | MIT Logs generated during the container build are catched by the BufferingLogger with a special flag. They are persist by the LoggerDataCollector and are available in the logger profiler. In the profiler toolbar, the "container build" logs increment the current logs counter (even if the container was previously built). <img width="540" alt="capture d ecran 2017-02-01 a 20 56 40" src="https://cloud.githubusercontent.com/assets/1017746/22523826/0bc12e4a-e8c1-11e6-830f-7f6238ea7423.png"> <img width="1022" alt="capture d ecran 2017-02-01 a 20 57 55" src="https://cloud.githubusercontent.com/assets/1017746/22523859/2c48a698-e8c1-11e6-9bdb-d85f3e692938.png"> The BufferingLogger now require the cachePath and the filesystem to persist a (unique) container build logs. If the current workflow is ok, I will update the test coverage (actually they fail). Maybe we can display the appDevDebugProjectContainerCompiler.log content in that logger profile. Commits ------- 2fd18b5 [VarDumper] Fine tune dumping log messages ce3ef6a Persist app bootstrapping logs for logger datacollector
2 parents f9bb4dc + 2fd18b5 commit ad86e2d

File tree

18 files changed

+288
-62
lines changed

18 files changed

+288
-62
lines changed

src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
<tag name="data_collector" template="@WebProfiler/Collector/logger.html.twig" id="logger" priority="300" />
3333
<tag name="monolog.logger" channel="profiler" />
3434
<argument type="service" id="logger" on-invalid="ignore" />
35+
<argument>%kernel.cache_dir%/%kernel.container_class%</argument>
3536
</service>
3637

3738
<service id="data_collector.time" class="Symfony\Component\HttpKernel\DataCollector\TimeDataCollector" public="false">

src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,52 @@
124124
</div>
125125
</div>
126126

127+
{% set compilerLogTotal = 0 %}
128+
{% for logs in collector.compilerLogs %}
129+
{% set compilerLogTotal = compilerLogTotal + logs|length %}
130+
{% endfor %}
131+
<div class="tab">
132+
<h3 class="tab-title">Container Compilation<span class="badge">{{ compilerLogTotal }}</span></h3>
133+
134+
<div class="tab-content">
135+
{% if collector.compilerLogs is empty %}
136+
<div class="empty">
137+
<p>There are no compiler log messages.</p>
138+
</div>
139+
{% else %}
140+
<table class="logs">
141+
<thead>
142+
<tr>
143+
<th class="full-width">Class</th>
144+
<th>Messages</th>
145+
</tr>
146+
</thead>
147+
148+
<tbody>
149+
{% for class, logs in collector.compilerLogs %}
150+
<tr class="">
151+
<td class="font-normal">
152+
{% set context_id = 'context-compiler-' ~ loop.index %}
153+
154+
<a class="btn btn-link sf-toggle" data-toggle-selector="#{{ context_id }}" data-toggle-alt-content="{{ class }}">{{ class }}</a>
155+
156+
<div id="{{ context_id }}" class="context sf-toggle-content sf-toggle-hidden">
157+
<ul>
158+
{% for log in logs %}
159+
<li>{{ profiler_dump_log(log.message) }}</li>
160+
{% endfor %}
161+
</ul>
162+
</div>
163+
</td>
164+
<td class="font-normal text-right">{{ logs|length }}</td>
165+
</tr>
166+
{% endfor %}
167+
</tbody>
168+
</table>
169+
{% endif %}
170+
</div>
171+
</div>
172+
127173
</div>
128174
{% endif %}
129175
{% endblock %}
@@ -165,24 +211,24 @@
165211

166212
{% endif %}
167213

168-
<td class="font-normal">{{ helper.render_log_message(category, loop.index, log, is_deprecation) }}</td>
214+
<td class="font-normal">{{ helper.render_log_message(category, loop.index, log) }}</td>
169215
</tr>
170216
{% endfor %}
171217
</tbody>
172218
</table>
173219
{% endmacro %}
174220

175-
{% macro render_log_message(category, log_index, log, is_deprecation = false) %}
176-
{% if is_deprecation %}
177-
{{ log.message }}
221+
{% macro render_log_message(category, log_index, log) %}
222+
{% if log.context.exception.trace is defined %}
223+
{{ profiler_dump_log(log.message, log.context) }}
178224

179225
{% set context_id = 'context-' ~ category ~ '-' ~ log_index %}
180226

181227
<span class="metadata">
182228
<a class="btn btn-link text-small sf-toggle" data-toggle-selector="#{{ context_id }}" data-toggle-alt-content="Hide trace">Show trace</a>
183229

184230
<div id="{{ context_id }}" class="context sf-toggle-content sf-toggle-hidden">
185-
{{ profiler_dump(log.context.exception['\0Exception\0trace'], maxDepth=2) }}
231+
{{ profiler_dump(log.context.exception.trace, maxDepth=1) }}
186232
</div>
187233
</span>
188234
{% elseif log.context is defined and log.context is not empty %}
@@ -198,6 +244,6 @@
198244
</div>
199245
</span>
200246
{% else %}
201-
{{ log.message }}
247+
{{ profiler_dump_log(log.message) }}
202248
{% endif %}
203249
{% endmacro %}

src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,7 @@ table.logs .metadata {
913913
#collector-content .sf-dump-key { color: #789339; }
914914
#collector-content .sf-dump-ref { color: #6E6E6E; }
915915
#collector-content .sf-dump-ellipsis { color: #CC7832; max-width: 100em; }
916+
#collector-content .sf-dump-ellipsis-path { max-width: 5em; }
916917

917918
#collector-content .sf-dump {
918919
margin: 0;

src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,18 +89,19 @@ public function dumpData(\Twig_Environment $env, Data $data, $maxDepth = 0)
8989
return str_replace("\n</pre", '</pre', rtrim($dump));
9090
}
9191

92-
public function dumpLog(\Twig_Environment $env, $message, Data $context)
92+
public function dumpLog(\Twig_Environment $env, $message, Data $context = null)
9393
{
9494
$message = twig_escape_filter($env, $message);
95+
$message = preg_replace('/&quot;(.*?)&quot;/', '&quot;<b>$1</b>&quot;', $message);
9596

96-
if (false === strpos($message, '{')) {
97+
if (null === $context || false === strpos($message, '{')) {
9798
return '<span class="dump-inline">'.$message.'</span>';
9899
}
99100

100101
$replacements = array();
101102
foreach ($context as $k => $v) {
102103
$k = '{'.twig_escape_filter($env, $k).'}';
103-
$replacements['&quot;'.$k.'&quot;'] = $replacements[$k] = $this->dumpData($env, $v);
104+
$replacements['&quot;<b>'.$k.'</b>&quot;'] = $replacements['&quot;'.$k.'&quot;'] = $replacements[$k] = $this->dumpData($env, $v);
104105
}
105106

106107
return '<span class="dump-inline">'.strtr($message, $replacements).'</span>';

src/Symfony/Component/Debug/DebugClassLoader.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ public function loadClass($class)
162162
$name = $refl->getName();
163163

164164
if ($name !== $class && 0 === strcasecmp($name, $class)) {
165-
throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: %s vs %s', $class, $name));
165+
throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: "%s" vs "%s".', $class, $name));
166166
}
167167

168168
$parent = get_parent_class($class);
@@ -174,7 +174,7 @@ public function loadClass($class)
174174
}
175175

176176
if ($parent && isset(self::$final[$parent])) {
177-
@trigger_error(sprintf('The %s class is considered final%s. It may change without further notice as of its next major version. You should not extend it from %s.', $parent, self::$final[$parent], $name), E_USER_DEPRECATED);
177+
@trigger_error(sprintf('The "%s" class is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $parent, self::$final[$parent], $name), E_USER_DEPRECATED);
178178
}
179179

180180
// Inherit @final annotations
@@ -186,7 +186,7 @@ public function loadClass($class)
186186
}
187187

188188
if ($parent && isset(self::$finalMethods[$parent][$method->name])) {
189-
@trigger_error(sprintf('%s It may change without further notice as of its next major version. You should not extend it from %s.', self::$finalMethods[$parent][$method->name], $name), E_USER_DEPRECATED);
189+
@trigger_error(sprintf('%s It may change without further notice as of its next major version. You should not extend it from "%s".', self::$finalMethods[$parent][$meth D7AE od->name], $name), E_USER_DEPRECATED);
190190
}
191191

192192
$doc = $method->getDocComment();
@@ -196,13 +196,13 @@ public function loadClass($class)
196196

197197
if (preg_match('#\n\s+\* @final(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$)#s', $doc, $notice)) {
198198
$message = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : '';
199-
self::$finalMethods[$name][$method->name] = sprintf('The %s::%s() method is considered final%s.', $name, $method->name, $message);
199+
self::$finalMethods[$name][$method->name] = sprintf('The "%s::%s()" method is considered final%s.', $name, $method->name, $message);
200200
}
201201
}
202202
}
203203

204204
if (in_array(strtolower($refl->getShortName()), self::$php7Reserved)) {
205-
@trigger_error(sprintf('%s uses a reserved class name (%s) that will break on PHP 7 and higher', $name, $refl->getShortName()), E_USER_DEPRECATED);
205+
@trigger_error(sprintf('The "%s" class uses the reserved name "%s", it will break on PHP 7 and higher', $name, $refl->getShortName()), E_USER_DEPRECATED);
206206
} elseif (preg_match('#\n \* @deprecated (.*?)\r?\n \*(?: @|/$)#s', $refl->getDocComment(), $notice)) {
207207
self::$deprecated[$name] = preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]);
208208
} else {
@@ -223,7 +223,7 @@ public function loadClass($class)
223223

224224
if (!$parent || strncmp($ns, $parent, $len)) {
225225
if ($parent && isset(self::$deprecated[$parent]) && strncmp($ns, $parent, $len)) {
226-
@trigger_error(sprintf('The %s class extends %s that is deprecated %s', $name, $parent, self::$deprecated[$parent]), E_USER_DEPRECATED);
226+
@trigger_error(sprintf('The "%s" class extends "%s" that is deprecated %s', $name, $parent, self::$deprecated[$parent]), E_USER_DEPRECATED);
227227
}
228228

229229
$parentInterfaces = array();
@@ -245,7 +245,7 @@ public function loadClass($class)
245245

246246
foreach ($deprecatedInterfaces as $interface) {
247247
if (!isset($parentInterfaces[$interface])) {
248-
@trigger_error(sprintf('The %s %s %s that is deprecated %s', $name, $refl->isInterface() ? 'interface extends' : 'class implements', $interface, self::$deprecated[$interface]), E_USER_DEPRECATED);
248+
@trigger_error(sprintf('The "%s" %s "%s" that is deprecated %s', $name, $refl->isInterface() ? 'interface extends' : 'class implements', $interface, self::$deprecated[$interface]), E_USER_DEPRECATED);
249249
}
250250
}
251251
}
@@ -342,7 +342,7 @@ public function loadClass($class)
342342
if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true)
343343
&& 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false)
344344
) {
345-
throw new \RuntimeException(sprintf('Case mismatch between class and real file names: %s vs %s in %s', substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1)));
345+
throw new \RuntimeException(sprintf('Case mismatch between class and real file names: "%s" vs "%s" in "%s".', substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1)));
346346
}
347347
}
348348

src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ class_exists('Test\\'.__NAMESPACE__.'\\'.$class, true);
185185

186186
$xError = array(
187187
'type' => E_USER_DEPRECATED,
188-
'message' => 'The Test\Symfony\Component\Debug\Tests\\'.$class.' class '.$type.' Symfony\Component\Debug\Tests\Fixtures\\'.$super.' that is deprecated but this is a test deprecation notice.',
188+
'message' => 'The "Test\Symfony\Component\Debug\Tests\\'.$class.'" class '.$type.' "Symfony\Component\Debug\Tests\Fixtures\\'.$super.'" that is deprecated but this is a test deprecation notice.',
189189
);
190190

191191
$this->assertSame($xError, $lastError);
@@ -263,7 +263,7 @@ class_exists('Test\\'.__NAMESPACE__.'\\Float', true);
263263

264264
$xError = array(
265265
'type' => E_USER_DEPRECATED,
266-
'message' => 'Test\Symfony\Component\Debug\Tests\Float uses a reserved class name (Float) that will break on PHP 7 and higher',
266+
'message' => 'The "Test\Symfony\Component\Debug\Tests\Float" class uses the reserved name "Float", it will break on PHP 7 and higher',
267267
);
268268

269269
$this->assertSame($xError, $lastError);
@@ -285,7 +285,7 @@ class_exists('Test\\'.__NAMESPACE__.'\\ExtendsFinalClass', true);
285285

286286
$xError = array(
287287
'type' => E_USER_DEPRECATED,
288-
'message' => 'The Symfony\Component\Debug\Tests\Fixtures\FinalClass class is considered final since version 3.3. It may change without further notice as of its next major version. You should not extend it from Test\Symfony\Component\Debug\Tests\ExtendsFinalClass.',
288+
'message' => 'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass" class is considered final since version 3.3. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass".',
289289
);
290290

291291
$this->assertSame($xError, $lastError);
@@ -307,7 +307,7 @@ class_exists(__NAMESPACE__.'\\Fixtures\\ExtendedFinalMethod', true);
307307

308308
$xError = array(
309309
'type' => E_USER_DEPRECATED,
310-
'message' => 'The Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod() method is considered final since version 3.3. It may change without further notice as of its next major version. You should not extend it from Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod.',
310+
'message' => 'The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod()" method is considered final since version 3.3. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod".',
311311
);
312312

313313
$this->assertSame($xError, $lastError);

src/Symfony/Component/DependencyInjection/Compiler/Compiler.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ public function addLogMessage($string)
114114
*/
115115
public function log(CompilerPassInterface $pass, $message)
116116
{
117+
if (false !== strpos($message, "\n")) {
118+
$message = str_replace("\n", "\n".get_class($pass).': ', trim($message));
119+
}
120+
117121
$this->log[] = get_class($pass).': '.$message;
118122
}
119123

src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,15 @@
2424
class LoggerDataCollector extends DataCollector implements LateDataCollectorInterface
2525
{
2626
private $logger;
27+
private $containerPathPrefix;
2728

28-
public function __construct($logger = null)
29+
public function __construct($logger = null, $containerPathPrefix = null)
2930
{
3031
if (null !== $logger && $logger instanceof DebugLoggerInterface) {
3132
$this->logger = $logger;
3233
}
34+
35+
$this->containerPathPrefix = $containerPathPrefix;
3336
}
3437

3538
/**
@@ -47,7 +50,11 @@ public function lateCollect()
4750
{
4851
if (null !== $this->logger) {
4952
$this->data = $this->computeErrorsCount();
50-
$this->data['logs'] = $this->sanitizeLogs($this->logger->getLogs());
53+
54+
$containerDeprecationLogs = $this->getContainerDeprecationLogs();
55+
$this->data['deprecation_count'] += count($containerDeprecationLogs);
56+
$this->data['compiler_logs'] = $this->getContainerCompilerLogs();
57+
$this->data['logs'] = $this->sanitizeLogs(array_merge($this->logger->getLogs(), $containerDeprecationLogs));
5158
$this->data = $this->cloneVar($this->data);
5259
}
5360
}
@@ -87,6 +94,11 @@ public function countScreams()
8794
return isset($this->data['scream_count']) ? $this->data['scream_count'] : 0;
8895
}
8996

97+
public function getCompilerLogs()
98+
{
99+
return isset($this->data['compiler_logs']) ? $this->data['compiler_logs'] : array();
100+
}
101+
90102
/**
91103
* {@inheritdoc}
92104
*/
@@ -95,6 +107,44 @@ public function getName()
95107
return 'logger';
96108
}
97109

110+
private function getContainerDeprecationLogs()
111+
{
112+
if (null === $this->containerPathPrefix || !file_exists($file = $this->containerPathPrefix.'Deprecations.log')) {
113+
return array();
114+
}
115+
116+
$stubs = array();
117+
$bootTime = filemtime($file);
118+
$logs = array();
119+
foreach (unserialize(file_get_contents($file)) as $log) {
120+
$log['context'] = array('exception' => new SilencedErrorContext($log['type'], $log['file'], $log['line']));
121+
$log['timestamp'] = $bootTime;
122+
$log['priority'] = 100;
123+
$log['priorityName'] = 'DEBUG';
124+
$log['channel'] = '-';
125+
$log['scream'] = false;
126+
$logs[] = $log;
127+
}
128+
129+
return $logs;
130+
}
131+
132+
private function getContainerCompilerLogs()
133+
{
134+
if (null === $this->containerPathPrefix || !file_exists($file = $this->containerPathPrefix.'Compiler.log')) {
135+
return array();
136+
}
137+
138+
$logs = array();
139+
foreach (file($file, FILE_IGNORE_NEW_LINES) as $log) {
140+
$log = explode(': ', $log, 2);
141+
142+
$logs[$log[0]][] = array('message' => $log[1]);
143+
}
144+
145+
return $logs;
146+
}
147+
98148
private function sanitizeLogs($logs)
99149
{
100150
$sanitizedLogs = array();
@@ -107,7 +157,7 @@ private function sanitizeLogs($logs)
107157
}
108158

109159
$exception = $log['context']['exception'];
110-
$errorId = md5("{$exception->getSeverity()}/{$exception->getLine()}/{$exception->getFile()}".($exception instanceof \Exception ? "\0".$exception->getMessage() : ''), true);
160+
$errorId = md5("{$exception->getSeverity()}/{$exception->getLine()}/{$exception->getFile()}\0{$log['message']}", true);
111161

112162
if (isset($sanitizedLogs[$errorId])) {
113163
++$sanitizedLogs[$errorId]['errorCount'];

src/Symfony/Component/HttpKernel/Kernel.php

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -539,12 +539,32 @@ protected function initializeContainer()
539539
$cache = new ConfigCache($this->getCacheDir().'/'.$class.'.php', $this->debug);
540540
$fresh = true;
541541
if (!$cache->isFresh()) {
542-
$container = $this->buildContainer();
542+
if ($this->debug) {
543+
$collectedLogs = array();
544+
$previousHandler = set_error_handler(function ($type, $message, $file, $line) use (&$collectedLogs, &$previousHandler) {
545+
if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) {
546+
return $previousHandler ? $previousHandler($type, $message, $file, $line) : false;
547+
}
548+
549+
$collectedLogs[] = array(
550+
'type' => $type,
551+
'message' => $message,
552+
'file' => $file,
553+
'line' => $line,
554+
);
555+
});
556+
}
557+
543558
try {
559+
$container = null;
560+
$container = $this->buildContainer();
544561
$container->compile();
545562
} finally {
546563
if ($this->debug) {
547-
file_put_contents($this->getCacheDir().'/'.$class.'Compiler.log', implode("\n", $container->getCompiler()->getLog()));
564+
restore_error_handler();
565+
566+
file_put_contents($this->getCacheDir().'/'.$class.'Deprecations.log', serialize($collectedLogs));
567+
file_put_contents($this->getCacheDir().'/'.$class.'Compiler.log', null !== $container ? implode("\n", $container->getCompiler()->getLog()) : '');
548568
}
549569
}
550570
$this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass());

0 commit comments

Comments
 (0)
0