Skip to content

Commit 804716f

Browse files
committed
add telemetry for addProject actions
1 parent e4895e2 commit 804716f

3 files changed

Lines changed: 76 additions & 14 deletions

File tree

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/telemetry/constants.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export enum EventNames {
1111
VENV_CREATION = 'VENV.CREATION',
1212

1313
PACKAGE_MANAGEMENT = 'PACKAGE_MANAGEMENT',
14+
ADD_PROJECT = 'ADD_PROJECT',
1415
}
1516

1617
// Map all events to their properties
@@ -86,4 +87,19 @@ export interface IEventNamePropertyMapping {
8687
managerId: string;
8788
result: 'success' | 'error' | 'cancelled';
8889
};
90+
91+
/* __GDPR__
92+
"add_project": {
93+
"template": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" },
94+
"quickCreate": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" },
95+
"totalProjectCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" },
96+
"triggeredLocation": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eleanorjboyd" }
97+
}
98+
*/
99+
[EventNames.ADD_PROJECT]: {
100+
template: string;
101+
quickCreate: boolean;
102+
totalProjectCount: number;
103+
triggeredLocation: 'templateCreate' | 'add' | 'addGivenResource';
104+
};
89105
}

src/extension.ts

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { commands, extensions, ExtensionContext, LogOutputChannel, Terminal, Uri, window, workspace } from 'vscode';
1+
import { commands, ExtensionContext, extensions, LogOutputChannel, Terminal, Uri, window, workspace } from 'vscode';
22
import { PythonEnvironment, PythonEnvironmentApi, PythonProjectCreator } from './api';
33
import { ensureCorrectVersion } from './common/extVersion';
44
import { registerLogger, traceError, traceInfo } from './common/logging';
@@ -75,27 +75,27 @@ import { registerPyenvFeatures } from './managers/pyenv/main';
7575
async function collectEnvironmentInfo(
7676
context: ExtensionContext,
7777
envManagers: EnvironmentManagers,
78-
projectManager: PythonProjectManager
78+
projectManager: PythonProjectManager,
7979
): Promise<string> {
8080
const info: string[] = [];
81-
81+
8282
try {
8383
// Extension version
8484
const extensionVersion = context.extension?.packageJSON?.version || 'unknown';
8585
info.push(`Extension Version: ${extensionVersion}`);
86-
86+
8787
// Python extension version
8888
const pythonExtension = extensions.getExtension('ms-python.python');
8989
const pythonVersion = pythonExtension?.packageJSON?.version || 'not installed';
9090
info.push(`Python Extension Version: ${pythonVersion}`);
91-
91+
9292
// Environment managers
9393
const managers = envManagers.managers;
9494
info.push(`\nRegistered Environment Managers (${managers.length}):`);
95-
managers.forEach(manager => {
95+
managers.forEach((manager) => {
9696
info.push(` - ${manager.id} (${manager.displayName})`);
9797
});
98-
98+
9999
// Available environments
100100
const allEnvironments: PythonEnvironment[] = [];
101101
for (const manager of managers) {
@@ -106,7 +106,7 @@ async function collectEnvironmentInfo(
106106
info.push(` Error getting environments from ${manager.id}: ${err}`);
107107
}
108108
}
109-
109+
110110
info.push(`\nTotal Available Environments: ${allEnvironments.length}`);
111111
if (allEnvironments.length > 0) {
112112
info.push('Environment Details:');
@@ -117,7 +117,7 @@ async function collectEnvironmentInfo(
117117
info.push(` ... and ${allEnvironments.length - 10} more environments`);
118118
}
119119
}
120-
120+
121121
// Python projects
122122
const projects = projectManager.getProjects();
123123
info.push(`\nPython Projects (${projects.length}):`);
@@ -133,18 +133,17 @@ async function collectEnvironmentInfo(
133133
info.push(` Error getting environment: ${err}`);
134134
}
135135
}
136-
136+
137137
// Current settings (non-sensitive)
138138
const config = workspace.getConfiguration('python-envs');
139139
info.push('\nExtension Settings:');
140140
info.push(` Default Environment Manager: ${config.get('defaultEnvManager')}`);
141141
info.push(` Default Package Manager: ${config.get('defaultPackageManager')}`);
142142
info.push(` Terminal Auto Activation: ${config.get('terminal.autoActivationType')}`);
143-
144143
} catch (err) {
145144
info.push(`\nError collecting environment information: ${err}`);
146145
}
147-
146+
148147
return info.join('\n');
149148
}
150149

@@ -273,13 +272,28 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
273272
}),
274273
commands.registerCommand('python-envs.addPythonProject', async () => {
275274
await addPythonProjectCommand(undefined, projectManager, envManagers, projectCreators);
275+
const totalProjectCount = projectManager.getProjects().length + 1;
276+
sendTelemetryEvent(EventNames.ADD_PROJECT, undefined, {
277+
template: 'none',
278+
quickCreate: false,
279+
totalProjectCount,
280+
triggeredLocation: 'add',
281+
});
276282
}),
277283
commands.registerCommand('python-envs.addPythonProjectGivenResource', async (resource) => {
278284
// Set context to show/hide menu item depending on whether the resource is already a Python project
279285
if (resource instanceof Uri) {
280286
commands.executeCommand('setContext', 'python-envs:isExistingProject', isExistingProject(resource));
281287
}
288+
282289
await addPythonProjectCommand(resource, projectManager, envManagers, projectCreators);
290+
const totalProjectCount = projectManager.getProjects().length + 1;
291+
sendTelemetryEvent(EventNames.ADD_PROJECT, undefined, {
292+
template: 'none',
293+
quickCreate: false,
294+
totalProjectCount,
295+
triggeredLocation: 'addGivenResource',
296+
});
283297
}),
284298
commands.registerCommand('python-envs.removePythonProject', async (item) => {
285299
// Clear environment association before removing project
@@ -335,6 +349,9 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
335349
commands.registerCommand(
336350
'python-envs.createNewProjectFromTemplate',
337351
async (projectType: string, quickCreate: boolean, newProjectName: string, newProjectPath: string) => {
352+
let projectTemplateName = projectType || 'unknown';
353+
let triggeredLocation: 'templateCreate' = 'templateCreate';
354+
let totalProjectCount = projectManager.getProjects().length + 1;
338355
if (quickCreate) {
339356
if (!projectType || !newProjectName || !newProjectPath) {
340357
throw new Error('Project type, name, and path are required for quick create.');
@@ -358,19 +375,26 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
358375
} else {
359376
const selected = await newProjectSelection(projectCreators.getProjectCreators());
360377
if (selected) {
378+
projectTemplateName = selected.name || 'unknown';
361379
await selected.create();
362380
}
363381
}
382+
sendTelemetryEvent(EventNames.ADD_PROJECT, undefined, {
383+
template: projectTemplateName,
384+
quickCreate: quickCreate,
385+
totalProjectCount,
386+
triggeredLocation,
387+
});
364388
},
365389
),
366390
commands.registerCommand('python-envs.reportIssue', async () => {
367391
try {
368392
const issueData = await collectEnvironmentInfo(context, envManagers, projectManager);
369-
393+
370394
await commands.executeCommand('workbench.action.openIssueReporter', {
371395
extensionId: 'ms-python.vscode-python-envs',
372396
issueTitle: '[Python Environments] ',
373-
issueBody: `<!-- Please describe the issue you're experiencing -->\n\n<!-- The following information was automatically generated -->\n\n<details>\n<summary>Environment Information</summary>\n\n\`\`\`\n${issueData}\n\`\`\`\n\n</details>`
397+
issueBody: `<!-- Please describe the issue you're experiencing -->\n\n<!-- The following information was automatically generated -->\n\n<details>\n<summary>Environment Information</summary>\n\n\`\`\`\n${issueData}\n\`\`\`\n\n</details>`,
374398
});
375399
} catch (error) {
376400
window.showErrorMessage(`Failed to open issue reporter: ${error}`);

0 commit comments

Comments
 (0)