Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const ENVS_EXTENSION_ID = 'ms-python.vscode-python-envs';
export const PYTHON_EXTENSION_ID = 'ms-python.python';
export const JUPYTER_EXTENSION_ID = 'ms-toolsai.jupyter';
export const EXTENSION_ROOT_DIR = path.dirname(__dirname);
export const ISSUES_URL = 'https://github.com/microsoft/vscode-python-environments/issues';

export const DEFAULT_PACKAGE_MANAGER_ID = 'ms-python.python:pip';
export const DEFAULT_ENV_MANAGER_ID = 'ms-python.python:venv';
Expand Down
38 changes: 37 additions & 1 deletion src/common/telemetry/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { getDefaultEnvManagerSetting, getDefaultPkgManagerSetting } from '../../features/settings/settingHelpers';
import { EnvironmentManagers, PythonProjectManager } from '../../internal.api';
import { ISSUES_URL } from '../constants';
import { traceInfo, traceWarn } from '../logging';
import { getWorkspaceFolders } from '../workspace.apis';
import { EventNames } from './constants';
import { sendTelemetryEvent } from './sender';
Expand Down Expand Up @@ -58,7 +60,7 @@ export async function sendProjectStructureTelemetry(
for (const wsFolder of workspaceFolders) {
const workspacePath = wsFolder.uri.fsPath;
const projectPath = project.uri.fsPath;

// Check if project is a subdirectory of workspace folder:
// - Path must start with workspace path
// - Path must not be equal to workspace path
Expand All @@ -80,3 +82,37 @@ export async function sendProjectStructureTelemetry(
projectUnderRoot,
});
}

/**
* Logs a summary of environment discovery results after startup.
* If no environments are found, logs guidance to help users troubleshoot.
*/
export async function logDiscoverySummary(envManagers: EnvironmentManagers): Promise<void> {
const managers = envManagers.managers;
let totalEnvCount = 0;
const managerSummaries: string[] = [];

for (const manager of managers) {
try {
const envs = await manager.getEnvironments('all');
totalEnvCount += envs.length;
if (envs.length > 0) {
managerSummaries.push(`${manager.displayName}: ${envs.length}`);
}
} catch {
// Discovery errors are already logged by InternalEnvironmentManager.refresh()
}
}

if (totalEnvCount === 0) {
traceWarn(
`No Python environments were found. ` +
`Try running "Python Environments: Run Python Environment Tool (PET) in Terminal..." from the Command Palette to diagnose. ` +
`If environments should be detected, please report this: ${ISSUES_URL}/new`,
);
} else {
traceInfo(
`Environment discovery complete: ${totalEnvCount} environments found (${managerSummaries.join(', ')})`,
);
}
}
9 changes: 8 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ import { clearPersistentState, setPersistentState } from './common/persistentSta
import { newProjectSelection } from './common/pickers/managers';
import { StopWatch } from './common/stopWatch';
import { EventNames } from './common/telemetry/constants';
import { sendManagerSelectionTelemetry, sendProjectStructureTelemetry } from './common/telemetry/helpers';
import {
logDiscoverySummary,
sendManagerSelectionTelemetry,
sendProjectStructureTelemetry,
} from './common/telemetry/helpers';
import { sendTelemetryEvent } from './common/telemetry/sender';
import { createDeferred } from './common/utils/deferred';

Expand Down Expand Up @@ -545,6 +549,9 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
await terminalManager.initialize(api);
sendManagerSelectionTelemetry(projectManager);
await sendProjectStructureTelemetry(projectManager, envManagers);

// Log discovery summary to help users troubleshoot environment detection issues
await logDiscoverySummary(envManagers);
} catch (error) {
traceError('Failed to initialize environment managers:', error);
// Show a user-friendly error message
Expand Down
30 changes: 27 additions & 3 deletions src/internal.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ import {
ResolveEnvironmentContext,
SetEnvironmentScope,
} from './api';
import { ISSUES_URL } from './common/constants';
import { CreateEnvironmentNotSupported, RemoveEnvironmentNotSupported } from './common/errors/NotSupportedError';
import { traceWarn } from './common/logging';
import { StopWatch } from './common/stopWatch';
import { EventNames } from './common/telemetry/constants';
import { sendTelemetryEvent } from './common/telemetry/sender';
Expand Down Expand Up @@ -204,26 +206,48 @@ export class InternalEnvironmentManager implements EnvironmentManager {

async refresh(options: RefreshEnvironmentsScope): Promise<void> {
const sw = new StopWatch();
const SLOW_DISCOVERY_THRESHOLD_MS = 15000;
try {
await this.manager.refresh(options);
const envs = await this.manager.getEnvironments('all').catch(() => []);
sendTelemetryEvent(EventNames.ENVIRONMENT_DISCOVERY, sw.elapsedTime, {
const duration = sw.elapsedTime;
sendTelemetryEvent(EventNames.ENVIRONMENT_DISCOVERY, duration, {
managerId: this.id,
result: 'success',
envCount: envs.length,
});

// Log warning for slow discovery
if (duration > SLOW_DISCOVERY_THRESHOLD_MS) {
traceWarn(
`[${this.displayName}] Environment discovery took ${(duration / 1000).toFixed(1)}s (found ${envs.length} environments). ` +
`If this is causing problems, please report it: ${ISSUES_URL}/new`,
);
}
} catch (ex) {
const duration = sw.elapsedTime;
const isTimeout = ex instanceof Error && ex.message.includes('timed out');
const errorType = ex instanceof Error ? ex.name : 'unknown';
sendTelemetryEvent(
EventNames.ENVIRONMENT_DISCOVERY,
sw.elapsedTime,
duration,
{
managerId: this.id,
result: isTimeout ? 'timeout' : 'error',
errorType: ex instanceof Error ? ex.name : 'unknown',
errorType,
},
ex instanceof Error ? ex : undefined,
);

// Log verbose failure message to help users report issues
const errorMessage = ex instanceof Error ? ex.message : String(ex);
traceWarn(
`[${this.displayName}] Environment discovery failed after ${(duration / 1000).toFixed(1)}s.\n` +
` Error: ${errorType} - ${errorMessage}\n` +
` If environments are not being detected correctly, please report this issue:\n` +
` ${ISSUES_URL}/new`,
);

throw ex;
}
}
Expand Down