From b6524e410a428e9cc94c5077d1bd0512c7ae6c8d Mon Sep 17 00:00:00 2001 From: Sehoon Shon Date: Tue, 4 Nov 2025 16:02:31 -0500 Subject: [PATCH] migrate console.error to coreEvents/debugger for sandbox, logger, chatRecordingService (#12253) --- packages/cli/src/utils/sandbox.ts | 36 +++++++++---------- packages/core/src/core/logger.ts | 28 ++++++++++----- .../core/src/services/chatRecordingService.ts | 23 +++++++----- 3 files changed, 52 insertions(+), 35 deletions(-) diff --git a/packages/cli/src/utils/sandbox.ts b/packages/cli/src/utils/sandbox.ts index a08003732a..7b98fe3307 100644 --- a/packages/cli/src/utils/sandbox.ts +++ b/packages/cli/src/utils/sandbox.ts @@ -15,6 +15,7 @@ import { USER_SETTINGS_DIR } from '../config/settings.js'; import { promisify } from 'node:util'; import type { Config, SandboxConfig } from '@google/gemini-cli-core'; import { + coreEvents, debugLogger, FatalSandboxError, GEMINI_DIR, @@ -87,9 +88,8 @@ async function shouldUseCurrentUserInSandbox(): Promise { osReleaseContent.match(/^ID_LIKE=.*debian.*/m) || // Covers derivatives osReleaseContent.match(/^ID_LIKE=.*ubuntu.*/m) // Covers derivatives ) { - // note here and below we use console.error for informational messages on stderr - console.error( - 'INFO: Defaulting to use current user UID/GID for Debian/Ubuntu-based Linux.', + debugLogger.log( + 'Defaulting to use current user UID/GID for Debian/Ubuntu-based Linux.', ); return true; } @@ -216,8 +216,7 @@ export async function start_sandbox( `Missing macos seatbelt profile file '${profileFile}'`, ); } - // Log on STDERR so it doesn't clutter the output on STDOUT - console.error(`using macos seatbelt (profile: ${profile}) ...`); + debugLogger.log(`using macos seatbelt (profile: ${profile}) ...`); // if DEBUG is set, convert to --inspect-brk in NODE_OPTIONS const nodeOptions = [ ...(process.env['DEBUG'] ? ['--inspect-brk'] : []), @@ -319,7 +318,7 @@ export async function start_sandbox( // console.info(data.toString()); // }); proxyProcess.stderr?.on('data', (data) => { - console.error(data.toString()); + debugLogger.debug(`[PROXY STDERR]: ${data.toString().trim()}`); }); proxyProcess.on('close', (code, signal) => { if (sandboxProcess?.pid) { @@ -348,7 +347,7 @@ export async function start_sandbox( }); } - console.error(`hopping into sandbox (command: ${config.command}) ...`); + debugLogger.log(`hopping into sandbox (command: ${config.command}) ...`); // determine full path for gemini-cli to distinguish linked vs installed setting const gcPath = fs.realpathSync(process.argv[1]); @@ -373,7 +372,7 @@ export async function start_sandbox( 'run `npm link ./packages/cli` under gemini-cli repo to switch to linked binary.', ); } else { - console.error('building sandbox ...'); + debugLogger.log('building sandbox ...'); const gcRoot = gcPath.split('/packages/')[0]; // if project folder has sandbox.Dockerfile under project settings folder, use that let buildArgs = ''; @@ -382,7 +381,7 @@ export async function start_sandbox( 'sandbox.Dockerfile', ); if (isCustomProjectSandbox) { - console.error(`using ${projectSandboxDockerfile} for sandbox`); + debugLogger.log(`using ${projectSandboxDockerfile} for sandbox`); buildArgs += `-f ${path.resolve(projectSandboxDockerfile)} -i ${image}`; } execSync( @@ -497,7 +496,7 @@ export async function start_sandbox( `Missing mount path '${from}' listed in SANDBOX_MOUNTS`, ); } - console.error(`SANDBOX_MOUNTS: ${from} -> ${to} (${opts})`); + debugLogger.log(`SANDBOX_MOUNTS: ${from} -> ${to} (${opts})`); args.push('--volume', mount); } } @@ -679,7 +678,7 @@ export async function start_sandbox( for (let env of process.env['SANDBOX_ENV'].split(',')) { if ((env = env.trim())) { if (env.includes('=')) { - console.error(`SANDBOX_ENV: ${env}`); + debugLogger.log(`SANDBOX_ENV: ${env}`); args.push('--env', env); } else { throw new FatalSandboxError( @@ -789,7 +788,7 @@ export async function start_sandbox( // console.info(data.toString()); // }); proxyProcess.stderr?.on('data', (data) => { - console.error(data.toString().trim()); + debugLogger.debug(`[PROXY STDERR]: ${data.toString().trim()}`); }); proxyProcess.on('close', (code, signal) => { if (sandboxProcess?.pid) { @@ -818,7 +817,7 @@ export async function start_sandbox( return new Promise((resolve, reject) => { sandboxProcess.on('error', (err) => { - console.error('Sandbox process error:', err); + coreEvents.emitFeedback('error', 'Sandbox process error', err); reject(err); }); @@ -939,13 +938,13 @@ async function ensureSandboxImageIsPresent( sandbox: string, image: string, ): Promise { - console.info(`Checking for sandbox image: ${image}`); + debugLogger.log(`Checking for sandbox image: ${image}`); if (await imageExists(sandbox, image)) { - console.info(`Sandbox image ${image} found locally.`); + debugLogger.log(`Sandbox image ${image} found locally.`); return true; } - console.info(`Sandbox image ${image} not found locally.`); + debugLogger.log(`Sandbox image ${image} not found locally.`); if (image === LOCAL_DEV_SANDBOX_IMAGE_NAME) { // user needs to build the image themselves return false; @@ -954,7 +953,7 @@ async function ensureSandboxImageIsPresent( if (await pullImage(sandbox, image)) { // After attempting to pull, check again to be certain if (await imageExists(sandbox, image)) { - console.info(`Sandbox image ${image} is now available after pulling.`); + debugLogger.log(`Sandbox image ${image} is now available after pulling.`); return true; } else { debugLogger.warn( @@ -964,7 +963,8 @@ async function ensureSandboxImageIsPresent( } } - console.error( + coreEvents.emitFeedback( + 'error', `Failed to obtain sandbox image ${image} after check and pull attempt.`, ); return false; // Pull command failed or image still not present diff --git a/packages/core/src/core/logger.ts b/packages/core/src/core/logger.ts index 793b2666a6..ce7812b6d8 100644 --- a/packages/core/src/core/logger.ts +++ b/packages/core/src/core/logger.ts @@ -9,6 +9,7 @@ import { promises as fs } from 'node:fs'; import type { Content } from '@google/genai'; import type { Storage } from '../config/storage.js'; import { debugLogger } from '../utils/debugLogger.js'; +import { coreEvents } from '../utils/events.js'; const LOG_FILE_NAME = 'logs.json'; @@ -158,7 +159,7 @@ export class Logger { : 0; this.initialized = true; } catch (err) { - console.error('Failed to initialize logger:', err); + coreEvents.emitFeedback('error', 'Failed to initialize logger:', err); this.initialized = false; } } @@ -315,7 +316,7 @@ export class Logger { async saveCheckpoint(conversation: Content[], tag: string): Promise { if (!this.initialized) { - console.error( + debugLogger.error( 'Logger not initialized or checkpoint file path not set. Cannot save a checkpoint.', ); return; @@ -325,13 +326,13 @@ export class Logger { try { await fs.writeFile(path, JSON.stringify(conversation, null, 2), 'utf-8'); } catch (error) { - console.error('Error writing to checkpoint file:', error); + debugLogger.error('Error writing to checkpoint file:', error); } } async loadCheckpoint(tag: string): Promise { if (!this.initialized) { - console.error( + debugLogger.error( 'Logger not initialized or checkpoint file path not set. Cannot load checkpoint.', ); return []; @@ -354,14 +355,17 @@ export class Logger { // This is okay, it just means the checkpoint doesn't exist in either format. return []; } - console.error(`Failed to read or parse checkpoint file ${path}:`, error); + debugLogger.error( + `Failed to read or parse checkpoint file ${path}:`, + error, + ); return []; } } async deleteCheckpoint(tag: string): Promise { if (!this.initialized || !this.geminiDir) { - console.error( + debugLogger.error( 'Logger not initialized or checkpoint file path not set. Cannot delete checkpoint.', ); return false; @@ -377,7 +381,10 @@ export class Logger { } catch (error) { const nodeError = error as NodeJS.ErrnoException; if (nodeError.code !== 'ENOENT') { - console.error(`Failed to delete checkpoint file ${newPath}:`, error); + debugLogger.error( + `Failed to delete checkpoint file ${newPath}:`, + error, + ); throw error; // Rethrow unexpected errors } // It's okay if it doesn't exist. @@ -392,7 +399,10 @@ export class Logger { } catch (error) { const nodeError = error as NodeJS.ErrnoException; if (nodeError.code !== 'ENOENT') { - console.error(`Failed to delete checkpoint file ${oldPath}:`, error); + debugLogger.error( + `Failed to delete checkpoint file ${oldPath}:`, + error, + ); throw error; // Rethrow unexpected errors } // It's okay if it doesn't exist. @@ -421,7 +431,7 @@ export class Logger { return false; // It truly doesn't exist in either format. } // A different error occurred. - console.error( + debugLogger.error( `Failed to check checkpoint existence for ${ filePath ?? `path for tag "${tag}"` }:`, diff --git a/packages/core/src/services/chatRecordingService.ts b/packages/core/src/services/chatRecordingService.ts index 9817941c92..2ac9f4d99a 100644 --- a/packages/core/src/services/chatRecordingService.ts +++ b/packages/core/src/services/chatRecordingService.ts @@ -15,6 +15,7 @@ import type { PartListUnion, GenerateContentResponseUsageMetadata, } from '@google/genai'; +import { debugLogger } from '../utils/debugLogger.js'; export const SESSION_FILE_PREFIX = 'session-'; @@ -170,7 +171,7 @@ export class ChatRecordingService { this.queuedThoughts = []; this.queuedTokens = null; } catch (error) { - console.error('Error initializing chat recording service:', error); + debugLogger.error('Error initializing chat recording service:', error); throw error; } } @@ -222,7 +223,7 @@ export class ChatRecordingService { } }); } catch (error) { - console.error('Error saving message:', error); + debugLogger.error('Error saving message to chat history.', error); throw error; } } @@ -239,7 +240,7 @@ export class ChatRecordingService { timestamp: new Date().toISOString(), }); } catch (error) { - console.error('Error saving thought:', error); + debugLogger.error('Error saving thought to chat history.', error); throw error; } } @@ -273,7 +274,10 @@ export class ChatRecordingService { } }); } catch (error) { - console.error('Error updating message tokens:', error); + debugLogger.error( + 'Error updating message tokens in chat history.', + error, + ); throw error; } } @@ -367,7 +371,10 @@ export class ChatRecordingService { } }); } catch (error) { - console.error('Error adding tool call to message:', error); + debugLogger.error( + 'Error adding tool call to message in chat history.', + error, + ); throw error; } } @@ -381,7 +388,7 @@ export class ChatRecordingService { return JSON.parse(this.cachedLastConvData); } catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') { - console.error('Error reading conversation file:', error); + debugLogger.error('Error reading conversation file.', error); throw error; } @@ -413,7 +420,7 @@ export class ChatRecordingService { fs.writeFileSync(this.conversationFile, newContent); } } catch (error) { - console.error('Error writing conversation file:', error); + debugLogger.error('Error writing conversation file.', error); throw error; } } @@ -442,7 +449,7 @@ export class ChatRecordingService { const sessionPath = path.join(chatsDir, `${sessionId}.json`); fs.unlinkSync(sessionPath); } catch (error) { - console.error('Error deleting session:', error); + debugLogger.error('Error deleting session file.', error); throw error; } }