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; width: 1em;
} }
.fa-icon > .fa-icon_bar-chart {
width: calc(1em * 2048 / 1792);
}
.fa-icon > .fa-icon_eraser, .fa-icon > .fa-icon_eraser,
.fa-icon > .fa-icon_film { .fa-icon > .fa-icon_film {
width: calc(1em * 1920 / 1792); width: calc(1em * 1920 / 1792);

View file

@ -829,31 +829,25 @@ body.colorBlind #netFilteringDialog > div.panes > .dynamic .entry > .action > .
margin-bottom: 0; margin-bottom: 0;
} }
#loggerSettingsDialog { #loggerStatsDialog .sortedEntries {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
font-size: smaller;
} }
#loggerSettingsDialog > div { #loggerStatsDialog .sortedEntries > div {
padding-bottom: 1em; display: flex;
} }
#loggerSettingsDialog > div:last-of-type { #loggerStatsDialog .sortedEntries > div > span:first-of-type {
padding-bottom: 0; flex-grow: 0;
flex-shrink: 0;
padding: 0 2em 0 0;
text-align: right;
width: 3em;
} }
#loggerSettingsDialog ul { #loggerStatsDialog .sortedEntries > div > span:last-of-type {
padding: 0; flex-grow: 1;
} flex-shrink: 1;
body[dir="ltr"] #loggerSettingsDialog ul { white-space: pre;
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;
} }
#loggerExportDialog { #loggerExportDialog {
@ -890,6 +884,33 @@ body[dir="rtl"] #loggerSettingsDialog ul {
white-space: pre; 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 { .hide {
display: none !important; 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;"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display: none;">
<defs> <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="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="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="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> <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 ) { for ( const entry of entries ) {
const unboxed = JSON.parse(entry); const unboxed = JSON.parse(entry);
if ( unboxed.filter instanceof Object ){
loggerStats.processFilter(unboxed.filter);
}
if ( netInspectorPaused ) { continue; }
const parsed = parseLogEntry(unboxed); const parsed = parseLogEntry(unboxed);
if ( if (
parsed.tabId !== undefined && parsed.tabId !== undefined &&
@ -384,7 +388,7 @@ const parseLogEntry = function(details) {
/******************************************************************************/ /******************************************************************************/
const viewPort = (function() { const viewPort = (( ) => {
const vwRenderer = document.getElementById('vwRenderer'); const vwRenderer = document.getElementById('vwRenderer');
const vwScroller = document.getElementById('vwScroller'); const vwScroller = document.getElementById('vwScroller');
const vwVirtualContent = document.getElementById('vwVirtualContent'); const vwVirtualContent = document.getElementById('vwVirtualContent');
@ -802,14 +806,14 @@ const viewPort = (function() {
/******************************************************************************/ /******************************************************************************/
const updateCurrentTabTitle = (function() { const updateCurrentTabTitle = (( ) => {
const i18nCurrentTab = vAPI.i18n('loggerCurrentTab'); const i18nCurrentTab = vAPI.i18n('loggerCurrentTab');
return function() { return function() {
const select = uDom.nodeFromId('pageSelector'); const select = uDom.nodeFromId('pageSelector');
if ( select.value !== '_' || activeTabId === 0 ) { return; } if ( select.value !== '_' || activeTabId === 0 ) { return; }
const opt0 = select.querySelector('[value="_"]'); const opt0 = select.querySelector('[value="_"]');
const opt1 = select.querySelector('[value="' + activeTabId + '"]'); const opt1 = select.querySelector(`[value="${activeTabId}"]`);
let text = i18nCurrentTab; let text = i18nCurrentTab;
if ( opt1 !== null ) { if ( opt1 !== null ) {
text += ' / ' + opt1.textContent; text += ' / ' + opt1.textContent;
@ -934,9 +938,7 @@ const onLogBufferRead = function(response) {
pageSelectorFromURLHash(); pageSelectorFromURLHash();
} }
if ( netInspectorPaused === false ) {
processLoggerEntries(response); processLoggerEntries(response);
}
// Synchronize DOM with sent logger data // Synchronize DOM with sent logger data
document.body.classList.toggle( document.body.classList.toggle(
@ -955,7 +957,7 @@ const onLogBufferRead = function(response) {
/******************************************************************************/ /******************************************************************************/
const readLogBuffer = (function() { const readLogBuffer = (( ) => {
let timer; let timer;
const readLogBufferNow = function() { const readLogBufferNow = function() {
@ -1011,7 +1013,7 @@ const pageSelectorChanged = function() {
pageSelectorFromURLHash(); pageSelectorFromURLHash();
}; };
const pageSelectorFromURLHash = (function() { const pageSelectorFromURLHash = (( ) => {
let lastHash; let lastHash;
let lastSelectedTabId; let lastSelectedTabId;
@ -1806,7 +1808,7 @@ const reloadTab = function(ev) {
/******************************************************************************/ /******************************************************************************/
/******************************************************************************/ /******************************************************************************/
const rowFilterer = (function() { const rowFilterer = (( ) => {
const userFilters = []; const userFilters = [];
const builtinFilters = []; const builtinFilters = [];
@ -2017,7 +2019,7 @@ const rowFilterer = (function() {
// - Max number of entry per distinct tab // - Max number of entry per distinct tab
// - Max entry age // - Max entry age
const rowJanitor = (function() { const rowJanitor = (( ) => {
const tabIdToDiscard = new Set(); const tabIdToDiscard = new Set();
const tabIdToLoadCountMap = new Map(); const tabIdToLoadCountMap = new Map();
const tabIdToEntryCountMap = new Map(); const tabIdToEntryCountMap = new Map();
@ -2226,7 +2228,7 @@ const toggleVCompactView = function() {
/******************************************************************************/ /******************************************************************************/
const popupManager = (function() { const popupManager = (( ) => {
let realTabId = 0; let realTabId = 0;
let popup = null; let popup = null;
let popupObserver = 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 lines = [];
const options = { const options = {
format: 'list', format: 'list',
@ -2505,7 +2595,7 @@ const popupManager = (function() {
// - an option to discard immediately filtered out new entries // - an option to discard immediately filtered out new entries
// - max entry count _per load_ // - max entry count _per load_
// //
const loggerSettings = (function() { const loggerSettings = (( ) => {
const settings = { const settings = {
discard: { discard: {
maxAge: 240, // global maxAge: 240, // global
@ -2611,7 +2701,7 @@ const loggerSettings = (function() {
viewPort.updateLayout(); viewPort.updateLayout();
}; };
uDom.nodeFromId('settings').addEventListener('click', toggleOn); uDom.nodeFromId('loggerSettings').addEventListener('click', toggleOn);
return settings; return settings;
})(); })();

View file

@ -70,8 +70,9 @@
</span> </span>
</div> </div>
<div> <div>
<span id="loggerStats" class="button fa-icon">bar-chart</span>
<span id="loggerExport" class="button fa-icon">clipboard</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> </div>
<div class="vscrollable"> <div class="vscrollable">
@ -159,6 +160,27 @@
<button id="createCosmeticFilters" class="custom important" type="button" data-i18n="pickerCreate"></button> <button id="createCosmeticFilters" class="custom important" type="button" data-i18n="pickerCreate"></button>
</div> </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 id="loggerSettingsDialog">
<div><span data-i18n="loggerSettingDiscardPrompt"></span> <div><span data-i18n="loggerSettingDiscardPrompt"></span>
<ul> <ul>
@ -178,22 +200,6 @@
<div><label data-i18n="loggerSettingPerEntryLineCount"><input type="number" min="2" max="6"></label></div> <div><label data-i18n="loggerSettingPerEntryLineCount"><input type="number" min="2" max="6"></label></div>
</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> </div>
<script src="js/fa-icons.js"></script> <script src="js/fa-icons.js"></script>