diff --git a/src/bootstrap.js b/src/bootstrap.js index 093706562c6..9e8e215ffb6 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -97,9 +97,9 @@ var writable = new stream.Writable({ write: function () { /* No OP */ } }); -process.__defineGetter__('stdout', function() { return writable; }); -process.__defineGetter__('stderr', function() { return writable; }); -process.__defineGetter__('stdin', function() { return writable; }); +// process.__defineGetter__('stdout', function() { return writable; }); +// process.__defineGetter__('stderr', function() { return writable; }); +// process.__defineGetter__('stdin', function() { return writable; }); // Handle uncaught exceptions process.on('uncaughtException', function (err) { diff --git a/src/vs/base/parts/ai/node/ai.app.ts b/src/vs/base/parts/ai/node/ai.app.ts index 7c0d5fbdf34..88ca69db3e4 100644 --- a/src/vs/base/parts/ai/node/ai.app.ts +++ b/src/vs/base/parts/ai/node/ai.app.ts @@ -7,10 +7,10 @@ import {TPromise} from 'vs/base/common/winjs.base'; import {IServer} from 'vs/base/parts/ipc/common/ipc'; -import {AIAdapter} from './aiAdapter'; +import {AppInsightsAppender} from 'vs/platform/telemetry/node/aiAdapter'; import {IAIChannel} from './ai.ipc'; -const adapter: { [handle: number]: AIAdapter } = Object.create(null); +const adapter: { [handle: number]: AppInsightsAppender } = Object.create(null); let idPool = 0; export function registerAIChannel(server: IServer) { @@ -20,7 +20,7 @@ export function registerAIChannel(server: IServer) { case 'create': { let handle = idPool++; let {key, eventPrefix, data} = arg; - adapter[handle] = new AIAdapter(eventPrefix, data, key); + adapter[handle] = new AppInsightsAppender(eventPrefix, data, key); return TPromise.as(handle); } case 'log': { diff --git a/src/vs/code/node/sharedProcessMain.ts b/src/vs/code/node/sharedProcessMain.ts index 85c7ccc35b3..3a4c153d91d 100644 --- a/src/vs/code/node/sharedProcessMain.ts +++ b/src/vs/code/node/sharedProcessMain.ts @@ -19,6 +19,11 @@ import { ExtensionManagementChannel } from 'vs/platform/extensionManagement/comm import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; +import product from 'vs/platform/product'; +import { ITelemetryAppender, combinedAppender, ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; +import { AppInsightsAppender } from 'vs/platform/telemetry/node/aiAdapter'; + function quit(err?: Error) { if (err) { console.error(err); @@ -40,6 +45,23 @@ function setupPlanB(parentPid: number): void { }, 5000); } +const eventPrefix = 'monacoworkbench'; + +function createAppender(): ITelemetryAppender { + const result: ITelemetryAppender[] = []; + const { key, asimovKey } = product.aiConfig; + + 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(); @@ -50,13 +72,31 @@ function main(server: Server): void { const instantiationService = new InstantiationService(services); instantiationService.invokeFunction(accessor => { - const extensionManagementService = accessor.get(IExtensionManagementService); - const channel = new ExtensionManagementChannel(extensionManagementService); - server.registerChannel('extensions', channel); - registerAIChannel(server); + const { appRoot, extensionsPath } = accessor.get(IEnvironmentService); - // eventually clean up old extensions - setTimeout(() => (extensionManagementService as ExtensionManagementService).removeDeprecatedExtensions(), 5000); + const appender = createAppender(); + const config: ITelemetryServiceConfig = { + appender, + commonProperties: TPromise.as({}), //resolveCommonProperties(storageService, contextService), + piiPaths: [appRoot, extensionsPath] + }; + + const services = new ServiceCollection(); + services.set(ITelemetryService, new SyncDescriptor(TelemetryService, config)); + const instantiationService2 = instantiationService.createChild(services); + + instantiationService2.invokeFunction(accessor => { + const telemetryService = accessor.get(ITelemetryService); + console.log(telemetryService); + + 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/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 755271153d5..40935bf8bac 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -10,6 +10,7 @@ export const IEnvironmentService = createDecorator('environ export interface IEnvironmentService { serviceId: ServiceIdentifier; + appRoot: string; userDataPath: string; extensionsPath: string; } diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index fb68bdd0beb..d8a767f0705 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -10,11 +10,15 @@ import pkg from 'vs/platform/package'; import * as os from 'os'; import * as path from 'path'; import { parseArgs } from 'vs/code/node/argv'; +import URI from 'vs/base/common/uri'; export class EnvironmentService implements IEnvironmentService { serviceId = IEnvironmentService; + private _appRoot: string; + get appRoot(): string { return this._appRoot; } + private _userDataPath: string; get userDataPath(): string { return this._userDataPath; } @@ -24,6 +28,7 @@ export class EnvironmentService implements IEnvironmentService { constructor() { const argv = parseArgs(process.argv); + this._appRoot = path.dirname(URI.parse(require.toUrl('')).fsPath); this._userDataPath = paths.getUserDataPath(process.platform, pkg.name, process.argv); const userHome = path.join(os.homedir(), product.dataFolderName); diff --git a/src/vs/platform/telemetry/common/telemetryIpc.ts b/src/vs/platform/telemetry/common/telemetryIpc.ts new file mode 100644 index 00000000000..84c0cc5fcb9 --- /dev/null +++ b/src/vs/platform/telemetry/common/telemetryIpc.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; +import { ITelemetryAppender } from './telemetry'; + +export interface ITelemetryLog { + eventName: string; + data?: any; +} + +export interface ITelemetryAppenderChannel extends IChannel { + call(command: 'log', data: ITelemetryLog): TPromise; + call(command: string, arg: any): TPromise; +} + +export class TelemetryAppenderChannel implements ITelemetryAppenderChannel { + + constructor(private appender: ITelemetryAppender) { } + + call(command: string, { eventName, data }: ITelemetryLog): TPromise { + return this.appender.log(eventName, data); + } +} + +export class TelemetryAppenderClient implements ITelemetryAppender { + + constructor(private channel: ITelemetryAppenderChannel) { } + + log(eventName: string, data?: any): any { + return this.channel.call('log', { eventName, data }); + } + + dispose(): any { + // TODO + } +} \ No newline at end of file diff --git a/src/vs/base/parts/ai/node/aiAdapter.ts b/src/vs/platform/telemetry/node/aiAdapter.ts similarity index 73% rename from src/vs/base/parts/ai/node/aiAdapter.ts rename to src/vs/platform/telemetry/node/aiAdapter.ts index 1060c2004cf..5cbcb695930 100644 --- a/src/vs/base/parts/ai/node/aiAdapter.ts +++ b/src/vs/platform/telemetry/node/aiAdapter.ts @@ -8,36 +8,34 @@ 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'; -namespace AI { +let _initialized = false; - 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); - 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; - } + _initialized = true; } +} - export function getClient(aiKey: string): typeof appInsights.client { +function getClient(aiKey: string): typeof appInsights.client { - ensureAIEngineIsInitialized(); + 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; + 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 { @@ -48,7 +46,7 @@ interface Measurements { [key: string]: number; } -export class AIAdapter { +export class AppInsightsAppender implements ITelemetryAppender { private _aiClient: typeof appInsights.client; @@ -62,7 +60,7 @@ export class AIAdapter { } if (typeof aiKeyOrClientFactory === 'string') { - this._aiClient = AI.getClient(aiKeyOrClientFactory); + this._aiClient = getClient(aiKeyOrClientFactory); } else if (typeof aiKeyOrClientFactory === 'function') { this._aiClient = aiKeyOrClientFactory(); } @@ -74,7 +72,7 @@ export class AIAdapter { const measurements: Measurements = Object.create(null); const flat = Object.create(null); - AIAdapter._flaten(data, flat); + AppInsightsAppender._flaten(data, flat); for (let prop in flat) { // enforce property names less than 150 char, take the last 150 char @@ -120,7 +118,7 @@ export class AIAdapter { } else if (isObject(value)) { if (order < 2) { - AIAdapter._flaten(value, result, order + 1, index + '.'); + AppInsightsAppender._flaten(value, result, order + 1, index + '.'); } else { result[index] = safeStringify(value); } @@ -135,7 +133,7 @@ export class AIAdapter { return; } data = mixin(data, this._defaultData); - let {properties, measurements} = AIAdapter._getData(data); + let {properties, measurements} = AppInsightsAppender._getData(data); this._aiClient.trackEvent(this._eventPrefix + '/' + eventName, properties, measurements); } diff --git a/src/vs/base/test/node/aiAdapter/aiAdapter.test.ts b/src/vs/platform/telemetry/test/node/aiAdapter.test.ts similarity index 93% rename from src/vs/base/test/node/aiAdapter/aiAdapter.test.ts rename to src/vs/platform/telemetry/test/node/aiAdapter.test.ts index e0cb1917f10..0de39762840 100644 --- a/src/vs/base/test/node/aiAdapter/aiAdapter.test.ts +++ b/src/vs/platform/telemetry/test/node/aiAdapter.test.ts @@ -5,7 +5,7 @@ 'use strict'; import * as assert from 'assert'; -import {AIAdapter} from 'vs/base/parts/ai/node/aiAdapter'; +import {AppInsightsAppender} from 'vs/platform/telemetry/node/aiAdapter'; interface IAppInsightsEvent { eventName: string; @@ -41,12 +41,12 @@ class AppInsightsMock { suite('AIAdapter', () => { var appInsightsMock: AppInsightsMock; - var adapter: AIAdapter; + var adapter: AppInsightsAppender; var prefix = 'prefix'; setup(() => { appInsightsMock = new AppInsightsMock(); - adapter = new AIAdapter(prefix, undefined, () => appInsightsMock); + adapter = new AppInsightsAppender(prefix, undefined, () => appInsightsMock); }); teardown(() => { @@ -61,7 +61,7 @@ suite('AIAdapter', () => { }); test('addional data', () => { - adapter = new AIAdapter(prefix, { first: '1st', second: 2, third: true }, () => appInsightsMock); + adapter = new AppInsightsAppender(prefix, { first: '1st', second: 2, third: true }, () => appInsightsMock); adapter.log('testEvent'); assert.equal(appInsightsMock.events.length, 1);