diff --git a/extensions/jupyter-adapter/src/JupyterKernel.ts b/extensions/jupyter-adapter/src/JupyterKernel.ts index 387cdc065a8..f95c5fd29f0 100644 --- a/extensions/jupyter-adapter/src/JupyterKernel.ts +++ b/extensions/jupyter-adapter/src/JupyterKernel.ts @@ -299,7 +299,7 @@ export class JupyterKernel extends EventEmitter implements vscode.Disposable { // Establish a log channel for the kernel we're connecting to, if we // don't already have one (we will if we're restarting) if (!this._logChannel) { - this._logChannel = vscode.window.createOutputChannel(`Runtime: ${this._spec.display_name}`); + this._logChannel = positron.window.createRawLogOutputChannel(`Runtime: ${this._spec.display_name}`); } // Bind to the Jupyter session diff --git a/src/positron-dts/positron.d.ts b/src/positron-dts/positron.d.ts index ba547e952c6..5c03834d1c3 100644 --- a/src/positron-dts/positron.d.ts +++ b/src/positron-dts/positron.d.ts @@ -819,6 +819,20 @@ declare module 'positron' { * @return New preview panel. */ export function createPreviewPanel(viewType: string, title: string, preserveFocus?: boolean, options?: PreviewOptions): PreviewPanel; + + /** + * Create a log output channel from raw data. + * + * Variant of `createOutputChannel()` that creates a "raw log" output channel. + * Compared to a normal `LogOutputChannel`, this doesn't add timestamps or info + * level. It's meant for extensions that create fully formed log lines but still + * want to benefit from the colourised rendering of log output channels. + * + * @param name Human-readable string which will be used to represent the channel in the UI. + * + * @return New log output channel. + */ + export function createRawLogOutputChannel(name: string): vscode.OutputChannel; } namespace runtime { diff --git a/src/vs/platform/log/node/spdlogLog.ts b/src/vs/platform/log/node/spdlogLog.ts index 4f5f1bf8a8c..365d80a34fe 100644 --- a/src/vs/platform/log/node/spdlogLog.ts +++ b/src/vs/platform/log/node/spdlogLog.ts @@ -101,6 +101,15 @@ export class SpdLogLogger extends AbstractMessageLogger implements ILogger { } } + // --- Start Positron --- + // Override pattern to avoid adding timestamps etc + public setRawLogger() { + this._loggerCreationPromise.then(() => { + this._logger!.setPattern('%v'); + }); + } + // --- End Positron --- + protected log(level: LogLevel, message: string): void { if (this._logger) { log(this._logger, level, message); diff --git a/src/vs/workbench/api/common/extHostOutput.ts b/src/vs/workbench/api/common/extHostOutput.ts index 502dd47fd47..7aebf5095e6 100644 --- a/src/vs/workbench/api/common/extHostOutput.ts +++ b/src/vs/workbench/api/common/extHostOutput.ts @@ -41,6 +41,15 @@ class ExtHostOutputChannel extends AbstractMessageLogger implements vscode.LogOu this._register(logger.onDidChangeLogLevel(level => this.setLevel(level))); } + // --- Start Positron --- + public setRawLogger() { + let loggerUnchecked = this.logger as any; + if (loggerUnchecked.setRawLogger) { + loggerUnchecked.setRawLogger(); + }; + } + // --- End Positron --- + get logLevel(): LogLevel { return this.getLevel(); } @@ -161,6 +170,24 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape { return log ? this.createExtHostLogOutputChannel(name, logLevel ?? this.logService.getLevel(), >extHostOutputChannel) : this.createExtHostOutputChannel(name, >extHostOutputChannel); } + // --- Start Positron --- + // Variant of createOutputChannel() that creates a "raw log" output channel. + // Compared to a normal `LogOutputChannel`, this doesn't add timestamps or info + // level. It's meant for extensions that create fully formed log lines but still + // want to benefit from the colourised rendering of log output channels. + createRawLogOutputChannel(name: string, extension: IExtensionDescription): vscode.OutputChannel { + const channel = this.createOutputChannel(name, { log: true }, extension); + + // Set our custom raw log + const channelUnchecked = channel as any + if (channelUnchecked.setRawLogger) { + channelUnchecked.setRawLogger(); + } + + return channel; + } + // --- End Positron --- + private async doCreateOutputChannel(name: string, languageId: string | undefined, extension: IExtensionDescription): Promise { if (!this.outputDirectoryPromise) { this.outputDirectoryPromise = this.extHostFileSystem.value.createDirectory(this.outputsLocation).then(() => this.outputsLocation); @@ -216,6 +243,13 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape { validate(); channelPromise.then(channel => channel.appendLine(value)); }, + // --- Start Positron --- + // @ts-ignore + setRawLogger(): void { + validate(); + channelPromise.then(channel => channel.setRawLogger()); + }, + // --- End Positron --- clear(): void { validate(); channelPromise.then(channel => channel.clear()); diff --git a/src/vs/workbench/api/common/positron/extHost.positron.api.impl.ts b/src/vs/workbench/api/common/positron/extHost.positron.api.impl.ts index 508c79d94fb..087550c3830 100644 --- a/src/vs/workbench/api/common/positron/extHost.positron.api.impl.ts +++ b/src/vs/workbench/api/common/positron/extHost.positron.api.impl.ts @@ -18,6 +18,7 @@ import { ExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview'; import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguageFeatures'; +import { ExtHostOutputService } from 'vs/workbench/api/common/extHostOutput'; /** * Factory interface for creating an instance of the Positron API. @@ -45,10 +46,7 @@ export function createPositronApiFactoryAndRegisterActors(accessor: ServicesAcce // VS Code API can share a single instance of `ExtHostWebViews`, which is // necessary since the instance effectively needs to be a singleton. const extHostWebviews: ExtHostWebviews = rpcProtocol.getRaw(ExtHostContext.ExtHostWebviews); - if (!extHostWebviews) { - throw new Error('Could not retrieve ExtHostWebviews from the RPC protocol. ' + - ' The VS Code API must be created before the Positron API.'); - } + const extHostOutputService: ExtHostOutputService = rpcProtocol.getRaw(ExtHostContext.ExtHostOutputService); // Same deal for the `ExtHostLanguageFeatures` object const extHostLanguageFeatures: ExtHostLanguageFeatures = @@ -96,6 +94,9 @@ export function createPositronApiFactoryAndRegisterActors(accessor: ServicesAcce const window: typeof positron.window = { createPreviewPanel(viewType: string, title: string, preserveFocus?: boolean, options?: vscode.WebviewPanelOptions & vscode.WebviewOptions) { return extHostPreviewPanels.createPreviewPanel(extension, viewType, title, preserveFocus, options); + }, + createRawLogOutputChannel(name: string): vscode.OutputChannel { + return extHostOutputService.createRawLogOutputChannel(name, extension); } }; diff --git a/src/vs/workbench/services/extensions/common/rpcProtocol.ts b/src/vs/workbench/services/extensions/common/rpcProtocol.ts index 03ecba6fbbb..517c38f123c 100644 --- a/src/vs/workbench/services/extensions/common/rpcProtocol.ts +++ b/src/vs/workbench/services/extensions/common/rpcProtocol.ts @@ -256,7 +256,8 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { */ public getRaw(identifier: ProxyIdentifier): R { if (!this._locals[identifier.nid]) { - throw new Error(`Missing actor ${identifier.sid}`); + throw new Error(`Missing actor ${identifier.sid}.` + + ' The VS Code API must be created before the Positron API.'); } return this._locals[identifier.nid]; }