Integrate bare-bone filter hit stats in the logger

Related issue:
- https://github.com/gorhill/uBlock/issues/983
- https://github.com/gorhill/uBlock/issues/1353

The current implementation reports statistics for all
static filters, and the presentation/featureset is
intentionally minimal: *Do not open issues about this.*
It's still a work in progress and it will be worked on
slowly and thoughtfully over time and as time allows.

Pausing the logger will not pause the collation of
filter hit statistics, thus it is possible to lower
the logger overhead by pausing logger output without
losing filter hit collation.
This commit is contained in:
Raymond Hill 2019-05-24 11:18:39 -04:00
parent e4681d0250
commit 26708b37c1
No known key found for this signature in database
GPG key ID: 25E1490B761470C2
5 changed files with 172 additions and 51 deletions

View file

@ -44,6 +44,9 @@
width: 1em;
}
.fa-icon > .fa-icon_bar-chart {
width: calc(1em * 2048 / 1792);
}
.fa-icon > .fa-icon_eraser,
.fa-icon > .fa-icon_film {
width: calc(1em * 1920 / 1792);

View file

@ -829,31 +829,25 @@ body.colorBlind #netFilteringDialog > div.panes > .dynamic .entry > .action > .
margin-bottom: 0;
}
#loggerSettingsDialog {
#loggerStatsDialog .sortedEntries {
display: flex;
flex-direction: column;
font-size: smaller;
}
#loggerSettingsDialog > div {
padding-bottom: 1em;
#loggerStatsDialog .sortedEntries > div {
display: flex;
}
#loggerSettingsDialog > div:last-of-type {
padding-bottom: 0;
#loggerStatsDialog .sortedEntries > div > span:first-of-type {
flex-grow: 0;
flex-shrink: 0;
padding: 0 2em 0 0;
text-align: right;
width: 3em;
}
#loggerSettingsDialog ul {
padding: 0;
}
body[dir="ltr"] #loggerSettingsDialog ul {
padding-left: 2em;
}
body[dir="rtl"] #loggerSettingsDialog ul {
padding-right: 2em;
}
#loggerSettingsDialog li {
list-style-type: none;
margin: 0.5em 0 0 0;
}
#loggerSettingsDialog input {
max-width: 6em;
#loggerStatsDialog .sortedEntries > div > span:last-of-type {
flex-grow: 1;
flex-shrink: 1;
white-space: pre;
}
#loggerExportDialog {
@ -890,6 +884,33 @@ body[dir="rtl"] #loggerSettingsDialog ul {
white-space: pre;
}
#loggerSettingsDialog {
display: flex;
flex-direction: column;
}
#loggerSettingsDialog > div {
padding-bottom: 1em;
}
#loggerSettingsDialog > div:last-of-type {
padding-bottom: 0;
}
#loggerSettingsDialog ul {
padding: 0;
}
body[dir="ltr"] #loggerSettingsDialog ul {
padding-left: 2em;
}
body[dir="rtl"] #loggerSettingsDialog ul {
padding-right: 2em;
}
#loggerSettingsDialog li {
list-style-type: none;
margin: 0.5em 0 0 0;
}
#loggerSettingsDialog input {
max-width: 6em;
}
.hide {
display: none !important;
}

View file

