mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-28 04:07:47 +00:00
[LOG] refining the filter
- Added highlight filter phrases - Added ordered AND operator (&) - Added exclusion (!)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
@@ -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)');
|
||||
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user