Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
37 changes: 36 additions & 1 deletion src/common/telemetry/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { getDefaultEnvManagerSetting, getDefaultPkgManagerSetting } from '../../features/settings/settingHelpers';
import { EnvironmentManagers, PythonProjectManager } from '../../internal.api';
import { getUvEnvironments } from '../../managers/builtin/uvEnvironments';
import { traceVerbose } from '../logging';
import { ISSUES_URL } from '../constants';
import { traceInfo, traceVerbose, traceWarn } from '../logging';
import { getWorkspaceFolders } from '../workspace.apis';
import { EventNames } from './constants';
import { sendTelemetryEvent } from './sender';
Expand Down Expand Up @@ -154,3 +155,37 @@ export async function sendEnvironmentToolUsageTelemetry(
traceVerbose('Failed to send environment tool usage telemetry:', error);
}
}

/**
* 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(', ')})`,
);
}
}
4 changes: 4 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { newProjectSelection } from './common/pickers/managers';
import { StopWatch } from './common/stopWatch';
import { EventNames } from './common/telemetry/constants';
import {
logDiscoverySummary,
sendEnvironmentToolUsageTelemetry,
sendManagerSelectionTelemetry,
sendProjectStructureTelemetry,
Expand Down Expand Up @@ -550,6 +551,9 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
sendManagerSelectionTelemetry(projectManager);
await sendProjectStructureTelemetry(projectManager, envManagers);
await sendEnvironmentToolUsageTelemetry(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