From 12d4ec2e071c8ad491ad8530571a8c32801992d0 Mon Sep 17 00:00:00 2001 From: Shreya Keshive Date: Thu, 2 Oct 2025 13:10:13 -0400 Subject: [PATCH] feat(ide extension): introduce debug logging (#10416) --- packages/vscode-ide-companion/package.json | 10 ++++ .../vscode-ide-companion/src/diff-manager.ts | 3 - .../src/extension.test.ts | 3 + .../vscode-ide-companion/src/ide-server.ts | 55 +++++++++++++------ .../vscode-ide-companion/src/utils/logger.ts | 8 ++- 5 files changed, 57 insertions(+), 22 deletions(-) diff --git a/packages/vscode-ide-companion/package.json b/packages/vscode-ide-companion/package.json index 3a130eccca..2ec84c603c 100644 --- a/packages/vscode-ide-companion/package.json +++ b/packages/vscode-ide-companion/package.json @@ -31,6 +31,16 @@ "onStartupFinished" ], "contributes": { + "configuration": { + "title": "Gemini CLI", + "properties": { + "gemini-cli.debug.logging.enabled": { + "type": "boolean", + "default": false, + "description": "Enable detailed logging for debugging the Gemini CLI Companion." + } + } + }, "languages": [ { "id": "gemini-diff-editable" diff --git a/packages/vscode-ide-companion/src/diff-manager.ts b/packages/vscode-ide-companion/src/diff-manager.ts index 5cdb31aed0..78cd293ebe 100644 --- a/packages/vscode-ide-companion/src/diff-manager.ts +++ b/packages/vscode-ide-companion/src/diff-manager.ts @@ -168,7 +168,6 @@ export class DiffManager { async acceptDiff(rightDocUri: vscode.Uri) { const diffInfo = this.diffDocuments.get(rightDocUri.toString()); if (!diffInfo) { - this.log(`No diff info found for ${rightDocUri.toString()}`); return; } @@ -194,8 +193,6 @@ export class DiffManager { async cancelDiff(rightDocUri: vscode.Uri) { const diffInfo = this.diffDocuments.get(rightDocUri.toString()); if (!diffInfo) { - this.log(`No diff info found for ${rightDocUri.toString()}`); - // Even if we don't have diff info, we should still close the editor. await this.closeDiffEditor(rightDocUri); return; } diff --git a/packages/vscode-ide-companion/src/extension.test.ts b/packages/vscode-ide-companion/src/extension.test.ts index e79f273e83..ef99ae670e 100644 --- a/packages/vscode-ide-companion/src/extension.test.ts +++ b/packages/vscode-ide-companion/src/extension.test.ts @@ -47,6 +47,9 @@ vi.mock('vscode', () => ({ registerTextDocumentContentProvider: vi.fn(), onDidChangeWorkspaceFolders: vi.fn(), onDidGrantWorkspaceTrust: vi.fn(), + getConfiguration: vi.fn(() => ({ + get: vi.fn(), + })), }, commands: { registerCommand: vi.fn(), diff --git a/packages/vscode-ide-companion/src/ide-server.ts b/packages/vscode-ide-companion/src/ide-server.ts index eb04aa0320..e158d50250 100644 --- a/packages/vscode-ide-companion/src/ide-server.ts +++ b/packages/vscode-ide-companion/src/ide-server.ts @@ -107,13 +107,6 @@ function sendIdeContextUpdateNotification( params: ideContext, }); - log( - `Sending IDE context update notification: ${JSON.stringify( - notification, - null, - 2, - )}`, - ); transport.send(notification); } @@ -190,7 +183,7 @@ export class IDEServer { next(); }); - const mcpServer = createMcpServer(this.diffManager); + const mcpServer = createMcpServer(this.diffManager, this.log); this.openFilesManager = new OpenFilesManager(context); const onDidChangeSubscription = this.openFilesManager.onDidChange(() => { @@ -222,16 +215,27 @@ export class IDEServer { this.transports[newSessionId] = transport; }, }); + let missedPings = 0; const keepAlive = setInterval(() => { - try { - transport.send({ jsonrpc: '2.0', method: 'ping' }); - } catch (e) { - this.log( - 'Failed to send keep-alive ping, cleaning up interval.' + e, - ); - clearInterval(keepAlive); - } - }, 30000); // 30 sec + const sessionId = transport.sessionId ?? 'unknown'; + transport + .send({ jsonrpc: '2.0', method: 'ping' }) + .then(() => { + missedPings = 0; + }) + .catch((error) => { + missedPings++; + this.log( + `Failed to send keep-alive ping for session ${sessionId}. Missed pings: ${missedPings}. Error: ${error.message}`, + ); + if (missedPings >= 3) { + this.log( + `Session ${sessionId} missed ${missedPings} pings. Closing connection and cleaning up interval.`, + ); + clearInterval(keepAlive); + } + }); + }, 60000); // 60 sec transport.onclose = () => { clearInterval(keepAlive); @@ -315,6 +319,8 @@ export class IDEServer { app.get('/mcp', handleSessionRequest); app.use((err: Error, req: Request, res: Response, next: NextFunction) => { + this.log(`Error processing request: ${err.message}`); + this.log(`Stack trace: ${err.stack}`); if (err instanceof CORSError) { res.status(403).json({ error: 'Request denied by CORS policy.' }); } else { @@ -349,6 +355,14 @@ export class IDEServer { } resolve(); }); + + this.server.on('close', () => { + this.log('IDE server connection closed.'); + }); + + this.server.on('error', (error) => { + this.log(`IDE server error: ${error.message}`); + }); }); } @@ -421,7 +435,10 @@ export class IDEServer { } } -const createMcpServer = (diffManager: DiffManager) => { +const createMcpServer = ( + diffManager: DiffManager, + log: (message: string) => void, +) => { const server = new McpServer( { name: 'gemini-cli-companion-mcp-server', @@ -437,6 +454,7 @@ const createMcpServer = (diffManager: DiffManager) => { inputSchema: OpenDiffRequestSchema.shape, }, async ({ filePath, newContent }: z.infer) => { + log(`Received openDiff request for filePath: ${filePath}`); await diffManager.showDiff(filePath, newContent); return { content: [] }; }, @@ -451,6 +469,7 @@ const createMcpServer = (diffManager: DiffManager) => { filePath, suppressNotification, }: z.infer) => { + log(`Received closeDiff request for filePath: ${filePath}`); const content = await diffManager.closeDiff( filePath, suppressNotification, diff --git a/packages/vscode-ide-companion/src/utils/logger.ts b/packages/vscode-ide-companion/src/utils/logger.ts index b3f8ad1e98..8c405558ca 100644 --- a/packages/vscode-ide-companion/src/utils/logger.ts +++ b/packages/vscode-ide-companion/src/utils/logger.ts @@ -11,7 +11,13 @@ export function createLogger( logger: vscode.OutputChannel, ) { return (message: string) => { - if (context.extensionMode === vscode.ExtensionMode.Development) { + const isDevMode = + context.extensionMode === vscode.ExtensionMode.Development; + const isLoggingEnabled = vscode.workspace + .getConfiguration('gemini-cli.debug') + .get('logging.enabled'); + + if (isDevMode || isLoggingEnabled) { logger.appendLine(message); } };