Fix rough edges around extension updates (#10926)

This commit is contained in:
Jacob MacDonald
2025-10-10 14:28:13 -07:00
committed by GitHub
parent bf0f61e656
commit a6e00d9183
14 changed files with 404 additions and 105 deletions
@@ -36,6 +36,7 @@ import type {
AgentStartEvent,
AgentFinishEvent,
WebFetchFallbackAttemptEvent,
ExtensionUpdateEvent,
} from '../types.js';
import { EventMetadataKey } from './event-metadata-key.js';
import type { Config } from '../../config/config.js';
@@ -77,6 +78,7 @@ export enum EventNames {
EXTENSION_DISABLE = 'extension_disable',
EXTENSION_INSTALL = 'extension_install',
EXTENSION_UNINSTALL = 'extension_uninstall',
EXTENSION_UPDATE = 'extension_update',
TOOL_OUTPUT_TRUNCATED = 'tool_output_truncated',
MODEL_ROUTING = 'model_routing',
MODEL_SLASH_COMMAND = 'model_slash_command',
@@ -929,6 +931,38 @@ export class ClearcutLogger {
});
}
logExtensionUpdateEvent(event: ExtensionUpdateEvent): void {
const data: EventValue[] = [
{
gemini_cli_key: EventMetadataKey.GEMINI_CLI_EXTENSION_NAME,
value: event.extension_name,
},
{
gemini_cli_key: EventMetadataKey.GEMINI_CLI_EXTENSION_VERSION,
value: event.extension_version,
},
{
gemini_cli_key: EventMetadataKey.GEMINI_CLI_EXTENSION_PREVIOUS_VERSION,
value: event.extension_previous_version,
},
{
gemini_cli_key: EventMetadataKey.GEMINI_CLI_EXTENSION_SOURCE,
value: event.extension_source,
},
{
gemini_cli_key: EventMetadataKey.GEMINI_CLI_EXTENSION_UPDATE_STATUS,
value: event.status,
},
];
this.enqueueLogEvent(
this.createLogEvent(EventNames.EXTENSION_UPDATE, data),
);
this.flushToClearcut().catch((error) => {
console.debug('Error flushing to Clearcut:', error);
});
}
logToolOutputTruncatedEvent(event: ToolOutputTruncatedEvent): void {
const data: EventValue[] = [
{
@@ -367,7 +367,7 @@ export enum EventMetadataKey {
GEMINI_CLI_NODE_VERSION = 83,
// ==========================================================================
// Extension Install Event Keys
// Extension Event Keys
// ===========================================================================
// Logs the name of the extension.
@@ -376,6 +376,9 @@ export enum EventMetadataKey {
// Logs the version of the extension.
GEMINI_CLI_EXTENSION_VERSION = 86,
// Logs the previous version of the extension.
GEMINI_CLI_EXTENSION_PREVIOUS_VERSION = 117,
// Logs the source of the extension.
GEMINI_CLI_EXTENSION_SOURCE = 87,
@@ -385,6 +388,9 @@ export enum EventMetadataKey {
// Logs the status of the extension uninstall
GEMINI_CLI_EXTENSION_UNINSTALL_STATUS = 96,
// Logs the status of the extension uninstall
GEMINI_CLI_EXTENSION_UPDATE_STATUS = 118,
// Logs the setting scope for an extension enablement.
GEMINI_CLI_EXTENSION_ENABLE_SETTING_SCOPE = 102,
+1
View File
@@ -44,6 +44,7 @@ export {
logExtensionEnable,
logExtensionInstallEvent,
logExtensionUninstall,
logExtensionUpdateEvent,
logWebFetchFallbackAttempt,
} from './loggers.js';
export type { SlashCommandEvent, ChatCompressionEvent } from './types.js';
@@ -42,6 +42,7 @@ import {
logAgentStart,
logAgentFinish,
logWebFetchFallbackAttempt,
logExtensionUpdateEvent,
} from './loggers.js';
import { ToolCallDecision } from './tool-call-decision.js';
import {
@@ -82,6 +83,8 @@ import {
AgentStartEvent,
AgentFinishEvent,
WebFetchFallbackAttemptEvent,
ExtensionUpdateEvent,
EVENT_EXTENSION_UPDATE,
} from './types.js';
import * as metrics from './metrics.js';
import {
@@ -1292,6 +1295,9 @@ describe('loggers', () => {
const mockConfig = {
getSessionId: () => 'test-session-id',
getUsageStatisticsEnabled: () => true,
getContentGeneratorConfig: () => null,
getUseSmartEdit: () => null,
getUseModelRouter: () => null,
} as unknown as Config;
beforeEach(() => {
@@ -1333,10 +1339,63 @@ describe('loggers', () => {
});
});
describe('logExtensionUpdate', () => {
const mockConfig = {
getSessionId: () => 'test-session-id',
getUsageStatisticsEnabled: () => true,
getContentGeneratorConfig: () => null,
getUseSmartEdit: () => null,
getUseModelRouter: () => null,
} as unknown as Config;
beforeEach(() => {
vi.spyOn(ClearcutLogger.prototype, 'logExtensionUpdateEvent');
});
afterEach(() => {
vi.resetAllMocks();
});
it('should log extension update event', () => {
const event = new ExtensionUpdateEvent(
'vscode',
'0.1.0',
'0.1.1',
'git',
'success',
);
logExtensionUpdateEvent(mockConfig, event);
expect(
ClearcutLogger.prototype.logExtensionUpdateEvent,
).toHaveBeenCalledWith(event);
expect(mockLogger.emit).toHaveBeenCalledWith({
body: 'Updated extension vscode',
attributes: {
'session.id': 'test-session-id',
'user.email': 'test-user@example.com',
'installation.id': 'test-installation-id',
'event.name': EVENT_EXTENSION_UPDATE,
'event.timestamp': '2025-01-01T00:00:00.000Z',
extension_name: 'vscode',
extension_version: '0.1.0',
extension_previous_version: '0.1.1',
extension_source: 'git',
status: 'success',
},
});
});
});
describe('logExtensionUninstall', () => {
const mockConfig = {
getSessionId: () => 'test-session-id',
getUsageStatisticsEnabled: () => true,
getContentGeneratorConfig: () => null,
getUseSmartEdit: () => null,
getUseModelRouter: () => null,
} as unknown as Config;
beforeEach(() => {
+16
View File
@@ -47,6 +47,7 @@ import type {
AgentStartEvent,
AgentFinishEvent,
WebFetchFallbackAttemptEvent,
ExtensionUpdateEvent,
} from './types.js';
import {
recordApiErrorMetrics,
@@ -531,6 +532,21 @@ export function logExtensionUninstall(
logger.emit(logRecord);
}
export function logExtensionUpdateEvent(
config: Config,
event: ExtensionUpdateEvent,
): void {
ClearcutLogger.getInstance(config)?.logExtensionUpdateEvent(event);
if (!isTelemetrySdkInitialized()) return;
const logger = logs.getLogger(SERVICE_NAME);
const logRecord: LogRecord = {
body: event.toLogBody(),
attributes: event.toOpenTelemetryAttributes(config),
};
logger.emit(logRecord);
}
export function logExtensionEnable(
config: Config,
event: ExtensionEnableEvent,
+44
View File
@@ -1156,6 +1156,50 @@ export class ExtensionUninstallEvent implements BaseTelemetryEvent {
}
}
export const EVENT_EXTENSION_UPDATE = 'gemini_cli.extension_update';
export class ExtensionUpdateEvent implements BaseTelemetryEvent {
'event.name': 'extension_update';
'event.timestamp': string;
extension_name: string;
extension_previous_version: string;
extension_version: string;
extension_source: string;
status: 'success' | 'error';
constructor(
extension_name: string,
extension_version: string,
extension_previous_version: string,
extension_source: string,
status: 'success' | 'error',
) {
this['event.name'] = 'extension_update';
this['event.timestamp'] = new Date().toISOString();
this.extension_name = extension_name;
this.extension_version = extension_version;
this.extension_previous_version = extension_previous_version;
this.extension_source = extension_source;
this.status = status;
}
toOpenTelemetryAttributes(config: Config): LogAttributes {
return {
...getCommonAttributes(config),
'event.name': EVENT_EXTENSION_UPDATE,
'event.timestamp': this['event.timestamp'],
extension_name: this.extension_name,
extension_version: this.extension_version,
extension_previous_version: this.extension_previous_version,
extension_source: this.extension_source,
status: this.status,
};
}
toLogBody(): string {
return `Updated extension ${this.extension_name}`;
}
}
export const EVENT_EXTENSION_ENABLE = 'gemini_cli.extension_enable';
export class ExtensionEnableEvent implements BaseTelemetryEvent {
'event.name': 'extension_enable';