fix(log-viewer): strip headers in container logs when TTY is disabled (#1861)

pull/1862/head
Anthony Lapenna 2018-05-04 09:45:05 +02:00 committed by GitHub
parent 1e55ada6af
commit e0cf088428
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 81 additions and 26 deletions

View File

@ -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;
}]);

View File

@ -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' },

View File

@ -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).

View File

@ -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
}
});
}]);

View File

@ -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
}
});
}]);

View File

@ -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) {

View File

@ -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;

View File

@ -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;

View File

@ -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');