Skip to content

Commit 581c18d

Browse files
authored
Add telemetry for download, extract, and analyze. (#2597)
Fixes #2461 (alternate fix to PR #2593)
1 parent 5232537 commit 581c18d

File tree

15 files changed

+114
-17
lines changed

15 files changed

+114
-17
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ npm-debug.log
1212
!yarn.lock
1313
coverage/
1414
.vscode-test/**
15-
.venv
15+
.venv*/
1616
pythonFiles/experimental/ptvsd/**
1717
debug_coverage*/**
1818
languageServer/**

news/1 Enhancements/2461.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add telemetry to download, extract, and analyze, phases of the Python Language Server

news/2 Fixes/2297.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Stop duplicate initializations of Python Language Server progress reporter.

src/client/activation/downloader.ts

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,14 @@ import * as path from 'path';
77
import * as requestProgress from 'request-progress';
88
import { ProgressLocation, window } from 'vscode';
99
import { createDeferred } from '../../utils/async';
10+
import { StopWatch } from '../../utils/stopWatch';
1011
import { IFileSystem } from '../common/platform/types';
1112
import { IExtensionContext, IOutputChannel } from '../common/types';
13+
import { sendTelemetryEvent } from '../telemetry';
14+
import {
15+
PYTHON_LANGUAGE_SERVER_DOWNLOADED,
16+
PYTHON_LANGUAGE_SERVER_EXTRACTED
17+
} from '../telemetry/constants';
1218
import { PlatformData, PlatformName } from './platformData';
1319
import { IDownloadFileService } from './types';
1420

@@ -28,6 +34,7 @@ export const DownloadLinks = {
2834
};
2935

3036
export class LanguageServerDownloader {
37+
3138
constructor(
3239
private readonly output: IOutputChannel,
3340
private readonly fs: IFileSystem,
@@ -43,19 +50,40 @@ export class LanguageServerDownloader {
4350

4451
public async downloadLanguageServer(context: IExtensionContext): Promise<void> {
4552
const downloadUri = this.getDownloadUri();
46-
53+
const timer: StopWatch = new StopWatch();
54+
let success: boolean = true;
4755
let localTempFilePath = '';
56+
4857
try {
4958
localTempFilePath = await this.downloadFile(downloadUri, 'Downloading Microsoft Python Language Server... ');
59+
} catch (err) {
60+
this.output.appendLine('download failed.');
61+
this.output.appendLine(err);
62+
success = false;
63+
throw new Error(err);
64+
} finally {
65+
sendTelemetryEvent(
66+
PYTHON_LANGUAGE_SERVER_DOWNLOADED,
67+
timer.elapsedTime,
68+
{ success }
69+
);
70+
}
71+
72+
timer.reset();
73+
try {
5074
await this.unpackArchive(context.extensionPath, localTempFilePath);
5175
} catch (err) {
52-
this.output.appendLine('failed.');
76+
this.output.appendLine('extraction failed.');
5377
this.output.appendLine(err);
78+
success = false;
5479
throw new Error(err);
5580
} finally {
56-
if (localTempFilePath.length > 0) {
57-
await this.fs.deleteFile(localTempFilePath);
58-
}
81+
sendTelemetryEvent(
82+
PYTHON_LANGUAGE_SERVER_EXTRACTED,
83+
timer.elapsedTime,
84+
{ success }
85+
);
86+
await this.fs.deleteFile(localTempFilePath);
5987
}
6088
}
6189

src/client/activation/languageServer.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4+
'use strict';
5+
46
import { inject, injectable } from 'inversify';
57
import * as path from 'path';
68
import {
@@ -29,7 +31,6 @@ import { IEnvironmentVariablesProvider } from '../common/variables/types';
2931
import { IServiceContainer } from '../ioc/types';
3032
import { LanguageServerSymbolProvider } from '../providers/symbolProvider';
3133
import {
32-
PYTHON_LANGUAGE_SERVER_DOWNLOADED,
3334
PYTHON_LANGUAGE_SERVER_ENABLED,
3435
PYTHON_LANGUAGE_SERVER_ERROR
3536
} from '../telemetry/constants';
@@ -143,7 +144,6 @@ export class LanguageServerExtensionActivator implements IExtensionActivator {
143144

144145
private async startLanguageServer(clientOptions: LanguageClientOptions): Promise<boolean> {
145146
// Determine if we are running MSIL/Universal via dotnet or self-contained app.
146-
147147
const reporter = getTelemetryReporter();
148148
reporter.sendTelemetryEvent(PYTHON_LANGUAGE_SERVER_ENABLED);
149149

@@ -164,7 +164,6 @@ export class LanguageServerExtensionActivator implements IExtensionActivator {
164164
new RequestWithProxy(this.workspace.getConfiguration('http').get('proxy', '')),
165165
languageServerFolder);
166166
await downloader.downloadLanguageServer(this.context);
167-
reporter.sendTelemetryEvent(PYTHON_LANGUAGE_SERVER_DOWNLOADED);
168167
}
169168

170169
const serverModule = path.join(this.context.extensionPath, languageServerFolder, this.platformData.getEngineExecutableName());

src/client/activation/progress.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,21 @@
44
import { Progress, ProgressLocation, window } from 'vscode';
55
import { Disposable, LanguageClient } from 'vscode-languageclient';
66
import { createDeferred, Deferred } from '../../utils/async';
7+
import { StopWatch } from '../../utils/stopWatch';
8+
import { sendTelemetryEvent } from '../telemetry';
9+
import { PYTHON_LANGUAGE_SERVER_ANALYSISTIME } from '../telemetry/constants';
10+
11+
// Draw the line at Language Server analysis 'timing out'
12+
// and becoming a failure-case at 1 minute:
13+
const ANALYSIS_TIMEOUT_MS: number = 60000;
714

815
export class ProgressReporting {
916
private statusBarMessage: Disposable | undefined;
1017
private progress: Progress<{ message?: string; increment?: number }> | undefined;
1118
private progressDeferred: Deferred<void> | undefined;
19+
private progressTimer?: StopWatch;
20+
// tslint:disable-next-line:no-unused-variable
21+
private progressTimeout?: NodeJS.Timer;
1222

1323
constructor(private readonly languageClient: LanguageClient) {
1424
this.languageClient.onNotification('python/setStatusBarMessage', (m: string) => {
@@ -19,7 +29,17 @@ export class ProgressReporting {
1929
});
2030

2131
this.languageClient.onNotification('python/beginProgress', async _ => {
32+
if (this.progressDeferred) {
33+
return;
34+
}
35+
2236
this.progressDeferred = createDeferred<void>();
37+
this.progressTimer = new StopWatch();
38+
this.progressTimeout = setTimeout(
39+
this.handleTimeout.bind(this),
40+
ANALYSIS_TIMEOUT_MS
41+
);
42+
2343
window.withProgress({
2444
location: ProgressLocation.Window,
2545
title: ''
@@ -40,7 +60,26 @@ export class ProgressReporting {
4060
if (this.progressDeferred) {
4161
this.progressDeferred.resolve();
4262
this.progressDeferred = undefined;
63+
this.progress = undefined;
64+
this.completeAnalysisTracking(true);
4365
}
4466
});
4567
}
68+
69+
private completeAnalysisTracking(success: boolean): void {
70+
if (this.progressTimer) {
71+
sendTelemetryEvent(
72+
PYTHON_LANGUAGE_SERVER_ANALYSISTIME,
73+
this.progressTimer.elapsedTime,
74+
{ success }
75+
);
76+
}
77+
this.progressTimer = undefined;
78+
this.progressTimeout = undefined;
79+
}
80+
81+
// tslint:disable-next-line:no-any
82+
private handleTimeout(_args: any[]): void {
83+
this.completeAnalysisTracking(false);
84+
}
4685
}

src/client/common/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ export const PYTHON = [
66
{ scheme: 'untitled', language: PYTHON_LANGUAGE }
77
];
88

9+
export const PVSC_EXTENSION_ID = 'ms-python.python';
10+
911
export namespace Commands {
1012
export const Set_Interpreter = 'python.setInterpreter';
1113
export const Set_ShebangInterpreter = 'python.setShebangInterpreter';

src/client/telemetry/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,12 @@ export const UNITTEST_STOP = 'UNITTEST.STOP';
3232
export const UNITTEST_RUN = 'UNITTEST.RUN';
3333
export const UNITTEST_DISCOVER = 'UNITTEST.DISCOVER';
3434
export const UNITTEST_VIEW_OUTPUT = 'UNITTEST.VIEW_OUTPUT';
35+
export const PYTHON_LANGUAGE_SERVER_ANALYSISTIME = 'PYTHON_LANGUAGE_SERVER.ANALYSIS_TIME';
3536
export const PYTHON_LANGUAGE_SERVER_ENABLED = 'PYTHON_LANGUAGE_SERVER.ENABLED';
37+
export const PYTHON_LANGUAGE_SERVER_EXTRACTED = 'PYTHON_LANGUAGE_SERVER.EXTRACTED';
3638
export const PYTHON_LANGUAGE_SERVER_DOWNLOADED = 'PYTHON_LANGUAGE_SERVER.DOWNLOADED';
3739
export const PYTHON_LANGUAGE_SERVER_ERROR = 'PYTHON_LANGUAGE_SERVER.ERROR';
3840
export const PYTHON_LANGUAGE_SERVER_STARTUP = 'PYTHON_LANGUAGE_SERVER.STARTUP';
3941
export const PYTHON_LANGUAGE_SERVER_PLATFORM_NOT_SUPPORTED = 'PYTHON_LANGUAGE_SERVER.PLATFORM_NOT_SUPPORTED';
42+
4043
export const TERMINAL_CREATE = 'TERMINAL.CREATE';

src/client/telemetry/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ export function sendTelemetryEvent(eventName: string, durationMs?: number, prope
2626
}
2727

2828
// tslint:disable-next-line:no-any function-name
29-
export function captureTelemetry(eventName: string, properties?: TelemetryProperties, captureDuration: boolean = true) {
29+
export function captureTelemetry(
30+
eventName: string,
31+
properties?: TelemetryProperties,
32+
captureDuration: boolean = true
33+
) {
3034
// tslint:disable-next-line:no-function-expression no-any
3135
return function (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) {
3236
const originalMethod = descriptor.value;
@@ -53,6 +57,7 @@ export function captureTelemetry(eventName: string, properties?: TelemetryProper
5357
})
5458
// tslint:disable-next-line:promise-function-async
5559
.catch(ex => {
60+
// tslint:disable-next-line:no-any
5661
sendTelemetryEvent(eventName, stopWatch.elapsedTime, properties);
5762
return Promise.reject(ex);
5863
});

src/client/telemetry/telemetry.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@
88
import { extensions } from 'vscode';
99
// tslint:disable-next-line:import-name
1010
import TelemetryReporter from 'vscode-extension-telemetry';
11+
import { PVSC_EXTENSION_ID } from '../common/constants';
1112

1213
// tslint:disable-next-line:no-any
1314
let telemetryReporter: TelemetryReporter;
1415
export function getTelemetryReporter() {
1516
if (telemetryReporter) {
1617
return telemetryReporter;
1718
}
18-
const extensionId = 'ms-python.python';
19+
const extensionId = PVSC_EXTENSION_ID;
1920
// tslint:disable-next-line:no-non-null-assertion
2021
const extension = extensions.getExtension(extensionId)!;
2122
// tslint:disable-next-line:no-unsafe-any

src/client/telemetry/types.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ export type FormatTelemetry = {
1515
formatSelection: boolean;
1616
};
1717

18+
export type LanguageServerTelemetry = {
19+
success: boolean;
20+
};
21+
1822
export type LinterTrigger = 'auto' | 'save';
1923

2024
export type LintingTelemetry = {
@@ -81,4 +85,15 @@ export type TerminalTelemetry = {
8185
pythonVersion?: string;
8286
interpreterType?: InterpreterType;
8387
};
84-
export type TelemetryProperties = FormatTelemetry | LintingTelemetry | EditorLoadTelemetry | PythonInterpreterTelemetry | CodeExecutionTelemetry | TestRunTelemetry | TestDiscoverytTelemetry | FeedbackTelemetry | TerminalTelemetry | DebuggerTelemetryV2 | SettingsTelemetry;
88+
export type TelemetryProperties = FormatTelemetry
89+
| LanguageServerTelemetry
90+
| LintingTelemetry
91+
| EditorLoadTelemetry
92+
| PythonInterpreterTelemetry
93+
| CodeExecutionTelemetry
94+
| TestRunTelemetry
95+
| TestDiscoverytTelemetry
96+
| FeedbackTelemetry
97+
| TerminalTelemetry
98+
| DebuggerTelemetryV2
99+
| SettingsTelemetry;

src/test/aaFirstTest/aaFirstTest.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import { expect } from 'chai';
55
import { extensions } from 'vscode';
6+
import { PVSC_EXTENSION_ID } from '../../client/common/constants';
67
import { initialize } from '../initialize';
78

89
// NOTE:
@@ -17,6 +18,6 @@ suite('Activate Extension', () => {
1718
await initialize();
1819
});
1920
test('Python extension has activated', async () => {
20-
expect(extensions.getExtension('ms-python.python')!.isActive).to.equal(true, 'Extension has not been activated');
21+
expect(extensions.getExtension(PVSC_EXTENSION_ID)!.isActive).to.equal(true, 'Extension has not been activated');
2122
});
2223
});

src/test/initialize.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as path from 'path';
44
import * as vscode from 'vscode';
55
import { IExtensionApi } from '../client/api';
66
import { PythonSettings } from '../client/common/configSettings';
7+
import { PVSC_EXTENSION_ID } from '../client/common/constants';
78
import { clearPythonPathInWorkspaceFolder, PYTHON_PATH, resetGlobalPythonPathSetting, setPythonPathInWorkspaceRoot } from './common';
89

910
export * from './constants';
@@ -32,7 +33,7 @@ export async function initialize(): Promise<any> {
3233
PythonSettings.dispose();
3334
}
3435
export async function activateExtension() {
35-
const extension = vscode.extensions.getExtension<IExtensionApi>('ms-python.python')!;
36+
const extension = vscode.extensions.getExtension<IExtensionApi>(PVSC_EXTENSION_ID)!;
3637
if (extension.isActive) {
3738
return;
3839
}

src/test/performance/load.perf.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import * as fs from 'fs-extra';
1010
import { EOL } from 'os';
1111
import * as path from 'path';
1212
import { commands, extensions } from 'vscode';
13+
import { PVSC_EXTENSION_ID } from '../../client/common/constants';
1314
import { StopWatch } from '../../utils/stopWatch';
1415

1516
const AllowedIncreaseInActivationDelayInMS = 500;
@@ -22,7 +23,7 @@ suite('Activation Times', () => {
2223
return;
2324
}
2425
test(`Capture Extension Activation Times (Version: ${process.env.ACTIVATION_TIMES_EXT_VERSION}, sample: ${sampleCounter})`, async () => {
25-
const pythonExtension = extensions.getExtension('ms-python.python');
26+
const pythonExtension = extensions.getExtension(PVSC_EXTENSION_ID);
2627
if (!pythonExtension) {
2728
throw new Error('Python Extension not found');
2829
}

src/test/performanceTest.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import * as download from 'download';
1919
import * as fs from 'fs-extra';
2020
import * as path from 'path';
2121
import * as request from 'request';
22-
import { EXTENSION_ROOT_DIR } from '../client/common/constants';
22+
import { EXTENSION_ROOT_DIR, PVSC_EXTENSION_ID } from '../client/common/constants';
2323

2424
const NamedRegexp = require('named-js-regexp');
2525
const StreamZip = require('node-stream-zip');
@@ -123,7 +123,7 @@ class TestRunner {
123123
}
124124

125125
private async getReleaseVersion(): Promise<string> {
126-
const url = 'https://marketplace.visualstudio.com/items?itemName=ms-python.python';
126+
const url = `https://marketplace.visualstudio.com/items?itemName=${PVSC_EXTENSION_ID}`;
127127
const content = await new Promise<string>((resolve, reject) => {
128128
request(url, (error, response, body) => {
129129
if (error) {

0 commit comments

Comments
 (0)