Skip to content
This repository was archived by the owner on May 14, 2025. It is now read-only.

Commit 7179de4

Browse files
ilayaperumalgtzolov
authored andcommitted
Retrieve stream/task application logs
- Add REST endpoints for stream apps log retrieval at Runtime apps controller - Add REST endpoint for task app log retrieval at TaskExecution controller - Add getLog() method SPI for TaskExecutionService and implemented it in DefaultTaskExecutionService - Add getLog() method in StreamDeployer and have it implemented in SkipperStreamDeployer - Add and update tests - Update CF task platform configuration to update doppler client to CF operations - Update Skipper client changes - Fix REST endpoint path for runtime apps log retrieval - Add separate controllers for stream/task logs retrieval - Change the return type of SCDF controllers that get logs - Fix API documentation and RootController - Remove runtime apps controller entries on log retrieval Resolves #3306 Resolves #3307
1 parent ca67cc5 commit 7179de4

File tree

23 files changed

+441
-12
lines changed

23 files changed

+441
-12
lines changed

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,11 @@
6161

6262
<spring-cloud-dataflow-ui.version>2.1.1.BUILD-SNAPSHOT</spring-cloud-dataflow-ui.version>
6363

64-
<spring-cloud-deployer.version>2.0.2.BUILD-SNAPSHOT</spring-cloud-deployer.version>
64+
<spring-cloud-deployer.version>2.0.3.BUILD-SNAPSHOT</spring-cloud-deployer.version>
6565
<spring-cloud-deployer-local.version>2.0.4.BUILD-SNAPSHOT</spring-cloud-deployer-local.version>
6666

6767
<!-- note - check version of reactor at deployer-cf/cf-java-client uses -->
68-
<spring-cloud-deployer-cloudfoundry.version>2.0.4.BUILD-SNAPSHOT</spring-cloud-deployer-cloudfoundry.version>
68+
<spring-cloud-deployer-cloudfoundry.version>2.0.6.BUILD-SNAPSHOT</spring-cloud-deployer-cloudfoundry.version>
6969
<reactor.version>3.2.0.RELEASE</reactor.version>
7070
<spring-cloud-deployer-kubernetes.version>2.0.3.BUILD-SNAPSHOT</spring-cloud-deployer-kubernetes.version>
7171
<kubernetes-client.version>4.1.0</kubernetes-client.version>

