mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Add initial telemetry for schema compare (#5595)
* add initial telemetry to schema compare * addressing comments
This commit is contained in:
@@ -11,6 +11,7 @@ import * as os from 'os';
|
|||||||
import { SchemaCompareResult } from '../schemaCompareResult';
|
import { SchemaCompareResult } from '../schemaCompareResult';
|
||||||
import { isNullOrUndefined } from 'util';
|
import { isNullOrUndefined } from 'util';
|
||||||
import { existsSync } from 'fs';
|
import { existsSync } from 'fs';
|
||||||
|
import { Telemetry } from '../telemetry';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
const OkButtonText: string = localize('schemaCompareDialog.ok', 'Ok');
|
const OkButtonText: string = localize('schemaCompareDialog.ok', 'Ok');
|
||||||
@@ -137,6 +138,10 @@ export class SchemaCompareDialog {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Telemetry.sendTelemetryEvent('SchemaCompareStart', {
|
||||||
|
'sourceIsDacpac': this.sourceIsDacpac.toString(),
|
||||||
|
'targetIsDacpac': this.targetIsDacpac.toString()
|
||||||
|
});
|
||||||
let schemaCompareResult = new SchemaCompareResult(sourceName, targetName, sourceEndpointInfo, targetEndpointInfo);
|
let schemaCompareResult = new SchemaCompareResult(sourceName, targetName, sourceEndpointInfo, targetEndpointInfo);
|
||||||
schemaCompareResult.start();
|
schemaCompareResult.start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ import * as nls from 'vscode-nls';
|
|||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import { SchemaCompareOptionsDialog } from './dialogs/schemaCompareOptionsDialog';
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import { SchemaCompareOptionsDialog } from './dialogs/schemaCompareOptionsDialog';
|
||||||
|
import { Telemetry } from './telemetry';
|
||||||
|
import { getTelemetryErrorType } from './utils';
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
const diffEditorTitle = localize('schemaCompare.ObjectDefinitionsTitle', 'Object Definitions');
|
const diffEditorTitle = localize('schemaCompare.ObjectDefinitionsTitle', 'Object Definitions');
|
||||||
|
|
||||||
@@ -180,13 +181,21 @@ export class SchemaCompareResult {
|
|||||||
// take updates if any
|
// take updates if any
|
||||||
this.deploymentOptions = this.schemaCompareOptionDialog.deploymentOptions;
|
this.deploymentOptions = this.schemaCompareOptionDialog.deploymentOptions;
|
||||||
}
|
}
|
||||||
|
Telemetry.sendTelemetryEvent('SchemaComparisonStarted');
|
||||||
let service = await SchemaCompareResult.getService('MSSQL');
|
let service = await SchemaCompareResult.getService('MSSQL');
|
||||||
this.comparisonResult = await service.schemaCompare(this.sourceEndpointInfo, this.targetEndpointInfo, azdata.TaskExecutionMode.execute, this.deploymentOptions);
|
this.comparisonResult = await service.schemaCompare(this.sourceEndpointInfo, this.targetEndpointInfo, azdata.TaskExecutionMode.execute, this.deploymentOptions);
|
||||||
if (!this.comparisonResult || !this.comparisonResult.success) {
|
if (!this.comparisonResult || !this.comparisonResult.success) {
|
||||||
|
Telemetry.sendTelemetryEventForError('SchemaComparisonFailed', {
|
||||||
|
'errorType': getTelemetryErrorType(this.comparisonResult.errorMessage),
|
||||||
|
'operationId': this.comparisonResult.operationId
|
||||||
|
});
|
||||||
vscode.window.showErrorMessage(localize('schemaCompare.compareErrorMessage', "Schema Compare failed: {0}", this.comparisonResult.errorMessage ? this.comparisonResult.errorMessage : 'Unknown'));
|
vscode.window.showErrorMessage(localize('schemaCompare.compareErrorMessage', "Schema Compare failed: {0}", this.comparisonResult.errorMessage ? this.comparisonResult.errorMessage : 'Unknown'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Telemetry.sendTelemetryEvent('SchemaComparisonFinished', {
|
||||||
|
'endTime': Date.now().toString(),
|
||||||
|
'operationId': this.comparisonResult.operationId
|
||||||
|
});
|
||||||
|
|
||||||
let data = this.getAllDifferences(this.comparisonResult.differences);
|
let data = this.getAllDifferences(this.comparisonResult.differences);
|
||||||
|
|
||||||
@@ -418,12 +427,24 @@ export class SchemaCompareResult {
|
|||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.generateScriptButton.onDidClick(async (click) => {
|
this.generateScriptButton.onDidClick(async (click) => {
|
||||||
|
Telemetry.sendTelemetryEvent('SchemaCompareGenerateScriptStarted', {
|
||||||
|
'startTime:': Date.now().toString(),
|
||||||
|
'operationId': this.comparisonResult.operationId
|
||||||
|
});
|
||||||
let service = await SchemaCompareResult.getService('MSSQL');
|
let service = await SchemaCompareResult.getService('MSSQL');
|
||||||
let result = await service.schemaCompareGenerateScript(this.comparisonResult.operationId, this.targetEndpointInfo.serverName, this.targetEndpointInfo.databaseName, azdata.TaskExecutionMode.script);
|
let result = await service.schemaCompareGenerateScript(this.comparisonResult.operationId, this.targetEndpointInfo.serverName, this.targetEndpointInfo.databaseName, azdata.TaskExecutionMode.script);
|
||||||
if (!result || !result.success) {
|
if (!result || !result.success) {
|
||||||
|
Telemetry.sendTelemetryEvent('SchemaCompareGenerateScriptFailed', {
|
||||||
|
'errorType': getTelemetryErrorType(result.errorMessage),
|
||||||
|
'operationId': this.comparisonResult.operationId
|
||||||
|
});
|
||||||
vscode.window.showErrorMessage(
|
vscode.window.showErrorMessage(
|
||||||
localize('schemaCompare.generateScriptErrorMessage', "Generate script failed: '{0}'", (result && result.errorMessage) ? result.errorMessage : 'Unknown'));
|
localize('schemaCompare.generateScriptErrorMessage', "Generate script failed: '{0}'", (result && result.errorMessage) ? result.errorMessage : 'Unknown'));
|
||||||
}
|
}
|
||||||
|
Telemetry.sendTelemetryEvent('SchemaCompareGenerateScriptEnded', {
|
||||||
|
'endTime:': Date.now().toString(),
|
||||||
|
'operationId': this.comparisonResult.operationId
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,6 +459,9 @@ export class SchemaCompareResult {
|
|||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.optionsButton.onDidClick(async (click) => {
|
this.optionsButton.onDidClick(async (click) => {
|
||||||
|
Telemetry.sendTelemetryEvent('SchemaCompareOptionsOpened', {
|
||||||
|
'operationId': this.comparisonResult.operationId
|
||||||
|
});
|
||||||
//restore options from last time
|
//restore options from last time
|
||||||
if (this.schemaCompareOptionDialog && this.schemaCompareOptionDialog.deploymentOptions) {
|
if (this.schemaCompareOptionDialog && this.schemaCompareOptionDialog.deploymentOptions) {
|
||||||
this.deploymentOptions = this.schemaCompareOptionDialog.deploymentOptions;
|
this.deploymentOptions = this.schemaCompareOptionDialog.deploymentOptions;
|
||||||
@@ -459,12 +483,24 @@ export class SchemaCompareResult {
|
|||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.applyButton.onDidClick(async (click) => {
|
this.applyButton.onDidClick(async (click) => {
|
||||||
|
Telemetry.sendTelemetryEvent('SchemaCompareApplyStarted', {
|
||||||
|
'startTime': Date.now().toString(),
|
||||||
|
'operationId': this.comparisonResult.operationId
|
||||||
|
});
|
||||||
let service = await SchemaCompareResult.getService('MSSQL');
|
let service = await SchemaCompareResult.getService('MSSQL');
|
||||||
let result = await service.schemaComparePublishChanges(this.comparisonResult.operationId, this.targetEndpointInfo.serverName, this.targetEndpointInfo.databaseName, azdata.TaskExecutionMode.execute);
|
let result = await service.schemaComparePublishChanges(this.comparisonResult.operationId, this.targetEndpointInfo.serverName, this.targetEndpointInfo.databaseName, azdata.TaskExecutionMode.execute);
|
||||||
if (!result || !result.success) {
|
if (!result || !result.success) {
|
||||||
|
Telemetry.sendTelemetryEvent('SchemaCompareApplyFailed', {
|
||||||
|
'errorType': getTelemetryErrorType(result.errorMessage),
|
||||||
|
'operationId': this.comparisonResult.operationId
|
||||||
|
});
|
||||||
vscode.window.showErrorMessage(
|
vscode.window.showErrorMessage(
|
||||||
localize('schemaCompare.updateErrorMessage', "Schema Compare Apply failed '{0}'", result.errorMessage ? result.errorMessage : 'Unknown'));
|
localize('schemaCompare.updateErrorMessage', "Schema Compare Apply failed '{0}'", result.errorMessage ? result.errorMessage : 'Unknown'));
|
||||||
}
|
}
|
||||||
|
Telemetry.sendTelemetryEvent('SchemaCompareApplyEnded', {
|
||||||
|
'endTime': Date.now().toString(),
|
||||||
|
'operationId': this.comparisonResult.operationId
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,6 +532,8 @@ export class SchemaCompareResult {
|
|||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.switchButton.onDidClick(async (click) => {
|
this.switchButton.onDidClick(async (click) => {
|
||||||
|
Telemetry.sendTelemetryEvent('SchemaCompareSwitch');
|
||||||
|
|
||||||
// switch source and target
|
// switch source and target
|
||||||
[this.sourceEndpointInfo, this.targetEndpointInfo] = [this.targetEndpointInfo, this.sourceEndpointInfo];
|
[this.sourceEndpointInfo, this.targetEndpointInfo] = [this.targetEndpointInfo, this.sourceEndpointInfo];
|
||||||
[this.sourceName, this.targetName] = [this.targetName, this.sourceName];
|
[this.sourceName, this.targetName] = [this.targetName, this.sourceName];
|
||||||
|
|||||||
102
extensions/schema-compare/src/telemetry.ts
Normal file
102
extensions/schema-compare/src/telemetry.ts
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
import TelemetryReporter from 'vscode-extension-telemetry';
|
||||||
|
|
||||||
|
import * as Utils from './utils';
|
||||||
|
|
||||||
|
const packageJson = require('../package.json');
|
||||||
|
|
||||||
|
export interface ITelemetryEventProperties {
|
||||||
|
[key: string]: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ITelemetryEventMeasures {
|
||||||
|
[key: string]: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 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<boolean>('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 a general error
|
||||||
|
* @param err The error to log
|
||||||
|
*/
|
||||||
|
public static sendTelemetryEventForError(err: string, properties?: ITelemetryEventProperties): void {
|
||||||
|
this.sendTelemetryEvent('Error', { error: err, ...properties });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
34
extensions/schema-compare/src/utils.ts
Normal file
34
extensions/schema-compare/src/utils.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
export interface IPackageInfo {
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
aiKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPackageInfo(packageJson: any): IPackageInfo {
|
||||||
|
if (packageJson) {
|
||||||
|
return {
|
||||||
|
name: packageJson.name,
|
||||||
|
version: packageJson.version,
|
||||||
|
aiKey: packageJson.aiKey
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map an error message into a short name for the type of error.
|
||||||
|
* @param msg The error message to map
|
||||||
|
*/
|
||||||
|
export function getTelemetryErrorType(msg: string): string {
|
||||||
|
if (msg.indexOf('Object reference not set to an instance of an object') !== -1) {
|
||||||
|
return 'ObjectReferenceNotSet';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 'Other';
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user