mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-02 07:54:48 -07:00
Migrate console to coreEvents.emitFeedback or debugLogger (#15219)
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
type HookPolicyDecision,
|
||||
} from './types.js';
|
||||
import { safeJsonStringify } from '../utils/safeJsonStringify.js';
|
||||
import { debugLogger } from '../utils/debugLogger.js';
|
||||
|
||||
export class MessageBus extends EventEmitter {
|
||||
constructor(
|
||||
@@ -45,7 +46,7 @@ export class MessageBus extends EventEmitter {
|
||||
|
||||
async publish(message: Message): Promise<void> {
|
||||
if (this.debug) {
|
||||
console.debug(`[MESSAGE_BUS] publish: ${safeJsonStringify(message)}`);
|
||||
debugLogger.debug(`[MESSAGE_BUS] publish: ${safeJsonStringify(message)}`);
|
||||
}
|
||||
try {
|
||||
if (!this.isValidMessage(message)) {
|
||||
|
||||
@@ -30,7 +30,7 @@ const logger = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
debug: (...args: any[]) => debugLogger.debug('[DEBUG] [IDEClient]', ...args),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
error: (...args: any[]) => console.error('[ERROR] [IDEClient]', ...args),
|
||||
error: (...args: any[]) => debugLogger.error('[ERROR] [IDEClient]', ...args),
|
||||
};
|
||||
|
||||
export type DiffUpdateResult =
|
||||
|
||||
@@ -418,7 +418,10 @@ export class OAuthUtils {
|
||||
return payload.exp * 1000; // Convert seconds to milliseconds
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to parse ID token for expiry time with error:', e);
|
||||
debugLogger.error(
|
||||
'Failed to parse ID token for expiry time with error:',
|
||||
e,
|
||||
);
|
||||
}
|
||||
|
||||
// Return undefined if try block fails or 'exp' is missing/invalid
|
||||
|
||||
@@ -324,7 +324,11 @@ export class KeychainTokenStorage
|
||||
.filter((cred) => cred.account.startsWith(SECRET_PREFIX))
|
||||
.map((cred) => cred.account.substring(SECRET_PREFIX.length));
|
||||
} catch (error) {
|
||||
console.error('Failed to list secrets from keychain:', error);
|
||||
coreEvents.emitFeedback(
|
||||
'error',
|
||||
'Failed to list secrets from keychain',
|
||||
error,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
} from '../confirmation-bus/types.js';
|
||||
import { type MessageBus } from '../confirmation-bus/message-bus.js';
|
||||
import { coreEvents } from '../utils/events.js';
|
||||
import { debugLogger } from '../utils/debugLogger.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
@@ -312,7 +313,7 @@ export function createPolicyUpdater(
|
||||
existingData = toml.parse(fileContent) as { rule?: TomlRule[] };
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
||||
console.warn(
|
||||
debugLogger.warn(
|
||||
`Failed to parse ${policyFile}, overwriting with new policy.`,
|
||||
error,
|
||||
);
|
||||
|
||||
@@ -14,6 +14,14 @@ import type {
|
||||
} from '../routingStrategy.js';
|
||||
import type { Config } from '../../config/config.js';
|
||||
import type { BaseLlmClient } from '../../core/baseLlmClient.js';
|
||||
import { debugLogger } from '../../utils/debugLogger.js';
|
||||
import { coreEvents } from '../../utils/events.js';
|
||||
|
||||
vi.mock('../../utils/debugLogger.js', () => ({
|
||||
debugLogger: {
|
||||
warn: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('CompositeStrategy', () => {
|
||||
let mockContext: RoutingContext;
|
||||
@@ -22,6 +30,7 @@ describe('CompositeStrategy', () => {
|
||||
let mockStrategy1: RoutingStrategy;
|
||||
let mockStrategy2: RoutingStrategy;
|
||||
let mockTerminalStrategy: TerminalStrategy;
|
||||
let emitFeedbackSpy: ReturnType<typeof vi.spyOn>;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
@@ -30,6 +39,8 @@ describe('CompositeStrategy', () => {
|
||||
mockConfig = {} as Config;
|
||||
mockBaseLlmClient = {} as BaseLlmClient;
|
||||
|
||||
emitFeedbackSpy = vi.spyOn(coreEvents, 'emitFeedback');
|
||||
|
||||
mockStrategy1 = {
|
||||
name: 'strategy1',
|
||||
route: vi.fn().mockResolvedValue(null),
|
||||
@@ -112,9 +123,6 @@ describe('CompositeStrategy', () => {
|
||||
});
|
||||
|
||||
it('should handle errors in non-terminal strategies and continue', async () => {
|
||||
const consoleErrorSpy = vi
|
||||
.spyOn(console, 'error')
|
||||
.mockImplementation(() => {});
|
||||
vi.spyOn(mockStrategy1, 'route').mockRejectedValue(
|
||||
new Error('Strategy 1 failed'),
|
||||
);
|
||||
@@ -130,18 +138,14 @@ describe('CompositeStrategy', () => {
|
||||
mockBaseLlmClient,
|
||||
);
|
||||
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
expect(debugLogger.warn).toHaveBeenCalledWith(
|
||||
"[Routing] Strategy 'strategy1' failed. Continuing to next strategy. Error:",
|
||||
expect.any(Error),
|
||||
);
|
||||
expect(result.model).toBe('terminal-model');
|
||||
consoleErrorSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should re-throw an error from the terminal strategy', async () => {
|
||||
const consoleErrorSpy = vi
|
||||
.spyOn(console, 'error')
|
||||
.mockImplementation(() => {});
|
||||
const terminalError = new Error('Terminal strategy failed');
|
||||
vi.spyOn(mockTerminalStrategy, 'route').mockRejectedValue(terminalError);
|
||||
|
||||
@@ -151,11 +155,11 @@ describe('CompositeStrategy', () => {
|
||||
composite.route(mockContext, mockConfig, mockBaseLlmClient),
|
||||
).rejects.toThrow(terminalError);
|
||||
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
expect(emitFeedbackSpy).toHaveBeenCalledWith(
|
||||
'error',
|
||||
"[Routing] Critical Error: Terminal strategy 'terminal' failed. Routing cannot proceed. Error:",
|
||||
terminalError,
|
||||
);
|
||||
consoleErrorSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should correctly finalize the decision metadata', async () => {
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
import type { Config } from '../../config/config.js';
|
||||
import type { BaseLlmClient } from '../../core/baseLlmClient.js';
|
||||
import { debugLogger } from '../../utils/debugLogger.js';
|
||||
import { coreEvents } from '../../utils/events.js';
|
||||
import type {
|
||||
RoutingContext,
|
||||
RoutingDecision,
|
||||
@@ -59,7 +61,7 @@ export class CompositeStrategy implements TerminalStrategy {
|
||||
return this.finalizeDecision(decision, startTime);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
debugLogger.warn(
|
||||
`[Routing] Strategy '${strategy.name}' failed. Continuing to next strategy. Error:`,
|
||||
error,
|
||||
);
|
||||
@@ -76,7 +78,8 @@ export class CompositeStrategy implements TerminalStrategy {
|
||||
|
||||
return this.finalizeDecision(decision, startTime);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
coreEvents.emitFeedback(
|
||||
'error',
|
||||
`[Routing] Critical Error: Terminal strategy '${terminalStrategy.name}' failed. Routing cannot proceed. Error:`,
|
||||
error,
|
||||
);
|
||||
|
||||
@@ -277,7 +277,7 @@ export class ClearcutLogger {
|
||||
this.enqueueHelper(event);
|
||||
} catch (error) {
|
||||
if (this.config?.getDebugMode()) {
|
||||
console.error('ClearcutLogger: Failed to enqueue log event.', error);
|
||||
debugLogger.warn('ClearcutLogger: Failed to enqueue log event.', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,9 +301,7 @@ export class ClearcutLogger {
|
||||
this.enqueueHelper(event);
|
||||
});
|
||||
} catch (error) {
|
||||
if (this.config?.getDebugMode()) {
|
||||
console.error('ClearcutLogger: Failed to enqueue log event.', error);
|
||||
}
|
||||
debugLogger.warn('ClearcutLogger: Failed to enqueue log event.', error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,7 +433,7 @@ export class ClearcutLogger {
|
||||
};
|
||||
} else {
|
||||
if (this.config?.getDebugMode()) {
|
||||
console.error(
|
||||
debugLogger.warn(
|
||||
`Error flushing log events: HTTP ${response.status}: ${response.statusText}`,
|
||||
);
|
||||
}
|
||||
@@ -445,7 +443,7 @@ export class ClearcutLogger {
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
if (this.config?.getDebugMode()) {
|
||||
console.error('Error flushing log events:', e as Error);
|
||||
debugLogger.warn('Error flushing log events:', e as Error);
|
||||
}
|
||||
|
||||
// Re-queue failed events for retry
|
||||
|
||||
@@ -157,7 +157,6 @@ export async function initializeTelemetry(
|
||||
) {
|
||||
const message = `Telemetry credentials have changed (from ${activeTelemetryEmail} to ${credentials.client_email}), but telemetry cannot be re-initialized in this process. Please restart the CLI to use the new account for telemetry.`;
|
||||
debugLogger.error(message);
|
||||
console.error(message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -309,7 +308,7 @@ export async function initializeTelemetry(
|
||||
initializeMetrics(config);
|
||||
void flushTelemetryBuffer();
|
||||
} catch (error) {
|
||||
console.error('Error starting OpenTelemetry SDK:', error);
|
||||
debugLogger.error('Error starting OpenTelemetry SDK:', error);
|
||||
}
|
||||
|
||||
// Note: We don't use process.on('exit') here because that callback is synchronous
|
||||
@@ -343,7 +342,7 @@ export async function flushTelemetry(config: Config): Promise<void> {
|
||||
debugLogger.log('OpenTelemetry SDK flushed successfully.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error flushing SDK:', error);
|
||||
debugLogger.error('Error flushing SDK:', error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,7 +360,7 @@ export async function shutdownTelemetry(
|
||||
debugLogger.log('OpenTelemetry SDK shut down successfully.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error shutting down SDK:', error);
|
||||
debugLogger.error('Error shutting down SDK:', error);
|
||||
} finally {
|
||||
telemetryInitialized = false;
|
||||
sdk = undefined;
|
||||
|
||||
@@ -1159,7 +1159,7 @@ describe('EditTool', () => {
|
||||
result.returnDisplay.diffStat?.model_removed_lines,
|
||||
);
|
||||
} else if (result.error) {
|
||||
console.error(`Edit failed for ${file.path}:`, result.error);
|
||||
throw result.error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -277,9 +277,6 @@ class MemoryToolInvocation extends BaseToolInvocation<
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : String(error);
|
||||
console.warn(
|
||||
`[MemoryTool] Error executing save_memory for fact "${fact}": ${errorMessage}`,
|
||||
);
|
||||
return {
|
||||
llmContent: JSON.stringify({
|
||||
success: false,
|
||||
@@ -367,10 +364,6 @@ export class MemoryTool
|
||||
|
||||
await fsAdapter.writeFile(memoryFilePath, newContent, 'utf-8');
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`[MemoryTool] Error adding memory entry to ${memoryFilePath}:`,
|
||||
error,
|
||||
);
|
||||
throw new Error(
|
||||
`[MemoryTool] Failed to add memory entry: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
|
||||
@@ -729,7 +729,7 @@ describe('SmartEditTool', () => {
|
||||
result.returnDisplay.diffStat?.model_removed_lines,
|
||||
);
|
||||
} else if (result.error) {
|
||||
console.error(`Edit failed for ${file.path}:`, result.error);
|
||||
throw result.error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -416,7 +416,7 @@ export class ToolRegistry {
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`Tool discovery command "${discoveryCmd}" failed:`, e);
|
||||
debugLogger.error(`Tool discovery command "${discoveryCmd}" failed:`, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { ToolErrorType } from './tool-error.js';
|
||||
import { getErrorMessage } from '../utils/errors.js';
|
||||
import { type Config } from '../config/config.js';
|
||||
import { getResponseText } from '../utils/partUtils.js';
|
||||
import { debugLogger } from '../utils/debugLogger.js';
|
||||
|
||||
interface GroundingChunkWeb {
|
||||
uri?: string;
|
||||
@@ -167,7 +168,7 @@ class WebSearchToolInvocation extends BaseToolInvocation<
|
||||
const errorMessage = `Error during web search for query "${
|
||||
this.params.query
|
||||
}": ${getErrorMessage(error)}`;
|
||||
console.error(errorMessage, error);
|
||||
debugLogger.warn(errorMessage, error);
|
||||
return {
|
||||
llmContent: `Error: ${errorMessage}`,
|
||||
returnDisplay: `Error performing web search.`,
|
||||
|
||||
@@ -44,6 +44,7 @@ import { FileOperation } from '../telemetry/metrics.js';
|
||||
import { getSpecificMimeType } from '../utils/fileUtils.js';
|
||||
import { getLanguageFromFilePath } from '../utils/language-detection.js';
|
||||
import type { MessageBus } from '../confirmation-bus/message-bus.js';
|
||||
import { debugLogger } from '../utils/debugLogger.js';
|
||||
|
||||
/**
|
||||
* Parameters for the WriteFile tool
|
||||
@@ -377,7 +378,7 @@ class WriteFileToolInvocation extends BaseToolInvocation<
|
||||
|
||||
// Include stack trace in debug mode for better troubleshooting
|
||||
if (this.config.getDebugMode() && error.stack) {
|
||||
console.error('Write file error stack:', error.stack);
|
||||
debugLogger.error('Write file error stack:', error.stack);
|
||||
}
|
||||
} else if (error instanceof Error) {
|
||||
errorMsg = `Error writing to file: ${error.message}`;
|
||||
|
||||
@@ -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