From 021a997e60a4efa4a2a104c8222219d031f7e483 Mon Sep 17 00:00:00 2001 From: Vijay Upadya <41652029+vijayupadya@users.noreply.github.com> Date: Tue, 31 Mar 2026 18:46:59 -0700 Subject: [PATCH] Agent Debug: Add configurable max session log size setting for debug file logging (#4890) * Add configurable max session log size setting for debug file logging * update --- extensions/copilot/package.json | 11 +++++++ extensions/copilot/package.nls.json | 1 + .../vscode-node/chatDebugFileLoggerService.ts | 30 ++++++++++++++----- .../common/configurationService.ts | 1 + 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/extensions/copilot/package.json b/extensions/copilot/package.json index dc683c6ffbf..c5bd6e10dfe 100644 --- a/extensions/copilot/package.json +++ b/extensions/copilot/package.json @@ -4650,6 +4650,17 @@ "onExp" ] }, + "github.copilot.chat.agentDebugLog.fileLogging.maxSessionLogSizeMB": { + "type": "number", + "default": 100, + "minimum": 1, + "markdownDescription": "%github.copilot.config.chat.agentDebugLog.fileLogging.maxSessionLogSizeMB%", + "tags": [ + "advanced", + "experimental", + "onExp" + ] + }, "github.copilot.chat.otel.enabled": { "type": "boolean", "default": false, diff --git a/extensions/copilot/package.nls.json b/extensions/copilot/package.nls.json index 87fdda96c0a..8d0810289bb 100644 --- a/extensions/copilot/package.nls.json +++ b/extensions/copilot/package.nls.json @@ -379,6 +379,7 @@ "github.copilot.config.chat.agentDebugLog.fileLogging.enabled": "Enable writing chat debug events to JSONL files on disk for diagnostics. When disabled, the built-in `troubleshoot` skill is also disabled. Requires window reload to take effect.", "github.copilot.config.chat.agentDebugLog.fileLogging.flushIntervalMs": "How often (in milliseconds) buffered debug log entries are flushed to disk. Lower values provide more up-to-date logs at the cost of more frequent disk writes.", "github.copilot.config.chat.agentDebugLog.fileLogging.maxRetainedSessionLogs": "Maximum number of chat debug session log directories to retain on disk. Each chat session produces one directory. Older session logs are automatically deleted when this limit is exceeded.", + "github.copilot.config.chat.agentDebugLog.fileLogging.maxSessionLogSizeMB": "Maximum size in megabytes for a single chat debug session log file. When the log exceeds this size, older entries are truncated to retain the most recent data. Defaults to 100 MB.", "github.copilot.config.inlineEdits.diagnosticsContextProvider.enabled": "Enable diagnostics context provider for next edit suggestions.", "github.copilot.config.inlineEdits.chatSessionContextProvider.enabled": "Enable chat session context provider for next edit suggestions.", "github.copilot.config.codesearch.agent.enabled": "Enable code search capabilities in agent mode.", diff --git a/extensions/copilot/src/extension/chat/vscode-node/chatDebugFileLoggerService.ts b/extensions/copilot/src/extension/chat/vscode-node/chatDebugFileLoggerService.ts index 6e5de0c9b2e..ac3d1634fcf 100644 --- a/extensions/copilot/src/extension/chat/vscode-node/chatDebugFileLoggerService.ts +++ b/extensions/copilot/src/extension/chat/vscode-node/chatDebugFileLoggerService.ts @@ -25,8 +25,8 @@ const DEFAULT_FLUSH_INTERVAL_MS = 4_000; const MIN_FLUSH_INTERVAL_MS = 2_000; const MAX_ATTR_VALUE_LENGTH = 5_000; const MAX_PENDING_CORE_EVENTS = 100; -const MAX_SESSION_LOG_BYTES = 100 * 1024 * 1024; // 100MB -const TRUNCATION_RETAIN_BYTES = 60 * 1024 * 1024; // 60 MB +const DEFAULT_MAX_SESSION_LOG_MB = 100; +const TRUNCATION_RETAIN_RATIO = 0.6; // retain 60% of max on truncation const MAX_SPAN_SESSION_INDEX = 10_000; @@ -87,6 +87,7 @@ export class ChatDebugFileLoggerService extends Disposable implements IChatDebug private _debugLogsDirUri: URI | undefined; private _autoFlushTimer: ReturnType | undefined; private _autoFlushIntervalMs: number; + private _maxSessionLogBytes: number; private _totalBytesWritten = 0; private _totalSessionCount = 0; @@ -111,17 +112,22 @@ export class ChatDebugFileLoggerService extends Disposable implements IChatDebug */ this._telemetryService.sendMSFTTelemetryEvent('chatDebugFileLogger.disabled'); this._autoFlushIntervalMs = DEFAULT_FLUSH_INTERVAL_MS; + this._maxSessionLogBytes = DEFAULT_MAX_SESSION_LOG_MB * 1024 * 1024; return; } this._autoFlushIntervalMs = Math.max(MIN_FLUSH_INTERVAL_MS, this._configurationService.getConfig(ConfigKey.Advanced.ChatDebugFileLoggingFlushInterval) ?? DEFAULT_FLUSH_INTERVAL_MS); + this._maxSessionLogBytes = this._resolveMaxSessionLogBytes(); - // React to flush interval changes at runtime + // React to changes at runtime this._register(this._configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(ConfigKey.Advanced.ChatDebugFileLoggingFlushInterval.fullyQualifiedId)) { this._autoFlushIntervalMs = Math.max(MIN_FLUSH_INTERVAL_MS, this._configurationService.getConfig(ConfigKey.Advanced.ChatDebugFileLoggingFlushInterval) ?? DEFAULT_FLUSH_INTERVAL_MS); this._restartFlushTimer(); } + if (e.affectsConfiguration(ConfigKey.Advanced.ChatDebugFileLoggingMaxSessionLogSizeMB.fullyQualifiedId)) { + this._maxSessionLogBytes = this._resolveMaxSessionLogBytes(); + } })); // Subscribe to OTel span completions @@ -142,6 +148,12 @@ export class ChatDebugFileLoggerService extends Disposable implements IChatDebug } } + private _resolveMaxSessionLogBytes(): number { + const raw = this._configurationService.getExperimentBasedConfig(ConfigKey.Advanced.ChatDebugFileLoggingMaxSessionLogSizeMB, this._experimentationService); + const mb = typeof raw === 'number' && Number.isFinite(raw) ? raw : DEFAULT_MAX_SESSION_LOG_MB; + return Math.max(1, Math.floor(mb)) * 1024 * 1024; + } + override dispose(): void { if (this._autoFlushTimer) { clearInterval(this._autoFlushTimer); @@ -764,7 +776,7 @@ export class ChatDebugFileLoggerService extends Disposable implements IChatDebug } await fs.promises.appendFile(session.uri.fsPath, content, 'utf-8'); session.bytesWritten += Buffer.byteLength(content, 'utf-8'); - if (session.bytesWritten > MAX_SESSION_LOG_BYTES) { + if (session.bytesWritten > this._maxSessionLogBytes) { await this._truncateLogFile(session); } } catch (err) { @@ -773,18 +785,20 @@ export class ChatDebugFileLoggerService extends Disposable implements IChatDebug } /** - * Truncate a log file to retain the newest ~80MB using a streaming - * approach via a temp file to avoid loading the entire tail into memory. + * Truncate a log file to retain the newest portion (TRUNCATION_RETAIN_RATIO + * of the configured max size) using a streaming approach via a temp file + * to avoid loading the entire tail into memory. */ private async _truncateLogFile(session: IActiveLogSession): Promise { try { const filePath = session.uri.fsPath; const stat = await fs.promises.stat(filePath); - if (stat.size <= MAX_SESSION_LOG_BYTES) { + if (stat.size <= this._maxSessionLogBytes) { return; } - const skipBytes = stat.size - TRUNCATION_RETAIN_BYTES; + const retainBytes = Math.floor(this._maxSessionLogBytes * TRUNCATION_RETAIN_RATIO); + const skipBytes = stat.size - retainBytes; const fd = await fs.promises.open(filePath, 'r'); try { // Read a small probe around the cut point to find the next newline diff --git a/extensions/copilot/src/platform/configuration/common/configurationService.ts b/extensions/copilot/src/platform/configuration/common/configurationService.ts index c472bec1063..0d91451b5c3 100644 --- a/extensions/copilot/src/platform/configuration/common/configurationService.ts +++ b/extensions/copilot/src/platform/configuration/common/configurationService.ts @@ -692,6 +692,7 @@ export namespace ConfigKey { export const ChatDebugFileLogging = defineAndMigrateExpSetting('chat.chatDebug.fileLogging.enabled', 'chat.agentDebugLog.fileLogging.enabled', false); export const ChatDebugFileLoggingFlushInterval = defineAndMigrateSetting('chat.chatDebug.fileLogging.flushIntervalMs', 'chat.agentDebugLog.fileLogging.flushIntervalMs', 4000); export const ChatDebugFileLoggingMaxRetainedSessionLogs = defineSetting('chat.agentDebugLog.fileLogging.maxRetainedSessionLogs', ConfigType.ExperimentBased, 50); + export const ChatDebugFileLoggingMaxSessionLogSizeMB = defineSetting('chat.agentDebugLog.fileLogging.maxSessionLogSizeMB', ConfigType.ExperimentBased, 100); // OTel settings export const OTelEnabled = defineSetting('chat.otel.enabled', ConfigType.Simple, false);