Skip to content

Commit 7eb0c72

Browse files
authored
Merge pull request #2657 from dotnet/copilot/add-dotnet-getacquisitionlog-command
Add dotnet.getAcquisitionLog command
2 parents 5d3ed11 + 2a9be03 commit 7eb0c72

8 files changed

Lines changed: 76 additions & 1 deletion

File tree

Documentation/commands.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,14 @@ This is a **user-facing** command that opens an input box pre-filled with the re
124124
125125
This command surfaces an output channel to the user which provides status messages during extension commands. It does not accept parameters and has a void return type.
126126

127+
### dotnet.getAcquisitionLog
128+
129+
This command returns the full path to the log file that the currently running instance of the extension is writing to, wrapped in an [`IDotnetLogResult`](https://github.com/dotnet/vscode-dotnet-runtime/blob/main/vscode-dotnet-runtime-library/src/IDotnetLogResult.ts) object (containing a `logPath` string). It does not accept parameters. Before returning, it flushes any buffered log entries to disk so the file reflects the latest state.
130+
131+
Note: Each VS Code window gets its own extension host log folder, so the returned path only points to the log for the current window. Other concurrent VS Code instances (for example VS Code Insiders versus VS Code) maintain their own logs and must invoke this command separately. The path is derived from `ExtensionContext.logPath`, which [VS Code constructs from the log URI's `fsPath`](https://github.com/microsoft/vscode/blob/a837f16fbe459f3a067aa94da0ddb9b9ae04ebe0/src/vs/workbench/api/common/extHostExtensionService.ts#L538), so it should reflect the remote/WSL extension host when the extension runs there; this is the behavior as of 4/21/2026, despite that VS Code's own commands to get the log path return the Windows path on WSL.
132+
133+
**Offline behavior:** Works offline.
134+
127135
### dotnet.ensureDotnetDependencies
128136

129137
This command is only applicable to Linux machines. It attempts to ensure that .NET dependencies are present and, if they are not, installs them or prompts the user to do so. It accepts a [IDotnetEnsureDependenciesContext](https://github.com/dotnet/vscode-dotnet-runtime/blob/main/vscode-dotnet-runtime-library/src/IDotnetEnsureDependenciesContext.ts) object and has a void return type. It is no longer supported but remains to support legacy behavior.

sample/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@
6565
"title": "Show .NET runtime acquisition log",
6666
"category": "Sample"
6767
},
68+
{
69+
"command": "sample.dotnet.getAcquisitionLog",
70+
"title": "Get the .NET runtime acquisition log file path",
71+
"category": "Sample"
72+
},
6873
{
6974
"command": "sample.dotnet.acquireGlobalSDK",
7075
"title": "Install .NET SDK Globally via .NET Install Tool (Former Runtime Extension)",

sample/src/extension.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import
1515
IDotnetAcquireResult,
1616
IDotnetFindPathContext,
1717
IDotnetListVersionsResult,
18+
IDotnetLogResult,
1819
} from 'vscode-dotnet-runtime-library';
1920

2021
export function activate(context: vscode.ExtensionContext)
@@ -222,6 +223,19 @@ ${stderr}`);
222223
}
223224
});
224225

226+
const sampleGetAcquisitionLogRegistration = vscode.commands.registerCommand('sample.dotnet.getAcquisitionLog', async () =>
227+
{
228+
try
229+
{
230+
const result = await vscode.commands.executeCommand<IDotnetLogResult>('dotnet.getAcquisitionLog');
231+
vscode.window.showInformationMessage(`.NET acquisition log path: ${result?.logPath ?? 'undefined'}`);
232+
}
233+
catch (error)
234+
{
235+
vscode.window.showErrorMessage((error as Error).toString());
236+
}
237+
});
238+
225239
const sampleGlobalSDKFromRuntimeRegistration = vscode.commands.registerCommand('sample.dotnet.acquireGlobalSDK', async (version) =>
226240
{
227241
if (!version)
@@ -334,6 +348,7 @@ ${JSON.stringify(result) ?? 'undefined'}`);
334348
sampleConcurrentTest,
335349
sampleConcurrentASPNETTest,
336350
sampleShowAcquisitionLogRegistration,
351+
sampleGetAcquisitionLogRegistration,
337352
sampleFindPathRegistration,
338353
sampleAvailableInstallsRegistration
339354
);

