mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 21:09:43 +00:00
Tag existing events with experimentation info in typescript-language-features (#161463)
* Associate experiment info with existing telemetry. * Move the ExperimentTelemetryReporter to its own file. * Roughly copied the 'getPackageInfo' function from other extensions. * Addressed code review feedback.
This commit is contained in:
parent
25d51ed33f
commit
da506e9b8d
|
@ -0,0 +1,50 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import VsCodeTelemetryReporter from '@vscode/extension-telemetry';
|
||||
import * as tas from 'vscode-tas-client';
|
||||
|
||||
export interface IExperimentationTelemetryReporter extends tas.IExperimentationTelemetry, vscode.Disposable {
|
||||
postEventObj(eventName: string, props: { [prop: string]: string }): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* This reporter *supports* experimentation telemetry,
|
||||
* but will only do so when passed to an {@link ExperimentationService}.
|
||||
*/
|
||||
|
||||
export class ExperimentationTelemetryReporter
|
||||
implements IExperimentationTelemetryReporter {
|
||||
private _sharedProperties: Record<string, string> = {};
|
||||
private _reporter: VsCodeTelemetryReporter;
|
||||
constructor(reporter: VsCodeTelemetryReporter) {
|
||||
this._reporter = reporter;
|
||||
}
|
||||
|
||||
setSharedProperty(name: string, value: string): void {
|
||||
this._sharedProperties[name] = value;
|
||||
}
|
||||
|
||||
postEvent(eventName: string, props: Map<string, string>): void {
|
||||
const propsObject = {
|
||||
...this._sharedProperties,
|
||||
...Object.fromEntries(props),
|
||||
};
|
||||
this._reporter.sendTelemetryEvent(eventName, propsObject);
|
||||
}
|
||||
|
||||
postEventObj(eventName: string, props: { [prop: string]: string }) {
|
||||
this._reporter.sendTelemetryEvent(eventName, {
|
||||
...this._sharedProperties,
|
||||
...props,
|
||||
});
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._reporter.dispose();
|
||||
}
|
||||
}
|
||||
|
|
@ -4,20 +4,21 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import VsCodeTelemetryReporter from '@vscode/extension-telemetry';
|
||||
import * as tas from 'vscode-tas-client';
|
||||
|
||||
import { IExperimentationTelemetryReporter } from './experimentTelemetryReporter';
|
||||
|
||||
interface ExperimentTypes {
|
||||
// None for now.
|
||||
}
|
||||
|
||||
export class ExperimentationService implements vscode.Disposable {
|
||||
export class ExperimentationService {
|
||||
private _experimentationServicePromise: Promise<tas.IExperimentationService>;
|
||||
private _telemetryReporter: ExperimentTelemetryReporter;
|
||||
private _telemetryReporter: IExperimentationTelemetryReporter;
|
||||
|
||||
constructor(private readonly _extensionContext: vscode.ExtensionContext) {
|
||||
this._telemetryReporter = new ExperimentTelemetryReporter(_extensionContext);
|
||||
this._experimentationServicePromise = this.createExperimentationService();
|
||||
constructor(telemetryReporter: IExperimentationTelemetryReporter, id: string, version: string, globalState: vscode.Memento) {
|
||||
this._telemetryReporter = telemetryReporter;
|
||||
this._experimentationServicePromise = createExperimentationService(this._telemetryReporter, id, version, globalState);
|
||||
}
|
||||
|
||||
public async getTreatmentVariable<K extends keyof ExperimentTypes>(name: K, defaultValue: ExperimentTypes[K]): Promise<ExperimentTypes[K]> {
|
||||
|
@ -29,70 +30,33 @@ export class ExperimentationService implements vscode.Disposable {
|
|||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
private async createExperimentationService(): Promise<tas.IExperimentationService> {
|
||||
let targetPopulation: tas.TargetPopulation;
|
||||
switch (vscode.env.uriScheme) {
|
||||
case 'vscode':
|
||||
targetPopulation = tas.TargetPopulation.Public;
|
||||
break;
|
||||
case 'vscode-insiders':
|
||||
targetPopulation = tas.TargetPopulation.Insiders;
|
||||
break;
|
||||
case 'vscode-exploration':
|
||||
targetPopulation = tas.TargetPopulation.Internal;
|
||||
break;
|
||||
case 'code-oss':
|
||||
targetPopulation = tas.TargetPopulation.Team;
|
||||
break;
|
||||
default:
|
||||
targetPopulation = tas.TargetPopulation.Public;
|
||||
break;
|
||||
}
|
||||
|
||||
const id = this._extensionContext.extension.id;
|
||||
const version = this._extensionContext.extension.packageJSON.version || '';
|
||||
const experimentationService = tas.getExperimentationService(id, version, targetPopulation, this._telemetryReporter, this._extensionContext.globalState);
|
||||
await experimentationService.initialFetch;
|
||||
return experimentationService;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public dispose() {
|
||||
this._telemetryReporter.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class ExperimentTelemetryReporter
|
||||
implements tas.IExperimentationTelemetry, vscode.Disposable {
|
||||
private _sharedProperties: Record<string, string> = {};
|
||||
private _reporter: VsCodeTelemetryReporter;
|
||||
constructor(ctxt: vscode.ExtensionContext) {
|
||||
const extension = ctxt.extension;
|
||||
const packageJSON = extension.packageJSON;
|
||||
this._reporter = new VsCodeTelemetryReporter(
|
||||
extension.id,
|
||||
packageJSON.version || '',
|
||||
packageJSON.aiKey || '');
|
||||
|
||||
export async function createExperimentationService(
|
||||
reporter: IExperimentationTelemetryReporter,
|
||||
id: string,
|
||||
version: string,
|
||||
globalState: vscode.Memento): Promise<tas.IExperimentationService> {
|
||||
let targetPopulation: tas.TargetPopulation;
|
||||
switch (vscode.env.uriScheme) {
|
||||
case 'vscode':
|
||||
targetPopulation = tas.TargetPopulation.Public;
|
||||
break;
|
||||
case 'vscode-insiders':
|
||||
targetPopulation = tas.TargetPopulation.Insiders;
|
||||
break;
|
||||
case 'vscode-exploration':
|
||||
targetPopulation = tas.TargetPopulation.Internal;
|
||||
break;
|
||||
case 'code-oss':
|
||||
targetPopulation = tas.TargetPopulation.Team;
|
||||
break;
|
||||
default:
|
||||
targetPopulation = tas.TargetPopulation.Public;
|
||||
break;
|
||||
}
|
||||
|
||||
setSharedProperty(name: string, value: string): void {
|
||||
this._sharedProperties[name] = value;
|
||||
}
|
||||
|
||||
postEvent(eventName: string, props: Map<string, string>): void {
|
||||
const propsObject = {
|
||||
...this._sharedProperties,
|
||||
...Object.fromEntries(props),
|
||||
};
|
||||
this._reporter.sendTelemetryEvent(eventName, propsObject);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._reporter.dispose();
|
||||
}
|
||||
const experimentationService = tas.getExperimentationService(id, version, targetPopulation, reporter, globalState);
|
||||
await experimentationService.initialFetch;
|
||||
return experimentationService;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import VsCodeTelemetryReporter from '@vscode/extension-telemetry';
|
||||
import { Api, getExtensionApi } from './api';
|
||||
import { CommandManager } from './commands/commandManager';
|
||||
import { registerBaseCommands } from './commands/index';
|
||||
|
@ -17,6 +18,8 @@ import API from './utils/api';
|
|||
import { TypeScriptServiceConfiguration } from './utils/configuration';
|
||||
import { BrowserServiceConfigurationProvider } from './utils/configuration.browser';
|
||||
import { PluginManager } from './utils/plugins';
|
||||
import { ExperimentationTelemetryReporter, IExperimentationTelemetryReporter } from './experimentTelemetryReporter';
|
||||
import { getPackageInfo } from './utils/packageInfo';
|
||||
|
||||
class StaticVersionProvider implements ITypeScriptVersionProvider {
|
||||
|
||||
|
@ -57,6 +60,15 @@ export function activate(
|
|||
vscode.Uri.joinPath(context.extensionUri, 'dist/browser/typescript/tsserver.web.js').toString(),
|
||||
API.fromSimpleString('4.8.2')));
|
||||
|
||||
let experimentTelemetryReporter: IExperimentationTelemetryReporter | undefined;
|
||||
const packageInfo = getPackageInfo(context);
|
||||
if (packageInfo) {
|
||||
const { name: id, version, aiKey } = packageInfo;
|
||||
const vscTelemetryReporter = new VsCodeTelemetryReporter(id, version, aiKey);
|
||||
experimentTelemetryReporter = new ExperimentationTelemetryReporter(vscTelemetryReporter);
|
||||
context.subscriptions.push(experimentTelemetryReporter);
|
||||
}
|
||||
|
||||
const lazyClientHost = createLazyClientHost(context, false, {
|
||||
pluginManager,
|
||||
commandManager,
|
||||
|
@ -66,6 +78,7 @@ export function activate(
|
|||
processFactory: WorkerServerProcess,
|
||||
activeJsTsEditorTracker,
|
||||
serviceConfigurationProvider: new BrowserServiceConfigurationProvider(),
|
||||
experimentTelemetryReporter,
|
||||
}, item => {
|
||||
onCompletionAccepted.fire(item);
|
||||
});
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
|
||||
import * as fs from 'fs';
|
||||
import * as vscode from 'vscode';
|
||||
import VsCodeTelemetryReporter from '@vscode/extension-telemetry';
|
||||
import { Api, getExtensionApi } from './api';
|
||||
import { CommandManager } from './commands/commandManager';
|
||||
import { registerBaseCommands } from './commands/index';
|
||||
import { ExperimentationService } from './experimentationService';
|
||||
import { ExperimentationTelemetryReporter, IExperimentationTelemetryReporter } from './experimentTelemetryReporter';
|
||||
import { createLazyClientHost, lazilyActivateClient } from './lazyClientHost';
|
||||
import { nodeRequestCancellerFactory } from './tsServer/cancellation.electron';
|
||||
import { NodeLogDirectoryProvider } from './tsServer/logDirectoryProvider.electron';
|
||||
|
@ -20,6 +22,7 @@ import { ElectronServiceConfigurationProvider } from './utils/configuration.elec
|
|||
import { onCaseInsensitiveFileSystem } from './utils/fileSystem.electron';
|
||||
import { PluginManager } from './utils/plugins';
|
||||
import * as temp from './utils/temp.electron';
|
||||
import { getPackageInfo } from './utils/packageInfo';
|
||||
|
||||
export function activate(
|
||||
context: vscode.ExtensionContext
|
||||
|
@ -42,6 +45,19 @@ export function activate(
|
|||
const jsWalkthroughState = new JsWalkthroughState();
|
||||
context.subscriptions.push(jsWalkthroughState);
|
||||
|
||||
let experimentTelemetryReporter: IExperimentationTelemetryReporter | undefined;
|
||||
const packageInfo = getPackageInfo(context);
|
||||
if (packageInfo) {
|
||||
const { name: id, version, aiKey } = packageInfo;
|
||||
const vscTelemetryReporter = new VsCodeTelemetryReporter(id, version, aiKey);
|
||||
experimentTelemetryReporter = new ExperimentationTelemetryReporter(vscTelemetryReporter);
|
||||
context.subscriptions.push(experimentTelemetryReporter);
|
||||
|
||||
// Currently we have no experiments, but creating the service adds the appropriate
|
||||
// shared properties to the ExperimentationTelemetryReporter we just created.
|
||||
new ExperimentationService(experimentTelemetryReporter, id, version, context.globalState);
|
||||
}
|
||||
|
||||
const lazyClientHost = createLazyClientHost(context, onCaseInsensitiveFileSystem(), {
|
||||
pluginManager,
|
||||
commandManager,
|
||||
|
@ -51,6 +67,7 @@ export function activate(
|
|||
processFactory: new ElectronServiceProcessFactory(),
|
||||
activeJsTsEditorTracker,
|
||||
serviceConfigurationProvider: new ElectronServiceConfigurationProvider(),
|
||||
experimentTelemetryReporter,
|
||||
}, item => {
|
||||
onCompletionAccepted.fire(item);
|
||||
});
|
||||
|
@ -58,9 +75,6 @@ export function activate(
|
|||
registerBaseCommands(commandManager, lazyClientHost, pluginManager, activeJsTsEditorTracker);
|
||||
registerJsNodeWalkthrough(commandManager, jsWalkthroughState);
|
||||
|
||||
// Currently no variables in use.
|
||||
context.subscriptions.push(new ExperimentationService(context));
|
||||
|
||||
import('./task/taskProvider').then(module => {
|
||||
context.subscriptions.push(module.register(lazyClientHost.map(x => x.serviceClient)));
|
||||
});
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
import * as vscode from 'vscode';
|
||||
import { CommandManager } from './commands/commandManager';
|
||||
import { IExperimentationTelemetryReporter } from './experimentTelemetryReporter';
|
||||
import { OngoingRequestCancellerFactory } from './tsServer/cancellation';
|
||||
import { ILogDirectoryProvider } from './tsServer/logDirectoryProvider';
|
||||
import { TsServerProcessFactory } from './tsServer/server';
|
||||
|
@ -30,6 +31,7 @@ export function createLazyClientHost(
|
|||
processFactory: TsServerProcessFactory;
|
||||
activeJsTsEditorTracker: ActiveJsTsEditorTracker;
|
||||
serviceConfigurationProvider: ServiceConfigurationProvider;
|
||||
experimentTelemetryReporter: IExperimentationTelemetryReporter | undefined;
|
||||
},
|
||||
onCompletionAccepted: (item: vscode.CompletionItem) => void,
|
||||
): Lazy<TypeScriptServiceClientHost> {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
import * as vscode from 'vscode';
|
||||
import { CommandManager } from './commands/commandManager';
|
||||
import { IExperimentationTelemetryReporter } from './experimentTelemetryReporter';
|
||||
import { DiagnosticKind } from './languageFeatures/diagnostics';
|
||||
import FileConfigurationManager from './languageFeatures/fileConfigurationManager';
|
||||
import LanguageProvider from './languageProvider';
|
||||
|
@ -72,6 +73,7 @@ export default class TypeScriptServiceClientHost extends Disposable {
|
|||
processFactory: TsServerProcessFactory;
|
||||
activeJsTsEditorTracker: ActiveJsTsEditorTracker;
|
||||
serviceConfigurationProvider: ServiceConfigurationProvider;
|
||||
experimentTelemetryReporter: IExperimentationTelemetryReporter | undefined;
|
||||
},
|
||||
onCompletionAccepted: (item: vscode.CompletionItem) => void,
|
||||
) {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { IExperimentationTelemetryReporter } from './experimentTelemetryReporter';
|
||||
import { DiagnosticKind, DiagnosticsManager } from './languageFeatures/diagnostics';
|
||||
import * as Proto from './protocol';
|
||||
import { EventName } from './protocol.const';
|
||||
|
@ -137,6 +138,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
|||
versionProvider: ITypeScriptVersionProvider;
|
||||
processFactory: TsServerProcessFactory;
|
||||
serviceConfigurationProvider: ServiceConfigurationProvider;
|
||||
experimentTelemetryReporter: IExperimentationTelemetryReporter | undefined;
|
||||
},
|
||||
allModeIds: readonly string[]
|
||||
) {
|
||||
|
@ -205,14 +207,14 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
|||
}
|
||||
}, this, this._disposables);
|
||||
|
||||
this.telemetryReporter = this._register(new VSCodeTelemetryReporter(() => {
|
||||
this.telemetryReporter = new VSCodeTelemetryReporter(services.experimentTelemetryReporter, () => {
|
||||
if (this.serverState.type === ServerState.Type.Running) {
|
||||
if (this.serverState.tsserverVersion) {
|
||||
return this.serverState.tsserverVersion;
|
||||
}
|
||||
}
|
||||
return this.apiVersion.fullVersionString;
|
||||
}));
|
||||
});
|
||||
|
||||
this.typescriptServerSpawner = new TypeScriptServerSpawner(this.versionProvider, this._versionManager, this.logDirectoryProvider, this.pluginPathsProvider, this.logger, this.telemetryReporter, this.tracer, this.processFactory);
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export interface PackageInfo {
|
||||
name: string;
|
||||
version: string;
|
||||
aiKey: string;
|
||||
}
|
||||
|
||||
export function getPackageInfo(context: vscode.ExtensionContext) {
|
||||
const packageJSON = context.extension.packageJSON;
|
||||
if (packageJSON && typeof packageJSON === 'object') {
|
||||
return {
|
||||
name: packageJSON.name ?? '',
|
||||
version: packageJSON.version ?? '',
|
||||
aiKey: packageJSON.aiKey ?? '',
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
|
@ -3,15 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import VsCodeTelemetryReporter from '@vscode/extension-telemetry';
|
||||
import { memoize } from './memoize';
|
||||
|
||||
interface PackageInfo {
|
||||
readonly name: string;
|
||||
readonly version: string;
|
||||
readonly aiKey: string;
|
||||
}
|
||||
import { IExperimentationTelemetryReporter } from '../experimentTelemetryReporter';
|
||||
|
||||
export interface TelemetryProperties {
|
||||
readonly [prop: string]: string | number | boolean | undefined;
|
||||
|
@ -19,14 +11,11 @@ export interface TelemetryProperties {
|
|||
|
||||
export interface TelemetryReporter {
|
||||
logTelemetry(eventName: string, properties?: TelemetryProperties): void;
|
||||
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export class VSCodeTelemetryReporter implements TelemetryReporter {
|
||||
private _reporter: VsCodeTelemetryReporter | null = null;
|
||||
|
||||
constructor(
|
||||
private readonly reporter: IExperimentationTelemetryReporter | undefined,
|
||||
private readonly clientVersionDelegate: () => string
|
||||
) { }
|
||||
|
||||
|
@ -43,38 +32,6 @@ export class VSCodeTelemetryReporter implements TelemetryReporter {
|
|||
*/
|
||||
properties['version'] = this.clientVersionDelegate();
|
||||
|
||||
reporter.sendTelemetryEvent(eventName, properties);
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
if (this._reporter) {
|
||||
this._reporter.dispose();
|
||||
this._reporter = null;
|
||||
}
|
||||
}
|
||||
|
||||
@memoize
|
||||
private get reporter(): VsCodeTelemetryReporter | null {
|
||||
if (this.packageInfo?.aiKey) {
|
||||
this._reporter = new VsCodeTelemetryReporter(
|
||||
this.packageInfo.name,
|
||||
this.packageInfo.version,
|
||||
this.packageInfo.aiKey);
|
||||
return this._reporter;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@memoize
|
||||
private get packageInfo(): PackageInfo | null {
|
||||
const { packageJSON } = vscode.extensions.getExtension('vscode.typescript-language-features')!;
|
||||
if (packageJSON) {
|
||||
return {
|
||||
name: packageJSON.name,
|
||||
version: packageJSON.version,
|
||||
aiKey: packageJSON.aiKey
|
||||
};
|
||||
}
|
||||
return null;
|
||||
reporter.postEventObj(eventName, properties);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue