You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
490 lines
15 KiB
Plaintext
490 lines
15 KiB
Plaintext
<!DOCTYPE html>
|
|
<!--
|
|
Tomato GUI
|
|
Copyright (C) 2006-2010 Jonathan Zarate
|
|
http://www.polarcloud.com/tomato/
|
|
|
|
For use with Tomato Firmware only.
|
|
No part of this file may be used without permission.
|
|
-->
|
|
<html lang="en-GB">
|
|
<head>
|
|
<meta http-equiv="content-type" content="text/html;charset=utf-8">
|
|
<meta name="robots" content="noindex,nofollow">
|
|
<title>[<% ident(); %>] Status: Logs</title>
|
|
<link rel="stylesheet" type="text/css" href="tomato.css?rel=<% version(); %>">
|
|
<% css(); %>
|
|
<script src="tomato.js?rel=<% version(); %>"></script>
|
|
|
|
<script>
|
|
|
|
// <% nvram("log_file"); %>
|
|
|
|
var cprefix = 'status_log';
|
|
|
|
var currentSearch = '';
|
|
var negativeSearch = 0;
|
|
var currentFilterValue = 0;
|
|
var currentlyScrolling = false;
|
|
var scrollingDetectorTimeout;
|
|
var messages;
|
|
var entriesMode = 0;
|
|
var entriesLast = -1;
|
|
var time = '';
|
|
|
|
var LINE_PARSE_MAP_DATE_POS = 0;
|
|
var LINE_PARSE_MAP_FACILITY_POS = 1;
|
|
var LINE_PARSE_MAP_LEVEL_POS = 2;
|
|
var LINE_PARSE_MAP_LEVEL_ATTR_POS = 3;
|
|
var LINE_PARSE_MAP_LEVEL_PROCESS_POS = 4;
|
|
var LINE_PARSE_MAP_LEVEL_MESSAGE_POS = 5;
|
|
|
|
var ref = new TomatoRefresh('update.cgi', 'exec=showlog', 5, 'status_log');
|
|
|
|
ref.refresh = function(text) {
|
|
try {
|
|
messages = text.split('\n');
|
|
if (!currentlyScrolling) {
|
|
var willScroll = false;
|
|
var tableDiv = E('log-table');
|
|
if (tableDiv.offsetHeight + tableDiv.scrollTop >= tableDiv.scrollHeight)
|
|
willScroll = true;
|
|
|
|
logGrid.populate();
|
|
|
|
if (willScroll)
|
|
scrollToBottom();
|
|
}
|
|
}
|
|
catch (ex) {
|
|
}
|
|
}
|
|
|
|
var logHeaderGrid = new TomatoGrid();
|
|
|
|
logHeaderGrid.setup = function() {
|
|
this.init('log-table-header');
|
|
this.headerSet(['Date','Facility','Level','Process','Message']);
|
|
}
|
|
|
|
var logGrid = new TomatoGrid();
|
|
|
|
logGrid.setup = function() {
|
|
this.init('log-table', '', 4001);
|
|
this.canSort = false;
|
|
this.canEdit = false;
|
|
this.canMove = false;
|
|
this.canDelete = false
|
|
this.populate();
|
|
}
|
|
|
|
logGrid.populate = function() {
|
|
this.removeAllData();
|
|
|
|
if (messages != null) {
|
|
var messagesToAdd = messages.concat();
|
|
time = messagesToAdd.shift(); /* always present in response */
|
|
|
|
if (entriesMode != 0)
|
|
messagesToAdd = messagesToAdd.slice(-1 * entriesMode - 1);
|
|
|
|
var localSearch;
|
|
if (currentSearch) {
|
|
localSearch = currentSearch;
|
|
if (localSearch.substr(0, 1) == '-') {
|
|
localSearch = localSearch.substr(1);
|
|
negativeSearch = 1;
|
|
}
|
|
else
|
|
negativeSearch = 0;
|
|
}
|
|
|
|
var count = 0;
|
|
for (var index = 0; index < messagesToAdd.length; ++index) {
|
|
if (messagesToAdd[index]) {
|
|
var logLineMap = getLogLineParsedMap(messagesToAdd[index]);
|
|
if ((currentFilterValue == 0) || (E('maxlevel').checked ? (currentFilterValue >= logLineMap[LINE_PARSE_MAP_LEVEL_ATTR_POS][1]) : (currentFilterValue == logLineMap[LINE_PARSE_MAP_LEVEL_ATTR_POS][1]))) {
|
|
if (!localSearch || containsSearch(logLineMap, localSearch)) {
|
|
var row = createHighlightedRow(logLineMap);
|
|
this.insert(-1, row, row, true);
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
E('log-occurence-span').style.display = (currentSearch ? 'inline' : 'none');
|
|
elem.setInnerHTML('log-occurence-value', count);
|
|
if (time.indexOf('Not Available') === -1)
|
|
elem.setInnerHTML('log-refresh-time', time.match(/(\d+\:\d+\:\d+)\s(.*)/i)[1]+' - Last Refreshed');
|
|
|
|
var e = E('log-table').children[0].children[0].children[0];
|
|
if (e) {
|
|
var d = E('log-table-header').children[0].children[0].children[0];
|
|
d.children[0].style.width = getComputedStyle(e.children[0]).width;
|
|
d.children[1].style.width = getComputedStyle(e.children[1]).width;
|
|
d.children[2].style.width = getComputedStyle(e.children[2]).width;
|
|
d.children[3].style.width = getComputedStyle(e.children[3]).width;
|
|
}
|
|
}
|
|
}
|
|
|
|
logGrid.onClick = function(cell) {
|
|
copyRowContent(PR(cell));
|
|
var e = E('footer-msg2');
|
|
e.style.display = 'inline-block';
|
|
setTimeout(
|
|
function() {
|
|
e.style.display = 'none';
|
|
}, 5000);
|
|
}
|
|
|
|
function copyRowContent(el) {
|
|
var body = document.body, range, sel;
|
|
if (document.createRange && window.getSelection) {
|
|
range = document.createRange();
|
|
sel = window.getSelection();
|
|
sel.removeAllRanges();
|
|
try {
|
|
range.selectNodeContents(el);
|
|
sel.addRange(range);
|
|
} catch (e) {
|
|
range.selectNode(el);
|
|
sel.addRange(range);
|
|
}
|
|
document.execCommand('copy');
|
|
sel.removeAllRanges();
|
|
}
|
|
else if (body.createTextRange) {
|
|
range = body.createTextRange();
|
|
range.moveToElementText(el);
|
|
range.select();
|
|
range.execCommand('Copy');
|
|
}
|
|
}
|
|
|
|
var logLineRegex = new RegExp(/(\w+\s+\d+\s\d+\:\d+\:\d+)\s\S+\s(\w+).(\w+)\s(\S+)\s(.*)/mi);
|
|
var errRegex = new RegExp(/^(.*?)err.*/i);
|
|
var infRegex = new RegExp(/^(.*?)inf.*/i);
|
|
var noticeRegex = new RegExp(/^(.*?)notic.*/i);
|
|
var warnRegex = new RegExp(/^(.*?)war.*/i);
|
|
var alertRegex = new RegExp(/^(.*?)aler.*/i);
|
|
var criticalRegex = new RegExp(/^(.*?)crit.*/i);
|
|
var emergencyRegex = new RegExp(/^(.*?)emer.*/i);
|
|
var debugRegex = new RegExp(/^(.*?)debu.*/i);
|
|
|
|
function containsSearch(logLineMap, text) {
|
|
var ret = (String(logLineMap[LINE_PARSE_MAP_DATE_POS]).toUpperCase().indexOf(text.toUpperCase()) >= 0 ||
|
|
String(logLineMap[LINE_PARSE_MAP_FACILITY_POS]).toUpperCase().indexOf(text.toUpperCase()) >= 0 ||
|
|
String(logLineMap[LINE_PARSE_MAP_LEVEL_PROCESS_POS]).toUpperCase().indexOf(text.toUpperCase()) >= 0 ||
|
|
String(logLineMap[LINE_PARSE_MAP_LEVEL_MESSAGE_POS]).toUpperCase().indexOf(text.toUpperCase()) >= 0);
|
|
|
|
if (negativeSearch == 1)
|
|
return !ret;
|
|
else
|
|
return ret;
|
|
}
|
|
|
|
function getLevelColor(level) {
|
|
if (emergencyRegex.test(level)) {
|
|
this.levelColor = 'loglevel-1';
|
|
this.levelNum = 1;
|
|
}
|
|
else if (alertRegex.test(level)) {
|
|
this.levelColor = 'loglevel-2';
|
|
this.levelNum = 2;
|
|
}
|
|
else if (criticalRegex.test(level)) {
|
|
this.levelColor = 'loglevel-3';
|
|
this.levelNum = 3;
|
|
}
|
|
else if (errRegex.test(level)) {
|
|
this.levelColor = 'loglevel-4';
|
|
this.levelNum = 4;
|
|
}
|
|
else if (warnRegex.test(level)) {
|
|
this.levelColor = 'loglevel-5';
|
|
this.levelNum = 5;
|
|
}
|
|
else if (noticeRegex.test(level)) {
|
|
this.levelColor = 'loglevel-6';
|
|
this.levelNum = 6;
|
|
}
|
|
else if (infRegex.test(level)) {
|
|
this.levelColor = 'loglevel-7';
|
|
this.levelNum = 7;
|
|
}
|
|
else if (debugRegex.test(level)) {
|
|
this.levelColor = 'loglevel-8';
|
|
this.levelNum = 8;
|
|
}
|
|
|
|
return [this.levelColor, this.levelNum];
|
|
}
|
|
|
|
function getLogLineParsedMap(logLine) {
|
|
var returnedArray = [];
|
|
var matchedArray = logLine.match(logLineRegex);
|
|
if (matchedArray != null) {
|
|
returnedArray[LINE_PARSE_MAP_DATE_POS] = matchedArray[1];
|
|
returnedArray[LINE_PARSE_MAP_FACILITY_POS] = matchedArray[2];
|
|
returnedArray[LINE_PARSE_MAP_LEVEL_POS] = matchedArray[3];
|
|
returnedArray[LINE_PARSE_MAP_LEVEL_ATTR_POS] = getLevelColor(returnedArray[LINE_PARSE_MAP_LEVEL_POS]);
|
|
returnedArray[LINE_PARSE_MAP_LEVEL_PROCESS_POS] = matchedArray[4].slice(0, -1);
|
|
returnedArray[LINE_PARSE_MAP_LEVEL_MESSAGE_POS] = matchedArray[5];
|
|
}
|
|
return returnedArray;
|
|
}
|
|
|
|
function createHighlightedRow(logLineMap) {
|
|
return [ generateHighlightSpan(logLineMap[LINE_PARSE_MAP_DATE_POS], 'co1', null),
|
|
generateHighlightSpan(logLineMap[LINE_PARSE_MAP_FACILITY_POS], 'co2', null),
|
|
generateHighlightSpan(logLineMap[LINE_PARSE_MAP_LEVEL_POS], 'co3', logLineMap[LINE_PARSE_MAP_LEVEL_ATTR_POS][0]),
|
|
generateHighlightSpan(logLineMap[LINE_PARSE_MAP_LEVEL_PROCESS_POS], 'co4', null),
|
|
generateHighlightSpan(escapeHTML(''+logLineMap[LINE_PARSE_MAP_LEVEL_MESSAGE_POS]), 'co5', null)
|
|
];
|
|
}
|
|
|
|
function generateHighlightSpan(innerText, classN, customStyle) {
|
|
var newText = document.createElement('td');
|
|
newText.className = classN;
|
|
if (customStyle)
|
|
newText.className += ' '+customStyle;
|
|
|
|
var indexOfSearch = innerText.toUpperCase().indexOf(currentSearch.toUpperCase());
|
|
if (indexOfSearch == -1)
|
|
elem.setInnerHTML(newText, innerText);
|
|
else {
|
|
var sizeOfSearch = currentSearch.length;
|
|
|
|
var stringBeforeFound = '';
|
|
if (indexOfSearch != 0) {
|
|
stringBeforeFound = innerText.substring(0, indexOfSearch);
|
|
newText.innerHTML += stringBeforeFound;
|
|
}
|
|
|
|
var highlightedString = innerText.substring(indexOfSearch, indexOfSearch + sizeOfSearch);
|
|
if (highlightedString)
|
|
newText.innerHTML += '<span style="background-color:yellow">'+highlightedString+'<\/span>';
|
|
|
|
var stringAfterFound = '';
|
|
if (indexOfSearch + sizeOfSearch < innerText.length) {
|
|
stringAfterFound = innerText.substring(indexOfSearch + sizeOfSearch, innerText.length);
|
|
newText.innerHTML += stringAfterFound;
|
|
}
|
|
}
|
|
|
|
return newText;
|
|
}
|
|
|
|
function filterLevelChanged() {
|
|
var filterSelector = E('filterLevelSelector');
|
|
currentFilterValue = parseInt(filterSelector.options[filterSelector.selectedIndex].value, 10);
|
|
logGrid.populate();
|
|
filterSelector.blur();
|
|
scrollToBottom();
|
|
}
|
|
|
|
function scrollToBottom() {
|
|
var objDiv = E('log-table');
|
|
objDiv.scrollTop = objDiv.scrollHeight;
|
|
}
|
|
|
|
function showEntries() {
|
|
if (entriesMode == entriesLast)
|
|
return;
|
|
|
|
var e;
|
|
elem.removeClass('entries'+entriesLast, 'selected');
|
|
if ((e = E('entries'+entriesMode)) != null) {
|
|
elem.addClass(e, 'selected');
|
|
e.blur();
|
|
}
|
|
|
|
/* retrieve only needed number of lines */
|
|
ref.postData = 'exec=showlog'+(entriesMode > 0 ? '&arg0=&arg1='+entriesMode : '');
|
|
ref.initPage(0, 1);
|
|
if (!ref.running)
|
|
ref.once = 1;
|
|
|
|
ref.start();
|
|
|
|
entriesLast = entriesMode;
|
|
}
|
|
|
|
function viewLast(n) {
|
|
entriesMode = n;
|
|
logGrid.populate();
|
|
scrollToBottom();
|
|
showEntries();
|
|
cookie.set(cprefix+'_entries', entriesMode);
|
|
}
|
|
|
|
function onTableScroll() {
|
|
/* Detector for smooth scrolling */
|
|
if (scrollingDetectorTimeout)
|
|
clearTimeout(scrollingDetectorTimeout);
|
|
|
|
currentlyScrolling = true;
|
|
scrollingDetectorTimeout = setTimeout(
|
|
function() {
|
|
currentlyScrolling = false;
|
|
},
|
|
150);
|
|
}
|
|
|
|
function onKeyUpEvent(event) {
|
|
if (event.defaultPrevented)
|
|
return;
|
|
|
|
var key = event.key || event.keyCode;
|
|
if (key === 'Escape' || key === 'Esc' || key === 27) {
|
|
E('log-find-text').value = '';
|
|
currentSearch = '';
|
|
E('log-occurence-span').style.display = 'none';
|
|
logGrid.populate();
|
|
scrollToBottom();
|
|
}
|
|
}
|
|
|
|
var onInputEvent = debounce(function() {
|
|
currentSearch = E('log-find-text').value;
|
|
logGrid.populate();
|
|
if (currentSearch.length == 0) {
|
|
E('log-occurence-span').style.display = 'none';
|
|
scrollToBottom();
|
|
}
|
|
}, 1500);
|
|
|
|
function debounce(func, wait, immediate) {
|
|
var timeout;
|
|
return function() {
|
|
var context = this, args = arguments;
|
|
var later = function() {
|
|
timeout = null;
|
|
if (!immediate)
|
|
func.apply(context, args);
|
|
};
|
|
var callNow = immediate && !timeout;
|
|
clearTimeout(timeout);
|
|
timeout = setTimeout(later, wait);
|
|
if (callNow)
|
|
func.apply(context, args);
|
|
};
|
|
}
|
|
|
|
function init() {
|
|
if (nvram.log_file != 1) {
|
|
E('logging').style.display = 'none';
|
|
E('note-disabled').style.display = 'block';
|
|
return;
|
|
}
|
|
|
|
var c;
|
|
if (((c = cookie.get(cprefix+'_entries')) != null) && (c >= '0'))
|
|
entriesMode = (cookie.get(cprefix+'_entries'));
|
|
|
|
showEntries();
|
|
|
|
logHeaderGrid.setup();
|
|
logGrid.setup();
|
|
|
|
addEvent(E('log-find-text'), 'input', onInputEvent);
|
|
addEvent(document, 'keyup', onKeyUpEvent);
|
|
|
|
ref.initPage(0, 1);
|
|
if (!ref.running)
|
|
ref.once = 1;
|
|
|
|
ref.start();
|
|
}
|
|
</script>
|
|
</head>
|
|
|
|
<body onload="init()">
|
|
<form id="t_fom" onsubmit="return false;">
|
|
<table id="container">
|
|
<tr><td colspan="2" id="header">
|
|
<div class="title"><a href="/">FreshTomato</a></div>
|
|
<div class="version">Version <% version(); %> on <% nv("t_model_name"); %></div>
|
|
</td></tr>
|
|
<tr id="body"><td id="navi"><script>navi()</script></td>
|
|
<td id="content">
|
|
<div id="ident"><% ident(); %> | <script>wikiLink();</script></div>
|
|
|
|
<!-- / / / -->
|
|
|
|
<div class="section-title">Logs</div>
|
|
<div id="logging">
|
|
<div class="section">
|
|
<div class="log-searchbox">
|
|
|
|
<span class="log-clear">» <a href="javascript:scrollToBottom()">Scroll to bottom<span class="scrollbm_icon"> ⏬</span></a></span>
|
|
|
|
<span>
|
|
Filter level:
|
|
<small class="maxlevel">Max</small> <input type="checkbox" id="maxlevel" onchange="logGrid.populate();scrollToBottom();">
|
|
<select id="filterLevelSelector" onchange="filterLevelChanged();">
|
|
<option value="0">All</option>
|
|
<option value="1">Emergency</option>
|
|
<option value="2">Alert</option>
|
|
<option value="3">Critical</option>
|
|
<option value="4">Error</option>
|
|
<option value="5">Warning</option>
|
|
<option value="6">Notice</option>
|
|
<option value="7">Info</option>
|
|
<option value="8">Debug</option>
|
|
</select>
|
|
</span>
|
|
|
|
<span>
|
|
Find in syslog:
|
|
<input type="text" id="log-find-text" autocomplete="off" maxlength="64" title="Press Escape to clear search; use '-' in front to make a negative search">
|
|
<span style="display:none" id="log-occurence-span">Occurences: <b><span id="log-occurence-value"></span></b></span>
|
|
</span>
|
|
|
|
</div>
|
|
|
|
<div class="tomato-grid" id="log-table-header"></div>
|
|
<div class="tomato-grid" id="log-table" onscroll="onTableScroll();"></div>
|
|
|
|
<div id="log-refresh-time"></div>
|
|
|
|
<div class="log-clear log-viewlast">
|
|
<span class="log-clear">» <a href="javascript:scrollToBottom()">Scroll to bottom <span class="scrollbm_icon">⏬</span></a></span>
|
|
<span style="display:block;margin-top:0.9em"> </span>
|
|
<span class="log-download">» <a href="logs/syslog.txt?_http_id=<% nv(http_id) %>">Download Log File <span class="download_icon">🔽</span></a></span>
|
|
<span style="display:block;line-height:0.3em"> </span>
|
|
<span class="log-configuration">» <a href="admin-log.asp">Logging Configuration <span class="lconfig_icon">⚙️</span></a></span>
|
|
<br><br>
|
|
</div>
|
|
<div class="log-viewlast">
|
|
<div class="log-viewlast-left">» <a href="javascript:viewLast(25)" id="entries25">Trim to last 25 entries</a></div><div class="log-viewlast-right"><a href="logs/view.cgi?which=25&_http_id=<% nv(http_id) %>">(RAW)</a></div><br>
|
|
<div class="log-viewlast-left">» <a href="javascript:viewLast(50)" id="entries50">Trim to last 50 entries</a></div><div class="log-viewlast-right"><a href="logs/view.cgi?which=50&_http_id=<% nv(http_id) %>">(RAW)</a></div><br>
|
|
<div class="log-viewlast-left">» <a href="javascript:viewLast(100)" id="entries100">Trim to last 100 entries</a></div><div class="log-viewlast-right"><a href="logs/view.cgi?which=100&_http_id=<% nv(http_id) %>">(RAW)</a></div><br>
|
|
<div class="log-viewlast-left">» <a href="javascript:viewLast(0)" id="entries0">Show all </a></div><div class="log-viewlast-right"><a href="logs/view.cgi?which=all&_http_id=<% nv(http_id) %>">(RAW)</a></div><br>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- / / / -->
|
|
|
|
<div class="note-disabled" id="note-disabled"><b>Internal logging disabled.</b><br><br><a href="admin-log.asp">Enable »</a></div>
|
|
|
|
<!-- / / / -->
|
|
|
|
<div id="footer">
|
|
<div class="log-controls">
|
|
<span id="footer-msg2" style="display:none">Highlighted row copied to clipboard.</span>
|
|
<img src="spin.gif" alt="" id="refresh-spinner">
|
|
<script>genStdTimeList('refresh-time', 'One off', 0);</script>
|
|
<input type="button" value="Refresh" onclick="ref.toggle()" id="refresh-button">
|
|
</div>
|
|
</div>
|
|
|
|
</td></tr>
|
|
</table>
|
|
</form>
|
|
</body>
|
|
</html>
|