Skip to content

Persist app bootstrapping logs for logger datacollector #21502

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
<tag name="data_collector" template="@WebProfiler/Collector/logger.html.twig" id="logger" priority="300" />
<tag name="monolog.logger" channel="profiler" />
<argument type="service" id="logger" on-invalid="ignore" />
<argument>%kernel.cache_dir%/%kernel.container_class%</argument>
</service>

<service id="data_collector.time" class="Symfony\Component\HttpKernel\DataCollector\TimeDataCollector" public="false">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,52 @@
</div>
</div>

{% set compilerLogTotal = 0 %}
{% for logs in collector.compilerLogs %}
{% set compilerLogTotal = compilerLogTotal + logs|length %}
{% endfor %}
<div class="tab">
<h3 class="tab-title">Container Compilation<span class="badge">{{ compilerLogTotal }}</span></h3>

<div class="tab-content">
{% if collector.compilerLogs is empty %}
<div class="empty">
<p>There are no compiler log messages.</p>
</div>
{% else %}
<table class="logs">
<thead>
<tr>
<th class="full-width">Class</th>
<th>Messages</th>
</tr>
</thead>

<tbody>
{% for class, logs in collector.compilerLogs %}
<tr class="">
<td class="font-normal">
{% set context_id = 'context-compiler-' ~ loop.index %}

<a class="btn btn-link sf-toggle" data-toggle-selector="#{{ context_id }}" data-toggle-alt-content="{{ class }}">{{ class }}</a>

<div id="{{ context_id }}" class="context sf-toggle-content sf-toggle-hidden">
<ul>
{% for log in logs %}
<li>{{ profiler_dump_log(log.message) }}</li>
{% endfor %}
</ul>
</div>
</td>
<td class="font-normal text-right">{{ logs|length }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
</div>

</div>
{% endif %}
{% endblock %}
Expand Down Expand Up @@ -165,24 +211,24 @@

{% endif %}

<td class="font-normal">{{ helper.render_log_message(category, loop.index, log, is_deprecation) }}</td>
<td class="font-normal">{{ helper.render_log_message(category, loop.index, log) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endmacro %}

{% macro render_log_message(category, log_index, log, is_deprecation = false) %}
{% if is_deprecation %}
{{ log.message }}
{% macro render_log_message(category, log_index, log) %}
{% if log.context.exception.trace is defined %}
{{ profiler_dump_log(log.message, log.context) }}

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

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

<div id="{{ context_id }}" class="context sf-toggle-content sf-toggle-hidden">
{{ profiler_dump(log.context.exception['\0Exception\0trace'], maxDepth=2) }}
{{ profiler_dump(log.context.exception.trace, maxDepth=1) }}
</div>
</span>
{% elseif log.context is defined and log.context is not empty %}
Expand All @@ -198,6 +244,6 @@
</div>
</span>
{% else %}
{{ log.message }}
{{ profiler_dump_log(log.message) }}
{% endif %}
{% endmacro %}
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,7 @@ table.logs .metadata {
#collector-content .sf-dump-key { color: #789339; }
#collector-content .sf-dump-ref { color: #6E6E6E; }
#collector-content .sf-dump-ellipsis { color: #CC7832; max-width: 100em; }
#collector-content .sf-dump-ellipsis-path { max-width: 5em; }

#collector-content .sf-dump {
margin: 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,18 +89,19 @@ public function dumpData(\Twig_Environment $env, Data $data, $maxDepth = 0)
return str_replace("\n</pre", '</pre', rtrim($dump));
}

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

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

$replacements = array();
foreach ($context as $k => $v) {
$k = '{'.twig_escape_filter($env, $k).'}';
$replacements['&quot;'.$k.'&quot;'] = $replacements[$k] = $this->dumpData($env, $v);
$replacements['&quot;<b>'.$k.'</b>&quot;'] = $replacements['&quot;'.$k.'&quot;'] = $replacements[$k] = $this->dumpData($env, $v);
}

return '<span class="dump-inline">'.strtr($message, $replacements).'</span>';
Expand Down
16 changes: 8 additions & 8 deletions src/Symfony/Component/Debug/DebugClassLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ public function loadClass($class)
$name = $refl->getName();

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

$parent = get_parent_class($class);
Expand All @@ -174,7 +174,7 @@ public function loadClass($class)
}

if ($parent && isset(self::$final[$parent])) {
@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);
@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);
}

// Inherit @final annotations
Expand All @@ -186,7 +186,7 @@ public function loadClass($class)
}

if ($parent && isset(self::$finalMethods[$parent][$method->name])) {
@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);
@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);
}

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

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

if (in_array(strtolower($refl->getShortName()), self::$php7Reserved)) {
@trigger_error(sprintf('%s uses a reserved class name (%s) that will break on PHP 7 and higher', $name, $refl->getShortName()), E_USER_DEPRECATED);
@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);
} elseif (preg_match('#\n \* @deprecated (.*?)\r?\n \*(?: @|/$)#s', $refl->getDocComment(), $notice)) {
self::$deprecated[$name] = preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]);
} else {
Expand All @@ -223,7 +223,7 @@ public function loadClass($class)

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

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

foreach ($deprecatedInterfaces as $interface) {
if (!isset($parentInterfaces[$interface])) {
@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);
@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);
}
}
}
Expand Down Expand Up @@ -342,7 +342,7 @@ public function loadClass($class)
if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true)
&& 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false)
) {
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)));
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)));
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ class_exists('Test\\'.__NAMESPACE__.'\\'.$class, true);

