diff --git a/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts b/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts index 82a517e288c..e0e07ea27ce 100644 --- a/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts @@ -18,6 +18,7 @@ import 'vs/workbench/api/node/apiCommands'; import './mainThreadClipboard'; import './mainThreadCommands'; import './mainThreadConfiguration'; +import './mainThreadConsole'; import './mainThreadDebugService'; import './mainThreadDecorations'; import './mainThreadDiagnostics'; diff --git a/src/vs/workbench/api/electron-browser/mainThreadConsole.ts b/src/vs/workbench/api/electron-browser/mainThreadConsole.ts new file mode 100644 index 00000000000..0f4e37223aa --- /dev/null +++ b/src/vs/workbench/api/electron-browser/mainThreadConsole.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; +import { MainContext, MainThreadConsoleShape, IExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IRemoteConsoleLog, log, parse } from 'vs/base/node/console'; +import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { IBroadcastService } from 'vs/platform/broadcast/electron-browser/broadcastService'; +import { EXTENSION_LOG_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost'; + +@extHostNamedCustomer(MainContext.MainThreadConsole) +export class MainThreadConsole implements MainThreadConsoleShape { + + private readonly _isExtensionDevHost: boolean; + private readonly _isExtensionDevTestFromCli: boolean; + + constructor( + extHostContext: IExtHostContext, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IWindowsService private readonly _windowsService: IWindowsService, + @IBroadcastService private readonly _broadcastService: IBroadcastService, + ) { + const devOpts = parseExtensionDevOptions(this._environmentService); + this._isExtensionDevHost = devOpts.isExtensionDevHost; + this._isExtensionDevTestFromCli = devOpts.isExtensionDevTestFromCli; + } + + dispose(): void { + // + } + + $logExtensionHostMessage(entry: IRemoteConsoleLog): void { + // Send to local console unless we run tests from cli + if (!this._isExtensionDevTestFromCli) { + log(entry, 'Extension Host'); + } + + // Log on main side if running tests from cli + if (this._isExtensionDevTestFromCli) { + this._windowsService.log(entry.severity, ...parse(entry).args); + } + + // Broadcast to other windows if we are in development mode + else if (!this._environmentService.isBuilt || this._isExtensionDevHost) { + this._broadcastService.broadcast({ + channel: EXTENSION_LOG_BROADCAST_CHANNEL, + payload: { + logEntry: entry, + debugId: this._environmentService.debugExtensionHost.debugId + } + }); + } + } +} diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 7935656d6ba..d9f02bbeb03 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -37,13 +37,14 @@ import { IAdapterDescriptor, IConfig, ITerminalSettings } from 'vs/workbench/par import { ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder'; import { ITerminalDimensions } from 'vs/workbench/parts/terminal/common/terminal'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { IRPCProtocol, ProxyIdentifier, createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId } from 'vs/workbench/services/extensions/node/proxyIdentifier'; +import { IRPCProtocol, createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId } from 'vs/workbench/services/extensions/node/proxyIdentifier'; import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress'; import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import * as vscode from 'vscode'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { IRemoteConsoleLog } from 'vs/base/node/console'; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; @@ -232,6 +233,10 @@ export interface MainThreadErrorsShape extends IDisposable { $onUnexpectedError(err: any | SerializedError): void; } +export interface MainThreadConsoleShape extends IDisposable { + $logExtensionHostMessage(msg: IRemoteConsoleLog): void; +} + export interface ISerializedRegExp { pattern: string; flags?: string; @@ -1068,10 +1073,11 @@ export interface ExtHostStorageShape { // --- proxy identifiers export const MainContext = { - MainThreadClipboard: >createMainId('MainThreadClipboard'), - MainThreadCommands: >createMainId('MainThreadCommands'), + MainThreadClipboard: createMainId('MainThreadClipboard'), + MainThreadCommands: createMainId('MainThreadCommands'), MainThreadComments: createMainId('MainThreadComments'), MainThreadConfiguration: createMainId('MainThreadConfiguration'), + MainThreadConsole: createMainId('MainThreadConsole'), MainThreadDebugService: createMainId('MainThreadDebugService'), MainThreadDecorations: createMainId('MainThreadDecorations'), MainThreadDiagnostics: createMainId('MainThreadDiagnostics'), diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts index 06730f4e594..ec9cd8be9e4 100644 --- a/src/vs/workbench/node/extensionHostMain.ts +++ b/src/vs/workbench/node/extensionHostMain.ts @@ -10,7 +10,7 @@ import { Counter } from 'vs/base/common/numbers'; import { URI, setUriThrowOnMissingScheme } from 'vs/base/common/uri'; import { IURITransformer } from 'vs/base/common/uriIpc'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; -import { IEnvironment, IInitData, MainContext } from 'vs/workbench/api/node/extHost.protocol'; +import { IEnvironment, IInitData, MainContext, MainThreadConsoleShape } from 'vs/workbench/api/node/extHost.protocol'; import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; @@ -67,6 +67,8 @@ export class ExtensionHostMain { const allowExit = !!this._environment.extensionTestsPath; // to support other test frameworks like Jasmin that use process.exit (https://github.com/Microsoft/vscode/issues/37708) patchProcess(allowExit); + this._patchPatchedConsole(rpcProtocol.getProxy(MainContext.MainThreadConsole)); + // services this._extHostLogService = new ExtHostLogService(initData.logLevel, initData.logsLocation.fsPath); this.disposables.push(this._extHostLogService); @@ -114,6 +116,18 @@ export class ExtensionHostMain { }); } + private _patchPatchedConsole(mainThreadConsole: MainThreadConsoleShape): void { + // The console is already patched to use `process.send()` + const nativeProcessSend = process.send; + process.send = (...args: any[]) => { + if (args.length === 0 || !args[0] || args[0].type !== '__$console') { + return nativeProcessSend.apply(process, args); + } + + mainThreadConsole.$logExtensionHostMessage(args[0]); + }; + } + terminate(): void { if (this._isTerminating) { // we are already shutting down...