mirror of
https://github.com/gSpotx2f/ruantiblock_openwrt.git
synced 2026-05-14 14:40:58 +00:00
luci-app: updated log.
This commit is contained in:
@@ -11,7 +11,7 @@ LUCI_APP=1
|
|||||||
OWRT_VERSION="current"
|
OWRT_VERSION="current"
|
||||||
RUAB_VERSION="1.3-1"
|
RUAB_VERSION="1.3-1"
|
||||||
RUAB_MOD_LUA_VERSION="1.3-2"
|
RUAB_MOD_LUA_VERSION="1.3-2"
|
||||||
RUAB_LUCI_APP_VERSION="1.3-4"
|
RUAB_LUCI_APP_VERSION="1.3-5"
|
||||||
BASE_URL="https://raw.githubusercontent.com/gSpotx2f/packages-openwrt/master"
|
BASE_URL="https://raw.githubusercontent.com/gSpotx2f/packages-openwrt/master"
|
||||||
PKG_DIR="/tmp"
|
PKG_DIR="/tmp"
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_VERSION:=1.3-4
|
PKG_VERSION:=1.3-5
|
||||||
LUCI_TITLE:=LuCI support for ruantiblock
|
LUCI_TITLE:=LuCI support for ruantiblock
|
||||||
LUCI_DEPENDS:=+ruantiblock
|
LUCI_DEPENDS:=+ruantiblock
|
||||||
LUCI_PKGARCH:=all
|
LUCI_PKGARCH:=all
|
||||||
|
|||||||
@@ -152,14 +152,16 @@ return baseclass.extend({
|
|||||||
view: view.extend({
|
view: view.extend({
|
||||||
/**
|
/**
|
||||||
* View name (for local storage and downloads).
|
* View name (for local storage and downloads).
|
||||||
* Must be overridden by a subclass!
|
*
|
||||||
*/
|
* @property {string} viewName
|
||||||
|
*/
|
||||||
viewName : null,
|
viewName : null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page title.
|
* Page title.
|
||||||
* Must be overridden by a subclass!
|
*
|
||||||
*/
|
* @property {string} title
|
||||||
|
*/
|
||||||
title : null,
|
title : null,
|
||||||
|
|
||||||
pollInterval : L.env.pollinterval,
|
pollInterval : L.env.pollinterval,
|
||||||
@@ -208,6 +210,8 @@ return baseclass.extend({
|
|||||||
|
|
||||||
timeFilterValue : null,
|
timeFilterValue : null,
|
||||||
|
|
||||||
|
timeFilterReValue : false,
|
||||||
|
|
||||||
hostFilterValue : [],
|
hostFilterValue : [],
|
||||||
|
|
||||||
facilityFilterValue : [],
|
facilityFilterValue : [],
|
||||||
@@ -216,10 +220,14 @@ return baseclass.extend({
|
|||||||
|
|
||||||
msgFilterValue : null,
|
msgFilterValue : null,
|
||||||
|
|
||||||
|
msgFilterReValue : false,
|
||||||
|
|
||||||
logSortingValue : 'asc',
|
logSortingValue : 'asc',
|
||||||
|
|
||||||
autoRefreshValue : true,
|
autoRefreshValue : true,
|
||||||
|
|
||||||
|
isAutorefresh : true,
|
||||||
|
|
||||||
isHosts : false,
|
isHosts : false,
|
||||||
|
|
||||||
isFacilities : false,
|
isFacilities : false,
|
||||||
@@ -238,7 +246,7 @@ return baseclass.extend({
|
|||||||
|
|
||||||
totalLogLines : 0,
|
totalLogLines : 0,
|
||||||
|
|
||||||
lastBytes : null,
|
lastHash : null,
|
||||||
|
|
||||||
actionButtons : [],
|
actionButtons : [],
|
||||||
|
|
||||||
@@ -334,9 +342,32 @@ return baseclass.extend({
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setRegexpValidator(elem, flag) {
|
||||||
|
ui.addValidator(
|
||||||
|
elem,
|
||||||
|
'string',
|
||||||
|
true,
|
||||||
|
v => {
|
||||||
|
if(!flag.checked) {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
new RegExp(v, 'giu');
|
||||||
|
return true;
|
||||||
|
} catch(err) {
|
||||||
|
return _('Invalid regular expression') + ':\n' + err.message;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
'blur',
|
||||||
|
'focus',
|
||||||
|
'input'
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
setFilterSettings() {
|
setFilterSettings() {
|
||||||
this.tailValue = this.checkZeroValue(this.tailInput.value);
|
this.tailValue = this.checkZeroValue(this.tailInput.value);
|
||||||
this.timeFilterValue = this.timeFilter.value;
|
this.timeFilterValue = this.timeFilter.value;
|
||||||
|
this.timeFilterReValue = this.timeFilterRe.checked;
|
||||||
if(this.isHosts) {
|
if(this.isHosts) {
|
||||||
this.hostFilterValue = this.logHostsDropdown.getValue();
|
this.hostFilterValue = this.logHostsDropdown.getValue();
|
||||||
};
|
};
|
||||||
@@ -347,20 +378,24 @@ return baseclass.extend({
|
|||||||
this.levelFilterValue = this.logLevelsDropdown.getValue();
|
this.levelFilterValue = this.logLevelsDropdown.getValue();
|
||||||
};
|
};
|
||||||
this.msgFilterValue = this.msgFilter.value;
|
this.msgFilterValue = this.msgFilter.value;
|
||||||
|
this.msgFilterReValue = this.msgFilterRe.checked;
|
||||||
this.logSortingValue = this.logSorting.value;
|
this.logSortingValue = this.logSorting.value;
|
||||||
this.autoRefreshValue = this.autoRefresh.checked;
|
this.autoRefreshValue = this.autoRefresh.checked;
|
||||||
if(this.autoRefreshValue) {
|
if(this.isAutorefresh) {
|
||||||
poll.add(this.pollFuncWrapper, this.pollInterval);
|
if(this.autoRefreshValue) {
|
||||||
this.refreshBtn.style.visibility = 'hidden';
|
poll.add(this.pollFuncWrapper, this.pollInterval);
|
||||||
} else {
|
this.refreshBtn.style.visibility = 'hidden';
|
||||||
poll.remove(this.pollFuncWrapper);
|
} else {
|
||||||
this.refreshBtn.style.visibility = 'visible';
|
poll.remove(this.pollFuncWrapper);
|
||||||
|
this.refreshBtn.style.visibility = 'visible';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
resetFormValues() {
|
resetFormValues() {
|
||||||
this.tailInput.value = this.tailValue;
|
this.tailInput.value = this.tailValue;
|
||||||
this.timeFilter.value = this.timeFilterValue;
|
this.timeFilter.value = this.timeFilterValue;
|
||||||
|
this.timeFilterRe.checked = this.timeFilterReValue;
|
||||||
if(this.isHosts) {
|
if(this.isHosts) {
|
||||||
this.logHostsDropdown.setValue(this.hostFilterValue);
|
this.logHostsDropdown.setValue(this.hostFilterValue);
|
||||||
};
|
};
|
||||||
@@ -371,55 +406,78 @@ return baseclass.extend({
|
|||||||
this.logLevelsDropdown.setValue(this.levelFilterValue);
|
this.logLevelsDropdown.setValue(this.levelFilterValue);
|
||||||
};
|
};
|
||||||
this.msgFilter.value = this.msgFilterValue;
|
this.msgFilter.value = this.msgFilterValue;
|
||||||
|
this.msgFilterRe.checked = this.msgFilterReValue;
|
||||||
this.logSorting.value = this.logSortingValue;
|
this.logSorting.value = this.logSortingValue;
|
||||||
this.autoRefresh.checked = this.autoRefreshValue;
|
this.autoRefresh.checked = this.autoRefreshValue;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receives raw log data.
|
* Receives raw log data.
|
||||||
* Abstract method, must be overridden by a subclass!
|
* Abstract method, must be overridden by a subclass!
|
||||||
*
|
*
|
||||||
* @param {number} tail
|
* @instance
|
||||||
* @returns {string}
|
* @abstract
|
||||||
* Returns the raw content of the log.
|
*
|
||||||
*/
|
* @param {number} tail
|
||||||
|
* @returns {string}
|
||||||
|
* Returns the raw content of the log.
|
||||||
|
*/
|
||||||
getLogData(tail) {
|
getLogData(tail) {
|
||||||
throw new Error('getLogData must be overridden by a subclass');
|
throw new Error('getLogData must be overridden by a subclass');
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses log data.
|
* Parses log data.
|
||||||
* Abstract method, must be overridden by a subclass!
|
* Abstract method, must be overridden by a subclass!
|
||||||
*
|
*
|
||||||
* @param {string} logdata
|
* @instance
|
||||||
* @param {number} tail
|
* @abstract
|
||||||
* @returns {Array<number, string|null, string|null, string|null, string|null, string|null>}
|
*
|
||||||
* Returns an array of values: [ #, Timestamp, Host, Level, Facility, Message ].
|
* @param {string} logdata
|
||||||
*/
|
* @param {number} tail
|
||||||
|
* @returns {Array<number, string|null, string|null, string|null, string|null, string|null>}
|
||||||
|
* Returns an array of values: [ #, Timestamp, Host, Level, Facility, Message ].
|
||||||
|
*/
|
||||||
parseLogData(logdata, tail) {
|
parseLogData(logdata, tail) {
|
||||||
throw new Error('parseLogData must be overridden by a subclass');
|
throw new Error('parseLogData must be overridden by a subclass');
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Highlight the result of a regular expression.
|
* Highlight the result of a regular expression.
|
||||||
* Abstract method, must be overridden by a subclass!
|
* Abstract method, must be overridden by a subclass!
|
||||||
*
|
*
|
||||||
* @param {string} logdata
|
* @instance
|
||||||
* @returns {string}
|
* @abstract
|
||||||
* Returns a string with the highlighted part.
|
*
|
||||||
*/
|
* @param {string} logdata
|
||||||
regexpFilterHighlightFunc(match) {
|
* @returns {string}
|
||||||
throw new Error('regexpFilterHighlightFunc must be overridden by a subclass');
|
* Returns a string with the highlighted part.
|
||||||
|
*/
|
||||||
|
filterHighlightFunc(match) {
|
||||||
|
throw new Error('filterHighlightFunc must be overridden by a subclass');
|
||||||
},
|
},
|
||||||
|
|
||||||
setRegexpFilter(entriesArray, fieldNum, pattern) {
|
setStringFilter(entriesArray, fieldNum, pattern) {
|
||||||
|
let fArr = [];
|
||||||
|
entriesArray.forEach((e, i) => {
|
||||||
|
if(e[fieldNum] !== null && e[fieldNum].includes(pattern)) {
|
||||||
|
if(this.filterHighlightFunc) {
|
||||||
|
e[fieldNum] = e[fieldNum].replace(pattern, this.filterHighlightFunc);
|
||||||
|
};
|
||||||
|
fArr.push(e);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return fArr;
|
||||||
|
},
|
||||||
|
|
||||||
|
setRegexpFilter(entriesArray, fieldNum, pattern, formElem) {
|
||||||
let fArr = [];
|
let fArr = [];
|
||||||
try {
|
try {
|
||||||
let regExp = new RegExp(pattern, 'giu');
|
let regExp = new RegExp(pattern, 'giu');
|
||||||
entriesArray.forEach((e, i) => {
|
entriesArray.forEach((e, i) => {
|
||||||
if(e[fieldNum] !== null && regExp.test(e[fieldNum])) {
|
if(e[fieldNum] !== null && regExp.test(e[fieldNum])) {
|
||||||
if(this.regexpFilterHighlightFunc) {
|
if(this.filterHighlightFunc) {
|
||||||
e[fieldNum] = e[fieldNum].replace(regExp, this.regexpFilterHighlightFunc);
|
e[fieldNum] = e[fieldNum].replace(regExp, this.filterHighlightFunc);
|
||||||
};
|
};
|
||||||
fArr.push(e);
|
fArr.push(e);
|
||||||
};
|
};
|
||||||
@@ -437,12 +495,14 @@ return baseclass.extend({
|
|||||||
return fArr;
|
return fArr;
|
||||||
},
|
},
|
||||||
|
|
||||||
setDateFilter(entriesArray) {
|
setTimeFilter(entriesArray) {
|
||||||
let fPattern = this.timeFilterValue;
|
let fPattern = this.timeFilterValue;
|
||||||
if(!fPattern) {
|
if(!fPattern) {
|
||||||
return entriesArray;
|
return entriesArray;
|
||||||
};
|
};
|
||||||
return this.setRegexpFilter(entriesArray, 1, fPattern);
|
return (this.timeFilterReValue) ?
|
||||||
|
this.setRegexpFilter(entriesArray, 1, fPattern, this.timeFilter) :
|
||||||
|
this.setStringFilter(entriesArray, 1, fPattern);
|
||||||
},
|
},
|
||||||
|
|
||||||
setHostFilter(entriesArray) {
|
setHostFilter(entriesArray) {
|
||||||
@@ -484,17 +544,22 @@ return baseclass.extend({
|
|||||||
if(!fPattern) {
|
if(!fPattern) {
|
||||||
return entriesArray;
|
return entriesArray;
|
||||||
};
|
};
|
||||||
return this.setRegexpFilter(entriesArray, 5, fPattern);
|
return (this.msgFilterReValue) ?
|
||||||
|
this.setRegexpFilter(entriesArray, 5, fPattern, this.msgFilter) :
|
||||||
|
this.setStringFilter(entriesArray, 5, fPattern);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the contents of the log area.
|
* Creates the contents of the log area.
|
||||||
* Abstract method, must be overridden by a subclass!
|
* Abstract method, must be overridden by a subclass!
|
||||||
*
|
*
|
||||||
* @param {Array<number, string|null, string|null, string|null, string|null, string|null>} logdataArray
|
* @instance
|
||||||
* @returns {Node}
|
* @abstract
|
||||||
* Returns a DOM node containing the log area.
|
*
|
||||||
*/
|
* @param {Array<number, string|null, string|null, string|null, string|null, string|null>} logdataArray
|
||||||
|
* @returns {Node}
|
||||||
|
* Returns a DOM node containing the log area.
|
||||||
|
*/
|
||||||
makeLogArea(logdataArray) {
|
makeLogArea(logdataArray) {
|
||||||
throw new Error('makeLogArea must be overridden by a subclass');
|
throw new Error('makeLogArea must be overridden by a subclass');
|
||||||
},
|
},
|
||||||
@@ -524,7 +589,7 @@ return baseclass.extend({
|
|||||||
});
|
});
|
||||||
link.click();
|
link.click();
|
||||||
URL.revokeObjectURL(link.href);
|
URL.revokeObjectURL(link.href);
|
||||||
}).catch(() => {
|
}).catch(err => {
|
||||||
ui.addNotification(null,
|
ui.addNotification(null,
|
||||||
E('p', {}, _('Download error') + ': ' + err.message));
|
E('p', {}, _('Download error') + ': ' + err.message));
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
@@ -541,9 +606,11 @@ return baseclass.extend({
|
|||||||
if(logSortingLocal) {
|
if(logSortingLocal) {
|
||||||
this.logSortingValue = logSortingLocal;
|
this.logSortingValue = logSortingLocal;
|
||||||
};
|
};
|
||||||
let autoRefreshLocal = localStorage.getItem(`luci-app-${this.viewName}-autoRefreshValue`);
|
if(this.isAutorefresh) {
|
||||||
if(autoRefreshLocal) {
|
let autoRefreshLocal = localStorage.getItem(`luci-app-${this.viewName}-autoRefreshValue`);
|
||||||
this.autoRefreshValue = Boolean(Number(autoRefreshLocal));
|
if(autoRefreshLocal) {
|
||||||
|
this.autoRefreshValue = Boolean(Number(autoRefreshLocal));
|
||||||
|
};
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -557,9 +624,11 @@ return baseclass.extend({
|
|||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
`luci-app-${this.viewName}-logSortingValue`, logSortingValue);
|
`luci-app-${this.viewName}-logSortingValue`, logSortingValue);
|
||||||
};
|
};
|
||||||
if(this.autoRefreshValue != autoRefreshValue) {
|
if(this.isAutorefresh) {
|
||||||
localStorage.setItem(
|
if(this.autoRefreshValue != autoRefreshValue) {
|
||||||
`luci-app-${this.viewName}-autoRefreshValue`, String(Number(autoRefreshValue)));
|
localStorage.setItem(
|
||||||
|
`luci-app-${this.viewName}-autoRefreshValue`, String(Number(autoRefreshValue)));
|
||||||
|
};
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -578,7 +647,7 @@ return baseclass.extend({
|
|||||||
this.setFacilityFilter(
|
this.setFacilityFilter(
|
||||||
this.setLevelFilter(
|
this.setLevelFilter(
|
||||||
this.setHostFilter(
|
this.setHostFilter(
|
||||||
this.setDateFilter(
|
this.setTimeFilter(
|
||||||
this.parseLogData(logdata, tail)
|
this.parseLogData(logdata, tail)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -598,13 +667,16 @@ return baseclass.extend({
|
|||||||
this.logHostsDropdownElem = this.makeLogHostsDropdownSection();
|
this.logHostsDropdownElem = this.makeLogHostsDropdownSection();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if(!autorefresh) {
|
||||||
|
poll.start();
|
||||||
|
};
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
if(modal) {
|
if(modal) {
|
||||||
ui.hideModal();
|
ui.hideModal();
|
||||||
};
|
};
|
||||||
if(!autorefresh) {
|
if(!autorefresh) {
|
||||||
this.enableFormElems();
|
this.enableFormElems();
|
||||||
poll.start();
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -619,16 +691,51 @@ return baseclass.extend({
|
|||||||
'class': 'cbi-value-title',
|
'class': 'cbi-value-title',
|
||||||
'for' : 'tailInput',
|
'for' : 'tailInput',
|
||||||
}, _('Last entries')),
|
}, _('Last entries')),
|
||||||
E('div', { 'class': 'cbi-value-field' },
|
E('div', { 'class': 'cbi-value-field' }, [
|
||||||
this.tailInput
|
this.tailInput,
|
||||||
),
|
E('button', {
|
||||||
|
'class': 'cbi-button btn',
|
||||||
|
'click': L.bind(ev => {
|
||||||
|
ev.target.blur();
|
||||||
|
ev.preventDefault();
|
||||||
|
this.tailInput.value = 0;
|
||||||
|
this.tailInput.focus();
|
||||||
|
}, this),
|
||||||
|
}, '⌫'),
|
||||||
|
]),
|
||||||
]),
|
]),
|
||||||
E('div', { 'class': 'cbi-value' }, [
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
E('label', {
|
E('label', {
|
||||||
'class': 'cbi-value-title',
|
'class': 'cbi-value-title',
|
||||||
'for' : 'timeFilter',
|
'for' : 'timeFilter',
|
||||||
}, _('Timestamp filter')),
|
}, _('Timestamp filter')),
|
||||||
E('div', { 'class': 'cbi-value-field' }, this.timeFilter),
|
E('div', { 'class': 'cbi-value-field' }, [
|
||||||
|
this.timeFilter,
|
||||||
|
E('button', {
|
||||||
|
'class': 'cbi-button btn',
|
||||||
|
'click': L.bind(ev => {
|
||||||
|
ev.target.blur();
|
||||||
|
ev.preventDefault();
|
||||||
|
this.timeFilter.value = null;
|
||||||
|
this.timeFilter.focus();
|
||||||
|
}, this),
|
||||||
|
}, '⌫'),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
|
E('label', {
|
||||||
|
'class': 'cbi-value-title',
|
||||||
|
'for' : 'timeFilterRe',
|
||||||
|
}, _('Filter is regexp')),
|
||||||
|
E('div', { 'class': 'cbi-value-field' }, [
|
||||||
|
E('div', { 'class': 'cbi-checkbox' }, [
|
||||||
|
this.timeFilterRe,
|
||||||
|
E('label', {}),
|
||||||
|
]),
|
||||||
|
E('div', { 'class': 'cbi-value-description' },
|
||||||
|
_('Apply timestamp filter as regular expression')
|
||||||
|
),
|
||||||
|
]),
|
||||||
]),
|
]),
|
||||||
this.logHostsDropdownElem,
|
this.logHostsDropdownElem,
|
||||||
this.logFacilitiesDropdownElem,
|
this.logFacilitiesDropdownElem,
|
||||||
@@ -638,9 +745,34 @@ return baseclass.extend({
|
|||||||
'class': 'cbi-value-title',
|
'class': 'cbi-value-title',
|
||||||
'for' : 'msgFilter',
|
'for' : 'msgFilter',
|
||||||
}, _('Message filter')),
|
}, _('Message filter')),
|
||||||
E('div', { 'class': 'cbi-value-field' }, this.msgFilter),
|
E('div', { 'class': 'cbi-value-field' }, [
|
||||||
|
this.msgFilter,
|
||||||
|
E('button', {
|
||||||
|
'class': 'cbi-button btn',
|
||||||
|
'click': L.bind(ev => {
|
||||||
|
ev.target.blur();
|
||||||
|
ev.preventDefault();
|
||||||
|
this.msgFilter.value = null;
|
||||||
|
this.msgFilter.focus();
|
||||||
|
}, this),
|
||||||
|
}, '⌫'),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
|
E('label', {
|
||||||
|
'class': 'cbi-value-title',
|
||||||
|
'for' : 'msgFilterRe',
|
||||||
|
}, _('Filter is regexp')),
|
||||||
|
E('div', { 'class': 'cbi-value-field' }, [
|
||||||
|
E('div', { 'class': 'cbi-checkbox' }, [
|
||||||
|
this.msgFilterRe,
|
||||||
|
E('label', {}),
|
||||||
|
]),
|
||||||
|
E('div', { 'class': 'cbi-value-description' },
|
||||||
|
_('Apply message filter as regular expression')
|
||||||
|
),
|
||||||
|
]),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
E('div', { 'class': 'cbi-value' }, [
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
E('label', {
|
E('label', {
|
||||||
'class': 'cbi-value-title',
|
'class': 'cbi-value-title',
|
||||||
@@ -648,19 +780,19 @@ return baseclass.extend({
|
|||||||
}, _('Sorting entries')),
|
}, _('Sorting entries')),
|
||||||
E('div', { 'class': 'cbi-value-field' }, this.logSorting),
|
E('div', { 'class': 'cbi-value-field' }, this.logSorting),
|
||||||
]),
|
]),
|
||||||
|
((this.isAutorefresh) ?
|
||||||
E('div', { 'class': 'cbi-value' }, [
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
E('label', {
|
E('label', {
|
||||||
'class': 'cbi-value-title',
|
'class': 'cbi-value-title',
|
||||||
'for' : 'autoRefresh',
|
'for' : 'autoRefresh',
|
||||||
}, _('Auto refresh')),
|
}, _('Auto refresh')),
|
||||||
E('div', { 'class': 'cbi-value-field' },
|
E('div', { 'class': 'cbi-value-field' },
|
||||||
E('div', { 'class': 'cbi-checkbox' }, [
|
E('div', { 'class': 'cbi-checkbox' }, [
|
||||||
this.autoRefresh,
|
this.autoRefresh,
|
||||||
E('label', {})
|
E('label', {}),
|
||||||
])
|
])
|
||||||
),
|
),
|
||||||
]),
|
]) : ''),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
@@ -670,8 +802,10 @@ return baseclass.extend({
|
|||||||
'class': 'btn',
|
'class': 'btn',
|
||||||
'click': ev => {
|
'click': ev => {
|
||||||
ev.target.blur();
|
ev.target.blur();
|
||||||
ui.hideModal();
|
|
||||||
this.resetFormValues();
|
this.resetFormValues();
|
||||||
|
this.timeFilter.focus();
|
||||||
|
this.msgFilter.focus();
|
||||||
|
ui.hideModal();
|
||||||
},
|
},
|
||||||
}, _('Dismiss')),
|
}, _('Dismiss')),
|
||||||
' ',
|
' ',
|
||||||
@@ -696,20 +830,27 @@ return baseclass.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a promise for the RPC request.
|
* Creates a promise for the RPC request.
|
||||||
* Abstract method, must be overridden by a subclass!
|
* Abstract method, must be overridden by a subclass!
|
||||||
*
|
*
|
||||||
* @returns {Promise}
|
* To completely disable the auto log refresh option, views extending
|
||||||
* Returns a promise that returns the size of the log in bytes.
|
* this base class should overwrite the `getLogHash` function
|
||||||
*/
|
* with `null`.
|
||||||
getLogSize() {
|
*
|
||||||
throw new Error('getLogSize must be overridden by a subclass');
|
* @instance
|
||||||
|
* @abstract
|
||||||
|
*
|
||||||
|
* @returns {Promise}
|
||||||
|
* Returns a promise that returns the unique value for the current log state.
|
||||||
|
*/
|
||||||
|
getLogHash() {
|
||||||
|
throw new Error('getLogHash must be overridden by a subclass');
|
||||||
},
|
},
|
||||||
|
|
||||||
async pollFunc() {
|
async pollFunc() {
|
||||||
await this.getLogSize().then(async bytes => {
|
await this.getLogHash().then(async hash => {
|
||||||
if(this.lastBytes != bytes) {
|
if(this.lastHash !== hash) {
|
||||||
this.lastBytes = bytes;
|
this.lastHash = hash;
|
||||||
return await this.updateLog(true);
|
return await this.updateLog(true);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -725,6 +866,10 @@ return baseclass.extend({
|
|||||||
|
|
||||||
load() {
|
load() {
|
||||||
this.restoreSettingsFromLocalStorage();
|
this.restoreSettingsFromLocalStorage();
|
||||||
|
if(typeof(this.getLogHash) != 'function') {
|
||||||
|
this.isAutorefresh = false;
|
||||||
|
this.autoRefreshValue = false;
|
||||||
|
};
|
||||||
return this.getLogData(this.tailValue);
|
return this.getLogData(this.tailValue);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -769,18 +914,38 @@ return baseclass.extend({
|
|||||||
'type' : 'text',
|
'type' : 'text',
|
||||||
'form' : 'logFilterForm',
|
'form' : 'logFilterForm',
|
||||||
'class' : 'cbi-input-text',
|
'class' : 'cbi-input-text',
|
||||||
'placeholder': _('Type an expression...'),
|
'placeholder': _('Type a search pattern...'),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.timeFilterRe = E('input', {
|
||||||
|
'id' : 'timeFilterRe',
|
||||||
|
'name' : 'timeFilterRe',
|
||||||
|
'type' : 'checkbox',
|
||||||
|
'form' : 'logFilterForm',
|
||||||
|
'change': ev => this.timeFilter.focus(),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setRegexpValidator(this.timeFilter, this.timeFilterRe);
|
||||||
|
|
||||||
this.msgFilter = E('input', {
|
this.msgFilter = E('input', {
|
||||||
'id' : 'msgFilter',
|
'id' : 'msgFilter',
|
||||||
'name' : 'msgFilter',
|
'name' : 'msgFilter',
|
||||||
'type' : 'text',
|
'type' : 'text',
|
||||||
'form' : 'logFilterForm',
|
'form' : 'logFilterForm',
|
||||||
'class' : 'cbi-input-text',
|
'class' : 'cbi-input-text',
|
||||||
'placeholder': _('Type an expression...'),
|
'placeholder': _('Type a search pattern...'),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.msgFilterRe = E('input', {
|
||||||
|
'id' : 'msgFilterRe',
|
||||||
|
'name' : 'msgFilterRe',
|
||||||
|
'type' : 'checkbox',
|
||||||
|
'form' : 'logFilterForm',
|
||||||
|
'change': ev => this.msgFilter.focus(),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setRegexpValidator(this.msgFilter, this.msgFilterRe);
|
||||||
|
|
||||||
this.logSorting = E('select', {
|
this.logSorting = E('select', {
|
||||||
'id' : 'logSorting',
|
'id' : 'logSorting',
|
||||||
'name' : 'logSorting',
|
'name' : 'logSorting',
|
||||||
@@ -823,7 +988,7 @@ return baseclass.extend({
|
|||||||
|
|
||||||
this.refreshBtn = E('button', {
|
this.refreshBtn = E('button', {
|
||||||
'title' : _('Refresh log'),
|
'title' : _('Refresh log'),
|
||||||
'class' : 'btn log-side-btn',
|
'class' : 'cbi-button btn log-side-btn',
|
||||||
'style' : `visibility:${(this.autoRefreshValue) ? 'hidden' : 'visible'}`,
|
'style' : `visibility:${(this.autoRefreshValue) ? 'hidden' : 'visible'}`,
|
||||||
'click' : ui.createHandlerFn(this, function(ev) {
|
'click' : ui.createHandlerFn(this, function(ev) {
|
||||||
ev.target.blur();
|
ev.target.blur();
|
||||||
@@ -833,7 +998,7 @@ return baseclass.extend({
|
|||||||
|
|
||||||
this.moreEntriesBtn = E('button', {
|
this.moreEntriesBtn = E('button', {
|
||||||
'title': _('Get more entries'),
|
'title': _('Get more entries'),
|
||||||
'class': 'btn log-side-btn',
|
'class': 'cbi-button btn log-side-btn',
|
||||||
'style': 'margin-top:1px !important',
|
'style': 'margin-top:1px !important',
|
||||||
'click': ui.createHandlerFn(this, function(ev) {
|
'click': ui.createHandlerFn(this, function(ev) {
|
||||||
ev.target.blur();
|
ev.target.blur();
|
||||||
@@ -849,7 +1014,7 @@ return baseclass.extend({
|
|||||||
|
|
||||||
this.allEntriesBtn = E('button', {
|
this.allEntriesBtn = E('button', {
|
||||||
'title': _('Get all entries'),
|
'title': _('Get all entries'),
|
||||||
'class': 'btn log-side-btn',
|
'class': 'cbi-button btn log-side-btn',
|
||||||
'style': 'margin-top:1px !important',
|
'style': 'margin-top:1px !important',
|
||||||
'click': ui.createHandlerFn(this, function(ev) {
|
'click': ui.createHandlerFn(this, function(ev) {
|
||||||
ev.target.blur();
|
ev.target.blur();
|
||||||
@@ -860,7 +1025,7 @@ return baseclass.extend({
|
|||||||
|
|
||||||
this.filterModalBtn = E('button', {
|
this.filterModalBtn = E('button', {
|
||||||
'title': _('Filter settings'),
|
'title': _('Filter settings'),
|
||||||
'class': 'btn log-side-btn',
|
'class': 'cbi-button btn log-side-btn',
|
||||||
'style': 'margin-top:10px !important',
|
'style': 'margin-top:10px !important',
|
||||||
'click': ev => {
|
'click': ev => {
|
||||||
ev.target.blur();
|
ev.target.blur();
|
||||||
@@ -883,7 +1048,7 @@ return baseclass.extend({
|
|||||||
this.allEntriesBtn,
|
this.allEntriesBtn,
|
||||||
this.filterModalBtn,
|
this.filterModalBtn,
|
||||||
E('button', {
|
E('button', {
|
||||||
'class': 'btn log-side-btn',
|
'class': 'cbi-button btn log-side-btn',
|
||||||
'style': 'margin-top:10px !important',
|
'style': 'margin-top:10px !important',
|
||||||
'click': ev => {
|
'click': ev => {
|
||||||
document.getElementById('logTitle').scrollIntoView(true);
|
document.getElementById('logTitle').scrollIntoView(true);
|
||||||
@@ -891,7 +1056,7 @@ return baseclass.extend({
|
|||||||
},
|
},
|
||||||
}, '↑'),
|
}, '↑'),
|
||||||
E('button', {
|
E('button', {
|
||||||
'class': 'btn log-side-btn',
|
'class': 'cbi-button btn log-side-btn',
|
||||||
'style': 'margin-top:1px !important',
|
'style': 'margin-top:1px !important',
|
||||||
'click': ev => {
|
'click': ev => {
|
||||||
this.logWrapper.scrollIntoView(false);
|
this.logWrapper.scrollIntoView(false);
|
||||||
@@ -901,7 +1066,7 @@ return baseclass.extend({
|
|||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
if(this.autoRefreshValue) {
|
if(this.isAutorefresh && this.autoRefreshValue) {
|
||||||
poll.add(this.pollFuncWrapper, this.pollInterval);
|
poll.add(this.pollFuncWrapper, this.pollInterval);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ document.head.append(E('style', {'type': 'text/css'},
|
|||||||
return baseclass.extend({
|
return baseclass.extend({
|
||||||
view: abc.view.extend({
|
view: abc.view.extend({
|
||||||
|
|
||||||
regexpFilterHighlightFunc(match) {
|
filterHighlightFunc(match) {
|
||||||
return `<span class="log-highlight-item">${match}</span>`;
|
return `<span class="log-highlight-item">${match}</span>`;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -15,23 +15,7 @@ return abc.view.extend({
|
|||||||
|
|
||||||
entriesHandler : null,
|
entriesHandler : null,
|
||||||
|
|
||||||
lastBytes : 0,
|
getLogHash : null,
|
||||||
|
|
||||||
callLogSize: rpc.declare({
|
|
||||||
object: 'luci.ruantiblock',
|
|
||||||
method: 'getLogSize',
|
|
||||||
expect: { '': {} }
|
|
||||||
}),
|
|
||||||
|
|
||||||
getLogSize() {
|
|
||||||
return this.callLogSize().then((data) => {
|
|
||||||
if(data.bytes) {
|
|
||||||
return Number(data.bytes);
|
|
||||||
} else {
|
|
||||||
throw new Error(_('An error occurred while trying to get the log size!'));
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// logd
|
// logd
|
||||||
logdHandler(strArray, lineNum) {
|
logdHandler(strArray, lineNum) {
|
||||||
@@ -62,23 +46,34 @@ return abc.view.extend({
|
|||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
|
||||||
getLogData(tail) {
|
checkLogread() {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
L.resolveDefault(fs.stat('/sbin/logread'), null),
|
L.resolveDefault(fs.stat('/sbin/logread'), null),
|
||||||
L.resolveDefault(fs.stat('/usr/sbin/logread'), null),
|
L.resolveDefault(fs.stat('/usr/sbin/logread'), null),
|
||||||
]).then(stat => {
|
]).then(stat => {
|
||||||
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.appName + ':' ], 'text').catch(err => {
|
this.logger = logger;
|
||||||
ui.addNotification(
|
} else {
|
||||||
null, E('p', {}, _('Unable to load log data:') + ' ' + err.message));
|
throw new Error(_('Logread not found'));
|
||||||
return '';
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async getLogData(tail, extraTstamp=false) {
|
||||||
|
if(!this.logger) {
|
||||||
|
await this.checkLogread();
|
||||||
|
};
|
||||||
|
let loggerArgs = [];
|
||||||
|
loggerArgs.push('-e', tools.appName + ':');
|
||||||
|
if(extraTstamp) {
|
||||||
|
loggerArgs.push('-t');
|
||||||
|
};
|
||||||
|
return fs.exec_direct(this.logger, loggerArgs, 'text').catch(err => {
|
||||||
|
throw new Error(_('Unable to load log data:') + ' ' + err.message);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
parseLogData(logdata, tail) {
|
parseLogData(logdata, tail) {
|
||||||
if(!logdata) {
|
if(!logdata) {
|
||||||
return [];
|
return [];
|
||||||
@@ -127,11 +122,7 @@ return abc.view.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if(unsupportedLog) {
|
if(unsupportedLog) {
|
||||||
ui.addNotification(
|
throw new Error(_('Unable to load log data:') + ' ' + _('Unsupported log format'));
|
||||||
null,
|
|
||||||
E('p', {}, _('Unable to load log data:') + ' ' + _('Unsupported log format'))
|
|
||||||
);
|
|
||||||
return [];
|
|
||||||
} else {
|
} else {
|
||||||
if(this.logSortingValue === 'desc') {
|
if(this.logSortingValue === 'desc') {
|
||||||
entriesArray.reverse();
|
entriesArray.reverse();
|
||||||
|
|||||||
@@ -263,6 +263,9 @@ msgstr "Записывать события в лог"
|
|||||||
msgid "Logging levels"
|
msgid "Logging levels"
|
||||||
msgstr "Уровни логирования"
|
msgstr "Уровни логирования"
|
||||||
|
|
||||||
|
msgid "Logread not found"
|
||||||
|
msgstr "Logread не найден"
|
||||||
|
|
||||||
msgid "Main settings"
|
msgid "Main settings"
|
||||||
msgstr "Основные настройки"
|
msgstr "Основные настройки"
|
||||||
|
|
||||||
|
|||||||
@@ -244,6 +244,9 @@ msgstr ""
|
|||||||
msgid "Logging levels"
|
msgid "Logging levels"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Logread not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Main settings"
|
msgid "Main settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
. /lib/functions.sh
|
|
||||||
. /usr/share/libubox/jshn.sh
|
|
||||||
|
|
||||||
readonly LOGREAD_CMD="logread -e ruantiblock:"
|
|
||||||
|
|
||||||
make_size_value() {
|
|
||||||
json_init
|
|
||||||
json_add_string 'bytes' "$1"
|
|
||||||
json_dump
|
|
||||||
json_cleanup
|
|
||||||
}
|
|
||||||
|
|
||||||
get_log_size() {
|
|
||||||
make_size_value "`$LOGREAD_CMD | wc -c`"
|
|
||||||
}
|
|
||||||
|
|
||||||
case "$1" in
|
|
||||||
list)
|
|
||||||
json_init
|
|
||||||
json_add_object "getLogSize"
|
|
||||||
json_close_object
|
|
||||||
json_dump
|
|
||||||
json_cleanup
|
|
||||||
;;
|
|
||||||
call)
|
|
||||||
case "$2" in
|
|
||||||
getLogSize)
|
|
||||||
get_log_size
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
@@ -18,8 +18,7 @@
|
|||||||
},
|
},
|
||||||
"uci": [ "network", "ruantiblock" ],
|
"uci": [ "network", "ruantiblock" ],
|
||||||
"ubus": {
|
"ubus": {
|
||||||
"luci": [ "getInitList", "setInitAction" ],
|
"luci": [ "getInitList", "setInitAction" ]
|
||||||
"luci.ruantiblock": [ "getLogSize" ]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"write": {
|
"write": {
|
||||||
|
|||||||
Reference in New Issue
Block a user