mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-27 21:44:25 -07:00
Migrate console to coreEvents.emitFeedback or debugLogger (#15219)
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* eslint-disable no-console */
|
||||
import * as fs from 'node:fs';
|
||||
import * as util from 'node:util';
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
} from '../utils/messageInspectors.js';
|
||||
import * as fs from 'node:fs';
|
||||
import { promptIdContext } from './promptIdContext.js';
|
||||
import { debugLogger } from './debugLogger.js';
|
||||
|
||||
const CODE_CORRECTION_SYSTEM_PROMPT = `
|
||||
You are an expert code-editing assistant. Your task is to analyze a failed edit attempt and provide a corrected version of the text snippets.
|
||||
@@ -434,7 +435,7 @@ Return ONLY the corrected target snippet in the specified JSON format with the k
|
||||
throw error;
|
||||
}
|
||||
|
||||
console.error(
|
||||
debugLogger.warn(
|
||||
'Error during LLM call for old string snippet correction:',
|
||||
error,
|
||||
);
|
||||
@@ -523,7 +524,7 @@ Return ONLY the corrected string in the specified JSON format with the key 'corr
|
||||
throw error;
|
||||
}
|
||||
|
||||
console.error('Error during LLM call for new_string correction:', error);
|
||||
debugLogger.warn('Error during LLM call for new_string correction:', error);
|
||||
return originalNewString;
|
||||
}
|
||||
}
|
||||
@@ -593,7 +594,7 @@ Return ONLY the corrected string in the specified JSON format with the key 'corr
|
||||
throw error;
|
||||
}
|
||||
|
||||
console.error(
|
||||
debugLogger.warn(
|
||||
'Error during LLM call for new_string escaping correction:',
|
||||
error,
|
||||
);
|
||||
@@ -660,7 +661,7 @@ Return ONLY the corrected string in the specified JSON format with the key 'corr
|
||||
throw error;
|
||||
}
|
||||
|
||||
console.error(
|
||||
debugLogger.warn(
|
||||
'Error during LLM call for string escaping correction:',
|
||||
error,
|
||||
);
|
||||
|
||||
@@ -9,12 +9,13 @@ import fs from 'node:fs/promises';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { reportError } from './errorReporting.js';
|
||||
import { debugLogger } from './debugLogger.js';
|
||||
|
||||
// Use a type alias for SpyInstance as it's not directly exported
|
||||
type SpyInstance = ReturnType<typeof vi.spyOn>;
|
||||
|
||||
describe('reportError', () => {
|
||||
let consoleErrorSpy: SpyInstance;
|
||||
let debugLoggerErrorSpy: SpyInstance;
|
||||
let testDir: string;
|
||||
const MOCK_TIMESTAMP = '2025-01-01T00-00-00-000Z';
|
||||
|
||||
@@ -22,7 +23,9 @@ describe('reportError', () => {
|
||||
// Create a temporary directory for logs
|
||||
testDir = await fs.mkdtemp(path.join(os.tmpdir(), 'gemini-report-test-'));
|
||||
vi.resetAllMocks();
|
||||
consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
debugLoggerErrorSpy = vi
|
||||
.spyOn(debugLogger, 'error')
|
||||
.mockImplementation(() => {});
|
||||
vi.spyOn(Date.prototype, 'toISOString').mockReturnValue(MOCK_TIMESTAMP);
|
||||
});
|
||||
|
||||
@@ -54,9 +57,10 @@ describe('reportError', () => {
|
||||
context,
|
||||
});
|
||||
|
||||
// Verify the console log
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
// Verify the user feedback
|
||||
expect(debugLoggerErrorSpy).toHaveBeenCalledWith(
|
||||
`${baseMessage} Full report available at: ${expectedReportPath}`,
|
||||
error,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -75,8 +79,9 @@ describe('reportError', () => {
|
||||
error: { message: 'Test plain object error' },
|
||||
});
|
||||
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
expect(debugLoggerErrorSpy).toHaveBeenCalledWith(
|
||||
`${baseMessage} Full report available at: ${expectedReportPath}`,
|
||||
error,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -95,8 +100,9 @@ describe('reportError', () => {
|
||||
error: { message: 'Just a string error' },
|
||||
});
|
||||
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
expect(debugLoggerErrorSpy).toHaveBeenCalledWith(
|
||||
`${baseMessage} Full report available at: ${expectedReportPath}`,
|
||||
error,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -109,15 +115,18 @@ describe('reportError', () => {
|
||||
|
||||
await reportError(error, baseMessage, context, type, nonExistentDir);
|
||||
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
expect(debugLoggerErrorSpy).toHaveBeenCalledWith(
|
||||
`${baseMessage} Additionally, failed to write detailed error report:`,
|
||||
expect.any(Error), // The actual write error
|
||||
);
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
expect(debugLoggerErrorSpy).toHaveBeenCalledWith(
|
||||
'Original error that triggered report generation:',
|
||||
error,
|
||||
);
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith('Original context:', context);
|
||||
expect(debugLoggerErrorSpy).toHaveBeenCalledWith(
|
||||
'Original context:',
|
||||
context,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle stringification failure of report content (e.g. BigInt in context)', async () => {
|
||||
@@ -146,15 +155,15 @@ describe('reportError', () => {
|
||||
|
||||
await reportError(error, baseMessage, context, type, testDir);
|
||||
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
expect(debugLoggerErrorSpy).toHaveBeenCalledWith(
|
||||
`${baseMessage} Could not stringify report content (likely due to context):`,
|
||||
stringifyError,
|
||||
);
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
expect(debugLoggerErrorSpy).toHaveBeenCalledWith(
|
||||
'Original error that triggered report generation:',
|
||||
error,
|
||||
);
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
expect(debugLoggerErrorSpy).toHaveBeenCalledWith(
|
||||
'Original context could not be stringified or included in report.',
|
||||
);
|
||||
|
||||
@@ -165,8 +174,9 @@ describe('reportError', () => {
|
||||
error: { message: error.message, stack: error.stack },
|
||||
});
|
||||
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
expect(debugLoggerErrorSpy).toHaveBeenCalledWith(
|
||||
`${baseMessage} Partial report (excluding context) available at: ${expectedMinimalReportPath}`,
|
||||
error,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -186,8 +196,9 @@ describe('reportError', () => {
|
||||
error: { message: 'Error without context', stack: 'No context stack' },
|
||||
});
|
||||
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
expect(debugLoggerErrorSpy).toHaveBeenCalledWith(
|
||||
`${baseMessage} Full report available at: ${expectedReportPath}`,
|
||||
error,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ import fs from 'node:fs/promises';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import type { Content } from '@google/genai';
|
||||
import { debugLogger } from './debugLogger.js';
|
||||
|
||||
interface ErrorReportData {
|
||||
error: { message: string; stack?: string } | { message: string };
|
||||
@@ -16,7 +17,7 @@ interface ErrorReportData {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an error report, writes it to a temporary file, and logs information to console.error.
|
||||
* Generates an error report, writes it to a temporary file, and logs information to user
|
||||
* @param error The error object.
|
||||
* @param context The relevant context (e.g., chat history, request contents).
|
||||
* @param type A string to identify the type of error (e.g., 'startChat', 'generateJson-api').
|
||||
@@ -59,13 +60,16 @@ export async function reportError(
|
||||
stringifiedReportContent = JSON.stringify(reportContent, null, 2);
|
||||
} catch (stringifyError) {
|
||||
// This can happen if context contains something like BigInt
|
||||
console.error(
|
||||
debugLogger.error(
|
||||
`${baseMessage} Could not stringify report content (likely due to context):`,
|
||||
stringifyError,
|
||||
);
|
||||
console.error('Original error that triggered report generation:', error);
|
||||
debugLogger.error(
|
||||
'Original error that triggered report generation:',
|
||||
error,
|
||||
);
|
||||
if (context) {
|
||||
console.error(
|
||||
debugLogger.error(
|
||||
'Original context could not be stringified or included in report.',
|
||||
);
|
||||
}
|
||||
@@ -75,11 +79,12 @@ export async function reportError(
|
||||
stringifiedReportContent = JSON.stringify(minimalReportContent, null, 2);
|
||||
// Still try to write the minimal report
|
||||
await fs.writeFile(reportPath, stringifiedReportContent);
|
||||
console.error(
|
||||
debugLogger.error(
|
||||
`${baseMessage} Partial report (excluding context) available at: ${reportPath}`,
|
||||
error,
|
||||
);
|
||||
} catch (minimalWriteError) {
|
||||
console.error(
|
||||
debugLogger.error(
|
||||
`${baseMessage} Failed to write even a minimal error report:`,
|
||||
minimalWriteError,
|
||||
);
|
||||
@@ -89,28 +94,37 @@ export async function reportError(
|
||||
|
||||
try {
|
||||
await fs.writeFile(reportPath, stringifiedReportContent);
|
||||
console.error(`${baseMessage} Full report available at: ${reportPath}`);
|
||||
debugLogger.error(
|
||||
`${baseMessage} Full report available at: ${reportPath}`,
|
||||
error,
|
||||
);
|
||||
} catch (writeError) {
|
||||
console.error(
|
||||
debugLogger.error(
|
||||
`${baseMessage} Additionally, failed to write detailed error report:`,
|
||||
writeError,
|
||||
);
|
||||
// Log the original error as a fallback if report writing fails
|
||||
console.error('Original error that triggered report generation:', error);
|
||||
debugLogger.error(
|
||||
'Original error that triggered report generation:',
|
||||
error,
|
||||
);
|
||||
|
||||
if (context) {
|
||||
// Context was stringifiable, but writing the file failed.
|
||||
// We already have stringifiedReportContent, but it might be too large for console.
|
||||
// So, we try to log the original context object, and if that fails, its stringified version (truncated).
|
||||
try {
|
||||
console.error('Original context:', context);
|
||||
debugLogger.error('Original context:', context);
|
||||
} catch {
|
||||
try {
|
||||
console.error(
|
||||
debugLogger.error(
|
||||
'Original context (stringified, truncated):',
|
||||
JSON.stringify(context).substring(0, 1000),
|
||||
);
|
||||
} catch {
|
||||
console.error('Original context could not be logged or stringified.');
|
||||
debugLogger.error(
|
||||
'Original context could not be logged or stringified.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,7 +342,10 @@ export async function getFolderStructure(
|
||||
|
||||
return `${summary}\n\n${resolvedPath}${path.sep}\n${structureLines.join('\n')}`;
|
||||
} catch (error: unknown) {
|
||||
console.error(`Error getting folder structure for ${resolvedPath}:`, error);
|
||||
debugLogger.warn(
|
||||
`Error getting folder structure for ${resolvedPath}:`,
|
||||
error,
|
||||
);
|
||||
return `Error processing directory "${resolvedPath}": ${getErrorMessage(error)}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ const logger = {
|
||||
debugLogger.warn('[WARN] [MemoryDiscovery]', ...args),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
error: (...args: any[]) =>
|
||||
console.error('[ERROR] [MemoryDiscovery]', ...args),
|
||||
debugLogger.error('[ERROR] [MemoryDiscovery]', ...args),
|
||||
};
|
||||
|
||||
export interface GeminiFileContent {
|
||||
|
||||
@@ -232,7 +232,7 @@ export async function retryWithBackoff<T>(
|
||||
continue;
|
||||
}
|
||||
} catch (fallbackError) {
|
||||
console.warn('Model fallback failed:', fallbackError);
|
||||
debugLogger.warn('Model fallback failed:', fallbackError);
|
||||
}
|
||||
}
|
||||
throw classifiedError instanceof RetryableQuotaError
|
||||
@@ -241,7 +241,7 @@ export async function retryWithBackoff<T>(
|
||||
}
|
||||
|
||||
if (classifiedError instanceof RetryableQuotaError) {
|
||||
console.warn(
|
||||
debugLogger.warn(
|
||||
`Attempt ${attempt} failed: ${classifiedError.message}. Retrying after ${classifiedError.retryDelayMs}ms...`,
|
||||
);
|
||||
await delay(classifiedError.retryDelayMs, signal);
|
||||
@@ -300,7 +300,7 @@ function logRetryAttempt(
|
||||
if (errorStatus === 429) {
|
||||
debugLogger.warn(message, error);
|
||||
} else if (errorStatus && errorStatus >= 500 && errorStatus < 600) {
|
||||
console.error(message, error);
|
||||
debugLogger.warn(message, error);
|
||||
} else if (error instanceof Error) {
|
||||
// Fallback for errors that might not have a status but have a message
|
||||
if (error.message.includes('429')) {
|
||||
@@ -309,7 +309,7 @@ function logRetryAttempt(
|
||||
error,
|
||||
);
|
||||
} else if (error.message.match(/5\d{2}/)) {
|
||||
console.error(
|
||||
debugLogger.warn(
|
||||
`Attempt ${attempt} failed with 5xx error. Retrying with backoff...`,
|
||||
error,
|
||||
);
|
||||
|
||||
@@ -19,6 +19,7 @@ import type {
|
||||
ResolvedModelConfig,
|
||||
} from '../services/modelConfigService.js';
|
||||
import { DEFAULT_GEMINI_MODEL } from '../config/models.js';
|
||||
import { debugLogger } from './debugLogger.js';
|
||||
|
||||
// Mock GeminiClient and Config constructor
|
||||
vi.mock('../core/client.js');
|
||||
@@ -58,11 +59,11 @@ describe('summarizers', () => {
|
||||
(mockGeminiClient.generateContent as Mock) = vi.fn();
|
||||
|
||||
vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
vi.spyOn(debugLogger, 'warn').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
(console.error as Mock).mockRestore();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe('summarizeToolOutput', () => {
|
||||
|
||||
Reference in New Issue
Block a user