From 9a543b59a4040fc874c88730254f3ef3cdec079a Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 8 Jun 2016 15:21:39 +0200 Subject: [PATCH] isolate telemetry in vs/platform/telemetry --- src/vs/base/parts/ai/node/ai.app.ts | 51 ------ src/vs/base/parts/ai/node/ai.ipc.ts | 16 -- src/vs/base/parts/ai/node/ai.ts | 53 ------ src/vs/code/node/sharedProcessMain.ts | 49 +++--- src/vs/platform/telemetry/common/telemetry.ts | 11 +- .../platform/telemetry/common/telemetryIpc.ts | 3 +- src/vs/platform/telemetry/node/aiAdapter.ts | 151 ------------------ .../telemetry/node/appInsightsAppender.ts | 149 +++++++++++++++-- ...er.test.ts => appInsightsAppender.test.ts} | 2 +- src/vs/workbench/electron-browser/shell.ts | 40 ++--- .../debug/electron-browser/debugService.ts | 20 ++- .../parts/debug/node/rawDebugSession.ts | 5 +- 12 files changed, 196 insertions(+), 354 deletions(-) delete mode 100644 src/vs/base/parts/ai/node/ai.app.ts delete mode 100644 src/vs/base/parts/ai/node/ai.ipc.ts delete mode 100644 src/vs/base/parts/ai/node/ai.ts delete mode 100644 src/vs/platform/telemetry/node/aiAdapter.ts rename src/vs/platform/telemetry/test/node/{aiAdapter.test.ts => appInsightsAppender.test.ts} (98%) diff --git a/src/vs/base/parts/ai/node/ai.app.ts b/src/vs/base/parts/ai/node/ai.app.ts deleted file mode 100644 index 88ca69db3e4..00000000000 --- a/src/vs/base/parts/ai/node/ai.app.ts +++ /dev/null @@ -1,51 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 {TPromise} from 'vs/base/common/winjs.base'; -import {IServer} from 'vs/base/parts/ipc/common/ipc'; -import {AppInsightsAppender} from 'vs/platform/telemetry/node/aiAdapter'; -import {IAIChannel} from './ai.ipc'; - -const adapter: { [handle: number]: AppInsightsAppender } = Object.create(null); -let idPool = 0; - -export function registerAIChannel(server: IServer) { - server.registerChannel('ai', { - call(command: string, arg: any): TPromise { - switch (command) { - case 'create': { - let handle = idPool++; - let {key, eventPrefix, data} = arg; - adapter[handle] = new AppInsightsAppender(eventPrefix, data, key); - return TPromise.as(handle); - } - case 'log': { - let {handle, eventName, data} = arg; - adapter[handle].log(eventName, data); - return TPromise.as(undefined); - } - case 'dispose': { - let {handle} = arg; - adapter[handle].dispose(); - delete adapter[handle]; - return TPromise.as(undefined); - } - } - } - }); -} - -// It is important to dispose the AI adapter properly because -// only then they flush remaining data. -process.on('SIGTERM', function () { - let promises: TPromise[] = []; - for (let handle in adapter) { - let ai = adapter[handle]; - promises.push(ai.dispose()); - } - TPromise.join(promises).then(_ => process.exit(0)); -}); diff --git a/src/vs/base/parts/ai/node/ai.ipc.ts b/src/vs/base/parts/ai/node/ai.ipc.ts deleted file mode 100644 index bb76d301788..00000000000 --- a/src/vs/base/parts/ai/node/ai.ipc.ts +++ /dev/null @@ -1,16 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 {TPromise} from 'vs/base/common/winjs.base'; -import {IChannel} from 'vs/base/parts/ipc/common/ipc'; - -export interface IAIChannel extends IChannel { - call(command: 'create', data: { key: string; eventPrefix: string; data: { [k: string]: any }; }): TPromise; - call(command: 'log', data: { handle: number; eventName: string; data: { [k: string]: any }; }): TPromise; - call(command: 'dispose', data: { handle: number; }): TPromise; - call(command: string, arg: any): TPromise; -} diff --git a/src/vs/base/parts/ai/node/ai.ts b/src/vs/base/parts/ai/node/ai.ts deleted file mode 100644 index ad0a74ebaa8..00000000000 --- a/src/vs/base/parts/ai/node/ai.ts +++ /dev/null @@ -1,53 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 {IAIChannel} from './ai.ipc'; -import {TPromise} from 'vs/base/common/winjs.base'; -import {connect} from 'vs/base/parts/ipc/node/ipc.net'; - -export interface IAIAdapter { - log(eventName: string, data?: any): void; - dispose(): void; -} - -export function createAIAdapter(key: string, eventPrefix: string, data: { [key: string]: any }): IAIAdapter { - - let beforeReadyMessages: { type: string; args: any }[] = []; - let handle: number = undefined; - let channel: IAIChannel = { - call(type: string, args: any): TPromise { - beforeReadyMessages.push({ type, args }); - return TPromise.as(void 0); - } - }; - - connect(process.env['VSCODE_SHARED_IPC_HOOK']).then(client => client.getChannel('ai')).then(actualChannel => { - - return actualChannel.call('create', { key, eventPrefix, data }).then(actualHandle => { - // channel has been created, store handle etc, - // and flush all early messages - handle = actualHandle; - channel = actualChannel; - for (let m of beforeReadyMessages) { - let {type, args} = m; - args.handle = handle; - channel.call(type, args); - } - beforeReadyMessages.length = 0; - }); - }); - - return { - log(eventName: string, data?: any) { - channel.call('log', { handle, eventName, data }); - }, - dispose() { - channel.call('dispose', { handle }); - } - }; - -} diff --git a/src/vs/code/node/sharedProcessMain.ts b/src/vs/code/node/sharedProcessMain.ts index 1e41a779866..92f68487586 100644 --- a/src/vs/code/node/sharedProcessMain.ts +++ b/src/vs/code/node/sharedProcessMain.ts @@ -7,7 +7,6 @@ import * as fs from 'fs'; import * as platform from 'vs/base/common/platform'; import { serve, Server, connect } from 'vs/base/parts/ipc/node/ipc.net'; import { TPromise } from 'vs/base/common/winjs.base'; -import { registerAIChannel } from 'vs/base/parts/ai/node/ai.app'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; @@ -22,9 +21,10 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { NodeConfigurationService } from 'vs/platform/configuration/node/nodeConfigurationService'; import product from 'vs/platform/product'; -import { ITelemetryAppender, combinedAppender, ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ITelemetryAppender, ITelemetryService, combinedAppender } from 'vs/platform/telemetry/common/telemetry'; +import { TelemetryAppenderChannel } from 'vs/platform/telemetry/common/telemetryIpc'; import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; -import { AppInsightsAppender } from 'vs/platform/telemetry/node/aiAdapter'; +import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender'; function quit(err?: Error) { if (err) { @@ -49,21 +49,6 @@ function setupPlanB(parentPid: number): void { const eventPrefix = 'monacoworkbench'; -function createAppender(): ITelemetryAppender { - const result: ITelemetryAppender[] = []; - const { key, asimovKey } = product.aiConfig || { key: null, asimovKey: null }; - - if (key) { - result.push(new AppInsightsAppender(eventPrefix, null, key)); - } - - if (asimovKey) { - result.push(new AppInsightsAppender(eventPrefix, null, asimovKey)); - } - - return combinedAppender(...result); -} - function main(server: Server): void { const services = new ServiceCollection(); @@ -75,12 +60,30 @@ function main(server: Server): void { const instantiationService = new InstantiationService(services); instantiationService.invokeFunction(accessor => { - const { appRoot, extensionsPath } = accessor.get(IEnvironmentService); + const aiAppenders: AppInsightsAppender[] = []; - const appender = createAppender(); + if (product.aiConfig && product.aiConfig.key) { + aiAppenders.push(new AppInsightsAppender(eventPrefix, null, product.aiConfig.key)); + } + + if (product.aiConfig && product.aiConfig.asimovKey) { + aiAppenders.push(new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey)); + } + + const appenders: ITelemetryAppender[] = aiAppenders.slice(); + appenders.push({ log: (e,d) => console.log(`Telemetry event: ${ e }\n${ JSON.stringify(d) }`) }); + + // It is important to dispose the AI adapter properly because + // only then they flush remaining data. + process.once('exit', () => aiAppenders.forEach(a => a.dispose())); + + const appender = combinedAppender(...appenders); + server.registerChannel('telemetryAppender', new TelemetryAppenderChannel(appender)); + + const { appRoot, extensionsPath } = accessor.get(IEnvironmentService); const config: ITelemetryServiceConfig = { appender, - commonProperties: TPromise.as({}), //resolveCommonProperties(storageService, contextService), + commonProperties: TPromise.as({}), piiPaths: [appRoot, extensionsPath] }; @@ -89,13 +92,11 @@ function main(server: Server): void { const instantiationService2 = instantiationService.createChild(services); instantiationService2.invokeFunction(accessor => { - const telemetryService = accessor.get(ITelemetryService); - console.log(telemetryService); + // const telemetryService = accessor.get(ITelemetryService); const extensionManagementService = accessor.get(IExtensionManagementService); const channel = new ExtensionManagementChannel(extensionManagementService); server.registerChannel('extensions', channel); - registerAIChannel(server); // eventually clean up old extensions setTimeout(() => (extensionManagementService as ExtensionManagementService).removeDeprecatedExtensions(), 5000); diff --git a/src/vs/platform/telemetry/common/telemetry.ts b/src/vs/platform/telemetry/common/telemetry.ts index f15dbaf2831..67db50e2266 100644 --- a/src/vs/platform/telemetry/common/telemetry.ts +++ b/src/vs/platform/telemetry/common/telemetry.ts @@ -5,7 +5,6 @@ 'use strict'; import {TPromise} from 'vs/base/common/winjs.base'; -import {IDisposable} from 'vs/base/common/lifecycle'; import {ITimerEvent, nullEvent} from 'vs/base/common/timer'; import {createDecorator, ServiceIdentifier} from 'vs/platform/instantiation/common/instantiation'; @@ -51,17 +50,15 @@ export const NullTelemetryService: ITelemetryService = { } }; -export interface ITelemetryAppender extends IDisposable { - log(eventName: string, data?: any): any; +export interface ITelemetryAppender { + log(eventName: string, data: any): void; } export function combinedAppender(...appenders: ITelemetryAppender[]): ITelemetryAppender { - const log = (e, d) => appenders.forEach(a => a.log(e, d)); - const dispose = () => appenders.forEach(a => a.dispose()); - return { log, dispose }; + return { log: (e, d) => appenders.forEach(a => a.log(e,d)) }; } -export const NullAppender = combinedAppender(); +export const NullAppender: ITelemetryAppender = { log: () => null }; // --- util diff --git a/src/vs/platform/telemetry/common/telemetryIpc.ts b/src/vs/platform/telemetry/common/telemetryIpc.ts index 84c0cc5fcb9..7cf10b99e70 100644 --- a/src/vs/platform/telemetry/common/telemetryIpc.ts +++ b/src/vs/platform/telemetry/common/telemetryIpc.ts @@ -24,7 +24,8 @@ export class TelemetryAppenderChannel implements ITelemetryAppenderChannel { constructor(private appender: ITelemetryAppender) { } call(command: string, { eventName, data }: ITelemetryLog): TPromise { - return this.appender.log(eventName, data); + this.appender.log(eventName, data); + return TPromise.as(null); } } diff --git a/src/vs/platform/telemetry/node/aiAdapter.ts b/src/vs/platform/telemetry/node/aiAdapter.ts deleted file mode 100644 index 5cbcb695930..00000000000 --- a/src/vs/platform/telemetry/node/aiAdapter.ts +++ /dev/null @@ -1,151 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 * as appInsights from 'applicationinsights'; -import {isObject} from 'vs/base/common/types'; -import {safeStringify, mixin} from 'vs/base/common/objects'; -import {TPromise} from 'vs/base/common/winjs.base'; -import {ITelemetryAppender} from '../common/telemetry'; - -let _initialized = false; - -function ensureAIEngineIsInitialized(): void { - if (_initialized === false) { - // we need to pass some fake key, otherwise AI throws an exception - appInsights.setup('2588e01f-f6c9-4cd6-a348-143741f8d702') - .setAutoCollectConsole(false) - .setAutoCollectExceptions(false) - .setAutoCollectPerformance(false) - .setAutoCollectRequests(false); - - _initialized = true; - } -} - -function getClient(aiKey: string): typeof appInsights.client { - - ensureAIEngineIsInitialized(); - - const client = appInsights.getClient(aiKey); - client.channel.setOfflineMode(true); - client.context.tags[client.context.keys.deviceMachineName] = ''; //prevent App Insights from reporting machine name - if (aiKey.indexOf('AIF-') === 0) { - client.config.endpointUrl = 'https://vortex.data.microsoft.com/collect/v1'; - } - return client; -} - -interface Properties { - [key: string]: string; -} - -interface Measurements { - [key: string]: number; -} - -export class AppInsightsAppender implements ITelemetryAppender { - - private _aiClient: typeof appInsights.client; - - constructor( - private _eventPrefix: string, - private _defaultData: { [key: string]: any }, - aiKeyOrClientFactory: string | (() => typeof appInsights.client) // allow factory function for testing - ) { - if (!this._defaultData) { - this._defaultData = Object.create(null); - } - - if (typeof aiKeyOrClientFactory === 'string') { - this._aiClient = getClient(aiKeyOrClientFactory); - } else if (typeof aiKeyOrClientFactory === 'function') { - this._aiClient = aiKeyOrClientFactory(); - } - } - - private static _getData(data?: any): { properties: Properties, measurements: Measurements } { - - const properties: Properties = Object.create(null); - const measurements: Measurements = Object.create(null); - - const flat = Object.create(null); - AppInsightsAppender._flaten(data, flat); - - for (let prop in flat) { - // enforce property names less than 150 char, take the last 150 char - prop = prop.length > 150 ? prop.substr(prop.length - 149) : prop; - var value = flat[prop]; - - if (typeof value === 'number') { - measurements[prop] = value; - - } else if (typeof value === 'boolean') { - measurements[prop] = value ? 1 : 0; - - } else if (typeof value === 'string') { - //enforce property value to be less than 1024 char, take the first 1024 char - properties[prop] = value.substring(0, 1023); - - } else if (typeof value !== 'undefined' && value !== null) { - properties[prop] = value; - } - } - - return { - properties, - measurements - }; - } - - private static _flaten(obj: any, result: {[key: string]: any }, order: number = 0, prefix?: string): void { - if (!obj) { - return; - } - - for(var item of Object.getOwnPropertyNames(obj)){ - const value = obj[item]; - const index = prefix ? prefix + item : item; - - if (Array.isArray(value)) { - result[index] = safeStringify(value); - - } else if (value instanceof Date) { - // TODO unsure why this is here and not in _getData - result[index] = value.toISOString(); - - } else if (isObject(value)) { - if (order < 2) { - AppInsightsAppender._flaten(value, result, order + 1, index + '.'); - } else { - result[index] = safeStringify(value); - } - } else { - result[index] = value; - } - } - } - - public log(eventName: string, data?: any): void { - if (!this._aiClient) { - return; - } - data = mixin(data, this._defaultData); - let {properties, measurements} = AppInsightsAppender._getData(data); - this._aiClient.trackEvent(this._eventPrefix + '/' + eventName, properties, measurements); - } - - public dispose(): TPromise { - if (this._aiClient) { - return new TPromise(resolve => { - this._aiClient.sendPendingData(() => { - // all data flushed - this._aiClient = undefined; - resolve(void 0); - }); - }); - } - } -} \ No newline at end of file diff --git a/src/vs/platform/telemetry/node/appInsightsAppender.ts b/src/vs/platform/telemetry/node/appInsightsAppender.ts index 5471278d182..5ecc36ab459 100644 --- a/src/vs/platform/telemetry/node/appInsightsAppender.ts +++ b/src/vs/platform/telemetry/node/appInsightsAppender.ts @@ -4,23 +4,148 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import {IEnvironment} from 'vs/platform/workspace/common/workspace'; -import {ITelemetryAppender, combinedAppender} from 'vs/platform/telemetry/common/telemetry'; -import {createAIAdapter} from 'vs/base/parts/ai/node/ai'; +import * as appInsights from 'applicationinsights'; +import {isObject} from 'vs/base/common/types'; +import {safeStringify, mixin} from 'vs/base/common/objects'; +import {TPromise} from 'vs/base/common/winjs.base'; +import {ITelemetryAppender} from '../common/telemetry'; -const eventPrefix = 'monacoworkbench'; +let _initialized = false; -export function createAppender(env: IEnvironment): ITelemetryAppender { - const result: ITelemetryAppender[] = []; - const { key, asimovKey } = env.aiConfig; +function ensureAIEngineIsInitialized(): void { + if (_initialized === false) { + // we need to pass some fake key, otherwise AI throws an exception + appInsights.setup('2588e01f-f6c9-4cd6-a348-143741f8d702') + .setAutoCollectConsole(false) + .setAutoCollectExceptions(false) + .setAutoCollectPerformance(false) + .setAutoCollectRequests(false); - if (key) { - result.push(createAIAdapter(key, eventPrefix, undefined)); + _initialized = true; } +} + +function getClient(aiKey: string): typeof appInsights.client { - if (asimovKey) { - result.push(createAIAdapter(asimovKey, eventPrefix, undefined)); + ensureAIEngineIsInitialized(); + + const client = appInsights.getClient(aiKey); + client.channel.setOfflineMode(true); + client.context.tags[client.context.keys.deviceMachineName] = ''; //prevent App Insights from reporting machine name + if (aiKey.indexOf('AIF-') === 0) { + client.config.endpointUrl = 'https://vortex.data.microsoft.com/collect/v1'; } + return client; +} - return combinedAppender(...result); +interface Properties { + [key: string]: string; } + +interface Measurements { + [key: string]: number; +} + +export class AppInsightsAppender implements ITelemetryAppender { + + private _aiClient: typeof appInsights.client; + + constructor( + private _eventPrefix: string, + private _defaultData: { [key: string]: any }, + aiKeyOrClientFactory: string | (() => typeof appInsights.client) // allow factory function for testing + ) { + if (!this._defaultData) { + this._defaultData = Object.create(null); + } + + if (typeof aiKeyOrClientFactory === 'string') { + this._aiClient = getClient(aiKeyOrClientFactory); + } else if (typeof aiKeyOrClientFactory === 'function') { + this._aiClient = aiKeyOrClientFactory(); + } + } + + private static _getData(data?: any): { properties: Properties, measurements: Measurements } { + + const properties: Properties = Object.create(null); + const measurements: Measurements = Object.create(null); + + const flat = Object.create(null); + AppInsightsAppender._flaten(data, flat); + + for (let prop in flat) { + // enforce property names less than 150 char, take the last 150 char + prop = prop.length > 150 ? prop.substr(prop.length - 149) : prop; + var value = flat[prop]; + + if (typeof value === 'number') { + measurements[prop] = value; + + } else if (typeof value === 'boolean') { + measurements[prop] = value ? 1 : 0; + + } else if (typeof value === 'string') { + //enforce property value to be less than 1024 char, take the first 1024 char + properties[prop] = value.substring(0, 1023); + + } else if (typeof value !== 'undefined' && value !== null) { + properties[prop] = value; + } + } + + return { + properties, + measurements + }; + } + + private static _flaten(obj: any, result: {[key: string]: any }, order: number = 0, prefix?: string): void { + if (!obj) { + return; + } + + for(var item of Object.getOwnPropertyNames(obj)){ + const value = obj[item]; + const index = prefix ? prefix + item : item; + + if (Array.isArray(value)) { + result[index] = safeStringify(value); + + } else if (value instanceof Date) { + // TODO unsure why this is here and not in _getData + result[index] = value.toISOString(); + + } else if (isObject(value)) { + if (order < 2) { + AppInsightsAppender._flaten(value, result, order + 1, index + '.'); + } else { + result[index] = safeStringify(value); + } + } else { + result[index] = value; + } + } + } + + log(eventName: string, data?: any): void { + if (!this._aiClient) { + return; + } + data = mixin(data, this._defaultData); + let {properties, measurements} = AppInsightsAppender._getData(data); + this._aiClient.trackEvent(this._eventPrefix + '/' + eventName, properties, measurements); + } + + dispose(): TPromise { + if (this._aiClient) { + return new TPromise(resolve => { + this._aiClient.sendPendingData(() => { + // all data flushed + this._aiClient = undefined; + resolve(void 0); + }); + }); + } + } +} \ No newline at end of file diff --git a/src/vs/platform/telemetry/test/node/aiAdapter.test.ts b/src/vs/platform/telemetry/test/node/appInsightsAppender.test.ts similarity index 98% rename from src/vs/platform/telemetry/test/node/aiAdapter.test.ts rename to src/vs/platform/telemetry/test/node/appInsightsAppender.test.ts index 0de39762840..6929fa1f840 100644 --- a/src/vs/platform/telemetry/test/node/aiAdapter.test.ts +++ b/src/vs/platform/telemetry/test/node/appInsightsAppender.test.ts @@ -5,7 +5,7 @@ 'use strict'; import * as assert from 'assert'; -import {AppInsightsAppender} from 'vs/platform/telemetry/node/aiAdapter'; +import {AppInsightsAppender} from 'vs/platform/telemetry/node/appInsightsAppender'; interface IAppInsightsEvent { eventName: string; diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index 21f3e6551c9..d062c333d85 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -20,10 +20,10 @@ import timer = require('vs/base/common/timer'); import {Workbench} from 'vs/workbench/browser/workbench'; import {Storage, inMemoryLocalStorageInstance} from 'vs/workbench/common/storage'; import {ITelemetryService, NullTelemetryService} from 'vs/platform/telemetry/common/telemetry'; +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'; import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry'; -import {createAppender} from 'vs/platform/telemetry/node/appInsightsAppender'; import {resolveCommonProperties} from 'vs/platform/telemetry/node/commonProperties'; import {ElectronIntegration} from 'vs/workbench/electron-browser/integration'; import {Update} from 'vs/workbench/electron-browser/update'; @@ -48,7 +48,6 @@ import {IOptions} from 'vs/workbench/common/options'; import {IStorageService} from 'vs/platform/storage/common/storage'; import {ServiceCollection} from 'vs/platform/instantiation/common/serviceCollection'; import {InstantiationService} from 'vs/platform/instantiation/common/instantiationService'; -import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; import {IContextViewService} from 'vs/platform/contextview/browser/contextView'; import {IEventService} from 'vs/platform/event/common/event'; import {IFileService} from 'vs/platform/files/common/files'; @@ -197,6 +196,16 @@ export class WorkbenchShell { } private initServiceCollection(): [InstantiationService, ServiceCollection] { + const sharedProcess = connect(process.env['VSCODE_SHARED_IPC_HOOK']); + sharedProcess.done(service => { + service.onClose(() => { + this.messageService.show(Severity.Error, { + message: nls.localize('sharedProcessCrashed', "The shared process terminated unexpectedly. Please reload the window to recover."), + actions: [instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL)] + }); + }); + }, errors.onUnexpectedError); + const serviceCollection = new ServiceCollection(); serviceCollection.set(IEventService, this.eventService); serviceCollection.set(IWorkspaceContextService, this.contextService); @@ -215,9 +224,9 @@ export class WorkbenchShell { // Telemetry if (this.configuration.env.isBuilt && !this.configuration.env.extensionDevelopmentPath && !!this.configuration.env.enableTelemetry) { - const appender = createAppender(this.configuration.env); + const channel = getDelayedChannel(sharedProcess.then(c => c.getChannel('telemetryAppender'))); const config: ITelemetryServiceConfig = { - appender, + appender: new TelemetryAppenderClient(channel), commonProperties: resolveCommonProperties(this.storageService, this.contextService), piiPaths: [this.configuration.env.appRoot, this.configuration.env.userExtensionsHome] }; @@ -234,7 +243,7 @@ export class WorkbenchShell { : TelemetryService.IDLE_START_EVENT_NAME )); - disposables.add(telemetryService, errorTelemetry, listener, idleMonitor, appender); + disposables.add(telemetryService, errorTelemetry, listener, idleMonitor); } else { this.telemetryService = NullTelemetryService; } @@ -287,30 +296,13 @@ export class WorkbenchShell { let codeEditorService = instantiationService.createInstance(CodeEditorServiceImpl); serviceCollection.set(ICodeEditorService, codeEditorService); - let extensionManagementChannelClient = instantiationService.createInstance(ExtensionManagementChannelClient, this.initSharedProcessChannel(instantiationService, this.messageService)); + const extensionManagementChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('extensions'))); + const extensionManagementChannelClient = instantiationService.createInstance(ExtensionManagementChannelClient, extensionManagementChannel); serviceCollection.set(IExtensionManagementService, extensionManagementChannelClient); return [instantiationService, serviceCollection]; } - private initSharedProcessChannel(instantiationService: IInstantiationService, messageService: IMessageService): IExtensionManagementChannel { - const sharedProcessClientPromise = connect(process.env['VSCODE_SHARED_IPC_HOOK']); - - sharedProcessClientPromise.done(service => { - service.onClose(() => { - messageService.show(Severity.Error, { - message: nls.localize('sharedProcessCrashed', "The shared process terminated unexpectedly. Please reload the window to recover."), - actions: [instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL)] - }); - }); - }, errors.onUnexpectedError); - - const extensionManagementChannelPromise = sharedProcessClientPromise - .then(client => client.getChannel('extensions')); - - return getDelayedChannel(extensionManagementChannelPromise); - } - public open(): void { // Listen on unexpected errors diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 2266f06a882..f22406a2551 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -17,7 +17,6 @@ import errors = require('vs/base/common/errors'); import severity from 'vs/base/common/severity'; import { TPromise } from 'vs/base/common/winjs.base'; import aria = require('vs/base/browser/ui/aria/aria'); -import { IAIAdapter, createAIAdapter } from 'vs/base/parts/ai/node/ai'; import editorbrowser = require('vs/editor/browser/editorBrowser'); import { IKeybindingService, IKeybindingContextKey } from 'vs/platform/keybinding/common/keybindingService'; import {IMarkerService} from 'vs/platform/markers/common/markers'; @@ -28,6 +27,8 @@ import { IFileService, FileChangesEvent, FileChangeType, EventType } from 'vs/pl import { IEventService } from 'vs/platform/event/common/event'; import { IMessageService, CloseAction } from 'vs/platform/message/common/message'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; +import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import wbeditorcommon = require('vs/workbench/common/editor'); @@ -68,7 +69,7 @@ export class DebugService implements debug.IDebugService { private model: model.Model; private viewModel: viewmodel.ViewModel; private configurationManager: ConfigurationManager; - private telemetryAdapter: IAIAdapter; + private customTelemetryService: ITelemetryService; private lastTaskEvent: TaskEvent; private toDispose: lifecycle.IDisposable[]; private toDisposeOnSessionEnd: lifecycle.IDisposable[]; @@ -292,8 +293,8 @@ export class DebugService implements debug.IDebugService { if (event.body && event.body.category === 'telemetry') { // only log telemetry events from debug adapter if the adapter provided the telemetry key // and the user opted in telemetry - if (this.telemetryAdapter && this.telemetryService.isOptedIn) { - this.telemetryAdapter.log(event.body.output, event.body.data); + if (this.customTelemetryService && this.telemetryService.isOptedIn) { + this.customTelemetryService.publicLog(event.body.output, event.body.data); } } else if (event.body && typeof event.body.output === 'string' && event.body.output.length > 0) { this.onOutput(event); @@ -561,11 +562,12 @@ export class DebugService implements debug.IDebugService { telemetryInfo['common.vscodesessionid'] = info.sessionId; return telemetryInfo; }).then(data => { - let {aiKey, type} = this.configurationManager.adapter; - this.telemetryAdapter = createAIAdapter(aiKey, type, data); + const { aiKey, type } = this.configurationManager.adapter; + const appender = new AppInsightsAppender(type, data, aiKey); + this.customTelemetryService = new TelemetryService({ appender }, this.configurationService); }); - this.session = this.instantiationService.createInstance(session.RawDebugSession, configuration.debugServer, this.configurationManager.adapter, this.telemetryAdapter); + this.session = this.instantiationService.createInstance(session.RawDebugSession, configuration.debugServer, this.configurationManager.adapter, this.customTelemetryService); this.registerSessionListeners(); return this.session.initialize({ @@ -740,10 +742,6 @@ export class DebugService implements debug.IDebugService { }); this.model.updateBreakpoints(data); - if (this.telemetryAdapter) { - this.telemetryAdapter.dispose(); - this.telemetryAdapter = null; - } this.inDebugMode.reset(); } diff --git a/src/vs/workbench/parts/debug/node/rawDebugSession.ts b/src/vs/workbench/parts/debug/node/rawDebugSession.ts index bd37c05f9f0..f09a9e0b5cb 100644 --- a/src/vs/workbench/parts/debug/node/rawDebugSession.ts +++ b/src/vs/workbench/parts/debug/node/rawDebugSession.ts @@ -13,7 +13,6 @@ import { Action } from 'vs/base/common/actions'; import errors = require('vs/base/common/errors'); import { TPromise } from 'vs/base/common/winjs.base'; import severity from 'vs/base/common/severity'; -import { IAIAdapter } from 'vs/base/parts/ai/node/ai'; import stdfork = require('vs/base/node/stdFork'); import { IMessageService, CloseAction } from 'vs/platform/message/common/message'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -66,7 +65,7 @@ export class RawDebugSession extends v8.V8Protocol implements debug.IRawDebugSes constructor( private debugServerPort: number, private adapter: Adapter, - private telemtryAdapter: IAIAdapter, + private customTelemetryService: ITelemetryService, @IMessageService private messageService: IMessageService, @ITelemetryService private telemetryService: ITelemetryService, @IOutputService private outputService: IOutputService @@ -146,7 +145,7 @@ export class RawDebugSession extends v8.V8Protocol implements debug.IRawDebugSes const message = error ? debug.formatPII(error.format, false, error.variables) : errorResponse.message; if (error && error.sendTelemetry) { this.telemetryService.publicLog('debugProtocolErrorResponse', { error: message }); - this.telemtryAdapter.log('debugProtocolErrorResponse', { error: message }); + this.customTelemetryService.publicLog('debugProtocolErrorResponse', { error: message }); } if (error && error.showUser === false) { return TPromise.as(null); -- GitLab