mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-10 22:21:22 -07:00
feat(plan): telemetry to track adoption and usage of plan mode (#16863)
This commit is contained in:
@@ -18,6 +18,7 @@ Learn how to enable and setup OpenTelemetry for Gemini CLI.
|
||||
- [Logs and metrics](#logs-and-metrics)
|
||||
- [Logs](#logs)
|
||||
- [Sessions](#sessions)
|
||||
- [Approval Mode](#approval-mode)
|
||||
- [Tools](#tools)
|
||||
- [Files](#files)
|
||||
- [API](#api)
|
||||
@@ -315,6 +316,20 @@ Captures startup configuration and user prompt submissions.
|
||||
- `prompt` (string; excluded if `telemetry.logPrompts` is `false`)
|
||||
- `auth_type` (string)
|
||||
|
||||
#### Approval Mode
|
||||
|
||||
Tracks changes and duration of approval modes.
|
||||
|
||||
- `approval_mode_switch`: Approval mode was changed.
|
||||
- **Attributes**:
|
||||
- `from_mode` (string)
|
||||
- `to_mode` (string)
|
||||
|
||||
- `approval_mode_duration`: Duration spent in an approval mode.
|
||||
- **Attributes**:
|
||||
- `mode` (string)
|
||||
- `duration_ms` (int)
|
||||
|
||||
#### Tools
|
||||
|
||||
Captures tool executions, output truncation, and Edit behavior.
|
||||
|
||||
@@ -64,6 +64,8 @@ import { logRipgrepFallback, logFlashFallback } from '../telemetry/loggers.js';
|
||||
import {
|
||||
RipgrepFallbackEvent,
|
||||
FlashFallbackEvent,
|
||||
ApprovalModeSwitchEvent,
|
||||
ApprovalModeDurationEvent,
|
||||
} from '../telemetry/types.js';
|
||||
import type { FallbackModelHandler } from '../fallback/types.js';
|
||||
import { ModelAvailabilityService } from '../availability/modelAvailabilityService.js';
|
||||
@@ -105,6 +107,10 @@ import { debugLogger } from '../utils/debugLogger.js';
|
||||
import { SkillManager, type SkillDefinition } from '../skills/skillManager.js';
|
||||
import { startupProfiler } from '../telemetry/startupProfiler.js';
|
||||
import type { AgentDefinition } from '../agents/types.js';
|
||||
import {
|
||||
logApprovalModeSwitch,
|
||||
logApprovalModeDuration,
|
||||
} from '../telemetry/loggers.js';
|
||||
import { fetchAdminControls } from '../code_assist/admin/admin_controls.js';
|
||||
|
||||
export interface AccessibilitySettings {
|
||||
@@ -544,6 +550,7 @@ export class Config {
|
||||
private terminalBackground: string | undefined = undefined;
|
||||
private remoteAdminSettings: FetchAdminControlsResponse | undefined;
|
||||
private latestApiRequest: GenerateContentParameters | undefined;
|
||||
private lastModeSwitchTime: number = Date.now();
|
||||
|
||||
constructor(params: ConfigParameters) {
|
||||
this.sessionId = params.sessionId;
|
||||
@@ -1348,9 +1355,32 @@ export class Config {
|
||||
'Cannot enable privileged approval modes in an untrusted folder.',
|
||||
);
|
||||
}
|
||||
|
||||
const currentMode = this.getApprovalMode();
|
||||
if (currentMode !== mode) {
|
||||
this.logCurrentModeDuration(this.getApprovalMode());
|
||||
logApprovalModeSwitch(
|
||||
this,
|
||||
new ApprovalModeSwitchEvent(currentMode, mode),
|
||||
);
|
||||
this.lastModeSwitchTime = Date.now();
|
||||
}
|
||||
|
||||
this.policyEngine.setApprovalMode(mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the duration of the current approval mode.
|
||||
*/
|
||||
logCurrentModeDuration(mode: ApprovalMode): void {
|
||||
const now = Date.now();
|
||||
const duration = now - this.lastModeSwitchTime;
|
||||
logApprovalModeDuration(
|
||||
this,
|
||||
new ApprovalModeDurationEvent(mode, duration),
|
||||
);
|
||||
}
|
||||
|
||||
isYoloModeDisabled(): boolean {
|
||||
return this.disableYoloMode || !this.isTrustedFolder();
|
||||
}
|
||||
@@ -2054,6 +2084,7 @@ export class Config {
|
||||
* Disposes of resources and removes event listeners.
|
||||
*/
|
||||
async dispose(): Promise<void> {
|
||||
this.logCurrentModeDuration(this.getApprovalMode());
|
||||
coreEvents.off(CoreEvent.AgentsRefreshed, this.onAgentsRefreshed);
|
||||
this.agentRegistry?.dispose();
|
||||
this.geminiClient?.dispose();
|
||||
|
||||
@@ -42,6 +42,8 @@ import type {
|
||||
ExtensionUpdateEvent,
|
||||
LlmLoopCheckEvent,
|
||||
HookCallEvent,
|
||||
ApprovalModeSwitchEvent,
|
||||
ApprovalModeDurationEvent,
|
||||
} from '../types.js';
|
||||
import { EventMetadataKey } from './event-metadata-key.js';
|
||||
import type { Config } from '../../config/config.js';
|
||||
@@ -100,6 +102,8 @@ export enum EventNames {
|
||||
WEB_FETCH_FALLBACK_ATTEMPT = 'web_fetch_fallback_attempt',
|
||||
LLM_LOOP_CHECK = 'llm_loop_check',
|
||||
HOOK_CALL = 'hook_call',
|
||||
APPROVAL_MODE_SWITCH = 'approval_mode_switch',
|
||||
APPROVAL_MODE_DURATION = 'approval_mode_duration',
|
||||
}
|
||||
|
||||
export interface LogResponse {
|
||||
@@ -1467,6 +1471,42 @@ export class ClearcutLogger {
|
||||
this.flushIfNeeded();
|
||||
}
|
||||
|
||||
logApprovalModeSwitchEvent(event: ApprovalModeSwitchEvent): void {
|
||||
const data: EventValue[] = [
|
||||
{
|
||||
gemini_cli_key: EventMetadataKey.GEMINI_CLI_ACTIVE_APPROVAL_MODE,
|
||||
value: event.from_mode,
|
||||
},
|
||||
{
|
||||
gemini_cli_key: EventMetadataKey.GEMINI_CLI_APPROVAL_MODE_TO,
|
||||
value: event.to_mode,
|
||||
},
|
||||
];
|
||||
|
||||
this.enqueueLogEvent(
|
||||
this.createLogEvent(EventNames.APPROVAL_MODE_SWITCH, data),
|
||||
);
|
||||
this.flushIfNeeded();
|
||||
}
|
||||
|
||||
logApprovalModeDurationEvent(event: ApprovalModeDurationEvent): void {
|
||||
const data: EventValue[] = [
|
||||
{
|
||||
gemini_cli_key: EventMetadataKey.GEMINI_CLI_ACTIVE_APPROVAL_MODE,
|
||||
value: event.mode,
|
||||
},
|
||||
{
|
||||
gemini_cli_key: EventMetadataKey.GEMINI_CLI_APPROVAL_MODE_DURATION_MS,
|
||||
value: event.duration_ms.toString(),
|
||||
},
|
||||
];
|
||||
|
||||
this.enqueueLogEvent(
|
||||
this.createLogEvent(EventNames.APPROVAL_MODE_DURATION, data),
|
||||
);
|
||||
this.flushIfNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds default fields to data, and returns a new data array. This fields
|
||||
* should exist on all log events.
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// Defines valid event metadata keys for Clearcut logging.
|
||||
export enum EventMetadataKey {
|
||||
// Deleted enums: 24
|
||||
// Next ID: 137
|
||||
// Next ID: 144
|
||||
|
||||
GEMINI_CLI_KEY_UNKNOWN = 0,
|
||||
|
||||
@@ -529,4 +529,17 @@ export enum EventMetadataKey {
|
||||
|
||||
// Logs total RAM in GB of user machine.
|
||||
GEMINI_CLI_RAM_TOTAL_GB = 140,
|
||||
|
||||
// ==========================================================================
|
||||
// Approval Mode Event Keys
|
||||
// ==========================================================================
|
||||
|
||||
// Logs the active approval mode in the session.
|
||||
GEMINI_CLI_ACTIVE_APPROVAL_MODE = 141,
|
||||
|
||||
// Logs the new approval mode.
|
||||
GEMINI_CLI_APPROVAL_MODE_TO = 142,
|
||||
|
||||
// Logs the duration spent in an approval mode in milliseconds.
|
||||
GEMINI_CLI_APPROVAL_MODE_DURATION_MS = 143,
|
||||
}
|
||||
|
||||
@@ -48,9 +48,11 @@ import type {
|
||||
RecoveryAttemptEvent,
|
||||
WebFetchFallbackAttemptEvent,
|
||||
ExtensionUpdateEvent,
|
||||
LlmLoopCheckEvent,
|
||||
ApprovalModeSwitchEvent,
|
||||
ApprovalModeDurationEvent,
|
||||
HookCallEvent,
|
||||
StartupStatsEvent,
|
||||
LlmLoopCheckEvent,
|
||||
} from './types.js';
|
||||
import {
|
||||
recordApiErrorMetrics,
|
||||
@@ -671,6 +673,32 @@ export function logLlmLoopCheck(
|
||||
});
|
||||
}
|
||||
|
||||
export function logApprovalModeSwitch(
|
||||
config: Config,
|
||||
event: ApprovalModeSwitchEvent,
|
||||
) {
|
||||
ClearcutLogger.getInstance(config)?.logApprovalModeSwitchEvent(event);
|
||||
bufferTelemetryEvent(() => {
|
||||
logs.getLogger(SERVICE_NAME).emit({
|
||||
body: event.toLogBody(),
|
||||
attributes: event.toOpenTelemetryAttributes(config),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function logApprovalModeDuration(
|
||||
config: Config,
|
||||
event: ApprovalModeDurationEvent,
|
||||
) {
|
||||
ClearcutLogger.getInstance(config)?.logApprovalModeDurationEvent(event);
|
||||
bufferTelemetryEvent(() => {
|
||||
logs.getLogger(SERVICE_NAME).emit({
|
||||
body: event.toLogBody(),
|
||||
attributes: event.toOpenTelemetryAttributes(config),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function logHookCall(config: Config, event: HookCallEvent): void {
|
||||
ClearcutLogger.getInstance(config)?.logHookCallEvent(event);
|
||||
bufferTelemetryEvent(() => {
|
||||
|
||||
@@ -1845,6 +1845,58 @@ export class WebFetchFallbackAttemptEvent implements BaseTelemetryEvent {
|
||||
}
|
||||
|
||||
export const EVENT_HOOK_CALL = 'gemini_cli.hook_call';
|
||||
export class ApprovalModeSwitchEvent implements BaseTelemetryEvent {
|
||||
eventName = 'approval_mode_switch';
|
||||
from_mode: ApprovalMode;
|
||||
to_mode: ApprovalMode;
|
||||
|
||||
constructor(fromMode: ApprovalMode, toMode: ApprovalMode) {
|
||||
this.from_mode = fromMode;
|
||||
this.to_mode = toMode;
|
||||
}
|
||||
'event.name': string;
|
||||
'event.timestamp': string;
|
||||
|
||||
toOpenTelemetryAttributes(config: Config): LogAttributes {
|
||||
return {
|
||||
...getCommonAttributes(config),
|
||||
event_name: this.eventName,
|
||||
from_mode: this.from_mode,
|
||||
to_mode: this.to_mode,
|
||||
};
|
||||
}
|
||||
|
||||
toLogBody(): string {
|
||||
return `Approval mode switched from ${this.from_mode} to ${this.to_mode}.`;
|
||||
}
|
||||
}
|
||||
|
||||
export class ApprovalModeDurationEvent implements BaseTelemetryEvent {
|
||||
eventName = 'approval_mode_duration';
|
||||
mode: ApprovalMode;
|
||||
duration_ms: number;
|
||||
|
||||
constructor(mode: ApprovalMode, durationMs: number) {
|
||||
this.mode = mode;
|
||||
this.duration_ms = durationMs;
|
||||
}
|
||||
'event.name': string;
|
||||
'event.timestamp': string;
|
||||
|
||||
toOpenTelemetryAttributes(config: Config): LogAttributes {
|
||||
return {
|
||||
...getCommonAttributes(config),
|
||||
event_name: this.eventName,
|
||||
mode: this.mode,
|
||||
duration_ms: this.duration_ms,
|
||||
};
|
||||
}
|
||||
|
||||
toLogBody(): string {
|
||||
return `Approval mode ${this.mode} was active for ${this.duration_ms}ms.`;
|
||||
}
|
||||
}
|
||||
|
||||
export class HookCallEvent implements BaseTelemetryEvent {
|
||||
'event.name': string;
|
||||
'event.timestamp': string;
|
||||
|
||||
Reference in New Issue
Block a user