[LOG] refining the filter

- Added highlight filter phrases
- Added ordered AND operator (&)
- Added exclusion (!)
This commit is contained in:
chromoxdor
2026-01-24 12:33:30 +01:00
parent a4fac0082d
commit 6c5ce7e8f8
4 changed files with 201 additions and 28 deletions
+3
View File
@@ -464,6 +464,9 @@ To create/register a plugin, you have to :
#ifndef FEATURE_RULES_EASY_COLOR_CODE
#define FEATURE_RULES_EASY_COLOR_CODE 1
#endif
#ifndef WEBSERVER_INCLUDE_JS
#define WEBSERVER_INCLUDE_JS
#endif
#endif
#if FEATURE_FHEM
+67 -10
View File
@@ -341,12 +341,12 @@ static const char jsClipboardCopyPart2[] PROGMEM = {
// "if(t==null){i= max_loop + 1;"
"if(t==null){i=101;"
"}else{"
"cb+=t.innerHTML.replace(/<[Bb][Rr]\\s*\\/?>/gim,'\\n')+'"
"const visibleLines=Array.from(t.children).filter((e=>null!==e.offsetParent)).map((e=>e.innerHTML));visibleLines.length>0&&(cb+=visibleLines.join('\\n').replace(/<[Bb][Rr]\\s*\\/?>/gim,'\\n')+'"
};
//Fix HTML
static const char jsClipboardCopyPart3[] PROGMEM = {
"';}}"
"');}}"
"cb=cb.replace(/<\\/[Dd][Ii][Vv]\\s*\\/?>/gim,'\\n');"
"cb=cb.replace(/<[^>]*>/gim,'');"
"var ti=document.createElement('textarea');"
@@ -405,14 +405,71 @@ static const char DATA_UPDATE_SENSOR_VALUES_DEVICE_PAGE_JS[] PROGMEM = {
#ifdef WEBSERVER_INCLUDE_JS
static const char DATA_FETCH_AND_PARSE_LOG_JS[] PROGMEM = {
"function elId(e){return document.getElementById(e)}"
"function getBrowser(){var e,o=navigator.userAgent,r=o.match(/(opera|chrome|safari|firefox|msie|trident(?=\\/))\\/?\\s*(\\d+)/i)||[];return/trident/i.test(r[1])?{name:'IE',version:(e=/\brv[ :]+(\\d+)/g.exec(o)||[])[1]||''}:'Chrome'===r[1]&&null!=(e=o.match(/\bOPR|Edge\\/(\\d+)/))?{name:'Opera',version:e[1]}:(r=r[2]?[r[1],r[2]]:[navigator.appName,navigator.appVersion,'-?'],null!=(e=o.match(/version\\/(\\d+)/i))&&r.splice(1,1,e[1]),{name:r[0],version:r[1]})}"
"var browser=getBrowser(),currentBrowser=browser.name+browser.version;textToDisplay=(browser.name=browser.version<12)?'Error: '+currentBrowser+' is not supported! Please try a modern web browser.':'Fetching log entries...',elId('copyText_1').innerHTML=textToDisplay,loopDeLoop(1e3,0);var logLevel=['Unused','Error','Info','Debug','Debug More','Undefined','Undefined','Undefined','Undefined','Debug Dev'];"
"function loopDeLoop(e,o){let r='copyText_1';isNaN(o)&&(o=1),null==e&&(e=1e3),scrolling_type=e<=500?'auto':'smooth';var n,t,i='',l=0,"
"s=setInterval(function(){if(l>0){clearInterval(s);return}++o>1?l=1:fetch('/logjson').then(function(o){if(200!==o.status){console.log('Looks like there was a problem. Status Code: '+o.status);return}o.json()"
".then(function(o){var l;for(null==t&&(t=''),n=0;n<o.Log.nrEntries;++n)try{l=o.Log.Entries[n].timestamp}catch(a){l=a.name}finally{'TypeError'!==l&&(i=o.Log.Entries[n].timestamp,t+='<div class=level_'+o.Log.Entries[n].level+' id='+i+'><font color=\"gray\">'+o.Log.Entries[n].timestamp+':</font> '+o.Log.Entries[n].text+'</div>')}e=o.Log.TTL,''!==t&&('Fetching log entries...'==elId(r).innerHTML&&(elId(r).innerHTML=''),elId(r).innerHTML+=t),t='',!0==(autoscroll_on=elId('autoscroll').checked)&&''!==i&&elId(i).scrollIntoView({behavior:scrolling_type}),elId('current_loglevel').innerHTML='Logging: '+logLevel[o.Log.SettingsWebLogLevel]+' ('+o.Log.SettingsWebLogLevel+')',clearInterval(s),"
"loopDeLoop(e,0)})}).catch(function(o){elId(r).innerHTML+='<div>>> '+o.message+' <<</div>',autoscroll_on=elId('autoscroll').checked,elId(r).scrollTop=elId(r).scrollHeight,e=5e3,clearInterval(s),"
"loopDeLoop(e,0)}),l=1},e)}"
"function elId(e){return document.getElementById(e)}"
"function getBrowser(){var e,t=navigator.userAgent,o=t.match(/(opera|chrome|safari|firefox|msie|trident(?=\\/))\\/?\\s*(\\d+)/i)||[];"
"return/trident/i.test(o[1])?{name:'IE',version:(e=/\\brv[ :]+(\\d+)/g.exec(t)||[])[1]||''}:"
"'Chrome'===o[1]&&null!=(e=t.match(/\\bOPR|Edge\\/(\\d+)/))?"
"{name:'Opera',version:e[1]}:"
"(o=o[2]?[o[1],o[2]]:[navigator.appName,navigator.appVersion,'-?'],"
"null!=(e=t.match(/version\\/(\\d+)/i))&&o.splice(1,1,e[1]),{name:o[0],version:o[1]})}"
"var browser=getBrowser(),currentBrowser=browser.name+browser.version;"
"textToDisplay=(browser.name=browser.version<12)?"
"'Error: '+currentBrowser+' is not supported! Please try a modern web browser.':"
"'Fetching log entries...';"
"elId('copyText_1').innerHTML=textToDisplay;"
"loopDeLoop(1000,0);"
"var logLevel=['Unused','Error','Info','Debug','Debug More','Undefined','Undefined','Undefined','Undefined','Debug Dev'];"
"function loopDeLoop(e,t){let o='copyText_1';"
"isNaN(t)&&(t=1),null==e&&(e=1000),scrolling_type=e<=500?'auto':'smooth';"
"var r,n,i='',l=0,s=setInterval(function(){"
"if(l>0){clearInterval(s);return}"
"++t>1?l=1:fetch('/logjson').then(function(t){"
"if(200!==t.status){console.log('Looks like there was a problem. Status Code: '+t.status);return}"
"t.json().then(function(t){var l;"
"for(null==n&&(n=''),r=0;r<t.Log.nrEntries;++r)"
"try{l=t.Log.Entries[r].timestamp}"
"catch(a){l=a.name}"
"finally{'TypeError'!==l&&(i=t.Log.Entries[r].timestamp,"
"n+='<div class=level_'+t.Log.Entries[r].level+' id='+i+'><font color=\\'gray\\'>'+t.Log.Entries[r].timestamp+':</font> '+"
"t.Log.Entries[r].text+'</div>')}"
"e=t.Log.TTL,"
"''!==n&&('Fetching log entries...'==elId(o).innerHTML&&(elId(o).innerHTML=''),"
"elId(o).innerHTML+=n),"
"n='';"
"!0==(autoscroll_on=elId('autoscroll').checked)&&''!==i&&elId(i).scrollIntoView({behavior:scrolling_type});"
"applyLogFilter();"
"elId('current_loglevel').innerHTML='Logging: '+logLevel[t.Log.SettingsWebLogLevel]+' ('+t.Log.SettingsWebLogLevel+')';"
"clearInterval(s);loopDeLoop(e,0)"
"})}).catch(function(t){"
"elId(o).innerHTML+='<div>>> '+t.message+' <<</div>';"
"autoscroll_on=elId('autoscroll').checked;"
"elId(o).scrollTop=elId(o).scrollHeight;"
"e=5000;clearInterval(s);loopDeLoop(e,0)"
"}),l=1},e)}"
"function applyLogFilter(){let e=elId('logfilter').value.split(';').map(e=>e.trim()).filter(Boolean),"
"t=document.querySelectorAll('#copyText_1 > div'),"
"o=e.some(e=>!e.startsWith('!'));"
"for(let r of t){r.dataset.originalHtml||(r.dataset.originalHtml=r.innerHTML);"
"let n=r.dataset.originalHtml,i=r.textContent.toLowerCase(),l=[],s=!1,a=!o;"
"for(let d of e){"
"if(d.startsWith('!')){let c=d.slice(1);"
"if(!c.includes('&')||c.startsWith('&')||c.endsWith('&')){"
"if(c&&i.includes(c.toLowerCase())){s=!0;break}}else{"
"let g=c.split('&').map(e=>e.trim()).filter(Boolean),f=-1,u=!0;"
"for(let p of g){let L=i.indexOf(p.toLowerCase(),f+1);if(-1===L){u=!1;break}f=L}if(u){s=!0;break}}continue}"
"if(d.includes('&')&&!d.startsWith('&')&&!d.endsWith('&')){"
"let h=d.split('&').map(e=>e.trim()).filter(Boolean);"
"if(h.length>=2){let m=-1,$=!0;for(let _ of h){let b=i.indexOf(_.toLowerCase(),m+1);if(-1===b){$=!1;break}m=b}if($){l.push(...h),a=!0;break}}continue}"
"if(i.includes(d.toLowerCase())){l.push(d),a=!0;break}}"
"if(s||!a){r.style.display='none';continue}r.style.display='';r.innerHTML=highlightOutsideTags(n,l)}}"
"function highlightOutsideTags(e,t){if(!t.length)return e;"
"let o='',r=!1,n=0;"
"for(;n<e.length;){let i=e[n];"
"if('<'===i&&(r=!0),'>'===i){r=!1;o+=i;n++;continue}"
"if(!r){let l=!1;for(let s of t){let a=e.slice(n,n+s.length);"
"if(a.toLowerCase()===s.toLowerCase()){o+=`<span style='background-color: #bb9300;color: black;'>${a}</span>`;n+=s.length;l=!0;break}}if(l)continue}o+=i;n++}return o}"
"const input=document.getElementById('logfilter');"
"input&&(input.placeholder='\"!\"=exclude \";\"=or \"&\"=and (e.g. !act)');"
};
#endif // WEBSERVER_INCLUDE_JS
+121 -17
View File
@@ -96,7 +96,6 @@ function loopDeLoop(timeForNext, activeRequests) {
elId(ct1).innerHTML = '';
}
elId(ct1).innerHTML += logEntriesChunk;
applyLogFilter();
}
logEntriesChunk = '';
autoscroll_on = elId('autoscroll').checked;
@@ -105,6 +104,7 @@ function loopDeLoop(timeForNext, activeRequests) {
behavior: scrolling_type
});
}
applyLogFilter();
elId('current_loglevel').innerHTML = 'Logging: ' + logLevel[data.Log.SettingsWebLogLevel] + ' (' + data.Log.SettingsWebLogLevel + ')';
clearInterval(i);
loopDeLoop(timeForNext, 0);
@@ -125,24 +125,128 @@ function loopDeLoop(timeForNext, activeRequests) {
}
function applyLogFilter() {
const filtername = elId('logfilter').value;
const filterGroups = elId('logfilter').value
.split(';').map(s => s.trim()).filter(Boolean);
// prepare filters: "string1;string2"
const filters = filtername
.split(';')
.map(s => s.trim().toLowerCase())
.filter(Boolean);
const entries = document.querySelectorAll('#copyText_1 > div');
const hasPositiveFilter = filterGroups.some(g => !g.startsWith('!'));
const container = document.getElementById('copyText_1');
const entries = container.querySelectorAll('div');
for (const entry of entries) {
if (!entry.dataset.originalHtml) entry.dataset.originalHtml = entry.innerHTML;
let html = entry.dataset.originalHtml;
entries.forEach(entry => {
const text = entry.textContent.toLowerCase();
const textLower = entry.textContent.toLowerCase();
let matched = [];
let hiddenByExclusion = false;
const matches =
filters.length === 0 || filters.some(f => text.includes(f));
let isMatch = !hasPositiveFilter;
// show only matching entries
entry.style.display = matches ? '' : 'none';
});
}
for (const group of filterGroups) {
// exclusion rule with ordered AND
if (group.startsWith('!')) {
const g = group.slice(1);
if (g.includes('&') && !g.startsWith('&') && !g.endsWith('&')) {
const parts = g.split('&').map(s => s.trim()).filter(Boolean);
let lastIndex = -1;
let ok = true;
for (const p of parts) {
const idx = textLower.indexOf(p.toLowerCase(), lastIndex + 1);
if (idx === -1) {
ok = false;
break;
}
lastIndex = idx;
}
if (ok) {
hiddenByExclusion = true;
break;
}
} else {
if (g && textLower.includes(g.toLowerCase())) {
hiddenByExclusion = true;
break;
}
}
continue;
}
// ordered AND: a&b&c
if (group.includes('&') && !group.startsWith('&') && !group.endsWith('&')) {
const parts = group.split('&').map(s => s.trim()).filter(Boolean);
if (parts.length >= 2) {
let lastIndex = -1;
let ok = true;
for (const p of parts) {
const idx = textLower.indexOf(p.toLowerCase(), lastIndex + 1);
if (idx === -1) {
ok = false;
break;
}
lastIndex = idx;
}
if (ok) {
matched.push(...parts);
isMatch = true;
break;
}
}
continue;
}
// normal include
if (textLower.includes(group.toLowerCase())) {
matched.push(group);
isMatch = true;
break;
}
}
if (hiddenByExclusion || !isMatch) {
entry.style.display = 'none';
continue;
}
entry.style.display = '';
entry.innerHTML = highlightOutsideTags(html, matched);
}
}
function highlightOutsideTags(html, terms) {
if (!terms.length) return html;
let result = '';
let insideTag = false;
let i = 0;
while (i < html.length) {
const char = html[i];
if (char === '<') insideTag = true;
if (char === '>') {
insideTag = false;
result += char;
i++;
continue;
}
if (!insideTag) {
let matched = false;
for (const term of terms) {
const slice = html.slice(i, i + term.length);
if (slice.toLowerCase() === term.toLowerCase()) {
result += `<span style="background-color: #bb9300;color: black;">${slice}</span>`;
i += term.length;
matched = true;
break;
}
}
if (matched) continue;
}
result += char;
i++;
}
return result;
}
const input = document.getElementById("logfilter");
input && (input.placeholder = '"!"=exclude ";"=or "&"=and (e.g. !act)');
+10 -1
View File
@@ -11,7 +11,16 @@ function setGithubClipboard() {
if (i % 2 == 0) {
separatorSymbol += '\n'
}
clipboard += test.innerHTML.replace(/<[Bb][Rr]\s*\/?>/gim, '\n') + separatorSymbol;
// collect only visible log entry divs
const visibleLines = Array.from(test.children)
.filter(div => div.offsetParent !== null) // visible only
.map(div => div.innerHTML);
if (visibleLines.length > 0) {
clipboard +=
visibleLines.join('\n').replace(/<[Bb][Rr]\s*\/?>/gim, '\n') +
separatorSymbol;
}
}
}
clipboard = clipboard.replace(/<\/[Dd][Ii][Vv]\s*\/?>/gim, '\n');