diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 99df30ba63551616195ee692af57f30a5db0bf64..e56584e1573fcb6774bc4998bffb11e6cf0cc92c 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -138,8 +138,8 @@ export function createApiFactory( const extHostMessageService = new ExtHostMessageService(rpcProtocol); const extHostDialogs = new ExtHostDialogs(rpcProtocol); const extHostStatusBar = new ExtHostStatusBar(rpcProtocol); - const outputDir = posix.join(initData.logsPath, `output_logging_${initData.windowId}_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`); - const extHostOutputService = new ExtHostOutputService(outputDir, rpcProtocol); + const outputPath = posix.join(initData.logsLocation.fsPath, `output_logging_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`); + const extHostOutputService = new ExtHostOutputService(outputPath, rpcProtocol); const extHostLanguages = new ExtHostLanguages(rpcProtocol); // Register API-ish commands diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 7e5e959209c68f707a0ab6829e6b7c09e80d6487..1b14e2ca684ba9dce9e2db647c7ef76adc6df30e 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -66,9 +66,8 @@ export interface IInitData { extensions: IExtensionDescription[]; configuration: IConfigurationInitData; telemetryInfo: ITelemetryInfo; - windowId: number; logLevel: LogLevel; - logsPath: string; + logsLocation: URI; } export interface IConfigurationInitData extends IConfigurationData { diff --git a/src/vs/workbench/api/node/extHostLogService.ts b/src/vs/workbench/api/node/extHostLogService.ts index 31b0113188f95d278b2182ee059a2f0520ba3ae0..3ee4306857a974d57e333db3b0e39242c0e62c52 100644 --- a/src/vs/workbench/api/node/extHostLogService.ts +++ b/src/vs/workbench/api/node/extHostLogService.ts @@ -9,16 +9,19 @@ import { LogLevel } from 'vs/workbench/api/node/extHostTypes'; import { ILogService, DelegatedLogService } from 'vs/platform/log/common/log'; import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; import { ExtHostLogServiceShape } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions'; export class ExtHostLogService extends DelegatedLogService implements ILogService, ExtHostLogServiceShape { + private _logsPath: string; + constructor( - private _windowId: number, logLevel: LogLevel, - private _logsPath: string + logsPath: string, ) { - super(createSpdLogService(`exthost${_windowId}`, logLevel, _logsPath)); + super(createSpdLogService(ExtensionHostLogFileName, logLevel, logsPath)); + this._logsPath = logsPath; } $setLevel(level: LogLevel): void { @@ -26,6 +29,6 @@ export class ExtHostLogService extends DelegatedLogService implements ILogServic } getLogDirectory(extensionID: string): string { - return join(this._logsPath, `${extensionID}_${this._windowId}`); + return join(this._logsPath, extensionID); } } diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts index 9fb6c8a111f77b3cbd7b46c2321574d5e68a5bb8..426b4cfd2266c63d0d973ca7c0b5d54bce25dae8 100644 --- a/src/vs/workbench/node/extensionHostMain.ts +++ b/src/vs/workbench/node/extensionHostMain.ts @@ -100,8 +100,9 @@ export class ExtensionHostMain { this._workspace = rpcProtocol.transformIncomingURIs(initData.workspace); // ensure URIs are revived initData.extensions.forEach((ext) => (ext).extensionLocation = URI.revive(ext.extensionLocation)); + initData.logsLocation = URI.revive(initData.logsLocation); - this._extHostLogService = new ExtHostLogService(initData.windowId, initData.logLevel, initData.logsPath); + this._extHostLogService = new ExtHostLogService(initData.logLevel, initData.logsLocation.fsPath); this.disposables.push(this._extHostLogService); this._searchRequestIdProvider = new Counter(); diff --git a/src/vs/workbench/parts/logs/electron-browser/logs.contribution.ts b/src/vs/workbench/parts/logs/electron-browser/logs.contribution.ts index 9d04687681e7b395037d2c872dea376d1cc203bf..c12396268d997e989d8289e625292da1803c7e83 100644 --- a/src/vs/workbench/parts/logs/electron-browser/logs.contribution.ts +++ b/src/vs/workbench/parts/logs/electron-browser/logs.contribution.ts @@ -5,6 +5,7 @@ import * as nls from 'vs/nls'; import { join } from 'vs/base/common/paths'; +import * as resources from 'vs/base/common/resources'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IOutputChannelRegistry, Extensions as OutputExt, } from 'vs/workbench/parts/output/common/output'; @@ -13,26 +14,26 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IWindowService } from 'vs/platform/windows/common/windows'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import * as Constants from 'vs/workbench/parts/logs/common/logConstants'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { ShowLogsAction, OpenLogsFolderAction, SetLogLevelAction, OpenLogFileAction } from 'vs/workbench/parts/logs/electron-browser/logsActions'; +import { IExtensionService, ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions'; class LogOutputChannels extends Disposable implements IWorkbenchContribution { constructor( - @IWindowService private windowService: IWindowService, - @IEnvironmentService private environmentService: IEnvironmentService, - @IInstantiationService instantiationService: IInstantiationService + @IWindowService windowService: IWindowService, + @IEnvironmentService environmentService: IEnvironmentService, + @IExtensionService extensionService: IExtensionService ) { super(); let outputChannelRegistry = Registry.as(OutputExt.OutputChannels); - outputChannelRegistry.registerChannel({ id: Constants.mainLogChannelId, label: nls.localize('mainLog', "Log (Main)"), file: URI.file(join(this.environmentService.logsPath, `main.log`)), log: true }); - outputChannelRegistry.registerChannel({ id: Constants.sharedLogChannelId, label: nls.localize('sharedLog', "Log (Shared)"), file: URI.file(join(this.environmentService.logsPath, `sharedprocess.log`)), log: true }); - outputChannelRegistry.registerChannel({ id: Constants.rendererLogChannelId, label: nls.localize('rendererLog', "Log (Window)"), file: URI.file(join(this.environmentService.logsPath, `renderer${this.windowService.getCurrentWindowId()}.log`)), log: true }); - outputChannelRegistry.registerChannel({ id: Constants.extHostLogChannelId, label: nls.localize('extensionsLog', "Log (Extension Host)"), file: URI.file(join(this.environmentService.logsPath, `exthost${this.windowService.getCurrentWindowId()}.log`)), log: true }); + outputChannelRegistry.registerChannel({ id: Constants.mainLogChannelId, label: nls.localize('mainLog', "Log (Main)"), file: URI.file(join(environmentService.logsPath, `main.log`)), log: true }); + outputChannelRegistry.registerChannel({ id: Constants.sharedLogChannelId, label: nls.localize('sharedLog', "Log (Shared)"), file: URI.file(join(environmentService.logsPath, `sharedprocess.log`)), log: true }); + outputChannelRegistry.registerChannel({ id: Constants.rendererLogChannelId, label: nls.localize('rendererLog', "Log (Window)"), file: URI.file(join(environmentService.logsPath, `renderer${windowService.getCurrentWindowId()}.log`)), log: true }); + extensionService.getLogsLocations().then(([logsLocation]) => outputChannelRegistry.registerChannel({ id: Constants.extHostLogChannelId, label: nls.localize('extensionsLog', "Log (Extension Host)"), file: resources.joinPath(logsLocation, `${ExtensionHostLogFileName}.log`), log: true })); const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); const devCategory = nls.localize('developer', "Developer"); diff --git a/src/vs/workbench/parts/output/browser/outputActions.ts b/src/vs/workbench/parts/output/browser/outputActions.ts index 8190d5131a047e94ec186d17e28a91216a29aed6..ed4d811c127ac486ab5b5f37517fe49ea7f5fef8 100644 --- a/src/vs/workbench/parts/output/browser/outputActions.ts +++ b/src/vs/workbench/parts/output/browser/outputActions.ts @@ -20,6 +20,7 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView import { Registry } from 'vs/platform/registry/common/platform'; import { groupBy } from 'vs/base/common/arrays'; import { ICommandService } from 'vs/platform/commands/common/commands'; +import { URI } from 'vs/base/common/uri'; export class ToggleOutputAction extends TogglePanelAction { @@ -179,16 +180,16 @@ export class OpenLogOutputFile extends Action { } private update(): void { - this.enabled = this.isLogChannel(); + this.enabled = !!this.getLogFile(); } public run(): TPromise { - return this.commandService.executeCommand(COMMAND_OPEN_LOG_VIEWER, this.isLogChannel()); + return this.commandService.executeCommand(COMMAND_OPEN_LOG_VIEWER, this.getLogFile()); } - private isLogChannel(): boolean { + private getLogFile(): URI { const channel = this.outputService.getActiveChannel(); const descriptor = channel ? this.outputService.getChannelDescriptors().filter(c => c.id === channel.id)[0] : null; - return descriptor && descriptor.log; + return descriptor && descriptor.log ? descriptor.file : null; } } diff --git a/src/vs/workbench/services/commands/test/common/commandService.test.ts b/src/vs/workbench/services/commands/test/common/commandService.test.ts index 52d1f2fb54036035cab3fb5b62049339a0a00a82..3ef3c737394bd7c7b079f32ec2d44be76b06a472 100644 --- a/src/vs/workbench/services/commands/test/common/commandService.test.ts +++ b/src/vs/workbench/services/commands/test/common/commandService.test.ts @@ -14,6 +14,7 @@ import { InstantiationService } from 'vs/platform/instantiation/common/instantia import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { Event, Emitter } from 'vs/base/common/event'; import { NullLogService } from 'vs/platform/log/common/log'; +import { URI } from 'vs/base/common/uri'; class SimpleExtensionService implements IExtensionService { _serviceBrand: any; @@ -37,6 +38,7 @@ class SimpleExtensionService implements IExtensionService { getExtensions(): TPromise { return TPromise.wrap([]); } + getLogsLocations(): TPromise { return TPromise.as([]); } canProfileExtensionHost() { return false; } diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 521bac00dff38ace80f5b1ccf05a2cc4d91e2e14..54e4a201584b5ad2434eb08230201a945c3e2d1a 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -116,6 +116,8 @@ export class ExtensionPointContribution { } } +export const ExtensionHostLogFileName = 'exthost'; + export interface IExtensionService { _serviceBrand: any; @@ -151,6 +153,11 @@ export interface IExtensionService { */ getExtensions(): TPromise; + /** + * Return extension host log folder paths + */ + getLogsLocations(): TPromise; + /** * Read all contributions to an extension point. */ diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 5b06808f9eee038820892b3a445f648eddb663b5..f51d930700a0f8f52ed615589dcbdb6247487120 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -74,6 +74,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { constructor( private readonly _extensions: TPromise, + private readonly _logsLocation: TPromise, @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @INotificationService private readonly _notificationService: INotificationService, @IWindowsService private readonly _windowsService: IWindowsService, @@ -375,34 +376,35 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { } private _createExtHostInitData(): TPromise { - return TPromise.join([this._telemetryService.getTelemetryInfo(), this._extensions]).then(([telemetryInfo, extensionDescriptions]) => { - const configurationData: IConfigurationInitData = { ...this._configurationService.getConfigurationData(), configurationScopes: {} }; - const workspace = this._contextService.getWorkspace(); - const r: IInitData = { - parentPid: process.pid, - environment: { - isExtensionDevelopmentDebug: this._isExtensionDevDebug, - appRoot: this._environmentService.appRoot, - appSettingsHome: this._environmentService.appSettingsHome, - extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, - extensionTestsPath: this._environmentService.extensionTestsPath - }, - workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : { - configuration: workspace.configuration, - folders: workspace.folders, - id: workspace.id, - name: this._labelService.getWorkspaceLabel(workspace) - }, - extensions: extensionDescriptions, - // Send configurations scopes only in development mode. - configuration: !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment ? { ...configurationData, configurationScopes: getScopes() } : configurationData, - telemetryInfo, - windowId: this._windowService.getCurrentWindowId(), - logLevel: this._logService.getLevel(), - logsPath: this._environmentService.logsPath - }; - return r; - }); + const promises: TPromise[] = [this._telemetryService.getTelemetryInfo(), this._extensions, this._logsLocation]; + return TPromise.join(promises) + .then(([telemetryInfo, extensionDescriptions, logsLocation]) => { + const configurationData: IConfigurationInitData = { ...this._configurationService.getConfigurationData(), configurationScopes: {} }; + const workspace = this._contextService.getWorkspace(); + const r: IInitData = { + parentPid: process.pid, + environment: { + isExtensionDevelopmentDebug: this._isExtensionDevDebug, + appRoot: this._environmentService.appRoot, + appSettingsHome: this._environmentService.appSettingsHome, + extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, + extensionTestsPath: this._environmentService.extensionTestsPath + }, + workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : { + configuration: workspace.configuration, + folders: workspace.folders, + id: workspace.id, + name: this._labelService.getWorkspaceLabel(workspace) + }, + extensions: extensionDescriptions, + // Send configurations scopes only in development mode. + configuration: !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment ? { ...configurationData, configurationScopes: getScopes() } : configurationData, + telemetryInfo, + logLevel: this._logService.getLevel(), + logsLocation + }; + return r; + }); } private _logExtensionHostMessage(entry: IRemoteConsoleLog) { diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 3e4712fecd0ab12dee932733dbf76fadde4026c1..b21480693ec85a53dc248d2ae2abcb8ad8ce4ed0 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -368,7 +368,7 @@ export class ExtensionService extends Disposable implements IExtensionService { private _startExtensionHostProcess(initialActivationEvents: string[]): void { this._stopExtensionHostProcess(); - const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, this.getExtensions()); + const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, this.getExtensions(), this.getLogsLocations().then(([logsLocation]) => logsLocation)); const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, initialActivationEvents); extHostProcessManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal)); this._extensionHostProcessManagers.push(extHostProcessManager); @@ -436,6 +436,10 @@ export class ExtensionService extends Disposable implements IExtensionService { }); } + public getLogsLocations(): TPromise { + return TPromise.as([URI.file(path.posix.join(this._environmentService.logsPath, `exthost${this._windowService.getCurrentWindowId()}`))]); + } + public readExtensionPointContributions(extPoint: IExtensionPoint): TPromise[]> { return this._installedExtensionsReady.wait().then(() => { let availableExtensions = this._registry.getAllExtensionDescriptions(); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 6655424c803b06b35aa22bae077ae0be1f90a3cf..4f62661b43adeba0d3d60c9f9f3938c52e630f7d 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -307,6 +307,7 @@ export class TestExtensionService implements IExtensionService { activateByEvent(activationEvent: string): TPromise { return TPromise.as(void 0); } whenInstalledExtensionsRegistered(): TPromise { return TPromise.as(true); } getExtensions(): TPromise { return TPromise.as([]); } + getLogsLocations(): TPromise { return TPromise.as([]); } readExtensionPointContributions(extPoint: IExtensionPoint): TPromise[]> { return TPromise.as(Object.create(null)); } getExtensionsStatus(): { [id: string]: IExtensionsStatus; } { return Object.create(null); } canProfileExtensionHost(): boolean { return false; }