Telemetry for Query Execution Plans (#19039) (#19104)

* Adds telemetry around how the properties window is opened

* Adds telemetry around accessing execution plan top operations

* Adds key for viewing top operations

* Adds telemetry around using the open query button and context menu item

* Adds telemetry around execution plan zoom in, out, to fit, and custom

* Adds telemetry around searching for nodes in execution plans

* Reduces telemetry additional properties to 1.

* Code review changes

* Removes unnecessary export
This commit is contained in:
Lewis Sanchez
2022-04-14 16:56:09 -07:00
committed by GitHub
parent aeaf0ef473
commit 9400c56c0b
3 changed files with 92 additions and 23 deletions

View File

@@ -38,6 +38,7 @@ export const enum TelemetryView {
AgentNotebookHistory = 'AgentNotebookHistory', AgentNotebookHistory = 'AgentNotebookHistory',
AgentNotebooks = 'AgentNotebooks', AgentNotebooks = 'AgentNotebooks',
ConnectionDialog = 'ConnectionDialog', ConnectionDialog = 'ConnectionDialog',
ExecutionPlan = 'ExecutionPlan',
ExtensionHost = 'ExtensionHost', ExtensionHost = 'ExtensionHost',
ExtensionRecommendationDialog = 'ExtensionRecommendationDialog', ExtensionRecommendationDialog = 'ExtensionRecommendationDialog',
Notebook = 'Notebook', Notebook = 'Notebook',
@@ -55,6 +56,7 @@ export const enum TelemetryAction {
AddServerGroup = 'AddServerGroup', AddServerGroup = 'AddServerGroup',
adsCommandExecuted = 'adsCommandExecuted', adsCommandExecuted = 'adsCommandExecuted',
ConnectToServer = 'ConnectToServer', ConnectToServer = 'ConnectToServer',
CustomZoom = 'CustomZoom',
BackupCreated = 'BackupCreated', BackupCreated = 'BackupCreated',
DashboardNavigated = 'DashboardNavigated', DashboardNavigated = 'DashboardNavigated',
DatabaseConnected = 'DatabaseConnected', DatabaseConnected = 'DatabaseConnected',
@@ -69,6 +71,7 @@ export const enum TelemetryAction {
CancelQuery = 'CancelQuery', CancelQuery = 'CancelQuery',
ChartCreated = 'ChartCreated', ChartCreated = 'ChartCreated',
Click = 'Click', Click = 'Click',
FindNode = 'FindNode',
FirewallRuleRequested = 'FirewallRuleCreated', FirewallRuleRequested = 'FirewallRuleCreated',
GenerateScript = 'GenerateScript', GenerateScript = 'GenerateScript',
GeneratePreviewReport = 'GeneratePreviewReport', GeneratePreviewReport = 'GeneratePreviewReport',
@@ -82,6 +85,8 @@ export const enum TelemetryAction {
NewQuery = 'NewQuery', NewQuery = 'NewQuery',
ObjectExplorerExpand = 'ObjectExplorerExpand', ObjectExplorerExpand = 'ObjectExplorerExpand',
Open = 'Open', Open = 'Open',
OpenQuery = 'OpenQuery',
OpenExecutionPlanProperties = 'OpenExecutionPlanProperties',
PublishChanges = 'PublishChanges', PublishChanges = 'PublishChanges',
RestoreRequested = 'RestoreRequested', RestoreRequested = 'RestoreRequested',
RunAgentJob = 'RunAgentJob', RunAgentJob = 'RunAgentJob',
@@ -90,9 +95,13 @@ export const enum TelemetryAction {
RunQueryString = 'RunQueryString', RunQueryString = 'RunQueryString',
ShowChart = 'ShowChart', ShowChart = 'ShowChart',
StopAgentJob = 'StopAgentJob', StopAgentJob = 'StopAgentJob',
ViewTopOperations = 'ViewTopOperations',
WizardPagesNavigation = 'WizardPagesNavigation', WizardPagesNavigation = 'WizardPagesNavigation',
SearchStarted = 'SearchStarted', SearchStarted = 'SearchStarted',
SearchCompleted = 'SearchCompleted' SearchCompleted = 'SearchCompleted',
ZoomIn = 'ZoomIn',
ZoomOut = 'ZoomOut',
ZoomToFit = 'ZoomToFIt'
} }
export const enum NbTelemetryAction { export const enum NbTelemetryAction {

View File

@@ -33,6 +33,8 @@ import { VSBuffer } from 'vs/base/common/buffer';
import { CustomZoomWidget } from 'sql/workbench/contrib/executionPlan/browser/widgets/customZoomWidget'; import { CustomZoomWidget } from 'sql/workbench/contrib/executionPlan/browser/widgets/customZoomWidget';
import { NodeSearchWidget } from 'sql/workbench/contrib/executionPlan/browser/widgets/nodeSearchWidget'; import { NodeSearchWidget } from 'sql/workbench/contrib/executionPlan/browser/widgets/nodeSearchWidget';
import { AzdataGraphView } from 'sql/workbench/contrib/executionPlan/browser/azdataGraphView'; import { AzdataGraphView } from 'sql/workbench/contrib/executionPlan/browser/azdataGraphView';
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
export class ExecutionPlanView implements ISashLayoutProvider { export class ExecutionPlanView implements ISashLayoutProvider {
@@ -153,13 +155,13 @@ export class ExecutionPlanView implements ISashLayoutProvider {
const actionBarActions = [ const actionBarActions = [
new SavePlanFile(), new SavePlanFile(),
new OpenPlanFile(), new OpenPlanFile(),
new OpenQueryAction(), this._instantiationService.createInstance(OpenQueryAction, 'ActionBar'),
new SearchNodeAction(), this._instantiationService.createInstance(SearchNodeAction, 'ActionBar'),
new ZoomInAction(), this._instantiationService.createInstance(ZoomInAction, 'ActionBar'),
new ZoomOutAction(), this._instantiationService.createInstance(ZoomOutAction, 'ActionBar'),
new ZoomToFitAction(), this._instantiationService.createInstance(ZoomToFitAction, 'ActionBar'),
new CustomZoomAction(), this._instantiationService.createInstance(CustomZoomAction, 'ActionBar'),
new PropertiesAction(), this._instantiationService.createInstance(PropertiesAction, 'ActionBar'),
this.actionBarToggleTopTip this.actionBarToggleTopTip
]; ];
this._actionBar.pushAction(actionBarActions, { icon: true, label: false }); this._actionBar.pushAction(actionBarActions, { icon: true, label: false });
@@ -169,13 +171,13 @@ export class ExecutionPlanView implements ISashLayoutProvider {
const contextMenuAction = [ const contextMenuAction = [
new SavePlanFile(), new SavePlanFile(),
new OpenPlanFile(), new OpenPlanFile(),
new OpenQueryAction(), this._instantiationService.createInstance(OpenQueryAction, 'ContextMenu'),
new SearchNodeAction(), this._instantiationService.createInstance(SearchNodeAction, 'ContextMenu'),
new ZoomInAction(), this._instantiationService.createInstance(ZoomInAction, 'ContextMenu'),
new ZoomOutAction(), this._instantiationService.createInstance(ZoomOutAction, 'ContextMenu'),
new ZoomToFitAction(), this._instantiationService.createInstance(ZoomToFitAction, 'ContextMenu'),
new CustomZoomAction(), this._instantiationService.createInstance(CustomZoomAction, 'ContextMenu'),
new PropertiesAction(), this._instantiationService.createInstance(PropertiesAction, 'ContextMenu'),
this.contextMenuToggleTooltipAction this.contextMenuToggleTooltipAction
]; ];
const self = this; const self = this;
@@ -269,15 +271,24 @@ export class ExecutionPlanView implements ISashLayoutProvider {
} }
} }
type ExecutionPlanActionSource = 'ContextMenu' | 'ActionBar';
export class OpenQueryAction extends Action { export class OpenQueryAction extends Action {
public static ID = 'ep.OpenQueryAction'; public static ID = 'ep.OpenQueryAction';
public static LABEL = localize('openQueryAction', "Open Query"); public static LABEL = localize('openQueryAction', "Open Query");
constructor() { constructor(private source: ExecutionPlanActionSource,
@IAdsTelemetryService private readonly telemetryService: IAdsTelemetryService
) {
super(OpenQueryAction.ID, OpenQueryAction.LABEL, openQueryIconClassNames); super(OpenQueryAction.ID, OpenQueryAction.LABEL, openQueryIconClassNames);
} }
public override async run(context: ExecutionPlanView): Promise<void> { public override async run(context: ExecutionPlanView): Promise<void> {
this.telemetryService
.createActionEvent(TelemetryKeys.TelemetryView.ExecutionPlan, TelemetryKeys.TelemetryAction.OpenQuery)
.withAdditionalProperties({ source: this.source })
.send();
context.openQuery(); context.openQuery();
} }
} }
@@ -286,11 +297,18 @@ export class PropertiesAction extends Action {
public static ID = 'ep.propertiesAction'; public static ID = 'ep.propertiesAction';
public static LABEL = localize('executionPlanPropertiesActionLabel', "Properties"); public static LABEL = localize('executionPlanPropertiesActionLabel', "Properties");
constructor() { constructor(private source: ExecutionPlanActionSource,
@IAdsTelemetryService private readonly telemetryService: IAdsTelemetryService
) {
super(PropertiesAction.ID, PropertiesAction.LABEL, openPropertiesIconClassNames); super(PropertiesAction.ID, PropertiesAction.LABEL, openPropertiesIconClassNames);
} }
public override async run(context: ExecutionPlanView): Promise<void> { public override async run(context: ExecutionPlanView): Promise<void> {
this.telemetryService
.createActionEvent(TelemetryKeys.TelemetryView.ExecutionPlan, TelemetryKeys.TelemetryAction.OpenExecutionPlanProperties)
.withAdditionalProperties({ source: this.source })
.send();
context.propertiesView.toggleVisibility(); context.propertiesView.toggleVisibility();
} }
} }
@@ -299,11 +317,18 @@ export class ZoomInAction extends Action {
public static ID = 'ep.ZoomInAction'; public static ID = 'ep.ZoomInAction';
public static LABEL = localize('executionPlanZoomInActionLabel', "Zoom In"); public static LABEL = localize('executionPlanZoomInActionLabel', "Zoom In");
constructor() { constructor(private source: ExecutionPlanActionSource,
@IAdsTelemetryService private readonly telemetryService: IAdsTelemetryService
) {
super(ZoomInAction.ID, ZoomInAction.LABEL, zoomInIconClassNames); super(ZoomInAction.ID, ZoomInAction.LABEL, zoomInIconClassNames);
} }
public override async run(context: ExecutionPlanView): Promise<void> { public override async run(context: ExecutionPlanView): Promise<void> {
this.telemetryService
.createActionEvent(TelemetryKeys.TelemetryView.ExecutionPlan, TelemetryKeys.TelemetryAction.ZoomIn)
.withAdditionalProperties({ source: this.source })
.send();
context.executionPlanDiagram.zoomIn(); context.executionPlanDiagram.zoomIn();
} }
} }
@@ -312,11 +337,18 @@ export class ZoomOutAction extends Action {
public static ID = 'ep.ZoomOutAction'; public static ID = 'ep.ZoomOutAction';
public static LABEL = localize('executionPlanZoomOutActionLabel', "Zoom Out"); public static LABEL = localize('executionPlanZoomOutActionLabel', "Zoom Out");
constructor() { constructor(private source: ExecutionPlanActionSource,
@IAdsTelemetryService private readonly telemetryService: IAdsTelemetryService
) {
super(ZoomOutAction.ID, ZoomOutAction.LABEL, zoomOutIconClassNames); super(ZoomOutAction.ID, ZoomOutAction.LABEL, zoomOutIconClassNames);
} }
public override async run(context: ExecutionPlanView): Promise<void> { public override async run(context: ExecutionPlanView): Promise<void> {
this.telemetryService
.createActionEvent(TelemetryKeys.TelemetryView.ExecutionPlan, TelemetryKeys.TelemetryAction.ZoomOut)
.withAdditionalProperties({ source: this.source })
.send();
context.executionPlanDiagram.zoomOut(); context.executionPlanDiagram.zoomOut();
} }
} }
@@ -325,11 +357,18 @@ export class ZoomToFitAction extends Action {
public static ID = 'ep.FitGraph'; public static ID = 'ep.FitGraph';
public static LABEL = localize('executionPlanFitGraphLabel', "Zoom to fit"); public static LABEL = localize('executionPlanFitGraphLabel', "Zoom to fit");
constructor() { constructor(private source: ExecutionPlanActionSource,
@IAdsTelemetryService private readonly telemetryService: IAdsTelemetryService
) {
super(ZoomToFitAction.ID, ZoomToFitAction.LABEL, zoomToFitIconClassNames); super(ZoomToFitAction.ID, ZoomToFitAction.LABEL, zoomToFitIconClassNames);
} }
public override async run(context: ExecutionPlanView): Promise<void> { public override async run(context: ExecutionPlanView): Promise<void> {
this.telemetryService
.createActionEvent(TelemetryKeys.TelemetryView.ExecutionPlan, TelemetryKeys.TelemetryAction.ZoomToFit)
.withAdditionalProperties({ source: this.source })
.send();
context.executionPlanDiagram.zoomToFit(); context.executionPlanDiagram.zoomToFit();
} }
} }
@@ -371,11 +410,18 @@ export class CustomZoomAction extends Action {
public static ID = 'ep.customZoom'; public static ID = 'ep.customZoom';
public static LABEL = localize('executionPlanCustomZoom', "Custom Zoom"); public static LABEL = localize('executionPlanCustomZoom', "Custom Zoom");
constructor() { constructor(private source: ExecutionPlanActionSource,
@IAdsTelemetryService private readonly telemetryService: IAdsTelemetryService
) {
super(CustomZoomAction.ID, CustomZoomAction.LABEL, customZoomIconClassNames); super(CustomZoomAction.ID, CustomZoomAction.LABEL, customZoomIconClassNames);
} }
public override async run(context: ExecutionPlanView): Promise<void> { public override async run(context: ExecutionPlanView): Promise<void> {
this.telemetryService
.createActionEvent(TelemetryKeys.TelemetryView.ExecutionPlan, TelemetryKeys.TelemetryAction.CustomZoom)
.withAdditionalProperties({ source: this.source })
.send();
context.widgetController.toggleWidget(context._instantiationService.createInstance(CustomZoomWidget, context.widgetController, context.executionPlanDiagram)); context.widgetController.toggleWidget(context._instantiationService.createInstance(CustomZoomWidget, context.widgetController, context.executionPlanDiagram));
} }
} }
@@ -384,11 +430,18 @@ export class SearchNodeAction extends Action {
public static ID = 'ep.searchNode'; public static ID = 'ep.searchNode';
public static LABEL = localize('executionPlanSearchNodeAction', "Find Node"); public static LABEL = localize('executionPlanSearchNodeAction', "Find Node");
constructor() { constructor(private source: ExecutionPlanActionSource,
@IAdsTelemetryService private readonly telemetryService: IAdsTelemetryService
) {
super(SearchNodeAction.ID, SearchNodeAction.LABEL, searchIconClassNames); super(SearchNodeAction.ID, SearchNodeAction.LABEL, searchIconClassNames);
} }
public override async run(context: ExecutionPlanView): Promise<void> { public override async run(context: ExecutionPlanView): Promise<void> {
this.telemetryService
.createActionEvent(TelemetryKeys.TelemetryView.ExecutionPlan, TelemetryKeys.TelemetryAction.FindNode)
.withAdditionalProperties({ source: this.source })
.send();
context.widgetController.toggleWidget(context._instantiationService.createInstance(NodeSearchWidget, context.widgetController, context.executionPlanDiagram)); context.widgetController.toggleWidget(context._instantiationService.createInstance(NodeSearchWidget, context.widgetController, context.executionPlanDiagram));
} }
} }

View File

@@ -15,6 +15,8 @@ import { attachTableStyler } from 'sql/platform/theme/common/styler';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { TableDataView } from 'sql/base/browser/ui/table/tableDataView'; import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
import { TopOperationsState } from 'sql/workbench/common/editor/query/topOperationsState'; import { TopOperationsState } from 'sql/workbench/common/editor/query/topOperationsState';
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
const topOperationColumns: Array<Slick.Column<any>> = [ const topOperationColumns: Array<Slick.Column<any>> = [
{ name: localize('topOperations.operation', "Operation"), field: 'operation', sortable: true, width: 300 }, { name: localize('topOperations.operation', "Operation"), field: 'operation', sortable: true, width: 300 },
@@ -55,7 +57,10 @@ export class TopOperationsView extends Disposable implements IPanelView {
private container = document.createElement('div'); private container = document.createElement('div');
private dataView = new TableDataView(); private dataView = new TableDataView();
constructor(@IThemeService private themeService: IThemeService) { constructor(
@IThemeService private themeService: IThemeService,
@IAdsTelemetryService private readonly telemetryService: IAdsTelemetryService
) {
super(); super();
this.table = new Table(this.container, { this.table = new Table(this.container, {
columns: topOperationColumns, columns: topOperationColumns,
@@ -71,6 +76,8 @@ export class TopOperationsView extends Disposable implements IPanelView {
public render(container: HTMLElement): void { public render(container: HTMLElement): void {
container.appendChild(this.container); container.appendChild(this.container);
this.telemetryService.sendActionEvent(TelemetryKeys.TelemetryView.ExecutionPlan, TelemetryKeys.TelemetryAction.ViewTopOperations);
} }
public layout(dimension: Dimension): void { public layout(dimension: Dimension): void {