Browse Source

support show standard output / standard error and error event for startup command in debug console page

MaysWind 3 years ago
parent
commit
c98a5aa8aa

+ 1 - 0
app/langs/zh_Hans.txt

@@ -298,6 +298,7 @@ Clear Logs=清空日志
 Are you sure you want to clear debug logs?=您是否要清除调试日志?
 Show Detail=显示详情
 Log Detail=日志详情
+Startup Command Output=启动命令输出
 Aria2 RPC Debug=Aria2 RPC 调试
 Aria2 RPC Request Method=Aria2 RPC 请求方法
 Aria2 RPC Request Parameters=Aria2 RPC 请求参数

+ 1 - 0
app/langs/zh_Hant.txt

@@ -298,6 +298,7 @@ Clear Logs=清除記錄
 Are you sure you want to clear debug logs?=您是否要清除偵錯記錄?
 Show Detail=顯示詳情
 Log Detail=記錄詳情
+Startup Command Output=啟動命令輸出
 Aria2 RPC Debug=Aria2 RPC 偵錯
 Aria2 RPC Request Method=Aria2 RPC 要求方法
 Aria2 RPC Request Parameters=Aria2 RPC 要求參數

+ 1 - 0
app/scripts/config/defaultLanguage.js

@@ -302,6 +302,7 @@
             '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',
+            'Startup Command Output': 'Startup Command Output',
             'Aria2 RPC Debug': 'Aria2 RPC Debug',
             'Aria2 RPC Request Method': 'Aria2 RPC Request Method',
             'Aria2 RPC Request Parameters': 'Aria2 RPC Request Parameters',

+ 25 - 3
app/scripts/controllers/debug.js

