diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 2e79206ac401d61d3bd249149a63a74f07f6122d..2f0521cf00ebba5a072ea59bf70802a516c702c1 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -37,7 +37,7 @@ import { WindowsChannelClient } from 'vs/platform/windows/common/windowsIpc'; import { ipcRenderer } from 'electron'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { createSharedProcessContributions } from 'vs/code/electron-browser/sharedProcess/contrib/contributions'; -import { createLogService } from 'vs/platform/log/node/spdlogService'; +import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; import { ILogService } from 'vs/platform/log/common/log'; export interface ISharedProcessConfiguration { @@ -81,7 +81,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I const services = new ServiceCollection(); const environmentService = new EnvironmentService(initData.args, process.execPath); - const logService = createLogService('sharedprocess', environmentService); + const logService = createSpdLogService('sharedprocess', environmentService); process.once('exit', () => logService.dispose()); logService.info('main', JSON.stringify(configuration)); diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 3c042ea5a6717c573bde6b7bbcea4d7de5068dd8..4da562dfb9c2f51004fd58d3c780f0a872870309 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -42,16 +42,16 @@ import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/work import { IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces'; import { localize } from 'vs/nls'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; -import { createLogService } from 'vs/platform/log/node/spdlogService'; +import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; import { printDiagnostics } from 'vs/code/electron-main/diagnostics'; +import { BufferLogService } from 'vs/platform/log/common/bufferLog'; -function createServices(args: ParsedArgs): IInstantiationService { +function createServices(args: ParsedArgs, bufferLogService: BufferLogService): IInstantiationService { const services = new ServiceCollection(); const environmentService = new EnvironmentService(args, process.execPath); - const spdlogService = createLogService('main', environmentService); const consoleLogService = new ConsoleLogMainService(environmentService); - const logService = new MultiplexLogService([consoleLogService, spdlogService]); + const logService = new MultiplexLogService([consoleLogService, bufferLogService]); process.once('exit', () => logService.dispose()); @@ -284,7 +284,12 @@ function main() { return; } - const instantiationService = createServices(args); + // We need to buffer the spdlog logs until we are sure + // we are the only instance running, otherwise we'll have concurrent + // log file access on Windows + // https://github.com/Microsoft/vscode/issues/41218 + const bufferLogService = new BufferLogService(); + const instantiationService = createServices(args, bufferLogService); return instantiationService.invokeFunction(accessor => { @@ -300,7 +305,10 @@ function main() { // Startup return instantiationService.invokeFunction(a => createPaths(a.get(IEnvironmentService))) .then(() => instantiationService.invokeFunction(setupIPC)) - .then(mainIpcServer => instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnv).startup()); + .then(mainIpcServer => { + bufferLogService.logger = createSpdLogService('main', environmentService); + return instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnv).startup(); + }); }).done(null, err => instantiationService.invokeFunction(quit, err)); } diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index dfdaf922741a5774cead3cc86bb6dc0607a9ccab..0d99eadec4f500c249c8750a31bdc7fb5aa99421 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -35,7 +35,7 @@ import { ChoiceCliService } from 'vs/platform/message/node/messageCli'; import { getBaseLabel } from 'vs/base/common/labels'; import { IStateService } from 'vs/platform/state/common/state'; import { StateService } from 'vs/platform/state/node/stateService'; -import { createLogService } from 'vs/platform/log/node/spdlogService'; +import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; import { ILogService } from 'vs/platform/log/common/log'; import { isPromiseCanceledError } from 'vs/base/common/errors'; @@ -196,7 +196,7 @@ export function main(argv: ParsedArgs): TPromise { const services = new ServiceCollection(); const environmentService = new EnvironmentService(argv, process.execPath); - const logService = createLogService('cli', environmentService); + const logService = createSpdLogService('cli', environmentService); process.once('exit', () => logService.dispose()); logService.info('main', argv); diff --git a/src/vs/platform/log/common/bufferLog.ts b/src/vs/platform/log/common/bufferLog.ts new file mode 100644 index 0000000000000000000000000000000000000000..ce43f76d7fc0a84c3bd47998f1782a9e4b5187d7 --- /dev/null +++ b/src/vs/platform/log/common/bufferLog.ts @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { ILogService, LogLevel } from 'vs/platform/log/common/log'; + +interface ILog { + level: LogLevel; + args: IArguments; +} + +function getLogFunction(logger: ILogService, level: LogLevel): Function { + switch (level) { + case LogLevel.Trace: return logger.trace; + case LogLevel.Debug: return logger.debug; + case LogLevel.Info: return logger.info; + case LogLevel.Warning: return logger.warn; + case LogLevel.Error: return logger.error; + case LogLevel.Critical: return logger.critical; + default: throw new Error('Invalid log level'); + } +} + +export class BufferLogService implements ILogService { + + _serviceBrand: any; + private buffer: ILog[] = []; + private _logger: ILogService | undefined = undefined; + + constructor( + private level: LogLevel = LogLevel.Error + ) { + } + + set logger(logger: ILogService) { + this._logger = logger; + + for (const { level, args } of this.buffer) { + const fn = getLogFunction(logger, level); + fn.apply(logger, args); + } + + this.buffer = []; + } + + setLevel(logLevel: LogLevel): void { + this.level = logLevel; + } + + getLevel(): LogLevel { + return this.level; + } + + private _log(level: LogLevel, args: IArguments): void { + if (this._logger) { + const fn = getLogFunction(this._logger, level); + fn.apply(this._logger, args); + } else if (this.level < level) { + this.buffer.push({ level, args }); + } + } + + trace(): void { + this._log(LogLevel.Trace, arguments); + } + + debug(): void { + this._log(LogLevel.Debug, arguments); + } + + info(): void { + this._log(LogLevel.Info, arguments); + } + + warn(): void { + this._log(LogLevel.Warning, arguments); + } + + error(): void { + this._log(LogLevel.Error, arguments); + } + + critical(): void { + this._log(LogLevel.Critical, arguments); + } + + dispose(): void { + if (this._logger) { + this._logger.dispose(); + } + } +} \ No newline at end of file diff --git a/src/vs/platform/log/node/spdlogService.ts b/src/vs/platform/log/node/spdlogService.ts index d39af82182daef531d90704a98d5e76ddf07abb2..6493d6e1c8d7d4b358a081ebe691490d7c71b74e 100644 --- a/src/vs/platform/log/node/spdlogService.ts +++ b/src/vs/platform/log/node/spdlogService.ts @@ -10,7 +10,7 @@ import { ILogService, LogLevel, NullLogService } from 'vs/platform/log/common/lo import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { RotatingLogger, setAsyncMode } from 'spdlog'; -export function createLogService(processName: string, environmentService: IEnvironmentService, logsSubfolder?: string): ILogService { +export function createSpdLogService(processName: string, environmentService: IEnvironmentService, logsSubfolder?: string): ILogService { try { setAsyncMode(8192, 2000); const logsDirPath = logsSubfolder ? path.join(environmentService.logsPath, logsSubfolder) : environmentService.logsPath; diff --git a/src/vs/workbench/api/node/extHostLogService.ts b/src/vs/workbench/api/node/extHostLogService.ts index 150bde4af3d35c5595ef874adea2aa33ed6436d9..fff0755018d536c3256daa598f6ddb79087f6763 100644 --- a/src/vs/workbench/api/node/extHostLogService.ts +++ b/src/vs/workbench/api/node/extHostLogService.ts @@ -11,7 +11,7 @@ import { mkdirp, dirExists } from 'vs/base/node/pfs'; import Event, { Emitter } from 'vs/base/common/event'; import { LogLevel } from 'vs/workbench/api/node/extHostTypes'; import { ILogService } from 'vs/platform/log/common/log'; -import { createLogService } from 'vs/platform/log/node/spdlogService'; +import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { memoize } from 'vs/base/common/decorators'; @@ -23,7 +23,7 @@ export class ExtHostLogService { getExtLogger(extensionID: string): ExtHostLogger { if (!this._loggers.has(extensionID)) { - const logService = createLogService(extensionID, this._environmentService, extensionID); + const logService = createSpdLogService(extensionID, this._environmentService, extensionID); const logsDirPath = path.join(this._environmentService.logsPath, extensionID); this._loggers.set(extensionID, new ExtHostLogger(logService, logsDirPath)); } diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index 719c6d914d6abbef6064e620e9f39996804ea4e7..9e75b4e4cfa12d46516fd8ffa2f296f276da5f08 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -40,7 +40,7 @@ import { URLChannelClient } from 'vs/platform/url/common/urlIpc'; import { IURLService } from 'vs/platform/url/common/url'; import { WorkspacesChannelClient } from 'vs/platform/workspaces/common/workspacesIpc'; import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; -import { createLogService } from 'vs/platform/log/node/spdlogService'; +import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; import fs = require('fs'); import { ConsoleLogService, MultiplexLogService } from 'vs/platform/log/common/log'; @@ -73,7 +73,7 @@ function openWorkbench(configuration: IWindowConfiguration): TPromise { const mainServices = createMainProcessServices(mainProcessClient, configuration); const environmentService = new EnvironmentService(configuration, configuration.execPath); - const spdlogService = createLogService(`renderer${configuration.windowId}`, environmentService); + const spdlogService = createSpdLogService(`renderer${configuration.windowId}`, environmentService); const consoleLogService = new ConsoleLogService(environmentService); const logService = new MultiplexLogService([consoleLogService, spdlogService]); diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts index 76041a685a1d837f5d884ede619747770190d787..7af498132c55688c7129d747b2c8d45f057829c9 100644 --- a/src/vs/workbench/node/extensionHostMain.ts +++ b/src/vs/workbench/node/extensionHostMain.ts @@ -21,7 +21,7 @@ import * as watchdog from 'native-watchdog'; import * as glob from 'vs/base/common/glob'; import { ExtensionActivatedByEvent } from 'vs/workbench/api/node/extHostExtensionActivator'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { createLogService } from 'vs/platform/log/node/spdlogService'; +import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; @@ -92,7 +92,7 @@ export class ExtensionHostMain { const rpcProtocol = new RPCProtocol(protocol); const extHostWorkspace = new ExtHostWorkspace(rpcProtocol, initData.workspace); const environmentService = new EnvironmentService(initData.args, initData.execPath); - this._logService = createLogService(`exthost${initData.windowId}`, environmentService); + this._logService = createSpdLogService(`exthost${initData.windowId}`, environmentService); this.disposables.push(this._logService); this._logService.info('extension host started');