Add diagnostic logging option to help with advanced troubleshooting (#3809)
* Add diagnostic logging option to help troubleshoot issues * Use LogOutputChannel implementation * More diagnostic logging tweaks * Fix linting errors * Remove redundant warning label from log output * Respond to PR comments * Additional diagnostics refactor * Refactor to allow logging system info once and only once when debug logging is enabled * Refactor to move diagnostics out of the runtime code * More changes for PR * Use similar pattern for onCommand callback
This commit is contained in:
Родитель
8e9e2495fc
Коммит
de296ad220
|
@ -38,7 +38,7 @@
|
|||
"@types/node": "16.x",
|
||||
"@types/node-fetch": "^2.5.12",
|
||||
"@types/semver": "^7.3.9",
|
||||
"@types/vscode": "1.67.0",
|
||||
"@types/vscode": "1.74.0",
|
||||
"@types/xml2js": "^0.4.8",
|
||||
"@typescript-eslint/eslint-plugin": "^5.19.0",
|
||||
"@typescript-eslint/parser": "^5.19.0",
|
||||
|
@ -58,7 +58,7 @@
|
|||
"webpack-cli": "^4.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "^1.67.0"
|
||||
"vscode": "^1.74.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/abort-controller": {
|
||||
|
@ -917,9 +917,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/vscode": {
|
||||
"version": "1.67.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.67.0.tgz",
|
||||
"integrity": "sha512-GH8BDf8cw9AC9080uneJfulhSa7KHSMI2s/CyKePXoGNos9J486w2V4YKoeNUqIEkW4hKoEAWp6/cXTwyGj47g==",
|
||||
"version": "1.74.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.74.0.tgz",
|
||||
"integrity": "sha512-LyeCIU3jb9d38w0MXFwta9r0Jx23ugujkAxdwLTNCyspdZTKUc43t7ppPbCiPoQ/Ivd/pnDFZrb4hWd45wrsgA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/xml2js": {
|
||||
|
@ -7535,9 +7535,9 @@
|
|||
}
|
||||
},
|
||||
"@types/vscode": {
|
||||
"version": "1.67.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.67.0.tgz",
|
||||
"integrity": "sha512-GH8BDf8cw9AC9080uneJfulhSa7KHSMI2s/CyKePXoGNos9J486w2V4YKoeNUqIEkW4hKoEAWp6/cXTwyGj47g==",
|
||||
"version": "1.74.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.74.0.tgz",
|
||||
"integrity": "sha512-LyeCIU3jb9d38w0MXFwta9r0Jx23ugujkAxdwLTNCyspdZTKUc43t7ppPbCiPoQ/Ivd/pnDFZrb4hWd45wrsgA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/xml2js": {
|
||||
|
|
|
@ -2894,7 +2894,7 @@
|
|||
]
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "^1.67.0"
|
||||
"vscode": "^1.74.0"
|
||||
},
|
||||
"capabilities": {
|
||||
"virtualWorkspaces": false,
|
||||
|
@ -2939,7 +2939,7 @@
|
|||
"@types/node": "16.x",
|
||||
"@types/node-fetch": "^2.5.12",
|
||||
"@types/semver": "^7.3.9",
|
||||
"@types/vscode": "1.67.0",
|
||||
"@types/vscode": "1.74.0",
|
||||
"@types/xml2js": "^0.4.8",
|
||||
"@typescript-eslint/eslint-plugin": "^5.19.0",
|
||||
"@typescript-eslint/parser": "^5.19.0",
|
||||
|
|
|
@ -22,7 +22,7 @@ export class DockerAssignAcrPullRoleStep extends AzureWizardExecuteStep<IAppServ
|
|||
|
||||
public async execute(context: IAppServiceWizardContext, progress: Progress<{ message?: string; increment?: number }>): Promise<void> {
|
||||
const message: string = localize('vscode-docker.commands.registries.azure.deployImage.assigningPullRole', 'Granting permission for App Service to pull image from ACR...');
|
||||
ext.outputChannel.appendLine(message);
|
||||
ext.outputChannel.info(message);
|
||||
progress.report({ message: message });
|
||||
|
||||
const azExtAzureUtils = await getAzExtAzureUtils();
|
||||
|
|
|
@ -29,7 +29,7 @@ export class DockerSiteCreateStep extends AzureWizardExecuteStep<IAppServiceCont
|
|||
|
||||
public async execute(context: IAppServiceContainerWizardContext, progress: Progress<{ message?: string; increment?: number }>): Promise<void> {
|
||||
const creatingNewApp: string = localize('vscode-docker.commands.registries.azure.deployImage.creatingWebApp', 'Creating web app "{0}"...', context.newSiteName);
|
||||
ext.outputChannel.appendLine(creatingNewApp);
|
||||
ext.outputChannel.info(creatingNewApp);
|
||||
progress.report({ message: creatingNewApp });
|
||||
const siteConfig = await this.getNewSiteConfig(context);
|
||||
|
||||
|
|
|
@ -37,10 +37,10 @@ export class DockerWebhookCreateStep extends AzureWizardExecuteStep<IAppServiceW
|
|||
const appUri: string = (await siteClient.getWebAppPublishCredential()).scmUri;
|
||||
if (this._treeItem.parent instanceof AzureRepositoryTreeItem) {
|
||||
const creatingNewWebhook: string = localize('vscode-docker.commands.registries.azure.dockerWebhook.creatingWebhook', 'Creating webhook for web app "{0}"...', context.newSiteName);
|
||||
ext.outputChannel.appendLine(creatingNewWebhook);
|
||||
ext.outputChannel.info(creatingNewWebhook);
|
||||
progress.report({ message: creatingNewWebhook });
|
||||
const webhook = await this.createWebhookForApp(context, this._treeItem, context.site, appUri);
|
||||
ext.outputChannel.appendLine(localize('vscode-docker.commands.registries.azure.dockerWebhook.createdWebhook', 'Created webhook "{0}" with scope "{1}", id: "{2}" and location: "{3}"', webhook.name, webhook.scope, webhook.id, webhook.location));
|
||||
ext.outputChannel.info(localize('vscode-docker.commands.registries.azure.dockerWebhook.createdWebhook', 'Created webhook "{0}" with scope "{1}", id: "{2}" and location: "{3}"', webhook.name, webhook.scope, webhook.id, webhook.location));
|
||||
} else if (this._treeItem.parent instanceof DockerHubRepositoryTreeItem) {
|
||||
// point to dockerhub to create a webhook
|
||||
// http://cloud.docker.com/repository/docker/<registryName>/<repoName>/webHooks
|
||||
|
|
|
@ -67,7 +67,7 @@ export async function deployImageToAzure(context: IActionContext, node?: RemoteT
|
|||
const site: Site = nonNullProp(wizardContext, 'site');
|
||||
const siteUri: string = `https://${site.defaultHostName}`;
|
||||
const createdNewWebApp: string = localize('vscode-docker.commands.registries.azure.deployImage.created', 'Successfully created web app "{0}": {1}', site.name, siteUri);
|
||||
ext.outputChannel.appendLine(createdNewWebApp);
|
||||
ext.outputChannel.info(createdNewWebApp);
|
||||
|
||||
const openSite: string = localize('vscode-docker.commands.registries.azure.deployImage.openSite', 'Open Site');
|
||||
// don't wait
|
||||
|
|
|
@ -65,7 +65,7 @@ export async function scheduleRunRequest(context: IActionContext, requestType: '
|
|||
}
|
||||
|
||||
const uploadedSourceLocation: string = await uploadSourceCode(await node.getClient(context), node.registryName, node.resourceGroup, rootUri, tarFilePath);
|
||||
ext.outputChannel.appendLine(localize('vscode-docker.commands.registries.azure.tasks.uploaded', 'Uploaded source code from {0}', tarFilePath));
|
||||
ext.outputChannel.info(localize('vscode-docker.commands.registries.azure.tasks.uploaded', 'Uploaded source code from {0}', tarFilePath));
|
||||
|
||||
let runRequest: AcrDockerBuildRequest | AcrFileTaskRunRequest;
|
||||
if (requestType === 'DockerBuildRequest') {
|
||||
|
@ -87,11 +87,11 @@ export async function scheduleRunRequest(context: IActionContext, requestType: '
|
|||
}
|
||||
|
||||
// Schedule the run and Clean up.
|
||||
ext.outputChannel.appendLine(localize('vscode-docker.commands.registries.azure.tasks.setUp', 'Set up run request'));
|
||||
ext.outputChannel.info(localize('vscode-docker.commands.registries.azure.tasks.setUp', 'Set up run request'));
|
||||
|
||||
const client = await node.getClient(context);
|
||||
const run = await client.registries.beginScheduleRunAndWait(node.resourceGroup, node.registryName, runRequest);
|
||||
ext.outputChannel.appendLine(localize('vscode-docker.commands.registries.azure.tasks.scheduledRun', 'Scheduled run {0}', run.runId));
|
||||
ext.outputChannel.info(localize('vscode-docker.commands.registries.azure.tasks.scheduledRun', 'Scheduled run {0}', run.runId));
|
||||
|
||||
void streamLogs(context, node, run);
|
||||
|
||||
|
@ -134,21 +134,21 @@ async function quickPickImageName(context: IActionContext, rootFolder: vscode.Wo
|
|||
}
|
||||
|
||||
async function uploadSourceCode(client: ContainerRegistryManagementClient, registryName: string, resourceGroupName: string, rootFolder: vscode.Uri, tarFilePath: string): Promise<string> {
|
||||
ext.outputChannel.appendLine(localize('vscode-docker.commands.registries.azure.tasks.sendingSource', ' Sending source code to temp file'));
|
||||
ext.outputChannel.info(localize('vscode-docker.commands.registries.azure.tasks.sendingSource', ' Sending source code to temp file'));
|
||||
const source: string = rootFolder.fsPath;
|
||||
let items = await fse.readdir(source);
|
||||
items = items.filter(i => !(i in vcsIgnoreList));
|
||||
// tslint:disable-next-line:no-unsafe-any
|
||||
tar.c({ cwd: source }, items).pipe(fse.createWriteStream(tarFilePath));
|
||||
|
||||
ext.outputChannel.appendLine(localize('vscode-docker.commands.registries.azure.tasks.gettingBuildSourceUploadUrl', ' Getting build source upload URL'));
|
||||
ext.outputChannel.info(localize('vscode-docker.commands.registries.azure.tasks.gettingBuildSourceUploadUrl', ' Getting build source upload URL'));
|
||||
const sourceUploadLocation = await client.registries.getBuildSourceUploadUrl(resourceGroupName, registryName);
|
||||
const uploadUrl: string = sourceUploadLocation.uploadUrl;
|
||||
const relativePath: string = sourceUploadLocation.relativePath;
|
||||
|
||||
const storageBlob = await getStorageBlob();
|
||||
const blobClient = new storageBlob.BlockBlobClient(uploadUrl);
|
||||
ext.outputChannel.appendLine(localize('vscode-docker.commands.registries.azure.tasks.creatingBlockBlob', ' Creating block blob'));
|
||||
ext.outputChannel.info(localize('vscode-docker.commands.registries.azure.tasks.creatingBlockBlob', ' Creating block blob'));
|
||||
await blobClient.uploadFile(tarFilePath);
|
||||
|
||||
return relativePath;
|
||||
|
@ -185,7 +185,7 @@ async function streamLogs(context: IActionContext, node: AzureRegistryTreeItem,
|
|||
const content = bufferToString(contentBuffer);
|
||||
|
||||
if (content) {
|
||||
ext.outputChannel.appendLine(content);
|
||||
ext.outputChannel.info(content);
|
||||
}
|
||||
|
||||
if (properties?.metadata?.complete) {
|
||||
|
@ -202,7 +202,7 @@ function getTempSourceArchivePath(): string {
|
|||
/* tslint:disable-next-line:insecure-random */
|
||||
const id: number = Math.floor(Math.random() * Math.pow(10, idPrecision));
|
||||
const archive = `sourceArchive${id}.tar.gz`;
|
||||
ext.outputChannel.appendLine(localize('vscode-docker.commands.registries.azure.tasks.settingUpTempFile', 'Setting up temp file with \'{0}\'', archive));
|
||||
ext.outputChannel.info(localize('vscode-docker.commands.registries.azure.tasks.settingUpTempFile', 'Setting up temp file with \'{0}\'', archive));
|
||||
const tarFilePath: string = path.join(os.tmpdir(), archive);
|
||||
return tarFilePath;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ export async function logInToDockerCli(context: IActionContext, node?: RegistryT
|
|||
}
|
||||
|
||||
if (!username || !password) {
|
||||
ext.outputChannel.appendLine(localize('vscode-docker.commands.registries.logIn.skipping', 'WARNING: Skipping login for "{0}" because it does not require authentication.', creds.registryPath));
|
||||
ext.outputChannel.warn(localize('vscode-docker.commands.registries.logIn.skipping', 'Skipping login for "{0}" because it does not require authentication.', creds.registryPath));
|
||||
} else {
|
||||
const progressOptions: vscode.ProgressOptions = {
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
|
@ -49,7 +49,7 @@ export async function logInToDockerCli(context: IActionContext, node?: RegistryT
|
|||
stdInPipe: stream.Readable.from(password),
|
||||
}
|
||||
);
|
||||
ext.outputChannel.appendLine('Login succeeded.');
|
||||
ext.outputChannel.info('Login succeeded.');
|
||||
} catch (err) {
|
||||
const error = parseError(err);
|
||||
|
||||
|
|
|
@ -163,8 +163,8 @@ export class DockerDebugConfigurationProvider implements DebugConfigurationProvi
|
|||
}
|
||||
|
||||
if (portMappings.length > 0) {
|
||||
ext.outputChannel.appendLine(localize('vscode-docker.debug.configProvider.portMappings', 'The application is listening on the following port(s) (Host => Container):'));
|
||||
ext.outputChannel.appendLine(portMappings.join('\n'));
|
||||
ext.outputChannel.info(localize('vscode-docker.debug.configProvider.portMappings', 'The application is listening on the following port(s) (Host => Container):'));
|
||||
ext.outputChannel.info(portMappings.join('\n'));
|
||||
}
|
||||
} catch {
|
||||
// Best effort
|
||||
|
|
|
@ -65,11 +65,11 @@ async function getLatestAcquisitionScriptIfNecessary(): Promise<boolean> {
|
|||
return false;
|
||||
}
|
||||
|
||||
ext.outputChannel.appendLine(localize('vscode-docker.debugging.netCore.vsDbgHelper.acquiringScript', 'Acquiring latest VsDbg install script...'));
|
||||
ext.outputChannel.info(localize('vscode-docker.debugging.netCore.vsDbgHelper.acquiringScript', 'Acquiring latest VsDbg install script...'));
|
||||
await streamToFile(acquisition.url, acquisition.scriptPath);
|
||||
|
||||
await ext.context.globalState.update(scriptAcquiredDateKey, Date.now());
|
||||
ext.outputChannel.appendLine(localize('vscode-docker.debugging.netCore.vsDbgHelper.scriptAcquired', 'Script acquired.'));
|
||||
ext.outputChannel.info(localize('vscode-docker.debugging.netCore.vsDbgHelper.scriptAcquired', 'Script acquired.'));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -85,13 +85,13 @@ async function executeAcquisitionScriptIfNecessary(runtime: VsDbgRuntime, versio
|
|||
|
||||
const command = acquisition.getShellCommand(runtime, version);
|
||||
|
||||
ext.outputChannel.appendLine(localize('vscode-docker.debugging.netCore.vsDbgHelper.installingDebugger', 'Installing VsDbg, Runtime = {0}, Version = {1}...', runtime, version));
|
||||
ext.outputChannel.appendLine(command);
|
||||
ext.outputChannel.info(localize('vscode-docker.debugging.netCore.vsDbgHelper.installingDebugger', 'Installing VsDbg, Runtime = {0}, Version = {1}...', runtime, version));
|
||||
ext.outputChannel.info(command);
|
||||
|
||||
await execAsync(command, {}, (output: string) => {
|
||||
ext.outputChannel.append(output);
|
||||
ext.outputChannel.info(output);
|
||||
});
|
||||
|
||||
await ext.context.globalState.update(scriptExecutedDateKey, Date.now());
|
||||
ext.outputChannel.appendLine(localize('vscode-docker.debugging.netCore.vsDbgHelper.debuggerInstalled', 'VsDbg installed, Runtime = {0}, Version = {1}...', runtime, version));
|
||||
ext.outputChannel.info(localize('vscode-docker.debugging.netCore.vsDbgHelper.debuggerInstalled', 'VsDbg installed, Runtime = {0}, Version = {1}...', runtime, version));
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TelemetryEvent } from '@microsoft/compose-language-service/lib/client/TelemetryEvent';
|
||||
import { IActionContext, UserCancelledError, callWithTelemetryAndErrorHandling, createAzExtOutputChannel, createExperimentationService, registerErrorHandler, registerEvent, registerReportIssueCommand, registerUIExtensionVariables } from '@microsoft/vscode-azext-utils';
|
||||
import { IActionContext, UserCancelledError, callWithTelemetryAndErrorHandling, createExperimentationService, registerErrorHandler, registerEvent, registerReportIssueCommand, registerUIExtensionVariables } from '@microsoft/vscode-azext-utils';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import { ConfigurationParams, DidChangeConfigurationNotification, DocumentSelector, LanguageClient, LanguageClientOptions, Middleware, ServerOptions, TransportKind } from 'vscode-languageclient/node';
|
||||
|
@ -26,6 +26,8 @@ import { ContainerRuntimeManager } from './runtimes/ContainerRuntimeManager';
|
|||
import { OrchestratorRuntimeManager } from './runtimes/OrchestratorRuntimeManager';
|
||||
import { AutoConfigurableDockerClient } from './runtimes/clients/AutoConfigurableDockerClient';
|
||||
import { AutoConfigurableDockerComposeClient } from './runtimes/clients/AutoConfigurableDockerComposeClient';
|
||||
import { AzExtLogOutputChannelWrapper } from './utils/AzExtLogOutputChannelWrapper';
|
||||
import { logDockerEnvironment, logSystemInfo } from './utils/diagnostics';
|
||||
|
||||
export type KeyInfo = { [keyName: string]: string };
|
||||
|
||||
|
@ -42,10 +44,11 @@ const DOCUMENT_SELECTOR: DocumentSelector = [
|
|||
{ language: 'dockerfile', scheme: 'file' }
|
||||
];
|
||||
|
||||
|
||||
function initializeExtensionVariables(ctx: vscode.ExtensionContext): void {
|
||||
ext.context = ctx;
|
||||
|
||||
ext.outputChannel = createAzExtOutputChannel('Docker', ext.prefix);
|
||||
ext.outputChannel = new AzExtLogOutputChannelWrapper(vscode.window.createOutputChannel('Docker', { log: true }), ext.prefix);
|
||||
ctx.subscriptions.push(ext.outputChannel);
|
||||
|
||||
registerUIExtensionVariables(ext);
|
||||
|
@ -68,6 +71,8 @@ export async function activateInternal(ctx: vscode.ExtensionContext, perfStats:
|
|||
process.env.VSCODE_DOCKER_TEAM === '1' ? tas.TargetPopulation.Team : undefined // If VSCODE_DOCKER_TEAM isn't set, let @microsoft/vscode-azext-utils decide target population
|
||||
);
|
||||
|
||||
logSystemInfo(ext.outputChannel);
|
||||
|
||||
// Disabled for now
|
||||
// (new SurveyManager()).activate();
|
||||
|
||||
|
@ -151,6 +156,7 @@ function registerEnvironmentVariableContributions(): void {
|
|||
actionContext.errorHandling.suppressDisplay = true;
|
||||
|
||||
if (e.affectsConfiguration('docker.environment')) {
|
||||
logDockerEnvironment(ext.outputChannel);
|
||||
setEnvironmentVariableContributions();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { AzExtTreeDataProvider, AzExtTreeItem, IAzExtOutputChannel, IExperimentationServiceAdapter } from '@microsoft/vscode-azext-utils';
|
||||
import { AzExtTreeDataProvider, AzExtTreeItem, IExperimentationServiceAdapter } from '@microsoft/vscode-azext-utils';
|
||||
import { ExtensionContext, TreeView } from 'vscode';
|
||||
import { ContainerRuntimeManager } from './runtimes/ContainerRuntimeManager';
|
||||
import { IActivityMeasurementService } from './telemetry/ActivityMeasurementService';
|
||||
|
@ -15,6 +15,7 @@ import { RegistriesTreeItem } from './tree/registries/RegistriesTreeItem';
|
|||
import { VolumesTreeItem } from './tree/volumes/VolumesTreeItem';
|
||||
import { OrchestratorRuntimeManager } from './runtimes/OrchestratorRuntimeManager';
|
||||
import { runWithDefaults as runWithDefaultsImpl, streamWithDefaults as streamWithDefaultsImpl } from './runtimes/runners/runWithDefaults';
|
||||
import { AzExtLogOutputChannelWrapper } from './utils/AzExtLogOutputChannelWrapper';
|
||||
|
||||
/**
|
||||
* Namespace for common variables used throughout the extension. They must be initialized in the activate() method of extension.ts
|
||||
|
@ -22,7 +23,7 @@ import { runWithDefaults as runWithDefaultsImpl, streamWithDefaults as streamWit
|
|||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace ext {
|
||||
export let context: ExtensionContext;
|
||||
export let outputChannel: IAzExtOutputChannel;
|
||||
export let outputChannel: AzExtLogOutputChannelWrapper;
|
||||
|
||||
export let experimentationService: IExperimentationServiceAdapter;
|
||||
export let activityMeasurementService: IActivityMeasurementService;
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { ext } from '../../extensionVariables';
|
||||
import { logCommandPath } from '../../utils/diagnostics';
|
||||
import { DockerClient } from '../docker';
|
||||
import { AutoConfigurableClient } from './AutoConfigurableClient';
|
||||
|
||||
|
@ -17,5 +19,9 @@ export class AutoConfigurableDockerClient extends DockerClient implements AutoCo
|
|||
const config = vscode.workspace.getConfiguration('docker');
|
||||
const dockerCommand = config.get<string | undefined>('dockerPath') || 'docker';
|
||||
this.commandName = dockerCommand;
|
||||
|
||||
ext.outputChannel.debug(`docker.dockerPath: ${this.commandName}`);
|
||||
|
||||
logCommandPath(ext.outputChannel, this.commandName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ export class AutoConfigurableDockerComposeClient extends DockerComposeClient imp
|
|||
// User has not set a compose command, so we will attempt to autodetect it
|
||||
|
||||
try {
|
||||
ext.outputChannel.appendLine('Attempting to autodetect Docker Compose command...');
|
||||
ext.outputChannel.info('Attempting to autodetect Docker Compose command...');
|
||||
await execAsync('docker compose version');
|
||||
|
||||
// If successful, then assume we can use compose V2
|
||||
|
|
|
@ -24,7 +24,7 @@ import {
|
|||
StreamSpawnOptions,
|
||||
} from '../utils/spawnStreamAsync';
|
||||
|
||||
export type ShellStreamCommandRunnerOptions = Omit<StreamSpawnOptions, 'stdOutPipe'> & {
|
||||
export type ShellStreamCommandRunnerOptions = StreamSpawnOptions & {
|
||||
strict?: boolean;
|
||||
};
|
||||
|
||||
|
@ -51,7 +51,19 @@ export class ShellStreamCommandRunnerFactory<TOptions extends ShellStreamCommand
|
|||
accumulator = new AccumulatorStream();
|
||||
}
|
||||
|
||||
await spawnStreamAsync(command, args, { ...this.options, stdOutPipe: accumulator });
|
||||
// Determine the appropriate combination of streams that need to read from stdout
|
||||
let stdOutPipe: NodeJS.WritableStream | undefined = accumulator;
|
||||
if (accumulator && this.options.stdOutPipe) {
|
||||
const stdOutPassThrough = new stream.PassThrough();
|
||||
stdOutPassThrough.pipe(this.options.stdOutPipe);
|
||||
stdOutPassThrough.pipe(accumulator);
|
||||
|
||||
stdOutPipe = stdOutPassThrough;
|
||||
} else if (this.options.stdOutPipe) {
|
||||
stdOutPipe = this.options.stdOutPipe;
|
||||
}
|
||||
|
||||
await spawnStreamAsync(command, args, { ...this.options, stdOutPipe: stdOutPipe });
|
||||
|
||||
throwIfCancellationRequested(this.options.cancellationToken);
|
||||
|
||||
|
|
|
@ -229,7 +229,7 @@ export async function spawnStreamAsync(
|
|||
}
|
||||
|
||||
if (options.onCommand) {
|
||||
options.onCommand([command, ...args].join(' '));
|
||||
options.onCommand([command, ...normalizedArgs].join(' '));
|
||||
}
|
||||
|
||||
const childProcess = spawn(
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as stream from 'stream';
|
||||
import * as vscode from 'vscode';
|
||||
import { AccumulatorStream, ClientIdentity, GeneratorCommandResponse, IContainersClient, isChildProcessError, Like, normalizeCommandResponseLike, NoShell, PromiseCommandResponse, ShellStreamCommandRunnerFactory, ShellStreamCommandRunnerOptions, VoidCommandResponse } from '../docker';
|
||||
import { ext } from '../../extensionVariables';
|
||||
|
@ -104,12 +105,43 @@ class DefaultEnvStreamCommandRunnerFactory<TOptions extends DefaultEnvStreamComm
|
|||
public constructor(options: TOptions) {
|
||||
const errAccumulator = new AccumulatorStream();
|
||||
|
||||
let stdOutPipe: NodeJS.WritableStream | undefined;
|
||||
if (ext.outputChannel.isDebugLoggingEnabled) {
|
||||
stdOutPipe = new stream.PassThrough();
|
||||
stdOutPipe.on('data', (chunk: Buffer) => {
|
||||
try {
|
||||
ext.outputChannel.debug(chunk.toString());
|
||||
} catch {
|
||||
// Do not throw on diagnostic errors
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const stdErrPipe = new stream.PassThrough();
|
||||
stdErrPipe.on('data', (chunk: Buffer) => {
|
||||
try {
|
||||
ext.outputChannel.error(chunk.toString());
|
||||
} catch {
|
||||
// Do not throw on diagnostic errors
|
||||
}
|
||||
});
|
||||
stdErrPipe.pipe(errAccumulator);
|
||||
|
||||
const onCommand = (command) => {
|
||||
ext.outputChannel.debug(command);
|
||||
if (typeof options?.onCommand === 'function') {
|
||||
options.onCommand(command);
|
||||
}
|
||||
};
|
||||
|
||||
super({
|
||||
...options,
|
||||
env: withDockerEnvSettings(process.env),
|
||||
shell: false,
|
||||
shellProvider: new NoShell(),
|
||||
stdErrPipe: errAccumulator,
|
||||
onCommand,
|
||||
stdOutPipe,
|
||||
stdErrPipe,
|
||||
windowsVerbatimArguments: true,
|
||||
strict: true,
|
||||
});
|
||||
|
|
|
@ -157,7 +157,7 @@ export class RefreshManager extends vscode.Disposable {
|
|||
continue;
|
||||
} else {
|
||||
// Emit a message and rethrow to get telemetry
|
||||
ext.outputChannel.appendLine(
|
||||
ext.outputChannel.error(
|
||||
localize('vscode-docker.tree.refreshManager.eventSetupFailure', 'Failed to set up event listener: {0}', error.message)
|
||||
);
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ export class AzureRegistryCreateStep extends AzureWizardExecuteStep<IAzureRegist
|
|||
const armContainerRegistry = await getArmContainerRegistry();
|
||||
const client = azExtAzureUtils.createAzureClient(context, armContainerRegistry.ContainerRegistryManagementClient);
|
||||
const creating: string = localize('vscode-docker.tree.registries.azure.createWizard.creating', 'Creating registry "{0}"...', newRegistryName);
|
||||
ext.outputChannel.appendLine(creating);
|
||||
ext.outputChannel.info(creating);
|
||||
progress.report({ message: creating });
|
||||
|
||||
const location: AzExtLocation = await azExtAzureUtils.LocationListStep.getLocation(context);
|
||||
|
@ -49,7 +49,7 @@ export class AzureRegistryCreateStep extends AzureWizardExecuteStep<IAzureRegist
|
|||
}
|
||||
|
||||
const created = localize('vscode-docker.tree.registries.azure.createWizard.created', 'Successfully created registry "{0}".', newRegistryName);
|
||||
ext.outputChannel.appendLine(created);
|
||||
ext.outputChannel.info(created);
|
||||
}
|
||||
|
||||
public shouldExecute(context: IAzureRegistryWizardContext): boolean {
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IAzExtOutputChannel } from '@microsoft/vscode-azext-utils';
|
||||
import * as vscode from 'vscode';
|
||||
import { isLogLevelEnabled } from './diagnostics';
|
||||
|
||||
export class AzExtLogOutputChannelWrapper implements vscode.LogOutputChannel, IAzExtOutputChannel {
|
||||
public readonly name: string;
|
||||
public readonly extensionPrefix: string;
|
||||
public readonly onDidChangeLogLevel: vscode.Event<vscode.LogLevel>;
|
||||
private _logOutputChannel: vscode.LogOutputChannel;
|
||||
|
||||
constructor(logOutputChannel: vscode.LogOutputChannel, extensionPrefix: string) {
|
||||
this._logOutputChannel = logOutputChannel;
|
||||
this.name = this._logOutputChannel.name;
|
||||
this.extensionPrefix = extensionPrefix;
|
||||
this.onDidChangeLogLevel = this._logOutputChannel.onDidChangeLogLevel;
|
||||
}
|
||||
|
||||
public get logLevel() {
|
||||
return this._logOutputChannel.logLevel;
|
||||
}
|
||||
|
||||
public get isDebugLoggingEnabled(): boolean {
|
||||
return isLogLevelEnabled(this, vscode.LogLevel.Debug);
|
||||
}
|
||||
|
||||
appendLog(value: string, options?: { resourceName?: string; date?: Date; }): void {
|
||||
const enableOutputTimestampsSetting: string = 'enableOutputTimestamps';
|
||||
const projectConfiguration: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration(this.extensionPrefix);
|
||||
const result: boolean | undefined = projectConfiguration.get<boolean>(enableOutputTimestampsSetting);
|
||||
|
||||
if (!result) {
|
||||
this.info(value);
|
||||
} else {
|
||||
options ||= {};
|
||||
this.info(`${options.resourceName ? ' '.concat(options.resourceName) : ''}: ${value}`);
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
trace(message: string, ...args: any[]): void {
|
||||
this._logOutputChannel.trace(message, ...args);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
debug(message: string, ...args: any[]): void {
|
||||
this._logOutputChannel.debug(message, ...args);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
info(message: string, ...args: any[]): void {
|
||||
this._logOutputChannel.info(message, ...args);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
warn(message: string, ...args: any[]): void {
|
||||
this._logOutputChannel.warn(message, ...args);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
error(error: string | Error, ...args: any[]): void {
|
||||
this._logOutputChannel.error(error, ...args);
|
||||
}
|
||||
|
||||
append(value: string): void {
|
||||
this._logOutputChannel.append(value);
|
||||
}
|
||||
|
||||
appendLine(value: string): void {
|
||||
this._logOutputChannel.appendLine(value);
|
||||
}
|
||||
|
||||
replace(value: string): void {
|
||||
this._logOutputChannel.replace(value);
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this._logOutputChannel.clear();
|
||||
}
|
||||
|
||||
show(preserveFocus?: boolean): void;
|
||||
show(column?: vscode.ViewColumn, preserveFocus?: boolean): void;
|
||||
show(column?: boolean | vscode.ViewColumn, preserveFocus?: boolean): void {
|
||||
this._logOutputChannel.show(column as vscode.ViewColumn, preserveFocus);
|
||||
}
|
||||
|
||||
hide(): void {
|
||||
this._logOutputChannel.hide();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._logOutputChannel.dispose();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as stream from 'stream';
|
||||
import * as vscode from 'vscode';
|
||||
import { ext } from '../extensionVariables';
|
||||
import { CancellationError, CommandLineArgs, spawnStreamAsync, StreamSpawnOptions } from '../runtimes/docker';
|
||||
import { execAsync } from './execAsync';
|
||||
import { isLinux } from './osUtils';
|
||||
|
||||
export function isLogLevelEnabled(outputChannel: vscode.LogOutputChannel, logLevel: vscode.LogLevel): boolean {
|
||||
return outputChannel && outputChannel.logLevel > 0 && outputChannel.logLevel <= logLevel;
|
||||
}
|
||||
|
||||
export function logProcessEnvironment(outputChannel: vscode.LogOutputChannel): void {
|
||||
if (isLogLevelEnabled(outputChannel, vscode.LogLevel.Debug)) {
|
||||
try {
|
||||
outputChannel.debug(`--- Process Environment (${Object.getOwnPropertyNames(process.env).length}) ---`);
|
||||
|
||||
for (const key of Object.keys(process.env)) {
|
||||
outputChannel.debug(`${key}: ${process.env[key]}`);
|
||||
}
|
||||
} catch {
|
||||
// Do not throw for diagnostic logging
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function logDockerEnvironment(outputChannel: vscode.LogOutputChannel): void {
|
||||
if (isLogLevelEnabled(outputChannel, vscode.LogLevel.Debug)) {
|
||||
try {
|
||||
const settingValue: NodeJS.ProcessEnv = vscode.workspace.getConfiguration('docker').get<NodeJS.ProcessEnv>('environment', {});
|
||||
|
||||
outputChannel.debug(`--- Docker Environment (${Object.getOwnPropertyNames(settingValue).length}) ---`);
|
||||
for (const key of Object.keys(settingValue)) {
|
||||
outputChannel.debug(`${key}: ${settingValue[key]}`);
|
||||
}
|
||||
} catch {
|
||||
// Do not throw for diagnostic logging
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let loggedSystemInfo: boolean = false;
|
||||
let systemInfoDisposable: vscode.Disposable;
|
||||
export function logSystemInfo(outputChannel: vscode.LogOutputChannel): void {
|
||||
if (loggedSystemInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isLogLevelEnabled(outputChannel, vscode.LogLevel.Debug)) {
|
||||
loggedSystemInfo = true;
|
||||
|
||||
if (systemInfoDisposable) {
|
||||
systemInfoDisposable.dispose();
|
||||
systemInfoDisposable = null;
|
||||
}
|
||||
|
||||
logProcessEnvironment(outputChannel);
|
||||
|
||||
logDockerEnvironment(outputChannel);
|
||||
|
||||
// Linux specific system diagnostic logging
|
||||
if (isLinux()) {
|
||||
outputChannel.debug('--- System Info ---');
|
||||
try {
|
||||
execAsync('uname -a').catch(() => {/* Do not throw */ });
|
||||
} catch {
|
||||
// Do not throw for diagnostic logging
|
||||
}
|
||||
|
||||
try {
|
||||
execAsync('cat /etc/os-release').catch(() => {/* Do not throw */ });
|
||||
} catch {
|
||||
// Do not throw for diagnostic logging
|
||||
}
|
||||
}
|
||||
} else {
|
||||
systemInfoDisposable = outputChannel.onDidChangeLogLevel((logLevel) => {
|
||||
if (logLevel > vscode.LogLevel.Off && logLevel <= vscode.LogLevel.Debug) {
|
||||
logSystemInfo(outputChannel);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function logCommandPath(outputChannel: vscode.LogOutputChannel, command: string): void {
|
||||
if (isLogLevelEnabled(outputChannel, vscode.LogLevel.Debug)) {
|
||||
if (isLinux()) {
|
||||
try {
|
||||
execAsync(`which ${command}`).catch(() => {/* Do not throw errors */ });
|
||||
} catch {
|
||||
// Do not throw for diagnostic logging
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function spawnStreamWithDiagnosticsAsync(
|
||||
command: string,
|
||||
args: CommandLineArgs,
|
||||
options: StreamSpawnOptions,
|
||||
): Promise<void> {
|
||||
let stdOutPipe: NodeJS.WritableStream | undefined = options.stdOutPipe;
|
||||
if (ext.outputChannel.isDebugLoggingEnabled) {
|
||||
const debugStdOutPipe = new stream.PassThrough();
|
||||
debugStdOutPipe.on('data', (chunk: Buffer) => {
|
||||
try {
|
||||
ext.outputChannel.debug(chunk.toString());
|
||||
} catch {
|
||||
// Do not throw on diagnostic errors
|
||||
}
|
||||
});
|
||||
|
||||
if (stdOutPipe) {
|
||||
debugStdOutPipe.pipe(stdOutPipe);
|
||||
}
|
||||
stdOutPipe = debugStdOutPipe;
|
||||
}
|
||||
|
||||
const stdErrPipe = new stream.PassThrough();
|
||||
stdErrPipe.on('data', (chunk: Buffer) => {
|
||||
try {
|
||||
ext.outputChannel.error(chunk.toString());
|
||||
} catch {
|
||||
// Do not throw on diagnostic errors
|
||||
}
|
||||
});
|
||||
|
||||
if (options.stdErrPipe) {
|
||||
stdErrPipe.pipe(options.stdErrPipe);
|
||||
}
|
||||
|
||||
const onCommand = (command) => {
|
||||
ext.outputChannel.debug(command);
|
||||
if (typeof options?.onCommand === 'function') {
|
||||
options.onCommand(command);
|
||||
}
|
||||
};
|
||||
|
||||
options = {
|
||||
...options,
|
||||
onCommand,
|
||||
stdOutPipe,
|
||||
stdErrPipe,
|
||||
};
|
||||
|
||||
try {
|
||||
return spawnStreamAsync(command, args, options);
|
||||
} catch (err) {
|
||||
if (err instanceof CancellationError || err instanceof vscode.CancellationError) {
|
||||
ext.outputChannel.debug(err.message);
|
||||
} else if (err instanceof Error) {
|
||||
ext.outputChannel.error(err.message);
|
||||
} else {
|
||||
ext.outputChannel.error(`Unknown error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
|
||||
// Rethrow any errors
|
||||
throw err;
|
||||
}
|
||||
}
|
|
@ -5,8 +5,9 @@
|
|||
|
||||
import * as cp from 'child_process';
|
||||
import * as stream from 'stream';
|
||||
import { AccumulatorStream, Shell, spawnStreamAsync, StreamSpawnOptions } from '../runtimes/docker';
|
||||
import { AccumulatorStream, Shell, StreamSpawnOptions } from '../runtimes/docker';
|
||||
import { CancellationToken } from 'vscode';
|
||||
import { spawnStreamWithDiagnosticsAsync } from './diagnostics';
|
||||
|
||||
type Progress = (content: string, err: boolean) => void;
|
||||
|
||||
|
@ -57,7 +58,7 @@ export async function execAsync(command: string, options?: cp.ExecOptions & { st
|
|||
stdErrPipe: stderrIntermediate ?? stderrFinal,
|
||||
};
|
||||
|
||||
await spawnStreamAsync(command, [], spawnOptions);
|
||||
await spawnStreamWithDiagnosticsAsync(command, [], spawnOptions);
|
||||
|
||||
return {
|
||||
stdout: await stdoutFinal.getString(),
|
||||
|
|
Загрузка…
Ссылка в новой задаче