From 43db30d1da4f936fcfeee20557ac1b57ab58ae9b Mon Sep 17 00:00:00 2001 From: Maddy <12754347+MaddyDev@users.noreply.github.com> Date: Mon, 5 Apr 2021 16:08:39 -0700 Subject: [PATCH] Add more Notebook telemetry events (#14755) * initial checkin * telemetry for search in notebooks * telemetry for move notebook * address comments * feedback changes * fix tests paasing NullAdsTelemetryService * move changeKernel telemetry higher up * remove telemetry service * Fix new Notebook events (#14982) * Notebook telemetry fixes * update2 * Move event location * remove service * remove unused Co-authored-by: chgagnon --- extensions/notebook/src/book/bookTreeView.ts | 13 ++++++------ extensions/notebook/src/telemetry.ts | 3 ++- .../telemetry/common/telemetryKeys.ts | 9 ++++++-- .../notebook/browser/notebookActions.ts | 21 +++++++++++++++---- .../notebookExplorerViewlet.ts | 8 ++++++- .../notebookExplorer/notebookSearch.ts | 10 +++++++++ .../test/browser/notebookActions.test.ts | 7 ++++--- .../notebook/browser/models/modelFactory.ts | 2 +- .../notebook/browser/models/notebookModel.ts | 6 ++++++ 9 files changed, 61 insertions(+), 18 deletions(-) diff --git a/extensions/notebook/src/book/bookTreeView.ts b/extensions/notebook/src/book/bookTreeView.ts index 6684711888..d3f5f9fd7d 100644 --- a/extensions/notebook/src/book/bookTreeView.ts +++ b/extensions/notebook/src/book/bookTreeView.ts @@ -122,7 +122,7 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider { const dialog = new CreateBookDialog(this.bookTocManager); dialog.createDialog(); - TelemetryReporter.createActionEvent(BookTelemetryView, NbTelemetryActions.CreateBook).send(); + TelemetryReporter.sendActionEvent(BookTelemetryView, NbTelemetryActions.CreateBook); } async getSelectionQuickPick(movingElement: BookTreeItem): Promise { @@ -207,6 +207,7 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider { + TelemetryReporter.sendActionEvent(BookTelemetryView, NbTelemetryActions.MoveNotebook); const selectionResults = await this.getSelectionQuickPick(movingElement); if (selectionResults) { const pickedSection = selectionResults.quickPickSection; @@ -238,7 +239,7 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider book.bookPath === bookPath); @@ -298,7 +299,7 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider cell.active) ?? 0; editor.addCell(this.cellType, index); } + this._telemetryService.createActionEvent(TelemetryKeys.TelemetryView.Notebook, TelemetryKeys.NbTelemetryAction.AddCell) + .withAdditionalProperties({ cell_type: this.cellType }) + .send(); } } @@ -212,12 +218,14 @@ export class RunAllCellsAction extends Action { constructor( id: string, label: string, cssClass: string, @INotificationService private notificationService: INotificationService, - @INotebookService private _notebookService: INotebookService + @INotebookService private _notebookService: INotebookService, + @IAdsTelemetryService private _telemetryService: IAdsTelemetryService, ) { super(id, label, cssClass); } public async run(context: URI): Promise { try { + this._telemetryService.sendActionEvent(TelemetryKeys.TelemetryView.Notebook, TelemetryKeys.NbTelemetryAction.RunAll); const editor = this._notebookService.findNotebookEditor(context); await editor.runAllCells(); return true; @@ -372,7 +380,8 @@ const kernelDropdownElementId = 'kernel-dropdown'; export class KernelsDropdown extends SelectBox { private model: NotebookModel; private _showAllKernels: boolean = false; - constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, modelReady: Promise, @IConfigurationService private _configurationService: IConfigurationService) { + constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, modelReady: Promise, @IConfigurationService private _configurationService: IConfigurationService, + ) { super([msgLoading], msgLoading, contextViewProvider, container, { labelText: kernelLabel, labelOnTop: false, ariaLabel: kernelLabel, id: kernelDropdownElementId } as ISelectBoxOptionsWithLabel); if (modelReady) { @@ -641,13 +650,17 @@ export class NewNotebookAction extends Action { id: string, label: string, @ICommandService private commandService: ICommandService, - @IObjectExplorerService private objectExplorerService: IObjectExplorerService + @IObjectExplorerService private objectExplorerService: IObjectExplorerService, + @IAdsTelemetryService private _telemetryService: IAdsTelemetryService, ) { super(id, label); this.class = 'notebook-action new-notebook'; } async run(context?: azdata.ObjectExplorerContext): Promise { + this._telemetryService.createActionEvent(TelemetryKeys.TelemetryView.Notebook, TelemetryKeys.NbTelemetryAction.NewNotebookFromConnections) + .withConnectionInfo(context?.connectionProfile) + .send(); let connProfile: azdata.IConnectionProfile; if (context && context.nodeInfo) { let node = await this.objectExplorerService.getTreeNode(context.connectionProfile.id, context.nodeInfo.nodePath); diff --git a/src/sql/workbench/contrib/notebook/browser/notebookExplorer/notebookExplorerViewlet.ts b/src/sql/workbench/contrib/notebook/browser/notebookExplorer/notebookExplorerViewlet.ts index 0300a3b52e..83442d1285 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebookExplorer/notebookExplorerViewlet.ts +++ b/src/sql/workbench/contrib/notebook/browser/notebookExplorer/notebookExplorerViewlet.ts @@ -39,6 +39,8 @@ import { NotebookSearchView } from 'sql/workbench/contrib/notebook/browser/noteb import * as path from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; import { TreeViewPane } from 'vs/workbench/browser/parts/views/treeView'; +import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys'; +import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; export const VIEWLET_ID = 'workbench.view.notebooks'; @@ -124,7 +126,8 @@ export class NotebookExplorerViewPaneContainer extends ViewPaneContainer { @IMenuService private menuService: IMenuService, @IContextKeyService private contextKeyService: IContextKeyService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, - @IFileService private readonly fileService: IFileService + @IFileService private readonly fileService: IFileService, + @IAdsTelemetryService private _telemetryService: IAdsTelemetryService ) { super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService); this.inputBoxFocused = Constants.InputBoxFocusedKey.bindTo(this.contextKeyService); @@ -255,6 +258,9 @@ export class NotebookExplorerViewPaneContainer extends ViewPaneContainer { onQueryValidationError(err); return; } + this._telemetryService.createActionEvent(TelemetryKeys.TelemetryView.Notebook, TelemetryKeys.TelemetryAction.SearchStarted) + .withAdditionalProperties({ triggeredOnType: triggeredOnType }) + .send(); this.validateQuery(query).then(() => { if (this.views.length > 1) { diff --git a/src/sql/workbench/contrib/notebook/browser/notebookExplorer/notebookSearch.ts b/src/sql/workbench/contrib/notebook/browser/notebookExplorer/notebookSearch.ts index 64baa9a14e..1e871a7863 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebookExplorer/notebookSearch.ts +++ b/src/sql/workbench/contrib/notebook/browser/notebookExplorer/notebookSearch.ts @@ -43,6 +43,8 @@ import { searchClearIcon, searchCollapseAllIcon, searchExpandAllIcon, searchStop import { Action, IAction } from 'vs/base/common/actions'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { Memento } from 'vs/workbench/common/memento'; +import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; +import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys'; const $ = dom.$; @@ -82,6 +84,7 @@ export class NotebookSearchView extends SearchView { @IOpenerService openerService: IOpenerService, @ITelemetryService telemetryService: ITelemetryService, @ICommandService readonly commandService: ICommandService, + @IAdsTelemetryService private _telemetryService: IAdsTelemetryService, ) { super(options, fileService, editorService, progressService, notificationService, dialogService, contextViewService, instantiationService, viewDescriptorService, configurationService, contextService, searchWorkbenchService, contextKeyService, replaceService, textFileService, preferencesService, themeService, searchHistoryService, contextMenuService, menuService, accessibilityService, keybindingService, storageService, openerService, telemetryService); @@ -239,6 +242,7 @@ export class NotebookSearchView extends SearchView { } public startSearch(query: ITextQuery, excludePatternText: string, includePatternText: string, triggeredOnType: boolean, searchWidget: NotebookSearchWidget): Thenable { + let start = new Date().getTime(); let progressComplete: () => void; this.progressService.withProgress({ location: this.getProgressLocation(), delay: triggeredOnType ? 300 : 0 }, _progress => { return new Promise(resolve => progressComplete = resolve); @@ -253,6 +257,12 @@ export class NotebookSearchView extends SearchView { }, 2000); const onComplete = async (completed?: ISearchComplete) => { + let end = new Date().getTime(); + this._telemetryService.createActionEvent(TelemetryKeys.TelemetryView.Notebook, TelemetryKeys.TelemetryAction.SearchCompleted) + .withAdditionalProperties({ resultsReturned: completed.results.length }) + .withAdditionalMeasurements({ timeTakenMs: end - start }) + .send(); + clearTimeout(slowTimer); this.state = SearchUIState.Idle; diff --git a/src/sql/workbench/contrib/notebook/test/browser/notebookActions.test.ts b/src/sql/workbench/contrib/notebook/test/browser/notebookActions.test.ts index 9329c7c631..8614836377 100644 --- a/src/sql/workbench/contrib/notebook/test/browser/notebookActions.test.ts +++ b/src/sql/workbench/contrib/notebook/test/browser/notebookActions.test.ts @@ -24,6 +24,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; import { URI } from 'vs/base/common/uri'; +import { NullAdsTelemetryService } from 'sql/platform/telemetry/common/adsTelemetryService'; import { MockQuickInputService } from 'sql/workbench/contrib/notebook/test/common/quickInputServiceMock'; class TestClientSession extends ClientSessionStub { @@ -125,7 +126,7 @@ suite('Notebook Actions', function (): void { let actualCellType: CellType; - let action = new AddCellAction('TestId', 'TestLabel', 'TestClass', mockNotebookService.object); + let action = new AddCellAction('TestId', 'TestLabel', 'TestClass', mockNotebookService.object, new NullAdsTelemetryService()); action.cellType = testCellType; // Normal use case @@ -192,7 +193,7 @@ suite('Notebook Actions', function (): void { let mockNotification = TypeMoq.Mock.ofType(TestNotificationService); mockNotification.setup(n => n.notify(TypeMoq.It.isAny())); - let action = new RunAllCellsAction('TestId', 'TestLabel', 'TestClass', mockNotification.object, mockNotebookService.object); + let action = new RunAllCellsAction('TestId', 'TestLabel', 'TestClass', mockNotification.object, mockNotebookService.object, new NullAdsTelemetryService()); // Normal use case mockNotebookEditor.setup(c => c.runAllCells()).returns(() => Promise.resolve(true)); @@ -252,7 +253,7 @@ suite('Notebook Actions', function (): void { return Promise.resolve(true); }); - let action = new NewNotebookAction('TestId', 'TestLabel', mockCommandService.object, undefined); + let action = new NewNotebookAction('TestId', 'TestLabel', mockCommandService.object, undefined, new NullAdsTelemetryService()); action.run(undefined); assert.strictEqual(actualCmdId, NewNotebookAction.INTERNAL_NEW_NOTEBOOK_CMD_ID); diff --git a/src/sql/workbench/services/notebook/browser/models/modelFactory.ts b/src/sql/workbench/services/notebook/browser/models/modelFactory.ts index e5c120295d..764ebe32ff 100644 --- a/src/sql/workbench/services/notebook/browser/models/modelFactory.ts +++ b/src/sql/workbench/services/notebook/browser/models/modelFactory.ts @@ -20,6 +20,6 @@ export class ModelFactory implements IModelFactory { } public createClientSession(options: IClientSessionOptions): IClientSession { - return new ClientSession(options); + return this.instantiationService.createInstance(ClientSession, options); } } diff --git a/src/sql/workbench/services/notebook/browser/models/notebookModel.ts b/src/sql/workbench/services/notebook/browser/models/notebookModel.ts index bc47e097d0..f642b87cf4 100644 --- a/src/sql/workbench/services/notebook/browser/models/notebookModel.ts +++ b/src/sql/workbench/services/notebook/browser/models/notebookModel.ts @@ -971,6 +971,12 @@ export class NotebookModel extends Disposable implements INotebookModel { if (kernel.info) { this.updateLanguageInfo(kernel.info.language_info); } + this.adstelemetryService.createActionEvent(TelemetryKeys.TelemetryView.Notebook, TelemetryKeys.NbTelemetryAction.KernelChanged) + .withAdditionalProperties({ + name: kernel.name, + alias: kernelAlias || '' + }) + .send(); this._kernelChangedEmitter.fire({ newValue: kernel, oldValue: undefined,