diff --git a/src/vs/workbench/api/common/extHostOutput.ts b/src/vs/workbench/api/common/extHostOutput.ts index 1b6f01999bed0..abe63cff8da6c 100644 --- a/src/vs/workbench/api/common/extHostOutput.ts +++ b/src/vs/workbench/api/common/extHostOutput.ts @@ -17,8 +17,11 @@ import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSyste import { toLocalISOString } from 'vs/base/common/date'; import { VSBuffer } from 'vs/base/common/buffer'; import { isString } from 'vs/base/common/types'; +import { FileSystemProviderErrorCode, toFileSystemProviderErrorCode } from 'vs/platform/files/common/files'; -export class ExtHostLogOutputChannel extends AbstractMessageLogger implements vscode.LogOutputChannel { +class ExtHostOutputChannel extends AbstractMessageLogger implements vscode.LogOutputChannel { + + private offset: number = 0; private _disposed: boolean = false; get disposed(): boolean { return this._disposed; } @@ -35,51 +38,11 @@ export class ExtHostLogOutputChannel extends AbstractMessageLogger implements vs } appendLine(value: string): void { - this.info(value); - } - - show(preserveFocus?: boolean): void { - this.proxy.$reveal(this.id, !!preserveFocus); - } - - hide(): void { - this.proxy.$close(this.id); - } - - protected log(level: LogLevel, message: string): void { - log(this.logger, level, message); - } - - override dispose(): void { - super.dispose(); - - if (!this._disposed) { - this.proxy.$dispose(this.id); - this._disposed = true; - } - } - -} - -export class ExtHostOutputChannel extends ExtHostLogOutputChannel implements vscode.OutputChannel { - - private offset: number = 0; - - constructor( - id: string, name: string, - logger: ILogger, - proxy: MainThreadOutputServiceShape, - extension: IExtensionDescription - ) { - super(id, name, logger, proxy, extension); - } - - override appendLine(value: string): void { this.append(value + '\n'); } append(value: string): void { - this.write(value); + this.info(value); if (this.visible) { this.logger.flush(); this.proxy.$update(this.id, OutputChannelUpdateMode.Append); @@ -94,21 +57,42 @@ export class ExtHostOutputChannel extends ExtHostLogOutputChannel implements vsc replace(value: string): void { const till = this.offset; - this.write(value); + this.info(value); this.proxy.$update(this.id, OutputChannelUpdateMode.Replace, till); if (this.visible) { this.logger.flush(); } } - override show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void { + show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void { this.logger.flush(); - super.show(!!(typeof columnOrPreserveFocus === 'boolean' ? columnOrPreserveFocus : preserveFocus)); + this.proxy.$reveal(this.id, !!(typeof columnOrPreserveFocus === 'boolean' ? columnOrPreserveFocus : preserveFocus)); } - private write(value: string): void { - this.offset += VSBuffer.fromString(value).byteLength; - this.logger.info(value); + hide(): void { + this.proxy.$close(this.id); + } + + protected log(level: LogLevel, message: string): void { + this.offset += VSBuffer.fromString(message).byteLength; + log(this.logger, level, message); + } + + override dispose(): void { + super.dispose(); + + if (!this._disposed) { + this.proxy.$dispose(this.id); + this._disposed = true; + } + } + +} + +class ExtHostLogOutputChannel extends ExtHostOutputChannel { + + override appendLine(value: string): void { + this.append(value); } } @@ -121,6 +105,7 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape { private readonly outputsLocation: URI; private outputDirectoryPromise: Thenable | undefined; + private readonly extensionLogDirectoryPromise = new Map>(); private namePool: number = 1; private readonly channels = new Map(); @@ -128,7 +113,7 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape { constructor( @IExtHostRpcService extHostRpc: IExtHostRpcService, - @IExtHostInitDataService initData: IExtHostInitDataService, + @IExtHostInitDataService private readonly initData: IExtHostInitDataService, @IExtHostConsumerFileSystem private readonly extHostFileSystem: IExtHostConsumerFileSystem, @IExtHostFileSystemInfo private readonly extHostFileSystemInfo: IExtHostFileSystemInfo, @ILoggerService private readonly loggerService: ILoggerService, @@ -163,25 +148,40 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape { } private async doCreateOutputChannel(name: string, languageId: string | undefined, extension: IExtensionDescription): Promise { - const file = await this.createLogFile(name); + if (!this.outputDirectoryPromise) { + this.outputDirectoryPromise = this.extHostFileSystem.value.createDirectory(this.outputsLocation).then(() => this.outputsLocation); + } + const outputDir = await this.outputDirectoryPromise; + const file = this.extHostFileSystemInfo.extUri.joinPath(outputDir, `${this.namePool++}-${name.replace(/[\\/:\*\?"<>\|]/g, '')}.log`); const logger = this.loggerService.createLogger(file, { always: true, donotRotate: true, donotUseFormatters: true }); const id = await this.proxy.$register(name, file, false, languageId, extension.identifier.value); return new ExtHostOutputChannel(id, name, logger, this.proxy, extension); } private async doCreateLogOutputChannel(name: string, extension: IExtensionDescription): Promise { - const file = await this.createLogFile(name); + 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 id = await this.proxy.$register(name, file, true, undefined, extension.identifier.value); return new ExtHostLogOutputChannel(id, name, logger, this.proxy, extension); } - private async createLogFile(name: string): Promise { - if (!this.outputDirectoryPromise) { - this.outputDirectoryPromise = this.extHostFileSystem.value.createDirectory(this.outputsLocation).then(() => this.outputsLocation); + private createExtensionLogDirectory(extension: IExtensionDescription): Thenable { + let extensionLogDirectoryPromise = this.extensionLogDirectoryPromise.get(extension.identifier.value); + if (!extensionLogDirectoryPromise) { + const extensionLogDirectory = this.extHostFileSystemInfo.extUri.joinPath(this.initData.logsLocation, extension.identifier.value); + this.extensionLogDirectoryPromise.set(extension.identifier.value, extensionLogDirectoryPromise = (async () => { + try { + await this.extHostFileSystem.value.createDirectory(extensionLogDirectory); + } catch (err) { + if (toFileSystemProviderErrorCode(err) !== FileSystemProviderErrorCode.FileExists) { + throw err; + } + } + return extensionLogDirectory; + })()); } - const outputDir = await this.outputDirectoryPromise; - return this.extHostFileSystemInfo.extUri.joinPath(outputDir, `${this.namePool++}-${name.replace(/[\\/:\*\?"<>\|]/g, '')}.log`); + return extensionLogDirectoryPromise; } private createExtHostOutputChannel(name: string, channelPromise: Promise): vscode.OutputChannel { @@ -232,38 +232,26 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape { } }; return { - get name(): string { return name; }, - appendLine(value: string): void { + ...this.createExtHostOutputChannel(name, channelPromise), + trace(value: string, ...args: any[]): void { validate(); - channelPromise.then(channel => channel.appendLine(value)); - }, - trace(value: string): void { - validate(); - channelPromise.then(channel => channel.info(value)); - }, - debug(value: string): void { - validate(); - channelPromise.then(channel => channel.debug(value)); + channelPromise.then(channel => channel.info(value, ...args)); }, - info(value: string): void { + debug(value: string, ...args: any[]): void { validate(); - channelPromise.then(channel => channel.info(value)); + channelPromise.then(channel => channel.debug(value, ...args)); }, - warn(value: string): void { + info(value: string, ...args: any[]): void { validate(); - channelPromise.then(channel => channel.warn(value)); + channelPromise.then(channel => channel.info(value, ...args)); }, - error(value: Error | string): void { + warn(value: string, ...args: any[]): void { validate(); - channelPromise.then(channel => channel.error(value)); + channelPromise.then(channel => channel.warn(value, ...args)); }, - show(preserveFocus?: boolean): void { + error(value: Error | string, ...args: any[]): void { validate(); - channelPromise.then(channel => channel.show(preserveFocus)); - }, - hide(): void { - validate(); - channelPromise.then(channel => channel.hide()); + channelPromise.then(channel => channel.error(value, ...args)); }, dispose(): void { disposed = true; diff --git a/src/vscode-dts/vscode.proposed.extensionLog.d.ts b/src/vscode-dts/vscode.proposed.extensionLog.d.ts index 9122f0963a321..e575defff20a1 100644 --- a/src/vscode-dts/vscode.proposed.extensionLog.d.ts +++ b/src/vscode-dts/vscode.proposed.extensionLog.d.ts @@ -5,120 +5,50 @@ declare module 'vscode' { - export interface AbstractOutputChannel { - /** - * The human-readable name of this output channel. - */ - readonly name: string; - - /** - * Append the given value and a line feed character - * to the channel. - * - * @param value A string, falsy values will be printed. - */ - appendLine(value: string): void; - - /** - * Reveal this channel in the UI. - * - * @param preserveFocus When `true` the channel will not take focus. - */ - show(preserveFocus?: boolean): void; - - /** - * Hide this channel from the UI. - */ - hide(): void; - - /** - * Dispose and free associated resources. - */ - dispose(): void; - } - - /** - * An output channel is a container for readonly textual information. - * - * To get an instance of an `OutputChannel` use - * {@link window.createOutputChannel createOutputChannel}. - */ - export interface OutputChannel extends AbstractOutputChannel { - - /** - * Append the given value to the channel. - * - * @param value A string, falsy values will not be printed. - */ - append(value: string): void; - - /** - * Replaces all output from the channel with the given value. - * - * @param value A string, falsy values will not be printed. - */ - replace(value: string): void; - - /** - * Removes all output from the channel. - */ - clear(): void; - - /** - * Reveal this channel in the UI. - * - * @deprecated Use the overload with just one parameter (`show(preserveFocus?: boolean): void`). - * - * @param column This argument is **deprecated** and will be ignored. - * @param preserveFocus When `true` the channel will not take focus. - */ - show(column?: ViewColumn, preserveFocus?: boolean): void; - } - /** * A channel for containing log output. */ - export interface LogOutputChannel extends AbstractOutputChannel { + export interface LogOutputChannel extends OutputChannel { /** * Log the given trace message to the channel. * - * Messages are only printed when the user has enabled trace logging for the extension. + * Messages are only printed when the user has enabled trace logging. * * @param message trace message to log */ - trace(message: string): void; + trace(message: string, ...args: any[]): void; /** * Log the given debug message to the channel. * - * Messages are only printed when the user has enabled debug logging for the extension. + * Messages are only printed when the user has enabled debug logging. * * @param message debug message to log */ - debug(message: string): void; + debug(message: string, ...args: any[]): void; /** * Log the given info message to the channel. * - * Messages are only printed when the user has enabled info logging for the extension. + * Messages are only printed when the user has enabled info logging. * * @param message info message to log */ - info(message: string): void; + info(message: string, ...args: any[]): void; /** * Log the given warning message to the channel. * - * Messages are only printed when the user has enabled warn logging for the extension. + * Messages are only printed when the user has enabled warn logging. * * @param message warning message to log */ - warn(message: string): void; + warn(message: string, ...args: any[]): void; /** * Log the given error or error message to the channel. * - * Messages are only printed when the user has enabled error logging for the extension. + * Messages are only printed when the user has enabled error logging. * * @param error Error or error message to log */ - error(error: string | Error): void; + error(error: string | Error, ...args: any[]): void; } export namespace window {