telemetry for tde user actions (#22474)

* telemetry for user actions

* remove unused action

* try catch around admin function
This commit is contained in:
junierch
2023-03-27 17:48:47 -04:00
committed by GitHub
parent e80c6f2dcb
commit e741fa0bbd
7 changed files with 71 additions and 20 deletions

View File

@@ -13,6 +13,7 @@ import * as constants from '../constants/strings';
import { logError, TelemetryViews } from '../telemetry'; import { logError, TelemetryViews } from '../telemetry';
import { AdsMigrationStatus } from '../dashboard/tabBase'; import { AdsMigrationStatus } from '../dashboard/tabBase';
import { getMigrationMode, getMigrationStatus, getMigrationTargetType, hasRestoreBlockingReason, PipelineStatusCodes } from '../constants/helper'; import { getMigrationMode, getMigrationStatus, getMigrationTargetType, hasRestoreBlockingReason, PipelineStatusCodes } from '../constants/helper';
import * as os from 'os';
export type TargetServerType = azure.SqlVMServer | azureResource.AzureSqlManagedInstance | azure.AzureSqlDatabaseServer; export type TargetServerType = azure.SqlVMServer | azureResource.AzureSqlManagedInstance | azure.AzureSqlDatabaseServer;
@@ -923,3 +924,20 @@ export async function promptUserForFolder(): Promise<string> {
return ''; return '';
} }
export function isWindows(): boolean { return (os.platform() === 'win32') }
export async function isAdmin(): Promise<boolean> {
let isAdmin: boolean = false;
try {
if (isWindows()) {
isAdmin = (await import('native-is-elevated'))();
} else {
isAdmin = process.getuid() === 0;
}
} catch (e) {
//Ignore error and return false;
}
return isAdmin;
}

View File

@@ -6,7 +6,8 @@
import * as azdata from 'azdata'; import * as azdata from 'azdata';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as constants from '../../constants/strings'; import * as constants from '../../constants/strings';
import { logError, TelemetryErrorName, TelemetryViews } from '../../telemetry'; import * as utils from '../../api/utils';
import { logError, TelemetryAction, TelemetryViews, sendSqlMigrationActionEvent, getTelemetryProps } from '../../telemetry';
import { EOL } from 'os'; import { EOL } from 'os';
import { MigrationStateModel, OperationResult } from '../../models/stateMachine'; import { MigrationStateModel, OperationResult } from '../../models/stateMachine';
import { IconPathHelper } from '../../constants/iconPathHelper'; import { IconPathHelper } from '../../constants/iconPathHelper';
@@ -317,12 +318,28 @@ export class TdeMigrationDialog {
}; };
this._model.tdeMigrationConfig.setTdeMigrationResult(this._tdeMigrationResult); // Set value on success. this._model.tdeMigrationConfig.setTdeMigrationResult(this._tdeMigrationResult); // Set value on success.
sendSqlMigrationActionEvent(
TelemetryViews.TdeMigrationDialog,
TelemetryAction.TdeMigrationSuccess,
{
...getTelemetryProps(this._model)
},
{}
);
} }
else { else {
this._dialog!.okButton.enabled = false; this._dialog!.okButton.enabled = false;
const errorDetails = operationResult.errors.join(EOL);
logError(TelemetryViews.MigrationLocalStorage, TelemetryErrorName.StartMigrationFailed, errorDetails); sendSqlMigrationActionEvent(
TelemetryViews.TdeMigrationDialog,
TelemetryAction.TdeMigrationFailures,
{
...getTelemetryProps(this._model),
'runningAsAdmin': (await utils.isAdmin()).toString()
},
{}
);
} }
this._startMigrationLoader.loading = false; this._startMigrationLoader.loading = false;
@@ -339,6 +356,8 @@ export class TdeMigrationDialog {
this._copyButton.enabled = false; this._copyButton.enabled = false;
this._dialog!.okButton.enabled = false; this._dialog!.okButton.enabled = false;
this._progressReportText.value = ''; this._progressReportText.value = '';
logError(TelemetryViews.TdeMigrationDialog, TelemetryAction.TdeMigrationClientException, error);
} }
this._headingText.value = constants.TDE_MIGRATE_RESULTS_HEADING_COMPLETED; this._headingText.value = constants.TDE_MIGRATE_RESULTS_HEADING_COMPLETED;
@@ -433,13 +452,17 @@ export class TdeMigrationDialog {
} }
} }
private async _updateTableResultRow(dbName: string, succeeded: boolean, message: string): Promise<void> { private async _updateTableResultRow(dbName: string, succeeded: boolean, message: string, statusCode: string): Promise<void> {
if (!this._dbRowsMap.has(dbName)) { if (!this._dbRowsMap.has(dbName)) {
return; //Table not found return; //Table not found
} }
this._updateValidationResultRow(dbName, succeeded, message); this._updateValidationResultRow(dbName, succeeded, message);
if (!succeeded) {
logError(TelemetryViews.TdeMigrationDialog, statusCode, {});
}
// Update the table // Update the table
await this._updateTableData(); await this._updateTableData();
@@ -522,7 +545,4 @@ export class TdeMigrationDialog {
return IconPathHelper.notStartedMigration; return IconPathHelper.notStartedMigration;
} }
} }
} }

