From d6e0faed5ed8838da1e35f00d1ba520ef536be1f Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Mon, 2 Mar 2026 09:27:13 -0800 Subject: [PATCH 1/2] Show readme with log description and sharing instructions --- src/lsptoolshost/logging/collectLogs.ts | 41 +++++---- src/lsptoolshost/logging/loggingUtils.ts | 110 +++++++++++++++++++++++ 2 files changed, 135 insertions(+), 16 deletions(-) diff --git a/src/lsptoolshost/logging/collectLogs.ts b/src/lsptoolshost/logging/collectLogs.ts index 16f1fabad..0b7c42e7d 100644 --- a/src/lsptoolshost/logging/collectLogs.ts +++ b/src/lsptoolshost/logging/collectLogs.ts @@ -19,6 +19,8 @@ import { verifyOrAcquireDotnetTool, DumpType, createActivityLogCapture, + generateReadmeContent, + LogsToCollect, } from './loggingUtils'; import { runDotnetTraceInTerminal } from './profiling'; import { RazorLogger } from '../../razor/src/razorLogger'; @@ -30,13 +32,6 @@ interface CollectOptionQuickPickItem extends vscode.QuickPickItem { option: CollectOption; } -interface LogsToCollect { - activityLogs: boolean; - performanceTrace: boolean; - memoryDump: boolean; - gcDump: boolean; -} - /** * Registers the unified collect logs command. */ @@ -142,15 +137,29 @@ async function collectLogs( }), { modal: true } ); - } else if (archiveResult.uri) { - const openFolder = vscode.l10n.t('Open Folder'); - const result = await vscode.window.showInformationMessage( - vscode.l10n.t('C# logs saved successfully.'), - openFolder - ); - if (result === openFolder) { - await vscode.commands.executeCommand('revealFileInOS', archiveResult.uri); - } + return; + } + + if (archiveResult.uri) { + await showReadme(selectedLogs, archiveResult.uri.fsPath); + await showSuccessPopup(archiveResult.uri); + } +} + +async function showReadme(selectedLogs: LogsToCollect, archivePath: string) { + const readmeContent = generateReadmeContent(selectedLogs, archivePath); + const document = await vscode.workspace.openTextDocument({ + content: readmeContent, + language: 'markdown', + }); + await vscode.commands.executeCommand('markdown.showPreview', document.uri); +} + +async function showSuccessPopup(uri: vscode.Uri) { + const openFolder = vscode.l10n.t('Open Folder'); + const result = await vscode.window.showInformationMessage(vscode.l10n.t('C# logs saved successfully.'), openFolder); + if (result === openFolder) { + await vscode.commands.executeCommand('revealFileInOS', uri); } } diff --git a/src/lsptoolshost/logging/loggingUtils.ts b/src/lsptoolshost/logging/loggingUtils.ts index ca588bd7b..5f747667b 100644 --- a/src/lsptoolshost/logging/loggingUtils.ts +++ b/src/lsptoolshost/logging/loggingUtils.ts @@ -575,3 +575,113 @@ export async function createActivityLogCapture( }, }; } + +/** Describes which additional logs were selected for collection. */ +export interface LogsToCollect { + activityLogs: boolean; + performanceTrace: boolean; + memoryDump: boolean; + gcDump: boolean; +} + +/** + * Generate a readme.md file which describes the contents of the log archive, + * warns the user about potentially sensitive information in the logs, and + * provides instructions on how to share the logs with Microsoft for troubleshooting. + * @param options Which additional logs were selected for collection + * @param archivePath The absolute path where the archive was saved on disk + */ +export function generateReadmeContent(options: LogsToCollect, archivePath: string): string { + const lines: string[] = []; + + lines.push('# C# Extension Log Archive'); + lines.push(''); + lines.push( + 'An archive was generated by the **C# extension for Visual Studio Code** (`CSharp: Collect C# Logs` command).' + ); + lines.push(''); + lines.push(`**Archive location**: [${archivePath}](${archivePath})`); + lines.push(''); + + lines.push('## Contents'); + lines.push(''); + + lines.push('### Current Logs and Settings'); + lines.push(''); + lines.push('| File | Description |'); + lines.push('| --- | --- |'); + lines.push('| `csharp.log` | C# extension output log |'); + lines.push('| `csharp-lsp-trace.log` | LSP trace log between VS Code and the Roslyn language server |'); + lines.push('| `razor.log` | Razor language support log |'); + lines.push('| `csharp-settings.json` | Current C# extension settings at time of capture |'); + lines.push(''); + + if (options.activityLogs) { + lines.push('### Record Activity'); + lines.push(''); + lines.push( + 'Activity logs capture live output recorded during the diagnostic session with the log level set to Trace.' + ); + lines.push(''); + lines.push('| File | Description |'); + lines.push('| --- | --- |'); + lines.push('| `csharp.activity.log` | C# output captured during the recording session |'); + lines.push('| `csharp-lsp-trace.activity.log` | LSP trace captured during the recording session |'); + lines.push('| `razor.activity.log` | Razor output captured during the recording session |'); + lines.push(''); + } + + if (options.performanceTrace) { + lines.push('### Performance Trace'); + lines.push(''); + lines.push( + 'A `.nettrace` file captured using `dotnet-trace`. This file contains runtime events from the language server process.' + ); + lines.push(''); + lines.push( + 'You can view this file using [PerfView](https://github.com/microsoft/perfview), [dotnet-trace convert](https://learn.microsoft.com/dotnet/core/diagnostics/dotnet-trace#dotnet-trace-convert), or Visual Studio.' + ); + lines.push(''); + } + + if (options.memoryDump) { + lines.push('### Memory Dump'); + lines.push(''); + lines.push( + 'One or more `.dmp` files captured using `dotnet-dump`. These contain a full process memory dump of the language server.' + ); + lines.push(''); + lines.push( + '> **WARNING**: Memory dumps contain the full process memory and may include sensitive data such as source code, file contents, and credentials loaded in memory.' + ); + lines.push(''); + } + + if (options.gcDump) { + lines.push('### GC Dump'); + lines.push(''); + lines.push( + 'One or more `.gcdump` files captured using `dotnet-gcdump`. These contain managed heap information from the language server.' + ); + lines.push(''); + } + + lines.push('## Sharing'); + lines.push(''); + + lines.push('> **WARNING**: This archive may contain sensitive information such as file paths, project names,'); + lines.push('> source code fragments, and other workspace-specific details. Please review the contents before'); + lines.push('> sharing publicly.'); + lines.push(''); + + lines.push( + '**Publicly**: Attach this archive to your [GitHub issue](https://github.com/dotnet/vscode-csharp/issues).' + ); + lines.push(''); + lines.push( + '**Privately**: If the archive contains sensitive information, upload it via the [Developer Community](https://developercommunity.visualstudio.com/dotnet/report) page and reference your GitHub issue in the description.' + ); + lines.push(''); + + return lines.join('\n'); +} From 7c2aa499e301d71f3cbb8911c26a161ddcf5b9be Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Mon, 2 Mar 2026 15:01:28 -0800 Subject: [PATCH 2/2] Apply suggestion from @JoeRobich --- src/lsptoolshost/logging/loggingUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lsptoolshost/logging/loggingUtils.ts b/src/lsptoolshost/logging/loggingUtils.ts index 5f747667b..590c0a4ac 100644 --- a/src/lsptoolshost/logging/loggingUtils.ts +++ b/src/lsptoolshost/logging/loggingUtils.ts @@ -648,7 +648,7 @@ export function generateReadmeContent(options: LogsToCollect, archivePath: strin lines.push('### Memory Dump'); lines.push(''); lines.push( - 'One or more `.dmp` files captured using `dotnet-dump`. These contain a full process memory dump of the language server.' + 'One or more `.dmp` files captured using `dotnet-dump`. These contain a process memory dump of the language server.' ); lines.push(''); lines.push(