[HttpKernel] Use VarDumper in the Logs&Events panels of the profiler
This commit is contained in:
parent
41a76494ec
commit
eddecbd112
@ -45,7 +45,7 @@
|
||||
|
||||
{% for dump in collector.getDumps('html') %}
|
||||
<div class="sf-dump sf-reset">
|
||||
<h3>In
|
||||
<span class="metadata">In
|
||||
{% if dump.line %}
|
||||
{% set link = dump.file|file_link(dump.line) %}
|
||||
{% if link %}
|
||||
@ -56,10 +56,8 @@
|
||||
{% else %}
|
||||
{{ dump.name }}
|
||||
{% endif %}
|
||||
<small>line {{ dump.line }}</small>
|
||||
|
||||
<a class="text-small sf-toggle" data-toggle-selector="#sf-trace-{{ loop.index0 }}" data-toggle-alt-content="Hide code">Show code</a>
|
||||
</h3>
|
||||
line <a class="text-small sf-toggle" data-toggle-selector="#sf-trace-{{ loop.index0 }}">{{ dump.line }}</a>:
|
||||
</span>
|
||||
|
||||
<div class="sf-dump-compact hidden" id="sf-trace-{{ loop.index0 }}">
|
||||
<div class="trace">
|
||||
|
@ -3,12 +3,11 @@
|
||||
{% import _self as helper %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% if collector.counterrors or collector.countdeprecations or collector.countscreams %}
|
||||
{% if collector.counterrors or collector.countdeprecations or collector.countwarnings %}
|
||||
{% set icon %}
|
||||
{% set status_color = collector.counterrors ? 'red' : collector.countdeprecations ? 'yellow' : '' %}
|
||||
{% set error_count = collector.counterrors + collector.countdeprecations %}
|
||||
{% set status_color = collector.counterrors ? 'red' : 'yellow' %}
|
||||
{{ include('@WebProfiler/Icon/logger.svg') }}
|
||||
<span class="sf-toolbar-value">{{ error_count }}</span>
|
||||
<span class="sf-toolbar-value">{{ collector.counterrors ?: (collector.countdeprecations + collector.countwarnings) }}</span>
|
||||
{% endset %}
|
||||
|
||||
{% set text %}
|
||||
@ -18,13 +17,13 @@
|
||||
</div>
|
||||
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Deprecated Calls</b>
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.countdeprecations ? 'yellow' }}">{{ collector.countdeprecations|default(0) }}</span>
|
||||
<b>Warnings</b>
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.countwarnings ? 'yellow' }}">{{ collector.countwarnings|default(0) }}</span>
|
||||
</div>
|
||||
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Silenced Errors</b>
|
||||
<span class="sf-toolbar-status">{{ collector.countscreams|default(0) }}</span>
|
||||
<b>Deprecations</b>
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.countdeprecations ? 'yellow' }}">{{ collector.countdeprecations|default(0) }}</span>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
@ -33,12 +32,12 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
<span class="label label-status-{{ collector.counterrors ? 'error' : collector.countdeprecations ? 'warning' }} {{ collector.logs is empty ? 'disabled' }}">
|
||||
<span class="label label-status-{{ collector.counterrors ? 'error' : collector.countdeprecations or collector.countwarnings ? 'warning' }} {{ collector.logs is empty ? 'disabled' }}">
|
||||
<span class="icon">{{ include('@WebProfiler/Icon/logger.svg') }}</span>
|
||||
<strong>Logs</strong>
|
||||
{% if collector.counterrors or collector.countdeprecations %}
|
||||
{% if collector.counterrors or collector.countdeprecations or collector.countwarnings %}
|
||||
<span class="count">
|
||||
<span>{{ collector.counterrors ?: collector.countdeprecations }}</span>
|
||||
<span>{{ collector.counterrors ?: (collector.countdeprecations + collector.countwarnings) }}</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
@ -55,9 +54,9 @@
|
||||
{# sort collected logs in groups #}
|
||||
{% set deprecation_logs, debug_logs, info_and_error_logs, silenced_logs = [], [], [], [] %}
|
||||
{% for log in collector.logs %}
|
||||
{% if log.context.errorCount is defined and log.context.type is defined and log.context.type in ['E_DEPRECATED', 'E_USER_DEPRECATED'] %}
|
||||
{% if log.scream is defined and not log.scream %}
|
||||
{% set deprecation_logs = deprecation_logs|merge([log]) %}
|
||||
{% elseif log.context.scream is defined and log.context.scream == true %}
|
||||
{% elseif log.scream is defined and log.scream %}
|
||||
{% set silenced_logs = silenced_logs|merge([log]) %}
|
||||
{% elseif log.priorityName == 'DEBUG' %}
|
||||
{% set debug_logs = debug_logs|merge([log]) %}
|
||||
@ -68,7 +67,7 @@
|
||||
|
||||
<div class="sf-tabs">
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Info. & Errors <span class="badge">{{ info_and_error_logs|length }}</span></h3>
|
||||
<h3 class="tab-title">Info. & Errors <span class="badge status-{{ collector.counterrors ? 'error' : collector.countwarnings ? 'warning' }}">{{ collector.counterrors ?: info_and_error_logs|length }}</span></h3>
|
||||
|
||||
<div class="tab-content">
|
||||
{% if info_and_error_logs is empty %}
|
||||
@ -84,7 +83,7 @@
|
||||
<div class="tab">
|
||||
{# 'deprecation_logs|length' is not used because deprecations are
|
||||
now grouped and the group count doesn't match the message count #}
|
||||
<h3 class="tab-title">Deprecations <span class="badge">{{ collector.countdeprecations|default(0) }}</span></h3>
|
||||
<h3 class="tab-title">Deprecations <span class="badge status-{{ collector.countdeprecations ? 'warning' }}">{{ collector.countdeprecations|default(0) }}</span></h3>
|
||||
|
||||
<div class="tab-content">
|
||||
{% if deprecation_logs is empty %}
|
||||
@ -112,7 +111,7 @@
|
||||
</div>
|
||||
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Silenced Errors <span class="badge">{{ collector.countscreams|default(0) }}</span></h3>
|
||||
<h3 class="tab-title">Silenced PHP Notices<span class="badge">{{ collector.countscreams|default(0) }}</span></h3>
|
||||
|
||||
<div class="tab-content">
|
||||
{% if silenced_logs is empty %}
|
||||
@ -138,7 +137,7 @@
|
||||
<tr>
|
||||
<th>{{ show_level ? 'Level' : 'Time' }}</th>
|
||||
{% if channel_is_defined %}<th>Channel</th>{% endif %}
|
||||
<th>Message</th>
|
||||
<th class="full-width">Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@ -146,18 +145,24 @@
|
||||
{% for log in logs %}
|
||||
{% set css_class = is_deprecation ? ''
|
||||
: log.priorityName in ['CRITICAL', 'ERROR', 'ALERT', 'EMERGENCY'] ? 'status-error'
|
||||
: log.priorityName in ['NOTICE', 'WARNING'] ? 'status-warning'
|
||||
: log.priorityName == 'WARNING' ? 'status-warning'
|
||||
%}
|
||||
<tr class="{{ css_class }}">
|
||||
<td class="font-normal text-small">
|
||||
<td class="font-normal text-small" nowrap>
|
||||
{% if show_level %}
|
||||
<span class="colored text-bold nowrap">{{ log.priorityName }}</span>
|
||||
<span class="colored text-bold">{{ log.priorityName }}</span>
|
||||
{% endif %}
|
||||
<span class="text-muted nowrap newline">{{ log.timestamp|date('H:i:s') }}</span>
|
||||
<span class="text-muted newline">{{ log.timestamp|date('H:i:s') }}</span>
|
||||
</td>
|
||||
|
||||
{% if channel_is_defined %}
|
||||
<td class="font-normal text-small text-bold nowrap">{{ log.channel }}</td>
|
||||
<td class="font-normal text-small text-bold" nowrap>
|
||||
{{ log.channel }}
|
||||
{% if log.errorCount is defined and log.errorCount > 1 %}
|
||||
<span class="text-muted">({{ log.errorCount }} times)</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
{% endif %}
|
||||
|
||||
<td class="font-normal">{{ helper.render_log_message(category, loop.index, log, is_deprecation) }}</td>
|
||||
@ -168,69 +173,31 @@
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_log_message(category, log_index, log, is_deprecation = false) %}
|
||||
{% if is_deprecation %}
|
||||
{{ log.message }}
|
||||
|
||||
{% if log.context.errorCount is defined and log.context.errorCount > 1 %}
|
||||
<span class="text-small text-bold">({{ log.context.errorCount }} times)</span>
|
||||
{% endif %}
|
||||
|
||||
{% if is_deprecation %}
|
||||
{% set trace = log.context.trace|default([]) %}
|
||||
{% set trace_id = 'sf-call-trace-' ~ category ~ '-' ~ log_index %}
|
||||
|
||||
|
||||
{% if trace %}
|
||||
<button class="btn-link text-small sf-toggle" data-toggle-selector="#{{ trace_id }}" data-toggle-alt-content="Hide stack trace">Show stack trace</button>
|
||||
{% endif %}
|
||||
|
||||
{% for index, call in trace if index > 1 %}
|
||||
{% if index == 2 %}
|
||||
<ul class="sf-call-trace hidden" id="{{ trace_id }}">
|
||||
{% endif %}
|
||||
|
||||
{% if call.class is defined %}
|
||||
{% set from = call.class|abbr_class ~ '::' ~ call.function|abbr_method() %}
|
||||
{% elseif call.function is defined %}
|
||||
{% set from = call.function|abbr_method %}
|
||||
{% elseif call.file is defined %}
|
||||
{% set from = call.file %}
|
||||
{% else %}
|
||||
{% set from = '-' %}
|
||||
{% endif %}
|
||||
|
||||
{% set file_name = (call.file is defined and call.line is defined) ? call.file|replace({'\\': '/'})|split('/')|last %}
|
||||
|
||||
<li>
|
||||
{{ from|raw }}
|
||||
{% if file_name %}
|
||||
<span class="text-small">(called from {{ call.file|format_file(call.line, file_name)|raw }})</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
|
||||
{% if index == trace|length - 1 %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% if log.context is defined and log.context is not empty %}
|
||||
{% set context_id = 'context-' ~ category ~ '-' ~ log_index %}
|
||||
{% set context_dump = profiler_dump(log.context) %}
|
||||
|
||||
<div class="metadata">
|
||||
<strong>Context</strong>:
|
||||
<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>
|
||||
|
||||
{% if context_dump|length > 120 %}
|
||||
{{ context_dump[:120] }} ...
|
||||
|
||||
<a class="btn-link text-small sf-toggle" data-toggle-selector="#{{ context_id }}" data-toggle-alt-content="Hide full context">Show full context</a>
|
||||
|
||||
<div id="{{ context_id }}" class="context">
|
||||
{{ dump(log.context) }}
|
||||
<div id="{{ context_id }}" class="context sf-toggle-content sf-toggle-hidden">
|
||||
{{ profiler_dump(log.context.seek('exception').seek('\0Exception\0trace'), maxDepth=2) }}
|
||||
</div>
|
||||
</span>
|
||||
{% elseif log.context is defined and log.context is not empty %}
|
||||
{{ 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 context">Show context</a>
|
||||
|
||||
<div id="{{ context_id }}" class="context sf-toggle-content sf-toggle-hidden">
|
||||
{{ profiler_dump(log.context, maxDepth=1) }}
|
||||
</div>
|
||||
</span>
|
||||
{% else %}
|
||||
{{ context_dump }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{{ log.message }}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
@ -190,6 +190,9 @@ table tbody ul {
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
@ -806,6 +809,8 @@ tr.status-warning td {
|
||||
.tab-content > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.tab-navigation li .badge.status-warning { background: {{ colors.warning|raw }}; color: #FFF; }
|
||||
.tab-navigation li .badge.status-error { background: {{ colors.error|raw }}; color: #FFF; }
|
||||
|
||||
{# Toggles
|
||||
========================================================================= #}
|
||||
@ -838,32 +843,8 @@ tr.status-warning td {
|
||||
{# Logger panel
|
||||
========================================================================= #}
|
||||
table.logs .metadata {
|
||||
color: #777;
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
table.logs .metadata strong {
|
||||
color: #222;
|
||||
}
|
||||
table.logs .metadata .context {
|
||||
background: #F5F5F5;
|
||||
color: #222;
|
||||
}
|
||||
table.logs .metadata .context pre {
|
||||
margin: 5px 0;
|
||||
padding: 5px 10px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
table.logs .sf-call-stack {
|
||||
margin: 1em 0 1em 1.5em;
|
||||
}
|
||||
table.logs .sf-call-stack li {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
table.logs .sf-call-stack abbr {
|
||||
border: none;
|
||||
}
|
||||
|
||||
{# Doctrine panel
|
||||
@ -889,13 +870,16 @@ table.logs .sf-call-stack abbr {
|
||||
#collector-content .sf-dump samp {
|
||||
{{ mixins.monospace_font|raw }}
|
||||
}
|
||||
#collector-content .sf-dump h3 {
|
||||
font-size: 18px;
|
||||
margin: .5em 0 0;
|
||||
}
|
||||
#collector-content .sf-dump h3 a {
|
||||
#collector-content .sf-dump a {
|
||||
cursor: pointer;
|
||||
}
|
||||
#collector-content .sf-dump pre.sf-dump,
|
||||
#collector-content .sf-dump .trace {
|
||||
border: 1px solid #DDD;
|
||||
background: #FFF;
|
||||
padding: 10px;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
#collector-content pre.sf-dump { color: #CC7832; }
|
||||
#collector-content .sf-dump-str { color: #629755; }
|
||||
@ -924,21 +908,12 @@ table.logs .sf-call-stack abbr {
|
||||
width: 4em;
|
||||
}
|
||||
|
||||
#collector-content .sf-dump .trace {
|
||||
border: 1px solid #DDD;
|
||||
background: #FFF;
|
||||
padding: 10px;
|
||||
margin: 1em 0;
|
||||
}
|
||||
#collector-content .sf-dump .trace {
|
||||
font-size: 12px;
|
||||
}
|
||||
#collector-content .sf-dump .trace code {
|
||||
font-size: 14px;
|
||||
}
|
||||
#collector-content .sf-dump .trace li {
|
||||
margin-bottom: 0;
|
||||
padding: 5px 0;
|
||||
padding: 0;
|
||||
}
|
||||
#collector-content .sf-dump .trace li.selected {
|
||||
background: rgba(255, 255, 153, 0.5);
|
||||
|
@ -71,6 +71,7 @@ class WebProfilerExtension extends \Twig_Extension_Profiler
|
||||
|
||||
return array(
|
||||
new \Twig_SimpleFunction('profiler_dump', $profilerDump, array('is_safe' => array('html'), 'needs_environment' => true)),
|
||||
new \Twig_SimpleFunction('profiler_dump_log', array($this, 'dumpLog'), array('is_safe' => array('html'), 'needs_environment' => true)),
|
||||
);
|
||||
}
|
||||
|
||||
@ -88,6 +89,23 @@ class WebProfilerExtension extends \Twig_Extension_Profiler
|
||||
return str_replace("\n</pre", '</pre', rtrim($dump));
|
||||
}
|
||||
|
||||
public function dumpLog(\Twig_Environment $env, $message, Data $context)
|
||||
{
|
||||
$message = twig_escape_filter($env, $message);
|
||||
|
||||
if (false === strpos($message, '{')) {
|
||||
return '<span class="dump-inline">'.$message.'</span>';
|
||||
}
|
||||
|
||||
$replacements = array();
|
||||
foreach ($context->getRawData()[1] as $k => $v) {
|
||||
$v = '{'.twig_escape_filter($env, $k).'}';
|
||||
$replacements['"'.$v.'"'] = $replacements[$v] = $this->dumpData($env, $context->seek($k));
|
||||
}
|
||||
|
||||
return '<span class="dump-inline">'.strtr($message, $replacements).'</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 3.2, to be removed in 4.0. Use the dumpData() method instead.
|
||||
*/
|
||||
|
@ -29,6 +29,9 @@
|
||||
"symfony/dependency-injection": "~2.8|~3.0",
|
||||
"symfony/stopwatch": "~2.8|~3.0"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/event-dispatcher": "<3.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Bundle\\WebProfilerBundle\\": "" },
|
||||
"exclude-from-classmap": [
|
||||
|
@ -15,6 +15,8 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
use Symfony\Component\Stopwatch\Stopwatch;
|
||||
use Symfony\Component\VarDumper\Caster\ClassStub;
|
||||
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
@ -32,6 +34,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
private $called;
|
||||
private $dispatcher;
|
||||
private $wrappedListeners;
|
||||
private $cloner;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@ -47,6 +50,9 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
$this->logger = $logger;
|
||||
$this->called = array();
|
||||
$this->wrappedListeners = array();
|
||||
if (class_exists(ClassStub::class)) {
|
||||
$this->cloner = new VarCloner();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -345,6 +351,9 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
'pretty' => $class.'::'.$listener[1],
|
||||
);
|
||||
}
|
||||
if (null !== $this->cloner) {
|
||||
$info['data'] = $this->cloner->cloneVar(array(new ClassStub($info['pretty'].'()', $listener)))->seek(0);
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
@ -95,12 +95,16 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase
|
||||
$tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
|
||||
$tdispatcher->addListener('foo', $listener = function () {});
|
||||
|
||||
$listeners = $tdispatcher->getNotCalledListeners();
|
||||
unset($listeners['foo.closure']['data']);
|
||||
$this->assertEquals(array(), $tdispatcher->getCalledListeners());
|
||||
$this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure', 'priority' => 0)), $tdispatcher->getNotCalledListeners());
|
||||
$this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure', 'priority' => 0)), $listeners);
|
||||
|
||||
$tdispatcher->dispatch('foo');
|
||||
|
||||
$this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure', 'priority' => null)), $tdispatcher->getCalledListeners());
|
||||
$listeners = $tdispatcher->getCalledListeners();
|
||||
unset($listeners['foo.closure']['data']);
|
||||
$this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure', 'priority' => null)), $listeners);
|
||||
$this->assertEquals(array(), $tdispatcher->getNotCalledListeners());
|
||||
}
|
||||
|
||||
|
@ -23,24 +23,6 @@ use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
|
||||
*/
|
||||
class LoggerDataCollector extends DataCollector implements LateDataCollectorInterface
|
||||
{
|
||||
private $errorNames = array(
|
||||
E_DEPRECATED => 'E_DEPRECATED',
|
||||
E_USER_DEPRECATED => 'E_USER_DEPRECATED',
|
||||
E_NOTICE => 'E_NOTICE',
|
||||
E_USER_NOTICE => 'E_USER_NOTICE',
|
||||
E_STRICT => 'E_STRICT',
|
||||
E_WARNING => 'E_WARNING',
|
||||
E_USER_WARNING => 'E_USER_WARNING',
|
||||
E_COMPILE_WARNING => 'E_COMPILE_WARNING',
|
||||
E_CORE_WARNING => 'E_CORE_WARNING',
|
||||
E_USER_ERROR => 'E_USER_ERROR',
|
||||
E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
|
||||
E_COMPILE_ERROR => 'E_COMPILE_ERROR',
|
||||
E_PARSE => 'E_PARSE',
|
||||
E_ERROR => 'E_ERROR',
|
||||
E_CORE_ERROR => 'E_CORE_ERROR',
|
||||
);
|
||||
|
||||
private $logger;
|
||||
|
||||
public function __construct($logger = null)
|
||||
@ -94,6 +76,11 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
|
||||
return isset($this->data['deprecation_count']) ? $this->data['deprecation_count'] : 0;
|
||||
}
|
||||
|
||||
public function countWarnings()
|
||||
{
|
||||
return isset($this->data['warning_count']) ? $this->data['warning_count'] : 0;
|
||||
}
|
||||
|
||||
public function countScreams()
|
||||
{
|
||||
return isset($this->data['scream_count']) ? $this->data['scream_count'] : 0;
|
||||
@ -109,47 +96,32 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
|
||||
|
||||
private function sanitizeLogs($logs)
|
||||
{
|
||||
$errorContextById = array();
|
||||
$sanitizedLogs = array();
|
||||
|
||||
foreach ($logs as $log) {
|
||||
if (!$this->isSilencedOrDeprecationErrorLog($log)) {
|
||||
$log['context'] = $this->sanitizeContext($log['context']);
|
||||
$log['context'] = $log['context'] ? $this->cloneVar($log['context']) : $log['context'];
|
||||
$sanitizedLogs[] = $log;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$exception = $log['context']['exception'];
|
||||
$errorId = md5("{$exception->getSeverity()}/{$exception->getLine()}/{$exception->getFile()}".($exception instanceof \Exception ? "\0".$exception->getMessage() : ''), true);
|
||||
|
||||
$context = array(
|
||||
'type' => isset($this->errorNames[$exception->getSeverity()]) ? $this->errorNames[$exception->getSeverity()] : $exception->getSeverity(),
|
||||
'file' => $exception->getFile(),
|
||||
'line' => $exception->getLine(),
|
||||
'errorCount' => 0,
|
||||
if (isset($sanitizedLogs[$errorId])) {
|
||||
++$sanitizedLogs[$errorId]['errorCount'];
|
||||
} else {
|
||||
$log['context'] = $log['context'] ? $this->cloneVar($log['context']) : $log['context'];
|
||||
|
||||
$log += array(
|
||||
'errorCount' => 1,
|
||||
'scream' => $exception instanceof SilencedErrorContext,
|
||||
);
|
||||
|
||||
if ($exception instanceof \Exception) {
|
||||
$context['trace'] = array_map(function ($call) {
|
||||
unset($call['args']);
|
||||
|
||||
return $call;
|
||||
}, $exception->getTrace());
|
||||
}
|
||||
|
||||
$errorId = md5("{$context['type']}/{$context['line']}/{$context['file']}\x00{$log['message']}", true);
|
||||
|
||||
if (!isset($errorContextById[$errorId])) {
|
||||
$errorContextById[$errorId] = $context;
|
||||
}
|
||||
|
||||
$context['errorCount'] = ++$errorContextById[$errorId]['errorCount'];
|
||||
|
||||
$log['context'] = $this->sanitizeContext($context);
|
||||
|
||||
$sanitizedLogs[$errorId] = $log;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($sanitizedLogs);
|
||||
}
|
||||
@ -173,48 +145,12 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
|
||||
return false;
|
||||
}
|
||||
|
||||
private function sanitizeContext($context)
|
||||
{
|
||||
if (is_array($context)) {
|
||||
foreach ($context as $key => $value) {
|
||||
$context[$key] = $this->sanitizeContext($value);
|
||||
}
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
if (is_resource($context)) {
|
||||
return sprintf('Resource(%s)', get_resource_type($context));
|
||||
}
|
||||
|
||||
if ($context instanceof \Exception) {
|
||||
$trace = array_map(function ($call) {
|
||||
unset($call['args']);
|
||||
|
||||
return $call;
|
||||
}, $context->getTrace());
|
||||
|
||||
return array(
|
||||
'class' => get_class($context),
|
||||
'message' => $context->getMessage(),
|
||||
'file' => $context->getFile(),
|
||||
'line' => $context->getLine(),
|
||||
'trace' => $trace,
|
||||
);
|
||||
}
|
||||
|
||||
if (is_object($context)) {
|
||||
return sprintf('Object(%s)', get_class($context));
|
||||
}
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
private function computeErrorsCount()
|
||||
{
|
||||
$count = array(
|
||||
'error_count' => $this->logger->countErrors(),
|
||||
'deprecation_count' => 0,
|
||||
'warning_count' => 0,
|
||||
'scream_count' => 0,
|
||||
'priorities' => array(),
|
||||
);
|
||||
@ -228,6 +164,9 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
|
||||
'name' => $log['priorityName'],
|
||||
);
|
||||
}
|
||||
if ('WARNING' === $log['priorityName']) {
|
||||
++$count['warning_count'];
|
||||
}
|
||||
|
||||
if ($this->isSilencedOrDeprecationErrorLog($log)) {
|
||||
if ($log['context']['exception'] instanceof SilencedErrorContext) {
|
||||
|
@ -13,9 +13,12 @@ namespace Symfony\Component\HttpKernel\Tests\DataCollector;
|
||||
|
||||
use Symfony\Component\Debug\Exception\SilencedErrorContext;
|
||||
use Symfony\Component\HttpKernel\DataCollector\LoggerDataCollector;
|
||||
use Symfony\Component\VarDumper\Cloner\Data;
|
||||
|
||||
class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private static $data;
|
||||
|
||||
/**
|
||||
* @dataProvider getCollectTestData
|
||||
*/
|
||||
@ -25,19 +28,17 @@ class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
$logger->expects($this->once())->method('countErrors')->will($this->returnValue($nb));
|
||||
$logger->expects($this->exactly(2))->method('getLogs')->will($this->returnValue($logs));
|
||||
|
||||
$c = new LoggerDataCollector($logger);
|
||||
// disable cloning the context, to ease fixtures creation.
|
||||
$c = $this->getMockBuilder(LoggerDataCollector::class)
|
||||
->setMethods(array('cloneVar'))
|
||||
->setConstructorArgs(array($logger))
|
||||
->getMock();
|
||||
$c->expects($this->any())->method('cloneVar')->willReturn(self::$data);
|
||||
$c->lateCollect();
|
||||
|
||||
// Remove the trace from the real logs, to ease fixtures creation.
|
||||
$logs = array_map(function ($log) {
|
||||
unset($log['context']['trace'], $log['context']['exception']['trace']);
|
||||
|
||||
return $log;
|
||||
}, $c->getLogs());
|
||||
|
||||
$this->assertEquals('logger', $c->getName());
|
||||
$this->assertEquals($nb, $c->countErrors());
|
||||
$this->assertEquals($expectedLogs, $logs);
|
||||
$this->assertEquals($expectedLogs, $c->getLogs());
|
||||
$this->assertEquals($expectedDeprecationCount, $c->countDeprecations());
|
||||
$this->assertEquals($expectedScreamCount, $c->countScreams());
|
||||
|
||||
@ -48,6 +49,10 @@ class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function getCollectTestData()
|
||||
{
|
||||
if (null === self::$data) {
|
||||
self::$data = new Data(array());
|
||||
}
|
||||
|
||||
yield 'simple log' => array(
|
||||
1,
|
||||
array(array('message' => 'foo', 'context' => array(), 'priority' => 100, 'priorityName' => 'DEBUG')),
|
||||
@ -56,18 +61,10 @@ class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
0,
|
||||
);
|
||||
|
||||
yield 'log with a resource' => array(
|
||||
yield 'log with a context' => array(
|
||||
1,
|
||||
array(array('message' => 'foo', 'context' => array('foo' => fopen(__FILE__, 'r')), 'priority' => 100, 'priorityName' => 'DEBUG')),
|
||||
array(array('message' => 'foo', 'context' => array('foo' => 'Resource(stream)'), 'priority' => 100, 'priorityName' => 'DEBUG')),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
|
||||
yield 'log with an object' => array(
|
||||
1,
|
||||
array(array('message' => 'foo', 'context' => array('foo' => new \stdClass()), 'priority' => 100, 'priorityName' => 'DEBUG')),
|
||||
array(array('message' => 'foo', 'context' => array('foo' => 'Object(stdClass)'), 'priority' => 100, 'priorityName' => 'DEBUG')),
|
||||
array(array('message' => 'foo', 'context' => array('foo' => 'bar'), 'priority' => 100, 'priorityName' => 'DEBUG')),
|
||||
array(array('message' => 'foo', 'context' => self::$data, 'priority' => 100, 'priorityName' => 'DEBUG')),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
@ -84,9 +81,9 @@ class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
array('message' => 'foo2', 'context' => array('exception' => new \ErrorException('deprecated', 0, E_USER_DEPRECATED)), 'priority' => 100, 'priorityName' => 'DEBUG'),
|
||||
),
|
||||
array(
|
||||
array('message' => 'foo3', 'context' => array('exception' => array('file' => __FILE__, 'line' => 82, 'class' => \ErrorException::class, 'message' => 'warning')), 'priority' => 100, 'priorityName' => 'DEBUG'),
|
||||
array('message' => 'foo', 'context' => array('type' => 'E_DEPRECATED', 'file' => __FILE__, 'line' => 83, 'errorCount' => 1, 'scream' => false), 'priority' => 100, 'priorityName' => 'DEBUG'),
|
||||
array('message' => 'foo2', 'context' => array('type' => 'E_USER_DEPRECATED', 'file' => __FILE__, 'line' => 84, 'errorCount' => 1, 'scream' => false), 'priority' => 100, 'priorityName' => 'DEBUG'),
|
||||
array('message' => 'foo3', 'context' => self::$data, 'priority' => 100, 'priorityName' => 'DEBUG'),
|
||||
array('message' => 'foo', 'context' => self::$data, 'priority' => 100, 'priorityName' => 'DEBUG', 'errorCount' => 1, 'scream' => false),
|
||||
array('message' => 'foo2', 'context' => self::$data, 'priority' => 100, 'priorityName' => 'DEBUG', 'errorCount' => 1, 'scream' => false),
|
||||
),
|
||||
2,
|
||||
0,
|
||||
@ -100,8 +97,8 @@ class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
array('message' => 'foo3', 'context' => array('exception' => new SilencedErrorContext(E_USER_WARNING, __FILE__, __LINE__)), 'priority' => 100, 'priorityName' => 'DEBUG'),
|
||||
),
|
||||
array(
|
||||
array('message' => 'foo3', 'context' => array('exception' => array('file' => __FILE__, 'line' => 99, 'class' => \ErrorException::class, 'message' => 'warning')), 'priority' => 100, 'priorityName' => 'DEBUG'),
|
||||
array('message' => 'foo3', 'context' => array('type' => 'E_USER_WARNING', 'file' => __FILE__, 'line' => 100, 'errorCount' => 1, 'scream' => true), 'priority' => 100, 'priorityName' => 'DEBUG'),
|
||||
array('message' => 'foo3', 'context' => self::$data, 'priority' => 100, 'priorityName' => 'DEBUG'),
|
||||
array('message' => 'foo3', 'context' => self::$data, 'priority' => 100, 'priorityName' => 'DEBUG', 'errorCount' => 1, 'scream' => true),
|
||||
),
|
||||
0,
|
||||
1,
|
||||
|
Reference in New Issue
Block a user