Adding start and end activity function (#18328)
* Adding standard activity tracking method * renaming interfaces * Fixing more helper functions * Removing duplicate identifier * adding update activity and code cleanup
This commit is contained in:
Родитель
2a3274e27d
Коммит
1360ff7ece
|
@ -5,6 +5,12 @@
|
|||
|
||||
import * as vscode from "vscode";
|
||||
|
||||
import {
|
||||
ActivityStatus,
|
||||
ActivityObject,
|
||||
TelemetryActions,
|
||||
TelemetryViews,
|
||||
} from "../sharedInterfaces/telemetry";
|
||||
import {
|
||||
AuthenticationType,
|
||||
AzureSubscriptionInfo,
|
||||
|
@ -34,7 +40,11 @@ import {
|
|||
fetchServersFromAzure,
|
||||
promptForAzureSubscriptionFilter,
|
||||
} from "./azureHelper";
|
||||
import { getErrorMessage } from "../utils/utils";
|
||||
import {
|
||||
sendActionEvent,
|
||||
sendErrorEvent,
|
||||
startActivity,
|
||||
} from "../telemetry/telemetry";
|
||||
|
||||
import { ApiStatus } from "../sharedInterfaces/webview";
|
||||
import { AzureController } from "../azure/azureController";
|
||||
|
@ -49,12 +59,8 @@ import { UserSurvey } from "../nps/userSurvey";
|
|||
import VscodeWrapper from "../controllers/vscodeWrapper";
|
||||
import { connectionCertValidationFailedErrorCode } from "./connectionConstants";
|
||||
import { getConnectionDisplayName } from "../models/connectionInfo";
|
||||
import { getErrorMessage } from "../utils/utils";
|
||||
import { l10n } from "vscode";
|
||||
import {
|
||||
TelemetryActions,
|
||||
TelemetryViews,
|
||||
} from "../sharedInterfaces/telemetry";
|
||||
import { sendActionEvent, sendErrorEvent } from "../telemetry/telemetry";
|
||||
|
||||
export class ConnectionDialogWebviewController extends ReactWebviewPanelController<
|
||||
ConnectionDialogWebviewState,
|
||||
|
@ -1118,6 +1124,7 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
|
|||
private async loadAzureSubscriptions(
|
||||
state: ConnectionDialogWebviewState,
|
||||
): Promise<Map<string, AzureSubscription[]> | undefined> {
|
||||
let endActivity: ActivityObject;
|
||||
try {
|
||||
const auth = await confirmVscodeAzureSignin();
|
||||
|
||||
|
@ -1138,7 +1145,10 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
|
|||
string[] | undefined
|
||||
>(azureSubscriptionFilterConfigKey) !== undefined;
|
||||
|
||||
const startTime = Date.now();
|
||||
endActivity = startActivity(
|
||||
TelemetryViews.ConnectionDialog,
|
||||
TelemetryActions.LoadAzureSubscriptions,
|
||||
);
|
||||
|
||||
this._azureSubscriptions = new Map(
|
||||
(await auth.getSubscriptions(shouldUseFilter)).map((s) => [
|
||||
|
@ -1166,16 +1176,13 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
|
|||
state.azureSubscriptions = subs;
|
||||
state.loadingAzureSubscriptionsStatus = ApiStatus.Loaded;
|
||||
|
||||
sendActionEvent(
|
||||
TelemetryViews.ConnectionDialog,
|
||||
TelemetryActions.LoadAzureSubscriptions,
|
||||
endActivity.end(
|
||||
ActivityStatus.Succeeded,
|
||||
undefined, // additionalProperties
|
||||
{
|
||||
subscriptionCount: subs.length,
|
||||
msToLoadSubscriptions: Date.now() - startTime,
|
||||
},
|
||||
);
|
||||
|
||||
this.updateState();
|
||||
|
||||
return tenantSubMap;
|
||||
|
@ -1183,14 +1190,7 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
|
|||
state.formError = l10n.t("Error loading Azure subscriptions.");
|
||||
state.loadingAzureSubscriptionsStatus = ApiStatus.Error;
|
||||
console.error(state.formError + "\n" + getErrorMessage(error));
|
||||
|
||||
sendErrorEvent(
|
||||
TelemetryViews.ConnectionDialog,
|
||||
TelemetryActions.LoadAzureSubscriptions,
|
||||
error,
|
||||
false, // includeErrorMessage
|
||||
);
|
||||
|
||||
endActivity.endFailed(error, false);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
@ -1198,6 +1198,10 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
|
|||
private async loadAllAzureServers(
|
||||
state: ConnectionDialogWebviewState,
|
||||
): Promise<void> {
|
||||
const endActivity = startActivity(
|
||||
TelemetryViews.ConnectionDialog,
|
||||
TelemetryActions.LoadAzureServers,
|
||||
);
|
||||
try {
|
||||
const tenantSubMap = await this.loadAzureSubscriptions(state);
|
||||
|
||||
|
@ -1213,9 +1217,6 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
|
|||
state.loadingAzureServersStatus = ApiStatus.Loading;
|
||||
state.azureServers = [];
|
||||
this.updateState();
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
const promiseArray: Promise<void>[] = [];
|
||||
for (const t of tenantSubMap.keys()) {
|
||||
for (const s of tenantSubMap.get(t)) {
|
||||
|
@ -1228,14 +1229,11 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
|
|||
}
|
||||
}
|
||||
await Promise.all(promiseArray);
|
||||
|
||||
sendActionEvent(
|
||||
TelemetryViews.ConnectionDialog,
|
||||
TelemetryActions.LoadAzureServers,
|
||||
endActivity.end(
|
||||
ActivityStatus.Succeeded,
|
||||
undefined, // additionalProperties
|
||||
{
|
||||
subscriptionCount: promiseArray.length,
|
||||
msToLoadServers: Date.now() - startTime,
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -1247,13 +1245,10 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
|
|||
state.loadingAzureServersStatus = ApiStatus.Error;
|
||||
console.error(state.formError + "\n" + getErrorMessage(error));
|
||||
|
||||
sendErrorEvent(
|
||||
TelemetryViews.ConnectionDialog,
|
||||
TelemetryActions.LoadAzureServers,
|
||||
endActivity.endFailed(
|
||||
error,
|
||||
false, // includeErrorMessage
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
import * as vscode from "vscode";
|
||||
|
||||
import {
|
||||
ActivityStatus,
|
||||
TelemetryActions,
|
||||
TelemetryViews,
|
||||
} from "../sharedInterfaces/telemetry";
|
||||
|
@ -13,7 +14,11 @@ import {
|
|||
WebviewTelemetryActionEvent,
|
||||
WebviewTelemetryErrorEvent,
|
||||
} from "../sharedInterfaces/webview";
|
||||
import { sendActionEvent, sendErrorEvent } from "../telemetry/telemetry";
|
||||
import {
|
||||
sendActionEvent,
|
||||
sendErrorEvent,
|
||||
startActivity,
|
||||
} from "../telemetry/telemetry";
|
||||
|
||||
import { getNonce } from "../utils/utils";
|
||||
|
||||
|
@ -47,36 +52,68 @@ export abstract class ReactWebviewBaseController<State, Reducers>
|
|||
>;
|
||||
private _isFirstLoad: boolean = true;
|
||||
private _loadStartTime: number = Date.now();
|
||||
private _endLoadActivity = startActivity(
|
||||
TelemetryViews.WebviewController,
|
||||
TelemetryActions.Load,
|
||||
);
|
||||
private _onDisposed: vscode.EventEmitter<void> =
|
||||
new vscode.EventEmitter<void>();
|
||||
public readonly onDisposed: vscode.Event<void> = this._onDisposed.event;
|
||||
protected _webviewMessageHandler = async (message) => {
|
||||
if (message.type === "request") {
|
||||
const endActivity = startActivity(
|
||||
TelemetryViews.WebviewController,
|
||||
TelemetryActions.WebviewRequest,
|
||||
);
|
||||
const handler = this._webviewRequestHandlers[message.method];
|
||||
if (handler) {
|
||||
const startTime = Date.now();
|
||||
const result = await handler(message.params);
|
||||
this.postMessage({ type: "response", id: message.id, result });
|
||||
const endTime = Date.now();
|
||||
sendActionEvent(
|
||||
TelemetryViews.WebviewController,
|
||||
TelemetryActions.WebviewRequest,
|
||||
{
|
||||
try {
|
||||
const result = await handler(message.params);
|
||||
this.postMessage({
|
||||
type: "response",
|
||||
id: message.id,
|
||||
result,
|
||||
});
|
||||
endActivity.end(ActivityStatus.Succeeded, {
|
||||
type: this._sourceFile,
|
||||
method: message.method,
|
||||
reducer:
|
||||
message.method === "action"
|
||||
? message.params.type
|
||||
: undefined,
|
||||
},
|
||||
{
|
||||
durationMs: endTime - startTime,
|
||||
},
|
||||
);
|
||||
});
|
||||
} catch (error) {
|
||||
endActivity.endFailed(
|
||||
error,
|
||||
false,
|
||||
"RequestHandlerFailed",
|
||||
"RequestHandlerFailed",
|
||||
{
|
||||
type: this._sourceFile,
|
||||
method: message.method,
|
||||
reducer:
|
||||
message.method === "action"
|
||||
? message.params.type
|
||||
: undefined,
|
||||
},
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
const error = new Error(
|
||||
`No handler registered for method ${message.method}`,
|
||||
);
|
||||
endActivity.endFailed(
|
||||
error,
|
||||
true,
|
||||
"NoHandlerRegistered",
|
||||
"NoHandlerRegistered",
|
||||
{
|
||||
type: this._sourceFile,
|
||||
method: message.method,
|
||||
},
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -189,16 +226,9 @@ export abstract class ReactWebviewBaseController<State, Reducers>
|
|||
"\n" +
|
||||
`Total time: ${timeToLoad} ms`,
|
||||
);
|
||||
sendActionEvent(
|
||||
TelemetryViews.WebviewController,
|
||||
TelemetryActions.Load,
|
||||
{
|
||||
type: this._sourceFile,
|
||||
},
|
||||
{
|
||||
durationMs: timeToLoad,
|
||||
},
|
||||
);
|
||||
this._endLoadActivity.end(ActivityStatus.Succeeded, {
|
||||
type: this._sourceFile,
|
||||
});
|
||||
this._isFirstLoad = false;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -58,3 +58,68 @@ export enum TelemetryActions {
|
|||
LoadAzureSubscriptions = "LoadAzureSubscriptions",
|
||||
OpenExecutionPlan = "OpenExecutionPlan",
|
||||
}
|
||||
|
||||
/**
|
||||
* The status of an activity
|
||||
*/
|
||||
export enum ActivityStatus {
|
||||
Succeeded = "Succeeded",
|
||||
Pending = "Pending",
|
||||
Failed = "Failed",
|
||||
Canceled = "Canceled",
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish an activity. This should be called when the activity is complete to send the final telemetry event
|
||||
*/
|
||||
export type FinishActivity = (
|
||||
activityStatus: Exclude<ActivityStatus, ActivityStatus.Failed>,
|
||||
additionalProperties?: Record<string, string>,
|
||||
additionalMeasurements?: Record<string, number>,
|
||||
) => void;
|
||||
|
||||
/**
|
||||
* Finish an activity with a failure. This should be called when the activity fails to send the final telemetry event
|
||||
*/
|
||||
export type FinishActivityFailed = (
|
||||
error?: Error,
|
||||
includeErrorMessage?: boolean,
|
||||
errorCode?: string,
|
||||
errorType?: string,
|
||||
additionalProperties?: Record<string, string>,
|
||||
additionalMeasurements?: Record<string, number>,
|
||||
) => void;
|
||||
|
||||
/**
|
||||
* Update an activity. This should be called when the activity is still in progress to send intermediate telemetry events
|
||||
*/
|
||||
export type UpdateActivity = (
|
||||
additionalProperties?: Record<string, string>,
|
||||
additionalMeasurements?: Record<string, number>,
|
||||
) => void;
|
||||
|
||||
/**
|
||||
* An object that contains the functions to update and finish an activity. This is returned when an activity is started
|
||||
*/
|
||||
export type ActivityObject = {
|
||||
/**
|
||||
* Update the activity with additional properties and measurements
|
||||
*/
|
||||
update: UpdateActivity;
|
||||
/**
|
||||
* Finish the activity
|
||||
*/
|
||||
end: FinishActivity;
|
||||
/**
|
||||
* Finish the activity with a failure
|
||||
*/
|
||||
endFailed: FinishActivityFailed;
|
||||
/**
|
||||
* The correlation id for the activity
|
||||
*/
|
||||
correlationId: string;
|
||||
/**
|
||||
* The start time of the activity generated by performance.now()
|
||||
*/
|
||||
startTime: number;
|
||||
};
|
||||
|
|
|
@ -11,8 +11,9 @@ import * as designer from "../sharedInterfaces/tableDesigner";
|
|||
import UntitledSqlDocumentService from "../controllers/untitledSqlDocumentService";
|
||||
import { getDesignerView } from "./tableDesignerTabDefinition";
|
||||
import { TreeNodeInfo } from "../objectExplorer/treeNodeInfo";
|
||||
import { sendActionEvent } from "../telemetry/telemetry";
|
||||
import { sendActionEvent, startActivity } from "../telemetry/telemetry";
|
||||
import {
|
||||
ActivityStatus,
|
||||
TelemetryActions,
|
||||
TelemetryViews,
|
||||
} from "../sharedInterfaces/telemetry";
|
||||
|
@ -70,7 +71,6 @@ export class TableDesignerWebviewController extends ReactWebviewPanelController<
|
|||
}
|
||||
|
||||
private async initialize() {
|
||||
const intializeStartTime = Date.now();
|
||||
if (!this._targetNode) {
|
||||
await vscode.window.showErrorMessage(
|
||||
"Unable to find object explorer node",
|
||||
|
@ -109,6 +109,16 @@ export class TableDesignerWebviewController extends ReactWebviewPanelController<
|
|||
return;
|
||||
}
|
||||
|
||||
const endActivity = startActivity(
|
||||
TelemetryViews.TableDesigner,
|
||||
TelemetryActions.Initialize,
|
||||
this._correlationId,
|
||||
{
|
||||
correlationId: this._correlationId,
|
||||
isEdit: this._isEdit.toString(),
|
||||
},
|
||||
);
|
||||
|
||||
try {
|
||||
let tableInfo: designer.TableInfo;
|
||||
if (this._isEdit) {
|
||||
|
@ -135,28 +145,17 @@ export class TableDesignerWebviewController extends ReactWebviewPanelController<
|
|||
};
|
||||
}
|
||||
this.panel.title = tableInfo.title;
|
||||
const intializeData =
|
||||
const initializeResult =
|
||||
await this._tableDesignerService.initializeTableDesigner(
|
||||
tableInfo,
|
||||
);
|
||||
const intializeEndTime = Date.now();
|
||||
sendActionEvent(
|
||||
TelemetryViews.TableDesigner,
|
||||
TelemetryActions.Initialize,
|
||||
{
|
||||
correlationId: this._correlationId,
|
||||
isEdit: this._isEdit.toString(),
|
||||
},
|
||||
{
|
||||
durationMs: intializeEndTime - intializeStartTime,
|
||||
},
|
||||
);
|
||||
intializeData.tableInfo.database = databaseName ?? "master";
|
||||
endActivity.end(ActivityStatus.Succeeded);
|
||||
initializeResult.tableInfo.database = databaseName ?? "master";
|
||||
this.state = {
|
||||
tableInfo: tableInfo,
|
||||
view: getDesignerView(intializeData.view),
|
||||
model: intializeData.viewModel,
|
||||
issues: intializeData.issues,
|
||||
view: getDesignerView(initializeResult.view),
|
||||
model: initializeResult.viewModel,
|
||||
issues: initializeResult.issues,
|
||||
isValid: true,
|
||||
tabStates: {
|
||||
mainPaneTab: designer.DesignerMainPaneTabs.Columns,
|
||||
|
@ -168,6 +167,7 @@ export class TableDesignerWebviewController extends ReactWebviewPanelController<
|
|||
},
|
||||
};
|
||||
} catch (e) {
|
||||
endActivity.endFailed(e, false);
|
||||
this.state.apiState.initializeState = designer.LoadState.Error;
|
||||
this.state = this.state;
|
||||
}
|
||||
|
@ -235,6 +235,14 @@ export class TableDesignerWebviewController extends ReactWebviewPanelController<
|
|||
});
|
||||
|
||||
this.registerReducer("publishChanges", async (state, payload) => {
|
||||
const endActivity = startActivity(
|
||||
TelemetryViews.TableDesigner,
|
||||
TelemetryActions.Publish,
|
||||
this._correlationId,
|
||||
{
|
||||
correlationId: this._correlationId,
|
||||
},
|
||||
);
|
||||
this.state = {
|
||||
...this.state,
|
||||
apiState: {
|
||||
|
@ -247,14 +255,7 @@ export class TableDesignerWebviewController extends ReactWebviewPanelController<
|
|||
await this._tableDesignerService.publishChanges(
|
||||
payload.table,
|
||||
);
|
||||
|
||||
sendActionEvent(
|
||||
TelemetryViews.TableDesigner,
|
||||
TelemetryActions.Publish,
|
||||
{
|
||||
correlationId: this._correlationId,
|
||||
},
|
||||
);
|
||||
endActivity.end(ActivityStatus.Succeeded);
|
||||
state = {
|
||||
...state,
|
||||
tableInfo: publishResponse.newTableInfo,
|
||||
|
@ -278,14 +279,7 @@ export class TableDesignerWebviewController extends ReactWebviewPanelController<
|
|||
},
|
||||
publishingError: e.toString(),
|
||||
};
|
||||
sendActionEvent(
|
||||
TelemetryViews.TableDesigner,
|
||||
TelemetryActions.Publish,
|
||||
{
|
||||
correlationId: this._correlationId,
|
||||
error: "true",
|
||||
},
|
||||
);
|
||||
endActivity.endFailed(e, false);
|
||||
}
|
||||
|
||||
let targetNode = this._targetNode;
|
||||
|
|
|
@ -3,17 +3,22 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from "vscode";
|
||||
import * as vscodeMssql from "vscode-mssql";
|
||||
|
||||
import {
|
||||
ActivityStatus,
|
||||
ActivityObject,
|
||||
TelemetryActions,
|
||||
TelemetryViews,
|
||||
} from "../sharedInterfaces/telemetry";
|
||||
import AdsTelemetryReporter, {
|
||||
TelemetryEventMeasures,
|
||||
TelemetryEventProperties,
|
||||
} from "@microsoft/ads-extension-telemetry";
|
||||
import * as vscodeMssql from "vscode-mssql";
|
||||
|
||||
import { IConnectionProfile } from "../models/interfaces";
|
||||
import * as vscode from "vscode";
|
||||
import {
|
||||
TelemetryActions,
|
||||
TelemetryViews,
|
||||
} from "../sharedInterfaces/telemetry";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
const packageJson = vscode.extensions.getExtension(
|
||||
vscodeMssql.extension.name,
|
||||
|
@ -110,3 +115,92 @@ export function sendErrorEvent(
|
|||
}
|
||||
errorEvent.send();
|
||||
}
|
||||
|
||||
export function startActivity(
|
||||
telemetryView: TelemetryViews,
|
||||
telemetryAction: TelemetryActions,
|
||||
correlationId?: string,
|
||||
additionalProps: TelemetryEventProperties = {},
|
||||
additionalMeasurements: TelemetryEventMeasures = {},
|
||||
): ActivityObject {
|
||||
const startTime = performance.now();
|
||||
if (!correlationId) {
|
||||
correlationId = uuidv4();
|
||||
}
|
||||
|
||||
sendActionEvent(telemetryView, telemetryAction, additionalProps, {
|
||||
...additionalMeasurements,
|
||||
startTime: Math.round(startTime),
|
||||
});
|
||||
|
||||
function update(
|
||||
additionalProps: TelemetryEventProperties,
|
||||
additionalMeasurements: TelemetryEventMeasures,
|
||||
): void {
|
||||
sendActionEvent(
|
||||
telemetryView,
|
||||
telemetryAction,
|
||||
{
|
||||
...additionalProps,
|
||||
activityStatus: ActivityStatus.Pending,
|
||||
},
|
||||
{
|
||||
...additionalMeasurements,
|
||||
timeElapsedMs: Math.round(performance.now() - startTime),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function end(
|
||||
activityStatus: ActivityStatus,
|
||||
additionalProps: TelemetryEventProperties,
|
||||
additionalMeasurements: TelemetryEventMeasures,
|
||||
) {
|
||||
sendActionEvent(
|
||||
telemetryView,
|
||||
telemetryAction,
|
||||
{
|
||||
...additionalProps,
|
||||
activityStatus: activityStatus,
|
||||
},
|
||||
{
|
||||
...additionalMeasurements,
|
||||
durationMs: Math.round(performance.now() - startTime),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function endFailed(
|
||||
error?: Error,
|
||||
includeErrorMessage?: boolean,
|
||||
errorCode?: string,
|
||||
errorType?: string,
|
||||
additionalProps?: TelemetryEventProperties,
|
||||
additionalMeasurements?: TelemetryEventMeasures,
|
||||
) {
|
||||
sendErrorEvent(
|
||||
telemetryView,
|
||||
telemetryAction,
|
||||
error,
|
||||
includeErrorMessage,
|
||||
errorCode,
|
||||
errorType,
|
||||
{
|
||||
...additionalProps,
|
||||
activityStatus: ActivityStatus.Failed,
|
||||
},
|
||||
{
|
||||
...additionalMeasurements,
|
||||
durationMs: Math.round(performance.now() - startTime),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
startTime,
|
||||
correlationId,
|
||||
update,
|
||||
end,
|
||||
endFailed,
|
||||
};
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче