mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-10 22:21:22 -07:00
fix(core): improve error type extraction for telemetry (#19565)
Co-authored-by: Yuna Seol <yunaseol@google.com>
This commit is contained in:
@@ -32,6 +32,7 @@ import { LoggingContentGenerator } from './loggingContentGenerator.js';
|
||||
import type { Config } from '../config/config.js';
|
||||
import { UserTierId } from '../code_assist/types.js';
|
||||
import { ApiRequestEvent, LlmRole } from '../telemetry/types.js';
|
||||
import { FatalAuthenticationError } from '../utils/errors.js';
|
||||
|
||||
describe('LoggingContentGenerator', () => {
|
||||
let wrapped: ContentGenerator;
|
||||
@@ -137,6 +138,19 @@ describe('LoggingContentGenerator', () => {
|
||||
const errorEvent = vi.mocked(logApiError).mock.calls[0][1];
|
||||
expect(errorEvent.duration_ms).toBe(1000);
|
||||
});
|
||||
|
||||
describe('error type extraction', () => {
|
||||
it('should extract error type correctly', async () => {
|
||||
const req = { contents: [], model: 'm' };
|
||||
const error = new FatalAuthenticationError('test');
|
||||
vi.mocked(wrapped.generateContent).mockRejectedValue(error);
|
||||
await expect(
|
||||
loggingContentGenerator.generateContent(req, 'id', LlmRole.MAIN),
|
||||
).rejects.toThrow();
|
||||
const errorEvent = vi.mocked(logApiError).mock.calls[0][1];
|
||||
expect(errorEvent.error_type).toBe('FatalAuthenticationError');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('generateContentStream', () => {
|
||||
|
||||
@@ -36,6 +36,7 @@ import { toContents } from '../code_assist/converter.js';
|
||||
import { isStructuredError } from '../utils/quotaErrorDetection.js';
|
||||
import { runInDevTraceSpan, type SpanMetadata } from '../telemetry/trace.js';
|
||||
import { debugLogger } from '../utils/debugLogger.js';
|
||||
import { getErrorType } from '../utils/errors.js';
|
||||
|
||||
interface StructuredError {
|
||||
status: number;
|
||||
@@ -167,7 +168,7 @@ export class LoggingContentGenerator implements ContentGenerator {
|
||||
serverDetails?: ServerDetails,
|
||||
): void {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
const errorType = error instanceof Error ? error.name : 'unknown';
|
||||
const errorType = getErrorType(error);
|
||||
|
||||
logApiError(
|
||||
this.config,
|
||||
|
||||
@@ -12,6 +12,14 @@ import {
|
||||
BadRequestError,
|
||||
ForbiddenError,
|
||||
getErrorMessage,
|
||||
getErrorType,
|
||||
FatalAuthenticationError,
|
||||
FatalCancellationError,
|
||||
FatalInputError,
|
||||
FatalSandboxError,
|
||||
FatalConfigError,
|
||||
FatalTurnLimitedError,
|
||||
FatalToolExecutionError,
|
||||
} from './errors.js';
|
||||
|
||||
describe('getErrorMessage', () => {
|
||||
@@ -201,3 +209,44 @@ describe('toFriendlyError', () => {
|
||||
expect(toFriendlyError(error)).toBe(error);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getErrorType', () => {
|
||||
it('should return error name for standard errors', () => {
|
||||
expect(getErrorType(new Error('test'))).toBe('Error');
|
||||
expect(getErrorType(new TypeError('test'))).toBe('TypeError');
|
||||
expect(getErrorType(new SyntaxError('test'))).toBe('SyntaxError');
|
||||
});
|
||||
|
||||
it('should return constructor name for custom errors', () => {
|
||||
expect(getErrorType(new FatalAuthenticationError('test'))).toBe(
|
||||
'FatalAuthenticationError',
|
||||
);
|
||||
expect(getErrorType(new FatalInputError('test'))).toBe('FatalInputError');
|
||||
expect(getErrorType(new FatalSandboxError('test'))).toBe(
|
||||
'FatalSandboxError',
|
||||
);
|
||||
expect(getErrorType(new FatalConfigError('test'))).toBe('FatalConfigError');
|
||||
expect(getErrorType(new FatalTurnLimitedError('test'))).toBe(
|
||||
'FatalTurnLimitedError',
|
||||
);
|
||||
expect(getErrorType(new FatalToolExecutionError('test'))).toBe(
|
||||
'FatalToolExecutionError',
|
||||
);
|
||||
expect(getErrorType(new FatalCancellationError('test'))).toBe(
|
||||
'FatalCancellationError',
|
||||
);
|
||||
expect(getErrorType(new ForbiddenError('test'))).toBe('ForbiddenError');
|
||||
expect(getErrorType(new UnauthorizedError('test'))).toBe(
|
||||
'UnauthorizedError',
|
||||
);
|
||||
expect(getErrorType(new BadRequestError('test'))).toBe('BadRequestError');
|
||||
});
|
||||
|
||||
it('should return "unknown" for non-Error objects', () => {
|
||||
expect(getErrorType('string error')).toBe('unknown');
|
||||
expect(getErrorType(123)).toBe('unknown');
|
||||
expect(getErrorType({})).toBe('unknown');
|
||||
expect(getErrorType(null)).toBe('unknown');
|
||||
expect(getErrorType(undefined)).toBe('unknown');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,6 +26,15 @@ export function getErrorMessage(error: unknown): string {
|
||||
}
|
||||
}
|
||||
|
||||
export function getErrorType(error: unknown): string {
|
||||
if (!(error instanceof Error)) return 'unknown';
|
||||
|
||||
// Return constructor name if the generic 'Error' name is used (for custom errors)
|
||||
return error.name === 'Error'
|
||||
? (error.constructor?.name ?? 'Error')
|
||||
: error.name;
|
||||
}
|
||||
|
||||
export class FatalError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
|
||||
Reference in New Issue
Block a user