From 4026f653b40e5755a4601b907416f46470477946 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 27 May 2019 15:26:48 +0200 Subject: [PATCH] #72906 Restructure spdlog service --- .../issue/issueReporterMain.ts | 4 +- .../sharedProcess/sharedProcessMain.ts | 6 +- src/vs/code/electron-main/main.ts | 9 +- src/vs/code/node/cliProcessMain.ts | 4 +- src/vs/platform/log/node/spdlogService.ts | 102 +++++++++++++----- src/vs/workbench/electron-browser/main.ts | 4 +- .../node/extensionHostProcessSetup.ts | 4 +- 7 files changed, 89 insertions(+), 44 deletions(-) diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index a01437487bd..d753746fe85 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -33,7 +33,6 @@ import { EnvironmentService } from 'vs/platform/environment/node/environmentServ import { IssueReporterModel, IssueReporterData as IssueReporterModelData } from 'vs/code/electron-browser/issue/issueReporterModel'; import { IssueReporterData, IssueReporterStyles, IssueType, ISettingsSearchIssueReporterData, IssueReporterFeatures, IssueReporterExtensionData } from 'vs/platform/issue/common/issue'; import BaseHtml from 'vs/code/electron-browser/issue/issueReporterPage'; -import { createBufferSpdLogService } from 'vs/platform/log/node/spdlogService'; import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc'; import { ILogService, getLogLevel } from 'vs/platform/log/common/log'; import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; @@ -41,6 +40,7 @@ import { normalizeGitHubUrl } from 'vs/code/electron-browser/issue/issueReporter import { Button } from 'vs/base/browser/ui/button/button'; import { withUndefinedAsNull } from 'vs/base/common/types'; import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService'; +import { SpdLogService } from 'vs/platform/log/node/spdlogService'; const MAX_URL_LENGTH = 2045; @@ -300,7 +300,7 @@ export class IssueReporter extends Disposable { serviceCollection.set(IWindowsService, new WindowsService(mainProcessService)); this.environmentService = new EnvironmentService(configuration, configuration.execPath); - const logService = createBufferSpdLogService(`issuereporter${configuration.windowId}`, getLogLevel(this.environmentService), this.environmentService.logsPath); + const logService = new SpdLogService(`issuereporter${configuration.windowId}`, this.environmentService.logsPath, getLogLevel(this.environmentService)); const logLevelClient = new LogLevelSetterChannelClient(mainProcessService.getChannel('loglevel')); this.logService = new FollowerLogService(logLevelClient, logService); diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index e0125000316..419048741e7 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -30,7 +30,6 @@ import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppen import { IWindowsService, ActiveWindowManager } from 'vs/platform/windows/common/windows'; import { WindowsService } from 'vs/platform/windows/electron-browser/windowsService'; import { ipcRenderer } from 'electron'; -import { createBufferSpdLogService } from 'vs/platform/log/node/spdlogService'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc'; import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; @@ -48,6 +47,7 @@ import { StorageDataCleaner } from 'vs/code/electron-browser/sharedProcess/contr import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { SpdLogService } from 'vs/platform/log/node/spdlogService'; export interface ISharedProcessConfiguration { readonly machineId: string; @@ -94,7 +94,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I const mainRouter = new StaticRouter(ctx => ctx === 'main'); const logLevelClient = new LogLevelSetterChannelClient(server.getChannel('loglevel', mainRouter)); - const logService = new FollowerLogService(logLevelClient, createBufferSpdLogService('sharedprocess', initData.logLevel, environmentService.logsPath)); + const logService = new FollowerLogService(logLevelClient, new SpdLogService('sharedprocess', environmentService.logsPath, initData.logLevel)); disposables.push(logService); logService.info('main', JSON.stringify(configuration)); @@ -122,7 +122,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I const services = new ServiceCollection(); const environmentService = accessor.get(IEnvironmentService); const { appRoot, extensionsPath, extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService; - const telemetryLogService = new FollowerLogService(logLevelClient, createBufferSpdLogService('telemetry', initData.logLevel, environmentService.logsPath)); + const telemetryLogService = new FollowerLogService(logLevelClient, new SpdLogService('telemetry', environmentService.logsPath, initData.logLevel)); telemetryLogService.info('The below are logs for every telemetry event sent from VS Code once the log level is set to trace.'); telemetryLogService.info('==========================================================='); diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 60229a78d9a..9c0d5f9ab91 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -32,7 +32,7 @@ import * as fs from 'fs'; import { CodeApplication } from 'vs/code/electron-main/app'; import { localize } from 'vs/nls'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; -import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; +import { SpdLogService } from 'vs/platform/log/node/spdlogService'; import { IDiagnosticsService, DiagnosticsService } from 'vs/platform/diagnostics/electron-main/diagnosticsService'; import { BufferLogService } from 'vs/platform/log/common/bufferLog'; import { uploadLogs } from 'vs/code/electron-main/logUploader'; @@ -293,12 +293,7 @@ async function startup(args: ParsedArgs): Promise { } const mainIpcServer = await instantiationService.invokeFunction(setupIPC); - - (async () => { - const logger = await createSpdLogService('main', bufferLogService.getLevel(), environmentService.logsPath); - bufferLogService.logger = logger; - })(); - + bufferLogService.logger = new SpdLogService('main', environmentService.logsPath, bufferLogService.getLevel()); return instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnvironment).startup(); }); } catch (error) { diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 8146b7877b9..f5116c42827 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -31,7 +31,6 @@ import { mkdirp, writeFile } from 'vs/base/node/pfs'; import { getBaseLabel } from 'vs/base/common/labels'; import { IStateService } from 'vs/platform/state/common/state'; import { StateService } from 'vs/platform/state/node/stateService'; -import { createBufferSpdLogService } from 'vs/platform/log/node/spdlogService'; import { ILogService, getLogLevel } from 'vs/platform/log/common/log'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { areSameExtensions, adoptToGalleryExtensionId, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; @@ -41,6 +40,7 @@ import { IExtensionManifest, ExtensionType, isLanguagePackExtension } from 'vs/p import { CancellationToken } from 'vs/base/common/cancellation'; import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; import { Schemas } from 'vs/base/common/network'; +import { SpdLogService } from 'vs/platform/log/node/spdlogService'; const notFound = (id: string) => localize('notFound', "Extension '{0}' not found.", id); const notInstalled = (id: string) => localize('notInstalled', "Extension '{0}' is not installed.", id); @@ -279,7 +279,7 @@ export function main(argv: ParsedArgs): Promise { const services = new ServiceCollection(); const environmentService = new EnvironmentService(argv, process.execPath); - const logService = createBufferSpdLogService('cli', getLogLevel(environmentService), environmentService.logsPath); + const logService: ILogService = new SpdLogService('cli', environmentService.logsPath, getLogLevel(environmentService)); process.once('exit', () => logService.dispose()); logService.info('main', argv); diff --git a/src/vs/platform/log/node/spdlogService.ts b/src/vs/platform/log/node/spdlogService.ts index 3ecc06bbf90..1037273704d 100644 --- a/src/vs/platform/log/node/spdlogService.ts +++ b/src/vs/platform/log/node/spdlogService.ts @@ -4,24 +4,20 @@ *--------------------------------------------------------------------------------------------*/ import * as path from 'vs/base/common/path'; -import { ILogService, LogLevel, NullLogService, AbstractLogService } from 'vs/platform/log/common/log'; +import { ILogService, LogLevel, AbstractLogService } from 'vs/platform/log/common/log'; import * as spdlog from 'spdlog'; -import { BufferLogService } from 'vs/platform/log/common/bufferLog'; -export async function createSpdLogService(processName: string, logLevel: LogLevel, logsFolder: string): Promise { +async function createSpdLogLogger(processName: string, logsFolder: string): Promise { // Do not crash if spdlog cannot be loaded try { - const _spdlog: typeof spdlog = require.__$__nodeRequire('spdlog'); + const _spdlog = await import('spdlog'); _spdlog.setAsyncMode(8192, 500); const logfilePath = path.join(logsFolder, `${processName}.log`); - const logger = await _spdlog.createRotatingLoggerAsync(processName, logfilePath, 1024 * 1024 * 5, 6); - logger.setLevel(0); - - return new SpdLogService(logger, logLevel); + return _spdlog.createRotatingLoggerAsync(processName, logfilePath, 1024 * 1024 * 5, 6); } catch (e) { console.error(e); } - return new NullLogService(); + return null; } export function createRotatingLogger(name: string, filename: string, filesize: number, filecount: number): spdlog.RotatingLogger { @@ -29,45 +25,87 @@ export function createRotatingLogger(name: string, filename: string, filesize: n return _spdlog.createRotatingLogger(name, filename, filesize, filecount); } -export function createBufferSpdLogService(processName: string, logLevel: LogLevel, logsFolder: string): ILogService { - const bufferLogService = new BufferLogService(); - createSpdLogService(processName, logLevel, logsFolder).then(logger => bufferLogService.logger = logger); - return bufferLogService; +interface ILog { + level: LogLevel; + message: string; +} + +function log(logger: spdlog.RotatingLogger, level: LogLevel, message: string): void { + switch (level) { + case LogLevel.Trace: return logger.trace(message); + case LogLevel.Debug: return logger.debug(message); + case LogLevel.Info: return logger.info(message); + case LogLevel.Warning: return logger.warn(message); + case LogLevel.Error: return logger.error(message); + case LogLevel.Critical: return logger.critical(message); + default: throw new Error('Invalid log level'); + } } -class SpdLogService extends AbstractLogService implements ILogService { +export class SpdLogService extends AbstractLogService implements ILogService { _serviceBrand: any; - constructor( - private readonly logger: spdlog.RotatingLogger, - level: LogLevel = LogLevel.Error - ) { + private buffer: ILog[] = []; + private _loggerPromise: Promise | undefined = undefined; + private _logger: spdlog.RotatingLogger | undefined; + + constructor(private readonly name: string, private readonly logsFolder: string, level: LogLevel) { super(); this.setLevel(level); + this._createSpdLogLogger(); + this._register(this.onDidChangeLogLevel(level => { + if (this._logger) { + this._logger.setLevel(level); + } + })); + } + + private _createSpdLogLogger() { + if (!this._loggerPromise) { + this._loggerPromise = createSpdLogLogger(this.name, this.logsFolder); + this._loggerPromise.then(logger => { + if (logger) { + this._logger = logger; + this._logger.setLevel(this.getLevel()); + for (const { level, message } of this.buffer) { + log(this._logger, level, message); + } + } + }); + } + this.buffer = []; + } + + private _log(level: LogLevel, message: string): void { + if (this._logger) { + log(this._logger, level, message); + } else if (this.getLevel() <= level) { + this.buffer.push({ level, message }); + } } trace(): void { if (this.getLevel() <= LogLevel.Trace) { - this.logger.trace(this.format(arguments)); + this._log(LogLevel.Trace, this.format(arguments)); } } debug(): void { if (this.getLevel() <= LogLevel.Debug) { - this.logger.debug(this.format(arguments)); + this._log(LogLevel.Debug, this.format(arguments)); } } info(): void { if (this.getLevel() <= LogLevel.Info) { - this.logger.info(this.format(arguments)); + this._log(LogLevel.Info, this.format(arguments)); } } warn(): void { if (this.getLevel() <= LogLevel.Warning) { - this.logger.warn(this.format(arguments)); + this._log(LogLevel.Warning, this.format(arguments)); } } @@ -78,21 +116,33 @@ class SpdLogService extends AbstractLogService implements ILogService { if (arg instanceof Error) { const array = Array.prototype.slice.call(arguments) as any[]; array[0] = arg.stack; - this.logger.error(this.format(array)); + this._log(LogLevel.Error, this.format(array)); } else { - this.logger.error(this.format(arguments)); + this._log(LogLevel.Error, this.format(arguments)); } } } critical(): void { if (this.getLevel() <= LogLevel.Critical) { - this.logger.critical(this.format(arguments)); + this._log(LogLevel.Critical, this.format(arguments)); } } dispose(): void { - this.logger.drop(); + if (this._logger) { + this.disposeLogger(); + } else if (this._loggerPromise) { + this._loggerPromise.then(() => this.disposeLogger()); + } + this._loggerPromise = undefined; + } + + private disposeLogger(): void { + if (this._logger) { + this._logger.drop(); + this._logger = undefined; + } } private format(args: any): string { diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index 54eb96b7bc3..b6029707bdf 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -23,7 +23,6 @@ import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { webFrame } from 'electron'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceInitializationPayload, ISingleFolderWorkspaceInitializationPayload, reviveWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { createBufferSpdLogService } from 'vs/platform/log/node/spdlogService'; import { ConsoleLogService, MultiplexLogService, ILogService } from 'vs/platform/log/common/log'; import { StorageService } from 'vs/platform/storage/node/storageService'; import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc'; @@ -49,6 +48,7 @@ import { REMOTE_FILE_SYSTEM_CHANNEL_NAME, RemoteExtensionsFileSystemProvider } f import { DefaultConfigurationExportHelper } from 'vs/workbench/services/configuration/node/configurationExportHelper'; import { ConfigurationCache } from 'vs/workbench/services/configuration/node/configurationCache'; import { ConfigurationFileService } from 'vs/workbench/services/configuration/node/configurationFileService'; +import { SpdLogService } from 'vs/platform/log/node/spdlogService'; class CodeRendererMain extends Disposable { @@ -334,7 +334,7 @@ class CodeRendererMain extends Disposable { } private createLogService(mainProcessService: IMainProcessService, environmentService: IWorkbenchEnvironmentService): ILogService { - const spdlogService = createBufferSpdLogService(`renderer${this.configuration.windowId}`, this.configuration.logLevel, environmentService.logsPath); + const spdlogService = new SpdLogService(`renderer${this.configuration.windowId}`, environmentService.logsPath, this.configuration.logLevel); const consoleLogService = new ConsoleLogService(this.configuration.logLevel); const logService = new MultiplexLogService([consoleLogService, spdlogService]); const logLevelClient = new LogLevelSetterChannelClient(mainProcessService.getChannel('loglevel')); diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts index 5ecb5fe5816..d7b8965f8ac 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts @@ -16,12 +16,12 @@ import { IInitData, MainThreadConsoleShape } from 'vs/workbench/api/common/extHo import { MessageType, createMessageOfType, isMessageOfType, IExtHostSocketMessage, IExtHostReadyMessage } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { ExtensionHostMain, IExitFn, ILogServiceFn } from 'vs/workbench/services/extensions/node/extensionHostMain'; import { VSBuffer } from 'vs/base/common/buffer'; -import { createBufferSpdLogService } from 'vs/platform/log/node/spdlogService'; import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions'; import { IURITransformer, URITransformer, IRawURITransformer } from 'vs/base/common/uriIpc'; import { exists } from 'vs/base/node/pfs'; import { realpath } from 'vs/base/node/extpath'; import { IHostUtils } from 'vs/workbench/api/node/extHostExtensionService'; +import { SpdLogService } from 'vs/platform/log/node/spdlogService'; interface ParsedExtHostArgs { uriTransformerPath?: string; @@ -82,7 +82,7 @@ function patchPatchedConsole(mainThreadConsole: MainThreadConsoleShape): void { }; } -const createLogService: ILogServiceFn = initData => createBufferSpdLogService(ExtensionHostLogFileName, initData.logLevel, initData.logsLocation.fsPath); +const createLogService: ILogServiceFn = initData => new SpdLogService(ExtensionHostLogFileName, initData.logsLocation.fsPath, initData.logLevel); interface IRendererConnection { protocol: IMessagePassingProtocol; -- GitLab