mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-10 05:28:19 -05:00
Separate exeriments from telemetry (fixes #29339)
This commit is contained in:
@@ -33,7 +33,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeybindingsRegistry, IKeybindingItem } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { MenuId, IMenu, IMenuService } from 'vs/platform/actions/common/actions';
|
||||
import { Menu } from 'vs/platform/actions/common/menu';
|
||||
import { ITelemetryService, ITelemetryExperiments, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { ResolvedKeybinding, Keybinding, createKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
|
||||
import { OS } from 'vs/base/common/platform';
|
||||
@@ -513,10 +513,6 @@ export class StandaloneTelemetryService implements ITelemetryService {
|
||||
public getTelemetryInfo(): TPromise<ITelemetryInfo> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public getExperiments(): ITelemetryExperiments {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export class SimpleWorkspaceContextService implements IWorkspaceContextService {
|
||||
|
||||
74
src/vs/platform/telemetry/common/experiments.ts
Normal file
74
src/vs/platform/telemetry/common/experiments.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export interface ITelemetryExperiments {
|
||||
}
|
||||
|
||||
const defaultExperiments: ITelemetryExperiments = {
|
||||
};
|
||||
|
||||
export function loadExperiments(accessor?: ServicesAccessor): ITelemetryExperiments {
|
||||
|
||||
// shortcut since there are currently no experiments (should introduce separate service to load only once)
|
||||
if (!accessor) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const storageService = accessor.get(IStorageService);
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
|
||||
let {
|
||||
} = splitExperimentsRandomness(storageService);
|
||||
|
||||
return applyOverrides(defaultExperiments, configurationService);
|
||||
}
|
||||
|
||||
function applyOverrides(experiments: ITelemetryExperiments, configurationService: IConfigurationService): ITelemetryExperiments {
|
||||
const experimentsConfig = getExperimentsOverrides(configurationService);
|
||||
Object.keys(experiments).forEach(key => {
|
||||
if (key in experimentsConfig) {
|
||||
experiments[key] = experimentsConfig[key];
|
||||
}
|
||||
});
|
||||
return experiments;
|
||||
}
|
||||
|
||||
function splitExperimentsRandomness(storageService: IStorageService): ITelemetryExperiments {
|
||||
const random1 = getExperimentsRandomness(storageService);
|
||||
const [random2, /* showTaskDocumentation */] = splitRandom(random1);
|
||||
const [random3, /* openUntitledFile */] = splitRandom(random2);
|
||||
const [random4, /* mergeQuickLinks */] = splitRandom(random3);
|
||||
// tslint:disable-next-line:no-unused-variable (https://github.com/Microsoft/TypeScript/issues/16628)
|
||||
const [random5, /* enableWelcomePage */] = splitRandom(random4);
|
||||
return {
|
||||
};
|
||||
}
|
||||
|
||||
function getExperimentsRandomness(storageService: IStorageService) {
|
||||
const key = 'experiments.randomness';
|
||||
let valueString = storageService.get(key);
|
||||
if (!valueString) {
|
||||
valueString = Math.random().toString();
|
||||
storageService.store(key, valueString);
|
||||
}
|
||||
|
||||
return parseFloat(valueString);
|
||||
}
|
||||
|
||||
function splitRandom(random: number): [number, boolean] {
|
||||
const scaled = random * 2;
|
||||
const i = Math.floor(scaled);
|
||||
return [scaled - i, i === 1];
|
||||
}
|
||||
|
||||
function getExperimentsOverrides(configurationService: IConfigurationService): ITelemetryExperiments {
|
||||
const config: any = configurationService.getConfiguration('telemetry');
|
||||
return config && config.experiments || {};
|
||||
}
|
||||
@@ -21,10 +21,6 @@ export interface ITelemetryData {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface ITelemetryExperiments {
|
||||
mergeQuickLinks: boolean;
|
||||
}
|
||||
|
||||
export interface ITelemetryService {
|
||||
|
||||
_serviceBrand: any;
|
||||
@@ -38,6 +34,4 @@ export interface ITelemetryService {
|
||||
getTelemetryInfo(): TPromise<ITelemetryInfo>;
|
||||
|
||||
isOptedIn: boolean;
|
||||
|
||||
getExperiments(): ITelemetryExperiments;
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { escapeRegExpCharacters } from 'vs/base/common/strings';
|
||||
import { ITelemetryService, ITelemetryInfo, ITelemetryExperiments, ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { ITelemetryAppender, defaultExperiments } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { ITelemetryService, ITelemetryInfo, ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { optional } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
@@ -22,7 +22,6 @@ export interface ITelemetryServiceConfig {
|
||||
commonProperties?: TPromise<{ [name: string]: any }>;
|
||||
piiPaths?: string[];
|
||||
userOptIn?: boolean;
|
||||
experiments?: ITelemetryExperiments;
|
||||
}
|
||||
|
||||
export class TelemetryService implements ITelemetryService {
|
||||
@@ -36,7 +35,6 @@ export class TelemetryService implements ITelemetryService {
|
||||
private _commonProperties: TPromise<{ [name: string]: any; }>;
|
||||
private _piiPaths: string[];
|
||||
private _userOptIn: boolean;
|
||||
private _experiments: ITelemetryExperiments;
|
||||
|
||||
private _disposables: IDisposable[] = [];
|
||||
private _cleanupPatterns: [RegExp, string][] = [];
|
||||
@@ -49,7 +47,6 @@ export class TelemetryService implements ITelemetryService {
|
||||
this._commonProperties = config.commonProperties || TPromise.as({});
|
||||
this._piiPaths = config.piiPaths || [];
|
||||
this._userOptIn = typeof config.userOptIn === 'undefined' ? true : config.userOptIn;
|
||||
this._experiments = config.experiments || defaultExperiments;
|
||||
|
||||
// static cleanup patterns for:
|
||||
// #1 `file:///DANGEROUS/PATH/resources/app/Useful/Information`
|
||||
@@ -81,10 +78,6 @@ export class TelemetryService implements ITelemetryService {
|
||||
return this._userOptIn;
|
||||
}
|
||||
|
||||
getExperiments(): ITelemetryExperiments {
|
||||
return this._experiments;
|
||||
}
|
||||
|
||||
getTelemetryInfo(): TPromise<ITelemetryInfo> {
|
||||
return this._commonProperties.then(values => {
|
||||
// well known properties
|
||||
|
||||
@@ -12,17 +12,10 @@ import URI from 'vs/base/common/uri';
|
||||
import { ConfigurationSource, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IKeybindingService, KeybindingSource } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ILifecycleService, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { ITelemetryService, ITelemetryExperiments, ITelemetryInfo, ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export const defaultExperiments: ITelemetryExperiments = {
|
||||
mergeQuickLinks: false,
|
||||
};
|
||||
import { ITelemetryService, ITelemetryInfo, ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
||||
export const NullTelemetryService = {
|
||||
_serviceBrand: undefined,
|
||||
_experiments: defaultExperiments,
|
||||
publicLog(eventName: string, data?: ITelemetryData) {
|
||||
return TPromise.as<void>(null);
|
||||
},
|
||||
@@ -33,67 +26,9 @@ export const NullTelemetryService = {
|
||||
sessionId: 'someValue.sessionId',
|
||||
machineId: 'someValue.machineId'
|
||||
});
|
||||
},
|
||||
getExperiments(): ITelemetryExperiments {
|
||||
return this._experiments;
|
||||
}
|
||||
};
|
||||
|
||||
export function loadExperiments(accessor: ServicesAccessor): ITelemetryExperiments {
|
||||
// const storageService = accessor.get(IStorageService);
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
|
||||
// let {
|
||||
// } = splitExperimentsRandomness(storageService);
|
||||
|
||||
return applyOverrides(defaultExperiments, configurationService);
|
||||
}
|
||||
|
||||
function applyOverrides(experiments: ITelemetryExperiments, configurationService: IConfigurationService): ITelemetryExperiments {
|
||||
const experimentsConfig = getExperimentsOverrides(configurationService);
|
||||
Object.keys(experiments).forEach(key => {
|
||||
if (key in experimentsConfig) {
|
||||
experiments[key] = experimentsConfig[key];
|
||||
}
|
||||
});
|
||||
return experiments;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
function splitExperimentsRandomness(storageService: IStorageService): ITelemetryExperiments {
|
||||
const random1 = getExperimentsRandomness(storageService);
|
||||
const [random2, /* showTaskDocumentation */] = splitRandom(random1);
|
||||
const [random3, /* openUntitledFile */] = splitRandom(random2);
|
||||
const [random4, mergeQuickLinks] = splitRandom(random3);
|
||||
// tslint:disable-next-line:no-unused-variable (https://github.com/Microsoft/TypeScript/issues/16628)
|
||||
const [random5, /* enableWelcomePage */] = splitRandom(random4);
|
||||
return {
|
||||
mergeQuickLinks,
|
||||
};
|
||||
}
|
||||
|
||||
function getExperimentsRandomness(storageService: IStorageService) {
|
||||
const key = 'experiments.randomness';
|
||||
let valueString = storageService.get(key);
|
||||
if (!valueString) {
|
||||
valueString = Math.random().toString();
|
||||
storageService.store(key, valueString);
|
||||
}
|
||||
|
||||
return parseFloat(valueString);
|
||||
}
|
||||
|
||||
function splitRandom(random: number): [number, boolean] {
|
||||
const scaled = random * 2;
|
||||
const i = Math.floor(scaled);
|
||||
return [scaled - i, i === 1];
|
||||
}
|
||||
|
||||
function getExperimentsOverrides(configurationService: IConfigurationService): ITelemetryExperiments {
|
||||
const config: any = configurationService.getConfiguration('telemetry');
|
||||
return config && config.experiments || {};
|
||||
}
|
||||
|
||||
export interface ITelemetryAppender {
|
||||
log(eventName: string, data: any): void;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import { notImplemented } from 'vs/base/common/errors';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ITelemetryService, ITelemetryInfo, ITelemetryExperiments } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IThreadService } from 'vs/workbench/services/thread/common/threadService';
|
||||
import { MainContext, MainThreadTelemetryShape } from './extHost.protocol';
|
||||
|
||||
@@ -26,10 +26,6 @@ export class RemoteTelemetryService implements ITelemetryService {
|
||||
throw notImplemented();
|
||||
}
|
||||
|
||||
getExperiments(): ITelemetryExperiments {
|
||||
throw notImplemented();
|
||||
}
|
||||
|
||||
getTelemetryInfo(): TPromise<ITelemetryInfo> {
|
||||
return this._proxy.$getTelemetryInfo();
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ import pkg from 'vs/platform/node/package';
|
||||
import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService';
|
||||
import { Workbench, IWorkbenchStartedInfo } from 'vs/workbench/electron-browser/workbench';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { NullTelemetryService, configurationTelemetry, loadExperiments, lifecycleTelemetry } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { NullTelemetryService, configurationTelemetry, lifecycleTelemetry } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { loadExperiments } from 'vs/platform/telemetry/common/experiments';
|
||||
import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc';
|
||||
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
|
||||
import { IdleMonitor, UserStatus } from 'vs/platform/telemetry/browser/idleMonitor';
|
||||
@@ -218,7 +219,7 @@ export class WorkbenchShell {
|
||||
customKeybindingsCount: info.customKeybindingsCount,
|
||||
theme: this.themeService.getColorTheme().id,
|
||||
language: platform.language,
|
||||
experiments: this.telemetryService.getExperiments(),
|
||||
experiments: loadExperiments(),
|
||||
pinnedViewlets: info.pinnedViewlets,
|
||||
restoredViewlet: info.restoredViewlet,
|
||||
restoredEditors: info.restoredEditors.length,
|
||||
@@ -287,8 +288,7 @@ export class WorkbenchShell {
|
||||
const config: ITelemetryServiceConfig = {
|
||||
appender: new TelemetryAppenderClient(channel),
|
||||
commonProperties: resolveWorkbenchCommonProperties(this.storageService, commit, version),
|
||||
piiPaths: [this.environmentService.appRoot, this.environmentService.extensionsPath],
|
||||
experiments: instantiationService.invokeFunction(loadExperiments)
|
||||
piiPaths: [this.environmentService.appRoot, this.environmentService.extensionsPath]
|
||||
};
|
||||
|
||||
const telemetryService = instantiationService.createInstance(TelemetryService, config);
|
||||
@@ -305,7 +305,6 @@ export class WorkbenchShell {
|
||||
|
||||
disposables.push(telemetryService, errorTelemetry, listener, idleMonitor);
|
||||
} else {
|
||||
NullTelemetryService._experiments = instantiationService.invokeFunction(loadExperiments);
|
||||
this.telemetryService = NullTelemetryService;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,13 +72,6 @@ export default () => `
|
||||
<li class="showInteractivePlayground"><button data-href="command:workbench.action.showInteractivePlayground"><h3 class="caption">${escape(localize('welcomePage.interactivePlayground', "Interactive playground"))}</h3> <span class="detail">${escape(localize('welcomePage.interactivePlaygroundDescription', "Try essential editor features out in a short walkthrough"))}</span></button></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section quickLinks">
|
||||
<h2 class="caption">${escape(localize('welcomePage.quickLinks', "Quick links"))}</h2>
|
||||
<ul>
|
||||
<li class="keybindingsReference"><button data-href="command:workbench.action.keybindingsReference"><h3 class="caption">${escape(localize('welcomePage.keybindingsReference', "Keyboard shortcuts reference"))}</h3> <span class="detail">${escape(localize('welcomePage.keybindingsReferenceDescription', "A printable PDF with the most common keyboard shortcuts"))}</span></button></li>
|
||||
<li class="openGlobalSettings"><button data-href="command:workbench.action.openGlobalSettings"><h3 class="caption">${escape(localize('welcomePage.configureSettings', "Configure settings"))}</h3> <span class="detail">${escape(localize('welcomePage.configureSettingsDescription', "Unlock the full power of VS Code by tweaking the settings"))}</span></button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -106,16 +106,6 @@ export class WelcomePageAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
const reorderedQuickLinks = [
|
||||
'showInterfaceOverview',
|
||||
'selectTheme',
|
||||
'showRecommendedKeymapExtensions',
|
||||
'showCommands',
|
||||
'keybindingsReference',
|
||||
'openGlobalSettings',
|
||||
'showInteractivePlayground',
|
||||
];
|
||||
|
||||
interface ExtensionSuggestion {
|
||||
name: string;
|
||||
id: string;
|
||||
@@ -271,24 +261,6 @@ class WelcomePage {
|
||||
});
|
||||
}).then(null, onUnexpectedError);
|
||||
|
||||
const customize = container.querySelector('.commands .section.customize');
|
||||
const learn = container.querySelector('.commands .section.learn');
|
||||
const quickLinks = container.querySelector('.commands .section.quickLinks');
|
||||
if (this.telemetryService.getExperiments().mergeQuickLinks) {
|
||||
const ul = quickLinks.querySelector('ul');
|
||||
reorderedQuickLinks.forEach(clazz => {
|
||||
const link = container.querySelector(`.commands .${clazz}`);
|
||||
if (link) {
|
||||
ul.appendChild(link);
|
||||
}
|
||||
});
|
||||
customize.remove();
|
||||
learn.remove();
|
||||
container.querySelector('.keybindingsReferenceLink').remove();
|
||||
} else {
|
||||
quickLinks.remove();
|
||||
}
|
||||
|
||||
this.addExtensionList(container, '.extensionPackList', extensionPacks, extensionPackStrings);
|
||||
this.addExtensionList(container, '.keymapList', keymapExtensions, keymapStrings);
|
||||
|
||||
|
||||
@@ -11,8 +11,7 @@ import { IWorkspaceContextService, LegacyWorkspace } from 'vs/platform/workspace
|
||||
import { createSyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { ISearchService } from 'vs/platform/search/common/search';
|
||||
import { ITelemetryService, ITelemetryInfo, ITelemetryExperiments } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { defaultExperiments } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import * as minimist from 'minimist';
|
||||
@@ -177,8 +176,4 @@ class TestTelemetryService implements ITelemetryService {
|
||||
machineId: 'someValue.machineId'
|
||||
});
|
||||
}
|
||||
|
||||
public getExperiments(): ITelemetryExperiments {
|
||||
return defaultExperiments;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -12,8 +12,7 @@ import { IWorkspaceContextService, LegacyWorkspace } from 'vs/platform/workspace
|
||||
import { createSyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { ISearchService, IQueryOptions } from 'vs/platform/search/common/search';
|
||||
import { ITelemetryService, ITelemetryInfo, ITelemetryExperiments } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { defaultExperiments } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import * as minimist from 'minimist';
|
||||
@@ -166,8 +165,4 @@ class TestTelemetryService implements ITelemetryService {
|
||||
machineId: 'someValue.machineId'
|
||||
});
|
||||
}
|
||||
|
||||
public getExperiments(): ITelemetryExperiments {
|
||||
return defaultExperiments;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user