Add copilotTrackingId common telemetry property (#306601)

add copilotTrackingId common property
This commit is contained in:
Vijay Upadya
2026-03-30 23:14:34 -07:00
committed by GitHub
parent 513b43f0b7
commit 79c33d84df
11 changed files with 71 additions and 0 deletions

View File

@@ -46,6 +46,7 @@ const commonTelemetryProperties = new Set([
'common.cli',
'common.useragent',
'common.istouchdevice',
'common.copilottrackingid',
]);
export default new class NoTelemetryCommonProperty implements eslint.Rule.RuleModule {

View File

@@ -803,6 +803,7 @@ class StandaloneTelemetryService implements ITelemetryService {
readonly sendErrorTelemetry = false;
setEnabled(): void { }
setExperimentProperty(): void { }
setCommonProperty(): void { }
publicLog() { }
publicLog2() { }
publicLogError() { }

View File

@@ -70,6 +70,10 @@ export class InterceptingTelemetryService implements ITelemetryService {
setExperimentProperty(name: string, value: string): void {
this._baseService.setExperimentProperty(name, value);
}
setCommonProperty(name: string, value: string): void {
this._baseService.setCommonProperty(name, value);
}
}
export interface IEditTelemetryData {

View File

@@ -51,6 +51,12 @@ export interface ITelemetryService {
publicLogError2<E extends ClassifiedEvent<OmitMetadata<T>> = never, T extends IGDPRProperty = never>(eventName: string, data?: StrictPropertyCheck<T, E>): void;
setExperimentProperty(name: string, value: string): void;
/**
* Sets a common property that will be attached to all telemetry events.
* Common properties are added after PII cleaning and cannot be overridden by event data.
*/
setCommonProperty(name: string, value: string): void;
}
export function telemetryLevelEnabled(service: ITelemetryService, level: TelemetryLevel): boolean {

View File

@@ -135,6 +135,10 @@ export class TelemetryService implements ITelemetryService {
}
}
setCommonProperty(name: string, value: string): void {
this._commonProperties[name] = value;
}
private _flushPendingEvents(): void {
if (this._isExperimentPropertySet) {
return;

View File

@@ -40,6 +40,7 @@ export class NullTelemetryServiceShape implements ITelemetryService {
publicLogError() { }
publicLogError2() { }
setExperimentProperty() { }
setCommonProperty() { }
}
export const NullTelemetryService = new NullTelemetryServiceShape();

View File

@@ -212,6 +212,22 @@ suite('TelemetryService', () => {
service.dispose();
});
test('setCommonProperty adds property to all subsequent events', function () {
const testAppender = new TestTelemetryAppender();
const service = new TelemetryService({
appenders: [testAppender],
}, new TestConfigurationService(), TestProductService);
service.publicLog('eventBeforeSet');
service.setCommonProperty('common.copilotTrackingId', 'test-tracking-id');
service.publicLog('eventAfterSet');
assert.strictEqual(testAppender.events[0].data['common.copilotTrackingId'], undefined);
assert.strictEqual(testAppender.events[1].data['common.copilotTrackingId'], 'test-tracking-id');
service.dispose();
});
test('telemetry on by default', function () {
const testAppender = new TestTelemetryAppender();
const service = new TelemetryService({ appenders: [testAppender] }, new TestConfigurationService(), TestProductService);

View File

@@ -23,6 +23,7 @@ import product from '../../../../platform/product/common/product.js';
import { Registry } from '../../../../platform/registry/common/platform.js';
import { EditorPaneDescriptor, IEditorPaneRegistry } from '../../../browser/editor.js';
import { Extensions, IConfigurationMigrationRegistry } from '../../../common/configuration.js';
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
import { IWorkbenchContribution, WorkbenchPhase, registerWorkbenchContribution2 } from '../../../common/contributions.js';
import { EditorExtensions, IEditorFactoryRegistry } from '../../../common/editor.js';
import { IWorkbenchAssignmentService } from '../../../services/assignment/common/assignmentService.js';
@@ -1526,6 +1527,32 @@ class ChatResolverContribution extends Disposable {
}
}
class CopilotTelemetryContribution extends Disposable implements IWorkbenchContribution {
static readonly ID = 'workbench.contrib.copilotTelemetry';
constructor(
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IChatEntitlementService private readonly chatEntitlementService: IChatEntitlementService,
) {
super();
this.updateCopilotTrackingId();
this._register(this.chatEntitlementService.onDidChangeEntitlement(() => {
this.updateCopilotTrackingId();
}));
}
private updateCopilotTrackingId(): void {
const copilotTrackingId = this.chatEntitlementService.copilotTrackingId;
if (copilotTrackingId) {
// __GDPR__COMMON__ "common.copilotTrackingId" : { "endPoint": "GoogleAnalyticsID", "classification": "EndUserPseudonymizedInformation", "purpose": "BusinessInsight", "comment": "The anonymized Copilot analytics tracking ID from the entitlement API." }
this.telemetryService.setCommonProperty('common.copilotTrackingId', copilotTrackingId);
}
}
}
class ChatDebugResolverContribution implements IWorkbenchContribution {
static readonly ID = 'workbench.contrib.chatDebugResolver';
@@ -1849,6 +1876,7 @@ registerEditorFeature(ChatInputBoxContentProvider);
Registry.as<IEditorFactoryRegistry>(EditorExtensions.EditorFactory).registerEditorSerializer(ChatEditorInput.TypeID, ChatEditorInputSerializer);
Registry.as<IEditorFactoryRegistry>(EditorExtensions.EditorFactory).registerEditorSerializer(ChatDebugEditorInput.ID, ChatDebugEditorInputSerializer);
registerWorkbenchContribution2(CopilotTelemetryContribution.ID, CopilotTelemetryContribution, WorkbenchPhase.BlockRestore);
registerWorkbenchContribution2(ChatResolverContribution.ID, ChatResolverContribution, WorkbenchPhase.BlockStartup);
registerWorkbenchContribution2(ChatDebugResolverContribution.ID, ChatDebugResolverContribution, WorkbenchPhase.BlockStartup);
registerWorkbenchContribution2(PromptsDebugContribution.ID, PromptsDebugContribution, WorkbenchPhase.BlockRestore);

View File

@@ -135,6 +135,10 @@ export class TelemetryService extends Disposable implements ITelemetryService {
return this.impl.setExperimentProperty(name, value);
}
setCommonProperty(name: string, value: string): void {
this.impl.setCommonProperty(name, value);
}
get telemetryLevel(): TelemetryLevel {
return this.impl.telemetryLevel;
}

View File

@@ -101,6 +101,10 @@ export class TelemetryService extends Disposable implements ITelemetryService {
return this.impl.setExperimentProperty(name, value);
}
setCommonProperty(name: string, value: string): void {
this.impl.setCommonProperty(name, value);
}
get telemetryLevel(): TelemetryLevel {
return this.impl.telemetryLevel;
}

View File

@@ -69,6 +69,8 @@ class MockTelemetryService implements ITelemetryService {
}
setExperimentProperty(name: string, value: string): void {
}
setCommonProperty(name: string, value: string): void {
}
}