Skip to content

Commit e88993e

Browse files
authored
add and update telemetry for bundle release (#559)
#276
1 parent b2b4e6e commit e88993e

File tree

4 files changed

+117
-14
lines changed

4 files changed

+117
-14
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
mode: agent
3+
---
4+
5+
If the user does not specify an event name or properties, pick an informative and descriptive name for the telemetry event based on the task or feature. Add properties as you see fit to collect the necessary information to achieve the telemetry goal, ensuring they are relevant and useful for diagnostics or analytics.
6+
7+
When adding telemetry:
8+
9+
- If the user wants to record when an action is started (such as a command invocation), place the telemetry call at the start of the handler or function.
10+
- If the user wants to record successful completions or outcomes, place the telemetry call at the end of the action, after the operation has succeeded (and optionally, record errors or failures as well).
11+
12+
Instructions to add a new telemetry event:
13+
14+
1. Add a new event name to the `EventNames` enum in `src/common/telemetry/constants.ts`.
15+
2. Add a corresponding entry to the `IEventNamePropertyMapping` interface in the same file, including a GDPR comment and the expected properties.
16+
3. In the relevant code location, call `sendTelemetryEvent` with the new event name and required properties. Example:
17+
```typescript
18+
sendTelemetryEvent(EventNames.YOUR_EVENT_NAME, undefined, { property: value });
19+
```
20+
4. If the event is triggered by a command, ensure the call is placed at the start of the command handler.
21+
22+
Expected output: The new event is tracked in telemetry and follows the GDPR and codebase conventions.

src/common/pickers/environments.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
import { Uri, ThemeIcon, QuickPickItem, QuickPickItemKind, ProgressLocation, QuickInputButtons } from 'vscode';
1+
import { ProgressLocation, QuickInputButtons, QuickPickItem, QuickPickItemKind, ThemeIcon, Uri } from 'vscode';
22
import { IconPath, PythonEnvironment, PythonProject } from '../../api';
33
import { InternalEnvironmentManager } from '../../internal.api';
44
import { Common, Interpreter, Pickers } from '../localize';
5-
import { showQuickPickWithButtons, showQuickPick, showOpenDialog, withProgress } from '../window.apis';
65
import { traceError } from '../logging';
7-
import { pickEnvironmentManager } from './managers';
8-
import { handlePythonPath } from '../utils/pythonPath';
6+
import { EventNames } from '../telemetry/constants';
7+
import { sendTelemetryEvent } from '../telemetry/sender';
98
import { isWindows } from '../utils/platformUtils';
9+
import { handlePythonPath } from '../utils/pythonPath';
10+
import { showOpenDialog, showQuickPick, showQuickPickWithButtons, withProgress } from '../window.apis';
11+
import { pickEnvironmentManager } from './managers';
1012

1113
type QuickPickIcon =
1214
| Uri
@@ -80,6 +82,7 @@ async function createEnvironment(
8082
const manager = managers.find((m) => m.id === managerId);
8183
if (manager) {
8284
try {
85+
// add telemetry here
8386
const env = await manager.create(
8487
options.projects.map((p) => p.uri),
8588
undefined,
@@ -111,6 +114,10 @@ async function pickEnvironmentImpl(
111114
if (selected.label === Interpreter.browsePath) {
112115
return browseForPython(managers, projectEnvManagers);
113116
} else if (selected.label === Interpreter.createVirtualEnvironment) {
117+
sendTelemetryEvent(EventNames.CREATE_ENVIRONMENT, undefined, {
118+
manager: 'none',
119+
triggeredLocation: 'pickEnv',
120+
});
114121
return createEnvironment(managers, projectEnvManagers, options);
115122
}
116123
return (selected as { result: PythonEnvironment })?.result;

src/common/telemetry/constants.ts

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,34 @@ export enum EventNames {
1111
VENV_CREATION = 'VENV.CREATION',
1212

1313
PACKAGE_MANAGEMENT = 'PACKAGE_MANAGEMENT',
14+
ADD_PROJECT = 'ADD_PROJECT',
15+
/**
16+
* Telemetry event for when a Python environment is created via command.
17+
* Properties:
18+
* - manager: string (the id of the environment manager used, or 'none')
19+
* - triggeredLocation: string (where the create command is called from)
20+
*/
21+
CREATE_ENVIRONMENT = 'CREATE_ENVIRONMENT',
1422
}
1523

1624
// Map all events to their properties
1725
export interface IEventNamePropertyMapping {
1826
/* __GDPR__
1927
"extension.activation_duration": {
20-
"duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" }
28+
"duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" }
2129
}
2230
*/
2331
[EventNames.EXTENSION_ACTIVATION_DURATION]: never | undefined;
2432
/* __GDPR__
2533
"extension.manager_registration_duration": {
26-
"duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" }
34+
"duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" }
2735
}
2836
*/
2937
[EventNames.EXTENSION_MANAGER_REGISTRATION_DURATION]: never | undefined;
3038

3139
/* __GDPR__
3240
"environment_manager.registered": {
33-
"managerId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" }
41+
"managerId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" }
3442
}
3543
*/
3644
[EventNames.ENVIRONMENT_MANAGER_REGISTERED]: {
@@ -39,7 +47,7 @@ export interface IEventNamePropertyMapping {
3947

4048
/* __GDPR__
4149
"package_manager.registered": {
42-
"managerId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" }
50+
"managerId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" }
4351
}
4452
*/
4553
[EventNames.PACKAGE_MANAGER_REGISTERED]: {
@@ -48,7 +56,7 @@ export interface IEventNamePropertyMapping {
4856

4957
/* __GDPR__
5058
"environment_manager.selected": {
51-
"managerId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" }
59+
"managerId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" }
5260
}
5361
*/
5462
[EventNames.ENVIRONMENT_MANAGER_SELECTED]: {
@@ -57,19 +65,19 @@ export interface IEventNamePropertyMapping {
5765

5866
/* __GDPR__
5967
"package_manager.selected": {
60-
"managerId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" }
68+
"managerId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" }
6169
}
6270
*/
6371
[EventNames.PACKAGE_MANAGER_SELECTED]: {
6472
managerId: string;
6573
};
6674

6775
/* __GDPR__
68-
"venv.using_uv": {"owner": "karthiknadig" }
76+
"venv.using_uv": {"owner": "eleanorjboyd" }
6977
*/
7078
[EventNames.VENV_USING_UV]: never | undefined /* __GDPR__
7179
"venv.creation": {
72-
"creationType": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" }
80+
"creationType": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" }
7381
}
7482
*/;
7583
[EventNames.VENV_CREATION]: {
@@ -78,12 +86,38 @@ export interface IEventNamePropertyMapping {
7886

7987
/* __GDPR__
8088
"package_management": {
81-
"managerId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" },
82-
"result": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" }
89+
"managerId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" },
90+
"result": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" }
8391
}
8492
*/
8593
[EventNames.PACKAGE_MANAGEMENT]: {
8694
managerId: string;
8795
result: 'success' | 'error' | 'cancelled';
8896
};
97+
98+
/* __GDPR__
99+
"add_project": {
100+
"template": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" },
101+
"quickCreate": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" },
102+
"totalProjectCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" },
103+
"triggeredLocation": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" }
104+
}
105+
*/
106+
[EventNames.ADD_PROJECT]: {
107+
template: string;
108+
quickCreate: boolean;
109+
totalProjectCount: number;
110+
triggeredLocation: 'templateCreate' | 'add' | 'addGivenResource';
111+
};
112+
113+
/* __GDPR__
114+
"create_environment": {
115+
"manager": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" },
116+
"triggeredLocation": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" }
117+
}
118+
*/
119+
[EventNames.CREATE_ENVIRONMENT]: {
120+
manager: string;
121+
triggeredLocation: string;
122+
};
89123
}

src/extension.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ async function collectEnvironmentInfo(
7878
projectManager: PythonProjectManager,
7979
): Promise<string> {
8080
const info: string[] = [];
81+
8182
try {
8283
// Extension version
8384
const extensionVersion = context.extension?.packageJSON?.version || 'unknown';
@@ -241,9 +242,23 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
241242
await refreshPackagesCommand(item, envManagers);
242243
}),
243244
commands.registerCommand('python-envs.create', async (item) => {
245+
// Telemetry: record environment creation attempt with selected manager
246+
let managerId = 'unknown';
247+
if (item && item.manager && item.manager.id) {
248+
managerId = item.manager.id;
249+
}
250+
sendTelemetryEvent(EventNames.CREATE_ENVIRONMENT, undefined, {
251+
manager: managerId,
252+
triggeredLocation: 'createSpecifiedCommand',
253+
});
244254
return await createEnvironmentCommand(item, envManagers, projectManager);
245255
}),
246256
commands.registerCommand('python-envs.createAny', async (options) => {
257+
// Telemetry: record environment creation attempt with no specific manager
258+
sendTelemetryEvent(EventNames.CREATE_ENVIRONMENT, undefined, {
259+
manager: 'none',
260+
triggeredLocation: 'createAnyCommand',
261+
});
247262
return await createAnyEnvironmentCommand(
248263
envManagers,
249264
projectManager,
@@ -282,13 +297,28 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
282297
}),
283298
commands.registerCommand('python-envs.addPythonProject', async () => {
284299
await addPythonProjectCommand(undefined, projectManager, envManagers, projectCreators);
300+
const totalProjectCount = projectManager.getProjects().length + 1;
301+
sendTelemetryEvent(EventNames.ADD_PROJECT, undefined, {
302+
template: 'none',
303+
quickCreate: false,
304+
totalProjectCount,
305+
triggeredLocation: 'add',
306+
});
285307
}),
286308
commands.registerCommand('python-envs.addPythonProjectGivenResource', async (resource) => {
287309
// Set context to show/hide menu item depending on whether the resource is already a Python project
288310
if (resource instanceof Uri) {
289311
commands.executeCommand('setContext', 'python-envs:isExistingProject', isExistingProject(resource));
290312
}
313+
291314
await addPythonProjectCommand(resource, projectManager, envManagers, projectCreators);
315+
const totalProjectCount = projectManager.getProjects().length + 1;
316+
sendTelemetryEvent(EventNames.ADD_PROJECT, undefined, {
317+
template: 'none',
318+
quickCreate: false,
319+
totalProjectCount,
320+
triggeredLocation: 'addGivenResource',
321+
});
292322
}),
293323
commands.registerCommand('python-envs.removePythonProject', async (item) => {
294324
// Clear environment association before removing project
@@ -344,6 +374,9 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
344374
commands.registerCommand(
345375
'python-envs.createNewProjectFromTemplate',
346376
async (projectType: string, quickCreate: boolean, newProjectName: string, newProjectPath: string) => {
377+
let projectTemplateName = projectType || 'unknown';
378+
let triggeredLocation: 'templateCreate' = 'templateCreate';
379+
let totalProjectCount = projectManager.getProjects().length + 1;
347380
if (quickCreate) {
348381
if (!projectType || !newProjectName || !newProjectPath) {
349382
throw new Error('Project type, name, and path are required for quick create.');
@@ -367,9 +400,16 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
367400
} else {
368401
const selected = await newProjectSelection(projectCreators.getProjectCreators());
369402
if (selected) {
403+
projectTemplateName = selected.name || 'unknown';
370404
await selected.create();
371405
}
372406
}
407+
sendTelemetryEvent(EventNames.ADD_PROJECT, undefined, {
408+
template: projectTemplateName,
409+
quickCreate: quickCreate,
410+
totalProjectCount,
411+
triggeredLocation,
412+
});
373413
},
374414
),
375415
commands.registerCommand('python-envs.reportIssue', async () => {

0 commit comments

Comments
 (0)