mirror of
https://github.com/caiwx86/small-packages.git
synced 2026-10-30 05:05:13 +08:00
update 2026-07-28 01:38:11
This commit is contained in:
1 parent
20b786ab77
commit
25d570431d
6243 files changed
+2850542
No files matched your search
@@ -0,0 +1,168 @@
|
||||
'use strict';
|
||||
'require fs';
|
||||
'require rpc';
|
||||
'require ui';
|
||||
'require view.log-viewer.log-widget as widget';
|
||||
|
||||
return widget.view.extend({
|
||||
viewName : 'dmesg',
|
||||
|
||||
title : _('Kernel Log'),
|
||||
|
||||
enableAutoRefresh : true,
|
||||
|
||||
enableConvertTimestamp: true,
|
||||
|
||||
entryRegexp : new RegExp(/^<(\d{1,2})>\[([\d\s.]+)\]\s+(.*)$/),
|
||||
|
||||
entriesHandler : null,
|
||||
|
||||
facilityName : [
|
||||
'kern',
|
||||
'user',
|
||||
'mail',
|
||||
'daemon',
|
||||
'auth',
|
||||
'syslog',
|
||||
'lpr',
|
||||
'news',
|
||||
],
|
||||
|
||||
localtime : null,
|
||||
|
||||
uptime : null,
|
||||
|
||||
days : {
|
||||
0: 'Sun',
|
||||
1: 'Mon',
|
||||
2: 'Tue',
|
||||
3: 'Wed',
|
||||
4: 'Thu',
|
||||
5: 'Fri',
|
||||
6: 'Sat',
|
||||
7: 'Sun',
|
||||
},
|
||||
|
||||
months : {
|
||||
1: 'Jan',
|
||||
2: 'Feb',
|
||||
3: 'Mar',
|
||||
4: 'Apr',
|
||||
5: 'May',
|
||||
6: 'Jun',
|
||||
7: 'Jul',
|
||||
8: 'Aug',
|
||||
9: 'Sep',
|
||||
10: 'Oct',
|
||||
11: 'Nov',
|
||||
12: 'Dec',
|
||||
},
|
||||
|
||||
logCols : [
|
||||
'#',
|
||||
_('Timestamp'),
|
||||
null,
|
||||
_('Facility'),
|
||||
_('Level'),
|
||||
_('Message'),
|
||||
],
|
||||
|
||||
callLogHash : rpc.declare({
|
||||
object: 'luci.log-viewer',
|
||||
method: 'getDmesgHash',
|
||||
expect: { '': {} }
|
||||
}),
|
||||
|
||||
callSystemInfo : rpc.declare({
|
||||
object: 'system',
|
||||
method: 'info'
|
||||
}),
|
||||
|
||||
getLogHash() {
|
||||
return this.callLogHash().then(data => {
|
||||
return data.hash || '';
|
||||
});
|
||||
},
|
||||
|
||||
convertTimestampFunc(t) {
|
||||
if(!this.convertTimestampValue || !this.localtime || !this.uptime) {
|
||||
return t;
|
||||
};
|
||||
let date = new Date((this.localtime - this.uptime + Math.round(Number(t))) * 1000);
|
||||
return '%s %s %2d %02d:%02d:%02d %d'.format(
|
||||
this.days[ date.getUTCDay() ],
|
||||
this.months[ date.getUTCMonth() + 1 ],
|
||||
date.getUTCDate(),
|
||||
date.getUTCHours(),
|
||||
date.getUTCMinutes(),
|
||||
date.getUTCSeconds(),
|
||||
date.getUTCFullYear()
|
||||
);
|
||||
},
|
||||
|
||||
async getLogData(tail) {
|
||||
await this.callSystemInfo().then(s => {
|
||||
this.localtime = s.localtime;
|
||||
this.uptime = s.uptime;
|
||||
}).catch(err => {});
|
||||
return fs.exec_direct('/bin/dmesg', [ '-r' ], 'text').catch(err => {
|
||||
throw new Error(_('Unable to load log data:') + ' ' + err.message);
|
||||
});
|
||||
},
|
||||
|
||||
parseLogData(logdata, tail) {
|
||||
if(!logdata) {
|
||||
return [];
|
||||
};
|
||||
|
||||
let unsupportedLog = false;
|
||||
this.logTimestampFlag = true;
|
||||
this.logFacilitiesFlag = true;
|
||||
this.logLevelsFlag = true;
|
||||
this.logHostsFlag = false;
|
||||
|
||||
let strings = logdata.trim().split(/\n/);
|
||||
|
||||
if(tail && tail > 0 && strings) {
|
||||
strings = strings.slice(-tail);
|
||||
};
|
||||
|
||||
this.totalLogLines = strings.length;
|
||||
|
||||
let entriesArray = strings.map((e, i) => {
|
||||
let logLevelsTranslate = Object.keys(this.logLevels);
|
||||
let strArray = e.match(this.entryRegexp);
|
||||
if(strArray) {
|
||||
let level = 0;
|
||||
let facility = 0;
|
||||
if(strArray[1].length > 1) {
|
||||
let fieldArray = Number(strArray[1]).toString(8).split('');
|
||||
level = logLevelsTranslate[Number(fieldArray[1])];
|
||||
facility = Number(fieldArray[0]);
|
||||
} else {
|
||||
level = logLevelsTranslate[Number(strArray[1]).toString(8)];
|
||||
};
|
||||
return [
|
||||
i + 1, // # (Number)
|
||||
this.convertTimestampFunc(strArray[2].trim()), // Timestamp (String)
|
||||
null, // Host (String)
|
||||
this.facilityName[ facility ], // Facility (String)
|
||||
level, // Level (String)
|
||||
this.htmlEntities(strArray[3]) || ' ', // Message (String)
|
||||
];
|
||||
} else {
|
||||
unsupportedLog = true;
|
||||
return;
|
||||
};
|
||||
});
|
||||
|
||||
if(unsupportedLog) {
|
||||
throw new Error(_('Unable to load log data:') + ' ' + _('Unsupported log format'));
|
||||
} else {
|
||||
if(this.logSortingValue == 'desc') {
|
||||
entriesArray.reverse();
|
||||
};
|
||||
return entriesArray;
|
||||
};
|
||||
},
|
||||
});
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,179 @@
|
||||
'use strict';
|
||||
'require baseclass';
|
||||
'require fs';
|
||||
'require view.log-viewer.log-widget as widget';
|
||||
|
||||
return baseclass.extend({
|
||||
view: widget.view.extend({
|
||||
enableAutoRefresh: true,
|
||||
|
||||
/**
|
||||
* Pattern for picking application-specific entries from the log.
|
||||
*
|
||||
* @property {string} appPattern
|
||||
*/
|
||||
appPattern : '^',
|
||||
|
||||
/**
|
||||
* Enable "tail" option for the logread (logread -l).
|
||||
* Must be disabled for application-specific log.
|
||||
*
|
||||
* @property {bool} loggerTail
|
||||
*/
|
||||
loggerTail : false,
|
||||
|
||||
logdRegexp : new RegExp(/^([^\s]{3}\s+[^\s]{3}\s+\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2}\s+\d{4})\s+([a-z0-9]+)\.([a-z]+)\s+(.*)$/),
|
||||
|
||||
syslog_ngRegexp : new RegExp(/^([^\s]{3}\s+\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2})\s+([^\s]+)\s+(.*)$/),
|
||||
|
||||
entryRegexp : null,
|
||||
|
||||
loggerChecked : false,
|
||||
|
||||
entriesHandler : null,
|
||||
|
||||
logger : null,
|
||||
|
||||
getLogHash() {
|
||||
return this.getLogData(1, true).then(data => {
|
||||
return data || '';
|
||||
});
|
||||
},
|
||||
|
||||
// logd
|
||||
logdHandler(strArray, lineNum) {
|
||||
return [
|
||||
lineNum, // # (Number)
|
||||
strArray[1], // Timestamp (String)
|
||||
null, // Host (String)
|
||||
strArray[2], // Facility (String)
|
||||
strArray[3], // Level (String)
|
||||
this.htmlEntities(strArray[4]) || ' ', // Message (String)
|
||||
];
|
||||
},
|
||||
|
||||
// syslog-ng
|
||||
syslog_ngHandler(strArray, lineNum) {
|
||||
return [
|
||||
lineNum, // # (Number)
|
||||
strArray[1], // Timestamp (String)
|
||||
strArray[2], // Host (String)
|
||||
null, // Facility (String)
|
||||
null, // Level (String)
|
||||
this.htmlEntities(strArray[3]) || ' ', // Message (String)
|
||||
];
|
||||
},
|
||||
|
||||
// unsupported log handler
|
||||
unsupportedLogHandler(line, lineNum) {
|
||||
return [
|
||||
lineNum, // # (Number)
|
||||
null, // Timestamp (String)
|
||||
null, // Host (String)
|
||||
null, // Facility (String)
|
||||
null, // Level (String)
|
||||
this.htmlEntities(line) || ' ', // Message (String)
|
||||
];
|
||||
},
|
||||
|
||||
checkLogread() {
|
||||
return Promise.all([
|
||||
L.resolveDefault(fs.stat('/sbin/logread'), null),
|
||||
L.resolveDefault(fs.stat('/usr/sbin/logread'), null),
|
||||
]).then(stat => {
|
||||
let logger = (stat[0]) ? stat[0].path : (stat[1]) ? stat[1].path : null;
|
||||
if(logger) {
|
||||
this.logger = logger;
|
||||
} else {
|
||||
throw new Error(_('Logread not found'));
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
async getLogData(tail, extraTstamp=false) {
|
||||
if(!this.logger) {
|
||||
await this.checkLogread();
|
||||
};
|
||||
let loggerArgs = [];
|
||||
if(this.loggerTail && tail) {
|
||||
loggerArgs.push('-l', String(tail));
|
||||
};
|
||||
loggerArgs.push('-e', this.appPattern);
|
||||
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) {
|
||||
if(!logdata) {
|
||||
return [];
|
||||
};
|
||||
|
||||
let strings = logdata.trim().split(/\n/);
|
||||
|
||||
if(!this.loggerTail && tail && tail > 0 && strings) {
|
||||
strings = strings.slice(-tail);
|
||||
};
|
||||
|
||||
this.totalLogLines = strings.length;
|
||||
|
||||
let entriesArray = strings.map((e, i) => {
|
||||
if(!this.loggerChecked) {
|
||||
if(this.logdRegexp.test(e)) {
|
||||
this.entryRegexp = this.logdRegexp;
|
||||
this.logTimestampFlag = true;
|
||||
this.logFacilitiesFlag = true;
|
||||
this.logLevelsFlag = true;
|
||||
this.logHostFlag = false;
|
||||
this.entriesHandler = this.logdHandler;
|
||||
this.logCols = [
|
||||
'#',
|
||||
_('Timestamp'),
|
||||
null,
|
||||
_('Facility'),
|
||||
_('Level'),
|
||||
_('Message'),
|
||||
];
|
||||
this.loggerChecked = true;
|
||||
}
|
||||
else if(this.syslog_ngRegexp.test(e)) {
|
||||
this.entryRegexp = this.syslog_ngRegexp;
|
||||
this.logTimestampFlag = true;
|
||||
this.logFacilitiesFlag = false;
|
||||
this.logLevelsFlag = false;
|
||||
this.logHostFlag = true;
|
||||
this.logFacilities = {};
|
||||
this.logLevels = {};
|
||||
this.entriesHandler = this.syslog_ngHandler;
|
||||
this.logCols = [
|
||||
'#',
|
||||
_('Timestamp'),
|
||||
_('Host'),
|
||||
null,
|
||||
null,
|
||||
_('Message'),
|
||||
];
|
||||
this.loggerChecked = true;
|
||||
} else {
|
||||
return this.unsupportedLogHandler(e, i + 1);
|
||||
};
|
||||
};
|
||||
|
||||
let strArray = e.match(this.entryRegexp);
|
||||
if(strArray) {
|
||||
return this.entriesHandler(strArray, i + 1);
|
||||
} else {
|
||||
return this.unsupportedLogHandler(e, i + 1);
|
||||
};
|
||||
});
|
||||
|
||||
if(this.logSortingValue == 'desc') {
|
||||
entriesArray.reverse();
|
||||
};
|
||||
return entriesArray;
|
||||
},
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,117 @@
|
||||
'use strict';
|
||||
'require baseclass';
|
||||
'require ui';
|
||||
'require view.log-viewer.log-base as base';
|
||||
|
||||
document.head.append(E('style', {'type': 'text/css'},
|
||||
`
|
||||
#logTable {
|
||||
width: calc(100% - 4px);
|
||||
}
|
||||
.log-entry-time-cell {
|
||||
min-width: 14em !important;
|
||||
white-space: pre-wrap !important;
|
||||
}
|
||||
.log-entry-host-cell {
|
||||
min-width: 10em !important;
|
||||
overflow-wrap: anywhere !important;
|
||||
}
|
||||
.log-entry-message-cell {
|
||||
min-width: 20em !important;
|
||||
white-space: pre-wrap !important;
|
||||
overflow-wrap: anywhere !important;
|
||||
}
|
||||
.log-entry-text-nowrap {
|
||||
white-space: nowrap !important;
|
||||
};
|
||||
`));
|
||||
|
||||
return baseclass.extend({
|
||||
view: base.view.extend({
|
||||
|
||||
filterHighlightFunc(match) {
|
||||
return `<span class="log-highlight-item">${match}</span>`;
|
||||
},
|
||||
|
||||
makeLogArea(logdataArray) {
|
||||
let lines = `<tr class="tr"><td class="td center">${_('No entries available...')}</td></tr>`;
|
||||
let logTable = E('table', { 'id': 'logTable', 'class': 'table' });
|
||||
|
||||
for(let level of Object.keys(this.logLevels)) {
|
||||
this.logLevelsStat[level] = 0;
|
||||
};
|
||||
|
||||
if(logdataArray.length > 0) {
|
||||
lines = [];
|
||||
logdataArray.forEach((e, i) => {
|
||||
if(e[4] in this.logLevels) {
|
||||
this.logLevelsStat[e[4]] = this.logLevelsStat[e[4]] + 1;
|
||||
};
|
||||
|
||||
let line = [ `<tr class="tr log-${(e[1]) ? e[4] || 'empty' : (this.entriesHandler) ? 'raw' : 'empty'}">` ];
|
||||
this.logCols.forEach((c, i) => {
|
||||
if(c) {
|
||||
let cellClass = '';
|
||||
switch(i) {
|
||||
case 0:
|
||||
case 3:
|
||||
case 4:
|
||||
cellClass = 'log-entry-text-nowrap';
|
||||
break;
|
||||
case 1:
|
||||
cellClass = 'log-entry-time-cell';
|
||||
break;
|
||||
case 2:
|
||||
cellClass = 'log-entry-host-cell';
|
||||
break;
|
||||
case 5:
|
||||
cellClass = 'log-entry-message-cell';
|
||||
break;
|
||||
};
|
||||
line.push(`<td class="td left ${cellClass}" data-title="${c}">${e[i] || ' '}</td>`);
|
||||
};
|
||||
});
|
||||
line.push('</tr>');
|
||||
lines.push(line.join(''));
|
||||
});
|
||||
lines = lines.join('');
|
||||
|
||||
let logTableHeader = E('tr', { 'class': 'tr table-titles' });
|
||||
this.logCols.forEach(e => {
|
||||
if(e) {
|
||||
logTableHeader.append(
|
||||
E('th', { 'class': 'th left log-entry-text-nowrap' }, e)
|
||||
);
|
||||
};
|
||||
});
|
||||
logTable.append(logTableHeader);
|
||||
};
|
||||
|
||||
try {
|
||||
logTable.insertAdjacentHTML('beforeend', lines);
|
||||
} catch(err) {
|
||||
if(err.name == 'SyntaxError') {
|
||||
ui.addNotification(null,
|
||||
E('p', {}, _('HTML/XML error') + ': ' + err.message), 'error');
|
||||
};
|
||||
throw err;
|
||||
};
|
||||
|
||||
let levelsStatString = '';
|
||||
if((Object.values(this.logLevelsStat).reduce((s,c) => s + c, 0)) > 0) {
|
||||
Object.entries(this.logLevelsStat).forEach(e => {
|
||||
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>`;
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
return E([
|
||||
E('div', { 'class': 'log-entries-count' },
|
||||
`${_('Entries')}: ${logdataArray.length} / ${this.totalLogLines}${levelsStatString}`
|
||||
),
|
||||
logTable,
|
||||
]);
|
||||
},
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
'use strict';
|
||||
'require view.log-viewer.log-system as abc';
|
||||
|
||||
return abc.view.extend({
|
||||
viewName : 'syslog',
|
||||
title : _('System Log'),
|
||||
enableAutoRefresh: true,
|
||||
appPattern : '^',
|
||||
loggerTail : true,
|
||||
});
|
||||
Reference in new issue
Block a user