diff options
Diffstat (limited to 'packages/core/src/telemetry')
| -rw-r--r-- | packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts | 18 | ||||
| -rw-r--r-- | packages/core/src/telemetry/clearcut-logger/event-metadata-key.ts | 12 | ||||
| -rw-r--r-- | packages/core/src/telemetry/metrics.test.ts | 82 | ||||
| -rw-r--r-- | packages/core/src/telemetry/metrics.ts | 8 | ||||
| -rw-r--r-- | packages/core/src/telemetry/types.ts | 20 |
5 files changed, 140 insertions, 0 deletions
diff --git a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts index bff9f28e..09ae6741 100644 --- a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts +++ b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts @@ -476,6 +476,24 @@ export class ClearcutLogger { }, ]; + if (event.metadata) { + const metadataMapping: { [key: string]: EventMetadataKey } = { + ai_added_lines: EventMetadataKey.GEMINI_CLI_AI_ADDED_LINES, + ai_removed_lines: EventMetadataKey.GEMINI_CLI_AI_REMOVED_LINES, + user_added_lines: EventMetadataKey.GEMINI_CLI_USER_ADDED_LINES, + user_removed_lines: EventMetadataKey.GEMINI_CLI_USER_REMOVED_LINES, + }; + + for (const [key, gemini_cli_key] of Object.entries(metadataMapping)) { + if (event.metadata[key] !== undefined) { + data.push({ + gemini_cli_key, + value: JSON.stringify(event.metadata[key]), + }); + } + } + } + const logEvent = this.createLogEvent(tool_call_event_name, data); this.enqueueLogEvent(logEvent); this.flushIfNeeded(); diff --git a/packages/core/src/telemetry/clearcut-logger/event-metadata-key.ts b/packages/core/src/telemetry/clearcut-logger/event-metadata-key.ts index 54f570f1..81f41603 100644 --- a/packages/core/src/telemetry/clearcut-logger/event-metadata-key.ts +++ b/packages/core/src/telemetry/clearcut-logger/event-metadata-key.ts @@ -197,6 +197,18 @@ export enum EventMetadataKey { // Logs the type of the IDE connection. GEMINI_CLI_IDE_CONNECTION_TYPE = 46, + + // Logs AI added lines in edit/write tool response. + GEMINI_CLI_AI_ADDED_LINES = 47, + + // Logs AI removed lines in edit/write tool response. + GEMINI_CLI_AI_REMOVED_LINES = 48, + + // Logs user added lines in edit/write tool response. + GEMINI_CLI_USER_ADDED_LINES = 49, + + // Logs user removed lines in edit/write tool response. + GEMINI_CLI_USER_REMOVED_LINES = 50, } export function getEventMetadataKey( diff --git a/packages/core/src/telemetry/metrics.test.ts b/packages/core/src/telemetry/metrics.test.ts index 4fcdd9e1..7b430884 100644 --- a/packages/core/src/telemetry/metrics.test.ts +++ b/packages/core/src/telemetry/metrics.test.ts @@ -221,5 +221,87 @@ describe('Telemetry Metrics', () => { mimetype: 'application/javascript', }); }); + + it('should include diffStat when provided', () => { + initializeMetricsModule(mockConfig); + mockCounterAddFn.mockClear(); + + const diffStat = { + ai_added_lines: 5, + ai_removed_lines: 2, + user_added_lines: 3, + user_removed_lines: 1, + }; + + recordFileOperationMetricModule( + mockConfig, + FileOperation.UPDATE, + undefined, + undefined, + undefined, + diffStat, + ); + + expect(mockCounterAddFn).toHaveBeenCalledWith(1, { + 'session.id': 'test-session-id', + operation: FileOperation.UPDATE, + ai_added_lines: 5, + ai_removed_lines: 2, + user_added_lines: 3, + user_removed_lines: 1, + }); + }); + + it('should not include diffStat attributes when diffStat is not provided', () => { + initializeMetricsModule(mockConfig); + mockCounterAddFn.mockClear(); + + recordFileOperationMetricModule( + mockConfig, + FileOperation.UPDATE, + 10, + 'text/plain', + 'txt', + undefined, + ); + + expect(mockCounterAddFn).toHaveBeenCalledWith(1, { + 'session.id': 'test-session-id', + operation: FileOperation.UPDATE, + lines: 10, + mimetype: 'text/plain', + extension: 'txt', + }); + }); + + it('should handle diffStat with all zero values', () => { + initializeMetricsModule(mockConfig); + mockCounterAddFn.mockClear(); + + const diffStat = { + ai_added_lines: 0, + ai_removed_lines: 0, + user_added_lines: 0, + user_removed_lines: 0, + }; + + recordFileOperationMetricModule( + mockConfig, + FileOperation.UPDATE, + undefined, + undefined, + undefined, + diffStat, + ); + + expect(mockCounterAddFn).toHaveBeenCalledWith(1, { + 'session.id': 'test-session-id', + operation: FileOperation.UPDATE, + ai_added_lines: 0, + ai_removed_lines: 0, + user_added_lines: 0, + user_removed_lines: 0, + }); + }); }); }); diff --git a/packages/core/src/telemetry/metrics.ts b/packages/core/src/telemetry/metrics.ts index 103f7f71..1e4509da 100644 --- a/packages/core/src/telemetry/metrics.ts +++ b/packages/core/src/telemetry/metrics.ts @@ -23,6 +23,7 @@ import { METRIC_FILE_OPERATION_COUNT, } from './constants.js'; import { Config } from '../config/config.js'; +import { DiffStat } from '../tools/tools.js'; export enum FileOperation { CREATE = 'create', @@ -189,6 +190,7 @@ export function recordFileOperationMetric( lines?: number, mimetype?: string, extension?: string, + diffStat?: DiffStat, ): void { if (!fileOperationCounter || !isMetricsInitialized) return; const attributes: Attributes = { @@ -198,5 +200,11 @@ export function recordFileOperationMetric( if (lines !== undefined) attributes.lines = lines; if (mimetype !== undefined) attributes.mimetype = mimetype; if (extension !== undefined) attributes.extension = extension; + if (diffStat !== undefined) { + attributes.ai_added_lines = diffStat.ai_added_lines; + attributes.ai_removed_lines = diffStat.ai_removed_lines; + attributes.user_added_lines = diffStat.user_added_lines; + attributes.user_removed_lines = diffStat.user_removed_lines; + } fileOperationCounter.add(1, attributes); } diff --git a/packages/core/src/telemetry/types.ts b/packages/core/src/telemetry/types.ts index 766d5f47..edcd8b1b 100644 --- a/packages/core/src/telemetry/types.ts +++ b/packages/core/src/telemetry/types.ts @@ -7,6 +7,7 @@ import { GenerateContentResponseUsageMetadata } from '@google/genai'; import { Config } from '../config/config.js'; import { CompletedToolCall } from '../core/coreToolScheduler.js'; +import { FileDiff } from '../tools/tools.js'; import { AuthType } from '../core/contentGenerator.js'; import { getDecisionFromOutcome, @@ -105,6 +106,8 @@ export class ToolCallEvent { error?: string; error_type?: string; prompt_id: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + metadata?: { [key: string]: any }; constructor(call: CompletedToolCall) { this['event.name'] = 'tool_call'; @@ -119,6 +122,23 @@ export class ToolCallEvent { this.error = call.response.error?.message; this.error_type = call.response.errorType; this.prompt_id = call.request.prompt_id; + + if ( + call.status === 'success' && + typeof call.response.resultDisplay === 'object' && + call.response.resultDisplay !== null && + 'diffStat' in call.response.resultDisplay + ) { + const diffStat = (call.response.resultDisplay as FileDiff).diffStat; + if (diffStat) { + this.metadata = { + ai_added_lines: diffStat.ai_added_lines, + ai_removed_lines: diffStat.ai_removed_lines, + user_added_lines: diffStat.user_added_lines, + user_removed_lines: diffStat.user_removed_lines, + }; + } + } } } |
