From b11f4ea336617863614620ef3ef060edd8c83ad7 Mon Sep 17 00:00:00 2001 From: saridsa2 Date: Fri, 17 Apr 2026 08:51:55 +0530 Subject: [PATCH] feat: log backfill endpoint for desktop log panel - LogStreamService: ring buffer (500 entries) + getRecentLogs() method - SupervisorController: GET /api/supervisor/logs/recent returns buffered log entries so the desktop log panel shows history on tab open, not just live stream Co-Authored-By: Claude Opus 4.6 (1M context) --- src/logging/log-stream.service.ts | 13 +++++++++++-- src/supervisor/supervisor.controller.ts | 5 +++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/logging/log-stream.service.ts b/src/logging/log-stream.service.ts index 7a29cb4..e9f26b7 100644 --- a/src/logging/log-stream.service.ts +++ b/src/logging/log-stream.service.ts @@ -15,14 +15,23 @@ export type LogEntry = { export class LogStreamService extends ConsoleLogger { static readonly instance = new LogStreamService(); readonly logSubject = new Subject(); + private readonly buffer: LogEntry[] = []; + private static readonly MAX_BUFFER = 500; + + getRecentLogs(limit = 200): LogEntry[] { + return this.buffer.slice(-limit); + } private emit(level: LogEntry['level'], message: unknown, context?: string) { - this.logSubject.next({ + const entry: LogEntry = { timestamp: new Date().toISOString(), level, context: context ?? this.context ?? '', message: typeof message === 'string' ? message : JSON.stringify(message), - }); + }; + this.buffer.push(entry); + if (this.buffer.length > LogStreamService.MAX_BUFFER) this.buffer.shift(); + this.logSubject.next(entry); } log(message: unknown, context?: string) { diff --git a/src/supervisor/supervisor.controller.ts b/src/supervisor/supervisor.controller.ts index 2e9d370..40fb357 100644 --- a/src/supervisor/supervisor.controller.ts +++ b/src/supervisor/supervisor.controller.ts @@ -78,6 +78,11 @@ export class SupervisorController { ); } + @Get('logs/recent') + getRecentLogs(@Query('limit') limit?: string) { + return LogStreamService.instance.getRecentLogs(limit ? parseInt(limit, 10) : 200); + } + @Sse('logs/stream') streamLogs(): Observable { this.logger.log('[SSE] Log stream opened');