Skip to content

Commit 7adc21b

Browse files
author
Kartik Raj
authored
Improve discovery related telemetry (microsoft#19075)
* Improve discovery related telemetry * Add trigger * News entry * Add comment
1 parent ceb949f commit 7adc21b

10 files changed

Lines changed: 41 additions & 27 deletions

File tree

news/3 Code Health/19077.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Capture whether environment discovery was triggered using Quickpick UI.

src/client/interpreter/configuration/interpreterSelector/commands/setInterpreter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ export class SetInterpreterCommand extends BaseInterpreterSelectorCommand {
134134
iconPath: getIcon(REFRESH_BUTTON_ICON),
135135
tooltip: InterpreterQuickPickList.refreshInterpreterList,
136136
},
137-
callback: () => this.interpreterService.triggerRefresh().ignoreErrors(),
137+
callback: () => this.interpreterService.triggerRefresh(undefined, 'ui').ignoreErrors(),
138138
},
139139
onChangeItem: {
140140
event: this.interpreterService.onDidChangeInterpreters,

src/client/interpreter/contracts.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export type PythonEnvironmentsChangedEvent = {
1717
export const IComponentAdapter = Symbol('IComponentAdapter');
1818
export interface IComponentAdapter {
1919
readonly onRefreshStart: Event<void>;
20-
triggerRefresh(query?: PythonLocatorQuery & { clearCache?: boolean }): Promise<void>;
20+
triggerRefresh(query?: PythonLocatorQuery & { clearCache?: boolean }, trigger?: 'auto' | 'ui'): Promise<void>;
2121
readonly refreshPromise: Promise<void> | undefined;
2222
readonly onChanged: Event<PythonEnvironmentsChangedEvent>;
2323
// VirtualEnvPrompt
@@ -64,7 +64,7 @@ export interface ICondaService {
6464
export const IInterpreterService = Symbol('IInterpreterService');
6565
export interface IInterpreterService {
6666
readonly onRefreshStart: Event<void>;
67-
triggerRefresh(query?: PythonLocatorQuery & { clearCache?: boolean }): Promise<void>;
67+
triggerRefresh(query?: PythonLocatorQuery & { clearCache?: boolean }, trigger?: 'auto' | 'ui'): Promise<void>;
6868
readonly refreshPromise: Promise<void> | undefined;
6969
readonly onDidChangeInterpreters: Event<PythonEnvironmentsChangedEvent>;
7070
onDidChangeInterpreterConfiguration: Event<Uri | undefined>;

src/client/interpreter/interpreterService.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,11 @@ export class InterpreterService implements Disposable, IInterpreterService {
4444
return this.pyenvs.onRefreshStart;
4545
}
4646

47-
public triggerRefresh(query?: PythonLocatorQuery & { clearCache?: boolean }): Promise<void> {
48-
return this.pyenvs.triggerRefresh(query);
47+
public triggerRefresh(
48+
query?: PythonLocatorQuery & { clearCache?: boolean },
49+
trigger?: 'auto' | 'ui',
50+
): Promise<void> {
51+
return this.pyenvs.triggerRefresh(query, trigger);
4952
}
5053

5154
public get refreshPromise(): Promise<void> | undefined {

src/client/pythonEnvironments/api.ts

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

4+
import { StopWatch } from '../common/utils/stopWatch';
5+
import { sendTelemetryEvent } from '../telemetry';
6+
import { EventName } from '../telemetry/constants';
7+
import { getEnvPath } from './base/info/env';
48
import { IDiscoveryAPI, PythonLocatorQuery } from './base/locator';
59

610
export type GetLocatorFunc = () => Promise<IDiscoveryAPI>;
@@ -42,8 +46,20 @@ class PythonEnvironments implements IDiscoveryAPI {
4246
return this.locator.resolveEnv(env);
4347
}
4448

45-
public async triggerRefresh(query?: PythonLocatorQuery) {
46-
return this.locator.triggerRefresh(query);
49+
public async triggerRefresh(query?: PythonLocatorQuery, trigger?: 'auto' | 'ui') {
50+
const stopWatch = new StopWatch();
51+
await this.locator.triggerRefresh(query);
52+
if (!query) {
53+
// Intent is to capture time taken for all of discovery to complete, so make sure
54+
// all interpreters are queried for.
55+
sendTelemetryEvent(EventName.PYTHON_INTERPRETER_DISCOVERY, stopWatch.elapsedTime, {
56+
interpreters: this.getEnvs().length,
57+
environmentsWithoutPython: this.getEnvs().filter(
58+
(e) => getEnvPath(e.executable.filename, e.location).pathType === 'envFolderPath',
59+
).length,
60+
trigger: trigger ?? 'auto',
61+
});
62+
}
4763
}
4864
}
4965

src/client/pythonEnvironments/base/locator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ export interface IDiscoveryAPI {
187187
/**
188188
* Triggers a new refresh for query if there isn't any already running.
189189
*/
190-
triggerRefresh(query?: PythonLocatorQuery & { clearCache?: boolean }): Promise<void>;
190+
triggerRefresh(query?: PythonLocatorQuery & { clearCache?: boolean }, trigger?: 'auto' | 'ui'): Promise<void>;
191191
/**
192192
* Get current list of known environments.
193193
*/

src/client/pythonEnvironments/base/locators/composite/envsCollectionService.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,9 @@
44
import { Event, EventEmitter } from 'vscode';
55
import '../../../../common/extensions';
66
import { createDeferred } from '../../../../common/utils/async';
7-
import { StopWatch } from '../../../../common/utils/stopWatch';
87
import { traceError } from '../../../../logging';
9-
import { sendTelemetryEvent } from '../../../../telemetry';
10-
import { EventName } from '../../../../telemetry/constants';
118
import { normalizePath } from '../../../common/externalDependencies';
129
import { PythonEnvInfo } from '../../info';
13-
import { getEnvPath } from '../../info/env';
1410
import { IDiscoveryAPI, IPythonEnvsIterator, IResolvingLocator, PythonLocatorQuery } from '../../locator';
1511
import { getQueryFilter } from '../../locatorUtils';
1612
import { PythonEnvCollectionChangedEvent, PythonEnvsWatcher } from '../../watcher';
@@ -95,7 +91,6 @@ export class EnvsCollectionService extends PythonEnvsWatcher<PythonEnvCollection
9591
}
9692

9793
private startRefresh(query: (PythonLocatorQuery & { clearCache?: boolean }) | undefined): Promise<void> {
98-
const stopWatch = new StopWatch();
9994
const deferred = createDeferred<void>();
10095

10196
if (query?.clearCache) {
@@ -111,13 +106,6 @@ export class EnvsCollectionService extends PythonEnvsWatcher<PythonEnvCollection
111106
// Ensure we delete this before we resolve the promise to accurately track when a refresh finishes.
112107
this.refreshPromises.delete(query);
113108
deferred.resolve();
114-
sendTelemetryEvent(EventName.PYTHON_INTERPRETER_DISCOVERY, stopWatch.elapsedTime, {
115-
interpreters: this.cache.getAllEnvs().length,
116-
environmentsWithoutPython: this.cache
117-
.getAllEnvs()
118-
.filter((e) => getEnvPath(e.executable.filename, e.location).pathType === 'envFolderPath')
119-
.length,
120-
});
121109
})
122110
.catch((ex) => deferred.reject(ex));
123111
}

src/client/pythonEnvironments/legacyIOC.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ class ComponentAdapter implements IComponentAdapter {
103103
});
104104
}
105105

106-
public triggerRefresh(query?: PythonLocatorQuery): Promise<void> {
107-
return this.api.triggerRefresh(query);
106+
public triggerRefresh(query?: PythonLocatorQuery, trigger?: 'auto' | 'ui'): Promise<void> {
107+
return this.api.triggerRefresh(query, trigger);
108108
}
109109

110110
public get refreshPromise() {

src/client/telemetry/index.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,24 +1263,30 @@ export interface IEventNamePropertyMapping {
12631263
useCachedInterpreter?: boolean;
12641264
};
12651265
/**
1266-
* Sends information regarding discovered python environments (virtualenv, conda, pipenv etc.)
1266+
* Telemetry event sent when discovery of all python environments (virtualenv, conda, pipenv etc.) finishes.
12671267
*/
12681268
/* __GDPR__
12691269
"python_interpreter_discovery" : {
12701270
"duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "owner": "karrtikr" },
12711271
"interpreters" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true , "owner": "karrtikr"},
12721272
"environmentsWithoutPython" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "owner": "karrtikr" }
1273+
"trigger" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karrtikr" }
12731274
}
12741275
*/
12751276
[EventName.PYTHON_INTERPRETER_DISCOVERY]: {
12761277
/**
1277-
* The number of the interpreters returned by locator
1278+
* The number of the interpreters discovered
12781279
*/
12791280
interpreters?: number;
12801281
/**
12811282
* The number of environments discovered not containing an interpreter
12821283
*/
12831284
environmentsWithoutPython?: number;
1285+
/*
1286+
* auto : Triggered automatically by the extension or via an API
1287+
* ui : Triggered by clicking a button, particularly the refresh button on the interpreter quickpick
1288+
*/
1289+
trigger: 'auto' | 'ui';
12841290
};
12851291
/**
12861292
* Telemetry event sent when pipenv interpreter discovery is executed.

src/test/configuration/interpreterSelector/commands/setInterpreter.unit.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
WorkspaceFolder,
1717
} from 'vscode';
1818
import { cloneDeep } from 'lodash';
19-
import { instance, mock, verify, when } from 'ts-mockito';
19+
import { anything, instance, mock, verify, when } from 'ts-mockito';
2020
import { IApplicationShell, ICommandManager, IWorkspaceService } from '../../../../client/common/application/types';
2121
import { PathUtils } from '../../../../client/common/platform/pathUtils';
2222
import { IPlatformService } from '../../../../client/common/platform/types';
@@ -472,9 +472,9 @@ suite('Set Interpreter Command', () => {
472472
const refreshButtonCallback = actualParameters!.customButtonSetup?.callback;
473473
expect(refreshButtonCallback).to.not.equal(undefined, 'Callback not set');
474474

475-
when(interpreterService.triggerRefresh()).thenResolve();
475+
when(interpreterService.triggerRefresh(anything(), anything())).thenResolve();
476476
await refreshButtonCallback!({} as QuickPick<QuickPickItem>); // Invoke callback, meaning that the refresh button is clicked.
477-
verify(interpreterService.triggerRefresh()).once();
477+
verify(interpreterService.triggerRefresh(anything(), anything())).once();
478478
});
479479

480480
test('Events to update quickpick updates the quickpick accordingly', async () => {

0 commit comments

Comments
 (0)