-
Notifications
You must be signed in to change notification settings - Fork 3
VS Code Session and Window Architecture Deep Dive
- Overview
- VS Code Session Architecture
- Window Management
- Log Directory Structure
- Extension Context and LogUri
- Real-Time vs Historical Monitoring Strategy
- Implementation Deep Dive
- Common Misconceptions
- Best Practices
Understanding VS Code's session and window architecture is crucial for building extensions that need to monitor logs, track user activity, or provide analytics. This document provides a comprehensive explanation of these concepts and demonstrates how to correctly implement log monitoring that respects VS Code's architectural boundaries.
A VS Code Session is a single instance of the VS Code service process that manages all editor windows and extensions for that particular launch. It's broader than just starting and stopping VS Code - once VS Code is running, everything that happens afterward goes into the same "session."
- Service-like behavior: One session can manage multiple windows over time
- Persistent across window operations: Opening new windows, workspaces, or projects doesn't create a new session
-
Unique identifier: Each session gets a timestamp-based directory name (e.g.,
20250813T155250) - Process boundary: Different VS Code instances (if launched separately) create separate sessions
VS Code Launch → Session Created (20250813T155250)
├── Initial window opened (window1)
├── User opens new workspace (window2)
├── User starts Extension Development Host via F5 (window3)
├── User opens different project (window4)
└── ... (all within same session until VS Code fully exits)
%AppData%\Code - Insiders\logs\
├── 20250813T110757\ # Previous session
├── 20250813T155250\ # Current session
│ ├── window1\ # First window
│ ├── window2\ # Second window
│ ├── window3\ # Extension Development Host
│ └── window4\ # Additional project
└── 20250813T160145\ # Future session (if VS Code restarted)
A VS Code Window is an individual editor interface within a session. Each window represents a separate workspace, project, or editor instance that users can interact with independently.
-
Initial Launch:
window1created automatically -
File → New Window: Creates
window2,window3, etc. - Opening Different Workspace: May create new window or reuse existing
- Extension Development Host (F5): Always creates new window for testing
- Opening Different Project: Creates new window if not replacing current
- Independent workspaces: Each window can have different projects, settings, extensions
- Separate extension hosts: Extensions run independently in each window
-
Individual log directories: Each window gets its own
exthostdirectory - Isolated contexts: Extension instances in different windows don't share state
20250813T155250\ # Session
├── window1\ # Regular workspace
│ ├── exthost\
│ │ ├── GitHub.copilot-chat\
│ │ ├── ms-python.python\
│ │ └── exthost.log
│ └── renderer.log
├── window2\ # Extension Development Host
│ ├── exthost\
│ │ ├── GitHub.copilot-chat\
│ │ ├── nickeolofsson.remember-mcp-vscode\ # Our extension
│ │ └── exthost.log
│ └── renderer.log
└── window3\ # Different project
├── exthost\
│ ├── GitHub.copilot-chat\
│ ├── ms-vscode.vscode-typescript-next\
│ └── exthost.log
└── renderer.log
Each window's exthost directory contains logs for all extensions running in that specific window:
window2\exthost\
├── exthost.log # Core extension host logs
├── extHostTelemetry.log # Telemetry data
├── GitHub.copilot\ # Copilot extension logs
├── GitHub.copilot-chat\ # Copilot Chat logs
│ └── GitHub Copilot Chat.log # Target log file
├── nickeolofsson.remember-mcp-vscode\ # Our extension logs
├── ms-python.python\ # Python extension logs
└── vscode.git\ # Git extension logs
-
Sibling directories: Extensions and Copilot logs are siblings in the same
exthostdirectory - Window isolation: Each window has completely separate extension log directories
- Independent instances: The same extension running in different windows gets separate log directories
VS Code provides each extension instance with a logUri that points to its specific log directory:
// Example logUri.fsPath for our extension in window2:
C:\Users\Niclas.Olofsson\AppData\Roaming\Code - Insiders\logs\20250813T155250\window2\exthost\nickeolofsson.remember-mcp-vscodeTo find the Copilot log directory from the extension's log directory:
const extensionLogDir = extensionContext.logUri.fsPath;
// C:\...\logs\20250813T155250\window2\exthost\nickeolofsson.remember-mcp-vscode
const exthostDir = path.dirname(extensionLogDir);
// C:\...\logs\20250813T155250\window2\exthost
const copilotLogDir = path.join(exthostDir, 'GitHub.copilot-chat');
// C:\...\logs\20250813T155250\window2\exthost\GitHub.copilot-chatThis approach ensures:
- ✅ Correct window targeting: Only monitors the same window the extension runs in
- ✅ Session isolation: Doesn't interfere with other sessions
- ✅ Sibling directory access: Leverages the fact that all extension logs are siblings
Our extension implements two completely different monitoring strategies:
Purpose: Provide complete usage history and analytics Scope: All sessions, all days, all windows, both VS Code Stable and Insiders
// Scans everything:
// - %AppData%\Code\logs\**\window*\exthost\GitHub.copilot-chat\*.log
// - %AppData%\Code - Insiders\logs\**\window*\exthost\GitHub.copilot-chat\*.log
async scanAllHistoricalLogs(): Promise<LogScanResult>Purpose: Live updates for current activity Scope: Only the specific window where this extension instance is running
// Watches only:
// - Current session, current window: window2\exthost\GitHub.copilot-chat\*.log
async setupLogWatcher(): Promise<void>- Performance: Real-time monitoring of all windows would create excessive file system overhead
- Relevance: Users care about live updates for their current workspace, not other windows
- Resource management: Prevents multiple extension instances from interfering with each other
- Data integrity: Ensures clean separation between live data and historical analytics
async findLogPath(): Promise<string | null> {
if (!this.extensionContext) {
return null;
}
// Get our extension's log directory
const sessionLogUri = this.extensionContext.logUri;
const sessionLogDir = sessionLogUri.fsPath;
// Navigate to sibling Copilot directory
const exthostDir = path.dirname(sessionLogDir);
const copilotLogDir = path.join(exthostDir, 'GitHub.copilot-chat');
// Find the actual log file
return await this.findLogInDirectory(copilotLogDir);
}private async setupLogWatcher(): Promise<void> {
const sessionLogUri = this.extensionContext.logUri;
const sessionLogDir = sessionLogUri.fsPath;
const exthostDir = path.dirname(sessionLogDir);
const copilotLogDir = path.join(exthostDir, 'GitHub.copilot-chat');
// Watch only this specific directory
this.watcher = new ForceFileWatcher(
new vscode.RelativePattern(copilotLogDir, '*.log'),
1000, // Force flush interval
300 // Debounce interval
);
// Handle file changes in current window only
this.watcher.onDidChange(async (uri) => {
const result = await this.scanLogFile(uri.fsPath);
this.notifyLogUpdateCallbacks(result);
});
}async findAllHistoricalLogPaths(): Promise<Array<{logPath: string, version: string, session: string}>> {
const logRoots = [
path.join(process.env.APPDATA, 'Code', 'logs'), // Stable
path.join(process.env.APPDATA, 'Code - Insiders', 'logs') // Insiders
];
const allLogPaths = [];
for (const logRoot of logRoots) {
const sessions = await fs.readdir(logRoot);
for (const sessionName of sessions) {
const windows = await fs.readdir(path.join(logRoot, sessionName));
for (const windowName of windows.filter(w => w.startsWith('window'))) {
const copilotLogDir = path.join(
logRoot, sessionName, windowName,
'exthost', 'GitHub.copilot-chat'
);
const logPath = await this.findLogInDirectory(copilotLogDir);
if (logPath) {
allLogPaths.push({ logPath, version: ..., session: sessionName });
}
}
}
}
return allLogPaths;
}Reality: Sessions persist across multiple window operations and can span hours of work
Reality: Must target the specific window where the extension is running, regardless of activity level
Reality: Real-time monitoring should be window-specific to avoid performance issues and cross-contamination
Reality: Each extension instance gets its own context tied to the specific window it's running in
Reality: They serve different purposes and require completely different scoping strategies
// ✅ Correct: Use extension's own logUri
const extensionLogDir = this.extensionContext.logUri.fsPath;
const exthostDir = path.dirname(extensionLogDir);
// ❌ Wrong: Search for "most active" or "latest" log
const mostActiveLog = await this.findMostActiveLogFile();// ✅ Correct: Separate methods for different purposes
async scanAllHistoricalLogs() // Comprehensive, all sessions/windows
async setupLogWatcher() // Window-specific, real-time only
// ❌ Wrong: One method trying to do both
async scanLogs(includeHistorical: boolean)// ✅ Correct: Target sibling directory in same window
const copilotDir = path.join(exthostDir, 'GitHub.copilot-chat');
// ❌ Wrong: Search across multiple windows
const allCopilotDirs = await this.findAllCopilotDirectories();// ✅ Correct: Clear scope indicators
this.logger.trace('REAL-TIME: Watching current window only');
this.logger.debug('HISTORICAL: Scanning all sessions');
// ❌ Wrong: Ambiguous logging
this.logger.debug('Scanning logs');// ✅ Correct: Each instance manages its own window
constructor(logger: ILogger, extensionContext?: vscode.ExtensionContext) {
this.extensionContext = extensionContext; // Window-specific context
}
// ❌ Wrong: Static/global monitoring
static globalLogWatcher = new LogWatcher();Understanding VS Code's session and window architecture is essential for building robust extensions that monitor logs or user activity. The key insights are:
- Sessions are broader service instances that manage multiple windows
- Windows are individual workspaces with isolated extension contexts
- Real-time monitoring should be window-specific using extension context
- Historical analytics can be comprehensive across all sessions and windows
- Sibling directory navigation is the correct approach for finding related logs
By following these principles, extensions can provide accurate analytics while respecting VS Code's architectural boundaries and maintaining optimal performance.