diff --git a/CHANGELOG.md b/CHANGELOG.md index e28e586470ee..e231721de64e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,10 @@ ([#10049](https://github.com/Microsoft/vscode-python/issues/10049)) 1. Track cold/warm times to execute notebook cells. ([#10176](https://github.com/Microsoft/vscode-python/issues/10176)) +1. Telemetry to capture connections to `localhost` using the connect to remote Jupyter server feature. + ([#10098](https://github.com/Microsoft/vscode-python/issues/10098)) +1. Telemetry to capture perceived startup times of Jupyter and time to execute a cell. + ([#10212](https://github.com/Microsoft/vscode-python/issues/10212)) ### Thanks diff --git a/src/client/datascience/constants.ts b/src/client/datascience/constants.ts index 07f4e8959013..6bd07eeed2c5 100644 --- a/src/client/datascience/constants.ts +++ b/src/client/datascience/constants.ts @@ -160,6 +160,7 @@ export enum Telemetry { SubmitCellThroughInput = 'DATASCIENCE.SUBMITCELLFROMREPL', ConnectLocalJupyter = 'DS_INTERNAL.CONNECTLOCALJUPYTER', ConnectRemoteJupyter = 'DS_INTERNAL.CONNECTREMOTEJUPYTER', + ConnectRemoteJupyterViaLocalHost = 'DS_INTERNAL.CONNECTREMOTEJUPYTER_VIA_LOCALHOST', ConnectFailedJupyter = 'DS_INTERNAL.CONNECTFAILEDJUPYTER', ConnectRemoteFailedJupyter = 'DS_INTERNAL.CONNECTREMOTEFAILEDJUPYTER', StartSessionFailedJupyter = 'DS_INTERNAL.START_SESSION_FAILED_JUPYTER', @@ -209,6 +210,8 @@ export enum Telemetry { ExecuteCell = 'DATASCIENCE.EXECUTE_CELL_TIME', ExecuteCellPerceivedCold = 'DS_INTERNAL.EXECUTE_CELL_PERCEIVED_COLD', ExecuteCellPerceivedWarm = 'DS_INTERNAL.EXECUTE_CELL_PERCEIVED_WARM', + PerceivedJupyterStartupNotebook = 'DS_INTERNAL.PERCEIVED_JUPYTER_STARTUP_NOTEBOOK', + StartExecuteNotebookCellPerceivedCold = 'DS_INTERNAL.START_EXECUTE_NOTEBOOK_CELL_PERCEIVED_COLD', WebviewStartup = 'DS_INTERNAL.WEBVIEW_STARTUP', VariableExplorerFetchTime = 'DS_INTERNAL.VARIABLE_EXPLORER_FETCH_TIME', WebviewStyleUpdate = 'DS_INTERNAL.WEBVIEW_STYLE_UPDATE', diff --git a/src/client/datascience/interactive-common/interactiveBase.ts b/src/client/datascience/interactive-common/interactiveBase.ts index 7c89937cefb9..a678ac389400 100644 --- a/src/client/datascience/interactive-common/interactiveBase.ts +++ b/src/client/datascience/interactive-common/interactiveBase.ts @@ -20,6 +20,7 @@ import { IFileSystem } from '../../common/platform/types'; import { IConfigurationService, IDisposableRegistry, IExperimentsManager } from '../../common/types'; import { createDeferred, Deferred } from '../../common/utils/async'; import * as localize from '../../common/utils/localize'; +import { StopWatch } from '../../common/utils/stopWatch'; import { IInterpreterService, PythonInterpreter } from '../../interpreter/contracts'; import { captureTelemetry, sendTelemetryEvent } from '../../telemetry'; import { generateCellRangesFromDocument } from '../cellFactory'; @@ -78,6 +79,7 @@ import { InteractiveWindowMessageListener } from './interactiveWindowMessageList export abstract class InteractiveBase extends WebViewHost implements IInteractiveBase { private unfinishedCells: ICell[] = []; private restartingKernel: boolean = false; + private perceivedJupyterStartupTelemetryCaptured: boolean = false; private potentiallyUnfinishedStatus: Disposable[] = []; private addSysInfoPromise: Deferred | undefined; private _notebook: INotebook | undefined; @@ -457,8 +459,8 @@ export abstract class InteractiveBase extends WebViewHost { traceInfo(`Submitting code for ${this.id}`); + const stopWatch = this._notebook && !this.perceivedJupyterStartupTelemetryCaptured ? new StopWatch() : undefined; let result = true; - // Do not execute or render empty code cells const cellMatcher = new CellMatcher(this.configService.getSettings().datascience); if (cellMatcher.stripFirstMarker(code).length === 0) { @@ -511,7 +513,16 @@ export abstract class InteractiveBase extends WebViewHost { + if (e === ServerStatus.Busy) { + sendTelemetryEvent(Telemetry.StartExecuteNotebookCellPerceivedCold, stopWatch?.elapsedTime); + disposable.dispose(); + } + }); + } const observable = this._notebook.executeObservable(code, file, line, id, false); // Indicate we executed some code diff --git a/src/client/datascience/jupyter/jupyterExecution.ts b/src/client/datascience/jupyter/jupyterExecution.ts index 3fbe3fdf1cd3..312018fdcdd8 100644 --- a/src/client/datascience/jupyter/jupyterExecution.ts +++ b/src/client/datascience/jupyter/jupyterExecution.ts @@ -31,6 +31,8 @@ import { JupyterWaitForIdleError } from './jupyterWaitForIdleError'; import { KernelSelector, KernelSpecInterpreter } from './kernels/kernelSelector'; import { NotebookStarter } from './notebookStarter'; +const LocalHosts = ['localhost', '127.0.0.1', '::1']; + export class JupyterExecutionBase implements IJupyterExecution { private usablePythonInterpreter: PythonInterpreter | undefined; private eventEmitter: EventEmitter = new EventEmitter(); @@ -143,6 +145,9 @@ export class JupyterExecutionBase implements IJupyterExecution { try { // Start or connect to the process [connection, kernelSpecInterpreter] = await Promise.all([this.startOrConnect(options, cancelToken), kernelSpecInterpreterPromise]); + if (!connection.localLaunch && LocalHosts.includes(connection.hostName.toLowerCase())) { + sendTelemetryEvent(Telemetry.ConnectRemoteJupyterViaLocalHost); + } // Create a server tha t we will then attempt to connect to. result = this.serviceContainer.get(INotebookServer); diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index c0c212d3abb2..d280af20dade 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -1436,6 +1436,10 @@ export interface IEventNamePropertyMapping { [Telemetry.ConnectFailedJupyter]: never | undefined; [Telemetry.ConnectLocalJupyter]: never | undefined; [Telemetry.ConnectRemoteJupyter]: never | undefined; + /** + * Connecting to an existing Jupyter server, but connecting to localhost. + */ + [Telemetry.ConnectRemoteJupyterViaLocalHost]: never | undefined; [Telemetry.ConnectRemoteFailedJupyter]: never | undefined; [Telemetry.ConnectRemoteSelfCertFailedJupyter]: never | undefined; [Telemetry.RegisterAndUseInterpreterAsKernel]: never | undefined; @@ -1469,6 +1473,14 @@ export interface IEventNamePropertyMapping { * If `notebook = true`, this its telemetry for native editor/notebooks. */ [Telemetry.ExecuteCellPerceivedWarm]: undefined | { notebook: boolean }; + /** + * Time take for jupyter server to start and be ready to run first user cell. + */ + [Telemetry.PerceivedJupyterStartupNotebook]: never | undefined; + /** + * Time take for jupyter server to be busy from the time user first hit `run` cell until jupyter reports it is busy running a cell. + */ + [Telemetry.StartExecuteNotebookCellPerceivedCold]: never | undefined; [Telemetry.ExecuteNativeCell]: never | undefined; [Telemetry.ExpandAll]: never | undefined; [Telemetry.ExportNotebook]: never | undefined;