Skip to content

Commit

Permalink
Merge pull request #245 from kubero-dev/243-show-buildlogs
Browse files Browse the repository at this point in the history
Feature / Add fetch and build logs viewe
  • Loading branch information
mms-gianni authored Oct 30, 2023
2 parents 62ac09b + f992add commit b2c0618
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 53 deletions.
17 changes: 13 additions & 4 deletions client/src/components/apps/logs.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
<template>
<div class="console" id="console" style="height: 100%;">
<div style="height: 90%;">
<v-tabs>
<template>
<v-tab @click="getLogHistory('web')">web</v-tab>
<v-tab @click="getLogHistory('builder')">build</v-tab>
<v-tab @click="getLogHistory('fetcher')">fetch</v-tab>
</template>
</v-tabs>
<div class="console" id="console">
<div v-for="line in loglines" :key="line.id">
{{ new Date(line.time).toISOString()}}<span :style="'color:' +line.color">[{{ line.podID }}/{{ line.container.replace('kuberoapp-', '') }}]</span>
{{ line.log }}
</div>
</div>
</div>
</template>

<script>
Expand All @@ -16,7 +25,7 @@ export default {
},
},
mounted() {
this.getLogHistory()
this.getLogHistory('web')
this.socketJoin()
this.startLogs()
},
Expand Down Expand Up @@ -70,8 +79,8 @@ export default {
console.log("logs started");
});
},
getLogHistory() {
axios.get(`/api/logs/${this.pipeline}/${this.phase}/${this.app}/history`).then((response) => {
getLogHistory(container) {
axios.get(`/api/logs/${this.pipeline}/${this.phase}/${this.app}/${container}/history`).then((response) => {
this.loglines = response.data;
});
},
Expand Down
107 changes: 60 additions & 47 deletions src/kubero.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import debug from 'debug';
import { Server } from "socket.io";
import { IApp, IPipeline, IPipelineList, IKubectlAppList, IDeployKeyPair, IKubectlPipelineList, IKubectlApp, IPodSize, IKuberoConfig} from './types';
import { IApp, IPipeline, IPipelineList, IKubectlAppList, IDeployKeyPair, IKubectlPipelineList, IKubectlApp, ILoglines, IKuberoConfig} from './types';
import { IPullrequest } from './git/types';
import { App } from './modules/application';
import { Buildpack } from './modules/config';
Expand Down Expand Up @@ -842,65 +842,78 @@ export class Kubero {
}
}

public async getLogsHistory(pipelineName: string, phaseName: string, appName: string) {
public async getLogsHistory(pipelineName: string, phaseName: string, appName: string, container: string) {
const contextName = this.getContext(pipelineName, phaseName);
const namespace = pipelineName+'-'+phaseName;

const logStream = new Stream.PassThrough();
let logs: String = '';
logStream.on('data', (chunk: any) => {
//console.log(chunk.toString());
logs += chunk.toString();
});

let loglines: any[] = [];
let loglines: ILoglines[] = [];
if (contextName) {
const pods = await this.kubectl.getPods(namespace, contextName);
for (const pod of pods) {

if (pod.metadata?.name?.startsWith(appName)) {
for (const container of pod.spec?.containers || []) {
console.log('getting logs for '+pod.metadata.name+' '+container.name);
try {
await this.kubectl.log.log(namespace, pod.metadata.name, container.name, logStream, {follow: false, tailLines: 80, pretty: false, timestamps: true})
} catch (error) {
console.log("error getting logs for "+pod.metadata.name+" "+container.name);
return loglines;
}


// sleep for 1 second to wait for all logs to be collected
await new Promise(r => setTimeout(r, 1000));

// split loglines into array
const loglinesArray = logs.split('\n').reverse();
for (const logline of loglinesArray) {
if (logline.length > 0) {
// split after first whitespace
const loglineArray = logline.split(/(?<=^\S+)\s/);
const loglineDate = new Date(loglineArray[0]);
const loglineText = loglineArray[1];



loglines.push({
id: uuidv4(),
time: loglineDate.getTime(),
pipeline: pipelineName,
phase: phaseName,
app: appName,
pod: pod.metadata.name,
podID: pod.metadata.name.split('-')[3]+'-'+pod.metadata.name.split('-')[4],
container: container.name,
color: this.logcolor(pod.metadata.name),
log: loglineText
});
}
if (container == 'web') {
for (const container of pod.spec?.containers || []) {
const ll = await this.fetchLogs(namespace, pod.metadata.name, container.name, pipelineName, phaseName, appName)
loglines = loglines.concat(ll);
}
} else if (container == 'builder' || container == 'fetcher') {
const ll = await this.fetchLogs(namespace, pod.metadata.name, "kuberoapp-"+container, pipelineName, phaseName, appName)
loglines = loglines.concat(ll);
} else {
// leace the loglines empty
console.log('unknown container: '+container);
}
}
}
}
return loglines;
}

private async fetchLogs(namespace: string, podName: string, containerName: string, pipelineName: string, phaseName: string, appName: string): Promise<ILoglines[]> {
let loglines: ILoglines[] = [];

const logStream = new Stream.PassThrough();
let logs: String = '';
logStream.on('data', (chunk: any) => {
//console.log(chunk.toString());
logs += chunk.toString();
});

console.log('getting logs for '+podName+' '+containerName);
try {
await this.kubectl.log.log(namespace, podName, containerName, logStream, {follow: false, tailLines: 80, pretty: false, timestamps: true})
} catch (error) {
console.log("error getting logs for "+podName+" "+containerName);
return [];
}

// sleep for 1 second to wait for all logs to be collected
await new Promise(r => setTimeout(r, 1000));

// split loglines into array
const loglinesArray = logs.split('\n').reverse();
for (const logline of loglinesArray) {
if (logline.length > 0) {
// split after first whitespace
const loglineArray = logline.split(/(?<=^\S+)\s/);
const loglineDate = new Date(loglineArray[0]);
const loglineText = loglineArray[1];

loglines.push({
id: uuidv4(),
time: loglineDate.getTime(),
pipeline: pipelineName,
phase: phaseName,
app: appName,
pod: podName,
podID: podName.split('-')[3]+'-'+podName.split('-')[4],
container: containerName,
color: this.logcolor(podName),
log: loglineText
});
}
}

return loglines;
}
Expand Down
5 changes: 3 additions & 2 deletions src/routes/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Router.get('/logs/:pipeline/:phase/:app', authMiddleware, async function (req: R
res.send('ok');
});

Router.get('/logs/:pipeline/:phase/:app/history', authMiddleware, async function (req: Request, res: Response) {
Router.get('/logs/:pipeline/:phase/:app/:container/history', authMiddleware, async function (req: Request, res: Response) {
// #swagger.tags = ['UI']
// #swagger.summary = 'Get logs history for a specific app'
// #swagger.description = 'Get logs history for a specific app'
Expand All @@ -37,7 +37,8 @@ Router.get('/logs/:pipeline/:phase/:app/history', authMiddleware, async function
const logs = await req.app.locals.kubero.getLogsHistory(
req.params.pipeline,
req.params.phase,
req.params.app
req.params.app,
req.params.container
);
res.send(logs);
});
Expand Down
13 changes: 13 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,4 +316,17 @@ export interface IDeployKeyPair {
pubKeyBase64: string;
privKey: string;
privKeyBase64: string;
}

export interface ILoglines {
id: string,
time: number,
pipeline: string,
phase: string,
app: string,
pod: string,
podID: string,
container: string,
color: string,
log: string,
}

0 comments on commit b2c0618

Please sign in to comment.