Skip to content

Commit fe657be

Browse files
committed
feature: allow to get logs longer than last 250
1 parent 2d70ef0 commit fe657be

2 files changed

Lines changed: 138 additions & 17 deletions

File tree

src/k8s/logging/logging.service.ts

Lines changed: 129 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,14 @@ export class LoggingService {
2929
public async getServiceLogs(
3030
service: string,
3131
stream: Writable,
32+
tailLines: number,
3233
previous = false,
3334
download = false,
3435
isJob = false,
36+
since?: {
37+
start: string;
38+
until: string;
39+
},
3540
): Promise<void> {
3641
let archive: archiver.Archiver;
3742

@@ -74,6 +79,7 @@ export class LoggingService {
7479
}
7580

7681
const podLogs: Promise<void>[] = [];
82+
7783
for (const pod of pods) {
7884
podLogs.push(
7985
this.getLogsForPod(
@@ -82,7 +88,8 @@ export class LoggingService {
8288
download,
8389
previous,
8490
archive,
85-
download ? undefined : 250,
91+
download ? undefined : tailLines,
92+
since,
8693
),
8794
);
8895
}
@@ -111,6 +118,40 @@ export class LoggingService {
111118
return podList.items;
112119
}
113120

121+
private async getFirstLogTimestamp(
122+
logApi: Log,
123+
namespace: string,
124+
pod: V1Pod,
125+
containerName: string,
126+
previous: boolean,
127+
) {
128+
const logStream = new PassThrough();
129+
await logApi.log(namespace, pod.metadata.name, containerName, logStream, {
130+
previous,
131+
timestamps: true,
132+
limitBytes: 8 * 1024,
133+
});
134+
135+
return await new Promise((resolve) => {
136+
logStream.on("data", (chunk) => {
137+
for (const line of chunk.toString().split("\n")) {
138+
const data = line.trim();
139+
if (data.length === 0) {
140+
return;
141+
}
142+
const log = this.parseLog(data);
143+
if (log.timestamp) {
144+
resolve(new Date(log.timestamp));
145+
}
146+
}
147+
});
148+
149+
logStream.on("end", () => {
150+
resolve(null);
151+
});
152+
});
153+
}
154+
114155
private async tryGetPodLogs(
115156
logApi: Log,
116157
namespace: string,
@@ -120,7 +161,8 @@ export class LoggingService {
120161
stream: Writable,
121162
previous: boolean,
122163
download: boolean,
123-
tailLines: number,
164+
tailLines: number = 250,
165+
since?: string,
124166
): Promise<void> {
125167
try {
126168
let podLogs: Awaited<ReturnType<typeof logApi.log>>;
@@ -147,8 +189,14 @@ export class LoggingService {
147189
previous,
148190
pretty: false,
149191
timestamps: true,
150-
follow: download === false,
151-
tailLines,
192+
tailLines: since || download ? undefined : tailLines || 250,
193+
...(since
194+
? {
195+
sinceTime: since,
196+
}
197+
: {
198+
follow: download === false,
199+
}),
152200
},
153201
);
154202
} catch (error) {
@@ -175,23 +223,67 @@ export class LoggingService {
175223
previous = false,
176224
archive?: archiver.Archiver,
177225
tailLines?: number,
226+
since?: {
227+
start: string;
228+
until: string;
229+
},
178230
) {
179231
let totalAdded = 0;
180232
let streamEnded = false;
181233

234+
let oldestTimestamp: Date;
235+
const until = since ? new Date(since.until) : undefined;
236+
182237
const endStream = () => {
183238
if (!streamEnded) {
184239
streamEnded = true;
185240
stream.end();
186241
}
187242
};
188243

244+
let totalLines = 0;
189245
for (const container of pod.spec.containers) {
190246
const logStream = new PassThrough();
191247

192-
logStream.on("end", () => {
193-
this.logger.log("log stream ended");
248+
logStream.on("end", async () => {
194249
++totalAdded;
250+
251+
if (totalLines < tailLines) {
252+
this.logger.log(
253+
`loading more logs from service ${pod.metadata.name}...`,
254+
);
255+
256+
const firstLogTimestamp = await this.getFirstLogTimestamp(
257+
logApi,
258+
this.namespace,
259+
pod,
260+
container.name,
261+
previous,
262+
);
263+
264+
const until = new Date(oldestTimestamp.toISOString());
265+
oldestTimestamp.setMinutes(oldestTimestamp.getMinutes() - 60);
266+
267+
if (oldestTimestamp < firstLogTimestamp) {
268+
endStream();
269+
return;
270+
}
271+
272+
await this.getLogsForPod(
273+
pod,
274+
stream,
275+
download,
276+
previous,
277+
archive,
278+
tailLines - totalLines,
279+
{
280+
start: oldestTimestamp.toISOString(),
281+
until: until.toISOString(),
282+
},
283+
);
284+
return;
285+
}
286+
195287
if (archive && totalAdded == pod.spec.containers.length) {
196288
void archive.finalize();
197289
}
@@ -209,15 +301,24 @@ export class LoggingService {
209301
return;
210302
}
211303

212-
for (let log of text.split(/\n/)) {
213-
const timestampMatch = log.match(
214-
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z/,
215-
);
216-
const timestamp = timestampMatch ? timestampMatch[0] : "";
217-
log = log.replace(
218-
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z\s*/,
219-
"",
220-
);
304+
for (let data of text.split(/\n/)) {
305+
const { timestamp, log } = this.parseLog(data);
306+
307+
if (since) {
308+
if (new Date(timestamp)) {
309+
const latestTimestamp = new Date(timestamp);
310+
311+
if (!oldestTimestamp || oldestTimestamp > latestTimestamp) {
312+
oldestTimestamp = latestTimestamp;
313+
}
314+
315+
if (latestTimestamp && latestTimestamp >= until) {
316+
continue;
317+
}
318+
}
319+
}
320+
321+
totalLines++;
221322

222323
stream.write(
223324
JSON.stringify({
@@ -236,7 +337,6 @@ export class LoggingService {
236337
});
237338

238339
logStream.on("close", () => {
239-
this.logger.log("log stream closed");
240340
endStream();
241341
});
242342

@@ -258,10 +358,23 @@ export class LoggingService {
258358
previous,
259359
download,
260360
tailLines,
361+
since?.start,
261362
);
262363
}
263364
}
264365

366+
private parseLog(log: string) {
367+
const timestampMatch = log.match(
368+
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z/,
369+
);
370+
const timestamp = timestampMatch ? timestampMatch[0] : "";
371+
log = log.replace(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z\s*/, "");
372+
return {
373+
log,
374+
timestamp,
375+
};
376+
}
377+
265378
public async getJobStatus(jobName: string) {
266379
try {
267380
const job = await this.batchApi.readNamespacedJob({

src/system/system.gateway.ts.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,15 @@ export class SystemGateway {
2727
data: {
2828
service: string;
2929
previous?: boolean;
30+
tailLines?: number;
31+
since?: {
32+
start: string;
33+
until: string;
34+
};
3035
},
3136
@ConnectedSocket() client: FiveStackWebSocketClient,
3237
) {
33-
let { service, previous } = data;
38+
let { service, previous, tailLines, since } = data;
3439

3540
if (!isRoleAbove(client.user.role, "administrator")) {
3641
return;
@@ -67,6 +72,7 @@ export class SystemGateway {
6772
event: `logs:${service}`,
6873
data: JSON.stringify({
6974
end: true,
75+
partial: !!since,
7076
job_finshed: jobFinshed,
7177
}),
7278
}),
@@ -81,9 +87,11 @@ export class SystemGateway {
8187
)
8288
: service,
8389
stream,
90+
tailLines,
8491
!!previous,
8592
false,
8693
isJob,
94+
since,
8795
);
8896
} catch (error) {
8997
this.logger.warn(

0 commit comments

Comments
 (0)