{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% import _self as helper %}
{% block toolbar %}
{% if collector.counterrors or collector.countdeprecations or collector.countscreams %}
{% set icon %}
{% set status_color = collector.counterrors ? 'red' : collector.countdeprecations ? 'yellow' : '' %}
{% set error_count = collector.counterrors + collector.countdeprecations + collector.countscreams %}
{{ include('@WebProfiler/Icon/logger.svg') }}
<span class="sf-toolbar-value">{{ error_count }}</span>
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>Errors</b>
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.counterrors ? 'red' }}">{{ collector.counterrors|default(0) }}</span>
</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>
</div>
<div class="sf-toolbar-info-piece">
<b>Silenced Errors</b>
<span class="sf-toolbar-status">{{ collector.countscreams|default(0) }}</span>
</div>
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_color }) }}
{% endif %}
{% endblock %}
{% block menu %}
<span class="label label-status-{{ collector.counterrors ? 'error' : collector.countdeprecations ? 'warning' }} {{ collector.logs is empty ? 'disabled' }}">
<span class="icon">{{ include('@WebProfiler/Icon/logger.svg') }}</span>
<strong>Logs</strong>
{% if collector.counterrors or collector.countdeprecations %}
<span class="count">
<span>{{ collector.counterrors ?: collector.countdeprecations }}</span>
</span>
{% endif %}
</span>
{% endblock %}
{% block panel %}
<h2>Log Messages</h2>
{% if collector.logs is empty %}
<div class="empty">
<p>No log messages available.</p>
</div>
{% else %}
{# sort collected logs in groups #}
{% set deprecation_logs, debug_logs, info_and_error_logs, silenced_logs = [], [], [], [] %}
{% for log in collector.logs %}
{% if log.context.level is defined and log.context.type is defined and log.context.type in [constant('E_DEPRECATED'), constant('E_USER_DEPRECATED')] %}
{% set deprecation_logs = deprecation_logs|merge([log]) %}
{% elseif log.context.scream is defined and log.context.scream == true %}
{% set silenced_logs = silenced_logs|merge([log]) %}
{% elseif log.priorityName == 'DEBUG' %}
{% set debug_logs = debug_logs|merge([log]) %}
{% else %}
{% set info_and_error_logs = info_and_error_logs|merge([log]) %}
{% endif %}
{% endfor %}
<div class="sf-tabs">
<div class="tab">
<h3 class="tab-title">Info. & Errors <span class="badge">{{ info_and_error_logs|length }}</span></h3>
<div class="tab-content">
{% if info_and_error_logs is empty %}
<div class="empty">
<p>There are no log messages of this level.</p>
</div>
{% else %}
{{ helper.render_table(info_and_error_logs, true) }}
{% endif %}
</div>
</div>
<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>
<div class="tab-content">
{% if deprecation_logs is empty %}
<div class="empty">
<p>There are no log messages about deprecated features.</p>
</div>
{% else %}
{{ helper.render_table(deprecation_logs, false, true) }}
{% endif %}
</div>
</div>
<div class="tab">
<h3 class="tab-title">Debug <span class="badge">{{ debug_logs|length }}</span></h3>
<div class="tab-content">
{% if debug_logs is empty %}
<div class="empty">
<p>There are no log messages of this level.</p>
</div>
{% else %}
{{ helper.render_table(debug_logs) }}
{% endif %}
</div>
</div>
<div class="tab">
<h3 class="tab-title">Silenced Errors <span class="badge">{{ collector.countscreams|default(0) }}</span></h3>
<div class="tab-content">
{% if silenced_logs is empty %}
<div class="empty">
<p>There are no log messages of this level.</p>
</div>
{% else %}
{{ helper.render_table(silenced_logs) }}
{% endif %}
</div>
</div>
</div>
{% endif %}
{% endblock %}
{% macro render_table(logs, show_level = false, is_deprecation = false) %}
{% import _self as helper %}
{% set channel_is_defined = (logs|first).channel is defined %}
<table class="logs">
<thead>
<tr>
<th>{{ show_level ? 'Level' : 'Time' }}</th>
{% if channel_is_defined %}<th>Channel</th>{% endif %}
<th>Message</th>
</tr>
</thead>
<tbody>
{% 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'
%}
<tr class="{{ css_class }}">
<td class="font-normal text-small">
{% if show_level %}
<span class="colored text-bold nowrap">{{ log.priorityName }}</span>
{% endif %}
<span class="text-muted nowrap 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>
{% endif %}
<td class="font-normal">{{ helper.render_log_message(loop.index, log, is_deprecation) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endmacro %}
{% macro render_log_message(log_index, log, is_deprecation = false) %}
{{ log.message }}
{% if is_deprecation %}
{% set stack = log.context.stack|default([]) %}
{% set id = 'sf-call-stack-' ~ log_index %}
{% if log.context.errorCount is defined %}
<span class="text-small text-bold">({{ log.context.errorCount }} times)</span>
{% endif %}
{% if stack %}
<button class="btn-link text-small sf-toggle" data-toggle-selector="#{{ id }}" data-toggle-alt-content="Hide stack trace">Show stack trace</button>
{% endif %}
{% for index, call in stack if index > 1 %}
{% if index == 2 %}
<ul class="sf-call-stack hidden" id="{{ 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 == stack|length - 1 %}
</ul>
{% endif %}
{% endfor %}
{% else %}
{% if log.context is defined and log.context is not empty %}
<span class="metadata">
<strong>Context</strong>: {{ log.context|json_encode(64 b-or 256)|replace({
'{"' : '{ "', '"}' : '" }', '":{' : '": {', '":"' : '": "', '","' : '", "'
}) }}
</span>
{% endif %}
{% endif %}
{% endmacro %}
|