Skip to content

Commit cb1fa33

Browse files
committed
Add install telemetry (microsoft#13653)
* Add extension install telemetry. * Add tests. * Fix typo * Fix tests. * Update comment to reflect what the installed actually does.
1 parent 687e141 commit cb1fa33

File tree

4 files changed

+74
-0
lines changed

4 files changed

+74
-0
lines changed

src/client/extensionActivation.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { Commands, PYTHON, PYTHON_LANGUAGE, STANDARD_OUTPUT_CHANNEL, UseProposed
1717
import { registerTypes as installerRegisterTypes } from './common/installer/serviceRegistry';
1818
import { traceError } from './common/logger';
1919
import { registerTypes as platformRegisterTypes } from './common/platform/serviceRegistry';
20+
import { IFileSystem } from './common/platform/types';
2021
import { registerTypes as processRegisterTypes } from './common/process/serviceRegistry';
2122
import { registerTypes as commonRegisterTypes } from './common/serviceRegistry';
2223
import {
@@ -57,6 +58,7 @@ import { registerTypes as providersRegisterTypes } from './providers/serviceRegi
5758
import { activateSimplePythonRefactorProvider } from './providers/simpleRefactorProvider';
5859
import { TerminalProvider } from './providers/terminalProvider';
5960
import { ISortImportsEditingProvider } from './providers/types';
61+
import { setExtensionInstallTelemetryProperties } from './telemetry/extensionInstallTelemetry';
6062
import { registerTypes as commonRegisterTerminalTypes } from './terminals/serviceRegistry';
6163
import { ICodeExecutionManager, ITerminalAutoActivation } from './terminals/types';
6264
import { TEST_OUTPUT_CHANNEL } from './testing/common/constants';
@@ -104,6 +106,10 @@ async function activateLegacy(
104106
platformRegisterTypes(serviceManager);
105107
processRegisterTypes(serviceManager);
106108

109+
// We need to setup this property before any telemetry is sent
110+
const fs = serviceManager.get<IFileSystem>(IFileSystem);
111+
await setExtensionInstallTelemetryProperties(fs);
112+
107113
const applicationEnv = serviceManager.get<IApplicationEnvironment>(IApplicationEnvironment);
108114
const enableProposedApi = applicationEnv.packageJson.enableProposedApi;
109115
serviceManager.addSingletonInstance<boolean>(UseProposedApi, enableProposedApi);
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import * as path from 'path';
5+
import { setSharedProperty } from '.';
6+
import { IFileSystem } from '../common/platform/types';
7+
import { EXTENSION_ROOT_DIR } from '../constants';
8+
9+
/**
10+
* Sets shared telemetry property about where the extension was installed from
11+
* currently we only detect installations from the Python coding pack installer.
12+
* Those installations get the 'pythonCodingPack'. Otherwise assume the default
13+
* case as 'MarketPlace'.
14+
*
15+
*/
16+
export async function setExtensionInstallTelemetryProperties(fs: IFileSystem) {
17+
// Look for PythonCodingPack file under `%USERPROFILE%/.vscode/extensions`
18+
// folder. If that file exists treat this extension as installed from coding
19+
// pack.
20+
//
21+
// Use parent of EXTENSION_ROOT_DIR to access %USERPROFILE%/.vscode/extensions
22+
// this is because the installer will add PythonCodingPack to %USERPROFILE%/.vscode/extensions
23+
// or %USERPROFILE%/.vscode-insiders/extensions depending on what was installed
24+
// previously by the user. If we always join (<home>, .vscode, extensions), we will
25+
// end up looking at the wrong place, with respect to the extension that was launched.
26+
const fileToCheck = path.join(path.dirname(EXTENSION_ROOT_DIR), 'PythonCodingPack');
27+
if (await fs.fileExists(fileToCheck)) {
28+
setSharedProperty('installSource', 'pythonCodingPack');
29+
} else {
30+
// We did not file the `PythonCodingPack` file, assume market place install.
31+
setSharedProperty('installSource', 'marketPlace');
32+
}
33+
}

src/client/telemetry/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,12 @@ export interface ISharedPropertyMapping {
332332
* For every DS telemetry we would like to know the type of Notebook Editor used when doing something.
333333
*/
334334
['ds_notebookeditor']: undefined | 'old' | 'custom' | 'native';
335+
336+
/**
337+
* For every telemetry event from the extension we want to make sure we can associate it with install
338+
* source. We took this approach to work around very limiting query performance issues.
339+
*/
340+
['installSource']: undefined | 'marketPlace' | 'pythonCodingPack';
335341
}
336342

337343
// Map all events to their properties
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import * as assert from 'assert';
2+
import * as sinon from 'sinon';
3+
import { anyString, instance, mock, when } from 'ts-mockito';
4+
import { FileSystem } from '../../client/common/platform/fileSystem';
5+
import { IFileSystem } from '../../client/common/platform/types';
6+
import * as Telemetry from '../../client/telemetry';
7+
import { setExtensionInstallTelemetryProperties } from '../../client/telemetry/extensionInstallTelemetry';
8+
9+
suite('Extension Install Telemetry', () => {
10+
let fs: IFileSystem;
11+
let telemetryPropertyStub: sinon.SinonStub;
12+
setup(() => {
13+
fs = mock(FileSystem);
14+
telemetryPropertyStub = sinon.stub(Telemetry, 'setSharedProperty');
15+
});
16+
teardown(() => {
17+
telemetryPropertyStub.restore();
18+
});
19+
test('PythonCodingPack exists', async () => {
20+
when(fs.fileExists(anyString())).thenResolve(true);
21+
await setExtensionInstallTelemetryProperties(instance(fs));
22+
assert.ok(telemetryPropertyStub.calledOnceWithExactly('installSource', 'pythonCodingPack'));
23+
});
24+
test('PythonCodingPack does not exists', async () => {
25+
when(fs.fileExists(anyString())).thenResolve(false);
26+
await setExtensionInstallTelemetryProperties(instance(fs));
27+
assert.ok(telemetryPropertyStub.calledOnceWithExactly('installSource', 'marketPlace'));
28+
});
29+
});

0 commit comments

Comments
 (0)