$xError = array(
'type' => E_USER_DEPRECATED,
'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.',
'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.',
);

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

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

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

$xError = array(
'type' => E_USER_DEPRECATED,
'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.',
'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".',
);

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

$xError = array(
'type' => E_USER_DEPRECATED,
'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.',
'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".',
);

$this->assertSame($xError, $lastError);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ public function addLogMessage($string)
*/
public function log(CompilerPassInterface $pass, $message)
{
if (false !== strpos($message, "\n")) {
$message = str_replace("\n", "\n".get_class($pass).': ', trim($message));
}

$this->log[] = get_class($pass).': '.$message;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,15 @@
class LoggerDataCollector extends DataCollector implements LateDataCollectorInterface
{
private $logger;
private $containerPathPrefix;

public function __construct($logger = null)
public function __construct($logger = null, $containerPathPrefix = null)
{
if (null !== $logger && $logger instanceof DebugLoggerInterface) {
$this->logger = $logger;
}

$this->containerPathPrefix = $containerPathPrefix;
}

/**
Expand All @@ -47,7 +50,11 @@ public function lateCollect()
{
if (null !== $this->logger) {
$this->data = $this->computeErrorsCount();
$this->data['logs'] = $this->sanitizeLogs($this->logger->getLogs());

$containerDeprecationLogs = $this->getContainerDeprecationLogs();
$this->data['deprecation_count'] += count($containerDeprecationLogs);
$this->data['compiler_logs'] = $this->getContainerCompilerLogs();
$this->data['logs'] = $this->sanitizeLogs(array_merge($this->logger->getLogs(), $containerDeprecationLogs));
$this->data = $this->cloneVar($this->data);
}
}
Expand Down Expand Up @@ -87,6 +94,11 @@ public function countScreams()
return isset($this->data['scream_count']) ? $this->data['scream_count'] : 0;
}

public function getCompilerLogs()
{
return isset($this->data['compiler_logs']) ? $this->data['compiler_logs'] : array();
}

/**
* {@inheritdoc}
*/
Expand All @@ -95,6 +107,44 @@ public function getName()
return 'logger';
}

private function getContainerDeprecationLogs()
{
if (null === $this->containerPathPrefix || !file_exists($file = $this->containerPathPrefix.'Deprecations.log')) {
return array();
}

$stubs = array();
$bootTime = filemtime($file);
$logs = array();
foreach (unserialize(file_get_contents($file)) as $log) {
$log['context'] = array('exception' => new SilencedErrorContext($log['type'], $log['file'], $log['line']));
$log['timestamp'] = $bootTime;
$log['priority'] = 100;
$log['priorityName'] = 'DEBUG';
$log['channel'] = '-';
$log['scream'] = false;
$logs[] = $log;
}

return $logs;
}

private function getContainerCompilerLogs()
{
if (null === $this->containerPathPrefix || !file_exists($file = $this->containerPathPrefix.'Compiler.log')) {
return array();
}

$logs = array();
foreach (file($file, FILE_IGNORE_NEW_LINES) as $log) {
$log = explode(': ', $log, 2);

$logs[$log[0]][] = array('message' => $log[1]);
}

return $logs;
}

private function sanitizeLogs($logs)
{
$sanitizedLogs = array();
Expand All @@ -107,7 +157,7 @@ private function sanitizeLogs($logs)
}

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

if (isset($sanitizedLogs[$errorId])) {
++$sanitizedLogs[$errorId]['errorCount'];
Expand Down
24 changes: 22 additions & 2 deletions src/Symfony/Component/HttpKernel/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -539,12 +539,32 @@ protected function initializeContainer()
$cache = new ConfigCache($this->getCacheDir().'/'.$class.'.php', $this->debug);
$fresh = true;
if (!$cache->isFresh()) {
$container = $this->buildContainer();
if ($this->debug) {
$collectedLogs = array();
$previousHandler = set_error_handler(function ($type, $message, $file, $line) use (&$collectedLogs, &$previousHandler) {
if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) {
return $previousHandler ? $previousHandler($type, $message, $file, $line) : false;
}

$collectedLogs[] = array(
'type' => $type,
'message' => $message,
'file' => $file,
'line' => $line,
);
});
}

try {
$container = null;
$container = $this->buildContainer();
$container->compile();
} finally {
if ($this->debug) {
file_put_contents($this->getCacheDir().'/'.$class.'Compiler.log', implode("\n", $container->getCompiler()->getLog()));
restore_error_handler();

file_put_contents($this->getCacheDir().'/'.$class.'Deprecations.log', serialize($collectedLogs));
file_put_contents($this->getCacheDir().'/'.$class.'Compiler.log', null !== $container ? implode("\n", $container->getCompiler()->getLog()) : '');
}
}
$this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass());
Expand Down
Loading