View File

@@ -947,7 +947,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
public async startTdeMigration( public async startTdeMigration(
accessToken: string, accessToken: string,
reportUpdate: (dbName: string, succeeded: boolean, message: string) => Promise<void>): Promise<OperationResult<TdeMigrationDbResult[]>> { reportUpdate: (dbName: string, succeeded: boolean, message: string, statusCode: string) => Promise<void>): Promise<OperationResult<TdeMigrationDbResult[]>> {
const tdeEnabledDatabases = this.tdeMigrationConfig.getTdeEnabledDatabases(); const tdeEnabledDatabases = this.tdeMigrationConfig.getTdeEnabledDatabases();
const connectionString = await getSourceConnectionString(); const connectionString = await getSourceConnectionString();

View File

@@ -500,7 +500,7 @@ export interface ISqlMigrationService {
targetManagedInstanceName: string, targetManagedInstanceName: string,
networkSharePath: string, networkSharePath: string,
accessToken: string, accessToken: string,
reportUpdate: (dbName: string, succeeded: boolean, message: string) => void): Promise<TdeMigrationResult | undefined>; reportUpdate: (dbName: string, succeeded: boolean, message: string, statusCode: string) => void): Promise<TdeMigrationResult | undefined>;
} }
export interface TdeMigrationRequest { export interface TdeMigrationRequest {
@@ -547,4 +547,5 @@ export interface TdeMigrateProgressParams {
name: string; name: string;
success: boolean; success: boolean;
message: string; message: string;
statusCode: string;
} }

View File

@@ -26,7 +26,7 @@ export abstract class MigrationExtensionService extends SqlOpsFeature<undefined>
} }
export class SqlMigrationService extends MigrationExtensionService implements contracts.ISqlMigrationService { export class SqlMigrationService extends MigrationExtensionService implements contracts.ISqlMigrationService {
private _reportUpdate: ((dbName: string, succeeded: boolean, error: string) => void) | undefined = undefined; private _reportUpdate: ((dbName: string, succeeded: boolean, error: string, statusCode: string) => void) | undefined = undefined;
override providerId = ApiType.SqlMigrationProvider; override providerId = ApiType.SqlMigrationProvider;
@@ -58,7 +58,7 @@ export class SqlMigrationService extends MigrationExtensionService implements co
if (this._reportUpdate === undefined) { if (this._reportUpdate === undefined) {
return; return;
} }
this._reportUpdate(e.name, e.success, e.message); this._reportUpdate(e.name, e.success, e.message, e.statusCode ?? '');
}); });
} }
@@ -288,7 +288,7 @@ export class SqlMigrationService extends MigrationExtensionService implements co
targetManagedInstanceName: string, targetManagedInstanceName: string,
networkSharePath: string, networkSharePath: string,
accessToken: string, accessToken: string,
reportUpdate: (dbName: string, succeeded: boolean, message: string) => void): Promise<contracts.TdeMigrationResult | undefined> { reportUpdate: (dbName: string, succeeded: boolean, message: string, statusCode: string) => void): Promise<contracts.TdeMigrationResult | undefined> {
this._reportUpdate = reportUpdate; this._reportUpdate = reportUpdate;
let params: contracts.TdeMigrationParams = { let params: contracts.TdeMigrationParams = {
@@ -306,7 +306,7 @@ export class SqlMigrationService extends MigrationExtensionService implements co
try { try {
// This call needs to be awaited so, the updates are sent during the execution of the task. // This call needs to be awaited so, the updates are sent during the execution of the task.
// If the task is not await, the finally block will execute and no update will be sent. // If the task is not awaited, the finally block will execute and no updates will be sent.
const result = await this._client.sendRequest(contracts.TdeMigrateRequest.type, params); const result = await this._client.sendRequest(contracts.TdeMigrateRequest.type, params);
return result; return result;
} }

View File

@@ -44,6 +44,7 @@ export enum TelemetryViews {
LoginMigrationSelectorPage = 'LoginMigrationSelectorPage', LoginMigrationSelectorPage = 'LoginMigrationSelectorPage',
LoginMigrationStatusPage = 'LoginMigrationStatusPage', LoginMigrationStatusPage = 'LoginMigrationStatusPage',
TdeConfigurationDialog = 'TdeConfigurationDialog', TdeConfigurationDialog = 'TdeConfigurationDialog',
TdeMigrationDialog = 'TdeMigrationDialog',
ValidIrDialog = 'validIrDialog', ValidIrDialog = 'validIrDialog',
} }
@@ -76,10 +77,11 @@ export enum TelemetryAction {
OpenLoginMigrationWizard = 'OpenLoginMigrationWizard', OpenLoginMigrationWizard = 'OpenLoginMigrationWizard',
LoginMigrationStarted = 'LoginMigrationStarted', LoginMigrationStarted = 'LoginMigrationStarted',
LoginMigrationCompleted = 'LoginMigrationCompleted', LoginMigrationCompleted = 'LoginMigrationCompleted',
} TdeMigrationSuccess = 'TdeMigrationSuccess',
TdeMigrationFailures = 'TdeMigrationFailures',
export enum TelemetryErrorName { TdeMigrationClientException = 'TdeMigrationClientException',
StartMigrationFailed = 'StartMigrationFailed' TdeConfigurationUseADS = 'TdeConfigurationUseADS',
TdeConfigurationIgnoreADS = 'TdeConfigurationIgnoreADS'
} }
export function logError(telemetryView: TelemetryViews, err: string, error: any): void { export function logError(telemetryView: TelemetryViews, err: string, error: any): void {

View File

@@ -18,10 +18,9 @@ import { IconPath, IconPathHelper } from '../constants/iconPathHelper';
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController'; import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
import * as styles from '../constants/styles'; import * as styles from '../constants/styles';
import { SkuEditParametersDialog } from '../dialog/skuRecommendationResults/skuEditParametersDialog'; import { SkuEditParametersDialog } from '../dialog/skuRecommendationResults/skuEditParametersDialog';
import { logError, TelemetryViews } from '../telemetry'; import { logError, TelemetryViews, TelemetryAction, sendSqlMigrationActionEvent, getTelemetryProps } from '../telemetry';
import { TdeConfigurationDialog } from '../dialog/tdeConfiguration/tdeConfigurationDialog'; import { TdeConfigurationDialog } from '../dialog/tdeConfiguration/tdeConfigurationDialog';
import { TdeMigrationModel } from '../models/tdeModels'; import { TdeMigrationModel } from '../models/tdeModels';
import * as os from 'os';
import { getSourceConnectionProfile } from '../api/sqlUtils'; import { getSourceConnectionProfile } from '../api/sqlUtils';
export interface Product { export interface Product {
@@ -817,7 +816,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
if (this._matchWithEncryptedDatabases(encryptedDbFound)) { if (this._matchWithEncryptedDatabases(encryptedDbFound)) {
this.migrationStateModel.tdeMigrationConfig = this._previousMiTdeMigrationConfig; this.migrationStateModel.tdeMigrationConfig = this._previousMiTdeMigrationConfig;
} else { } else {
if (os.platform() !== 'win32') //Only available for windows for now. if (!utils.isWindows()) //Only available for windows for now.
return; return;
//Set encrypted databases //Set encrypted databases
@@ -843,6 +842,17 @@ export class SKURecommendationPage extends MigrationWizardPage {
const tdeMsg = (this.migrationStateModel.tdeMigrationConfig.isTdeMigrationMethodAdsConfirmed()) ? constants.TDE_WIZARD_MSG_TDE : constants.TDE_WIZARD_MSG_MANUAL; const tdeMsg = (this.migrationStateModel.tdeMigrationConfig.isTdeMigrationMethodAdsConfirmed()) ? constants.TDE_WIZARD_MSG_TDE : constants.TDE_WIZARD_MSG_MANUAL;
this._tdedatabaseSelectedHelperText.value = constants.TDE_MSG_DATABASES_SELECTED(this.migrationStateModel.tdeMigrationConfig.getTdeEnabledDatabasesCount(), tdeMsg); this._tdedatabaseSelectedHelperText.value = constants.TDE_MSG_DATABASES_SELECTED(this.migrationStateModel.tdeMigrationConfig.getTdeEnabledDatabasesCount(), tdeMsg);
const tdeTelemetryAction = (this.migrationStateModel.tdeMigrationConfig.isTdeMigrationMethodAdsConfirmed()) ? TelemetryAction.TdeConfigurationUseADS : TelemetryAction.TdeConfigurationIgnoreADS;
sendSqlMigrationActionEvent(
TelemetryViews.TdeConfigurationDialog,
tdeTelemetryAction,
{
...getTelemetryProps(this.migrationStateModel)
},
{}
);
return this._tdeEditButton.focus(); return this._tdeEditButton.focus();
} }