From e0cf088428fb0971cab5fd42aa6564bc6673d6e9 Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Fri, 4 May 2018 09:45:05 +0200 Subject: [PATCH] fix(log-viewer): strip headers in container logs when TTY is disabled (#1861) --- app/docker/helpers/logHelper.js | 22 +++++++++++++++++++ app/docker/rest/container.js | 2 +- app/docker/rest/response/handlers.js | 12 ++++------ app/docker/rest/service.js | 2 +- app/docker/rest/task.js | 2 +- app/docker/services/containerService.js | 18 ++++++++++++--- app/docker/services/serviceService.js | 16 ++++++++++++-- app/docker/services/taskService.js | 16 ++++++++++++-- .../logs/containerLogsController.js | 17 +++++++------- 9 files changed, 81 insertions(+), 26 deletions(-) create mode 100644 app/docker/helpers/logHelper.js diff --git a/app/docker/helpers/logHelper.js b/app/docker/helpers/logHelper.js new file mode 100644 index 000000000..47f903056 --- /dev/null +++ b/app/docker/helpers/logHelper.js @@ -0,0 +1,22 @@ +angular.module('portainer.docker') +.factory('LogHelper', [function LogHelperFactory() { + 'use strict'; + var helper = {}; + + // Return an array with each line being an entry. + // It will also remove any ANSI code related character sequences. + // If the skipHeaders param is specified, it will strip the 8 first characters of each line. + helper.formatLogs = function(logs, skipHeaders) { + logs = logs.replace( + /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, ''); + + if (skipHeaders) { + logs = logs.substring(8); + logs = logs.replace(/\n(.{8})/g, '\n\r'); + } + + return logs.split('\n'); + }; + + return helper; +}]); diff --git a/app/docker/rest/container.js b/app/docker/rest/container.js index b2acedd59..f15ec77b3 100644 --- a/app/docker/rest/container.js +++ b/app/docker/rest/container.js @@ -16,7 +16,7 @@ angular.module('portainer.docker') logs: { method: 'GET', params: { id: '@id', action: 'logs' }, timeout: 4500, ignoreLoadingBar: true, - transformResponse: logsHandler, isArray: true + transformResponse: logsHandler }, stats: { method: 'GET', params: { id: '@id', stream: false, action: 'stats' }, diff --git a/app/docker/rest/response/handlers.js b/app/docker/rest/response/handlers.js index 53e660c39..7937c1f4e 100644 --- a/app/docker/rest/response/handlers.js +++ b/app/docker/rest/response/handlers.js @@ -45,15 +45,11 @@ function genericHandler(data) { } // The Docker API returns the logs as a single string. -// This handler will return an array with each line being an entry. -// It will also strip the 8 first characters of each line and remove any ANSI code related character sequences. +// This handler wraps the data in a JSON object under the "logs" property. function logsHandler(data) { - var logs = data; - logs = logs.substring(8); - logs = logs.replace(/\n(.{8})/g, '\n\r'); - logs = logs.replace( - /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, ''); - return logs.split('\n'); + return { + logs: data + }; } // Image delete API returns an array on success (Docker 1.9 -> Docker 1.12). diff --git a/app/docker/rest/service.js b/app/docker/rest/service.js index 3af88acd2..361186637 100644 --- a/app/docker/rest/service.js +++ b/app/docker/rest/service.js @@ -17,7 +17,7 @@ angular.module('portainer.docker') logs: { method: 'GET', params: { id: '@id', action: 'logs' }, timeout: 4500, ignoreLoadingBar: true, - transformResponse: logsHandler, isArray: true + transformResponse: logsHandler } }); }]); diff --git a/app/docker/rest/task.js b/app/docker/rest/task.js index 9683b05ba..ed21d5ee5 100644 --- a/app/docker/rest/task.js +++ b/app/docker/rest/task.js @@ -10,7 +10,7 @@ angular.module('portainer.docker') logs: { method: 'GET', params: { id: '@id', action: 'logs' }, timeout: 4500, ignoreLoadingBar: true, - transformResponse: logsHandler, isArray: true + transformResponse: logsHandler } }); }]); diff --git a/app/docker/services/containerService.js b/app/docker/services/containerService.js index 51efd1b8e..776b8ea21 100644 --- a/app/docker/services/containerService.js +++ b/app/docker/services/containerService.js @@ -1,5 +1,6 @@ angular.module('portainer.docker') -.factory('ContainerService', ['$q', 'Container', 'ResourceControlService', function ContainerServiceFactory($q, Container, ResourceControlService) { +.factory('ContainerService', ['$q', 'Container', 'ResourceControlService', 'LogHelper', +function ContainerServiceFactory($q, Container, ResourceControlService, LogHelper) { 'use strict'; var service = {}; @@ -131,7 +132,9 @@ angular.module('portainer.docker') return deferred.promise; }; - service.logs = function(id, stdout, stderr, timestamps, tail) { + service.logs = function(id, stdout, stderr, timestamps, tail, stripHeaders) { + var deferred = $q.defer(); + var parameters = { id: id, stdout: stdout || 0, @@ -140,7 +143,16 @@ angular.module('portainer.docker') tail: tail || 'all' }; - return Container.logs(parameters).$promise; + Container.logs(parameters).$promise + .then(function success(data) { + var logs = LogHelper.formatLogs(data.logs, stripHeaders); + deferred.resolve(logs); + }) + .catch(function error(err) { + deferred.reject(err); + }); + + return deferred.promise; }; service.containerStats = function(id) { diff --git a/app/docker/services/serviceService.js b/app/docker/services/serviceService.js index 1e28b75f9..94e8fa7bf 100644 --- a/app/docker/services/serviceService.js +++ b/app/docker/services/serviceService.js @@ -1,5 +1,6 @@ angular.module('portainer.docker') -.factory('ServiceService', ['$q', 'Service', 'ServiceHelper', 'TaskService', 'ResourceControlService', function ServiceServiceFactory($q, Service, ServiceHelper, TaskService, ResourceControlService) { +.factory('ServiceService', ['$q', 'Service', 'ServiceHelper', 'TaskService', 'ResourceControlService', 'LogHelper', +function ServiceServiceFactory($q, Service, ServiceHelper, TaskService, ResourceControlService, LogHelper) { 'use strict'; var service = {}; @@ -59,6 +60,8 @@ angular.module('portainer.docker') }; service.logs = function(id, stdout, stderr, timestamps, tail) { + var deferred = $q.defer(); + var parameters = { id: id, stdout: stdout || 0, @@ -67,7 +70,16 @@ angular.module('portainer.docker') tail: tail || 'all' }; - return Service.logs(parameters).$promise; + Service.logs(parameters).$promise + .then(function success(data) { + var logs = LogHelper.formatLogs(data.logs, true); + deferred.resolve(logs); + }) + .catch(function error(err) { + deferred.reject(err); + }); + + return deferred.promise; }; return service; diff --git a/app/docker/services/taskService.js b/app/docker/services/taskService.js index 4fdbe4331..960b6bd70 100644 --- a/app/docker/services/taskService.js +++ b/app/docker/services/taskService.js @@ -1,5 +1,6 @@ angular.module('portainer.docker') -.factory('TaskService', ['$q', 'Task', function TaskServiceFactory($q, Task) { +.factory('TaskService', ['$q', 'Task', 'LogHelper', +function TaskServiceFactory($q, Task, LogHelper) { 'use strict'; var service = {}; @@ -36,6 +37,8 @@ angular.module('portainer.docker') }; service.logs = function(id, stdout, stderr, timestamps, tail) { + var deferred = $q.defer(); + var parameters = { id: id, stdout: stdout || 0, @@ -44,7 +47,16 @@ angular.module('portainer.docker') tail: tail || 'all' }; - return Task.logs(parameters).$promise; + Task.logs(parameters).$promise + .then(function success(data) { + var logs = LogHelper.formatLogs(data.logs, true); + deferred.resolve(logs); + }) + .catch(function error(err) { + deferred.reject(err); + }); + + return deferred.promise; }; return service; diff --git a/app/docker/views/containers/logs/containerLogsController.js b/app/docker/views/containers/logs/containerLogsController.js index ba2f661ff..0c7f926ee 100644 --- a/app/docker/views/containers/logs/containerLogsController.js +++ b/app/docker/views/containers/logs/containerLogsController.js @@ -11,7 +11,7 @@ function ($scope, $transition$, $interval, ContainerService, Notifications) { if (!logCollectionStatus) { stopRepeater(); } else { - setUpdateRepeater(); + setUpdateRepeater(!$scope.container.Config.Tty); } }; @@ -31,10 +31,10 @@ function ($scope, $transition$, $interval, ContainerService, Notifications) { $scope.logs = logs; } - function setUpdateRepeater() { + function setUpdateRepeater(skipHeaders) { var refreshRate = $scope.state.refreshRate; $scope.repeater = $interval(function() { - ContainerService.logs($transition$.params().id, 1, 1, $scope.state.displayTimestamps ? 1 : 0, $scope.state.lineCount) + ContainerService.logs($transition$.params().id, 1, 1, $scope.state.displayTimestamps ? 1 : 0, $scope.state.lineCount, skipHeaders) .then(function success(data) { $scope.logs = data; }) @@ -45,11 +45,11 @@ function ($scope, $transition$, $interval, ContainerService, Notifications) { }, refreshRate * 1000); } - function startLogPolling() { - ContainerService.logs($transition$.params().id, 1, 1, $scope.state.displayTimestamps ? 1 : 0, $scope.state.lineCount) + function startLogPolling(skipHeaders) { + ContainerService.logs($transition$.params().id, 1, 1, $scope.state.displayTimestamps ? 1 : 0, $scope.state.lineCount, skipHeaders) .then(function success(data) { $scope.logs = data; - setUpdateRepeater(); + setUpdateRepeater(skipHeaders); }) .catch(function error(err) { stopRepeater(); @@ -60,8 +60,9 @@ function ($scope, $transition$, $interval, ContainerService, Notifications) { function initView() { ContainerService.container($transition$.params().id) .then(function success(data) { - $scope.container = data; - startLogPolling(); + var container = data; + $scope.container = container; + startLogPolling(!container.Config.Tty); }) .catch(function error(err) { Notifications.error('Failure', err, 'Unable to retrieve container information');