spring-cloud-dataflow-classic-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/ApiDocumentation.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ public void index() throws Exception {
106106
linkWithRel("tasks/executions/name").description("Returns all task executions for a given Task name"),
107107
linkWithRel("tasks/executions/execution").description("Provides details for a specific task execution"),
108108
linkWithRel("tasks/platforms").description("Provides platform accounts for launching tasks"),
109+
linkWithRel("tasks/logs").description("Retrieve the task application log"),
109110

110111
linkWithRel("streams/definitions").description("Exposes the Streams resource"),
111112
linkWithRel("streams/definitions/definition").description("Handle a specific Stream definition"),
@@ -118,6 +119,10 @@ public void index() throws Exception {
118119
linkWithRel("streams/deployments/rollback/{name}/{version}").description("Rollback the stream to the previous or a specific version of the stream"),
119120
linkWithRel("streams/deployments/update/{name}").description("Update the stream."),
120121
linkWithRel("streams/deployments/platform/list").description("List of supported deployment platforms"),
122+
linkWithRel("streams/logs").description("Retrieve application logs of the stream"),
123+
linkWithRel("streams/logs/{streamName}").description("Retrieve application logs of the stream"),
124+
linkWithRel("streams/logs/{streamName}/{appName}").description("Retrieve a specific application log of the stream"),
125+
121126

122127
linkWithRel("tools/parseTaskTextToGraph").description("Parse a task definition into a graph structure"),
123128
linkWithRel("tools/convertTaskGraphToText").description("Convert a graph format into " + "DSL text format")),
@@ -142,6 +147,12 @@ public void index() throws Exception {
142147
fieldWithPath("_links.runtime/streams.href").description("Link to the runtime/streams"),
143148
fieldWithPath("_links.runtime/streams.templated").type(JsonFieldType.BOOLEAN).optional().description("Link runtime/streams is templated"),
144149

150+
fieldWithPath("_links.streams/logs.href").description("Link to the streams/logs"),
151+
fieldWithPath("_links.streams/logs/{streamName}.href").description("Link to the streams/logs/{streamName}"),
152+
fieldWithPath("_links.streams/logs/{streamName}/{appName}.href").description("Link to the streams/logs/{streamName}/{appName}"),
153+
fieldWithPath("_links.streams/logs/{streamName}.templated").type(JsonFieldType.BOOLEAN).optional().description("Link streams/logs/{streamName} is templated"),
154+
fieldWithPath("_links.streams/logs/{streamName}/{appName}.templated").type(JsonFieldType.BOOLEAN).optional().description("Link streams/logs/{streamName}/{appName} is templated"),
155+
145156
fieldWithPath("_links.streams/deployments.href").description("Link to the streams/deployments"),
146157
fieldWithPath("_links.streams/deployments/{name}.href").description("Link to the streams/deployments/{name}"),
147158
fieldWithPath("_links.streams/deployments/{name}.templated").type(JsonFieldType.BOOLEAN).optional().description("Link streams/deployments/{name} is templated"),
@@ -173,6 +184,9 @@ public void index() throws Exception {
173184
fieldWithPath("_links.tasks/executions/execution.href").description("Link to the tasks/executions/execution"),
174185
fieldWithPath("_links.tasks/executions/execution.templated").type(JsonFieldType.BOOLEAN).optional().description("Link tasks/executions/execution is templated"),
175186

187+
fieldWithPath("_links.tasks/logs.href").description("Link to the tasks/logs"),
188+
fieldWithPath("_links.tasks/logs.templated").type(JsonFieldType.BOOLEAN).optional().description("Link tasks/logs is templated"),
189+
176190
fieldWithPath("_links.tasks/schedules.href").description("Link to the tasks/executions/schedules"),
177191
fieldWithPath("_links.tasks/schedules/instances.href").description("Link to the tasks/schedules/instances"),
178192
fieldWithPath("_links.tasks/schedules/instances.templated").type(JsonFieldType.BOOLEAN).optional().description("Link tasks/schedules/instances is templated"),

spring-cloud-dataflow-platform-cloudfoundry/src/main/java/org/springframework/cloud/dataflow/server/config/cloudfoundry/CloudFoundryTaskPlatformFactory.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.cloudfoundry.operations.DefaultCloudFoundryOperations;
2929
import org.cloudfoundry.reactor.ConnectionContext;
3030
import org.cloudfoundry.reactor.TokenProvider;
31+
import org.cloudfoundry.reactor.doppler.ReactorDopplerClient;
3132
import org.slf4j.Logger;
3233
import org.slf4j.LoggerFactory;
3334

@@ -46,6 +47,7 @@
4647

4748
/**
4849
* @author David Turanski
50+
* @author Ilayaperumal Gopinathan
4951
**/
5052
public class CloudFoundryTaskPlatformFactory extends AbstractTaskPlatformFactory<CloudFoundryPlatformProperties> {
5153

@@ -138,6 +140,9 @@ private CloudFoundryOperations cloudFoundryOperations(CloudFoundryClient cloudFo
138140
return DefaultCloudFoundryOperations
139141
.builder().cloudFoundryClient(cloudFoundryClient)
140142
.organization(connectionProperties(account).getOrg())
143+
.dopplerClient(ReactorDopplerClient.builder()
144+
.connectionContext(connectionContext(account))
145+
.tokenProvider(tokenProvider(account)).build())
141146
.space(connectionProperties(account).getSpace()).build();
142147
}
143148

spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/config/DataFlowControllerAutoConfiguration.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,11 @@
6969
import org.springframework.cloud.dataflow.server.controller.RuntimeStreamsController;
7070
import org.springframework.cloud.dataflow.server.controller.StreamDefinitionController;
7171
import org.springframework.cloud.dataflow.server.controller.StreamDeploymentController;
72+
import org.springframework.cloud.dataflow.server.controller.StreamLogsController;
7273
import org.springframework.cloud.dataflow.server.controller.StreamValidationController;
7374
import org.springframework.cloud.dataflow.server.controller.TaskDefinitionController;
7475
import org.springframework.cloud.dataflow.server.controller.TaskExecutionController;
76+
import org.springframework.cloud.dataflow.server.controller.TaskLogsController;
7577
import org.springframework.cloud.dataflow.server.controller.TaskPlatformController;
7678
import org.springframework.cloud.dataflow.server.controller.TaskSchedulerController;
7779
import org.springframework.cloud.dataflow.server.controller.ToolsController;
@@ -290,6 +292,11 @@ public TaskValidationService taskValidationService(AppRegistryService appRegistr
290292
public TaskValidationController taskValidationController(TaskValidationService taskValidationService) {
291293
return new TaskValidationController(taskValidationService);
292294
}
295+
296+
@Bean
297+
public TaskLogsController taskLogsController(TaskExecutionService taskExecutionService) {
298+
return new TaskLogsController(taskExecutionService);
299+
}
293300
}
294301

295302
@Configuration
@@ -332,6 +339,11 @@ public RuntimeStreamsController runtimeStreamsController(StreamDeployer streamDe
332339
return new RuntimeStreamsController(streamDeployer);
333340
}
334341

342+
@Bean
343+
public StreamLogsController streamLogsController(StreamDeployer streamDeployer) {
344+
return new StreamLogsController(streamDeployer);
345+
}
346+
335347
@Bean
336348
@ConditionalOnMissingBean(name = "runtimeAppsStatusFJPFB")
337349
public ForkJoinPoolFactoryBean runtimeAppsStatusFJPFB() {

spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/controller/RootController.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,12 @@ public RootResource info() {
114114
root.add(ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(StreamDeploymentController.class).platformList()).withRel("streams/deployments/platform/list"));
115115
root.add(ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(StreamDeploymentController.class).rollback(null, null)).withRel("streams/deployments/rollback/{name}/{version}"));
116116
root.add(ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(StreamDeploymentController.class).update(null, null)).withRel("streams/deployments/update/{name}"));
117-
118117
root.add(
119118
unescapeTemplateVariables(entityLinks.linkToSingleResource(StreamDeploymentResource.class, "{name}")
120119
.withRel("streams/deployments/deployment")));
120+
root.add(ControllerLinkBuilder.linkTo(StreamLogsController.class).withRel("streams/logs"));
121+
root.add(ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(StreamLogsController.class).getLog(null)).withRel("streams/logs/{streamName}"));
122+
root.add(ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(StreamLogsController.class).getLog(null, null)).withRel("streams/logs/{streamName}/{appName}"));
121123
}
122124
if (featuresProperties.isTasksEnabled()) {
123125
root.add(entityLinks.linkToCollectionResource(LauncherResource.class).withRel("tasks/platforms"));
@@ -134,6 +136,7 @@ public RootResource info() {
134136
.withRel("tasks/executions/execution")));
135137
root.add(unescapeTemplateVariables(entityLinks.linkToSingleResource(TaskAppStatusResource.class, "{name}")
136138
.withRel("tasks/validation")));
139+
root.add(ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(TaskLogsController.class).getLog(null, null)).withRel("tasks/logs"));
137140

