mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-25 17:23:10 -05:00
Notebooks: Log telemetry when all markdown cells rendered (#5862)
* Log telemetry when all markdown cells rendered * Enable referencing previous telemetry timestamps * Fix broken unit test to do a null check * Undo loading icon changes in textcelll * Addressing PR comments * PR comments II
This commit is contained in:
@@ -5,7 +5,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: column" (mouseover)="hover=true" (mouseleave)="hover=false">
|
||||
<loading-spinner [loading]="isLoading"></loading-spinner>
|
||||
<div class="notebook-text" style="flex: 0 0 auto;">
|
||||
<code-component *ngIf="isEditMode" [cellModel]="cellModel" (onContentChanged)="handleContentChanged()" [model]="model" [activeCellId]="activeCellId">
|
||||
</code-component>
|
||||
|
||||
@@ -75,7 +75,6 @@ export class TextCellComponent extends CellView implements OnInit, AfterContentI
|
||||
private _activeCellId: string;
|
||||
private readonly _onDidClickLink = this._register(new Emitter<URI>());
|
||||
public readonly onDidClickLink = this._onDidClickLink.event;
|
||||
protected isLoading: boolean;
|
||||
private _cellToggleMoreActions: CellToggleMoreActions;
|
||||
private _hover: boolean;
|
||||
|
||||
@@ -89,7 +88,6 @@ export class TextCellComponent extends CellView implements OnInit, AfterContentI
|
||||
) {
|
||||
super();
|
||||
this.isEditMode = true;
|
||||
this.isLoading = true;
|
||||
this._cellToggleMoreActions = this._instantiationService.createInstance(CellToggleMoreActions);
|
||||
}
|
||||
|
||||
@@ -110,7 +108,7 @@ export class TextCellComponent extends CellView implements OnInit, AfterContentI
|
||||
}
|
||||
|
||||
private setLoading(isLoading: boolean): void {
|
||||
this.isLoading = isLoading;
|
||||
this.cellModel.loaded = !isLoading;
|
||||
this._changeRef.detectChanges();
|
||||
}
|
||||
|
||||
@@ -122,7 +120,6 @@ export class TextCellComponent extends CellView implements OnInit, AfterContentI
|
||||
this._register(this.cellModel.onOutputsChanged(e => {
|
||||
this.updatePreview();
|
||||
}));
|
||||
this.setLoading(false);
|
||||
}
|
||||
|
||||
ngAfterContentInit(): void {
|
||||
@@ -210,6 +207,7 @@ export class TextCellComponent extends CellView implements OnInit, AfterContentI
|
||||
htmlcontent = this.sanitizeContent(htmlcontent);
|
||||
let outputElement = <HTMLElement>this.output.nativeElement;
|
||||
outputElement.innerHTML = htmlcontent;
|
||||
this.setLoading(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ export class CellModel implements ICellModel {
|
||||
public id: string;
|
||||
private _connectionManagementService: IConnectionManagementService;
|
||||
private _stdInHandler: nb.MessageHandler<nb.IStdinMessage>;
|
||||
private _onCellLoaded = new Emitter<string>();
|
||||
private _loaded: boolean;
|
||||
|
||||
constructor(cellData: nb.ICellContents,
|
||||
private _options: ICellModelOptions,
|
||||
@@ -183,6 +185,21 @@ export class CellModel implements ICellModel {
|
||||
this._onExecutionStateChanged.fire(this.executionState);
|
||||
}
|
||||
|
||||
public get onLoaded(): Event<string> {
|
||||
return this._onCellLoaded.event;
|
||||
}
|
||||
|
||||
public get loaded(): boolean {
|
||||
return this._loaded;
|
||||
}
|
||||
|
||||
public set loaded(val: boolean) {
|
||||
this._loaded = val;
|
||||
if (val) {
|
||||
this._onCellLoaded.fire(this._cellType);
|
||||
}
|
||||
}
|
||||
|
||||
private notifyExecutionComplete(): void {
|
||||
if (this._notebookService) {
|
||||
this._notebookService.serializeNotebookStateChange(this.notebookModel.notebookUri, NotebookChangeType.CellExecuted);
|
||||
|
||||
@@ -466,6 +466,8 @@ export interface ICellModel {
|
||||
setOverrideLanguage(language: string);
|
||||
equals(cellModel: ICellModel): boolean;
|
||||
toJSON(): nb.ICellContents;
|
||||
loaded: boolean;
|
||||
readonly onLoaded: Event<string>;
|
||||
}
|
||||
|
||||
export interface FutureInternal extends nb.IFuture {
|
||||
@@ -508,6 +510,7 @@ export interface INotebookModelOptions {
|
||||
notificationService: INotificationService;
|
||||
connectionService: IConnectionManagementService;
|
||||
capabilitiesService: ICapabilitiesService;
|
||||
editorLoadedTimestamp?: number;
|
||||
}
|
||||
|
||||
export interface ILanguageMagic {
|
||||
|
||||
@@ -10,9 +10,10 @@ import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
import { IClientSession, INotebookModel, IDefaultConnection, INotebookModelOptions, ICellModel, NotebookContentChange, notebookConstants } from './modelInterfaces';
|
||||
import { NotebookChangeType, CellType } from 'sql/workbench/parts/notebook/models/contracts';
|
||||
import { NotebookChangeType, CellType, CellTypes } from 'sql/workbench/parts/notebook/models/contracts';
|
||||
import { nbversion } from '../notebookConstants';
|
||||
import * as notebookUtils from '../notebookUtils';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
|
||||
import { INotebookManager, SQL_NOTEBOOK_PROVIDER, DEFAULT_NOTEBOOK_PROVIDER } from 'sql/workbench/services/notebook/common/notebookService';
|
||||
import { NotebookContexts } from 'sql/workbench/parts/notebook/models/notebookContexts';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
@@ -23,6 +24,7 @@ import { ConnectionProfile } from 'sql/platform/connection/common/connectionProf
|
||||
import { uriPrefixes } from 'sql/platform/connection/common/utils';
|
||||
import { keys } from 'vs/base/common/map';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
||||
/*
|
||||
* Used to control whether a message in a dialog/wizard is displayed as an error,
|
||||
@@ -72,13 +74,16 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
private _oldKernel: nb.IKernel;
|
||||
private _clientSessionListeners: IDisposable[] = [];
|
||||
private _connectionUrisToDispose: string[] = [];
|
||||
private _textCellsLoading: number = 0;
|
||||
|
||||
public requestConnectionHandler: () => Promise<boolean>;
|
||||
|
||||
constructor(
|
||||
private _notebookOptions: INotebookModelOptions,
|
||||
public connectionProfile: IConnectionProfile | undefined,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@INotificationService private readonly notificationService: INotificationService
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService
|
||||
) {
|
||||
super();
|
||||
if (!_notebookOptions || !_notebookOptions.notebookUri || !_notebookOptions.notebookManagers) {
|
||||
@@ -292,7 +297,11 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
this._defaultLanguageInfo = this.getDefaultLanguageInfo(contents);
|
||||
this._savedKernelInfo = this.getSavedKernelInfo(contents);
|
||||
if (contents.cells && contents.cells.length > 0) {
|
||||
this._cells = contents.cells.map(c => factory.createCell(c, { notebook: this, isTrusted: isTrusted }));
|
||||
this._cells = contents.cells.map(c => {
|
||||
let cellModel = factory.createCell(c, { notebook: this, isTrusted: isTrusted });
|
||||
this.trackMarkdownTelemetry(<nb.ICellContents>c, cellModel);
|
||||
return cellModel;
|
||||
});
|
||||
}
|
||||
}
|
||||
this.setDefaultKernelAndProviderId();
|
||||
@@ -920,6 +929,26 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
this._connectionUrisToDispose = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Track time it takes to render all markdown cells
|
||||
*/
|
||||
private trackMarkdownTelemetry(cellContent: nb.ICellContents, cellModel: ICellModel): void {
|
||||
if (cellContent && cellContent.cell_type === CellTypes.Markdown) {
|
||||
this._textCellsLoading++;
|
||||
}
|
||||
this._register(cellModel.onLoaded((cell_type) => {
|
||||
if (cell_type === CellTypes.Markdown) {
|
||||
this._textCellsLoading--;
|
||||
if (this._textCellsLoading <= 0) {
|
||||
if (this._notebookOptions.editorLoadedTimestamp) {
|
||||
let markdownRenderingTime = Date.now() - this._notebookOptions.editorLoadedTimestamp;
|
||||
this.telemetryService.publicLog(TelemetryKeys.NotebookMarkdownRendered, { markdownRenderingElapsedMs: markdownRenderingTime });
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the model to JSON.
|
||||
*/
|
||||
|
||||
@@ -48,6 +48,7 @@ import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { LabeledMenuItemActionItem, fillInActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
||||
|
||||
export const NOTEBOOK_SELECTOR: string = 'notebook-component';
|
||||
@@ -94,7 +95,8 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
@Inject(IViewletService) private viewletService: IViewletService,
|
||||
@Inject(ICapabilitiesService) private capabilitiesService: ICapabilitiesService,
|
||||
@Inject(ITextFileService) private textFileService: ITextFileService,
|
||||
@Inject(ILogService) private readonly logService: ILogService
|
||||
@Inject(ILogService) private readonly logService: ILogService,
|
||||
@Inject(ITelemetryService) private telemetryService: ITelemetryService
|
||||
) {
|
||||
super();
|
||||
this.updateProfile();
|
||||
@@ -278,8 +280,9 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
providerId: 'sql', // this is tricky; really should also depend on the connection profile
|
||||
defaultKernel: this._notebookParams.input.defaultKernel,
|
||||
layoutChanged: this._notebookParams.input.layoutChanged,
|
||||
capabilitiesService: this.capabilitiesService
|
||||
}, this.profile, this.logService, this.notificationService);
|
||||
capabilitiesService: this.capabilitiesService,
|
||||
editorLoadedTimestamp: this._notebookParams.input.editorOpenedTimestamp
|
||||
}, this.profile, this.logService, this.notificationService, this.telemetryService);
|
||||
model.onError((errInfo: INotification) => this.handleModelError(errInfo));
|
||||
let trusted = await this.notebookService.isNotebookTrustCached(this._notebookParams.notebookUri, this.isDirty());
|
||||
await model.requestModelLoad(trusted);
|
||||
|
||||
@@ -155,6 +155,7 @@ export class NotebookInput extends EditorInput {
|
||||
private _contentManager: IContentManager;
|
||||
private _providersLoaded: Promise<void>;
|
||||
private _dirtyListener: IDisposable;
|
||||
private _notebookEditorOpenedTimestamp: number;
|
||||
|
||||
constructor(private _title: string,
|
||||
private resource: URI,
|
||||
@@ -168,6 +169,7 @@ export class NotebookInput extends EditorInput {
|
||||
this.resource = resource;
|
||||
this._standardKernels = [];
|
||||
this._providersLoaded = this.assignProviders();
|
||||
this._notebookEditorOpenedTimestamp = Date.now();
|
||||
if (this._textInput) {
|
||||
this.hookDirtyListener(this._textInput.onDidChangeDirty, () => this._onDidChangeDirty.fire());
|
||||
}
|
||||
@@ -251,6 +253,10 @@ export class NotebookInput extends EditorInput {
|
||||
return this._layoutChanged.event;
|
||||
}
|
||||
|
||||
public get editorOpenedTimestamp(): number {
|
||||
return this._notebookEditorOpenedTimestamp;
|
||||
}
|
||||
|
||||
doChangeLayout(): any {
|
||||
this._layoutChanged.fire();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user