diff --git a/extensions/admin-tool-ext-win/src/main.ts b/extensions/admin-tool-ext-win/src/main.ts index 8880d2e54a..9d616cf90e 100644 --- a/extensions/admin-tool-ext-win/src/main.ts +++ b/extensions/admin-tool-ext-win/src/main.ts @@ -24,6 +24,7 @@ export async function activate(context: vscode.ExtensionContext): Promise const ssmsMinVer = JSON.parse(rawConfig.toString()).version; exePath = path.join(context.extensionPath, 'ssmsmin', 'Windows', ssmsMinVer, 'ssmsmin.exe'); registerCommands(context); + context.subscriptions.push(TelemetryReporter); } } diff --git a/extensions/azuremonitor/src/azuremonitorServer.ts b/extensions/azuremonitor/src/azuremonitorServer.ts index a203b06469..a2eac47bc9 100644 --- a/extensions/azuremonitor/src/azuremonitorServer.ts +++ b/extensions/azuremonitor/src/azuremonitorServer.ts @@ -10,7 +10,7 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import * as path from 'path'; import { getCommonLaunchArgsAndCleanupOldLogFiles } from './utils'; -import { Telemetry, LanguageClientErrorHandler } from './telemetry'; +import { TelemetryReporter, LanguageClientErrorHandler } from './telemetry'; import { SqlOpsDataClient, ClientOptions } from 'dataprotocol-client'; import { SerializationFeature } from './features/serializationFeature'; import { TelemetryFeature } from './features/telemetryFeature'; @@ -48,7 +48,7 @@ export class AzureMonitorServer { vscode.commands.registerCommand('azuremonitor.loadCompletionExtension', (params: CompletionExtensionParams) => { this.client.sendRequest(CompletionExtLoadRequest.type, params); }); - Telemetry.sendTelemetryEvent('startup/LanguageClientStarted', { + TelemetryReporter.sendTelemetryEvent('startup/LanguageClientStarted', { installationTime: String(installationComplete - installationStart), processStartupTime: String(processEnd - processStart), totalTime: String(processEnd - installationStart), @@ -61,7 +61,7 @@ export class AzureMonitorServer { await Promise.all([clientReadyPromise]); return this.client; } catch (e) { - Telemetry.sendTelemetryEvent('ServiceInitializingFailed'); + TelemetryReporter.sendTelemetryEvent('ServiceInitializingFailed'); vscode.window.showErrorMessage(localize('failedToStartServiceErrorMsg', "Failed to start {0}", Constants.serviceName)); throw e; } diff --git a/extensions/azuremonitor/src/constants.ts b/extensions/azuremonitor/src/constants.ts index 5aedc95ff8..c80113c5e1 100644 --- a/extensions/azuremonitor/src/constants.ts +++ b/extensions/azuremonitor/src/constants.ts @@ -6,6 +6,7 @@ export const serviceName = 'AzureMonitor Tools Service'; export const providerId = 'LOGANALYTICS'; export const serviceCrashLink = 'https://github.com/Microsoft/azuredatastudio/issues'; export const extensionConfigSectionName = 'azuremonitor'; +export const packageName = 'Microsoft.azuremonitor'; // DATA PROTOCOL VALUES /////////////////////////////////////////////////////////// export const azureMonitorClusterProviderName = 'azureMonitorCluster'; @@ -22,4 +23,4 @@ export const objectExplorerPrefix: string = 'objectexplorer://'; export const ViewType = 'view'; export const azuremonitorClusterNewNotebookTask = 'azuremonitorCluster.task.newNotebook'; -export const azuremonitorClusterOpenNotebookTask = 'azuremonitorCluster.task.openNotebook'; \ No newline at end of file +export const azuremonitorClusterOpenNotebookTask = 'azuremonitorCluster.task.openNotebook'; diff --git a/extensions/azuremonitor/src/features/contracts.ts b/extensions/azuremonitor/src/features/contracts.ts index 1b9912de2d..fc506efd46 100644 --- a/extensions/azuremonitor/src/features/contracts.ts +++ b/extensions/azuremonitor/src/features/contracts.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { NotificationType, RequestType } from 'vscode-languageclient'; -import { ITelemetryEventProperties, ITelemetryEventMeasures } from '../telemetry'; +import * as telemetry from '@microsoft/ads-extension-telemetry'; import * as azdata from 'azdata'; // ------------------------------- < Telemetry Sent Event > ------------------------------------ @@ -22,8 +22,8 @@ export namespace TelemetryNotification { export class TelemetryParams { public params!: { eventName: string; - properties: ITelemetryEventProperties; - measures: ITelemetryEventMeasures; + properties: telemetry.TelemetryEventProperties; + measures: telemetry.TelemetryEventMeasures; }; } diff --git a/extensions/azuremonitor/src/features/telemetryFeature.ts b/extensions/azuremonitor/src/features/telemetryFeature.ts index 7723f8fb64..fa0b135e41 100644 --- a/extensions/azuremonitor/src/features/telemetryFeature.ts +++ b/extensions/azuremonitor/src/features/telemetryFeature.ts @@ -5,7 +5,7 @@ import { SqlOpsDataClient } from 'dataprotocol-client'; import { ClientCapabilities, StaticFeature } from 'vscode-languageclient'; -import { Telemetry } from '../telemetry'; +import { TelemetryReporter } from '../telemetry'; import * as contracts from './contracts'; import * as Utils from '../utils'; @@ -19,7 +19,7 @@ export class TelemetryFeature implements StaticFeature { initialize(): void { this._client.onNotification(contracts.TelemetryNotification.type, e => { - Telemetry.sendTelemetryEvent(e.params.eventName, e.params.properties, e.params.measures); + TelemetryReporter.sendTelemetryEvent(e.params.eventName, e.params.properties, e.params.measures); }); } } diff --git a/extensions/azuremonitor/src/main.ts b/extensions/azuremonitor/src/main.ts index b42bcd0655..6bb358651b 100644 --- a/extensions/azuremonitor/src/main.ts +++ b/extensions/azuremonitor/src/main.ts @@ -19,6 +19,7 @@ import { AzureMonitorIconProvider } from './iconProvider'; import { createAzureMonitorApi } from './azuremonitorApiFactory'; import { AzureMonitorServer } from './azuremonitorServer'; import { promises as fs } from 'fs'; +import { TelemetryReporter } from './telemetry'; const localize = nls.loadMessageBundle(); @@ -54,7 +55,7 @@ export async function activate(context: vscode.ExtensionContext): Promise('enableTelemetry', true)) { - this.disable(); - return; - } - - let packageInfo = Utils.getPackageInfo(packageJson)!; - this.reporter = new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey); - } - } - - /** - * Send a telemetry event for an exception - */ - public static sendTelemetryEventForException( - err: any, methodName: string, _extensionConfigName: string): void { - let stackArray: string[]; - let firstLine: string = ''; - if (err !== undefined && err.stack !== undefined) { - stackArray = err.stack.split('\n'); - if (stackArray !== undefined && stackArray.length >= 2) { - firstLine = stackArray[1]; // The fist line is the error message and we don't want to send that telemetry event - firstLine = FilterErrorPath(firstLine)!; - } - } - - // Only adding the method name and the fist line of the stack trace. We don't add the error message because it might have PII - this.sendTelemetryEvent('Exception', { methodName: methodName, errorLine: firstLine }); - } - - /** - * Send a telemetry event using application insights - */ - public static sendTelemetryEvent( - eventName: string, - properties?: ITelemetryEventProperties, - measures?: ITelemetryEventMeasures): void { - - if (typeof this.disabled === 'undefined') { - this.disabled = false; - } - - if (this.disabled || typeof (this.reporter) === 'undefined') { - // Don't do anything if telemetry is disabled - return; - } - - if (!properties || typeof properties === 'undefined') { - properties = {}; - } - - try { - this.reporter.sendTelemetryEvent(eventName, properties, measures); - } catch (telemetryErr) { - // If sending telemetry event fails ignore it so it won't break the extension - console.error('Failed to send telemetry event. error: ' + telemetryErr); - } - - } -} +const packageInfo = vscode.extensions.getExtension(Constants.packageName)?.packageJSON; +export const TelemetryReporter = new AdsTelemetryReporter(packageInfo?.name, packageInfo?.version, packageInfo?.aiKey); /** * Handle Language Service client errors @@ -127,12 +27,12 @@ export class LanguageClientErrorHandler implements ErrorHandler { * @memberOf LanguageClientErrorHandler */ showOnErrorPrompt(): void { - Telemetry.sendTelemetryEvent(Constants.serviceName + 'Crash'); - vscode.window.showErrorMessage( + TelemetryReporter.sendTelemetryEvent(Constants.serviceName + 'Crash'); + void vscode.window.showErrorMessage( localize('serviceCrashMessage', "{0} component exited unexpectedly. Please restart Azure Data Studio.", Constants.serviceName), viewKnownIssuesAction).then(action => { if (action && action === viewKnownIssuesAction) { - vscode.env.openExternal(vscode.Uri.parse(Constants.serviceCrashLink)); + void vscode.env.openExternal(vscode.Uri.parse(Constants.serviceCrashLink)); } }); } @@ -163,5 +63,3 @@ export class LanguageClientErrorHandler implements ErrorHandler { return CloseAction.DoNotRestart; } } - -Telemetry.initialize(); diff --git a/extensions/dacpac/src/extension.ts b/extensions/dacpac/src/extension.ts index c66cffec83..284f22b250 100644 --- a/extensions/dacpac/src/extension.ts +++ b/extensions/dacpac/src/extension.ts @@ -6,9 +6,11 @@ import * as azdata from 'azdata'; import * as vscode from 'vscode'; import { DataTierApplicationWizard } from './wizard/dataTierApplicationWizard'; +import { TelemetryReporter } from './telemetry'; export async function activate(context: vscode.ExtensionContext) { - vscode.commands.registerCommand('dacFx.start', (profile: azdata.IConnectionProfile) => new DataTierApplicationWizard(undefined, context).start(profile)); + context.subscriptions.push(vscode.commands.registerCommand('dacFx.start', (profile: azdata.IConnectionProfile) => new DataTierApplicationWizard(undefined, context).start(profile))); + context.subscriptions.push(TelemetryReporter); } export function deactivate(): void { diff --git a/extensions/data-workspace/src/main.ts b/extensions/data-workspace/src/main.ts index 2cd49aa480..980eeb1e6c 100644 --- a/extensions/data-workspace/src/main.ts +++ b/extensions/data-workspace/src/main.ts @@ -16,6 +16,7 @@ import { ProjectDashboard } from './dialogs/projectDashboard'; import { getAzdataApi } from './common/utils'; import { createNewProjectWithQuickpick } from './dialogs/newProjectQuickpick'; import Logger from './common/logger'; +import { TelemetryReporter } from './common/telemetry'; export async function activate(context: vscode.ExtensionContext): Promise { const startTime = new Date().getTime(); @@ -103,6 +104,7 @@ export async function activate(context: vscode.ExtensionContext): Promise undefined, err => { apiWrapper.showErrorMessage(utils.getErrorMessage(err)); }); + extensionContext.subscriptions.push(TelemetryReporter); } diff --git a/extensions/datavirtualization/src/services/contracts.ts b/extensions/datavirtualization/src/services/contracts.ts index 450bf84589..93992ea958 100644 --- a/extensions/datavirtualization/src/services/contracts.ts +++ b/extensions/datavirtualization/src/services/contracts.ts @@ -3,8 +3,8 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ClientCapabilities as VSClientCapabilities, RequestType, NotificationType } from 'vscode-languageclient'; -import * as types from 'dataprotocol-client/lib/types'; +import { RequestType, NotificationType } from 'vscode-languageclient'; +import * as telemetry from '@microsoft/ads-extension-telemetry'; import * as azdata from 'azdata'; /** @@ -29,19 +29,11 @@ export namespace TelemetryNotification { export class TelemetryParams { public params: { eventName: string; - properties: ITelemetryEventProperties; - measures: ITelemetryEventMeasures; + properties: telemetry.TelemetryEventProperties; + measures: telemetry.TelemetryEventMeasures; }; } -export interface ITelemetryEventProperties { - [key: string]: string; -} - -export interface ITelemetryEventMeasures { - [key: string]: number; -} - // ------------------------------- ---------------------------------- diff --git a/extensions/datavirtualization/src/services/features.ts b/extensions/datavirtualization/src/services/features.ts index 9c737fc364..54ea0514a8 100644 --- a/extensions/datavirtualization/src/services/features.ts +++ b/extensions/datavirtualization/src/services/features.ts @@ -9,7 +9,7 @@ import * as UUID from 'vscode-languageclient/lib/utils/uuid'; import { Disposable } from 'vscode'; import * as azdata from 'azdata'; -import { Telemetry } from './telemetry'; +import { TelemetryReporter } from './telemetry'; import * as serviceUtils from './serviceUtils'; import { TelemetryNotification, @@ -33,7 +33,7 @@ export class TelemetryFeature implements StaticFeature { initialize(): void { this._client.onNotification(TelemetryNotification.type, e => { - Telemetry.sendTelemetryEvent(e.params.eventName, e.params.properties, e.params.measures); + TelemetryReporter.sendTelemetryEvent(e.params.eventName, e.params.properties, e.params.measures); }); } } diff --git a/extensions/datavirtualization/src/services/serviceClient.ts b/extensions/datavirtualization/src/services/serviceClient.ts index e00729452c..d70af6378e 100644 --- a/extensions/datavirtualization/src/services/serviceClient.ts +++ b/extensions/datavirtualization/src/services/serviceClient.ts @@ -12,7 +12,7 @@ const localize = nls.loadMessageBundle(); import * as path from 'path'; import { EventAndListener } from 'eventemitter2'; -import { Telemetry, LanguageClientErrorHandler } from './telemetry'; +import { LanguageClientErrorHandler, TelemetryReporter } from './telemetry'; import { ApiWrapper } from '../apiWrapper'; import * as Constants from '../constants'; import { TelemetryFeature, DataSourceWizardFeature } from './features'; @@ -51,7 +51,7 @@ export class ServiceClient { setTimeout(() => { this.statusView.hide(); }, 1500); - Telemetry.sendTelemetryEvent('startup/LanguageClientStarted', { + TelemetryReporter.sendTelemetryEvent('startup/LanguageClientStarted', { installationTime: String(installationComplete - installationStart), processStartupTime: String(processEnd - processStart), totalTime: String(processEnd - installationStart), @@ -64,7 +64,7 @@ export class ServiceClient { context.subscriptions.push(disposable); resolve(); }, e => { - Telemetry.sendTelemetryEvent('ServiceInitializingFailed'); + TelemetryReporter.sendTelemetryEvent('ServiceInitializingFailed'); this.apiWrapper.showErrorMessage(localize('serviceStartFailed', 'Failed to start {0}: {1}', Constants.serviceName, e)); // Just resolve to avoid unhandled promise. We show the error to the user. resolve(); diff --git a/extensions/datavirtualization/src/services/telemetry.ts b/extensions/datavirtualization/src/services/telemetry.ts index df8c594fee..bee1ba20c3 100644 --- a/extensions/datavirtualization/src/services/telemetry.ts +++ b/extensions/datavirtualization/src/services/telemetry.ts @@ -4,17 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import { ErrorAction, CloseAction } from 'vscode-languageclient'; -import TelemetryReporter from '@microsoft/ads-extension-telemetry'; -import { PlatformInformation } from '@microsoft/ads-service-downloader/out/platform'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); import { ApiWrapper } from '../apiWrapper'; import * as constants from '../constants'; -import * as serviceUtils from './serviceUtils'; -import { IMessage, ITelemetryEventProperties, ITelemetryEventMeasures } from './contracts'; +import { IMessage } from './contracts'; +import AdsTelemetryReporter from '@microsoft/ads-extension-telemetry'; +const packageInfo = vscode.extensions.getExtension(constants.packageName)?.packageJSON; +export const TelemetryReporter = new AdsTelemetryReporter(packageInfo?.name, packageInfo?.version, packageInfo?.aiKey); /** * Handle Language Service client errors @@ -36,9 +36,8 @@ export class LanguageClientErrorHandler { * Show an error message prompt with a link to known issues wiki page * @memberOf LanguageClientErrorHandler */ - showOnErrorPrompt(): void { - // TODO add telemetry - // Telemetry.sendTelemetryEvent('SqlToolsServiceCrash'); + public showOnErrorPrompt(): void { + TelemetryReporter.sendTelemetryEvent(constants.serviceName + 'Crash'); let crashButtonText = localize('serviceCrashButton', 'View Known Issues'); this.apiWrapper.showErrorMessage( localize('serviceCrashMessage', 'service component could not start'), @@ -60,7 +59,7 @@ export class LanguageClientErrorHandler { * * @memberOf LanguageClientErrorHandler */ - error(error: Error, message: IMessage, count: number): ErrorAction { + public error(error: Error, message: IMessage, count: number): ErrorAction { this.showOnErrorPrompt(); // we don't retry running the service since crashes leave the extension @@ -75,7 +74,7 @@ export class LanguageClientErrorHandler { * * @memberOf LanguageClientErrorHandler */ - closed(): CloseAction { + public closed(): CloseAction { this.showOnErrorPrompt(); // we don't retry running the service since crashes leave the extension @@ -83,134 +82,3 @@ export class LanguageClientErrorHandler { return CloseAction.DoNotRestart; } } - -/** - * Filters error paths to only include source files. Exported to support testing - */ -export function FilterErrorPath(line: string): string { - if (line) { - let values: string[] = line.split('/out/'); - if (values.length <= 1) { - // Didn't match expected format - return line; - } else { - return values[1]; - } - } -} - -export class Telemetry { - private static reporter: TelemetryReporter; - private static userId: string; - private static platformInformation: PlatformInformation; - private static disabled: boolean; - - // Get the unique ID for the current user of the extension - public static getUserId(): Promise { - return new Promise(resolve => { - // Generate the user id if it has not been created already - if (typeof this.userId === 'undefined') { - let id = serviceUtils.generateUserId(); - id.then(newId => { - this.userId = newId; - resolve(this.userId); - }); - } else { - resolve(this.userId); - } - }); - } - - public static getPlatformInformation(): Promise { - if (this.platformInformation) { - return Promise.resolve(this.platformInformation); - } else { - return new Promise(resolve => { - PlatformInformation.getCurrent().then(info => { - this.platformInformation = info; - resolve(this.platformInformation); - }); - }); - } - } - - /** - * Disable telemetry reporting - */ - public static disable(): void { - this.disabled = true; - } - - /** - * Initialize the telemetry reporter for use. - */ - public static initialize(): void { - if (typeof this.reporter === 'undefined') { - // Check if the user has opted out of telemetry - if (!vscode.workspace.getConfiguration('telemetry').get('enableTelemetry', true)) { - this.disable(); - return; - } - let packageInfo = vscode.extensions.getExtension('Microsoft.datavirtualization').packageJSON; - this.reporter = new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey); - } - } - - /** - * Send a telemetry event for an exception - */ - public static sendTelemetryEventForException( - err: any, methodName: string, extensionConfigName: string): void { - try { - let stackArray: string[]; - let firstLine: string = ''; - if (err !== undefined && err.stack !== undefined) { - stackArray = err.stack.split('\n'); - if (stackArray !== undefined && stackArray.length >= 2) { - firstLine = stackArray[1]; // The fist line is the error message and we don't want to send that telemetry event - firstLine = FilterErrorPath(firstLine); - } - } - - // Only adding the method name and the fist line of the stack trace. We don't add the error message because it might have PII - this.sendTelemetryEvent('Exception', { methodName: methodName, errorLine: firstLine }); - // Utils.logDebug('Unhandled Exception occurred. error: ' + err + ' method: ' + methodName, extensionConfigName); - } catch (telemetryErr) { - // If sending telemetry event fails ignore it so it won't break the extension - // Utils.logDebug('Failed to send telemetry event. error: ' + telemetryErr, extensionConfigName); - } - } - - /** - * Send a telemetry event using application insights - */ - public static sendTelemetryEvent( - eventName: string, - properties?: ITelemetryEventProperties, - measures?: ITelemetryEventMeasures): void { - - if (typeof this.disabled === 'undefined') { - this.disabled = false; - } - - if (this.disabled || typeof (this.reporter) === 'undefined') { - // Don't do anything if telemetry is disabled - return; - } - - if (!properties || typeof properties === 'undefined') { - properties = {}; - } - - // Augment the properties structure with additional common properties before sending - Promise.all([this.getUserId(), this.getPlatformInformation()]).then(() => { - properties['userId'] = this.userId; - properties['distribution'] = (this.platformInformation && this.platformInformation.distribution) ? - `${this.platformInformation.distribution.name}, ${this.platformInformation.distribution.version}` : ''; - - this.reporter.sendTelemetryEvent(eventName, properties, measures); - }); - } -} - -Telemetry.initialize(); diff --git a/extensions/import/src/common/constants.ts b/extensions/import/src/common/constants.ts index 28b91dd1ea..486b6bec35 100644 --- a/extensions/import/src/common/constants.ts +++ b/extensions/import/src/common/constants.ts @@ -12,6 +12,7 @@ export const providerId = 'FlatFileImport'; export const configLogDebugInfo = 'logDebugInfo'; export const sqlConfigSectionName = 'sql'; export const mssqlProvider = 'MSSQL'; +export const packageName = 'Microsoft.import'; // allow-any-unicode-next-line export const summaryErrorSymbol = '✗ '; diff --git a/extensions/import/src/main.ts b/extensions/import/src/main.ts index 473333f4e6..b7adfcf01d 100644 --- a/extensions/import/src/main.ts +++ b/extensions/import/src/main.ts @@ -7,6 +7,7 @@ import * as vscode from 'vscode'; import ControllerBase from './controllers/controllerBase'; import MainController from './controllers/mainController'; +import { TelemetryReporter } from './services/telemetry'; let controllers: ControllerBase[] = []; @@ -18,6 +19,7 @@ export async function activate(context: vscode.ExtensionContext): Promise context.subscriptions.push(mainController); await mainController.activate(); + context.subscriptions.push(TelemetryReporter); } export function deactivate() { diff --git a/extensions/import/src/services/contracts.ts b/extensions/import/src/services/contracts.ts index b53f78d498..0f2c0a75a6 100644 --- a/extensions/import/src/services/contracts.ts +++ b/extensions/import/src/services/contracts.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { RequestType, NotificationType } from 'vscode-languageclient'; +import * as telemetry from '@microsoft/ads-extension-telemetry'; export interface IMessage { jsonrpc: string; @@ -24,19 +25,11 @@ export namespace TelemetryNotification { export class TelemetryParams { public params: { eventName: string; - properties: ITelemetryEventProperties; - measures: ITelemetryEventMeasures; + properties: telemetry.TelemetryEventProperties; + measures: telemetry.TelemetryEventMeasures; }; } -export interface ITelemetryEventProperties { - [key: string]: string; -} - -export interface ITelemetryEventMeasures { - [key: string]: number; -} - /** * Contract Classes */ diff --git a/extensions/import/src/services/features.ts b/extensions/import/src/services/features.ts index 9b59f9de9c..5867273556 100644 --- a/extensions/import/src/services/features.ts +++ b/extensions/import/src/services/features.ts @@ -14,7 +14,7 @@ import { import * as UUID from 'vscode-languageclient/lib/utils/uuid'; import { Disposable } from 'vscode'; -import { Telemetry } from './telemetry'; +import { TelemetryReporter } from './telemetry'; import * as serviceUtils from './serviceUtils'; import * as Contracts from './contracts'; import { managerInstance, ApiType } from './serviceApiManager'; @@ -30,7 +30,7 @@ export class TelemetryFeature implements StaticFeature { initialize(): void { this._client.onNotification(Contracts.TelemetryNotification.type, e => { - Telemetry.sendTelemetryEvent(e.params.eventName, e.params.properties, e.params.measures); + TelemetryReporter.sendTelemetryEvent(e.params.eventName, e.params.properties, e.params.measures); }); } } diff --git a/extensions/import/src/services/serviceClient.ts b/extensions/import/src/services/serviceClient.ts index a8f2f1535b..084e1a67ec 100644 --- a/extensions/import/src/services/serviceClient.ts +++ b/extensions/import/src/services/serviceClient.ts @@ -12,7 +12,7 @@ const localize = nls.loadMessageBundle(); import * as path from 'path'; import { EventAndListener } from 'eventemitter2'; -import { Telemetry, LanguageClientErrorHandler } from './telemetry'; +import { TelemetryReporter, LanguageClientErrorHandler } from './telemetry'; import * as Constants from '../common/constants'; import { TelemetryFeature, FlatFileImportFeature } from './features'; import { promises as fs } from 'fs'; @@ -43,7 +43,7 @@ export class ServiceClient { setTimeout(() => { this.statusView.hide(); }, 1500); - Telemetry.sendTelemetryEvent('startup/LanguageClientStarted', { + TelemetryReporter.sendTelemetryEvent('startup/LanguageClientStarted', { installationTime: String(installationComplete - installationStart), processStartupTime: String(processEnd - processStart), totalTime: String(processEnd - installationStart), @@ -57,7 +57,7 @@ export class ServiceClient { return client; } catch (error) { - Telemetry.sendTelemetryEvent('ServiceInitializingFailed'); + TelemetryReporter.sendTelemetryEvent('ServiceInitializingFailed'); vscode.window.showErrorMessage(localize('flatFileImport.serviceStartFailed', "Failed to start {0}: {1}", Constants.serviceName, error)); // Just resolve to avoid unhandled promise. We show the error to the user. return undefined; diff --git a/extensions/import/src/services/telemetry.ts b/extensions/import/src/services/telemetry.ts index 3b1a7506ee..1a1bf5b5e2 100644 --- a/extensions/import/src/services/telemetry.ts +++ b/extensions/import/src/services/telemetry.ts @@ -4,12 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { ErrorAction, CloseAction } from 'vscode-languageclient'; -import TelemetryReporter from '@microsoft/ads-extension-telemetry'; +import AdsTelemetryReporter from '@microsoft/ads-extension-telemetry'; import * as vscode from 'vscode'; import * as constants from '../common/constants'; -import { IMessage, ITelemetryEventProperties, ITelemetryEventMeasures } from './contracts'; +import { IMessage } from './contracts'; +const packageInfo = vscode.extensions.getExtension(constants.packageName)?.packageJSON; +export const TelemetryReporter = new AdsTelemetryReporter(packageInfo?.name, packageInfo?.version, packageInfo?.aiKey); /** * Handle Language Service client errors @@ -29,8 +31,7 @@ export class LanguageClientErrorHandler { * @memberOf LanguageClientErrorHandler */ showOnErrorPrompt(): void { - // TODO add telemetry - // Telemetry.sendTelemetryEvent('SqlToolsServiceCrash'); + TelemetryReporter.sendTelemetryEvent(constants.serviceName + 'Crash'); vscode.window.showErrorMessage( constants.serviceCrashMessageText, constants.crashButtonText @@ -68,62 +69,3 @@ export class LanguageClientErrorHandler { } } - - -export class Telemetry { - private static reporter: TelemetryReporter; - private static disabled: boolean; - - /** - * Disable telemetry reporting - */ - public static disable(): void { - this.disabled = true; - } - - /** - * Initialize the telemetry reporter for use. - */ - public static initialize(): void { - if (typeof this.reporter === 'undefined') { - // Check if the user has opted out of telemetry - if (!vscode.workspace.getConfiguration('telemetry').get('enableTelemetry', true)) { - this.disable(); - return; - } - let packageInfo = vscode.extensions.getExtension('Microsoft.import').packageJSON; - this.reporter = new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey); - } - } - - /** - * Send a telemetry event using application insights - */ - public static sendTelemetryEvent( - eventName: string, - properties?: ITelemetryEventProperties, - measures?: ITelemetryEventMeasures): void { - - if (typeof this.disabled === 'undefined') { - this.disabled = false; - } - - if (this.disabled || typeof (this.reporter) === 'undefined') { - // Don't do anything if telemetry is disabled - return; - } - - if (!properties || typeof properties === 'undefined') { - properties = {}; - } - - try { - this.reporter.sendTelemetryEvent(eventName, properties, measures); - } catch (telemetryErr) { - // If sending telemetry event fails ignore it so it won't break the extension - console.error('Failed to send telemetry event. error: ' + telemetryErr); - } - } -} - -Telemetry.initialize(); diff --git a/extensions/kusto/src/constants.ts b/extensions/kusto/src/constants.ts index 8f68807341..5b4c1dafa3 100644 --- a/extensions/kusto/src/constants.ts +++ b/extensions/kusto/src/constants.ts @@ -6,6 +6,7 @@ export const serviceName = 'Kusto Tools Service'; export const providerId = 'KUSTO'; export const serviceCrashLink = 'https://github.com/Microsoft/vscode-kusto/wiki/SqlToolsService-Known-Issues'; export const extensionConfigSectionName = 'kusto'; +export const packageName = 'Microsoft.kusto'; // DATA PROTOCOL VALUES /////////////////////////////////////////////////////////// export const kustoClusterProviderName = 'kustoCluster'; diff --git a/extensions/kusto/src/contracts.ts b/extensions/kusto/src/contracts.ts index b16943abfe..fc506efd46 100644 --- a/extensions/kusto/src/contracts.ts +++ b/extensions/kusto/src/contracts.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { NotificationType, RequestType } from 'vscode-languageclient'; -import { ITelemetryEventProperties, ITelemetryEventMeasures } from './telemetry'; +import * as telemetry from '@microsoft/ads-extension-telemetry'; import * as azdata from 'azdata'; // ------------------------------- < Telemetry Sent Event > ------------------------------------ @@ -22,8 +22,8 @@ export namespace TelemetryNotification { export class TelemetryParams { public params!: { eventName: string; - properties: ITelemetryEventProperties; - measures: ITelemetryEventMeasures; + properties: telemetry.TelemetryEventProperties; + measures: telemetry.TelemetryEventMeasures; }; } diff --git a/extensions/kusto/src/features.ts b/extensions/kusto/src/features.ts index be46e5d776..d69b99bd7e 100644 --- a/extensions/kusto/src/features.ts +++ b/extensions/kusto/src/features.ts @@ -7,7 +7,7 @@ import * as nls from 'vscode-nls'; import { SqlOpsDataClient, SqlOpsFeature } from 'dataprotocol-client'; import { ClientCapabilities, StaticFeature, RPCMessageType, ServerCapabilities } from 'vscode-languageclient'; import { Disposable, window } from 'vscode'; -import { Telemetry } from './telemetry'; +import { TelemetryReporter } from './telemetry'; import * as contracts from './contracts'; import * as azdata from 'azdata'; import * as Utils from './utils'; @@ -25,7 +25,7 @@ export class TelemetryFeature implements StaticFeature { initialize(): void { this._client.onNotification(contracts.TelemetryNotification.type, e => { - Telemetry.sendTelemetryEvent(e.params.eventName, e.params.properties, e.params.measures); + TelemetryReporter.sendTelemetryEvent(e.params.eventName, e.params.properties, e.params.measures); }); } } diff --git a/extensions/kusto/src/kustoServer.ts b/extensions/kusto/src/kustoServer.ts index f191fd0dd8..f0a1655b36 100644 --- a/extensions/kusto/src/kustoServer.ts +++ b/extensions/kusto/src/kustoServer.ts @@ -10,7 +10,7 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import * as path from 'path'; import { getCommonLaunchArgsAndCleanupOldLogFiles } from './utils'; -import { Telemetry, LanguageClientErrorHandler } from './telemetry'; +import { TelemetryReporter, LanguageClientErrorHandler } from './telemetry'; import { SqlOpsDataClient, ClientOptions } from 'dataprotocol-client'; import { TelemetryFeature, SerializationFeature, AccountFeature } from './features'; import { AppContext } from './appContext'; @@ -46,7 +46,7 @@ export class KustoServer { vscode.commands.registerCommand('kusto.loadCompletionExtension', (params: CompletionExtensionParams) => { this.client.sendRequest(CompletionExtLoadRequest.type, params); }); - Telemetry.sendTelemetryEvent('startup/LanguageClientStarted', { + TelemetryReporter.sendTelemetryEvent('startup/LanguageClientStarted', { installationTime: String(installationComplete - installationStart), processStartupTime: String(processEnd - processStart), totalTime: String(processEnd - installationStart), @@ -59,7 +59,7 @@ export class KustoServer { await Promise.all([clientReadyPromise]); return this.client; } catch (e) { - Telemetry.sendTelemetryEvent('ServiceInitializingFailed'); + TelemetryReporter.sendTelemetryEvent('ServiceInitializingFailed'); vscode.window.showErrorMessage(localize('failedToStartServiceErrorMsg', "Failed to start {0}", Constants.serviceName)); throw e; } diff --git a/extensions/kusto/src/main.ts b/extensions/kusto/src/main.ts index 6694752167..8150913352 100644 --- a/extensions/kusto/src/main.ts +++ b/extensions/kusto/src/main.ts @@ -19,6 +19,7 @@ import { KustoIconProvider } from './iconProvider'; import { createKustoApi } from './kustoApiFactory'; import { KustoServer } from './kustoServer'; import { promises as fs } from 'fs'; +import { TelemetryReporter } from './telemetry'; const localize = nls.loadMessageBundle(); @@ -53,8 +54,8 @@ export async function activate(context: vscode.ExtensionContext): Promise('enableTelemetry', true)) { - this.disable(); - return; - } - - let packageInfo = Utils.getPackageInfo(packageJson)!; - this.reporter = new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey); - } - } - - /** - * Send a telemetry event for an exception - */ - public static sendTelemetryEventForException( - err: any, methodName: string, extensionConfigName: string): void { - let stackArray: string[]; - let firstLine: string = ''; - if (err !== undefined && err.stack !== undefined) { - stackArray = err.stack.split('\n'); - if (stackArray !== undefined && stackArray.length >= 2) { - firstLine = stackArray[1]; // The fist line is the error message and we don't want to send that telemetry event - firstLine = FilterErrorPath(firstLine)!; - } - } - - // Only adding the method name and the fist line of the stack trace. We don't add the error message because it might have PII - this.sendTelemetryEvent('Exception', { methodName: methodName, errorLine: firstLine }); - } - - /** - * Send a telemetry event using application insights - */ - public static sendTelemetryEvent( - eventName: string, - properties?: ITelemetryEventProperties, - measures?: ITelemetryEventMeasures): void { - - if (typeof this.disabled === 'undefined') { - this.disabled = false; - } - - if (this.disabled || typeof (this.reporter) === 'undefined') { - // Don't do anything if telemetry is disabled - return; - } - - if (!properties || typeof properties === 'undefined') { - properties = {}; - } - - try { - this.reporter.sendTelemetryEvent(eventName, properties, measures); - } catch (telemetryErr) { - // If sending telemetry event fails ignore it so it won't break the extension - console.error('Failed to send telemetry event. error: ' + telemetryErr); - } - - } -} +const packageInfo = vscode.extensions.getExtension(Constants.packageName)?.packageJSON; +export const TelemetryReporter = new AdsTelemetryReporter(packageInfo?.name, packageInfo?.version, packageInfo?.aiKey); /** * Handle Language Service client errors @@ -127,12 +27,12 @@ export class LanguageClientErrorHandler implements ErrorHandler { * @memberOf LanguageClientErrorHandler */ showOnErrorPrompt(): void { - Telemetry.sendTelemetryEvent(Constants.serviceName + 'Crash'); - vscode.window.showErrorMessage( + TelemetryReporter.sendTelemetryEvent(Constants.serviceName + 'Crash'); + void vscode.window.showErrorMessage( localize('serviceCrashMessage', "{0} component exited unexpectedly. Please restart Azure Data Studio.", Constants.serviceName), viewKnownIssuesAction).then(action => { if (action && action === viewKnownIssuesAction) { - vscode.env.openExternal(vscode.Uri.parse(Constants.serviceCrashLink)); + void vscode.env.openExternal(vscode.Uri.parse(Constants.serviceCrashLink)); } }); } @@ -142,7 +42,7 @@ export class LanguageClientErrorHandler implements ErrorHandler { * * @memberOf LanguageClientErrorHandler */ - error(error: Error, message: Message, count: number): ErrorAction { + error(_error: Error, _message: Message, _count: number): ErrorAction { this.showOnErrorPrompt(); // we don't retry running the service since crashes leave the extension @@ -163,5 +63,3 @@ export class LanguageClientErrorHandler implements ErrorHandler { return CloseAction.DoNotRestart; } } - -Telemetry.initialize(); diff --git a/extensions/mssql/src/constants.ts b/extensions/mssql/src/constants.ts index 55ab596cb8..b270f1a2c5 100644 --- a/extensions/mssql/src/constants.ts +++ b/extensions/mssql/src/constants.ts @@ -7,6 +7,7 @@ export const serviceName = 'SQL Tools Service'; export const providerId = 'MSSQL'; export const serviceCrashLink = 'https://github.com/Microsoft/vscode-mssql/wiki/SqlToolsService-Known-Issues'; export const extensionConfigSectionName = 'mssql'; +export const packageName = 'Microsoft.mssql'; // DATA PROTOCOL VALUES /////////////////////////////////////////////////////////// export const sqlProviderName = 'MSSQL'; diff --git a/extensions/mssql/src/contracts.ts b/extensions/mssql/src/contracts.ts index cafc603340..5b823e65ba 100644 --- a/extensions/mssql/src/contracts.ts +++ b/extensions/mssql/src/contracts.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { NotificationType, RequestType } from 'vscode-languageclient'; -import { ITelemetryEventProperties, ITelemetryEventMeasures } from './telemetry'; +import * as telemetry from '@microsoft/ads-extension-telemetry'; import * as azdata from 'azdata'; import { ConnectParams } from 'dataprotocol-client/lib/protocol'; import * as mssql from 'mssql'; @@ -24,8 +24,8 @@ export namespace TelemetryNotification { export class TelemetryParams { public params: { eventName: string; - properties: ITelemetryEventProperties; - measures: ITelemetryEventMeasures; + properties: telemetry.TelemetryEventProperties; + measures: telemetry.TelemetryEventMeasures; }; } diff --git a/extensions/mssql/src/features.ts b/extensions/mssql/src/features.ts index 127998ae6f..6619dcded5 100644 --- a/extensions/mssql/src/features.ts +++ b/extensions/mssql/src/features.ts @@ -6,7 +6,7 @@ import * as nls from 'vscode-nls'; import { SqlOpsDataClient, SqlOpsFeature } from 'dataprotocol-client'; import { ClientCapabilities, StaticFeature, RPCMessageType, ServerCapabilities } from 'vscode-languageclient'; import { Disposable, window, QuickPickItem, QuickPickOptions } from 'vscode'; -import { Telemetry } from './telemetry'; +import { TelemetryReporter } from './telemetry'; import * as contracts from './contracts'; import * as azdata from 'azdata'; import * as Utils from './utils'; @@ -27,7 +27,7 @@ export class TelemetryFeature implements StaticFeature { initialize(): void { this._client.onNotification(contracts.TelemetryNotification.type, e => { - Telemetry.sendTelemetryEvent(e.params.eventName, e.params.properties, e.params.measures); + TelemetryReporter.sendTelemetryEvent(e.params.eventName, e.params.properties, e.params.measures); }); } } diff --git a/extensions/mssql/src/main.ts b/extensions/mssql/src/main.ts index fae44fe5ab..ad8c95f5d8 100644 --- a/extensions/mssql/src/main.ts +++ b/extensions/mssql/src/main.ts @@ -22,6 +22,7 @@ import { IconPathHelper } from './iconHelper'; import * as nls from 'vscode-nls'; import { INotebookConvertService } from './notebookConvert/notebookConvertService'; import { registerTableDesignerCommands } from './tableDesigner/tableDesigner'; +import { TelemetryReporter } from './telemetry'; const localize = nls.loadMessageBundle(); @@ -88,6 +89,7 @@ export async function activate(context: vscode.ExtensionContext): Promise { return this.client.sendRequest(CompletionExtLoadRequest.type, params); }); - Telemetry.sendTelemetryEvent('startup/LanguageClientStarted', { + TelemetryReporter.sendTelemetryEvent('startup/LanguageClientStarted', { installationTime: String(installationComplete - installationStart), processStartupTime: String(processEnd - processStart), totalTime: String(processEnd - installationStart), @@ -82,7 +82,7 @@ export class SqlToolsServer { await Promise.all([this.activateFeatures(context), clientReadyPromise]); return this.client; } catch (e) { - Telemetry.sendTelemetryEvent('ServiceInitializingFailed'); + TelemetryReporter.sendTelemetryEvent('ServiceInitializingFailed'); void vscode.window.showErrorMessage(localize('failedToStartServiceErrorMsg', "Failed to start {0}", Constants.serviceName)); throw e; } diff --git a/extensions/mssql/src/tableDesigner/tableDesigner.ts b/extensions/mssql/src/tableDesigner/tableDesigner.ts index acff110885..6299fda82f 100644 --- a/extensions/mssql/src/tableDesigner/tableDesigner.ts +++ b/extensions/mssql/src/tableDesigner/tableDesigner.ts @@ -8,7 +8,8 @@ import * as azdata from 'azdata'; import * as vscode from 'vscode'; import { sqlProviderName } from '../constants'; import { generateUuid } from 'vscode-languageclient/lib/utils/uuid'; -import { ITelemetryEventProperties, Telemetry } from '../telemetry'; +import { fillServerInfo } from '../telemetry'; +import * as telemetry from '@microsoft/ads-extension-telemetry'; import * as nls from 'vscode-nls'; import { getConfigPreloadDatabaseModel, setConfigPreloadDatabaseModel } from '../utils'; const localize = nls.loadMessageBundle(); @@ -60,11 +61,12 @@ export function registerTableDesignerCommands(appContext: AppContext) { })); } -async function getTelemetryInfo(context: azdata.ObjectExplorerContext, tableType: string): Promise { +async function getTelemetryInfo(context: azdata.ObjectExplorerContext, tableType: string): Promise { const serverInfo = await azdata.connection.getServerInfo(context.connectionProfile.id); - const telemetryInfo: ITelemetryEventProperties = {}; - Telemetry.fillServerInfo(telemetryInfo, serverInfo); - telemetryInfo['tableType'] = tableType; + const telemetryInfo: telemetry.TelemetryEventProperties = { + tableType + }; + fillServerInfo(telemetryInfo, serverInfo); return telemetryInfo; } diff --git a/extensions/mssql/src/telemetry.ts b/extensions/mssql/src/telemetry.ts index 03293e46b1..7c80572fd3 100644 --- a/extensions/mssql/src/telemetry.ts +++ b/extensions/mssql/src/telemetry.ts @@ -4,129 +4,30 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import TelemetryReporter from '@microsoft/ads-extension-telemetry'; +import AdsTelemetryReporter from '@microsoft/ads-extension-telemetry'; import { ErrorAction, ErrorHandler, Message, CloseAction } from 'vscode-languageclient'; -import * as Utils from './utils'; import * as Constants from './constants'; import * as nls from 'vscode-nls'; import { ServerInfo } from 'azdata'; const localize = nls.loadMessageBundle(); -const packageJson = require('../package.json'); const viewKnownIssuesAction = localize('viewKnownIssuesText', "View Known Issues"); -export interface ITelemetryEventProperties { - [key: string]: string; -} - -export interface ITelemetryEventMeasures { - [key: string]: number; -} +const packageInfo = vscode.extensions.getExtension(Constants.packageName)?.packageJSON; +export const TelemetryReporter = new AdsTelemetryReporter(packageInfo?.name, packageInfo?.version, packageInfo?.aiKey); /** - * Filters error paths to only include source files. Exported to support testing + * Collects server information from ServerInfo to put into a + * property bag */ -function FilterErrorPath(line: string): string { - if (line) { - let values: string[] = line.split('/out/'); - if (values.length <= 1) { - // Didn't match expected format - return line; - } else { - return values[1]; - } - } - return undefined; -} - -export class Telemetry { - private static reporter: TelemetryReporter; - private static disabled: boolean; - - /** - * Disable telemetry reporting - */ - public static disable(): void { - this.disabled = true; - } - - /** - * Initialize the telemetry reporter for use. - */ - public static initialize(): void { - if (typeof this.reporter === 'undefined') { - // Check if the user has opted out of telemetry - if (!vscode.workspace.getConfiguration('telemetry').get('enableTelemetry', true)) { - this.disable(); - return; - } - - let packageInfo = Utils.getPackageInfo(packageJson); - this.reporter = new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey); - } - } - - /** - * Send a telemetry event for an exception - */ - public static sendTelemetryEventForException( - err: any, methodName: string, extensionConfigName: string): void { - let stackArray: string[]; - let firstLine: string = ''; - if (err !== undefined && err.stack !== undefined) { - stackArray = err.stack.split('\n'); - if (stackArray !== undefined && stackArray.length >= 2) { - firstLine = stackArray[1]; // The fist line is the error message and we don't want to send that telemetry event - firstLine = FilterErrorPath(firstLine); - } - } - - // Only adding the method name and the fist line of the stack trace. We don't add the error message because it might have PII - this.sendTelemetryEvent('Exception', { methodName: methodName, errorLine: firstLine }); - } - - /** - * Send a telemetry event using application insights - */ - public static sendTelemetryEvent( - eventName: string, - properties?: ITelemetryEventProperties, - measures?: ITelemetryEventMeasures): void { - - if (typeof this.disabled === 'undefined') { - this.disabled = false; - } - - if (this.disabled || typeof (this.reporter) === 'undefined') { - // Don't do anything if telemetry is disabled - return; - } - - if (!properties || typeof properties === 'undefined') { - properties = {}; - } - - try { - this.reporter.sendTelemetryEvent(eventName, properties, measures); - } catch (telemetryErr) { - // If sending telemetry event fails ignore it so it won't break the extension - console.error('Failed to send telemetry event. error: ' + telemetryErr); - } - } - - /** - * Collects server information from ServerInfo to put into a - * property bag - */ - public static fillServerInfo(telemetryInfo: { [key: string]: string }, serverInfo: ServerInfo): void { - telemetryInfo['serverEdition'] = serverInfo?.serverEdition; - telemetryInfo['serverLevel'] = serverInfo?.serverLevel; - telemetryInfo['serverMajorVersion'] = serverInfo?.serverMajorVersion.toString(); - telemetryInfo['serverMinorVersion'] = serverInfo?.serverMinorVersion.toString(); - telemetryInfo['isCloud'] = serverInfo?.isCloud.toString(); - } +export function fillServerInfo(telemetryInfo: { [key: string]: string }, serverInfo: ServerInfo): void { + telemetryInfo['serverEdition'] = serverInfo?.serverEdition; + telemetryInfo['serverLevel'] = serverInfo?.serverLevel; + telemetryInfo['serverMajorVersion'] = serverInfo?.serverMajorVersion.toString(); + telemetryInfo['serverMinorVersion'] = serverInfo?.serverMinorVersion.toString(); + telemetryInfo['isCloud'] = serverInfo?.isCloud.toString(); } /** @@ -139,7 +40,7 @@ export class LanguageClientErrorHandler implements ErrorHandler { * @memberOf LanguageClientErrorHandler */ showOnErrorPrompt(): void { - Telemetry.sendTelemetryEvent(Constants.serviceName + 'Crash'); + TelemetryReporter.sendTelemetryEvent(Constants.serviceName + 'Crash'); void vscode.window.showErrorMessage( localize('serviceCrashMessage', "{0} component exited unexpectedly. Please restart Azure Data Studio.", Constants.serviceName), viewKnownIssuesAction).then(action => { @@ -175,5 +76,3 @@ export class LanguageClientErrorHandler implements ErrorHandler { return CloseAction.DoNotRestart; } } - -Telemetry.initialize(); diff --git a/extensions/notebook/src/extension.ts b/extensions/notebook/src/extension.ts index 311c2bf96e..cd62836370 100644 --- a/extensions/notebook/src/extension.ts +++ b/extensions/notebook/src/extension.ts @@ -21,6 +21,7 @@ import { ExtensionContextHelper } from './common/extensionContextHelper'; import { BookTreeItem } from './book/bookTreeItem'; import Logger from './common/logger'; import { sendNotebookActionEvent, NbTelemetryView, NbTelemetryAction } from './telemetry'; +import { TelemetryReporter } from './telemetry'; const localize = nls.loadMessageBundle(); @@ -161,22 +162,23 @@ export async function activate(extensionContext: vscode.ExtensionContext): Promi const pinnedBookTreeViewProvider = appContext.pinnedBookTreeViewProvider; await pinnedBookTreeViewProvider.initialized; - azdata.nb.onDidChangeActiveNotebookEditor(e => { + extensionContext.subscriptions.push(azdata.nb.onDidChangeActiveNotebookEditor(e => { if (e.document.uri.scheme === 'untitled') { void providedBookTreeViewProvider.revealDocumentInTreeView(e.document.uri, false, false); } else { void bookTreeViewProvider.revealDocumentInTreeView(e.document.uri, false, false); } - }); + })); - azdata.nb.onDidOpenNotebookDocument(async e => { + extensionContext.subscriptions.push(azdata.nb.onDidOpenNotebookDocument(async e => { if (e.uri.scheme === 'untitled') { await vscode.commands.executeCommand(BuiltInCommands.SetContext, unsavedBooksContextKey, true); } else { await vscode.commands.executeCommand(BuiltInCommands.SetContext, unsavedBooksContextKey, false); } - }); + })); + extensionContext.subscriptions.push(TelemetryReporter); return { getJupyterController() { return controller; diff --git a/extensions/query-history/src/main.ts b/extensions/query-history/src/main.ts index 0e9689bdd5..74d5d64dc6 100644 --- a/extensions/query-history/src/main.ts +++ b/extensions/query-history/src/main.ts @@ -104,6 +104,7 @@ export async function activate(context: vscode.ExtensionContext): Promise context.subscriptions.push(vscode.commands.registerCommand('queryHistory.openStorageFolder', async () => { return vscode.env.openExternal(storageUri); })); + context.subscriptions.push(TelemetryReporter); } async function openQuery(item: QueryHistoryItem): Promise { diff --git a/extensions/resource-deployment/src/main.ts b/extensions/resource-deployment/src/main.ts index 8a390a5812..258f67aa4b 100644 --- a/extensions/resource-deployment/src/main.ts +++ b/extensions/resource-deployment/src/main.ts @@ -15,6 +15,7 @@ import { ResourceTypePickerDialog } from './ui/resourceTypePickerDialog'; import * as rd from 'resource-deployment'; import { getExtensionApi } from './api'; import { UriHandlerService } from './services/uriHandlerService'; +import { TelemetryReporter } from './services/telemetryService'; const localize = nls.loadMessageBundle(); @@ -46,9 +47,9 @@ export async function activate(context: vscode.ExtensionContext): Promise { + context.subscriptions.push(vscode.commands.registerCommand('azdata.resource.sql-image.deploy', () => { openDialog('sql-image'); - }); + })); /** * Command to open the Resource Deployment wizard - with options to filter the values shown @@ -59,7 +60,7 @@ export async function activate(context: vscode.ExtensionContext): Promise { + context.subscriptions.push(vscode.commands.registerCommand('azdata.resource.deploy', (defaultResourceTypeName?: string, resourceTypeNameFilters?: string[], optionValuesFilter?: OptionValuesFilter, initialVariableValues?: InitialVariableValues) => { if ((resourceTypeNameFilters && !Array.isArray(resourceTypeNameFilters) || (resourceTypeNameFilters && resourceTypeNameFilters.length > 0 && typeof resourceTypeNameFilters[0] !== 'string'))) { throw new Error('resourceTypeNameFilters must either be undefined or an array of strings'); @@ -76,11 +77,14 @@ export async function activate(context: vscode.ExtensionContext): Promise { + })); + + context.subscriptions.push(vscode.commands.registerCommand('azdata.openNotebookInputDialog', (dialogInfo: NotebookBasedDialogInfo) => { const dialog = new DeploymentInputDialog(notebookService, platformService, toolsService, dialogInfo); dialog.open(); - }); + })); + + context.subscriptions.push(TelemetryReporter); return getExtensionApi(); } diff --git a/extensions/schema-compare/src/extension.ts b/extensions/schema-compare/src/extension.ts index 55913eef55..aef7f1a488 100644 --- a/extensions/schema-compare/src/extension.ts +++ b/extensions/schema-compare/src/extension.ts @@ -6,11 +6,13 @@ import * as vscode from 'vscode'; import * as mssql from 'mssql'; import { SchemaCompareMainWindow } from './schemaCompareMainWindow'; +import { TelemetryReporter } from './telemetry'; export async function activate(extensionContext: vscode.ExtensionContext): Promise { - vscode.commands.registerCommand('schemaCompare.start', async (sourceContext: any, targetContext: any = undefined, comparisonResult: any = undefined) => { await new SchemaCompareMainWindow(undefined, extensionContext, undefined).start(sourceContext, targetContext, comparisonResult); }); - vscode.commands.registerCommand('schemaCompare.runComparison', async (source: mssql.SchemaCompareEndpointInfo | undefined, target: mssql.SchemaCompareEndpointInfo | undefined, runComparison: boolean = false, comparisonResult: mssql.SchemaCompareResult | undefined) => { await new SchemaCompareMainWindow(undefined, extensionContext, undefined).launch(source, target, runComparison, comparisonResult); }); - vscode.commands.registerCommand('schemaCompare.openInScmp', async (fileUri: vscode.Uri) => { await new SchemaCompareMainWindow(undefined, extensionContext, undefined).openScmpFile(fileUri); }); + extensionContext.subscriptions.push(vscode.commands.registerCommand('schemaCompare.start', async (sourceContext: any, targetContext: any = undefined, comparisonResult: any = undefined) => { await new SchemaCompareMainWindow(undefined, extensionContext, undefined).start(sourceContext, targetContext, comparisonResult); })); + extensionContext.subscriptions.push(vscode.commands.registerCommand('schemaCompare.runComparison', async (source: mssql.SchemaCompareEndpointInfo | undefined, target: mssql.SchemaCompareEndpointInfo | undefined, runComparison: boolean = false, comparisonResult: mssql.SchemaCompareResult | undefined) => { await new SchemaCompareMainWindow(undefined, extensionContext, undefined).launch(source, target, runComparison, comparisonResult); })); + extensionContext.subscriptions.push(vscode.commands.registerCommand('schemaCompare.openInScmp', async (fileUri: vscode.Uri) => { await new SchemaCompareMainWindow(undefined, extensionContext, undefined).openScmpFile(fileUri); })); + extensionContext.subscriptions.push(TelemetryReporter); } export function deactivate(): void { diff --git a/extensions/sql-assessment/src/main.ts b/extensions/sql-assessment/src/main.ts index 855cc07e62..fb7a9f85fd 100644 --- a/extensions/sql-assessment/src/main.ts +++ b/extensions/sql-assessment/src/main.ts @@ -6,12 +6,14 @@ import * as vscode from 'vscode'; import MainController from './maincontroller'; +import { TelemetryReporter } from './telemetry'; let mainController: MainController; -export function activate(context: vscode.ExtensionContext) { +export async function activate(context: vscode.ExtensionContext) { mainController = new MainController(context); - mainController.activate(); + await mainController.activate(); + context.subscriptions.push(TelemetryReporter); } // this method is called when your extension is deactivated diff --git a/extensions/sql-bindings/src/extension.ts b/extensions/sql-bindings/src/extension.ts index a251b380bc..afdf5db164 100644 --- a/extensions/sql-bindings/src/extension.ts +++ b/extensions/sql-bindings/src/extension.ts @@ -8,6 +8,7 @@ import { IExtension, BindingType, GetAzureFunctionsResult, ResultStatus, IConnec import { addSqlBinding, createAzureFunction, getAzureFunctions } from './services/azureFunctionsService'; import { launchAddSqlBindingQuickpick } from './dialogs/addSqlBindingQuickpick'; import { promptForBindingType, promptAndUpdateConnectionStringSetting, promptForObjectName, addSqlNugetReferenceToProjectFile } from './common/azureFunctionsUtils'; +import { TelemetryReporter } from './common/telemetry'; export async function activate(context: vscode.ExtensionContext): Promise { // register the add sql binding command @@ -16,6 +17,7 @@ export async function activate(context: vscode.ExtensionContext): Promise { return await createAzureFunction(node); })); + context.subscriptions.push(TelemetryReporter); return { addSqlBinding: async (bindingType: BindingType, filePath: string, functionName: string, objectName: string, connectionStringSetting: string): Promise => { return addSqlBinding(bindingType, filePath, functionName, objectName, connectionStringSetting); diff --git a/extensions/sql-database-projects/src/extension.ts b/extensions/sql-database-projects/src/extension.ts index 0c20dbd7df..a94933136f 100644 --- a/extensions/sql-database-projects/src/extension.ts +++ b/extensions/sql-database-projects/src/extension.ts @@ -7,6 +7,7 @@ import * as vscode from 'vscode'; import { getAzdataApi } from './common/utils'; import MainController from './controllers/mainController'; import { SqlDatabaseProjectProvider } from './projectProvider/projectProvider'; +import { TelemetryReporter } from './common/telemetry'; let controllers: MainController[] = []; @@ -16,7 +17,7 @@ export function activate(context: vscode.ExtensionContext): Promise { widget = new DashboardWidget(context); await widget.register(); + context.subscriptions.push(TelemetryReporter); return widget; }