feature #24263 Filter logs by level (ro0NL)

This PR was submitted for the 3.4 branch but it was merged into the 4.2-dev branch instead (closes #24263).

Discussion
----------

Filter logs by level

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #... <!-- #-prefixed issue number(s), if any -->
| License       | MIT
| Doc PR        | symfony/symfony-docs#... <!--highly recommended for new features-->

Proposal to filter logs by level. This PR competes with #23247 (but also see #23038) which propose to filter by channel.

<details>
<summary>Before</summary>

![image](https://user-images.githubusercontent.com/1047696/30607022-00536bbe-9d74-11e7-84dd-6427d328f50b.png)

</details>

<details>
<summary>After</summary>

![image](https://user-images.githubusercontent.com/1047696/31036405-6346da12-a56c-11e7-8747-b1ae89c549f2.png)

</details>

From https://github.com/symfony/symfony/pull/23247#issuecomment-322827776

> Adding configuration is always adding complexity for the end user. If we can do otherwise (including doing nothing), i think that might be better. I all depends on the current "brokenness" status.

This avoids that. Also single click; noise gone.

Commits
-------

8f88753381 Filter logs by level
This commit is contained in:
Fabien Potencier 2018-10-10 05:43:59 -07:00
commit 6de1577fc4
6 changed files with 118 additions and 5 deletions

View File

@ -1,6 +1,5 @@
{% set channel_is_defined = (logs|first).channel is defined %}
<table class="logs">
<table class="logs" data-log-levels="Emergency,Alert,Critical,Error,Warning,Notice,Info,Debug" data-default-log-level="Info">
<thead>
<tr>
<th>Level</th>
@ -19,7 +18,7 @@
{% set severity = log.context.exception.severity|default(false) %}
{% set status = severity is constant('E_DEPRECATED') or severity is constant('E_USER_DEPRECATED') ? 'warning' : 'normal' %}
{% endif %}
<tr class="status-{{ status }}">
<tr class="status-{{ status }}" data-log-level="{{ log.priorityName|lower }}">
<td class="text-small" nowrap>
<span class="colored text-bold">{{ log.priorityName }}</span>
<span class="text-muted newline">{{ log.timestamp|date('H:i:s') }}</span>

View File

@ -171,6 +171,53 @@
toggles[i].setAttribute('data-processed', 'true');
}
},
createLogLevels: function() {
document.querySelectorAll('.logs[data-log-levels]').forEach(function (el) {
var bullets = document.createElement('ul'),
levels = el.getAttribute('data-log-levels').toLowerCase().split(','),
defaultLevel = el.hasAttribute('data-default-log-level') ? levels.indexOf(el.getAttribute('data-default-log-level').toLowerCase()) : levels.length - 1;
addClass(bullets, 'log-levels');
el.getAttribute('data-log-levels').split(',').forEach(function (level, i) {
var bullet = document.createElement('li');
bullet.innerText = level;
bullet.setAttribute('data-log-level', String(i));
bullets.appendChild(bullet);
addEventListener(bullet, 'click', function() {
if (i === this.parentNode.querySelectorAll('.active').length - 1) {
return;
}
this.parentNode.querySelectorAll('li').forEach(function (bullet, j) {
if (parseInt(bullet.getAttribute('data-log-level')) <= levels.indexOf(level.toLowerCase())) {
addClass(bullet, 'active');
if (i === j) {
addClass(bullet, 'last-active');
} else {
removeClass(bullet, 'last-active');
}
} else {
removeClass(bullet, 'active');
removeClass(bullet, 'last-active');
}
});
el.querySelectorAll('tr[data-log-level]').forEach(function (row) {
row.style.display = i < levels.indexOf(row.getAttribute('data-log-level')) ? 'none' : '';
});
});
if (i <= defaultLevel) {
addClass(bullet, 'active');
if (i === defaultLevel) {
addClass(bullet, 'last-active');
}
} else {
el.querySelectorAll('tr[data-log-level="'+level.toLowerCase()+'"]').forEach(function (row) {
row.style.display = 'none';
});
}
});
el.parentNode.insertBefore(bullets, el);
});
}
};
})();
@ -178,6 +225,7 @@
Sfjs.addEventListener(document, 'DOMContentLoaded', function() {
Sfjs.createTabs();
Sfjs.createToggles();
Sfjs.createLogLevels();
});
/*]]>*/</script>

View File

