mirror of
https://github.com/gSpotx2f/ruantiblock_openwrt.git
synced 2026-05-14 06:30:59 +00:00
luci-app: Added support for syslog-ng
This commit is contained in:
@@ -11,7 +11,7 @@ LUCI_APP=1
|
|||||||
OWRT_VERSION="19.07"
|
OWRT_VERSION="19.07"
|
||||||
RUAB_VERSION="0.9.0-2"
|
RUAB_VERSION="0.9.0-2"
|
||||||
RUAB_MOD_LUA_VERSION="0.9.0-2"
|
RUAB_MOD_LUA_VERSION="0.9.0-2"
|
||||||
RUAB_LUCI_APP_VERSION="0.9.0-3"
|
RUAB_LUCI_APP_VERSION="0.9.0-4"
|
||||||
BASE_URL="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master"
|
BASE_URL="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master"
|
||||||
PKG_DIR="/tmp"
|
PKG_DIR="/tmp"
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_VERSION:=0.9.0
|
PKG_VERSION:=0.9.0
|
||||||
PKG_RELEASE:=3
|
PKG_RELEASE:=4
|
||||||
LUCI_TITLE:=LuCI support for ruantiblock
|
LUCI_TITLE:=LuCI support for ruantiblock
|
||||||
LUCI_DEPENDS:=+ruantiblock +luci-mod-admin-full
|
LUCI_DEPENDS:=+ruantiblock +luci-mod-admin-full
|
||||||
LUCI_PKGARCH:=all
|
LUCI_PKGARCH:=all
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ return L.Class.extend({
|
|||||||
/&/g, '&').replace(
|
/&/g, '&').replace(
|
||||||
/</g, '<').replace(
|
/</g, '<').replace(
|
||||||
/>/g, '>').replace(
|
/>/g, '>').replace(
|
||||||
/"/g, '"');
|
/"/g, '"').replace(
|
||||||
|
/'/g, ''');
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,6 +71,44 @@ return L.Class.extend({
|
|||||||
throw new Error('parseLogData needs to be reloaded in subclass');
|
throw new Error('parseLogData needs to be reloaded in subclass');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setLevelFilter: function(cArr) {
|
||||||
|
let logLevelsKeys = Object.keys(this.logLevels);
|
||||||
|
if(logLevelsKeys.length > 0) {
|
||||||
|
let selectedLevels = this.logLevelsDropdown.getValue();
|
||||||
|
if(logLevelsKeys.length === selectedLevels.length) {
|
||||||
|
return cArr;
|
||||||
|
};
|
||||||
|
return cArr.filter(s => selectedLevels.length === 0 || selectedLevels.includes(s[2]));
|
||||||
|
};
|
||||||
|
return cArr;
|
||||||
|
},
|
||||||
|
|
||||||
|
setRegexpFilter: function(cArr) {
|
||||||
|
let fPattern = document.getElementById('logFilter').value;
|
||||||
|
if(!fPattern) {
|
||||||
|
return cArr;
|
||||||
|
};
|
||||||
|
let fArr = [];
|
||||||
|
try {
|
||||||
|
let regExp = new RegExp(`(${fPattern})`, 'giu');
|
||||||
|
cArr.forEach((e, i) => {
|
||||||
|
if(e[4] !== null && regExp.test(e[4])) {
|
||||||
|
e[4] = e[4].replace(regExp, '<span class="log-highlight-item">$1</span>');
|
||||||
|
fArr.push(e);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} catch(err) {
|
||||||
|
if(err.name === 'SyntaxError') {
|
||||||
|
ui.addNotification(null,
|
||||||
|
E('p', {}, _('Invalid regular expression') + ': ' + err.message));
|
||||||
|
return cArr;
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return fArr;
|
||||||
|
},
|
||||||
|
|
||||||
makeLogArea: function(logdataArray) {
|
makeLogArea: function(logdataArray) {
|
||||||
let lines = `<div class="tr"><div class="td center log-entry-empty">${_('No entries available...')}</div></div>`;
|
let lines = `<div class="tr"><div class="td center log-entry-empty">${_('No entries available...')}</div></div>`;
|
||||||
let logTable = E('div', { 'id': 'logTable', 'class': 'table' });
|
let logTable = E('div', { 'id': 'logTable', 'class': 'table' });
|
||||||
@@ -81,15 +120,16 @@ return L.Class.extend({
|
|||||||
if(logdataArray.length > 0) {
|
if(logdataArray.length > 0) {
|
||||||
lines = [];
|
lines = [];
|
||||||
logdataArray.forEach((e, i) => {
|
logdataArray.forEach((e, i) => {
|
||||||
this.logLevelsStat[e[2]] = (this.logLevelsStat[e[2]] != undefined) ?
|
if(e[2] in this.logLevels) {
|
||||||
this.logLevelsStat[e[2]] + 1 : 1;
|
this.logLevelsStat[e[2]] = this.logLevelsStat[e[2]] + 1;
|
||||||
|
};
|
||||||
|
|
||||||
lines.push(
|
lines.push(
|
||||||
`<div class="tr log-${e[2] || 'empty'}"><div class="td left" data-title="#">${e[0]}</div>` +
|
`<div class="tr log-${e[2] || 'empty'}"><div class="td left" data-title="#">${e[0]}</div>` +
|
||||||
((e[1]) ? `<div class="td left" data-title="${_('Timestamp')}">${e[1]}</div>` : '') +
|
((e[1]) ? `<div class="td left" data-title="${_('Timestamp')}">${e[1]}</div>` : '') +
|
||||||
((e[2]) ? `<div class="td left" data-title="${_('Level')}">${e[2]}</div>` : '') +
|
((e[2]) ? `<div class="td left" data-title="${_('Level')}">${e[2]}</div>` : '') +
|
||||||
((e[3]) ? `<div class="td left" data-title="${_('Facility')}">${e[3]}</div>` : '') +
|
((e[3]) ? `<div class="td left" data-title="${_('Facility')}">${e[3]}</div>` : '') +
|
||||||
((e[4]) ? `<div class="td left" data-title="${_('Message')}">${e[4]}</div>` : '') +
|
((e[4]) ? `<div class="td left log-entry-message-cell" data-title="${_('Message')}">${e[4]}</div>` : '') +
|
||||||
`</div>`
|
`</div>`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -119,7 +159,7 @@ return L.Class.extend({
|
|||||||
let levelsStatString = '';
|
let levelsStatString = '';
|
||||||
if((Object.values(this.logLevelsStat).reduce((s,c) => s + c, 0)) > 0) {
|
if((Object.values(this.logLevelsStat).reduce((s,c) => s + c, 0)) > 0) {
|
||||||
Object.entries(this.logLevelsStat).forEach(e => {
|
Object.entries(this.logLevelsStat).forEach(e => {
|
||||||
if(e[1] > 0) {
|
if(e[0] in this.logLevels && e[1] > 0) {
|
||||||
levelsStatString += `<span class="log-entries-count-level log-${e[0]}" title="${e[0]}">${e[1]}</span>`;
|
levelsStatString += `<span class="log-entries-count-level log-${e[0]}" title="${e[0]}">${e[1]}</span>`;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -133,52 +173,14 @@ return L.Class.extend({
|
|||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
setLevelFilter: function(cArr) {
|
downloadLog: function(ev) {
|
||||||
let logLevelsKeys = Object.keys(this.logLevels);
|
|
||||||
if(logLevelsKeys.length > 0) {
|
|
||||||
let selectedLevels = this.logLevelsDropdown.getValue();
|
|
||||||
if(logLevelsKeys.length === selectedLevels.length) {
|
|
||||||
return cArr;
|
|
||||||
};
|
|
||||||
return cArr.filter(s => selectedLevels.length === 0 || selectedLevels.includes(s[2]));
|
|
||||||
};
|
|
||||||
return cArr;
|
|
||||||
},
|
|
||||||
|
|
||||||
setRegexpFilter: function(cArr) {
|
|
||||||
let fPattern = document.getElementById('logFilter').value;
|
|
||||||
if(!fPattern) {
|
|
||||||
return cArr;
|
|
||||||
};
|
|
||||||
let fArr = [];
|
|
||||||
try {
|
|
||||||
let regExp = new RegExp(`(${fPattern})`, 'giu');
|
|
||||||
cArr.forEach((e, i) => {
|
|
||||||
if(regExp.test(e[4])) {
|
|
||||||
e[4] = e[4].replace(regExp, '<span class="log-highlight-item">$1</span>');
|
|
||||||
fArr.push(e);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
} catch(err) {
|
|
||||||
if(err.name === 'SyntaxError') {
|
|
||||||
ui.addNotification(null,
|
|
||||||
E('p', {}, _('Invalid regular expression') + ': ' + err.message));
|
|
||||||
return cArr;
|
|
||||||
} else {
|
|
||||||
throw err;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
return fArr;
|
|
||||||
},
|
|
||||||
|
|
||||||
downloadLog: function() {
|
|
||||||
let formElems = Array.from(document.forms.logForm.elements);
|
let formElems = Array.from(document.forms.logForm.elements);
|
||||||
formElems.forEach(e => e.disabled = true);
|
formElems.forEach(e => e.disabled = true);
|
||||||
|
|
||||||
return this.getLogData(0).then(logdata => {
|
return this.getLogData(0).then(logdata => {
|
||||||
logdata = logdata || '';
|
logdata = logdata || '';
|
||||||
let link = E('a', {
|
let link = E('a', {
|
||||||
'download': this.viewName + '.txt',
|
'download': this.viewName + '.log',
|
||||||
'href': URL.createObjectURL(
|
'href': URL.createObjectURL(
|
||||||
new Blob([ logdata ], { type: 'text/plain' })),
|
new Blob([ logdata ], { type: 'text/plain' })),
|
||||||
});
|
});
|
||||||
@@ -228,16 +230,32 @@ return L.Class.extend({
|
|||||||
.log-entry-message {
|
.log-entry-message {
|
||||||
min-width: 25em !important;
|
min-width: 25em !important;
|
||||||
}
|
}
|
||||||
|
.log-entry-message-cell {
|
||||||
|
overflow-x: hidden !important;
|
||||||
|
text-overflow: ellipsis !important;
|
||||||
|
}
|
||||||
.log-empty {
|
.log-empty {
|
||||||
}
|
}
|
||||||
.log-emerg {
|
.log-emerg {
|
||||||
background-color: #a93734 !important;
|
background-color: #a93734 !important;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
log-emerg .td {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
log-emerg td {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
.log-alert {
|
.log-alert {
|
||||||
background-color: #ff7968 !important;
|
background-color: #ff7968 !important;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
.log-alert .td {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
.log-alert td {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
.log-crit {
|
.log-crit {
|
||||||
background-color: #fcc3bf !important;
|
background-color: #fcc3bf !important;
|
||||||
}
|
}
|
||||||
@@ -354,6 +372,7 @@ return L.Class.extend({
|
|||||||
|
|
||||||
let logSorting = E('select', {
|
let logSorting = E('select', {
|
||||||
'id': 'logSorting',
|
'id': 'logSorting',
|
||||||
|
'name': 'logSorting',
|
||||||
'form': 'logForm',
|
'form': 'logForm',
|
||||||
'class': "cbi-input-select",
|
'class': "cbi-input-select",
|
||||||
}, [
|
}, [
|
||||||
@@ -363,6 +382,8 @@ return L.Class.extend({
|
|||||||
logSorting.value = this.logSortingValue;
|
logSorting.value = this.logSortingValue;
|
||||||
|
|
||||||
let logDownloadBtn = E('button', {
|
let logDownloadBtn = E('button', {
|
||||||
|
'id': 'logDownloadBtn',
|
||||||
|
'name': 'logDownloadBtn',
|
||||||
'class': 'cbi-button btn',
|
'class': 'cbi-button btn',
|
||||||
'click': ui.createHandlerFn(this, this.downloadLog),
|
'click': ui.createHandlerFn(this, this.downloadLog),
|
||||||
}, _('Download log'));
|
}, _('Download log'));
|
||||||
@@ -412,7 +433,7 @@ return L.Class.extend({
|
|||||||
E('form', {
|
E('form', {
|
||||||
'id': 'logForm',
|
'id': 'logForm',
|
||||||
'name': 'logForm',
|
'name': 'logForm',
|
||||||
'style': 'display:inline-block',
|
'style': 'display:inline-block; margin-top:0.5em',
|
||||||
'submit': ui.createHandlerFn(this, function(ev) {
|
'submit': ui.createHandlerFn(this, function(ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
let formElems = Array.from(document.forms.logForm.elements);
|
let formElems = Array.from(document.forms.logForm.elements);
|
||||||
@@ -458,38 +479,33 @@ return L.Class.extend({
|
|||||||
])
|
])
|
||||||
),
|
),
|
||||||
E('div', { 'class': 'cbi-section fade-in' },
|
E('div', { 'class': 'cbi-section fade-in' },
|
||||||
E('div', { 'class': 'cbi-section-node' },
|
E('div', { 'class': 'cbi-section-node' }, [
|
||||||
E('div', { 'class': 'cbi-value' }, [
|
E('div', { 'style': 'position:fixed; z-index:1 !important' }, [
|
||||||
E('div', { 'style': 'position:fixed; z-index:1 !important' }, [
|
E('button', {
|
||||||
E('button', {
|
'class': 'btn',
|
||||||
'class': 'btn',
|
'style': 'position:relative; display:block; margin:0 !important; left:1px; top:1px',
|
||||||
'style': 'position:relative; display:block; margin:0 !important; left:1px; top:1px',
|
'click': ev => {
|
||||||
'click': ev => {
|
document.getElementById('logTitle').scrollIntoView(true);
|
||||||
document.getElementById('logTitle').scrollIntoView(true);
|
ev.target.blur();
|
||||||
ev.target.blur();
|
},
|
||||||
},
|
}, '↑'),
|
||||||
}, '↑'),
|
E('button', {
|
||||||
E('button', {
|
'class': 'btn',
|
||||||
'class': 'btn',
|
'style': 'position:relative; display:block; margin:0 !important; margin-top:1px !important; left:1px; top:1px',
|
||||||
'style': 'position:relative; display:block; margin:0 !important; margin-top:1px !important; left:1px; top:1px',
|
'click': ev => {
|
||||||
'click': ev => {
|
logWrapper.scrollIntoView(false);
|
||||||
logWrapper.scrollIntoView(false);
|
ev.target.blur();
|
||||||
ev.target.blur();
|
},
|
||||||
},
|
}, '↓'),
|
||||||
}, '↓'),
|
]),
|
||||||
]),
|
logWrapper,
|
||||||
logWrapper,
|
])
|
||||||
])
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
E('div', { 'class': 'cbi-section fade-in' },
|
E('div', { 'class': 'cbi-section fade-in' },
|
||||||
E('div', { 'class': 'cbi-section-node' },
|
E('div', { 'class': 'cbi-section-node' },
|
||||||
E('div', { 'class': 'cbi-value' },
|
E('div', { 'class': 'cbi-value' },
|
||||||
E('div', { 'style': 'width:100%; text-align:right !important' }, [
|
E('div', { 'style': 'width:100%; text-align:right !important' }, logDownloadBtn)
|
||||||
E('hr'),
|
),
|
||||||
logDownloadBtn,
|
|
||||||
])
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -1,13 +1,44 @@
|
|||||||
'require fs';
|
'require fs';
|
||||||
'require ui';
|
'require ui';
|
||||||
'require view.ruantiblock.baselog as baselog';
|
'require view.log.baselog as baselog';
|
||||||
'require view.ruantiblock.tools as tools';
|
'require view.ruantiblock.tools as tools';
|
||||||
|
|
||||||
return baselog.view.extend({
|
return baselog.view.extend({
|
||||||
viewName: 'ruantiblock-log',
|
viewName: 'ruantiblock',
|
||||||
|
|
||||||
title: _('Ruantiblock') + ' - ' + _('Log'),
|
title: _('Ruantiblock') + ' - ' + _('Log'),
|
||||||
|
|
||||||
|
appRegexp: new RegExp(`^.*${tools.app_name}\[[0-9]+\].*$`, 'gm'),
|
||||||
|
|
||||||
|
testRegexp: new RegExp(/([0-9]{2}:){2}[0-9]{2}/),
|
||||||
|
|
||||||
|
isLoggerChecked: false,
|
||||||
|
|
||||||
|
entriesHandler: null,
|
||||||
|
|
||||||
|
// logd
|
||||||
|
logdHandler: function(strArray, lineNum) {
|
||||||
|
let logLevel = strArray[5].split('.');
|
||||||
|
return [
|
||||||
|
lineNum, // # (Number)
|
||||||
|
strArray.slice(0, 5).join(' '), // Timestamp (String)
|
||||||
|
logLevel[1], // Level (String)
|
||||||
|
logLevel[0], // Facility (String)
|
||||||
|
this.htmlEntities(strArray.slice(6).join(' ')), // Message (String)
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
// syslog-ng
|
||||||
|
syslog_ngHandler: function(strArray, lineNum) {
|
||||||
|
return [
|
||||||
|
lineNum, // # (Number)
|
||||||
|
strArray.slice(0, 3).join(' '), // Timestamp (String)
|
||||||
|
null, // Level (String)
|
||||||
|
null, // Facility (String)
|
||||||
|
this.htmlEntities(strArray.slice(4).join(' ')), // Message (String)
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
getLogData: function(tail) {
|
getLogData: function(tail) {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
L.resolveDefault(fs.stat('/sbin/logread'), null),
|
L.resolveDefault(fs.stat('/sbin/logread'), null),
|
||||||
@@ -16,10 +47,8 @@ return baselog.view.extend({
|
|||||||
let logger = (stat[0]) ? stat[0].path : (stat[1]) ? stat[1].path : null;
|
let logger = (stat[0]) ? stat[0].path : (stat[1]) ? stat[1].path : null;
|
||||||
|
|
||||||
if(logger) {
|
if(logger) {
|
||||||
return fs.exec_direct(logger, [ '-e', '^' + tools.app_name ]).catch(err => {
|
return fs.exec_direct(logger, [ '-e', tools.app_name ]).catch(err => {
|
||||||
ui.addNotification(null, E('p', _('Unable to execute or read contents')
|
ui.addNotification(null, E('p', {}, _('Unable to load log data: ' + err.message)));
|
||||||
+ ': %s<br />[ %s ]'.format(err.message, logger)
|
|
||||||
));
|
|
||||||
return '';
|
return '';
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -31,7 +60,7 @@ return baselog.view.extend({
|
|||||||
return [];
|
return [];
|
||||||
};
|
};
|
||||||
|
|
||||||
let strings = logdata.trim().split(/\n/);
|
let strings = logdata.trim().match(this.appRegexp) || [];
|
||||||
|
|
||||||
if(tail && tail > 0 && strings) {
|
if(tail && tail > 0 && strings) {
|
||||||
strings = strings.slice(-tail);
|
strings = strings.slice(-tail);
|
||||||
@@ -41,15 +70,22 @@ return baselog.view.extend({
|
|||||||
|
|
||||||
let entriesArray = strings.map((e, i) => {
|
let entriesArray = strings.map((e, i) => {
|
||||||
let strArray = e.split(/\s+/);
|
let strArray = e.split(/\s+/);
|
||||||
let logLevel = strArray[5].split('.');
|
|
||||||
|
|
||||||
return [
|
if(!this.isLoggerChecked) {
|
||||||
i + 1, // # (Number)
|
/**
|
||||||
strArray.slice(0, 5).join(' '), // Timestamp (String)
|
* Checking the third field of a line.
|
||||||
logLevel[1], // Level (String)
|
* If it contains time then syslog-ng.
|
||||||
logLevel[0], // Facility (String)
|
*/
|
||||||
this.htmlEntities(strArray.slice(6).join(' ')), // Message (String)
|
if(this.testRegexp.test(strArray[2])) {
|
||||||
];
|
this.logLevels = {};
|
||||||
|
this.entriesHandler = this.syslog_ngHandler;
|
||||||
|
} else {
|
||||||
|
this.entriesHandler = this.logdHandler;
|
||||||
|
};
|
||||||
|
this.isLoggerChecked = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.entriesHandler(strArray, i + 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
if(this.logSortingValue === 'desc') {
|
if(this.logSortingValue === 'desc') {
|
||||||
|
|||||||
@@ -18,8 +18,8 @@
|
|||||||
"/etc/init.d/cron enabled": [ "exec" ],
|
"/etc/init.d/cron enabled": [ "exec" ],
|
||||||
"/etc/init.d/cron enable": [ "exec" ],
|
"/etc/init.d/cron enable": [ "exec" ],
|
||||||
"/etc/init.d/cron restart": [ "exec" ],
|
"/etc/init.d/cron restart": [ "exec" ],
|
||||||
"/sbin/logread -e ^ruantiblock": [ "exec" ],
|
"/sbin/logread -e ruantiblock": [ "exec" ],
|
||||||
"/usr/sbin/logread -e ^ruantiblock": [ "exec" ]
|
"/usr/sbin/logread -e ruantiblock": [ "exec" ]
|
||||||
},
|
},
|
||||||
"uci": [ "network", "ruantiblock" ]
|
"uci": [ "network", "ruantiblock" ]
|
||||||
},
|
},
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user