optimize debug logs, support auto refresh, set debug log display order, filter log level, clear debug logs

This commit is contained in:
MaysWind
2022-10-20 01:33:56 +08:00
parent 0dcda2b8c0
commit f0d51db398
11 changed files with 357 additions and 47 deletions

View File

@@ -385,12 +385,14 @@
<script src="scripts/directives/blobDownload.js"></script>
<script src="scripts/filters/dateDuration.js"></script>
<script src="scripts/filters/fileOrderBy.js"></script>
<script src="scripts/filters/logOrderBy.js"></script>
<script src="scripts/filters/longDate.js"></script>
<script src="scripts/filters/peerOrderBy.js"></script>
<script src="scripts/filters/percent.js"></script>
<script src="scripts/filters/reverse.js"></script>
<script src="scripts/filters/taskOrderBy.js"></script>
<script src="scripts/filters/taskStatus.js"></script>
<script src="scripts/filters/timeDisplayName.js"></script>
<script src="scripts/filters/volume.js"></script>
<script src="scripts/services/ariaNgAssetsCacheService.js"></script>
<script src="scripts/services/ariaNgLanguageLoader.js"></script>

View File

@@ -10,6 +10,10 @@ Cancel=取消
Close=关闭
True=是
False=否
DEBUG=调试 (Debug)
INFO=普通 (Info)
WARN=警告 (Warn)
ERROR=错误 (Error)
Connecting=连接中
Connected=已连接
Disconnected=未连接
@@ -256,6 +260,12 @@ Always=始终
Never=从不
BitTorrent=BitTorrent
Changes to the settings take effect after refreshing page.=设置将在页面刷新后生效.
Logging Time=记录时间
Log Level=日志级别
Auto Refresh=自动刷新
Refresh Now=立即刷新
Clear Logs=清空日志
Are you sure you want to clear debug logs?=您是否要清除调试日志?
Show Detail=显示详情
Log Detail=日志详情
Type is illegal!=类型错误!

View File

@@ -10,6 +10,10 @@ Cancel=取消
Close=關閉
True=是
False=否
DEBUG=偵錯 (Debug)
INFO=普通 (Info)
WARN=警告 (Warn)
ERROR=錯誤 (Error)
Connecting=連線中
Connected=已連線
Disconnected=未連線
@@ -256,6 +260,12 @@ Always=始終
Never=從不
BitTorrent=BitTorrent
Changes to the settings take effect after refreshing page.=設定將在頁面重新整理後生效.
Logging Time=記錄時間
Log Level=記錄層級
Auto Refresh=自動刷新
Refresh Now=立即刷新
Clear Logs=清除記錄
Are you sure you want to clear debug logs?=您是否要清除偵錯記錄?
Show Detail=顯示詳情
Log Detail=記錄詳情
Type is illegal!=類型錯誤!

View File

@@ -14,6 +14,10 @@
'Close': 'Close',
'True': 'True',
'False': 'False',
'DEBUG': 'Debug',
'INFO': 'Info',
'WARN': 'Warn',
'ERROR': 'Error',
'Connecting': 'Connecting',
'Connected': 'Connected',
'Disconnected': 'Disconnected',
@@ -261,6 +265,12 @@
'BitTorrent': 'BitTorrent',
'Changes to the settings take effect after refreshing page.': 'Changes to the settings take effect after refreshing page.',
'Latest {{count}} Logs': 'Latest {{count}} Logs',
'Logging Time': 'Logging Time',
'Log Level': 'Log Level',
'Auto Refresh': 'Auto Refresh',
'Refresh Now': 'Refresh Now',
'Clear Logs': 'Clear Logs',
'Are you sure you want to clear debug logs?': 'Are you sure you want to clear debug logs?',
'Show Detail': 'Show Detail',
'Log Detail': 'Log Detail',
'Type is illegal!': 'Type is illegal!',

View File

