support passing extension log level from cli (#163566)

This commit is contained in:
Sandeep Somavarapu
2022-10-13 18:21:47 +02:00
committed by GitHub
parent af4b19dd24
commit 2b50ab06b1
18 changed files with 125 additions and 67 deletions

View File

@@ -48,7 +48,7 @@ export interface NativeParsedArgs {
'trace-category-filter'?: string;
'trace-options'?: string;
'open-devtools'?: boolean;
log?: string;
log?: string[];
logExtensionHostCommunication?: boolean;
'extensions-dir'?: string;
'extensions-download-dir'?: string;

View File

@@ -81,6 +81,7 @@ export interface IEnvironmentService {
// --- logging
logsPath: string;
logLevel?: string;
extensionLogLevel?: [string, string][];
verbose: boolean;
isBuilt: boolean;

View File

@@ -14,6 +14,8 @@ import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
import { ExtensionKind, IDebugParams, IExtensionHostDebugParams, INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { IProductService } from 'vs/platform/product/common/productService';
export const EXTENSION_IDENTIFIER_WITH_LOG_REGEX = /^([^.]+\..+):(.+)$/;
export interface INativeEnvironmentPaths {
/**
@@ -216,7 +218,20 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron
get isBuilt(): boolean { return !env['VSCODE_DEV']; }
get verbose(): boolean { return !!this.args.verbose; }
get logLevel(): string | undefined { return this.args.log; }
@memoize
get logLevel(): string | undefined { return this.args.log?.find(entry => !EXTENSION_IDENTIFIER_WITH_LOG_REGEX.test(entry)); }
@memoize
get extensionLogLevel(): [string, string][] | undefined {
const result: [string, string][] = [];
for (const entry of this.args.log || []) {
const matches = EXTENSION_IDENTIFIER_WITH_LOG_REGEX.exec(entry);
if (matches && matches[1] && matches[2]) {
result.push([matches[1], matches[2]]);
}
}
return result.length ? result : undefined;
}
@memoize
get serviceMachineIdResource(): URI { return joinPath(URI.file(this.userDataPath), 'machineid'); }

View File

@@ -93,7 +93,7 @@ export const OPTIONS: OptionDescriptions<Required<NativeParsedArgs>> = {
'version': { type: 'boolean', cat: 't', alias: 'v', description: localize('version', "Print version.") },
'verbose': { type: 'boolean', cat: 't', global: true, description: localize('verbose', "Print verbose output (implies --wait).") },
'log': { type: 'string', cat: 't', args: 'level', global: true, description: localize('log', "Log level to use. Default is 'info'. Allowed values are 'critical', 'error', 'warn', 'info', 'debug', 'trace', 'off'.") },
'log': { type: 'string[]', cat: 't', args: 'level', global: true, description: localize('log', "Log level to use. Default is 'info'. Allowed values are 'critical', 'error', 'warn', 'info', 'debug', 'trace', 'off'. You can also configure the log level of an extension by passing extension id and log level in the following format: '${publisher}.${name}:${logLevel}'. For example: 'vscode.csharp:trace'. Can receive one or more such entries.") },
'status': { type: 'boolean', alias: 's', cat: 't', description: localize('status', "Print process usage and diagnostics information.") },
'prof-startup': { type: 'boolean', cat: 't', description: localize('prof-startup', "Run CPU profiler during startup.") },
'prof-append-timers': { type: 'string' },

View File

@@ -147,7 +147,7 @@ export class FileLoggerService extends AbstractLoggerService implements ILoggerS
@ILogService logService: ILogService,
@IFileService private readonly fileService: IFileService,
) {
super(logService.getLevel(), logService.onDidChangeLogLevel, []);
super(logService.getLevel(), logService.onDidChangeLogLevel);
}
protected doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger {

View File

@@ -112,7 +112,7 @@ export interface ILoggerService {
/**
* Creates a logger, or gets one if it already exists.
*/
createLogger(resource: URI, options?: ILoggerOptions): ILogger;
createLogger(resource: URI, options?: ILoggerOptions, logLevel?: LogLevel): ILogger;
/**
* Gets an existing logger, if any.
@@ -128,12 +128,6 @@ export interface ILoggerService {
* Get log level for a logger.
*/
getLogLevel(resource: URI): LogLevel | undefined;
/**
* Get default log level for a logger with given name.
* @param name logger name
*/
getDefaultLogLevel(name: string): LogLevel;
}
export abstract class AbstractLogger extends Disposable {
@@ -535,7 +529,6 @@ export abstract class AbstractLoggerService extends Disposable implements ILogge
constructor(
private logLevel: LogLevel,
onDidChangeLogLevel: Event<LogLevel>,
private readonly defaultLogLevels: [string, LogLevel][]
) {
super();
this._register(onDidChangeLogLevel(logLevel => this.setLevel(logLevel)));
@@ -549,11 +542,11 @@ export abstract class AbstractLoggerService extends Disposable implements ILogge
return this.loggerItems.get(resource)?.logger;
}
createLogger(resource: URI, options?: ILoggerOptions): ILogger {
createLogger(resource: URI, options?: ILoggerOptions, logLevel?: LogLevel): ILogger {
let logger = this.loggerItems.get(resource)?.logger;
if (!logger) {
const logLevel = options?.always ? LogLevel.Trace : undefined;
logger = this.doCreateLogger(resource, logLevel ?? (options?.name ? this.getDefaultLogLevel(options?.name) : this.logLevel), options);
logLevel = options?.always ? LogLevel.Trace : logLevel;
logger = this.doCreateLogger(resource, logLevel ?? this.logLevel, options);
this.loggerItems.set(resource, { logger, logLevel });
}
return logger;
@@ -587,10 +580,6 @@ export abstract class AbstractLoggerService extends Disposable implements ILogge
return logger?.logLevel;
}
getDefaultLogLevel(name: string): LogLevel {
return this.defaultLogLevels.find(([loggerName]) => loggerName === name)?.[1] ?? this.logLevel;
}
override dispose(): void {
this.loggerItems.forEach(({ logger }) => logger.dispose());
this.loggerItems.clear();

View File

@@ -111,7 +111,7 @@ export class LoggerChannel implements IServerChannel {
export class LoggerChannelClient extends AbstractLoggerService implements ILoggerService {
constructor(logLevel: LogLevel, onDidChangeLogLevel: Event<LogLevel>, private readonly channel: IChannel) {
super(logLevel, onDidChangeLogLevel, []);
super(logLevel, onDidChangeLogLevel);
}
createConsoleMainLogger(): ILogger {

View File

@@ -13,7 +13,7 @@ export class LoggerService extends AbstractLoggerService implements ILoggerServi
constructor(
@ILogService logService: ILogService
) {
super(logService.getLevel(), logService.onDidChangeLogLevel, []);
super(logService.getLevel(), logService.onDidChangeLogLevel);
}
protected doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger {

View File

@@ -147,7 +147,7 @@ export interface ServerParsedArgs {
'disable-telemetry'?: boolean;
'file-watcher-polling'?: string;
'log'?: string;
'log'?: string[];
'logsPath'?: string;
'force-disable-user-env'?: boolean;

View File

@@ -20,7 +20,7 @@ export class ExtHostLoggerService extends AbstractLoggerService implements ExtHo
@IExtHostRpcService rpc: IExtHostRpcService,
@IExtHostInitDataService initData: IExtHostInitDataService,
) {
super(initData.logLevel, Event.None, []);
super(initData.logLevel, Event.None);
this._proxy = rpc.getProxy(MainContext.MainThreadLogger);
}

View File

@@ -8,8 +8,8 @@ import type * as vscode from 'vscode';
import { URI } from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { AbstractMessageLogger, ILogger, ILoggerService, ILogService, log, LogLevel } from 'vs/platform/log/common/log';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { AbstractMessageLogger, ILogger, ILoggerService, ILogService, log, LogLevel, parseLogLevel } from 'vs/platform/log/common/log';
import { OutputChannelUpdateMode } from 'vs/workbench/services/output/common/output';
import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
@@ -151,12 +151,13 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape {
if (isString(languageId) && !languageId.trim()) {
throw new Error('illegal argument `languageId`. must not be empty');
}
const extHostOutputChannel = log ? this.doCreateLogOutputChannel(name, extension) : this.doCreateOutputChannel(name, languageId, extension);
const logLevel = this.getDefaultLogLevel(extension);
const extHostOutputChannel = log ? this.doCreateLogOutputChannel(name, logLevel, extension) : this.doCreateOutputChannel(name, languageId, extension);
extHostOutputChannel.then(channel => {
this.channels.set(channel.id, channel);
channel.visible = channel.id === this.visibleChannelId;
});
return log ? this.createExtHostLogOutputChannel(name, <Promise<ExtHostOutputChannel>>extHostOutputChannel) : this.createExtHostOutputChannel(name, <Promise<ExtHostOutputChannel>>extHostOutputChannel);
return log ? this.createExtHostLogOutputChannel(name, logLevel, <Promise<ExtHostOutputChannel>>extHostOutputChannel) : this.createExtHostOutputChannel(name, <Promise<ExtHostOutputChannel>>extHostOutputChannel);
}
private async doCreateOutputChannel(name: string, languageId: string | undefined, extension: IExtensionDescription): Promise<ExtHostOutputChannel> {
@@ -170,14 +171,23 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape {
return new ExtHostOutputChannel(id, name, logger, this.proxy, extension);
}
private async doCreateLogOutputChannel(name: string, extension: IExtensionDescription): Promise<ExtHostLogOutputChannel> {
private async doCreateLogOutputChannel(name: string, logLevel: LogLevel, extension: IExtensionDescription): Promise<ExtHostLogOutputChannel> {
const extensionLogDir = await this.createExtensionLogDirectory(extension);
const file = this.extHostFileSystemInfo.extUri.joinPath(extensionLogDir, `${name.replace(/[\\/:\*\?"<>\|]/g, '')}.log`);
const logger = this.loggerService.createLogger(file, { name });
const logger = this.loggerService.createLogger(file, { name }, logLevel);
const id = await this.proxy.$register(name, file, true, undefined, extension.identifier.value);
return new ExtHostLogOutputChannel(id, name, logger, this.proxy, extension);
}
private getDefaultLogLevel(extension: IExtensionDescription): LogLevel {
let logLevel: LogLevel | undefined;
const logLevelValue = this.initData.environment.extensionLogLevel?.find(([identifier]) => ExtensionIdentifier.equals(extension.identifier, identifier))?.[1];
if (logLevelValue) {
logLevel = parseLogLevel(logLevelValue);
}
return logLevel ?? this.logService.getLevel();
}
private createExtensionLogDirectory(extension: IExtensionDescription): Thenable<URI> {
let extensionLogDirectoryPromise = this.extensionLogDirectoryPromise.get(extension.identifier.value);
if (!extensionLogDirectoryPromise) {
@@ -236,14 +246,13 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape {
};
}
private createExtHostLogOutputChannel(name: string, channelPromise: Promise<ExtHostOutputChannel>): vscode.LogOutputChannel {
private createExtHostLogOutputChannel(name: string, logLevel: LogLevel, channelPromise: Promise<ExtHostOutputChannel>): vscode.LogOutputChannel {
const disposables = new DisposableStore();
const validate = () => {
if (disposables.isDisposed) {
throw new Error('Channel has been closed');
}
};
let logLevel = this.logService.getLevel();
const onDidChangeLogLevel = disposables.add(new Emitter<LogLevel>());
channelPromise.then(channel => {
disposables.add(channel);

View File

@@ -697,6 +697,11 @@ export interface IDevelopmentOptions {
*/
readonly logLevel?: LogLevel;
/**
* Extension log level.
*/
readonly extensionLogLevel?: [string, LogLevel][];
/**
* Location of a module containing extension tests to run once the workbench is open.
*/

View File

@@ -5,17 +5,19 @@
import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { ILogService, LogLevel, DEFAULT_LOG_LEVEL } from 'vs/platform/log/common/log';
import { ILogService, LogLevel, getLogLevel, parseLogLevel } from 'vs/platform/log/common/log';
import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
import { URI } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { dirname, basename, isEqual } from 'vs/base/common/resources';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IOutputService } from 'vs/workbench/services/output/common/output';
import { IOutputChannelDescriptor, IOutputService } from 'vs/workbench/services/output/common/output';
import { isUndefined } from 'vs/base/common/types';
import { ILogLevelService } from 'vs/workbench/contrib/logs/common/logLevelService';
import { extensionTelemetryLogChannelId, telemetryLogChannelId } from 'vs/workbench/contrib/logs/common/logConstants';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
export class SetLogLevelAction extends Action {
@@ -26,19 +28,20 @@ export class SetLogLevelAction extends Action {
@IQuickInputService private readonly quickInputService: IQuickInputService,
@ILogService private readonly logService: ILogService,
@ILogLevelService private readonly logLevelService: ILogLevelService,
@IOutputService private readonly outputService: IOutputService
@IOutputService private readonly outputService: IOutputService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
) {
super(id, label);
}
override async run(): Promise<void> {
const logger = await this.selectLogger();
if (!isUndefined(logger)) {
await this.selectLogLevel(logger);
const logChannel = await this.selectLogChannel();
if (!isUndefined(logChannel)) {
await this.selectLogLevel(logChannel);
}
}
private async selectLogger(): Promise<string | undefined | null> {
private async selectLogChannel(): Promise<IOutputChannelDescriptor | undefined | null> {
const extensionLogs = [], logs = [];
for (const channel of this.outputService.getChannelDescriptors()) {
if (!channel.log || channel.id === telemetryLogChannelId || channel.id === extensionTelemetryLogChannelId) {
@@ -50,55 +53,65 @@ export class SetLogLevelAction extends Action {
logs.push(channel);
}
}
const entries: ({ id?: string; label: string } | IQuickPickSeparator)[] = [];
const entries: ({ label: string; channel?: IOutputChannelDescriptor } | IQuickPickSeparator)[] = [];
entries.push({ label: nls.localize('all', "All") });
entries.push({ type: 'separator', label: nls.localize('loggers', "Logs") });
for (const { id, label } of logs.sort((a, b) => a.label.localeCompare(b.label))) {
entries.push({ id, label });
for (const channel of logs.sort((a, b) => a.label.localeCompare(b.label))) {
entries.push({ label: channel.label, channel });
}
if (extensionLogs.length && logs.length) {
entries.push({ type: 'separator', label: nls.localize('extensionLogs', "Extension Logs") });
}
for (const { id, label } of extensionLogs.sort((a, b) => a.label.localeCompare(b.label))) {
entries.push({ id, label });
for (const channel of extensionLogs.sort((a, b) => a.label.localeCompare(b.label))) {
entries.push({ label: channel.label, channel });
}
const entry = await this.quickInputService.pick(entries, { placeHolder: nls.localize('selectlog', "Select Log") });
return entry ? entry.id ?? null : undefined;
return entry ? entry.channel ?? null : undefined;
}
private async selectLogLevel(logger: string | null): Promise<void> {
const current = (logger ? this.logLevelService.getLogLevel(logger) : undefined) ?? this.logService.getLevel();
private async selectLogLevel(logChannel: IOutputChannelDescriptor | null): Promise<void> {
const defaultLogLevel = this.getDefaultLogLevel(logChannel);
const current = logChannel ? this.logLevelService.getLogLevel(logChannel.id) ?? defaultLogLevel : this.logService.getLevel();
const entries = [
{ label: nls.localize('trace', "Trace"), level: LogLevel.Trace, description: this.getDescription(LogLevel.Trace, current) },
{ label: nls.localize('debug', "Debug"), level: LogLevel.Debug, description: this.getDescription(LogLevel.Debug, current) },
{ label: nls.localize('info', "Info"), level: LogLevel.Info, description: this.getDescription(LogLevel.Info, current) },
{ label: nls.localize('warn', "Warning"), level: LogLevel.Warning, description: this.getDescription(LogLevel.Warning, current) },
{ label: nls.localize('err', "Error"), level: LogLevel.Error, description: this.getDescription(LogLevel.Error, current) },
{ label: nls.localize('critical', "Critical"), level: LogLevel.Critical, description: this.getDescription(LogLevel.Critical, current) },
{ label: nls.localize('off', "Off"), level: LogLevel.Off, description: this.getDescription(LogLevel.Off, current) },
{ label: this.getLabel(nls.localize('trace', "Trace"), LogLevel.Trace, current), level: LogLevel.Trace, description: this.getDescription(LogLevel.Trace, defaultLogLevel) },
{ label: this.getLabel(nls.localize('debug', "Debug"), LogLevel.Debug, current), level: LogLevel.Debug, description: this.getDescription(LogLevel.Debug, defaultLogLevel) },
{ label: this.getLabel(nls.localize('info', "Info"), LogLevel.Info, current), level: LogLevel.Info, description: this.getDescription(LogLevel.Info, defaultLogLevel) },
{ label: this.getLabel(nls.localize('warn', "Warning"), LogLevel.Warning, current), level: LogLevel.Warning, description: this.getDescription(LogLevel.Warning, defaultLogLevel) },
{ label: this.getLabel(nls.localize('err', "Error"), LogLevel.Error, current), level: LogLevel.Error, description: this.getDescription(LogLevel.Error, defaultLogLevel) },
{ label: this.getLabel(nls.localize('critical', "Critical"), LogLevel.Critical, current), level: LogLevel.Critical, description: this.getDescription(LogLevel.Critical, defaultLogLevel) },
{ label: this.getLabel(nls.localize('off', "Off"), LogLevel.Off, current), level: LogLevel.Off, description: this.getDescription(LogLevel.Off, defaultLogLevel) },
];
const entry = await this.quickInputService.pick(entries, { placeHolder: logger ? nls.localize('selectLogLevelFor', " {0}: Select log level", this.outputService.getChannelDescriptor(logger)?.label) : nls.localize('selectLogLevel', "Select log level"), activeItem: entries[this.logService.getLevel()] });
const entry = await this.quickInputService.pick(entries, { placeHolder: logChannel ? nls.localize('selectLogLevelFor', " {0}: Select log level", logChannel?.label) : nls.localize('selectLogLevel', "Select log level"), activeItem: entries[this.logService.getLevel()] });
if (entry) {
if (logger) {
this.logLevelService.setLogLevel(logger, entry.level);
if (logChannel) {
this.logLevelService.setLogLevel(logChannel.id, entry.level);
} else {
this.logService.setLevel(entry.level);
}
}
}
private getDescription(level: LogLevel, current: LogLevel): string | undefined {
if (DEFAULT_LOG_LEVEL === level && current === level) {
return nls.localize('default and current', "Default & Current");
private getLabel(label: string, level: LogLevel, current: LogLevel): string {
if (level === current) {
return `$(check) ${label}`;
}
if (DEFAULT_LOG_LEVEL === level) {
return nls.localize('default', "Default");
return label;
}
private getDescription(level: LogLevel, defaultLogLevel: LogLevel): string | undefined {
return defaultLogLevel === level ? nls.localize('default', "Default") : undefined;
}
private getDefaultLogLevel(outputChannel: IOutputChannelDescriptor | null): LogLevel {
let logLevel: LogLevel | undefined;
if (outputChannel?.extensionId) {
const logLevelValue = this.environmentService.extensionLogLevel?.find(([id]) => areSameExtensions({ id }, { id: outputChannel.extensionId! }))?.[1];
if (logLevelValue) {
logLevel = parseLogLevel(logLevelValue);
}
}
if (current === level) {
return nls.localize('current', "Current");
}
return undefined;
return logLevel ?? getLogLevel(this.environmentService);
}
}

View File

@@ -18,6 +18,7 @@ import { LogLevelToString } from 'vs/platform/log/common/log';
import { isUndefined } from 'vs/base/common/types';
import { refineServiceDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { EXTENSION_IDENTIFIER_WITH_LOG_REGEX } from 'vs/platform/environment/common/environmentService';
export const IBrowserWorkbenchEnvironmentService = refineServiceDecorator<IEnvironmentService, IBrowserWorkbenchEnvironmentService>(IEnvironmentService);
@@ -47,7 +48,28 @@ export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvi
get logsPath(): string { return this.logsHome.path; }
@memoize
get logLevel(): string | undefined { return this.payload?.get('logLevel') || (this.options.developmentOptions?.logLevel !== undefined ? LogLevelToString(this.options.developmentOptions?.logLevel) : undefined); }
get logLevel(): string | undefined {
const logLevelFromPayload = this.payload?.get('logLevel');
if (logLevelFromPayload) {
return logLevelFromPayload.split(',').find(entry => !EXTENSION_IDENTIFIER_WITH_LOG_REGEX.test(entry));
}
return this.options.developmentOptions?.logLevel !== undefined ? LogLevelToString(this.options.developmentOptions?.logLevel) : undefined;
}
get extensionLogLevel(): [string, string][] | undefined {
const logLevelFromPayload = this.payload?.get('logLevel');
if (logLevelFromPayload) {
const result: [string, string][] = [];
for (const entry of logLevelFromPayload.split(',')) {
const matches = EXTENSION_IDENTIFIER_WITH_LOG_REGEX.exec(entry);
if (matches && matches[1] && matches[2]) {
result.push([matches[1], matches[2]]);
}
}
return result.length ? result : undefined;
}
return this.options.developmentOptions?.extensionLogLevel !== undefined ? this.options.developmentOptions?.extensionLogLevel.map(([extension, logLevel]) => ([extension, LogLevelToString(logLevel)])) : undefined;
}
@memoize
get logFile(): URI { return joinPath(this.logsHome, 'window.log'); }

View File

@@ -298,6 +298,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI,
globalStorageHome: this._userDataProfilesService.defaultProfile.globalStorageHome,
workspaceStorageHome: this._environmentService.workspaceStorageHome,
extensionLogLevel: this._environmentService.extensionLogLevel
},
workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : {
configuration: workspace.configuration || undefined,

View File

@@ -54,6 +54,7 @@ export interface IEnvironment {
workspaceStorageHome: URI;
useHostProxy?: boolean;
skipWorkspaceStorageLock?: boolean;
extensionLogLevel?: [string, string][];
}
export interface IStaticWorkspaceData {

View File

@@ -226,7 +226,8 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost {
extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI,
extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI,
globalStorageHome: remoteInitData.globalStorageHome,
workspaceStorageHome: remoteInitData.workspaceStorageHome
workspaceStorageHome: remoteInitData.workspaceStorageHome,
extensionLogLevel: this._environmentService.extensionLogLevel
},
workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : {
configuration: workspace.configuration,

View File

@@ -450,6 +450,7 @@ export class SandboxLocalProcessExtensionHost implements IExtensionHost {
extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI,
globalStorageHome: this._userDataProfilesService.defaultProfile.globalStorageHome,
workspaceStorageHome: this._environmentService.workspaceStorageHome,
extensionLogLevel: this._environmentService.extensionLogLevel
},
workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : {
configuration: withNullAsUndefined(workspace.configuration),