138141
if (featuresProperties.isSchedulesEnabled()) {
139142
root.add(entityLinks.linkToCollectionResource(ScheduleInfoResource.class).withRel("tasks/schedules"));
@@ -163,6 +166,7 @@ public RootResource info() {
163166
taskTemplated = entityLinks.linkToCollectionResource(JobExecutionThinResource.class).getHref() + "{?name}";
164167
root.add(new Link(taskTemplated).withRel("jobs/thinexecutions/name"));
165168

169+
166170
}
167171
root.add(entityLinks.linkToCollectionResource(AppRegistrationResource.class).withRel("apps"));
168172
root.add(entityLinks.linkToCollectionResource(AboutResource.class).withRel("about"));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.dataflow.server.controller;
18+
19+
import org.springframework.cloud.dataflow.server.stream.StreamDeployer;
20+
import org.springframework.http.HttpStatus;
21+
import org.springframework.http.ResponseEntity;
22+
import org.springframework.util.Assert;
23+
import org.springframework.web.bind.annotation.PathVariable;
24+
import org.springframework.web.bind.annotation.RequestMapping;
25+
import org.springframework.web.bind.annotation.RestController;
26+
27+
/**
28+
* Retrieves logs of deployed stream applications.
29+
*
30+
* @author Ilayaperumal Gopinathan
31+
*/
32+
@RestController
33+
@RequestMapping("/streams/logs")
34+
public class StreamLogsController {
35+
36+
private final StreamDeployer streamDeployer;
37+
38+
/**
39+
* Construct Stream logs controller.
40+
*
41+
* @param streamDeployer the deployer this controller uses to get the logs of
42+
* deployed stream apps
43+
*/
44+
public StreamLogsController(StreamDeployer streamDeployer) {
45+
Assert.notNull(streamDeployer, "StreamDeployer must not be null");
46+
this.streamDeployer = streamDeployer;
47+
}
48+
49+
@RequestMapping("{streamName}")
50+
public ResponseEntity<String> getLog(@PathVariable String streamName) {
51+
return new ResponseEntity<>(this.streamDeployer.getLog(streamName), HttpStatus.OK);
52+
}
53+
54+
@RequestMapping("{streamName}/{appName}")
55+
public ResponseEntity<String> getLog(@PathVariable String streamName, @PathVariable String appName) {
56+
return new ResponseEntity<>(this.streamDeployer.getLog(streamName, appName), HttpStatus.OK);
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.dataflow.server.controller;
18+
19+
import org.springframework.cloud.dataflow.server.service.TaskExecutionService;
20+
import org.springframework.http.HttpStatus;
21+
import org.springframework.http.ResponseEntity;
22+
import org.springframework.util.Assert;
23+
import org.springframework.web.bind.annotation.RequestMapping;
24+
import org.springframework.web.bind.annotation.RequestMethod;
25+
import org.springframework.web.bind.annotation.RequestParam;
26+
import org.springframework.web.bind.annotation.ResponseStatus;
27+
import org.springframework.web.bind.annotation.RestController;
28+
29+
/**
30+
* Retrieves logs of task applications.
31+
*
32+
* @author Ilayaperumal Gopinathan
33+
*/
34+
@RestController
35+
@RequestMapping("/tasks/logs")
36+
public class TaskLogsController {
37+
38+
private final TaskExecutionService taskExecutionService;
39+
40+
/**
41+
* Construct Task logs controller.
42+
*
43+
* @param taskExecutionService the task execution service that this controller uses to get the logs of
44+
* launched task applications.
45+
*/
46+
public TaskLogsController(TaskExecutionService taskExecutionService) {
47+
Assert.notNull(taskExecutionService, "TaskExecutionService must not be null");
48+
this.taskExecutionService = taskExecutionService;
49+
}
50+
51+
@RequestMapping(value = "", method = RequestMethod.GET, params = "name")
52+
@ResponseStatus(HttpStatus.OK)
53+
public ResponseEntity<String> getLog(@RequestParam(required = false, defaultValue = "default") String platformName,
54+
@RequestParam("name") String taskName) {
55+
return new ResponseEntity<>(this.taskExecutionService.getLog(platformName, taskName), HttpStatus.OK);
56+
}
57+
}

spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/service/TaskExecutionService.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,13 @@ public interface TaskExecutionService {
4242
*/
4343
long executeTask(String taskName, Map<String, String> taskDeploymentProperties, List<String> commandLineArgs);
4444

45+
/**
46+
* Retrieve logs for the task application.
47+
*
48+
* @param platformName the name of the platform
49+
* @param taskId the ID that uniquely identifies the task
50+
* @return the logs of the task application.
51+
*/
52+
String getLog(String platformName, String taskId);
53+
4554
}

spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/service/impl/DefaultTaskExecutionService.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
import java.util.stream.Collectors;
2323
import java.util.stream.StreamSupport;
2424

25+
import org.apache.commons.logging.Log;
26+
import org.apache.commons.logging.LogFactory;
27+
2528
import org.springframework.cloud.dataflow.audit.service.AuditRecordService;
2629
import org.springframework.cloud.dataflow.core.AuditActionType;
2730
import org.springframework.cloud.dataflow.core.AuditOperationType;
@@ -92,6 +95,8 @@ public class DefaultTaskExecutionService implements TaskExecutionService {
9295

9396
public static final String TASK_PLATFORM_NAME = "spring.cloud.dataflow.task.platformName";
9497

98+
protected final Log logger = LogFactory.getLog(getClass().getName());
99+
95100
/**
96101
* Initializes the {@link DefaultTaskExecutionService}.
97102
*
@@ -182,6 +187,11 @@ public long executeTask(String taskName, Map<String, String> taskDeploymentPrope
182187
return taskExecution.getExecutionId();
183188
}
184189

190+
@Override
191+
public String getLog(String platformName, String taskId) {
192+
return findTaskLauncher(platformName).getLog(taskId);
193+
}
194+
185195
private TaskLauncher findTaskLauncher(String platformName) {
186196
Launcher launcher = this.launcherRepository.findByName(platformName);
187197
if (launcher == null) {

spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/stream/SkipperStreamDeployer.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,16 @@ public List<AppStatus> getStreamStatuses(String streamName) {
488488
return skipperStatus(streamName);
489489
}
490490

491+
@Override
492+
public String getLog(String streamName) {
493+
return this.skipperClient.getLog(streamName);
494+
}
495+
496+
@Override
497+
public String getLog(String streamName, String appName) {
498+
return this.skipperClient.getLog(streamName, appName);
499+
}
500+
491501
private List<AppStatus> getStreamsStatuses(List<String> streamNames) {
492502
try {
493503
return this.forkJoinPool.submit(() -> streamNames.stream().parallel()

0 commit comments

Comments
 (0)