@ -125,6 +125,14 @@ header .container { display: flex; justify-content: space-between; }
.trace-as-text .stacktrace { line-height: 1.8; margin: 0 0 15px; white-space: pre-wrap; }
table.logs tr td:last-child { width: 100%; }
.log-levels { width: 100%; margin: 0; padding: 0; display: flex; align-items: center; list-style: none; }
.log-levels li { width: 100%; padding: 3px; margin: 0; cursor: pointer; text-align: center; border: 2px dashed #e0e0e0; border-radius: 5px; color: #888; }
.log-levels li + li { margin-left: 10px; }
.log-levels li.active { background: #eee; color: #666; border-style: solid; border-width: 1px; padding: 4px; border-color: #aaa; }
.log-levels li.last-active { cursor: not-allowed; }
@media (min-width: 575px) {
.hidden-xs-down { display: initial; }
.help-link { margin-left: 30px; }

View File

@ -187,7 +187,7 @@
{% import _self as helper %}
{% set channel_is_defined = (logs|first).channel is defined %}
<table class="logs">
<table class="logs"{% if show_level %} data-log-levels="Emergency,Alert,Critical,Error,Warning,Notice,Info"{% endif %}>
<thead>
<tr>
<th>{{ show_level ? 'Level' : 'Time' }}</th>
@ -202,7 +202,7 @@
: log.priorityName in ['CRITICAL', 'ERROR', 'ALERT', 'EMERGENCY'] ? 'status-error'
: log.priorityName == 'WARNING' ? 'status-warning'
%}
<tr class="{{ css_class }}">
<tr class="{{ css_class }}"{% if show_level %} data-log-level="{{ log.priorityName|lower }}"{% endif %}>
<td class="font-normal text-small" nowrap>
{% if show_level %}
<span class="colored text-bold">{{ log.priorityName }}</span>
@ -225,6 +225,10 @@
{% endfor %}
</tbody>
</table>
{% if show_level %}
<script>Sfjs.createLogLevels();</script>
{% endif %}
{% endmacro %}
{% macro render_log_message(category, log_index, log) %}

View File

@ -682,6 +682,53 @@
toggles[i].setAttribute('data-processed', 'true');
}
},
createLogLevels: function() {
document.querySelectorAll('.logs[data-log-levels]').forEach(function (el) {
var bullets = document.createElement('ul'),
levels = el.getAttribute('data-log-levels').toLowerCase().split(','),
defaultLevel = el.hasAttribute('data-default-log-level') ? levels.indexOf(el.getAttribute('data-default-log-level').toLowerCase()) : levels.length - 1;
addClass(bullets, 'log-levels');
el.getAttribute('data-log-levels').split(',').forEach(function (level, i) {
var bullet = document.createElement('li');
bullet.innerText = level;
bullet.setAttribute('data-log-level', String(i));
bullets.appendChild(bullet);
addEventListener(bullet, 'click', function() {
if (i === this.parentNode.querySelectorAll('.active').length - 1) {
return;
}
this.parentNode.querySelectorAll('li').forEach(function (bullet, j) {
if (parseInt(bullet.getAttribute('data-log-level')) <= levels.indexOf(level.toLowerCase())) {
addClass(bullet, 'active');
if (i === j) {
addClass(bullet, 'last-active');
} else {
removeClass(bullet, 'last-active');
}
} else {
removeClass(bullet, 'active');
removeClass(bullet, 'last-active');
}
});
el.querySelectorAll('tr[data-log-level]').forEach(function (row) {
row.style.display = i < levels.indexOf(row.getAttribute('data-log-level')) ? 'none' : '';
});
});
if (i <= defaultLevel) {
addClass(bullet, 'active');
if (i === defaultLevel) {
addClass(bullet, 'last-active');
}
} else {
el.querySelectorAll('tr[data-log-level="'+level.toLowerCase()+'"]').forEach(function (row) {
row.style.display = 'none';
});
}
});
el.parentNode.insertBefore(bullets, el);
});
}
};
})();

View File

@ -955,6 +955,13 @@ table.logs .metadata {
display: block;
font-size: 12px;
}
table.logs tr td:last-child { width: 100%; }
.log-levels { width: 100%; margin: 0; padding: 0; display: flex; align-items: center; list-style: none; }
.log-levels li { width: 100%; padding: 3px; margin: 0; cursor: pointer; text-align: center; border: 2px dashed #e0e0e0; border-radius: 5px; color: #888; }
.log-levels li + li { margin-left: 10px; }
.log-levels li.active { background: #eee; color: #666; border-style: solid; border-width: 1px; padding: 4px; border-color: #aaa; }
.log-levels li.last-active { cursor: not-allowed; }
{# Doctrine panel
========================================================================= #}