mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-12 15:10:59 -07:00
feat(telemetry): migrate Google Auth events to event-based logging and fix token storage metric
- Add GoogleAuthStartEvent and GoogleAuthEndEvent types - Implement logGoogleAuthStart and logGoogleAuthEnd in telemetry loggers - Update ClearcutLogger to support Google Auth events - Update OAuth2 logic to use the new logging functions - Fix missing 'type' attribute in recordTokenStorageInitialization metric
This commit is contained in:
@@ -26,10 +26,7 @@ import {
|
||||
clearOauthClientCache,
|
||||
authEvents,
|
||||
} from './oauth2.js';
|
||||
import {
|
||||
recordGoogleAuthStart,
|
||||
recordGoogleAuthEnd,
|
||||
} from '../telemetry/metrics.js';
|
||||
import { logGoogleAuthStart, logGoogleAuthEnd } from '../telemetry/loggers.js';
|
||||
import { UserAccountManager } from '../utils/userAccountManager.js';
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
@@ -109,9 +106,9 @@ vi.mock('../mcp/token-storage/hybrid-token-storage.js', () => ({
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock('../telemetry/metrics.js', () => ({
|
||||
recordGoogleAuthStart: vi.fn(),
|
||||
recordGoogleAuthEnd: vi.fn(),
|
||||
vi.mock('../telemetry/loggers.js', () => ({
|
||||
logGoogleAuthStart: vi.fn(),
|
||||
logGoogleAuthEnd: vi.fn(),
|
||||
}));
|
||||
|
||||
const mockConfig = {
|
||||
@@ -1415,8 +1412,14 @@ describe('oauth2', () => {
|
||||
|
||||
await getOauthClient(AuthType.LOGIN_WITH_GOOGLE, mockConfig);
|
||||
|
||||
expect(recordGoogleAuthStart).toHaveBeenCalledWith(mockConfig);
|
||||
expect(recordGoogleAuthEnd).toHaveBeenCalledWith(mockConfig);
|
||||
expect(logGoogleAuthStart).toHaveBeenCalledWith(
|
||||
mockConfig,
|
||||
expect.any(Object),
|
||||
);
|
||||
expect(logGoogleAuthEnd).toHaveBeenCalledWith(
|
||||
mockConfig,
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it('should NOT record onboarding events for other auth types', async () => {
|
||||
|
||||
@@ -21,10 +21,11 @@ import { EventEmitter } from 'node:events';
|
||||
import open from 'open';
|
||||
import path from 'node:path';
|
||||
import { promises as fs } from 'node:fs';
|
||||
import { logGoogleAuthStart, logGoogleAuthEnd } from '../telemetry/loggers.js';
|
||||
import {
|
||||
recordGoogleAuthStart,
|
||||
recordGoogleAuthEnd,
|
||||
} from '../telemetry/metrics.js';
|
||||
GoogleAuthStartEvent,
|
||||
GoogleAuthEndEvent,
|
||||
} from '../telemetry/types.js';
|
||||
import type { Config } from '../config/config.js';
|
||||
import {
|
||||
getErrorMessage,
|
||||
@@ -117,9 +118,9 @@ async function initOauthClient(
|
||||
authType: AuthType,
|
||||
config: Config,
|
||||
): Promise<AuthClient> {
|
||||
const recordGoogleAuthEndIfApplicable = () => {
|
||||
const logGoogleAuthEndIfApplicable = () => {
|
||||
if (authType === AuthType.LOGIN_WITH_GOOGLE) {
|
||||
recordGoogleAuthEnd(config);
|
||||
logGoogleAuthEnd(config, new GoogleAuthEndEvent());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -154,7 +155,7 @@ async function initOauthClient(
|
||||
});
|
||||
|
||||
if (authType === AuthType.LOGIN_WITH_GOOGLE) {
|
||||
recordGoogleAuthStart(config);
|
||||
logGoogleAuthStart(config, new GoogleAuthStartEvent());
|
||||
}
|
||||
|
||||
const useEncryptedStorage = getUseEncryptedStorageFlag();
|
||||
@@ -203,7 +204,7 @@ async function initOauthClient(
|
||||
debugLogger.log('Loaded cached credentials.');
|
||||
await triggerPostAuthCallbacks(credentials as Credentials);
|
||||
|
||||
recordGoogleAuthEndIfApplicable();
|
||||
logGoogleAuthEndIfApplicable();
|
||||
return client;
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -295,7 +296,7 @@ async function initOauthClient(
|
||||
}
|
||||
|
||||
await triggerPostAuthCallbacks(client.credentials);
|
||||
recordGoogleAuthEndIfApplicable();
|
||||
logGoogleAuthEndIfApplicable();
|
||||
} else {
|
||||
// In ACP mode, we skip the interactive consent and directly open the browser
|
||||
if (!config.getAcpMode()) {
|
||||
@@ -402,7 +403,7 @@ async function initOauthClient(
|
||||
});
|
||||
|
||||
await triggerPostAuthCallbacks(client.credentials);
|
||||
recordGoogleAuthEndIfApplicable();
|
||||
logGoogleAuthEndIfApplicable();
|
||||
}
|
||||
|
||||
return client;
|
||||
|
||||
@@ -50,6 +50,8 @@ import type {
|
||||
KeychainAvailabilityEvent,
|
||||
TokenStorageInitializationEvent,
|
||||
StartupStatsEvent,
|
||||
GoogleAuthStartEvent,
|
||||
GoogleAuthEndEvent,
|
||||
} from '../types.js';
|
||||
import { EventMetadataKey } from './event-metadata-key.js';
|
||||
import type { Config } from '../../config/config.js';
|
||||
@@ -116,6 +118,8 @@ export enum EventNames {
|
||||
TOOL_OUTPUT_MASKING = 'tool_output_masking',
|
||||
KEYCHAIN_AVAILABILITY = 'keychain_availability',
|
||||
TOKEN_STORAGE_INITIALIZATION = 'token_storage_initialization',
|
||||
GOOGLE_AUTH_START = 'google_auth_start',
|
||||
GOOGLE_AUTH_END = 'google_auth_end',
|
||||
CONSECA_POLICY_GENERATION = 'conseca_policy_generation',
|
||||
CONSECA_VERDICT = 'conseca_verdict',
|
||||
STARTUP_STATS = 'startup_stats',
|
||||
@@ -1693,6 +1697,16 @@ export class ClearcutLogger {
|
||||
this.flushIfNeeded();
|
||||
}
|
||||
|
||||
logGoogleAuthStartEvent(_event: GoogleAuthStartEvent): void {
|
||||
this.enqueueLogEvent(this.createLogEvent(EventNames.GOOGLE_AUTH_START, []));
|
||||
this.flushIfNeeded();
|
||||
}
|
||||
|
||||
logGoogleAuthEndEvent(_event: GoogleAuthEndEvent): void {
|
||||
this.enqueueLogEvent(this.createLogEvent(EventNames.GOOGLE_AUTH_END, []));
|
||||
this.flushIfNeeded();
|
||||
}
|
||||
|
||||
logStartupStatsEvent(event: StartupStatsEvent): void {
|
||||
const data: EventValue[] = [
|
||||
{
|
||||
|
||||
@@ -56,6 +56,8 @@ import {
|
||||
type ToolOutputMaskingEvent,
|
||||
type KeychainAvailabilityEvent,
|
||||
type TokenStorageInitializationEvent,
|
||||
type GoogleAuthStartEvent,
|
||||
type GoogleAuthEndEvent,
|
||||
} from './types.js';
|
||||
import {
|
||||
recordApiErrorMetrics,
|
||||
@@ -77,6 +79,8 @@ import {
|
||||
recordKeychainAvailability,
|
||||
recordTokenStorageInitialization,
|
||||
recordInvalidChunk,
|
||||
recordGoogleAuthStart,
|
||||
recordGoogleAuthEnd,
|
||||
} from './metrics.js';
|
||||
import { bufferTelemetryEvent } from './sdk.js';
|
||||
import { uiTelemetryService, type UiEvent } from './uiTelemetry.js';
|
||||
@@ -844,6 +848,40 @@ export function logTokenStorageInitialization(
|
||||
});
|
||||
}
|
||||
|
||||
export function logGoogleAuthStart(
|
||||
config: Config,
|
||||
event: GoogleAuthStartEvent,
|
||||
): void {
|
||||
ClearcutLogger.getInstance(config)?.logGoogleAuthStartEvent(event);
|
||||
bufferTelemetryEvent(() => {
|
||||
const logger = logs.getLogger(SERVICE_NAME);
|
||||
const logRecord: LogRecord = {
|
||||
body: event.toLogBody(),
|
||||
attributes: event.toOpenTelemetryAttributes(config),
|
||||
};
|
||||
logger.emit(logRecord);
|
||||
|
||||
recordGoogleAuthStart(config);
|
||||
});
|
||||
}
|
||||
|
||||
export function logGoogleAuthEnd(
|
||||
config: Config,
|
||||
event: GoogleAuthEndEvent,
|
||||
): void {
|
||||
ClearcutLogger.getInstance(config)?.logGoogleAuthEndEvent(event);
|
||||
bufferTelemetryEvent(() => {
|
||||
const logger = logs.getLogger(SERVICE_NAME);
|
||||
const logRecord: LogRecord = {
|
||||
body: event.toLogBody(),
|
||||
attributes: event.toOpenTelemetryAttributes(config),
|
||||
};
|
||||
logger.emit(logRecord);
|
||||
|
||||
recordGoogleAuthEnd(config);
|
||||
});
|
||||
}
|
||||
|
||||
export function logBillingEvent(
|
||||
config: Config,
|
||||
event: BillingTelemetryEvent,
|
||||
|
||||
@@ -1397,6 +1397,7 @@ export function recordTokenStorageInitialization(
|
||||
tokenStorageTypeCounter.add(1, {
|
||||
...baseMetricDefinition.getCommonAttributes(config),
|
||||
forced: event.forced,
|
||||
type: event.type,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2307,6 +2307,52 @@ export class KeychainAvailabilityEvent implements BaseTelemetryEvent {
|
||||
}
|
||||
}
|
||||
|
||||
export const EVENT_GOOGLE_AUTH_START = 'gemini_cli.google_auth.start';
|
||||
export class GoogleAuthStartEvent implements BaseTelemetryEvent {
|
||||
'event.name': 'google_auth_start';
|
||||
'event.timestamp': string;
|
||||
|
||||
constructor() {
|
||||
this['event.name'] = 'google_auth_start';
|
||||
this['event.timestamp'] = new Date().toISOString();
|
||||
}
|
||||
|
||||
toOpenTelemetryAttributes(config: Config): LogAttributes {
|
||||
return {
|
||||
...getCommonAttributes(config),
|
||||
'event.name': EVENT_GOOGLE_AUTH_START,
|
||||
'event.timestamp': this['event.timestamp'],
|
||||
};
|
||||
}
|
||||
|
||||
toLogBody(): string {
|
||||
return 'Google auth started.';
|
||||
}
|
||||
}
|
||||
|
||||
export const EVENT_GOOGLE_AUTH_END = 'gemini_cli.google_auth.end';
|
||||
export class GoogleAuthEndEvent implements BaseTelemetryEvent {
|
||||
'event.name': 'google_auth_end';
|
||||
'event.timestamp': string;
|
||||
|
||||
constructor() {
|
||||
this['event.name'] = 'google_auth_end';
|
||||
this['event.timestamp'] = new Date().toISOString();
|
||||
}
|
||||
|
||||
toOpenTelemetryAttributes(config: Config): LogAttributes {
|
||||
return {
|
||||
...getCommonAttributes(config),
|
||||
'event.name': EVENT_GOOGLE_AUTH_END,
|
||||
'event.timestamp': this['event.timestamp'],
|
||||
};
|
||||
}
|
||||
|
||||
toLogBody(): string {
|
||||
return 'Google auth succeeded.';
|
||||
}
|
||||
}
|
||||
|
||||
export const EVENT_TOKEN_STORAGE_INITIALIZATION =
|
||||
'gemini_cli.token_storage.initialization';
|
||||
export class TokenStorageInitializationEvent implements BaseTelemetryEvent {
|
||||
|
||||
Reference in New Issue
Block a user