diff --git a/app/__module.js b/app/__module.js
index 15371bdf6..98591b7e6 100644
--- a/app/__module.js
+++ b/app/__module.js
@@ -13,6 +13,8 @@ angular.module('portainer', [
'angular-google-analytics',
'angular-json-tree',
'angular-loading-bar',
+ 'angular-clipboard',
+ 'luegg.directives',
'portainer.templates',
'portainer.app',
'portainer.docker',
diff --git a/app/docker/__module.js b/app/docker/__module.js
index e4777fcb9..0ede8298d 100644
--- a/app/docker/__module.js
+++ b/app/docker/__module.js
@@ -389,6 +389,17 @@ angular.module('portainer.docker', ['portainer.app'])
}
};
+ var taskLogs = {
+ name: 'docker.tasks.task.logs',
+ url: '/logs',
+ views: {
+ 'content@': {
+ templateUrl: 'app/docker/views/tasks/logs/tasklogs.html',
+ controller: 'TaskLogsController'
+ }
+ }
+ };
+
var templates = {
name: 'docker.templates',
url: '/templates',
@@ -488,6 +499,7 @@ angular.module('portainer.docker', ['portainer.app'])
$stateRegistryProvider.register(swarmVisualizer);
$stateRegistryProvider.register(tasks);
$stateRegistryProvider.register(task);
+ $stateRegistryProvider.register(taskLogs);
$stateRegistryProvider.register(templates);
$stateRegistryProvider.register(templatesLinuxServer);
$stateRegistryProvider.register(volumes);
diff --git a/app/docker/components/datatables/containers-datatable/containersDatatable.html b/app/docker/components/datatables/containers-datatable/containersDatatable.html
index 8c6e30c68..f9e82b812 100644
--- a/app/docker/components/datatables/containers-datatable/containersDatatable.html
+++ b/app/docker/components/datatables/containers-datatable/containersDatatable.html
@@ -196,7 +196,7 @@
@@ -63,6 +64,11 @@
{{ item.Slot ? item.Slot : '-' }} |
{{ item.NodeId | tasknodename: $ctrl.nodes }} |
{{ item.Updated | getisodate }} |
+
+
+ View logs
+
+ |
Loading... |
diff --git a/app/docker/components/datatables/tasks-datatable/tasksDatatable.js b/app/docker/components/datatables/tasks-datatable/tasksDatatable.js
index c8bae7d68..b560bdade 100644
--- a/app/docker/components/datatables/tasks-datatable/tasksDatatable.js
+++ b/app/docker/components/datatables/tasks-datatable/tasksDatatable.js
@@ -10,6 +10,7 @@ angular.module('portainer.docker').component('tasksDatatable', {
reverseOrder: '<',
nodes: '<',
showTextFilter: '<',
- showSlotColumn: '<'
+ showSlotColumn: '<',
+ showLogsButton: '<'
}
});
diff --git a/app/docker/components/log-viewer/log-viewer.js b/app/docker/components/log-viewer/log-viewer.js
new file mode 100644
index 000000000..5c7dc6d6c
--- /dev/null
+++ b/app/docker/components/log-viewer/log-viewer.js
@@ -0,0 +1,8 @@
+angular.module('portainer.docker').component('logViewer', {
+ templateUrl: 'app/docker/components/log-viewer/logViewer.html',
+ controller: 'LogViewerController',
+ bindings: {
+ data: '=',
+ logCollectionChange: '<'
+ }
+});
diff --git a/app/docker/components/log-viewer/logViewer.html b/app/docker/components/log-viewer/logViewer.html
new file mode 100644
index 000000000..e61923b38
--- /dev/null
+++ b/app/docker/components/log-viewer/logViewer.html
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+ No log line matching the '{{ $ctrl.state.search }}' filter
+
+
+
+
diff --git a/app/docker/components/log-viewer/logViewerController.js b/app/docker/components/log-viewer/logViewerController.js
new file mode 100644
index 000000000..10b544c58
--- /dev/null
+++ b/app/docker/components/log-viewer/logViewerController.js
@@ -0,0 +1,35 @@
+angular.module('portainer.docker')
+.controller('LogViewerController', ['clipboard',
+function (clipboard) {
+ var ctrl = this;
+
+ this.state = {
+ copySupported: clipboard.supported,
+ logCollection: true,
+ autoScroll: true,
+ search: '',
+ filteredLogs: [],
+ selectedLines: []
+ };
+
+ this.copy = function() {
+ clipboard.copyText(this.state.filteredLogs);
+ $('#refreshRateChange').show();
+ $('#refreshRateChange').fadeOut(1500);
+ };
+
+ this.copySelection = function() {
+ clipboard.copyText(this.state.selectedLines);
+ $('#refreshRateChange').show();
+ $('#refreshRateChange').fadeOut(1500);
+ };
+
+ this.selectLine = function(line) {
+ var idx = this.state.selectedLines.indexOf(line);
+ if (idx === -1) {
+ this.state.selectedLines.push(line);
+ } else {
+ this.state.selectedLines.splice(idx, 1);
+ }
+ };
+}]);
diff --git a/app/docker/models/container.js b/app/docker/models/container.js
index c37f2fe93..64d4d7047 100644
--- a/app/docker/models/container.js
+++ b/app/docker/models/container.js
@@ -36,3 +36,35 @@ function ContainerViewModel(data) {
}
}
}
+
+function ContainerStatsViewModel(data) {
+ this.Date = data.read;
+ this.MemoryUsage = data.memory_stats.usage;
+ this.PreviousCPUTotalUsage = data.precpu_stats.cpu_usage.total_usage;
+ this.PreviousCPUSystemUsage = data.precpu_stats.system_cpu_usage;
+ this.CurrentCPUTotalUsage = data.cpu_stats.cpu_usage.total_usage;
+ this.CurrentCPUSystemUsage = data.cpu_stats.system_cpu_usage;
+ if (data.cpu_stats.cpu_usage.percpu_usage) {
+ this.CPUCores = data.cpu_stats.cpu_usage.percpu_usage.length;
+ }
+ this.Networks = _.values(data.networks);
+}
+
+function ContainerDetailsViewModel(data) {
+ this.Model = data;
+ this.Id = data.Id;
+ this.State = data.State;
+ this.Created = data.Created;
+ this.Name = data.Name;
+ this.NetworkSettings = data.NetworkSettings;
+ this.Args = data.Args;
+ this.Image = data.Image;
+ this.Config = data.Config;
+ this.HostConfig = data.HostConfig;
+ this.Mounts = data.Mounts;
+ if (data.Portainer) {
+ if (data.Portainer.ResourceControl) {
+ this.ResourceControl = new ResourceControlViewModel(data.Portainer.ResourceControl);
+ }
+ }
+}
diff --git a/app/docker/models/containerDetails.js b/app/docker/models/containerDetails.js
deleted file mode 100644
index eae58c105..000000000
--- a/app/docker/models/containerDetails.js
+++ /dev/null
@@ -1,18 +0,0 @@
-function ContainerDetailsViewModel(data) {
- this.Model = data;
- this.Id = data.Id;
- this.State = data.State;
- this.Created = data.Created;
- this.Name = data.Name;
- this.NetworkSettings = data.NetworkSettings;
- this.Args = data.Args;
- this.Image = data.Image;
- this.Config = data.Config;
- this.HostConfig = data.HostConfig;
- this.Mounts = data.Mounts;
- if (data.Portainer) {
- if (data.Portainer.ResourceControl) {
- this.ResourceControl = new ResourceControlViewModel(data.Portainer.ResourceControl);
- }
- }
-}
diff --git a/app/docker/models/containerStats.js b/app/docker/models/containerStats.js
deleted file mode 100644
index aad3b48b3..000000000
--- a/app/docker/models/containerStats.js
+++ /dev/null
@@ -1,12 +0,0 @@
-function ContainerStatsViewModel(data) {
- this.Date = data.read;
- this.MemoryUsage = data.memory_stats.usage;
- this.PreviousCPUTotalUsage = data.precpu_stats.cpu_usage.total_usage;
- this.PreviousCPUSystemUsage = data.precpu_stats.system_cpu_usage;
- this.CurrentCPUTotalUsage = data.cpu_stats.cpu_usage.total_usage;
- this.CurrentCPUSystemUsage = data.cpu_stats.system_cpu_usage;
- if (data.cpu_stats.cpu_usage.percpu_usage) {
- this.CPUCores = data.cpu_stats.cpu_usage.percpu_usage.length;
- }
- this.Networks = _.values(data.networks);
-}
diff --git a/app/docker/rest/container.js b/app/docker/rest/container.js
index cfa0a0587..b2acedd59 100644
--- a/app/docker/rest/container.js
+++ b/app/docker/rest/container.js
@@ -13,6 +13,11 @@ angular.module('portainer.docker')
kill: {method: 'POST', params: {id: '@id', action: 'kill'}},
pause: {method: 'POST', params: {id: '@id', action: 'pause'}},
unpause: {method: 'POST', params: {id: '@id', action: 'unpause'}},
+ logs: {
+ method: 'GET', params: { id: '@id', action: 'logs' },
+ timeout: 4500, ignoreLoadingBar: true,
+ transformResponse: logsHandler, isArray: true
+ },
stats: {
method: 'GET', params: { id: '@id', stream: false, action: 'stats' },
timeout: 4500, ignoreLoadingBar: true
diff --git a/app/docker/rest/containerLogs.js b/app/docker/rest/containerLogs.js
deleted file mode 100644
index f4520f3b0..000000000
--- a/app/docker/rest/containerLogs.js
+++ /dev/null
@@ -1,21 +0,0 @@
-angular.module('portainer.docker')
-.factory('ContainerLogs', ['$http', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', function ContainerLogsFactory($http, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
- 'use strict';
- return {
- get: function (id, params, callback) {
- $http({
- method: 'GET',
- url: API_ENDPOINT_ENDPOINTS + '/' + EndpointProvider.endpointID() + '/docker/containers/' + id + '/logs',
- params: {
- 'stdout': params.stdout || 0,
- 'stderr': params.stderr || 0,
- 'timestamps': params.timestamps || 0,
- 'tail': params.tail || 'all'
- },
- ignoreLoadingBar: true
- }).success(callback).error(function (data, status, headers, config) {
- console.log(data);
- });
- }
- };
-}]);
diff --git a/app/docker/rest/response/handlers.js b/app/docker/rest/response/handlers.js
index 03aa6c1d1..53e660c39 100644
--- a/app/docker/rest/response/handlers.js
+++ b/app/docker/rest/response/handlers.js
@@ -44,6 +44,18 @@ function genericHandler(data) {
return response;
}
+// 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.
+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');
+}
+
// Image delete API returns an array on success (Docker 1.9 -> Docker 1.12).
// On error, it returns either an error message as a string (Docker < 1.12) or a JSON object with the field message
// container the error (Docker = 1.12).
diff --git a/app/docker/rest/service.js b/app/docker/rest/service.js
index 66e19468c..3af88acd2 100644
--- a/app/docker/rest/service.js
+++ b/app/docker/rest/service.js
@@ -13,6 +13,11 @@ angular.module('portainer.docker')
ignoreLoadingBar: true
},
update: { method: 'POST', params: {id: '@id', action: 'update', version: '@version'} },
- remove: { method: 'DELETE', params: {id: '@id'} }
+ remove: { method: 'DELETE', params: {id: '@id'} },
+ logs: {
+ method: 'GET', params: { id: '@id', action: 'logs' },
+ timeout: 4500, ignoreLoadingBar: true,
+ transformResponse: logsHandler, isArray: true
+ }
});
}]);
diff --git a/app/docker/rest/serviceLogs.js b/app/docker/rest/serviceLogs.js
deleted file mode 100644
index fbd25ec66..000000000
--- a/app/docker/rest/serviceLogs.js
+++ /dev/null
@@ -1,20 +0,0 @@
-angular.module('portainer.docker')
-.factory('ServiceLogs', ['$http', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', function ServiceLogsFactory($http, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
- 'use strict';
- return {
- get: function (id, params, callback) {
- $http({
- method: 'GET',
- url: API_ENDPOINT_ENDPOINTS + '/' + EndpointProvider.endpointID() + '/docker/services/' + id + '/logs',
- params: {
- 'stdout': params.stdout || 0,
- 'stderr': params.stderr || 0,
- 'timestamps': params.timestamps || 0,
- 'tail': params.tail || 'all'
- }
- }).success(callback).error(function (data, status, headers, config) {
- console.log(data);
- });
- }
- };
-}]);
diff --git a/app/docker/rest/task.js b/app/docker/rest/task.js
index 73032ae12..9683b05ba 100644
--- a/app/docker/rest/task.js
+++ b/app/docker/rest/task.js
@@ -1,11 +1,16 @@
angular.module('portainer.docker')
.factory('Task', ['$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', function TaskFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
'use strict';
- return $resource(API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/tasks/:id', {
+ return $resource(API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/tasks/:id/:action', {
endpointId: EndpointProvider.endpointID
},
{
get: { method: 'GET', params: {id: '@id'} },
- query: { method: 'GET', isArray: true, params: {filters: '@filters'} }
+ query: { method: 'GET', isArray: true, params: {filters: '@filters'} },
+ logs: {
+ method: 'GET', params: { id: '@id', action: 'logs' },
+ timeout: 4500, ignoreLoadingBar: true,
+ transformResponse: logsHandler, isArray: true
+ }
});
}]);
diff --git a/app/docker/services/containerService.js b/app/docker/services/containerService.js
index 204dbe545..51efd1b8e 100644
--- a/app/docker/services/containerService.js
+++ b/app/docker/services/containerService.js
@@ -131,6 +131,18 @@ angular.module('portainer.docker')
return deferred.promise;
};
+ service.logs = function(id, stdout, stderr, timestamps, tail) {
+ var parameters = {
+ id: id,
+ stdout: stdout || 0,
+ stderr: stderr || 0,
+ timestamps: timestamps || 0,
+ tail: tail || 'all'
+ };
+
+ return Container.logs(parameters).$promise;
+ };
+
service.containerStats = function(id) {
var deferred = $q.defer();
diff --git a/app/docker/services/serviceService.js b/app/docker/services/serviceService.js
index 7f136b6ab..1e28b75f9 100644
--- a/app/docker/services/serviceService.js
+++ b/app/docker/services/serviceService.js
@@ -58,5 +58,17 @@ angular.module('portainer.docker')
return Service.update({ id: service.Id, version: service.Version }, config).$promise;
};
+ service.logs = function(id, stdout, stderr, timestamps, tail) {
+ var parameters = {
+ id: id,
+ stdout: stdout || 0,
+ stderr: stderr || 0,
+ timestamps: timestamps || 0,
+ tail: tail || 'all'
+ };
+
+ return Service.logs(parameters).$promise;
+ };
+
return service;
}]);
diff --git a/app/docker/services/taskService.js b/app/docker/services/taskService.js
index 3280c5c15..4fdbe4331 100644
--- a/app/docker/services/taskService.js
+++ b/app/docker/services/taskService.js
@@ -35,5 +35,17 @@ angular.module('portainer.docker')
return deferred.promise;
};
+ service.logs = function(id, stdout, stderr, timestamps, tail) {
+ var parameters = {
+ id: id,
+ stdout: stdout || 0,
+ stderr: stderr || 0,
+ timestamps: timestamps || 0,
+ tail: tail || 'all'
+ };
+
+ return Task.logs(parameters).$promise;
+ };
+
return service;
}]);
diff --git a/app/docker/views/containers/edit/container.html b/app/docker/views/containers/edit/container.html
index 58fa5f819..e384da895 100644
--- a/app/docker/views/containers/edit/container.html
+++ b/app/docker/views/containers/edit/container.html
@@ -85,7 +85,7 @@
diff --git a/app/docker/views/containers/logs/containerLogsController.js b/app/docker/views/containers/logs/containerLogsController.js
index dc7169164..c874d2db8 100644
--- a/app/docker/views/containers/logs/containerLogsController.js
+++ b/app/docker/views/containers/logs/containerLogsController.js
@@ -1,70 +1,71 @@
angular.module('portainer.docker')
-.controller('ContainerLogsController', ['$scope', '$transition$', '$anchorScroll', 'ContainerLogs', 'Container', 'Notifications',
-function ($scope, $transition$, $anchorScroll, ContainerLogs, Container, Notifications) {
- $scope.state = {};
- $scope.state.displayTimestampsOut = false;
- $scope.state.displayTimestampsErr = false;
- $scope.stdout = '';
- $scope.stderr = '';
- $scope.tailLines = 2000;
+.controller('ContainerLogsController', ['$scope', '$transition$', '$interval', 'ContainerService', 'Notifications',
+function ($scope, $transition$, $interval, ContainerService, Notifications) {
+ $scope.state = {
+ refreshRate: 3,
+ lineCount: 2000
+ };
- Container.get({id: $transition$.params().id}, function (d) {
- $scope.container = d;
- }, function (e) {
- Notifications.error('Failure', e, 'Unable to retrieve container info');
+ $scope.changeLogCollection = function(logCollectionStatus) {
+ if (!logCollectionStatus) {
+ stopRepeater();
+ } else {
+ setUpdateRepeater();
+ }
+ };
+
+ $scope.$on('$destroy', function() {
+ stopRepeater();
});
- function getLogs() {
- getLogsStdout();
- getLogsStderr();
+ function stopRepeater() {
+ var repeater = $scope.repeater;
+ if (angular.isDefined(repeater)) {
+ $interval.cancel(repeater);
+ repeater = null;
+ }
}
- function getLogsStderr() {
- ContainerLogs.get($transition$.params().id, {
- stdout: 0,
- stderr: 1,
- timestamps: $scope.state.displayTimestampsErr,
- tail: $scope.tailLines
- }, function (data, status, headers, config) {
- // Replace carriage returns with newlines to clean up output
- data = data.replace(/[\r]/g, '\n');
- // Strip 8 byte header from each line of output
- data = data.substring(8);
- data = data.replace(/\n(.{8})/g, '\n');
- $scope.stderr = data;
+ function update(logs) {
+ $scope.logs = logs;
+ }
+
+ function setUpdateRepeater() {
+ var refreshRate = $scope.state.refreshRate;
+ $scope.repeater = $interval(function() {
+ ContainerService.logs($transition$.params().id, 1, 1, 0, $scope.state.lineCount)
+ .then(function success(data) {
+ $scope.logs = data;
+ })
+ .catch(function error(err) {
+ stopRepeater();
+ Notifications.error('Failure', err, 'Unable to retrieve container logs');
+ });
+ }, refreshRate * 1000);
+ }
+
+ function startLogPolling() {
+ ContainerService.logs($transition$.params().id, 1, 1, 0, $scope.state.lineCount)
+ .then(function success(data) {
+ $scope.logs = data;
+ setUpdateRepeater();
+ })
+ .catch(function error(err) {
+ stopRepeater();
+ Notifications.error('Failure', err, 'Unable to retrieve container logs');
});
}
- function getLogsStdout() {
- ContainerLogs.get($transition$.params().id, {
- stdout: 1,
- stderr: 0,
- timestamps: $scope.state.displayTimestampsOut,
- tail: $scope.tailLines
- }, function (data, status, headers, config) {
- // Replace carriage returns with newlines to clean up output
- data = data.replace(/[\r]/g, '\n');
- // Strip 8 byte header from each line of output
- data = data.substring(8);
- data = data.replace(/\n(.{8})/g, '\n');
- $scope.stdout = data;
+ function initView() {
+ ContainerService.container($transition$.params().id)
+ .then(function success(data) {
+ $scope.container = data;
+ startLogPolling();
+ })
+ .catch(function error(err) {
+ Notifications.error('Failure', err, 'Unable to retrieve container information');
});
}
- // initial call
- getLogs();
- var logIntervalId = window.setInterval(getLogs, 5000);
-
- $scope.$on('$destroy', function () {
- // clearing interval when view changes
- clearInterval(logIntervalId);
- });
-
- $scope.toggleTimestampsOut = function () {
- getLogsStdout();
- };
-
- $scope.toggleTimestampsErr = function () {
- getLogsStderr();
- };
+ initView();
}]);
diff --git a/app/docker/views/containers/logs/containerlogs.html b/app/docker/views/containers/logs/containerlogs.html
index 86ce31b13..48d0ef68e 100644
--- a/app/docker/views/containers/logs/containerlogs.html
+++ b/app/docker/views/containers/logs/containerlogs.html
@@ -5,50 +5,6 @@
-
-
-
-
-
-
-
- {{ container.Name|trimcontainername }}
-
-
-
-
-
-
-
-
-
+
diff --git a/app/docker/views/services/edit/includes/tasks.html b/app/docker/views/services/edit/includes/tasks.html
index 2cf7fdbaf..369276b9a 100644
--- a/app/docker/views/services/edit/includes/tasks.html
+++ b/app/docker/views/services/edit/includes/tasks.html
@@ -6,5 +6,6 @@
nodes="nodes"
show-text-filter="true"
show-slot-column="service.Mode !== 'global'"
+ show-logs-button="applicationState.endpoint.apiVersion >= 1.30"
>
diff --git a/app/docker/views/services/edit/service.html b/app/docker/views/services/edit/service.html
index 059da3dca..c2d86925c 100644
--- a/app/docker/views/services/edit/service.html
+++ b/app/docker/views/services/edit/service.html
@@ -73,7 +73,7 @@
|
- Service logs
+ Service logs
|
+
+ Task logs |
+
diff --git a/app/docker/views/tasks/edit/taskController.js b/app/docker/views/tasks/edit/taskController.js
index adc3d032c..4b4ec6616 100644
--- a/app/docker/views/tasks/edit/taskController.js
+++ b/app/docker/views/tasks/edit/taskController.js
@@ -1,16 +1,16 @@
angular.module('portainer.docker')
-.controller('TaskController', ['$scope', '$transition$', 'TaskService', 'Service', 'Notifications',
-function ($scope, $transition$, TaskService, Service, Notifications) {
+.controller('TaskController', ['$scope', '$transition$', 'TaskService', 'ServiceService', 'Notifications',
+function ($scope, $transition$, TaskService, ServiceService, Notifications) {
function initView() {
TaskService.task($transition$.params().id)
.then(function success(data) {
var task = data;
$scope.task = task;
- return Service.get({ id: task.ServiceId }).$promise;
+ return ServiceService.service(task.ServiceId);
})
.then(function success(data) {
- var service = new ServiceViewModel(data);
+ var service = data;
$scope.service = service;
})
.catch(function error(err) {
diff --git a/app/docker/views/tasks/logs/taskLogsController.js b/app/docker/views/tasks/logs/taskLogsController.js
new file mode 100644
index 000000000..e578c5d7a
--- /dev/null
+++ b/app/docker/views/tasks/logs/taskLogsController.js
@@ -0,0 +1,73 @@
+angular.module('portainer.docker')
+.controller('TaskLogsController', ['$scope', '$transition$', '$interval', 'TaskService', 'ServiceService', 'Notifications',
+function ($scope, $transition$, $interval, TaskService, ServiceService, Notifications) {
+ $scope.state = {
+ refreshRate: 3,
+ lineCount: 2000
+ };
+
+ $scope.changeLogCollection = function(logCollectionStatus) {
+ if (!logCollectionStatus) {
+ stopRepeater();
+ } else {
+ setUpdateRepeater();
+ }
+ };
+
+ $scope.$on('$destroy', function() {
+ stopRepeater();
+ });
+
+ function stopRepeater() {
+ var repeater = $scope.repeater;
+ if (angular.isDefined(repeater)) {
+ $interval.cancel(repeater);
+ repeater = null;
+ }
+ }
+
+ function setUpdateRepeater() {
+ var refreshRate = $scope.state.refreshRate;
+ $scope.repeater = $interval(function() {
+ TaskService.logs($transition$.params().id, 1, 1, 0, $scope.state.lineCount)
+ .then(function success(data) {
+ $scope.logs = data;
+ })
+ .catch(function error(err) {
+ stopRepeater();
+ Notifications.error('Failure', err, 'Unable to retrieve task logs');
+ });
+ }, refreshRate * 1000);
+ }
+
+ function startLogPolling() {
+ TaskService.logs($transition$.params().id, 1, 1, 0, $scope.state.lineCount)
+ .then(function success(data) {
+ $scope.logs = data;
+ setUpdateRepeater();
+ })
+ .catch(function error(err) {
+ stopRepeater();
+ Notifications.error('Failure', err, 'Unable to retrieve task logs');
+ });
+ }
+
+ function initView() {
+ TaskService.task($transition$.params().id)
+ .then(function success(data) {
+ var task = data;
+ $scope.task = task;
+ return ServiceService.service(task.ServiceId);
+ })
+ .then(function success(data) {
+ var service = data;
+ $scope.service = service;
+ startLogPolling();
+ })
+ .catch(function error(err) {
+ Notifications.error('Failure', err, 'Unable to retrieve task details');
+ });
+ }
+
+ initView();
+}]);
diff --git a/app/docker/views/tasks/logs/tasklogs.html b/app/docker/views/tasks/logs/tasklogs.html
new file mode 100644
index 000000000..d86db0f92
--- /dev/null
+++ b/app/docker/views/tasks/logs/tasklogs.html
@@ -0,0 +1,10 @@
+