mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
add(OTel): Add OTel logging for MalformedJsonEvent (#6912)
Co-authored-by: Shnatu <snatu@google.com> Co-authored-by: cornmander <shikhman@google.com>
This commit is contained in:
@@ -220,6 +220,10 @@ Logs are timestamped records of specific events. The following events are logged
|
|||||||
- `response_text` (if applicable)
|
- `response_text` (if applicable)
|
||||||
- `auth_type`
|
- `auth_type`
|
||||||
|
|
||||||
|
- `gemini_cli.malformed_json_response`: This event occurs when a `generateJson` response from Gemini API cannot be parsed as a json.
|
||||||
|
- **Attributes**:
|
||||||
|
- `model`
|
||||||
|
|
||||||
- `gemini_cli.flash_fallback`: This event occurs when Gemini CLI switches to flash as fallback.
|
- `gemini_cli.flash_fallback`: This event occurs when Gemini CLI switches to flash as fallback.
|
||||||
- **Attributes**:
|
- **Attributes**:
|
||||||
- `auth_type`
|
- `auth_type`
|
||||||
|
|||||||
@@ -46,13 +46,13 @@ import { ideContext } from '../ide/ideContext.js';
|
|||||||
import {
|
import {
|
||||||
logChatCompression,
|
logChatCompression,
|
||||||
logNextSpeakerCheck,
|
logNextSpeakerCheck,
|
||||||
|
logMalformedJsonResponse,
|
||||||
} from '../telemetry/loggers.js';
|
} from '../telemetry/loggers.js';
|
||||||
import {
|
import {
|
||||||
makeChatCompressionEvent,
|
makeChatCompressionEvent,
|
||||||
MalformedJsonResponseEvent,
|
MalformedJsonResponseEvent,
|
||||||
NextSpeakerCheckEvent,
|
NextSpeakerCheckEvent,
|
||||||
} from '../telemetry/types.js';
|
} from '../telemetry/types.js';
|
||||||
import { ClearcutLogger } from '../telemetry/clearcut-logger/clearcut-logger.js';
|
|
||||||
import { IdeContext, File } from '../ide/ideContext.js';
|
import { IdeContext, File } from '../ide/ideContext.js';
|
||||||
|
|
||||||
function isThinkingSupported(model: string) {
|
function isThinkingSupported(model: string) {
|
||||||
@@ -617,7 +617,8 @@ export class GeminiClient {
|
|||||||
const prefix = '```json';
|
const prefix = '```json';
|
||||||
const suffix = '```';
|
const suffix = '```';
|
||||||
if (text.startsWith(prefix) && text.endsWith(suffix)) {
|
if (text.startsWith(prefix) && text.endsWith(suffix)) {
|
||||||
ClearcutLogger.getInstance(this.config)?.logMalformedJsonResponseEvent(
|
logMalformedJsonResponse(
|
||||||
|
this.config,
|
||||||
new MalformedJsonResponseEvent(modelToUse),
|
new MalformedJsonResponseEvent(modelToUse),
|
||||||
);
|
);
|
||||||
text = text
|
text = text
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ export const EVENT_NEXT_SPEAKER_CHECK = 'gemini_cli.next_speaker_check';
|
|||||||
export const EVENT_SLASH_COMMAND = 'gemini_cli.slash_command';
|
export const EVENT_SLASH_COMMAND = 'gemini_cli.slash_command';
|
||||||
export const EVENT_IDE_CONNECTION = 'gemini_cli.ide_connection';
|
export const EVENT_IDE_CONNECTION = 'gemini_cli.ide_connection';
|
||||||
export const EVENT_CHAT_COMPRESSION = 'gemini_cli.chat_compression';
|
export const EVENT_CHAT_COMPRESSION = 'gemini_cli.chat_compression';
|
||||||
|
export const EVENT_MALFORMED_JSON_RESPONSE =
|
||||||
|
'gemini_cli.malformed_json_response';
|
||||||
export const METRIC_TOOL_CALL_COUNT = 'gemini_cli.tool.call.count';
|
export const METRIC_TOOL_CALL_COUNT = 'gemini_cli.tool.call.count';
|
||||||
export const METRIC_TOOL_CALL_LATENCY = 'gemini_cli.tool.call.latency';
|
export const METRIC_TOOL_CALL_LATENCY = 'gemini_cli.tool.call.latency';
|
||||||
export const METRIC_API_REQUEST_COUNT = 'gemini_cli.api.request.count';
|
export const METRIC_API_REQUEST_COUNT = 'gemini_cli.api.request.count';
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
EVENT_TOOL_CALL,
|
EVENT_TOOL_CALL,
|
||||||
EVENT_USER_PROMPT,
|
EVENT_USER_PROMPT,
|
||||||
EVENT_FLASH_FALLBACK,
|
EVENT_FLASH_FALLBACK,
|
||||||
|
EVENT_MALFORMED_JSON_RESPONSE,
|
||||||
} from './constants.js';
|
} from './constants.js';
|
||||||
import {
|
import {
|
||||||
logApiRequest,
|
logApiRequest,
|
||||||
@@ -35,6 +36,7 @@ import {
|
|||||||
logToolCall,
|
logToolCall,
|
||||||
logFlashFallback,
|
logFlashFallback,
|
||||||
logChatCompression,
|
logChatCompression,
|
||||||
|
logMalformedJsonResponse,
|
||||||
} from './loggers.js';
|
} from './loggers.js';
|
||||||
import { ToolCallDecision } from './tool-call-decision.js';
|
import { ToolCallDecision } from './tool-call-decision.js';
|
||||||
import {
|
import {
|
||||||
@@ -44,6 +46,7 @@ import {
|
|||||||
ToolCallEvent,
|
ToolCallEvent,
|
||||||
UserPromptEvent,
|
UserPromptEvent,
|
||||||
FlashFallbackEvent,
|
FlashFallbackEvent,
|
||||||
|
MalformedJsonResponseEvent,
|
||||||
makeChatCompressionEvent,
|
makeChatCompressionEvent,
|
||||||
} from './types.js';
|
} from './types.js';
|
||||||
import * as metrics from './metrics.js';
|
import * as metrics from './metrics.js';
|
||||||
@@ -818,4 +821,31 @@ describe('loggers', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('logMalformedJsonResponse', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.spyOn(ClearcutLogger.prototype, 'logMalformedJsonResponseEvent');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('logs the event to Clearcut and OTEL', () => {
|
||||||
|
const mockConfig = makeFakeConfig();
|
||||||
|
const event = new MalformedJsonResponseEvent('test-model');
|
||||||
|
|
||||||
|
logMalformedJsonResponse(mockConfig, event);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
ClearcutLogger.prototype.logMalformedJsonResponseEvent,
|
||||||
|
).toHaveBeenCalledWith(event);
|
||||||
|
|
||||||
|
expect(mockLogger.emit).toHaveBeenCalledWith({
|
||||||
|
body: 'Malformed JSON response from test-model.',
|
||||||
|
attributes: {
|
||||||
|
'session.id': 'test-session-id',
|
||||||
|
'event.name': EVENT_MALFORMED_JSON_RESPONSE,
|
||||||
|
'event.timestamp': '2025-01-01T00:00:00.000Z',
|
||||||
|
model: 'test-model',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
SERVICE_NAME,
|
SERVICE_NAME,
|
||||||
EVENT_SLASH_COMMAND,
|
EVENT_SLASH_COMMAND,
|
||||||
EVENT_CHAT_COMPRESSION,
|
EVENT_CHAT_COMPRESSION,
|
||||||
|
EVENT_MALFORMED_JSON_RESPONSE,
|
||||||
} from './constants.js';
|
} from './constants.js';
|
||||||
import {
|
import {
|
||||||
ApiErrorEvent,
|
ApiErrorEvent,
|
||||||
@@ -36,6 +37,7 @@ import {
|
|||||||
SlashCommandEvent,
|
SlashCommandEvent,
|
||||||
KittySequenceOverflowEvent,
|
KittySequenceOverflowEvent,
|
||||||
ChatCompressionEvent,
|
ChatCompressionEvent,
|
||||||
|
MalformedJsonResponseEvent,
|
||||||
} from './types.js';
|
} from './types.js';
|
||||||
import {
|
import {
|
||||||
recordApiErrorMetrics,
|
recordApiErrorMetrics,
|
||||||
@@ -449,3 +451,24 @@ export function logKittySequenceOverflow(
|
|||||||
};
|
};
|
||||||
logger.emit(logRecord);
|
logger.emit(logRecord);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function logMalformedJsonResponse(
|
||||||
|
config: Config,
|
||||||
|
event: MalformedJsonResponseEvent,
|
||||||
|
): void {
|
||||||
|
ClearcutLogger.getInstance(config)?.logMalformedJsonResponseEvent(event);
|
||||||
|
if (!isTelemetrySdkInitialized()) return;
|
||||||
|
|
||||||
|
const attributes: LogAttributes = {
|
||||||
|
...getCommonAttributes(config),
|
||||||
|
...event,
|
||||||
|
'event.name': EVENT_MALFORMED_JSON_RESPONSE,
|
||||||
|
};
|
||||||
|
|
||||||
|
const logger = logs.getLogger(SERVICE_NAME);
|
||||||
|
const logRecord: LogRecord = {
|
||||||
|
body: `Malformed JSON response from ${event.model}.`,
|
||||||
|
attributes,
|
||||||
|
};
|
||||||
|
logger.emit(logRecord);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user