diff --git a/src/vs/platform/telemetry/test/common/telemetryLogAppender.test.ts b/src/vs/platform/telemetry/test/common/telemetryLogAppender.test.ts index e39dd7c0576..b1cb552a339 100644 --- a/src/vs/platform/telemetry/test/common/telemetryLogAppender.test.ts +++ b/src/vs/platform/telemetry/test/common/telemetryLogAppender.test.ts @@ -58,7 +58,7 @@ class TestTelemetryLogger extends AbstractLogger implements ILogger { flush(): void { } } -class TestTelemetryLoggerService implements ILoggerService { +export class TestTelemetryLoggerService implements ILoggerService { _serviceBrand: undefined; logger?: TestTelemetryLogger; diff --git a/src/vs/workbench/api/common/extHostTelemetry.ts b/src/vs/workbench/api/common/extHostTelemetry.ts index b0095f90a14..809d8e2d5a8 100644 --- a/src/vs/workbench/api/common/extHostTelemetry.ts +++ b/src/vs/workbench/api/common/extHostTelemetry.ts @@ -34,7 +34,6 @@ export class ExtHostTelemetry implements ExtHostTelemetryShape { @IExtHostInitDataService private readonly initData: IExtHostInitDataService, @ILoggerService loggerService: ILoggerService, ) { - console.log(this.initData.environment.extensionTelemetryLogResource); this._outputLogger = loggerService.createLogger(URI.revive(this.initData.environment.extensionTelemetryLogResource)); this._outputLogger.info('Below are logs for extension telemetry events sent to the telemetry output channel API once the log level is set to trace.'); this._outputLogger.info('==========================================================='); @@ -183,16 +182,16 @@ export class ExtHostTelemetryLogger { if (!this._apiObject) { const that = this; const obj: vscode.TelemetryLogger = { - logUsage: that.logUsage, + logUsage: that.logUsage.bind(that), get isUsageEnabled() { return that._telemetryEnablements.isUsageEnabled; }, get isErrorsEnabled() { return that._telemetryEnablements.isErrorsEnabled; }, - logError: that.logError, - dispose: that.dispose, - onDidChangeEnableStates: that._onDidChangeEnableStates.event + logError: that.logError.bind(that), + dispose: that.dispose.bind(that), + onDidChangeEnableStates: that._onDidChangeEnableStates.event.bind(that) }; this._apiObject = Object.freeze(obj); } diff --git a/src/vs/workbench/api/test/browser/extHostTelemetry.test.ts b/src/vs/workbench/api/test/browser/extHostTelemetry.test.ts new file mode 100644 index 00000000000..2153a61f480 --- /dev/null +++ b/src/vs/workbench/api/test/browser/extHostTelemetry.test.ts @@ -0,0 +1,145 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { URI } from 'vs/base/common/uri'; +import { ExtensionIdentifier, IExtensionDescription, TargetPlatform } from 'vs/platform/extensions/common/extensions'; +import { DEFAULT_LOG_LEVEL } from 'vs/platform/log/common/log'; +import { ITelemetryInfo, TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; +import { TestTelemetryLoggerService } from 'vs/platform/telemetry/test/common/telemetryLogAppender.test'; +import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; +import { ExtHostTelemetry } from 'vs/workbench/api/common/extHostTelemetry'; +import { IEnvironment } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; +import { mock } from 'vs/workbench/test/common/workbenchTestServices'; +import type { TelemetryAppender } from 'vscode'; + +suite('ExtHostTelemetry', function () { + + const mockEnvironment: IEnvironment = { + isExtensionDevelopmentDebug: false, + extensionDevelopmentLocationURI: undefined, + extensionTestsLocationURI: undefined, + appRoot: undefined, + appName: 'test', + extensionTelemetryLogResource: URI.parse('fake'), + appHost: 'test', + appLanguage: 'en', + globalStorageHome: URI.parse('fake'), + workspaceStorageHome: URI.parse('fake'), + appUriScheme: 'test', + }; + + const mockTelemetryInfo: ITelemetryInfo = { + firstSessionDate: '2020-01-01T00:00:00.000Z', + sessionId: 'test', + machineId: 'test', + }; + + const mockRemote = { + authority: 'test', + isRemote: false, + connectionData: null + }; + + const mockExtensionIdentifier: IExtensionDescription = { + identifier: new ExtensionIdentifier('test-extension'), + targetPlatform: TargetPlatform.UNIVERSAL, + isBuiltin: true, + isUserBuiltin: true, + isUnderDevelopment: true, + name: 'test-extension', + publisher: 'vscode', + version: '1.0.0', + engines: { vscode: '*' }, + extensionLocation: URI.parse('fake') + }; + + test('Ensure logger gets proper telemetry level during initialization', function () { + const extensionTelemetry = new ExtHostTelemetry(new class extends mock() { + override environment: IEnvironment = mockEnvironment; + override telemetryInfo: ITelemetryInfo = mockTelemetryInfo; + override remote = mockRemote; + }, new TestTelemetryLoggerService(DEFAULT_LOG_LEVEL)); + extensionTelemetry.$initializeTelemetryLevel(TelemetryLevel.USAGE, { usage: true, error: true }); + let config = extensionTelemetry.getTelemetryDetails(); + assert.strictEqual(config.isCrashEnabled, true); + assert.strictEqual(config.isUsageEnabled, true); + assert.strictEqual(config.isErrorsEnabled, true); + + // Initialize would never be called twice, but this is just for testing + extensionTelemetry.$initializeTelemetryLevel(TelemetryLevel.ERROR, { usage: true, error: true }); + config = extensionTelemetry.getTelemetryDetails(); + assert.strictEqual(config.isCrashEnabled, true); + assert.strictEqual(config.isUsageEnabled, false); + assert.strictEqual(config.isErrorsEnabled, true); + + extensionTelemetry.$initializeTelemetryLevel(TelemetryLevel.CRASH, { usage: true, error: true }); + config = extensionTelemetry.getTelemetryDetails(); + assert.strictEqual(config.isCrashEnabled, true); + assert.strictEqual(config.isUsageEnabled, false); + assert.strictEqual(config.isErrorsEnabled, false); + + extensionTelemetry.$initializeTelemetryLevel(TelemetryLevel.USAGE, { usage: false, error: true }); + config = extensionTelemetry.getTelemetryDetails(); + assert.strictEqual(config.isCrashEnabled, true); + assert.strictEqual(config.isUsageEnabled, false); + assert.strictEqual(config.isErrorsEnabled, true); + }); + + test('Simple log event to TelemetryLogger', function () { + const sentData: any[] = []; + const sentExceptions: any[] = []; + let flushCalled = false; + + // This is the appender which the extension would contribute + const appender: TelemetryAppender = { + logEvent: (eventName: string, data) => { + sentData.push({ eventName, data }); + }, + logException: (exception, data) => { + sentExceptions.push({ exception, data }); + }, + ignoreBuiltInCommonProperties: false, + flush: () => { + flushCalled = true; + } + }; + + const extensionTelemetry = new ExtHostTelemetry(new class extends mock() { + override environment: IEnvironment = mockEnvironment; + override telemetryInfo: ITelemetryInfo = mockTelemetryInfo; + override remote = mockRemote; + }, new TestTelemetryLoggerService(DEFAULT_LOG_LEVEL)); + + extensionTelemetry.$initializeTelemetryLevel(TelemetryLevel.USAGE, { usage: true, error: true }); + + // Create the logger usting the appender + const logger = extensionTelemetry.instantiateLogger(mockExtensionIdentifier, appender); + + logger.logUsage('test-event', { 'test-data': 'test-data' }); + assert.strictEqual(sentData.length, 1); + assert.strictEqual(sentData[0].eventName, `${mockExtensionIdentifier.name}/test-event`); + assert.strictEqual(sentData[0].data['test-data'], 'test-data'); + + logger.logUsage('test-event', { 'test-data': 'test-data' }); + assert.strictEqual(sentData.length, 2); + + logger.logError('test-event', { 'test-data': 'test-data' }); + assert.strictEqual(sentData.length, 3); + + logger.logError(new Error('test-error'), { 'test-data': 'test-data' }); + assert.strictEqual(sentData.length, 3); + assert.strictEqual(sentExceptions.length, 1); + + + // Assert not flushed + assert.strictEqual(flushCalled, false); + + // Call flush and assert that flush occurs + logger.dispose(); + assert.strictEqual(flushCalled, true); + + }); +}); diff --git a/src/vscode-dts/vscode.proposed.telemetryLogger.d.ts b/src/vscode-dts/vscode.proposed.telemetryLogger.d.ts index dfd38a77dd8..94a9a0a186e 100644 --- a/src/vscode-dts/vscode.proposed.telemetryLogger.d.ts +++ b/src/vscode-dts/vscode.proposed.telemetryLogger.d.ts @@ -48,7 +48,7 @@ declare module 'vscode' { /** * Any additional common properties which should be injected into the data object. */ - readonly additionalCommonProperties: Record; + readonly additionalCommonProperties?: Record; /** * User-defined function which logs an event, used within the TelemetryLogger