@ -27,6 +27,7 @@ License - https://github.com/FortAwesome/Font-Awesome/tree/a8386aae19e200ddb0f68
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display: none;">
<defs>
<symbol id="angle-up" viewBox="0 0 998 582"><path d="m 998,499 q 0,13 -10,23 l -50,50 q -10,10 -23,10 -13,0 -23,-10 L 499,179 106,572 Q 96,582 83,582 70,582 60,572 L 10,522 Q 0,512 0,499 0,486 10,476 L 476,10 q 10,-10 23,-10 13,0 23,10 l 466,466 q 10,10 10,23 z"/></symbol>
<symbol id="bar-chart" viewBox="0 0 2048 1536"><path d="m 640,768 0,512 -256,0 0,-512 256,0 z m 384,-512 0,1024 -256,0 0,-1024 256,0 z m 1024,1152 0,128 L 0,1536 0,0 l 128,0 0,1408 1920,0 z m -640,-896 0,768 -256,0 0,-768 256,0 z m 384,-384 0,1152 -256,0 0,-1152 256,0 z"/></symbol>
<symbol id="bolt" viewBox="0 0 896 1664"><path d="m 885.08696,438 q 18,20 7,44 l -540,1157 q -13,25 -42,25 -4,0 -14,-2 -17,-5 -25.5,-19 -8.5,-14 -4.5,-30 l 197,-808 -406,101 q -4,1 -12,1 -18,0 -31,-11 Q -3.9130435,881 1.0869565,857 L 202.08696,32 q 4,-14 16,-23 12,-9 28,-9 l 328,0 q 19,0 32,12.5 13,12.5 13,29.5 0,8 -5,18 l -171,463 396,-98 q 8,-2 12,-2 19,0 34,15 z"/></symbol>
<symbol id="clipboard" viewBox="0 0 1792 1792"><path d="m 768,1664 896,0 0,-640 -416,0 q -40,0 -68,-28 -28,-28 -28,-68 l 0,-416 -384,0 0,1152 z m 256,-1440 0,-64 q 0,-13 -9.5,-22.5 Q 1005,128 992,128 l -704,0 q -13,0 -22.5,9.5 Q 256,147 256,160 l 0,64 q 0,13 9.5,22.5 9.5,9.5 22.5,9.5 l 704,0 q 13,0 22.5,-9.5 9.5,-9.5 9.5,-22.5 z m 256,672 299,0 -299,-299 0,299 z m 512,128 0,672 q 0,40 -28,68 -28,28 -68,28 l -960,0 q -40,0 -68,-28 -28,-28 -28,-68 l 0,-160 -544,0 Q 56,1536 28,1508 0,1480 0,1440 L 0,96 Q 0,56 28,28 56,0 96,0 l 1088,0 q 40,0 68,28 28,28 28,68 l 0,328 q 21,13 36,28 l 408,408 q 28,28 48,76 20,48 20,88 z"/></symbol>
<symbol id="code" viewBox="0 0 1830 1373"><path d="m 572,1125.5 -50,50 q -10,10 -23,10 -13,0 -23,-10 l -466,-466 q -10,-10 -10,-23 0,-13 10,-23 l 466,-466 q 10,-10 23,-10 13,0 23,10 l 50,50 q 10,10 10,23 0,13 -10,23 l -393,393 393,393 q 10,10 10,23 0,13 -10,23 z M 1163,58.476203 790,1349.4762 q -4,13 -15.5,19.5 -11.5,6.5 -23.5,2.5 l -62,-17 q -13,-4 -19.5,-15.5 -6.5,-11.5 -2.5,-24.5 L 1040,23.5 q 4,-13 15.5,-19.5 11.5,-6.5 23.5,-2.5 l 62,17 q 13,4 19.5,15.5 6.5,11.5 2.5,24.5 z m 657,651 -466,466 q -10,10 -23,10 -13,0 -23,-10 l -50,-50 q -10,-10 -10,-23 0,-13 10,-23 l 393,-393 -393,-393 q -10,-10 -10,-23 0,-13 10,-23 l 50,-50 q 10,-10 23,-10 13,0 23,10 l 466,466 q 10,10 10,23 0,13 -10,23 z"/></symbol>

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View file

