summaryrefslogtreecommitdiff
path: root/packages/core
diff options
context:
space:
mode:
authorowenofbrien <[email protected]>2025-08-19 15:06:00 -0500
committerGitHub <[email protected]>2025-08-19 20:06:00 +0000
commitb561d3bbede8756ec7c3734ed9cb6bb5cbc9ee61 (patch)
tree96b3eec2b9ee781fecc11ca8afa39a0d0d093db3 /packages/core
parentb9cf1ea3ce513717416317fa21b87ce98a107ec6 (diff)
Log all session metadata (#6423)
Diffstat (limited to 'packages/core')
-rw-r--r--packages/core/src/core/coreToolScheduler.test.ts24
-rw-r--r--packages/core/src/core/nonInteractiveToolExecutor.test.ts4
-rw-r--r--packages/core/src/telemetry/clearcut-logger/clearcut-logger.test.ts21
-rw-r--r--packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts164
-rw-r--r--packages/core/src/telemetry/loggers.ts2
5 files changed, 103 insertions, 112 deletions
diff --git a/packages/core/src/core/coreToolScheduler.test.ts b/packages/core/src/core/coreToolScheduler.test.ts
index 5cf49350..e0d7f65a 100644
--- a/packages/core/src/core/coreToolScheduler.test.ts
+++ b/packages/core/src/core/coreToolScheduler.test.ts
@@ -53,6 +53,10 @@ describe('CoreToolScheduler', () => {
getUsageStatisticsEnabled: () => true,
getDebugMode: () => false,
getApprovalMode: () => ApprovalMode.DEFAULT,
+ getContentGeneratorConfig: () => ({
+ model: 'test-model',
+ authType: 'oauth-personal',
+ }),
} as unknown as Config;
const scheduler = new CoreToolScheduler({
@@ -109,6 +113,10 @@ describe('CoreToolScheduler with payload', () => {
getUsageStatisticsEnabled: () => true,
getDebugMode: () => false,
getApprovalMode: () => ApprovalMode.DEFAULT,
+ getContentGeneratorConfig: () => ({
+ model: 'test-model',
+ authType: 'oauth-personal',
+ }),
} as unknown as Config;
const scheduler = new CoreToolScheduler({
@@ -405,6 +413,10 @@ describe('CoreToolScheduler edit cancellation', () => {
getUsageStatisticsEnabled: () => true,
getDebugMode: () => false,
getApprovalMode: () => ApprovalMode.DEFAULT,
+ getContentGeneratorConfig: () => ({
+ model: 'test-model',
+ authType: 'oauth-personal',
+ }),
} as unknown as Config;
const scheduler = new CoreToolScheduler({
@@ -493,6 +505,10 @@ describe('CoreToolScheduler YOLO mode', () => {
getUsageStatisticsEnabled: () => true,
getDebugMode: () => false,
getApprovalMode: () => ApprovalMode.YOLO,
+ getContentGeneratorConfig: () => ({
+ model: 'test-model',
+ authType: 'oauth-personal',
+ }),
} as unknown as Config;
const scheduler = new CoreToolScheduler({
@@ -578,6 +594,10 @@ describe('CoreToolScheduler request queueing', () => {
getUsageStatisticsEnabled: () => true,
getDebugMode: () => false,
getApprovalMode: () => ApprovalMode.YOLO, // Use YOLO to avoid confirmation prompts
+ getContentGeneratorConfig: () => ({
+ model: 'test-model',
+ authType: 'oauth-personal',
+ }),
} as unknown as Config;
const scheduler = new CoreToolScheduler({
@@ -687,6 +707,10 @@ describe('CoreToolScheduler request queueing', () => {
getUsageStatisticsEnabled: () => true,
getDebugMode: () => false,
getApprovalMode: () => ApprovalMode.YOLO,
+ getContentGeneratorConfig: () => ({
+ model: 'test-model',
+ authType: 'oauth-personal',
+ }),
} as unknown as Config;
const scheduler = new CoreToolScheduler({
diff --git a/packages/core/src/core/nonInteractiveToolExecutor.test.ts b/packages/core/src/core/nonInteractiveToolExecutor.test.ts
index 484d2e45..0c1164ea 100644
--- a/packages/core/src/core/nonInteractiveToolExecutor.test.ts
+++ b/packages/core/src/core/nonInteractiveToolExecutor.test.ts
@@ -20,6 +20,10 @@ const mockConfig = {
getSessionId: () => 'test-session-id',
getUsageStatisticsEnabled: () => true,
getDebugMode: () => false,
+ getContentGeneratorConfig: () => ({
+ model: 'test-model',
+ authType: 'oauth-personal',
+ }),
} as unknown as Config;
describe('executeToolCall', () => {
diff --git a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.test.ts b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.test.ts
index 39596616..2777f196 100644
--- a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.test.ts
+++ b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.test.ts
@@ -181,12 +181,25 @@ describe('ClearcutLogger', () => {
const event = logger?.createLogEvent(EventNames.API_ERROR, []);
- expect(event?.event_metadata[0][0]).toEqual({
+ expect(event?.event_metadata[0]).toContainEqual({
gemini_cli_key: EventMetadataKey.GEMINI_CLI_GOOGLE_ACCOUNTS_COUNT,
value: '9001',
});
});
+ it('logs the current surface from a github action', () => {
+ const { logger } = setup({});
+
+ vi.stubEnv('GITHUB_SHA', '8675309');
+
+ const event = logger?.createLogEvent(EventNames.CHAT_COMPRESSION, []);
+
+ expect(event?.event_metadata[0]).toContainEqual({
+ gemini_cli_key: EventMetadataKey.GEMINI_CLI_SURFACE,
+ value: 'GitHub',
+ });
+ });
+
it('logs the current surface', () => {
const { logger } = setup({});
@@ -195,7 +208,7 @@ describe('ClearcutLogger', () => {
const event = logger?.createLogEvent(EventNames.API_ERROR, []);
- expect(event?.event_metadata[0][1]).toEqual({
+ expect(event?.event_metadata[0]).toContainEqual({
gemini_cli_key: EventMetadataKey.GEMINI_CLI_SURFACE,
value: 'ide-1234',
});
@@ -238,7 +251,7 @@ describe('ClearcutLogger', () => {
expectedValue: 'cloudshell',
},
])(
- 'logs the current surface for as $expectedValue, preempting vscode detection',
+ 'logs the current surface as $expectedValue, preempting vscode detection',
({ env, expectedValue }) => {
const { logger } = setup({});
for (const [key, value] of Object.entries(env)) {
@@ -246,7 +259,7 @@ describe('ClearcutLogger', () => {
}
vi.stubEnv('TERM_PROGRAM', 'vscode');
const event = logger?.createLogEvent(EventNames.API_ERROR, []);
- expect(event?.event_metadata[0][1]).toEqual({
+ expect(event?.event_metadata[0][3]).toEqual({
gemini_cli_key: EventMetadataKey.GEMINI_CLI_SURFACE,
value: expectedValue,
});
diff --git a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts
index 7777fa24..7369bc1b 100644
--- a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts
+++ b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts
@@ -7,13 +7,11 @@
import { HttpsProxyAgent } from 'https-proxy-agent';
import {
StartSessionEvent,
- EndSessionEvent,
UserPromptEvent,
ToolCallEvent,
ApiRequestEvent,
ApiResponseEvent,
ApiErrorEvent,
- FlashFallbackEvent,
LoopDetectedEvent,
NextSpeakerCheckEvent,
SlashCommandEvent,
@@ -129,6 +127,8 @@ const MAX_RETRY_EVENTS = 100;
export class ClearcutLogger {
private static instance: ClearcutLogger;
private config?: Config;
+ private sessionData: EventValue[] = [];
+ private promptId: string = '';
/**
* Queue of pending events that need to be flushed to the server. New events
@@ -155,6 +155,7 @@ export class ClearcutLogger {
private constructor(config?: Config) {
this.config = config;
this.events = new FixedDeque<LogEventEntry[]>(Array, MAX_EVENTS);
+ this.promptId = config?.getSessionId() ?? '';
}
static getInstance(config?: Config): ClearcutLogger | undefined {
@@ -203,7 +204,10 @@ export class ClearcutLogger {
createLogEvent(eventName: EventNames, data: EventValue[] = []): LogEvent {
const email = getCachedGoogleAccount();
- data = addDefaultFields(data);
+ if (eventName !== EventNames.START_SESSION) {
+ data.push(...this.sessionData);
+ }
+ data = this.addDefaultFields(data);
const logEvent: LogEvent = {
console_type: 'GEMINI_CLI',
@@ -320,10 +324,6 @@ export class ClearcutLogger {
value: event.model,
},
{
- gemini_cli_key: EventMetadataKey.GEMINI_CLI_SESSION_ID,
- value: this.config?.getSessionId() ?? '',
- },
- {
gemini_cli_key:
EventMetadataKey.GEMINI_CLI_START_SESSION_EMBEDDING_MODEL,
value: event.embedding_model,
@@ -379,15 +379,8 @@ export class ClearcutLogger {
EventMetadataKey.GEMINI_CLI_START_SESSION_TELEMETRY_LOG_USER_PROMPTS_ENABLED,
value: event.telemetry_log_user_prompts_enabled.toString(),
},
- {
- gemini_cli_key: EventMetadataKey.GEMINI_CLI_VERSION,
- value: CLI_VERSION,
- },
- {
- gemini_cli_key: EventMetadataKey.GEMINI_CLI_GIT_COMMIT_HASH,
- value: GIT_COMMIT_INFO,
- },
];
+ this.sessionData = data;
// Flush start event immediately
this.enqueueLogEvent(this.createLogEvent(EventNames.START_SESSION, data));
@@ -397,23 +390,12 @@ export class ClearcutLogger {
}
logNewPromptEvent(event: UserPromptEvent): void {
+ this.promptId = event.prompt_id;
const data: EventValue[] = [
{
gemini_cli_key: EventMetadataKey.GEMINI_CLI_USER_PROMPT_LENGTH,
value: JSON.stringify(event.prompt_length),
},
- {
- gemini_cli_key: EventMetadataKey.GEMINI_CLI_SESSION_ID,
- value: this.config?.getSessionId() ?? '',
- },
- {
- gemini_cli_key: EventMetadataKey.GEMINI_CLI_PROMPT_ID,
- value: JSON.stringify(event.prompt_id),
- },
- {
- gemini_cli_key: EventMetadataKey.GEMINI_CLI_AUTH_TYPE,
- value: JSON.stringify(event.auth_type),
- },
];
this.enqueueLogEvent(this.createLogEvent(EventNames.NEW_PROMPT, data));
@@ -427,10 +409,6 @@ export class ClearcutLogger {
value: JSON.stringify(event.function_name),
},
{
- gemini_cli_key: EventMetadataKey.GEMINI_CLI_PROMPT_ID,
- value: JSON.stringify(event.prompt_id),
- },
- {
gemini_cli_key: EventMetadataKey.GEMINI_CLI_TOOL_CALL_DECISION,
value: JSON.stringify(event.decision),
},
@@ -485,10 +463,6 @@ export class ClearcutLogger {
gemini_cli_key: EventMetadataKey.GEMINI_CLI_API_REQUEST_MODEL,
value: JSON.stringify(event.model),
},
- {
- gemini_cli_key: EventMetadataKey.GEMINI_CLI_PROMPT_ID,
- value: JSON.stringify(event.prompt_id),
- },
];
this.enqueueLogEvent(this.createLogEvent(EventNames.API_REQUEST, data));
@@ -502,10 +476,6 @@ export class ClearcutLogger {
value: JSON.stringify(event.model),
},
{
- gemini_cli_key: EventMetadataKey.GEMINI_CLI_PROMPT_ID,
- value: JSON.stringify(event.prompt_id),
- },
- {
gemini_cli_key: EventMetadataKey.GEMINI_CLI_API_RESPONSE_STATUS_CODE,
value: JSON.stringify(event.status_code),
},
@@ -542,10 +512,6 @@ export class ClearcutLogger {
EventMetadataKey.GEMINI_CLI_API_RESPONSE_TOOL_TOKEN_COUNT,
value: JSON.stringify(event.tool_token_count),
},
- {
- gemini_cli_key: EventMetadataKey.GEMINI_CLI_AUTH_TYPE,
- value: JSON.stringify(event.auth_type),
- },
];
this.enqueueLogEvent(this.createLogEvent(EventNames.API_RESPONSE, data));
@@ -559,10 +525,6 @@ export class ClearcutLogger {
value: JSON.stringify(event.model),
},
{
- gemini_cli_key: EventMetadataKey.GEMINI_CLI_PROMPT_ID,
- value: JSON.stringify(event.prompt_id),
- },
- {
gemini_cli_key: EventMetadataKey.GEMINI_CLI_API_ERROR_TYPE,
value: JSON.stringify(event.error_type),
},
@@ -574,10 +536,6 @@ export class ClearcutLogger {
gemini_cli_key: EventMetadataKey.GEMINI_CLI_API_ERROR_DURATION_MS,
value: JSON.stringify(event.duration_ms),
},
- {
- gemini_cli_key: EventMetadataKey.GEMINI_CLI_AUTH_TYPE,
- value: JSON.stringify(event.auth_type),
- },
];
this.enqueueLogEvent(this.createLogEvent(EventNames.API_ERROR, data));
@@ -601,19 +559,8 @@ export class ClearcutLogger {
);
}
- logFlashFallbackEvent(event: FlashFallbackEvent): void {
- const data: EventValue[] = [
- {
- gemini_cli_key: EventMetadataKey.GEMINI_CLI_AUTH_TYPE,
- value: JSON.stringify(event.auth_type),
- },
- {
- gemini_cli_key: EventMetadataKey.GEMINI_CLI_SESSION_ID,
- value: this.config?.getSessionId() ?? '',
- },
- ];
-
- this.enqueueLogEvent(this.createLogEvent(EventNames.FLASH_FALLBACK, data));
+ logFlashFallbackEvent(): void {
+ this.enqueueLogEvent(this.createLogEvent(EventNames.FLASH_FALLBACK, []));
this.flushToClearcut().catch((error) => {
console.debug('Error flushing to Clearcut:', error);
});
@@ -622,10 +569,6 @@ export class ClearcutLogger {
logLoopDetectedEvent(event: LoopDetectedEvent): void {
const data: EventValue[] = [
{
- gemini_cli_key: EventMetadataKey.GEMINI_CLI_PROMPT_ID,
- value: JSON.stringify(event.prompt_id),
- },
- {
gemini_cli_key: EventMetadataKey.GEMINI_CLI_LOOP_DETECTED_TYPE,
value: JSON.stringify(event.loop_type),
},
@@ -638,10 +581,6 @@ export class ClearcutLogger {
logNextSpeakerCheck(event: NextSpeakerCheckEvent): void {
const data: EventValue[] = [
{
- gemini_cli_key: EventMetadataKey.GEMINI_CLI_PROMPT_ID,
- value: JSON.stringify(event.prompt_id),
- },
- {
gemini_cli_key: EventMetadataKey.GEMINI_CLI_RESPONSE_FINISH_REASON,
value: JSON.stringify(event.finish_reason),
},
@@ -649,10 +588,6 @@ export class ClearcutLogger {
gemini_cli_key: EventMetadataKey.GEMINI_CLI_NEXT_SPEAKER_CHECK_RESULT,
value: JSON.stringify(event.result),
},
- {
- gemini_cli_key: EventMetadataKey.GEMINI_CLI_SESSION_ID,
- value: this.config?.getSessionId() ?? '',
- },
];
this.enqueueLogEvent(
@@ -732,21 +667,57 @@ export class ClearcutLogger {
this.flushIfNeeded();
}
- logEndSessionEvent(event: EndSessionEvent): void {
- const data: EventValue[] = [
- {
- gemini_cli_key: EventMetadataKey.GEMINI_CLI_SESSION_ID,
- value: event?.session_id?.toString() ?? '',
- },
- ];
-
+ logEndSessionEvent(): void {
// Flush immediately on session end.
- this.enqueueLogEvent(this.createLogEvent(EventNames.END_SESSION, data));
+ this.enqueueLogEvent(this.createLogEvent(EventNames.END_SESSION, []));
this.flushToClearcut().catch((error) => {
console.debug('Error flushing to Clearcut:', error);
});
}
+ /**
+ * Adds default fields to data, and returns a new data array. This fields
+ * should exist on all log events.
+ */
+ addDefaultFields(data: EventValue[]): EventValue[] {
+ const totalAccounts = getLifetimeGoogleAccounts();
+ const surface = determineSurface();
+
+ const defaultLogMetadata: EventValue[] = [
+ {
+ gemini_cli_key: EventMetadataKey.GEMINI_CLI_SESSION_ID,
+ value: this.config?.getSessionId() ?? '',
+ },
+ {
+ gemini_cli_key: EventMetadataKey.GEMINI_CLI_AUTH_TYPE,
+ value: JSON.stringify(
+ this.config?.getContentGeneratorConfig()?.authType,
+ ),
+ },
+ {
+ gemini_cli_key: EventMetadataKey.GEMINI_CLI_GOOGLE_ACCOUNTS_COUNT,
+ value: `${totalAccounts}`,
+ },
+ {
+ gemini_cli_key: EventMetadataKey.GEMINI_CLI_SURFACE,
+ value: surface,
+ },
+ {
+ gemini_cli_key: EventMetadataKey.GEMINI_CLI_VERSION,
+ value: CLI_VERSION,
+ },
+ {
+ gemini_cli_key: EventMetadataKey.GEMINI_CLI_GIT_COMMIT_HASH,
+ value: GIT_COMMIT_INFO,
+ },
+ {
+ gemini_cli_key: EventMetadataKey.GEMINI_CLI_PROMPT_ID,
+ value: this.promptId,
+ },
+ ];
+ return [...data, ...defaultLogMetadata];
+ }
+
getProxyAgent() {
const proxyUrl = this.config?.getProxy();
if (!proxyUrl) return undefined;
@@ -760,8 +731,7 @@ export class ClearcutLogger {
}
shutdown() {
- const event = new EndSessionEvent(this.config);
- this.logEndSessionEvent(event);
+ this.logEndSessionEvent();
}
private requeueFailedEvents(eventsToSend: LogEventEntry[][]): void {
@@ -815,26 +785,6 @@ export class ClearcutLogger {
}
}
-/**
- * Adds default fields to data, and returns a new data array. This fields
- * should exist on all log events.
- */
-function addDefaultFields(data: EventValue[]): EventValue[] {
- const totalAccounts = getLifetimeGoogleAccounts();
- const surface = determineSurface();
- const defaultLogMetadata: EventValue[] = [
- {
- gemini_cli_key: EventMetadataKey.GEMINI_CLI_GOOGLE_ACCOUNTS_COUNT,
- value: `${totalAccounts}`,
- },
- {
- gemini_cli_key: EventMetadataKey.GEMINI_CLI_SURFACE,
- value: surface,
- },
- ];
- return [...data, ...defaultLogMetadata];
-}
-
export const TEST_ONLY = {
MAX_RETRY_EVENTS,
MAX_EVENTS,
diff --git a/packages/core/src/telemetry/loggers.ts b/packages/core/src/telemetry/loggers.ts
index 6504c0e7..9b9faf79 100644
--- a/packages/core/src/telemetry/loggers.ts
+++ b/packages/core/src/telemetry/loggers.ts
@@ -175,7 +175,7 @@ export function logFlashFallback(
config: Config,
event: FlashFallbackEvent,
): void {
- ClearcutLogger.getInstance(config)?.logFlashFallbackEvent(event);
+ ClearcutLogger.getInstance(config)?.logFlashFallbackEvent();
if (!isTelemetrySdkInitialized()) return;
const attributes: LogAttributes = {