Skip to content

VS Code Session and Window Architecture Deep Dive

Niclas Olofsson edited this page Aug 13, 2025 · 1 revision

VS Code Session and Window Architecture: A Deep Dive

Table of Contents

  1. Overview
  2. VS Code Session Architecture
  3. Window Management
  4. Log Directory Structure
  5. Extension Context and LogUri
  6. Real-Time vs Historical Monitoring Strategy
  7. Implementation Deep Dive
  8. Common Misconceptions
  9. Best Practices

Overview

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.

VS Code Session Architecture

What is a VS Code Session?

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."

Key Characteristics:

  • 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

Session Lifecycle:

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)

Session Directory Structure:

%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)

Window Management

What is a VS Code Window?

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.

Window Creation Scenarios:

  1. Initial Launch: window1 created automatically
  2. File → New Window: Creates window2, window3, etc.
  3. Opening Different Workspace: May create new window or reuse existing
  4. Extension Development Host (F5): Always creates new window for testing
  5. Opening Different Project: Creates new window if not replacing current

Window Characteristics:

  • 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 exthost directory
  • Isolated contexts: Extension instances in different windows don't share state

Window Directory Structure:

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

Log Directory Structure

Extension Host Logs

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

Key Insights:

  • Sibling directories: Extensions and Copilot logs are siblings in the same exthost directory
  • Window isolation: Each window has completely separate extension log directories
  • Independent instances: The same extension running in different windows gets separate log directories

Extension Context and LogUri

Understanding extensionContext.logUri

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-vscode

Navigation Strategy

To 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-chat

This 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

Real-Time vs Historical Monitoring Strategy

Two Distinct Use Cases

Our extension implements two completely different monitoring strategies:

1. Historical Analytics (Comprehensive)

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>

2. Real-Time Monitoring (Window-Specific)

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>

Why This Separation Matters

  1. Performance: Real-time monitoring of all windows would create excessive file system overhead
  2. Relevance: Users care about live updates for their current workspace, not other windows
  3. Resource management: Prevents multiple extension instances from interfering with each other
  4. Data integrity: Ensures clean separation between live data and historical analytics

Implementation Deep Dive

Real-Time Log Finding

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);
}

Real-Time Log Watching

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);
    });
}

Historical Scanning

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;
}

Common Misconceptions

❌ "Session = Starting/Stopping VS Code"

Reality: Sessions persist across multiple window operations and can span hours of work

❌ "Find the most active log file"

Reality: Must target the specific window where the extension is running, regardless of activity level

❌ "Monitor all windows for real-time updates"

Reality: Real-time monitoring should be window-specific to avoid performance issues and cross-contamination

❌ "Extension context is global"

Reality: Each extension instance gets its own context tied to the specific window it's running in

❌ "Historical and real-time scanning use same logic"

Reality: They serve different purposes and require completely different scoping strategies

Best Practices

1. Always Use Extension Context as Reference

// ✅ 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();

2. Separate Historical from Real-Time Logic

// ✅ 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)

3. Respect Window Boundaries

// ✅ 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();

4. Use Appropriate Logging

// ✅ 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');

5. Handle Multiple Extension Instances

// ✅ 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();

Conclusion

Understanding VS Code's session and window architecture is essential for building robust extensions that monitor logs or user activity. The key insights are:

  1. Sessions are broader service instances that manage multiple windows
  2. Windows are individual workspaces with isolated extension contexts
  3. Real-time monitoring should be window-specific using extension context
  4. Historical analytics can be comprehensive across all sessions and windows
  5. 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.

Clone this wiki locally