mirror of
https://github.com/Microsoft/vscode
synced 2024-10-06 03:17:00 +00:00
Share console wrapping code
This commit is contained in:
parent
5e5e4c86ba
commit
938124008c
|
@ -9,7 +9,6 @@ import { getNLSConfiguration } from 'vs/server/node/remoteLanguagePacks';
|
|||
import { FileAccess } from 'vs/base/common/network';
|
||||
import { join, delimiter } from 'vs/base/common/path';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { IRemoteConsoleLog } from 'vs/base/common/console';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { createRandomIPCHandle, NodeSocket, WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net';
|
||||
import { getResolvedShellEnv } from 'vs/platform/shell/node/shellEnv';
|
||||
|
@ -18,7 +17,6 @@ import { IRemoteExtensionHostStartParams } from 'vs/platform/remote/common/remot
|
|||
import { IExtHostReadyMessage, IExtHostSocketMessage, IExtHostReduceGraceTimeMessage } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
|
||||
import { IServerEnvironmentService } from 'vs/server/node/serverEnvironmentService';
|
||||
import { IProcessEnvironment, isWindows } from 'vs/base/common/platform';
|
||||
import { logRemoteEntry } from 'vs/workbench/services/extensions/common/remoteConsoleUtil';
|
||||
import { removeDangerousEnvVariables } from 'vs/base/common/processes';
|
||||
import { IExtensionHostStatusService } from 'vs/server/node/extensionHostStatusService';
|
||||
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
|
||||
|
@ -271,14 +269,6 @@ export class ExtensionHostConnection {
|
|||
onStdout((e) => this._log(`<${pid}> ${e}`));
|
||||
onStderr((e) => this._log(`<${pid}><stderr> ${e}`));
|
||||
|
||||
|
||||
// Support logging from extension host
|
||||
this._extensionHostProcess.on('message', msg => {
|
||||
if (msg && (<IRemoteConsoleLog>msg).type === '__$console') {
|
||||
logRemoteEntry(this._logService, (<IRemoteConsoleLog>msg), `${this._logPrefix}<${pid}>`);
|
||||
}
|
||||
});
|
||||
|
||||
// Lifecycle
|
||||
this._extensionHostProcess.on('error', (err) => {
|
||||
this._logError(`<${pid}> Extension Host Process had an error`);
|
||||
|
|
|
@ -9,14 +9,59 @@ import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
|||
|
||||
export abstract class AbstractExtHostConsoleForwarder {
|
||||
|
||||
protected readonly _mainThreadConsole: MainThreadConsoleShape;
|
||||
private readonly _mainThreadConsole: MainThreadConsoleShape;
|
||||
private readonly _includeStack: boolean;
|
||||
private readonly _logNative: boolean;
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||
) {
|
||||
this._mainThreadConsole = extHostRpc.getProxy(MainContext.MainThreadConsole);
|
||||
this._includeStack = initData.consoleForward.includeStack;
|
||||
this._logNative = initData.consoleForward.logNative;
|
||||
|
||||
// Pass console logging to the outside so that we have it in the main side if told so
|
||||
this._wrapConsoleMethod('info', 'log');
|
||||
this._wrapConsoleMethod('log', 'log');
|
||||
this._wrapConsoleMethod('warn', 'warn');
|
||||
this._wrapConsoleMethod('error', 'error');
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a console message so that it is transmitted to the renderer. If
|
||||
* native logging is turned on, the original console message will be written
|
||||
* as well. This is needed since the console methods are "magic" in V8 and
|
||||
* are the only methods that allow later introspection of logged variables.
|
||||
*
|
||||
* The wrapped property is not defined with `writable: false` to avoid
|
||||
* throwing errors, but rather a no-op setting. See https://github.com/microsoft/vscode-extension-telemetry/issues/88
|
||||
*/
|
||||
private _wrapConsoleMethod(method: 'log' | 'info' | 'warn' | 'error', severity: 'log' | 'warn' | 'error') {
|
||||
const that = this;
|
||||
const original = console[method];
|
||||
|
||||
Object.defineProperty(console, method, {
|
||||
set: () => { },
|
||||
get: () => function () {
|
||||
that._handleConsoleCall(method, severity, original, arguments);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private _handleConsoleCall(method: 'log' | 'info' | 'warn' | 'error', severity: 'log' | 'warn' | 'error', original: (...args: any[]) => void, args: IArguments): void {
|
||||
this._mainThreadConsole.$logExtensionHostMessage({
|
||||
type: '__$console',
|
||||
severity,
|
||||
arguments: safeStringifyArgumentsToArray(args, this._includeStack)
|
||||
});
|
||||
if (this._logNative) {
|
||||
this._nativeConsoleLogMessage(method, original, args);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract _nativeConsoleLogMessage(method: 'log' | 'info' | 'warn' | 'error', original: (...args: any[]) => void, args: IArguments): void;
|
||||
|
||||
}
|
||||
|
||||
const MAX_LENGTH = 100000;
|
||||
|
@ -24,7 +69,7 @@ const MAX_LENGTH = 100000;
|
|||
/**
|
||||
* Prevent circular stringify and convert arguments to real array
|
||||
*/
|
||||
export function safeStringifyArgumentsToArray(args: IArguments, includeStack: boolean): string {
|
||||
function safeStringifyArgumentsToArray(args: IArguments, includeStack: boolean): string {
|
||||
const seen: any[] = [];
|
||||
const argsArray = [];
|
||||
|
||||
|
|
|
@ -3,84 +3,34 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { AbstractExtHostConsoleForwarder, safeStringifyArgumentsToArray } from 'vs/workbench/api/common/extHostConsoleForwarder';
|
||||
import { AbstractExtHostConsoleForwarder } from 'vs/workbench/api/common/extHostConsoleForwarder';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { NativeLogMarkers } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
|
||||
|
||||
const MAX_STREAM_BUFFER_LENGTH = 1024 * 1024;
|
||||
|
||||
export class ExtHostConsoleForwarder extends AbstractExtHostConsoleForwarder {
|
||||
|
||||
private _isMakingConsoleCall: boolean = false;
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||
) {
|
||||
super(extHostRpc, initData);
|
||||
|
||||
pipeLoggingToParent(initData.consoleForward.includeStack, initData.consoleForward.logNative);
|
||||
|
||||
// Use IPC messages to forward console-calls, note that the console is
|
||||
// already patched to use`process.send()`
|
||||
const nativeProcessSend = process.send!;
|
||||
// const mainThreadConsole = this._extHostContext.getProxy(MainContext.MainThreadConsole);
|
||||
process.send = (...args) => {
|
||||
if ((args as unknown[]).length === 0 || !args[0] || args[0].type !== '__$console') {
|
||||
return nativeProcessSend.apply(process, args);
|
||||
}
|
||||
this._mainThreadConsole.$logExtensionHostMessage(args[0]);
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// TODO@Alex: remove duplication
|
||||
function pipeLoggingToParent(includeStack: boolean, logNative: boolean) {
|
||||
const MAX_STREAM_BUFFER_LENGTH = 1024 * 1024;
|
||||
|
||||
function safeSend(arg: { type: string; severity: string; arguments: string }) {
|
||||
try {
|
||||
if (process.send) {
|
||||
process.send(arg);
|
||||
}
|
||||
} catch (error) {
|
||||
// Can happen if the parent channel is closed meanwhile
|
||||
}
|
||||
this._wrapStream('stderr', 'error');
|
||||
this._wrapStream('stdout', 'log');
|
||||
}
|
||||
|
||||
function safeSendConsoleMessage(severity: 'log' | 'warn' | 'error', args: string) {
|
||||
safeSend({ type: '__$console', severity, arguments: args });
|
||||
}
|
||||
|
||||
let isMakingConsoleCall = false;
|
||||
|
||||
/**
|
||||
* Wraps a console message so that it is transmitted to the renderer. If
|
||||
* native logging is turned on, the original console message will be written
|
||||
* as well. This is needed since the console methods are "magic" in V8 and
|
||||
* are the only methods that allow later introspection of logged variables.
|
||||
*
|
||||
* The wrapped property is not defined with `writable: false` to avoid
|
||||
* throwing errors, but rather a no-op setting. See https://github.com/microsoft/vscode-extension-telemetry/issues/88
|
||||
*/
|
||||
function wrapConsoleMethod(method: 'log' | 'info' | 'warn' | 'error', severity: 'log' | 'warn' | 'error') {
|
||||
if (logNative) {
|
||||
const original = console[method];
|
||||
const stream = method === 'error' || method === 'warn' ? process.stderr : process.stdout;
|
||||
Object.defineProperty(console, method, {
|
||||
set: () => { },
|
||||
get: () => function () {
|
||||
safeSendConsoleMessage(severity, safeStringifyArgumentsToArray(arguments, includeStack));
|
||||
isMakingConsoleCall = true;
|
||||
stream.write(`\n${NativeLogMarkers.Start}\n`);
|
||||
original.apply(console, arguments as any);
|
||||
stream.write(`\n${NativeLogMarkers.End}\n`);
|
||||
isMakingConsoleCall = false;
|
||||
},
|
||||
});
|
||||
} else {
|
||||
Object.defineProperty(console, method, {
|
||||
set: () => { },
|
||||
get: () => function () { safeSendConsoleMessage(severity, safeStringifyArgumentsToArray(arguments, includeStack)); },
|
||||
});
|
||||
}
|
||||
protected override _nativeConsoleLogMessage(method: 'log' | 'info' | 'warn' | 'error', original: (...args: any[]) => void, args: IArguments) {
|
||||
const stream = method === 'error' || method === 'warn' ? process.stderr : process.stdout;
|
||||
this._isMakingConsoleCall = true;
|
||||
stream.write(`\n${NativeLogMarkers.Start}\n`);
|
||||
original.apply(console, args as any);
|
||||
stream.write(`\n${NativeLogMarkers.End}\n`);
|
||||
this._isMakingConsoleCall = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,7 +39,7 @@ function pipeLoggingToParent(includeStack: boolean, logNative: boolean) {
|
|||
* as to console.log with complete lines so that they're made available
|
||||
* to the debugger/CLI.
|
||||
*/
|
||||
function wrapStream(streamName: 'stdout' | 'stderr', severity: 'log' | 'warn' | 'error') {
|
||||
private _wrapStream(streamName: 'stdout' | 'stderr', severity: 'log' | 'warn' | 'error') {
|
||||
const stream = process[streamName];
|
||||
const original = stream.write;
|
||||
|
||||
|
@ -98,7 +48,7 @@ function pipeLoggingToParent(includeStack: boolean, logNative: boolean) {
|
|||
Object.defineProperty(stream, 'write', {
|
||||
set: () => { },
|
||||
get: () => (chunk: Uint8Array | string, encoding?: BufferEncoding, callback?: (err?: Error) => void) => {
|
||||
if (!isMakingConsoleCall) {
|
||||
if (!this._isMakingConsoleCall) {
|
||||
buf += (chunk as any).toString(encoding);
|
||||
const eol = buf.length > MAX_STREAM_BUFFER_LENGTH ? buf.length : buf.lastIndexOf('\n');
|
||||
if (eol !== -1) {
|
||||
|
@ -111,13 +61,4 @@ function pipeLoggingToParent(includeStack: boolean, logNative: boolean) {
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Pass console logging to the outside so that we have it in the main side if told so
|
||||
wrapConsoleMethod('info', 'log');
|
||||
wrapConsoleMethod('log', 'log');
|
||||
wrapConsoleMethod('warn', 'warn');
|
||||
wrapConsoleMethod('error', 'error');
|
||||
|
||||
wrapStream('stderr', 'error');
|
||||
wrapStream('stdout', 'log');
|
||||
}
|
||||
|
|
22
src/vs/workbench/api/worker/extHostConsoleForwarder.ts
Normal file
22
src/vs/workbench/api/worker/extHostConsoleForwarder.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { AbstractExtHostConsoleForwarder } from 'vs/workbench/api/common/extHostConsoleForwarder';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
|
||||
export class ExtHostConsoleForwarder extends AbstractExtHostConsoleForwarder {
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||
) {
|
||||
super(extHostRpc, initData);
|
||||
}
|
||||
|
||||
protected override _nativeConsoleLogMessage(method: 'log' | 'info' | 'warn' | 'error', original: (...args: any[]) => void, args: IArguments) {
|
||||
original.apply(console, args as any);
|
||||
}
|
||||
}
|
|
@ -11,8 +11,7 @@ import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterc
|
|||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { MainContext, MainThreadConsoleShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { safeStringifyArgumentsToArray } from 'vs/workbench/api/common/extHostConsoleForwarder';
|
||||
import { ExtHostConsoleForwarder } from 'vs/workbench/api/worker/extHostConsoleForwarder';
|
||||
|
||||
class WorkerRequireInterceptor extends RequireInterceptor {
|
||||
|
||||
|
@ -40,8 +39,8 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
|
|||
private _fakeModules?: WorkerRequireInterceptor;
|
||||
|
||||
protected async _beforeAlmostReadyToRunExtensions(): Promise<void> {
|
||||
const mainThreadConsole = this._extHostContext.getProxy(MainContext.MainThreadConsole);
|
||||
wrapConsoleMethods(mainThreadConsole, this._initData.consoleForward.includeStack, this._initData.environment.isExtensionDevelopmentDebug);
|
||||
// make sure console.log calls make it to the render
|
||||
this._instaService.createInstance(ExtHostConsoleForwarder);
|
||||
|
||||
// initialize API and register actors
|
||||
const apiFactory = this._instaService.invokeFunction(createApiFactoryAndRegisterActors);
|
||||
|
@ -141,22 +140,3 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
|
|||
function ensureSuffix(path: string, suffix: string): string {
|
||||
return path.endsWith(suffix) ? path : path + suffix;
|
||||
}
|
||||
|
||||
// TODO@Alex: remove duplication
|
||||
// copied from bootstrap-fork.js
|
||||
function wrapConsoleMethods(service: MainThreadConsoleShape, includeStack: boolean, logNative: boolean) {
|
||||
wrapConsoleMethod('info', 'log');
|
||||
wrapConsoleMethod('log', 'log');
|
||||
wrapConsoleMethod('warn', 'warn');
|
||||
wrapConsoleMethod('error', 'error');
|
||||
|
||||
function wrapConsoleMethod(method: 'log' | 'info' | 'warn' | 'error', severity: 'log' | 'warn' | 'error') {
|
||||
const original = console[method];
|
||||
console[method] = function () {
|
||||
service.$logExtensionHostMessage({ type: '__$console', severity, arguments: safeStringifyArgumentsToArray(arguments, includeStack) });
|
||||
if (logNative) {
|
||||
original.apply(console, arguments as any);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,8 +11,6 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
|||
import * as objects from 'vs/base/common/objects';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IRemoteConsoleLog, log } from 'vs/base/common/console';
|
||||
import { logRemoteEntry, logRemoteEntryIfError } from 'vs/workbench/services/extensions/common/remoteConsoleUtil';
|
||||
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { BufferedEmitter } from 'vs/base/parts/ipc/common/ipc.net';
|
||||
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
|
||||
|
@ -309,13 +307,6 @@ export class SandboxLocalProcessExtensionHost implements IExtensionHost {
|
|||
}
|
||||
});
|
||||
|
||||
// Support logging from extension host
|
||||
this._extensionHostProcess.onMessage(msg => {
|
||||
if (msg && (<IRemoteConsoleLog>msg).type === '__$console') {
|
||||
this._logExtensionHostMessage(<IRemoteConsoleLog>msg);
|
||||
}
|
||||
});
|
||||
|
||||
// Lifecycle
|
||||
|
||||
this._extensionHostProcess.onError((e) => this._onExtHostProcessError(e.error));
|
||||
|
@ -491,17 +482,6 @@ export class SandboxLocalProcessExtensionHost implements IExtensionHost {
|
|||
};
|
||||
}
|
||||
|
||||
private _logExtensionHostMessage(entry: IRemoteConsoleLog) {
|
||||
if (this._isExtensionDevTestFromCli) {
|
||||
// If running tests from cli, log to the log service everything
|
||||
logRemoteEntry(this._logService, entry);
|
||||
} else {
|
||||
// Log to the log service only errors and log everything to local console
|
||||
logRemoteEntryIfError(this._logService, entry, 'Extension Host');
|
||||
log(entry, 'Extension Host');
|
||||
}
|
||||
}
|
||||
|
||||
private _onExtHostProcessError(_err: SerializedError): void {
|
||||
let err: any = _err;
|
||||
if (_err && _err.$isError) {
|
||||
|
|
Loading…
Reference in a new issue