@ -263,6 +263,10 @@ const processLoggerEntries = function(response) {
for ( const entry of entries ) {
const unboxed = JSON.parse(entry);
if ( unboxed.filter instanceof Object ){
loggerStats.processFilter(unboxed.filter);
}
if ( netInspectorPaused ) { continue; }
const parsed = parseLogEntry(unboxed);
if (
parsed.tabId !== undefined &&
@ -384,7 +388,7 @@ const parseLogEntry = function(details) {
/******************************************************************************/
const viewPort = (function() {
const viewPort = (( ) => {
const vwRenderer = document.getElementById('vwRenderer');
const vwScroller = document.getElementById('vwScroller');
const vwVirtualContent = document.getElementById('vwVirtualContent');
@ -802,14 +806,14 @@ const viewPort = (function() {
/******************************************************************************/
const updateCurrentTabTitle = (function() {
const updateCurrentTabTitle = (( ) => {
const i18nCurrentTab = vAPI.i18n('loggerCurrentTab');
return function() {
const select = uDom.nodeFromId('pageSelector');
if ( select.value !== '_' || activeTabId === 0 ) { return; }
const opt0 = select.querySelector('[value="_"]');
const opt1 = select.querySelector('[value="' + activeTabId + '"]');
const opt1 = select.querySelector(`[value="${activeTabId}"]`);
let text = i18nCurrentTab;
if ( opt1 !== null ) {
text += ' / ' + opt1.textContent;
@ -934,9 +938,7 @@ const onLogBufferRead = function(response) {
pageSelectorFromURLHash();
}
if ( netInspectorPaused === false ) {
processLoggerEntries(response);
}
processLoggerEntries(response);
// Synchronize DOM with sent logger data
document.body.classList.toggle(
@ -955,7 +957,7 @@ const onLogBufferRead = function(response) {
/******************************************************************************/
const readLogBuffer = (function() {
const readLogBuffer = (( ) => {
let timer;
const readLogBufferNow = function() {
@ -1011,7 +1013,7 @@ const pageSelectorChanged = function() {
pageSelectorFromURLHash();
};
const pageSelectorFromURLHash = (function() {
const pageSelectorFromURLHash = (( ) => {
let lastHash;
let lastSelectedTabId;
@ -1806,7 +1808,7 @@ const reloadTab = function(ev) {
/******************************************************************************/
/******************************************************************************/
const rowFilterer = (function() {
const rowFilterer = (( ) => {
const userFilters = [];
const builtinFilters = [];
@ -2017,7 +2019,7 @@ const rowFilterer = (function() {
// - Max number of entry per distinct tab
// - Max entry age
const rowJanitor = (function() {
const rowJanitor = (( ) => {
const tabIdToDiscard = new Set();
const tabIdToLoadCountMap = new Map();
const tabIdToEntryCountMap = new Map();
@ -2226,7 +2228,7 @@ const toggleVCompactView = function() {
/******************************************************************************/
const popupManager = (function() {
const popupManager = (( ) => {
let realTabId = 0;
let popup = null;
let popupObserver = null;
@ -2316,7 +2318,95 @@ const popupManager = (function() {
/******************************************************************************/
(function() {
// Filter hit stats' MVP ("minimum viable product")
//
const loggerStats = (( ) => {
const filterHits = new Map();
let dialog;
let timer;
const makeRow = function() {
const div = document.createElement('div');
div.appendChild(document.createElement('span'));
div.appendChild(document.createElement('span'));
return div;
};
const fillRow = function(div, entry) {
div.children[0].textContent = entry[1].toLocaleString();
div.children[1].textContent = entry[0];
};
const updateList = function() {
const sortedHits = Array.from(filterHits).sort((a, b) => {
return b[1] - a[1];
});
const doc = document;
const parent = dialog.querySelector('.sortedEntries');
let i = 0;
// Reuse existing rows
for ( let iRow = 0; iRow < parent.childElementCount; iRow++ ) {
if ( i === sortedHits.length ) { break; }
fillRow(parent.children[iRow], sortedHits[i]);
i += 1;
}
// Append new rows
if ( i < sortedHits.length ) {
const list = doc.createDocumentFragment();
for ( ; i < sortedHits.length; i++ ) {
const div = makeRow();
fillRow(div, sortedHits[i]);
list.appendChild(div);
}
parent.appendChild(list);
}
// Remove extraneous rows
// [Should never happen at this point in this current
// bare-bone implementation]
};
const toggleOn = function() {
dialog = modalDialog.create(
'#loggerStatsDialog',
( ) => {
dialog = undefined;
if ( timer !== undefined ) {
self.cancelIdleCallback(timer);
timer = undefined;
}
}
);
updateList();
modalDialog.show();
};
uDom.nodeFromId('loggerStats').addEventListener('click', toggleOn);
return {
processFilter: function(filter) {
if ( filter.source !== 'static' && filter.source !== 'cosmetic' ) {
return;
}
filterHits.set(filter.raw, (filterHits.get(filter.raw) || 0) + 1);
if ( dialog === undefined || timer !== undefined ) { return; }
timer = self.requestIdleCallback(
( ) => {
timer = undefined;
updateList();
},
{ timeout: 2001 }
);
}
};
})();
/******************************************************************************/
(( ) => {
const lines = [];
const options = {
format: 'list',
@ -2505,7 +2595,7 @@ const popupManager = (function() {
// - an option to discard immediately filtered out new entries
// - max entry count _per load_
//
const loggerSettings = (function() {
const loggerSettings = (( ) => {
const settings = {
discard: {
maxAge: 240, // global
@ -2611,7 +2701,7 @@ const loggerSettings = (function() {
viewPort.updateLayout();
};
uDom.nodeFromId('settings').addEventListener('click', toggleOn);
uDom.nodeFromId('loggerSettings').addEventListener('click', toggleOn);
return settings;
})();

View file

@ -70,8 +70,9 @@
</span>
</div>
<div>
<span id="loggerStats" class="button fa-icon">bar-chart</span>
<span id="loggerExport" class="button fa-icon">clipboard</span>
<span id="settings" class="button fa-icon">cog</span>
<span id="loggerSettings" class="button fa-icon">cog</span>
</div>
</div>
<div class="vscrollable">
@ -159,6 +160,27 @@
<button id="createCosmeticFilters" class="custom important" type="button" data-i18n="pickerCreate"></button>
</div>
<div id="loggerStatsDialog">
<div class="sortedEntries"></div>
</div>
<div id="loggerExportDialog">
<div class="options">
<div data-radio="format">
<span data-i18n="loggerExportFormatList" data-radio-item="list"></span>
<span data-i18n="loggerExportFormatTable" data-radio-item="table"></span>
</div>
<div data-radio="encoding">
<span data-i18n="loggerExportEncodePlain" data-radio-item="plain"></span>
<span data-i18n="loggerExportEncodeMarkdown" data-radio-item="markdown"></span>
</div>
<div>
<span data-i18n="genericCopyToClipboard" class="pushbutton"></span>
</div>
</div>
<textarea class="output" readonly spellcheck="false"></textarea>
</div>
<div id="loggerSettingsDialog">
<div><span data-i18n="loggerSettingDiscardPrompt"></span>
<ul>
@ -178,22 +200,6 @@
<div><label data-i18n="loggerSettingPerEntryLineCount"><input type="number" min="2" max="6"></label></div>
</div>
<div id="loggerExportDialog">
<div class="options">
<div data-radio="format">
<span data-i18n="loggerExportFormatList" data-radio-item="list"></span>
<span data-i18n="loggerExportFormatTable" data-radio-item="table"></span>
</div>
<div data-radio="encoding">
<span data-i18n="loggerExportEncodePlain" data-radio-item="plain"></span>
<span data-i18n="loggerExportEncodeMarkdown" data-radio-item="markdown"></span>
</div>
<div>
<span data-i18n="genericCopyToClipboard" class="pushbutton"></span>
</div>
</div>
<textarea class="output" readonly spellcheck="false"></textarea>
</div>
</div>
<script src="js/fa-icons.js"></script>