This is an automated email from the ASF dual-hosted git repository. marat pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-karavan.git
commit edf46ac538c0beaaaa564971e76734ceec9dad39 Author: Marat Gubaidullin <marat.gubaidul...@gmail.com> AuthorDate: Tue May 2 22:32:54 2023 -0400 Fix #563 --- .../apache/camel/karavan/api/LogWatchResource.java | 23 +++++++------ .../camel/karavan/service/KubernetesService.java | 40 +++++----------------- karavan-app/src/main/webui/src/api/KaravanApi.tsx | 2 +- .../src/main/webui/src/projects/ProjectLog.tsx | 36 +++++++++++-------- 4 files changed, 44 insertions(+), 57 deletions(-) diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/api/LogWatchResource.java b/karavan-app/src/main/java/org/apache/camel/karavan/api/LogWatchResource.java index 5fa14161..ae7ef8b6 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/api/LogWatchResource.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/api/LogWatchResource.java @@ -59,26 +59,27 @@ public class LogWatchResource { @Produces(MediaType.SERVER_SENT_EVENTS) @Path("/{type}/{env}/{name}") public void eventSourcing(@PathParam("env") String env, - @PathParam("type") String type, - @PathParam("name") String name, - @Context SseEventSink eventSink, - @Context Sse sse - ) { + @PathParam("type") String type, + @PathParam("name") String name, + @Context SseEventSink eventSink, + @Context Sse sse + ) { managedExecutor.execute(() -> { + LOGGER.info("LogWatch for " + name + " starting..."); try (SseEventSink sink = eventSink) { - LogWatch logWatch = kubernetesService.getLogWatch(name); + LogWatch logWatch = type.equals("container") + ? kubernetesService.getContainerLogWatch(name) + : kubernetesService.getPipelineRunLogWatch(name); BufferedReader reader = new BufferedReader(new InputStreamReader(logWatch.getOutput())); try { for (String line; (line = reader.readLine()) != null && !sink.isClosed(); ) { - sink.send(sse.newEvent(line + System.lineSeparator())); + sink.send(sse.newEvent(line)); } } catch (IOException e) { LOGGER.error(e.getMessage()); } - if (sink.isClosed()) { - logWatch.close(); - LOGGER.info("LogWatch for " + name + " closed"); - } + logWatch.close(); + LOGGER.info("LogWatch for " + name + " closed"); } }); } diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java b/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java index 13b4484c..7bbac8ea 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java @@ -185,37 +185,15 @@ public class KubernetesService implements HealthCheck{ return logText; } - // TODO: implement log watch - public LogWatch getLogWatch(String podName) { - return kubernetesClient().pods().inNamespace(getNamespace()).withName(podName).watchLog(); - } -// public void startContainerLogWatch(String session, String podName) { -// Tuple2<CompletableFuture<Void>, LogWatch> old = logWatches.get(session); -// if (old != null) { -// LOGGER.info("Closing old"); -// old.getItem1().cancel(true); -// old.getItem2().close(); -// logWatches.remove(session); -// LOGGER.info("Closed old"); -// } -// -// LOGGER.info("Starting startContainerLogWatch"); -// CompletableFuture<Void> future = managedExecutor.runAsync(() -> { -// LogWatch logWatch = -// BufferedReader reader = new BufferedReader(new InputStreamReader(logWatch.getOutput())); -// try { -// for (String line; (line = reader.readLine()) != null; ) { -// eventBus.publish(session, System.lineSeparator()); -// eventBus.publish(session, line); -// System.out.println(line); -// } -// } catch (IOException e) { -// LOGGER.error(e.getMessage()); -// } -// }); -// logWatches.put(session, Tuple2.of(future, logWatch)); -// LOGGER.info("Done startContainerLogWatch"); -// } + public LogWatch getContainerLogWatch(String podName) { + return kubernetesClient().pods().inNamespace(getNamespace()).withName(podName).tailingLines(100).watchLog(); + } + + public LogWatch getPipelineRunLogWatch(String pipelineRuneName) { + List<TaskRun> tasks = getTaskRuns(pipelineRuneName, getNamespace()); + TaskRun taskRun = tasks.get(0); + return kubernetesClient().pods().inNamespace(getNamespace()).withName(taskRun.getStatus().getPodName()).tailingLines(100).watchLog(); + } public String getPipelineRunLog(String pipelineRuneName, String namespace) { StringBuilder result = new StringBuilder(); diff --git a/karavan-app/src/main/webui/src/api/KaravanApi.tsx b/karavan-app/src/main/webui/src/api/KaravanApi.tsx index b456a556..5210e0de 100644 --- a/karavan-app/src/main/webui/src/api/KaravanApi.tsx +++ b/karavan-app/src/main/webui/src/api/KaravanApi.tsx @@ -1,4 +1,4 @@ -import axios, {AxiosResponse, AxiosRequestConfig, InternalAxiosRequestConfig, AxiosRequestHeaders } from "axios"; +import axios, {AxiosResponse } from "axios"; import { CamelStatus, DeploymentStatus, diff --git a/karavan-app/src/main/webui/src/projects/ProjectLog.tsx b/karavan-app/src/main/webui/src/projects/ProjectLog.tsx index 2b81dda5..f9c3e64c 100644 --- a/karavan-app/src/main/webui/src/projects/ProjectLog.tsx +++ b/karavan-app/src/main/webui/src/projects/ProjectLog.tsx @@ -22,7 +22,8 @@ interface State { height?: number | string, logViewerRef: any, isTextWrapped: boolean - data: string[] + data: string, + currentLine: number } export class ProjectLog extends React.Component<Props, State> { @@ -32,12 +33,14 @@ export class ProjectLog extends React.Component<Props, State> { height: "30%", logViewerRef: React.createRef(), isTextWrapped: true, - data: [] + data: '', + currentLine: 0 } - + eventSource?: EventSource; sub?: Subscription; componentDidMount() { + this.eventSource?.close(); this.sub = ProjectEventBus.onShowLog()?.subscribe((log: ShowLogCommand) => { this.setState({showLog: true, log: log}); this.showLogs(log.type, log.name, log.environment); @@ -45,20 +48,20 @@ export class ProjectLog extends React.Component<Props, State> { } componentWillUnmount() { + this.eventSource?.close(); this.sub?.unsubscribe(); } showLogs = (type: 'container' | 'pipeline', name: string, environment: string) => { - if (type === 'pipeline') { - KaravanApi.getPipelineLog(environment, name, (res: any) => { - this.setState({data: res}); - }); - } else if (type === 'container') { - KaravanApi.getContainerLog(environment, name, (res: any) => { - this.setState({data: res}); - }); + this.eventSource?.close(); + this.eventSource = new EventSource("/api/logwatch/"+type+"/"+environment+"/"+name); + this.eventSource.onerror = (event) => { + this.eventSource?.close(); } - + this.eventSource.onmessage = (event) => { + const data = this.state.data.concat('\n').concat(event.data) + this.setState({data: data, currentLine: this.state.currentLine + 1}); + }; } getButtons() { @@ -76,12 +79,15 @@ export class ProjectLog extends React.Component<Props, State> { this.setState({height: h, showLog: true}); }} icon={height === "100%" ? <CollapseIcon/> : <ExpandIcon/>}/> </Tooltip> - <Button variant="plain" onClick={() => this.setState({height: "30%", showLog: false})} icon={<CloseIcon/>}/> + <Button variant="plain" onClick={() => { + this.eventSource?.close(); + this.setState({height: "30%", showLog: false, data: '', currentLine: 0}); + }} icon={<CloseIcon/>}/> </div>); } render() { - const {showLog, height, logViewerRef, isTextWrapped, data} = this.state; + const {showLog, height, logViewerRef, isTextWrapped, data, currentLine} = this.state; return (showLog ? <PageSection className="project-log" padding={{default: "noPadding"}} style={{height: height}}> <LogViewer @@ -92,6 +98,8 @@ export class ProjectLog extends React.Component<Props, State> { header={this.getButtons()} height={"100vh"} data={data} + scrollToRow={currentLine} + overScanCount={10} theme={'dark'}/> </PageSection> : <></>);