@@ -1,12 +1,16 @@
 (function () {
     'use strict';
 
-    angular.module('ariaNg').controller('AriaNgDebugController', ['$rootScope', '$scope', '$location', '$interval', '$timeout', '$filter', 'ariaNgConstants', 'ariaNgCommonService', 'ariaNgLocalizationService', 'ariaNgLogService', 'ariaNgKeyboardService', 'ariaNgSettingService', 'aria2RpcService', function ($rootScope, $scope, $location, $interval, $timeout, $filter, ariaNgConstants, ariaNgCommonService, ariaNgLocalizationService, ariaNgLogService, ariaNgKeyboardService, ariaNgSettingService, aria2RpcService) {
+    angular.module('ariaNg').controller('AriaNgDebugController', ['$rootScope', '$scope', '$location', '$interval', '$timeout', '$filter', 'ariaNgConstants', 'ariaNgCommonService', 'ariaNgLocalizationService', 'ariaNgLogService', 'ariaNgKeyboardService', 'ariaNgSettingService', 'aria2RpcService', 'ariaNgNativeElectronService', function ($rootScope, $scope, $location, $interval, $timeout, $filter, ariaNgConstants, ariaNgCommonService, ariaNgLocalizationService, ariaNgLogService, ariaNgKeyboardService, ariaNgSettingService, aria2RpcService, ariaNgNativeElectronService) {
         var tabStatusItems = [
             {
                 name: 'logs',
                 show: true
             },
+            {
+                name: 'startup-command',
+                show: true
+            },
             {
                 name: 'rpc',
                 show: true
@@ -41,6 +45,15 @@
             });
         };
 
+        var loadStartupCommandOutput = function () {
+            return ariaNgNativeElectronService.getStartupCommandOutputAsync()
+                .then(function (outputs) {
+                    $scope.$apply(function () {
+                        $scope.context.startupCommandOutput = outputs;
+                    });
+                });
+        };
+
         $scope.context = {
             currentTab: 'logs',
             logMaxCount: ariaNgConstants.cachedDebugLogsLimit,
@@ -50,6 +63,7 @@
             logLevelFilter: 'DEBUG',
             logs: [],
             currentLog: null,
+            startupCommandOutput: null,
             availableRpcMethods: [],
             rpcRequestMethod: '',
             rpcRequestParameters: '{}',
@@ -61,7 +75,11 @@
         };
 
         $scope.changeTab = function (tabName) {
-            if (tabName === 'rpc') {
+            if (tabName === 'logs') {
+                $scope.context.logs = ariaNgLogService.getDebugLogs().slice();
+            } else if (tabName === 'startup-command') {
+                $rootScope.loadPromise = loadStartupCommandOutput();
+            } else if (tabName === 'rpc') {
                 $rootScope.loadPromise = loadAria2RPCMethods();
             }
 
@@ -131,7 +149,11 @@
         };
 
         $scope.reloadLogs = function () {
-            $scope.context.logs = ariaNgLogService.getDebugLogs().slice();
+            if ($scope.context.currentTab === 'logs') {
+                $scope.context.logs = ariaNgLogService.getDebugLogs().slice();
+            } else if ($scope.context.currentTab === 'startup-command') {
+                return loadStartupCommandOutput();
+            }
         };
 
         $scope.clearDebugLogs = function () {

+ 3 - 0
app/scripts/services/ariaNgNativeElectronService.js

@@ -165,6 +165,9 @@
             setLastCheckUpdatesTime: function (value) {
                 invokeMainProcessMethod('render-set-native-config-last-check-updates-time', value);
             },
+            getStartupCommandOutputAsync: function () {
+                return invokeMainProcessMethodAsync('render-get-startup-command-process-output');
+            },
             requestHttp: function (requestContext) {
                 var deferred = $q.defer();
 

+ 42 - 0
app/views/debug.html

@@ -4,6 +4,9 @@
             <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>
+            <li ng-class="{'active': context.currentTab === 'startup-command'}">
+                <a class="pointer-cursor" ng-click="changeTab('startup-command')" translate>Startup Command Output</a>
+            </li>
             <li ng-class="{'active': context.currentTab === 'rpc'}">
                 <a class="pointer-cursor" ng-click="changeTab('rpc')" translate>Aria2 RPC Debug</a>
             </li>
@@ -85,6 +88,45 @@
                     </div>
                 </div>
             </div>
+            <div class="tab-pane" ng-class="{'active': context.currentTab === 'startup-command'}">
+                <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><i class="fa fa-display-order" ng-class="{'fa-sort-asc fa-order-asc': isLogListSetDisplayOrder('time:asc'), 'fa-sort-desc fa-order-desc': isLogListSetDisplayOrder('time:desc')}"></i>
+                                </a> <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>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="row" ng-repeat="output in context.startupCommandOutput | logOrderBy: getLogListOrderType()">
+                        <div class="col-sm-12">
+                            <span ng-bind="output.time | longDate"></span>
+                            <span class="label" ng-class="{'label-default': output.type === 'output' && output.source === 'stdout', 'label-warning': output.type === 'output' && output.source === 'close', 'label-danger': (output.type === 'output' && output.source !== 'stdout' && output.source !== 'close') || output.type !== 'output'}" ng-bind="output.source"></span>
+                            <span ng-bind="output.content"></span>
+                            <span class="badge" ng-if="output.count > 1" ng-bind="output.count"></span>
+                        </div>
+                    </div>
+                </div>
+            </div>
             <div class="tab-pane" ng-class="{'active': context.currentTab === 'rpc'}">
                 <form name="executeMethodForm" ng-submit="executeAria2Method()" novalidate>
                     <div class="settings-table striped hoverable">

+ 5 - 0
main/config/constants.js

@@ -1,5 +1,9 @@
 'use strict';
 
+const startupCommandConstants = {
+    outputLogLimit: 100
+};
+
 const ariaNgPageLocations = {
     NewTask: '/new',
     AriaNgSettings: '/settings/ariang'
@@ -12,6 +16,7 @@ const supportedFileExtensions = {
 };
 
 module.exports = {
+    startupCommandConstants: startupCommandConstants,
     ariaNgPageLocations: ariaNgPageLocations,
     supportedFileExtensions: supportedFileExtensions
 }

+ 1 - 0
main/core.js

@@ -2,6 +2,7 @@
 
 let core = {
     mainWindow: null,
+    startupCommandOutput: [],
     isConfirmExit: false
 };
 

+ 4 - 0
main/ipc/main-process.js

@@ -141,6 +141,10 @@ ipcMain.on('render-set-native-config-last-check-updates-time', (event, value) =>
     config.save('lastCheckUpdatesTime');
 });
 
+ipcMain.handle('render-get-startup-command-process-output', (event, requestContext) => {
+    return core.startupCommandOutput;
+});
+
 ipcMain.handle('render-request-http', (event, requestContext) => {
     return http.request(requestContext);
 });

+ 48 - 8
main/lib/process.js

@@ -1,14 +1,14 @@
 const child_process = require('child_process');
 
-let execCommandAsync = function (command, args, detached) {
-    if (!command || !command.trim()) {
+let execCommandAsync = function (options) {
+    if (!options || !options.command || !options.command.trim()) {
         return;
     }
 
     let finalArguments = [];
 
-    if (args) {
-        const argsArr = args.split('\n');
+    if (options.args) {
+        const argsArr = options.args.split('\n');
         for (let i = 0; i < argsArr.length; i++) {
             let arg = argsArr[i];
 
@@ -25,16 +25,56 @@ let execCommandAsync = function (command, args, detached) {
     }
 
     try {
-        const child = child_process.spawn(command, finalArguments, {
+        const child = child_process.spawn(options.command, finalArguments, {
             encoding: 'utf8',
-            detached: !!detached
+            detached: !!options.detached
         });
 
+        if (options.onoutput) {
+            child.stdout.on('data', (data) => {
+                options.onoutput({
+                    source: 'stdout',
+                    content: data.toString(),
+                    count: 1
+                });
+            });
+        }
+
+        if (options.onoutput) {
+            child.stderr.on('data', (data) => {
+                options.onoutput({
+                    source: 'stderr',
+                    content: data.toString(),
+                    count: 1
+                });
+            });
+        }
+
+        if (options.onoutput) {
+            child.on('close', (code) => {
+                options.onoutput({
+                    source: 'close',
+                    content: code,
+                    count: 1
+                });
+            });
+        }
+
         child.on('error', (error) => {
-            // Do Nothing
+            if (options.onerror) {
+                options.onerror({
+                    type: 'event',
+                    error: error
+                });
+            }
         });
     } catch (ex) {
-        // Do Nothing
+        if (options.onerror) {
+            options.onerror({
+                type: 'exception',
+                error: ex
+            });
+        }
     }
 };
 

+ 42 - 1
main/main.js

@@ -75,7 +75,48 @@ if (os.platform() === 'darwin') {
 }
 
 if (config.execCommandOnStartup) {
-    process.execCommandAsync(config.execCommandOnStartup, config.execCommandArgumentsOnStartup, config.execDetachedCommandOnStartup);
+    const options = {
+        command: config.execCommandOnStartup,
+        args: config.execCommandArgumentsOnStartup,
+        detached: config.execDetachedCommandOnStartup,
+    };
+
+    if (global.settings.isDevMode) {
+        options.onoutput = function (output) {
+            const lastOutput = (core.startupCommandOutput.length > 1 ? core.startupCommandOutput[core.startupCommandOutput.length - 1] : null);
+
+            if (lastOutput && lastOutput.source === output.source && lastOutput.content === output.content) {
+                lastOutput.count++
+            } else {
+                if (core.startupCommandOutput.length >= constants.startupCommandConstants.outputLogLimit) {
+                    core.startupCommandOutput.shift();
+                }
+
+                core.startupCommandOutput.push({
+                    time: new Date(),
+                    type: 'output',
+                    source: output.source,
+                    content: output.content,
+                    count: output.count
+                });
+            }
+        };
+
+        options.onerror = function (error) {
+            if (core.startupCommandOutput.length >= constants.startupCommandConstants.outputLogLimit) {
+                core.startupCommandOutput.shift();
+            }
+
+            core.startupCommandOutput.push({
+                time: new Date(),
+                type: 'error',
+                source: error.type,
+                content: error.error
+            });
+        };
+    }
+
+    process.execCommandAsync(options);
 }
 
 app.on('window-all-closed', () => {