@@ -1,27 +1,158 @@
(function () {
'use strict';
angular.module('ariaNg').controller('AriaNgDebugController', ['$rootScope', '$scope', '$location', '$timeout', 'ariaNgConstants', 'ariaNgCommonService', 'ariaNgLogService', 'ariaNgSettingService', function ($rootScope, $scope, $location, $timeout, ariaNgConstants, ariaNgCommonService, ariaNgLogService, ariaNgSettingService) {
$scope.logMaxCount = ariaNgConstants.cachedDebugLogsLimit;
$scope.currentLog = null;
angular.module('ariaNg').controller('AriaNgDebugController', ['$rootScope', '$scope', '$location', '$interval', '$timeout', 'ariaNgConstants', 'ariaNgCommonService', 'ariaNgLocalizationService', 'ariaNgLogService', 'ariaNgSettingService', function ($rootScope, $scope, $location, $interval, $timeout, ariaNgConstants, ariaNgCommonService, ariaNgLocalizationService, ariaNgLogService, ariaNgSettingService) {
var tabStatusItems = [
{
name: 'logs',
show: true
}
];
var debugLogRefreshPromise = null;
var getVisibleTabOrders = function () {
var items = [];
for (var i = 0; i < tabStatusItems.length; i++) {
if (tabStatusItems[i].show) {
items.push(tabStatusItems[i].name);
}
}
return items;
};
$scope.context = {
currentTab: 'logs',
logMaxCount: ariaNgConstants.cachedDebugLogsLimit,
logAutoRefreshAvailableInterval: ariaNgCommonService.getTimeOptions([100, 200, 500, 1000, 2000], true),
logAutoRefreshInterval: 0,
logListDisplayOrder: 'time:desc',
logLevelFilter: 'DEBUG',
logs: [],
currentLog: null
};
$scope.enableDebugMode = function () {
return ariaNgSettingService.isEnableDebugMode();
};
$scope.changeTab = function (tabName) {
$scope.context.currentTab = tabName;
};
$scope.changeLogListDisplayOrder = function (type) {
var oldType = ariaNgCommonService.parseOrderType($scope.context.logListDisplayOrder);
var newType = ariaNgCommonService.parseOrderType(type);
if (newType.type === oldType.type) {
newType.reverse = !oldType.reverse;
}
$scope.context.logListDisplayOrder = newType.getValue();
};
$scope.isLogListSetDisplayOrder = function (type) {
var orderType = ariaNgCommonService.parseOrderType($scope.context.logListDisplayOrder);
var targetType = ariaNgCommonService.parseOrderType(type);
return orderType.equals(targetType);
};
$scope.getLogListOrderType = function () {
return $scope.context.logListDisplayOrder;
};
$scope.filterLog = function (log) {
if (!log || !angular.isString(log.level)) {
return false;
}
if (!$scope.context.logLevelFilter) {
return true;
}
return ariaNgLogService.compareLogLevel(log.level, $scope.context.logLevelFilter) >= 0;
};
$scope.setLogLevelFilter = function (filter) {
$scope.context.logLevelFilter = filter;
};
$scope.isSetLogLevelFilter = function (filter) {
return $scope.context.logLevelFilter === filter;
};
$scope.getLogLevelFilter = function () {
return $scope.context.logLevelFilter;
};
$scope.setAutoRefreshInterval = function (interval) {
$scope.context.logAutoRefreshInterval = interval;
if (debugLogRefreshPromise) {
$interval.cancel(debugLogRefreshPromise);
}
if (interval > 0) {
$scope.reloadLogs();
debugLogRefreshPromise = $interval(function () {
$scope.reloadLogs();
}, $scope.context.logAutoRefreshInterval);
}
};
$scope.reloadLogs = function () {
$scope.logs = ariaNgLogService.getDebugLogs().slice();
$scope.context.logs = ariaNgLogService.getDebugLogs().slice();
};
$scope.clearDebugLogs = function () {
ariaNgCommonService.confirm('Confirm Clear', 'Are you sure you want to clear debug logs?', 'warning', function () {
ariaNgLogService.clearDebugLogs();
$scope.reloadLogs();
}, false);
};
$scope.showLogDetail = function (log) {
$scope.currentLog = log;
$scope.context.currentLog = log;
angular.element('#log-detail-modal').modal();
};
$('#log-detail-modal').on('hide.bs.modal', function (e) {
$scope.currentLog = null;
$scope.context.currentLog = null;
});
$scope.$on('$destroy', function () {
if (debugLogRefreshPromise) {
$interval.cancel(debugLogRefreshPromise);
}
});
$rootScope.swipeActions.extendLeftSwipe = function () {
var tabItems = getVisibleTabOrders();
var tabIndex = tabItems.indexOf($scope.context.currentTab);
if (tabIndex < tabItems.length - 1) {
$scope.changeTab(tabItems[tabIndex + 1]);
return true;
} else {
return false;
}
};
$rootScope.swipeActions.extendRightSwipe = function () {
var tabItems = getVisibleTabOrders();
var tabIndex = tabItems.indexOf($scope.context.currentTab);
if (tabIndex > 0) {
$scope.changeTab(tabItems[tabIndex - 1]);
return true;
} else {
return false;
}
};
$rootScope.loadPromise = $timeout(function () {
if (!ariaNgSettingService.isEnableDebugMode()) {
ariaNgCommonService.showError('Access Denied!', function () {

View File

@@ -0,0 +1,23 @@
(function () {
'use strict';
angular.module('ariaNg').filter('logOrderBy', ['$filter', 'ariaNgCommonService', function ($filter, ariaNgCommonService) {
return function (array, type) {
if (!angular.isArray(array) || !type) {
return array;
}
var orderType = ariaNgCommonService.parseOrderType(type);
if (orderType === null) {
return array;
}
if (orderType.type === 'time') {
return $filter('orderBy')(array, ['time'], orderType.reverse);
} else {
return array;
}
};
}]);
}());

View File

@@ -0,0 +1,17 @@
(function () {
'use strict';
angular.module('ariaNg').filter('timeDisplayName', ['ariaNgCommonService', 'ariaNgLocalizationService', function (ariaNgCommonService, ariaNgLocalizationService) {
return function (time, defaultName) {
if (!time) {
return ariaNgLocalizationService.getLocalizedText(defaultName);
}
var option = ariaNgCommonService.getTimeOption(time);
return ariaNgLocalizationService.getLocalizedText(option.name, {
value: option.value
});
};
}]);
}());

View File

@@ -2,6 +2,31 @@
'use strict';
angular.module('ariaNg').factory('ariaNgCommonService', ['$location', '$timeout', 'base64', 'moment', 'SweetAlert', 'ariaNgConstants', 'ariaNgLocalizationService', function ($location, $timeout, base64, moment, SweetAlert, ariaNgConstants, ariaNgLocalizationService) {
var getTimeOption = function (time) {
var name = '';
var value = time;
if (time < 1000) {
value = time;
name = (value === 1 ? 'format.time.millisecond' : 'format.time.milliseconds');
} else if (time < 1000 * 60) {
value = time / 1000;
name = (value === 1 ? 'format.time.second' : 'format.time.seconds');
} else if (time < 1000 * 60 * 24) {
value = time / 1000 / 60;
name = (value === 1 ? 'format.time.minute' : 'format.time.minutes');
} else {
value = time / 1000 / 60 / 24;
name = (value === 1 ? 'format.time.hour' : 'format.time.hours');
}
return {
name: name,
value: value,
optionValue: time
};
};
var showDialog = function (title, text, type, callback, options) {
$timeout(function () {
SweetAlert.swal({
@@ -273,6 +298,9 @@
formatDateTime: function (datetime, format) {
return moment(datetime).format(format);
},
getTimeOption: function (time) {
return getTimeOption(time);
},
getTimeOptions: function (timeList, withDisabled) {
var options = [];
@@ -290,28 +318,9 @@
for (var i = 0; i < timeList.length; i++) {
var time = timeList[i];
var name = '';
var value = time;
var option = getTimeOption(time);
if (time < 1000) {
value = time;
name = (value === 1 ? 'format.time.millisecond' : 'format.time.milliseconds');
} else if (time < 1000 * 60) {
value = time / 1000;
name = (value === 1 ? 'format.time.second' : 'format.time.seconds');
} else if (time < 1000 * 60 * 24) {
value = time / 1000 / 60;
name = (value === 1 ? 'format.time.minute' : 'format.time.minutes');
} else {
value = time / 1000 / 60 / 24;
name = (value === 1 ? 'format.time.hour' : 'format.time.hours');
}
options.push({
name: name,
value: value,
optionValue: time
});
options.push(option);
}
return options;

View File

@@ -2,11 +2,19 @@
'use strict';
angular.module('ariaNg').factory('ariaNgLogService', ['$log', 'ariaNgConstants', function ($log, ariaNgConstants) {
var logLevels = {
DEBUG: 1,
INFO: 2,
WARN: 3,
ERROR: 4
};
var logIndex = 0;
var enableDebugLog = false;
var cachedDebugLogs = [];
var createNewCacheLogItem = function (msg, level, obj) {
return {
id: ++logIndex,
time: new Date(),
level: level,
content: msg,
@@ -30,6 +38,26 @@
setEnableDebugLog: function (value) {
enableDebugLog = value;
},
compareLogLevel: function (level1, level2) {
var level1Val = logLevels[level1];
var level2Val = logLevels[level2];
if (!level1Val) {
level1Val = 0;
}
if (!level2Val) {
level2Val = 0;
}
if (level1Val > level2Val) {
return 1;
} else if (level1Val < level2Val) {
return -1;
} else {
return 0;
}
},
debug: function (msg, obj) {
if (enableDebugLog) {
if (obj) {
@@ -74,6 +102,10 @@
} else {
return [];
}
},
clearDebugLogs: function () {
logIndex = 0;
cachedDebugLogs.length = 0;
}
};
}]);

View File

@@ -19,6 +19,12 @@
.settings-table .settings-table-title a {
color: #000;
cursor: pointer;
}
.settings-table .settings-table-title .settings-table-title-toolbar {
display: inline-block;
margin-left: 10px;
}
.settings-table > div.row {

View File

@@ -1,24 +1,84 @@
<section class="content no-padding ng-cloak" ng-if="enableDebugMode()">
<div class="nav-tabs-custom">
<ul class="nav nav-tabs">
<li class="active">
<a class="pointer-cursor" ng-bind="('format.debug.latest-logs' | translate: {count: logMaxCount})">Latest Logs</a>
</li>
<li class="slim">
<a class="pointer-cursor" ng-click="reloadLogs()">
<i class="fa fa-refresh"></i>
</a>
<li ng-class="{'active': context.currentTab === 'logs'}">
<a class="pointer-cursor" ng-click="changeTab('logs')" ng-bind="('format.debug.latest-logs' | translate: {count: context.logMaxCount})">Latest Logs</a>
</li>
</ul>
<div class="tab-content no-padding">
<div class="settings-table striped hoverable">
<div class="row" ng-repeat="log in logs | reverse">
<div class="col-sm-12">
<span class="label label-default" ng-bind="'#' + ($index + 1)"></span>
<span ng-bind="log.time | longDate"></span>
<span class="label" ng-class="{'DEBUG':'label-default', 'INFO':'label-primary', 'WARN':'label-warning', 'ERROR':'label-danger'}[log.level]" ng-bind="log.level"></span>
<span ng-bind="log.content"></span>
<a class="pointer-cursor" ng-click="showLogDetail(log)" ng-if="log.attachment"><i class="fa fa-file-o"></i> <span translate>Show Detail</span></a>
<div class="tab-pane" ng-class="{'active': context.currentTab === 'logs'}">
<div class="settings-table striped hoverable">
<div class="settings-table-title">
<div class="row">
<div class="col-sm-12">
<a ng-click="changeLogListDisplayOrder('time:asc', true)">
<span translate>Logging Time</span>&nbsp;<i class="fa" ng-class="{'fa-sort-asc fa-order-asc': isLogListSetDisplayOrder('time:asc'), 'fa-sort-desc fa-order-desc': isLogListSetDisplayOrder('time:desc')}"></i>
</a>
<div class="settings-table-title-toolbar">
<div class="btn-group">
<button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span translate>Log Level</span>:&nbsp;<span ng-bind="context.logLevelFilter | translate"></span>&nbsp;<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li>
<a href="javascript:void(0);" ng-click="setLogLevelFilter('DEBUG')">
<span translate>DEBUG</span>
<i class="fa" ng-class="{'fa-check': isSetLogLevelFilter('DEBUG')}"></i>
</a>
</li>
<li>
<a href="javascript:void(0);" ng-click="setLogLevelFilter('INFO')">
<span translate>INFO</span>
<i class="fa" ng-class="{'fa-check': isSetLogLevelFilter('INFO')}"></i>
</a>
</li>
<li>
<a href="javascript:void(0);" ng-click="setLogLevelFilter('WARN')">
<span translate>WARN</span>
<i class="fa" ng-class="{'fa-check': isSetLogLevelFilter('WARN')}"></i>
</a>
</li>
<li>
<a href="javascript:void(0);" ng-click="setLogLevelFilter('ERROR')">
<span translate>ERROR</span>
<i class="fa" ng-class="{'fa-check': isSetLogLevelFilter('ERROR')}"></i>
</a>
</li>
</ul>
</div>&nbsp;<div class="btn-group">
<button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span translate>Auto Refresh</span>:&nbsp;<span ng-bind="context.logAutoRefreshInterval | timeDisplayName: 'Disabled'"></span>&nbsp;<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li ng-repeat="interval in context.logAutoRefreshAvailableInterval">
<a href="javascript:void(0);" ng-click="setAutoRefreshInterval(interval.optionValue)">
<span ng-bind="interval.name | translate: {value: interval.value}"></span>
<i class="fa" ng-class="{'fa-check': context.logAutoRefreshInterval === interval.optionValue}"></i>
</a>
</li>
<li class="divider"></li>
<li>
<a href="javascript:void(0);" ng-click="reloadLogs()">
<span translate>Refresh Now</span>
</a>
</li>
</ul>
</div><button class="btn btn-xs btn-default" ng-click="clearDebugLogs()">
<span translate>Clear Logs</span>
</button>
</div>
</div>
</div>
</div>
<div class="row" ng-repeat="log in context.logs | filter: filterLog | logOrderBy: getLogListOrderType()">
<div class="col-sm-12">
<span class="label label-default" ng-bind="'#' + log.id"></span>
<span ng-bind="log.time | longDate"></span>
<span class="label" ng-class="{'DEBUG':'label-default', 'INFO':'label-primary', 'WARN':'label-warning', 'ERROR':'label-danger'}[log.level]" ng-bind="log.level"></span>
<span ng-bind="log.content"></span>
<a class="pointer-cursor" ng-click="showLogDetail(log)" ng-if="log.attachment"><i class="fa fa-file-o"></i> <span translate>Show Detail</span></a>
</div>
</div>
</div>
</div>
@@ -36,14 +96,14 @@
<div class="settings-table striped">
<div class="row">
<div class="col-sm-12">
<span ng-bind="currentLog.time | longDate"></span>
<span class="label" ng-class="{'DEBUG':'label-default', 'INFO':'label-primary', 'WARN':'label-warning', 'ERROR':'label-danger'}[currentLog.level]" ng-bind="currentLog.level"></span>
<span ng-bind="currentLog.content"></span>
<span ng-bind="context.currentLog.time | longDate"></span>
<span class="label" ng-class="{'DEBUG':'label-default', 'INFO':'label-primary', 'WARN':'label-warning', 'ERROR':'label-danger'}[context.currentLog.level]" ng-bind="context.currentLog.level"></span>
<span ng-bind="context.currentLog.content"></span>
</div>
</div>
<div class="row" ng-if="currentLog.attachment">
<div class="row" ng-if="context.currentLog.attachment">
<div class="col-sm-12">
<pre ng-bind="currentLog.attachment | json"></pre>
<pre ng-bind="context.currentLog.attachment | json"></pre>
</div>
</div>
</div>