sample/yarn.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vscode-dotnet-runtime-extension/src/extension.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import
5757
IDotnetListInfo,
5858
IDotnetListVersionsContext,
5959
IDotnetListVersionsResult,
60+
IDotnetLogResult,
6061
IDotnetSearchContext,
6162
IDotnetSearchResult,
6263
IDotnetUninstallContext,
@@ -122,6 +123,7 @@ namespace commandKeys
122123
export const recommendedVersion = 'recommendedVersion'
123124
export const globalAcquireSDKPublic = 'acquireGlobalSDKPublic';
124125
export const showAcquisitionLog = 'showAcquisitionLog';
126+
export const getAcquisitionLog = 'getAcquisitionLog';
125127
export const ensureDotnetDependencies = 'ensureDotnetDependencies';
126128
export const reportIssue = 'reportIssue';
127129
export const resetData = 'resetData';
@@ -821,6 +823,13 @@ ${JSON.stringify(commandContext)}`));
821823

822824
const showOutputChannelRegistration = vscode.commands.registerCommand(`${commandPrefix}.${commandKeys.showAcquisitionLog}`, () => outputChannelObserver.showOutput());
823825

826+
const getAcquisitionLogRegistration = vscode.commands.registerCommand(`${commandPrefix}.${commandKeys.getAcquisitionLog}`, async (): Promise<IDotnetLogResult> =>
827+
{
828+
// Flush any buffered log entries so the file on disk reflects the latest state.
829+
await loggingObserver.flush();
830+
return { logPath: loggingObserver.getFileLocation() };
831+
});
832+
824833
const ensureDependenciesRegistration = vscode.commands.registerCommand(`${commandPrefix}.${commandKeys.ensureDotnetDependencies}`, async (commandContext: IDotnetEnsureDependenciesContext) =>
825834
{
826835
await callWithErrorHandling(async () =>
@@ -1024,6 +1033,7 @@ Installation will timeout in ${timeoutValue} seconds.`))
10241033
dotnetUninstallAllRegistration,
10251034
dotnetForceUpdateRegistration,
10261035
showOutputChannelRegistration,
1036+
getAcquisitionLogRegistration,
10271037
ensureDependenciesRegistration,
10281038
reportIssueRegistration,
10291039
resetUpdateTimerInternalRegistration,

vscode-dotnet-runtime-extension/src/test/functional/DotnetCoreAcquisitionExtension.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import
3131
IDotnetFindPathContext,
3232
IDotnetListVersionsContext,
3333
IDotnetListVersionsResult,
34+
IDotnetLogResult,
3435
IDotnetSearchContext,
3536
IDotnetSearchResult,
3637
IExistingPaths,
@@ -139,6 +140,28 @@ suite('DotnetCoreAcquisitionExtension End to End', function ()
139140
assert.deepEqual((mockState as any).syncedKeys, [], 'setKeysForSync should be called with empty array to prevent syncing install state');
140141
}).timeout(standardTimeoutTime);
141142

143+
test('dotnet.getAcquisitionLog returns the path to the current log file', async () =>
144+
{
145+
const result = await vscode.commands.executeCommand<IDotnetLogResult>('dotnet.getAcquisitionLog');
146+
assert.exists(result, 'dotnet.getAcquisitionLog returns a value');
147+
assert.exists(result!.logPath, 'dotnet.getAcquisitionLog result contains logPath');
148+
assert.isString(result!.logPath, 'dotnet.getAcquisitionLog returns a string logPath');
149+
assert.isTrue(result!.logPath.length > 0, 'dotnet.getAcquisitionLog returns a non-empty path');
150+
// The log file is named like `DotNetAcquisition-<extensionId>-<timestamp>.txt`
151+
// (see EventStreamRegistration.ts). Validate the filename shape so callers know
152+
// they are being handed the acquisition log rather than some other file.
153+
assert.include(path.basename(result!.logPath), 'DotNetAcquisition', 'Returned path points at a DotNetAcquisition log file');
154+
assert.isTrue(result!.logPath.endsWith('.txt'), 'Returned log file has a .txt extension');
155+
// The directory containing the log should exist after activation even if no log
156+
// lines have been flushed yet; ensureDirectory is invoked on flush.
157+
assert.isTrue(fs.existsSync(path.dirname(result!.logPath)), 'Log directory exists');
158+
// Activation performs JSON scanning which should always produce at least one
159+
// log entry, so the file should exist and be non-empty after awaiting flush.
160+
assert.isTrue(fs.existsSync(result!.logPath), 'Log file exists on disk');
161+
const logContents = fs.readFileSync(result!.logPath, 'utf8');
162+
assert.isTrue(logContents.length > 0, 'Log file is non-empty after activation');
163+
}).timeout(standardTimeoutTime);
164+
142165
async function installRuntime(dotnetVersion: string, installMode: DotnetInstallMode, arch?: string)
143166
{
144167
let context: IDotnetAcquireContext = { version: dotnetVersion, requestingExtensionId, mode: installMode };
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Licensed to the .NET Foundation under one or more agreements.
3+
* The .NET Foundation licenses this file to you under the MIT license.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
/**
7+
* The result of the dotnet.getAcquisitionLog command.
8+
* Contains the full path to the log file for this VS Code window/instance
9+
* of the .NET Install Tool extension.
10+
*/
11+
export interface IDotnetLogResult {
12+
logPath: string;
13+
}

vscode-dotnet-runtime-library/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export * from './IDotnetAcquireContext';
4747
export * from './IDotnetAcquireResult';
4848
export * from './IDotnetEnsureDependenciesContext';
4949
export * from './IDotnetFindPathContext';
50+
export * from './IDotnetLogResult';
5051
export * from './IDotnetListVersionsContext';
5152
export * from './IDotnetSearchContext';
5253
export * from './IDotnetSearchResult';

0 commit comments

Comments
 (0)