Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/api/common/extHostConsoleForwarder.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { IStackArgument } from '../../../base/common/console.js';
7
import { safeStringify } from '../../../base/common/objects.js';
8
import { MainContext, MainThreadConsoleShape } from './extHost.protocol.js';
9
import { IExtHostInitDataService } from './extHostInitDataService.js';
10
import { IExtHostRpcService } from './extHostRpcService.js';
11
12
export abstract class AbstractExtHostConsoleForwarder {
13
14
private readonly _mainThreadConsole: MainThreadConsoleShape;
15
private readonly _includeStack: boolean;
16
private readonly _logNative: boolean;
17
18
constructor(
19
@IExtHostRpcService extHostRpc: IExtHostRpcService,
20
@IExtHostInitDataService initData: IExtHostInitDataService,
21
) {
22
this._mainThreadConsole = extHostRpc.getProxy(MainContext.MainThreadConsole);
23
this._includeStack = initData.consoleForward.includeStack;
24
this._logNative = initData.consoleForward.logNative;
25
26
// Pass console logging to the outside so that we have it in the main side if told so
27
this._wrapConsoleMethod('info', 'log');
28
this._wrapConsoleMethod('log', 'log');
29
this._wrapConsoleMethod('warn', 'warn');
30
this._wrapConsoleMethod('debug', 'debug');
31
this._wrapConsoleMethod('error', 'error');
32
}
33
34
/**
35
* Wraps a console message so that it is transmitted to the renderer. If
36
* native logging is turned on, the original console message will be written
37
* as well. This is needed since the console methods are "magic" in V8 and
38
* are the only methods that allow later introspection of logged variables.
39
*
40
* The wrapped property is not defined with `writable: false` to avoid
41
* throwing errors, but rather a no-op setting. See https://github.com/microsoft/vscode-extension-telemetry/issues/88
42
*/
43
private _wrapConsoleMethod(method: 'log' | 'info' | 'warn' | 'error' | 'debug', severity: 'log' | 'warn' | 'error' | 'debug') {
44
const that = this;
45
const original = console[method];
46
47
Object.defineProperty(console, method, {
48
set: () => { },
49
get: () => function () {
50
that._handleConsoleCall(method, severity, original, arguments);
51
},
52
});
53
}
54
55
private _handleConsoleCall(method: 'log' | 'info' | 'warn' | 'error' | 'debug', severity: 'log' | 'warn' | 'error' | 'debug', original: (...args: any[]) => void, args: IArguments): void {
56
this._mainThreadConsole.$logExtensionHostMessage({
57
type: '__$console',
58
severity,
59
arguments: safeStringifyArgumentsToArray(args, this._includeStack)
60
});
61
if (this._logNative) {
62
this._nativeConsoleLogMessage(method, original, args);
63
}
64
}
65
66
protected abstract _nativeConsoleLogMessage(method: 'log' | 'info' | 'warn' | 'error' | 'debug', original: (...args: any[]) => void, args: IArguments): void;
67
68
}
69
70
const MAX_LENGTH = 100000;
71
72
/**
73
* Prevent circular stringify and convert arguments to real array
74
*/
75
function safeStringifyArgumentsToArray(args: IArguments, includeStack: boolean): string {
76
const argsArray = [];
77
78
// Massage some arguments with special treatment
79
if (args.length) {
80
for (let i = 0; i < args.length; i++) {
81
let arg = args[i];
82
83
// Any argument of type 'undefined' needs to be specially treated because
84
// JSON.stringify will simply ignore those. We replace them with the string
85
// 'undefined' which is not 100% right, but good enough to be logged to console
86
if (typeof arg === 'undefined') {
87
arg = 'undefined';
88
}
89
90
// Any argument that is an Error will be changed to be just the error stack/message
91
// itself because currently cannot serialize the error over entirely.
92
else if (arg instanceof Error) {
93
const errorObj = arg;
94
if (errorObj.stack) {
95
arg = errorObj.stack;
96
} else {
97
arg = errorObj.toString();
98
}
99
}
100
101
argsArray.push(arg);
102
}
103
}
104
105
// Add the stack trace as payload if we are told so. We remove the message and the 2 top frames
106
// to start the stacktrace where the console message was being written
107
if (includeStack) {
108
const stack = new Error().stack;
109
if (stack) {
110
argsArray.push({ __$stack: stack.split('\n').slice(3).join('\n') } satisfies IStackArgument);
111
}
112
}
113
114
try {
115
const res = safeStringify(argsArray);
116
117
if (res.length > MAX_LENGTH) {
118
return 'Output omitted for a large object that exceeds the limits';
119
}
120
121
return res;
122
} catch (error) {
123
return `Output omitted for an object that cannot be inspected ('${error.toString()}')`;
124
}
125
}
126
127