提交 87b8402b 编写于 作者: M Matt Bierner

Add experimental dual TS server

Fixes #75866
上级 8ec25590
......@@ -591,6 +591,12 @@
"default": true,
"description": "%configuration.surveys.enabled%",
"scope": "window"
},
"typescript.experimental.useSeparateSyntaxServer": {
"type": "boolean",
"default": false,
"description": "%configuration.experimental.useSeparateSyntaxServer%",
"scope": "window"
}
}
},
......
......@@ -49,6 +49,7 @@
"typescript.problemMatchers.tsc.label": "TypeScript problems",
"typescript.problemMatchers.tscWatch.label": "TypeScript problems (watch mode)",
"configuration.suggest.paths": "Enable/disable suggestions for paths in import statements and require calls.",
"configuration.experimental.useSeparateSyntaxServer": "Enable/disable spawning a separate TypeScript server that can more quickly respond to syntax related operations, such as calculating folding or computing document symbols. Note that you must restart the TypeScript server after changing this setting. Requires using TypeScript 3.4.0 or newer in the workspace.",
"typescript.locale": "Sets the locale used to report JavaScript and TypeScript errors. Requires using TypeScript 2.6.0 or newer in the workspace. Default of `null` uses VS Code's locale.",
"javascript.implicitProjectConfig.experimentalDecorators": "Enable/disable `experimentalDecorators` for JavaScript files that are not part of a project. Existing jsconfig.json or tsconfig.json files override this setting. Requires using TypeScript 2.3.1 or newer in the workspace.",
"configuration.suggest.autoImports": "Enable/disable auto import suggestions. Requires using TypeScript 2.6.1 or newer in the workspace.",
......
......@@ -62,7 +62,7 @@ suite('Server', () => {
test('should send requests with increasing sequence numbers', async () => {
const process = new FakeServerProcess();
const server = new ProcessBasedTsServer(process, undefined, new PipeRequestCanceller(undefined, tracer), undefined!, NoopTelemetryReporter, tracer);
const server = new ProcessBasedTsServer('semantic', process, undefined, new PipeRequestCanceller('semantic', undefined, tracer), undefined!, NoopTelemetryReporter, tracer);
const onWrite1 = process.onWrite();
server.executeImpl('geterr', {}, { isAsync: false, token: nulToken, expectsResult: true });
......
......@@ -7,7 +7,7 @@ import * as fs from 'fs';
import * as stream from 'stream';
import * as vscode from 'vscode';
import * as Proto from '../protocol';
import { ServerResponse } from '../typescriptService';
import { ServerResponse, TypeScriptRequests } from '../typescriptService';
import { Disposable } from '../utils/dispose';
import TelemetryReporter from '../utils/telemetry';
import Tracer from '../utils/tracer';
......@@ -23,6 +23,7 @@ export interface OngoingRequestCanceller {
export class PipeRequestCanceller implements OngoingRequestCanceller {
public constructor(
private readonly _serverId: string,
private readonly _cancellationPipeName: string | undefined,
private readonly _tracer: Tracer,
) { }
......@@ -31,7 +32,7 @@ export class PipeRequestCanceller implements OngoingRequestCanceller {
if (!this._cancellationPipeName) {
return false;
}
this._tracer.logTrace(`TypeScript Server: trying to cancel ongoing request with sequence number ${seq}`);
this._tracer.logTrace(this._serverId, `TypeScript Server: trying to cancel ongoing request with sequence number ${seq}`);
try {
fs.writeFileSync(this._cancellationPipeName + seq, '');
} catch {
......@@ -51,9 +52,9 @@ export interface ITypeScriptServer {
kill(): void;
executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined;
executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>;
executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined;
executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined;
executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>;
executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined;
dispose(): void;
}
......@@ -75,6 +76,7 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe
private readonly _pendingResponses = new Set<number>();
constructor(
private readonly _serverId: string,
private readonly _process: TsServerProcess,
private readonly _tsServerLogFile: string | undefined,
private readonly _requestCanceller: OngoingRequestCanceller,
......@@ -136,11 +138,11 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe
const seq = (event as Proto.RequestCompletedEvent).body.request_seq;
const p = this._callbacks.fetch(seq);
if (p) {
this._tracer.traceRequestCompleted('requestCompleted', seq, p.startTime);
this._tracer.traceRequestCompleted(this._serverId, 'requestCompleted', seq, p.startTime);
p.onSuccess(undefined);
}
} else {
this._tracer.traceEvent(event);
this._tracer.traceEvent(this._serverId, event);
this._onEvent.fire(event);
}
break;
......@@ -156,7 +158,7 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe
private tryCancelRequest(seq: number, command: string): boolean {
try {
if (this._requestQueue.tryDeletePendingRequest(seq)) {
this._tracer.logTrace(`TypeScript Server: canceled request with sequence number ${seq}`);
this.logTrace(`Canceled request with sequence number ${seq}`);
return true;
}
......@@ -164,7 +166,7 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe
return true;
}
this._tracer.logTrace(`TypeScript Server: tried to cancel request with sequence number ${seq}. But request got already delivered.`);
this.logTrace(`Tried to cancel request with sequence number ${seq}. But request got already delivered.`);
return false;
} finally {
const callback = this.fetchCallback(seq);
......@@ -180,7 +182,7 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe
return;
}
this._tracer.traceResponse(response, callback.startTime);
this._tracer.traceResponse(this._serverId, response, callback.startTime);
if (response.success) {
callback.onSuccess(response);
} else if (response.message === 'No content available.') {
......@@ -191,9 +193,9 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe
}
}
public executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined;
public executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>;
public executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined {
public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined;
public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>;
public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined {
const request = this._requestQueue.createRequest(command, args);
const requestInfo: RequestItem = {
request,
......@@ -255,7 +257,7 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe
private sendRequest(requestItem: RequestItem): void {
const serverRequest = requestItem.request;
this._tracer.traceRequest(serverRequest, requestItem.expectsResponse, this._requestQueue.length);
this._tracer.traceRequest(this._serverId, serverRequest, requestItem.expectsResponse, this._requestQueue.length);
if (requestItem.expectsResponse && !requestItem.isAsync) {
this._pendingResponses.add(requestItem.request.seq);
......@@ -281,6 +283,10 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe
return callback;
}
private logTrace(message: string) {
this._tracer.logTrace(this._serverId, message);
}
private static readonly fenceCommands = new Set(['change', 'close', 'open', 'updateOpen']);
private static getQueueingType(
......@@ -294,3 +300,53 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe
}
}
export class SyntaxRoutingTsServer extends Disposable implements ITypeScriptServer {
public constructor(
private readonly syntaxServer: ITypeScriptServer,
private readonly semanticServer: ITypeScriptServer,
) {
super();
this._register(syntaxServer.onEvent(e => this._onEvent.fire(e)));
this._register(semanticServer.onEvent(e => this._onEvent.fire(e)));
this._register(semanticServer.onExit(e => this._onExit.fire(e)));
this._register(semanticServer.onError(e => this._onError.fire(e)));
}
private readonly _onEvent = this._register(new vscode.EventEmitter<Proto.Event>());
public readonly onEvent = this._onEvent.event;
private readonly _onExit = this._register(new vscode.EventEmitter<any>());
public readonly onExit = this._onExit.event;
private readonly _onError = this._register(new vscode.EventEmitter<any>());
public readonly onError = this._onError.event;
public get onReaderError() { return this.semanticServer.onReaderError; }
public get tsServerLogFile() { return this.semanticServer.tsServerLogFile; }
public kill(): void {
this.syntaxServer.kill();
this.semanticServer.kill();
}
private static readonly syntaxCommands = new Set<keyof TypeScriptRequests>(['navtree', 'getOutliningSpans', 'jsxClosingTag', 'selectionRange']);
private static readonly sharedCommands = new Set<keyof TypeScriptRequests>(['change', 'close', 'open', 'updateOpen', 'configure', 'configurePlugin']);
public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined;
public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>;
public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined {
if (SyntaxRoutingTsServer.syntaxCommands.has(command)) {
return this.syntaxServer.executeImpl(command, args, executeInfo);
} else if (SyntaxRoutingTsServer.sharedCommands.has(command)) {
this.syntaxServer.executeImpl(command, args, executeInfo);
return this.semanticServer.executeImpl(command, args, executeInfo);
} else {
return this.semanticServer.executeImpl(command, args, executeInfo);
}
}
}
......@@ -18,7 +18,7 @@ import { PluginManager } from '../utils/plugins';
import TelemetryReporter from '../utils/telemetry';
import Tracer from '../utils/tracer';
import { TypeScriptVersion, TypeScriptVersionProvider } from '../utils/versionProvider';
import { ITypeScriptServer, TsServerProcess, ProcessBasedTsServer, PipeRequestCanceller } from './server';
import { ITypeScriptServer, PipeRequestCanceller, ProcessBasedTsServer, SyntaxRoutingTsServer, TsServerProcess } from './server';
export class TypeScriptServerSpawner {
public constructor(
......@@ -34,6 +34,30 @@ export class TypeScriptServerSpawner {
version: TypeScriptVersion,
configuration: TypeScriptServiceConfiguration,
pluginManager: PluginManager
): ITypeScriptServer {
if (this.shouldUserSeparateSyntaxServer(version)) {
const syntaxServer = this.spawnProcessBasedTsServer('syntax', version, configuration, pluginManager, ['--syntaxOnly', '--disableAutomaticTypingAcquisition']);
const semanticServer = this.spawnProcessBasedTsServer('semantic', version, configuration, pluginManager, []);
return new SyntaxRoutingTsServer(syntaxServer, semanticServer);
}
return this.spawnProcessBasedTsServer('main', version, configuration, pluginManager, []);
}
private shouldUserSeparateSyntaxServer(version: TypeScriptVersion): boolean {
if (!version.version || version.version.lt(API.v340)) {
return false;
}
return vscode.workspace.getConfiguration('typescript')
.get<boolean>('experimental.useSeparateSyntaxServer', false);
}
private spawnProcessBasedTsServer(
serverId: string,
version: TypeScriptVersion,
configuration: TypeScriptServiceConfiguration,
pluginManager: PluginManager,
extraForkArgs: readonly string[],
): ITypeScriptServer {
const apiVersion = version.version || API.defaultVersion;
......@@ -41,20 +65,21 @@ export class TypeScriptServerSpawner {
if (TypeScriptServerSpawner.isLoggingEnabled(apiVersion, configuration)) {
if (tsServerLogFile) {
this._logger.info(`TSServer log file: ${tsServerLogFile}`);
this._logger.info(`<${serverId}> Log file: ${tsServerLogFile}`);
} else {
this._logger.error('Could not create TSServer log directory');
this._logger.error(`<${serverId}> Could not create log directory`);
}
}
this._logger.info('Forking TSServer');
const childProcess = electron.fork(version.tsServerPath, args, this.getForkOptions());
this._logger.info('Started TSServer');
this._logger.info(`<${serverId}> Forking...`);
const childProcess = electron.fork(version.tsServerPath, [...args, ...extraForkArgs], this.getForkOptions());
this._logger.info(`<${serverId}> Starting...`);
return new ProcessBasedTsServer(
serverId,
new ChildServerProcess(childProcess),
tsServerLogFile,
new PipeRequestCanceller(cancellationPipeName, this._tracer),
new PipeRequestCanceller(serverId, cancellationPipeName, this._tracer),
version,
this._telemetryReporter,
this._tracer);
......
......@@ -26,7 +26,7 @@ export namespace ServerResponse {
export type Response<T extends Proto.Response> = T | Cancelled | typeof NoContent;
}
export interface TypeScriptRequestTypes {
interface StandardTsServerRequests {
'applyCodeActionCommand': [Proto.ApplyCodeActionCommandRequestArgs, Proto.ApplyCodeActionCommandResponse];
'completionEntryDetails': [Proto.CompletionDetailsRequestArgs, Proto.CompletionDetailsResponse];
'completionInfo': [Proto.CompletionsRequestArgs, Proto.CompletionInfoResponse];
......@@ -59,6 +59,22 @@ export interface TypeScriptRequestTypes {
'typeDefinition': [Proto.FileLocationRequestArgs, Proto.TypeDefinitionResponse];
}
interface NoResponseTsServerRequests {
'open': [Proto.OpenRequestArgs, null];
'close': [Proto.FileRequestArgs];
'change': [Proto.ChangeRequestArgs, null];
'updateOpen': [Proto.UpdateOpenRequestArgs, null];
'compilerOptionsForInferredProjects': [Proto.SetCompilerOptionsForInferredProjectsArgs, null];
'reloadProjects': [null, null];
'configurePlugin': [Proto.ConfigurePluginRequest, Proto.ConfigurePluginResponse];
}
interface AsyncTsServerRequests {
'geterr': [Proto.GeterrRequestArgs, Proto.Response];
}
export type TypeScriptRequests = StandardTsServerRequests & NoResponseTsServerRequests & AsyncTsServerRequests;
export interface ITypeScriptServiceClient {
/**
* Convert a resource (VS Code) to a normalized path (TypeScript).
......@@ -100,19 +116,17 @@ export interface ITypeScriptServiceClient {
readonly logger: Logger;
readonly bufferSyncSupport: BufferSyncSupport;
execute<K extends keyof TypeScriptRequestTypes>(
execute<K extends keyof StandardTsServerRequests>(
command: K,
args: TypeScriptRequestTypes[K][0],
args: StandardTsServerRequests[K][0],
token: vscode.CancellationToken,
lowPriority?: boolean
): Promise<ServerResponse.Response<TypeScriptRequestTypes[K][1]>>;
executeWithoutWaitingForResponse(command: 'open', args: Proto.OpenRequestArgs): void;
executeWithoutWaitingForResponse(command: 'close', args: Proto.FileRequestArgs): void;
executeWithoutWaitingForResponse(command: 'change', args: Proto.ChangeRequestArgs): void;
executeWithoutWaitingForResponse(command: 'updateOpen', args: Proto.UpdateOpenRequestArgs): void;
executeWithoutWaitingForResponse(command: 'compilerOptionsForInferredProjects', args: Proto.SetCompilerOptionsForInferredProjectsArgs): void;
executeWithoutWaitingForResponse(command: 'reloadProjects', args: null): void;
): Promise<ServerResponse.Response<StandardTsServerRequests[K][1]>>;
executeWithoutWaitingForResponse<K extends keyof NoResponseTsServerRequests>(
command: K,
args: NoResponseTsServerRequests[K][0]
): void;
executeAsync(command: 'geterr', args: Proto.GeterrRequestArgs, token: vscode.CancellationToken): Promise<ServerResponse.Response<Proto.Response>>;
......
......@@ -11,7 +11,7 @@ import BufferSyncSupport from './features/bufferSyncSupport';
import { DiagnosticKind, DiagnosticsManager } from './features/diagnostics';
import * as Proto from './protocol';
import { ITypeScriptServer } from './tsServer/server';
import { ITypeScriptServiceClient, ServerResponse } from './typescriptService';
import { ITypeScriptServiceClient, ServerResponse, TypeScriptRequests } from './typescriptService';
import API from './utils/api';
import { TsServerLogLevel, TypeScriptServiceConfiguration } from './utils/configuration';
import { Disposable } from './utils/dispose';
......@@ -607,7 +607,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType
return undefined;
}
public execute(command: string, args: any, token: vscode.CancellationToken, lowPriority?: boolean): Promise<ServerResponse.Response<Proto.Response>> {
public execute(command: keyof TypeScriptRequests, args: any, token: vscode.CancellationToken, lowPriority?: boolean): Promise<ServerResponse.Response<Proto.Response>> {
return this.executeImpl(command, args, {
isAsync: false,
token,
......@@ -616,7 +616,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType
});
}
public executeWithoutWaitingForResponse(command: string, args: any): void {
public executeWithoutWaitingForResponse(command: keyof TypeScriptRequests, args: any): void {
this.executeImpl(command, args, {
isAsync: false,
token: undefined,
......@@ -624,7 +624,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType
});
}
public executeAsync(command: string, args: Proto.GeterrRequestArgs, token: vscode.CancellationToken): Promise<ServerResponse.Response<Proto.Response>> {
public executeAsync(command: keyof TypeScriptRequests, args: Proto.GeterrRequestArgs, token: vscode.CancellationToken): Promise<ServerResponse.Response<Proto.Response>> {
return this.executeImpl(command, args, {
isAsync: true,
token,
......@@ -632,9 +632,9 @@ export default class TypeScriptServiceClient extends Disposable implements IType
});
}
private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined;
private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>;
private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined {
private executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined;
private executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>;
private executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined {
this.bufferSyncSupport.beforeCommand(command);
const runningServerState = this.service();
return runningServerState.server.executeImpl(command, args, executeInfo);
......@@ -769,7 +769,6 @@ export default class TypeScriptServiceClient extends Disposable implements IType
this.logTelemetry(telemetryData.telemetryEventName, properties);
}
private configurePlugin(pluginName: string, configuration: {}): any {
if (this.apiVersion.gte(API.v314)) {
this.executeWithoutWaitingForResponse('configurePlugin', { pluginName, configuration });
......
......@@ -50,7 +50,7 @@ export default class Tracer {
return result;
}
public traceRequest(request: Proto.Request, responseExpected: boolean, queueLength: number): void {
public traceRequest(serverId: string, request: Proto.Request, responseExpected: boolean, queueLength: number): void {
if (this.trace === Trace.Off) {
return;
}
......@@ -58,10 +58,10 @@ export default class Tracer {
if (this.trace === Trace.Verbose && request.arguments) {
data = `Arguments: ${JSON.stringify(request.arguments, null, 4)}`;
}
this.logTrace(`Sending request: ${request.command} (${request.seq}). Response expected: ${responseExpected ? 'yes' : 'no'}. Current queue length: ${queueLength}`, data);
this.logTrace(serverId, `Sending request: ${request.command} (${request.seq}). Response expected: ${responseExpected ? 'yes' : 'no'}. Current queue length: ${queueLength}`, data);
}
public traceResponse(response: Proto.Response, startTime: number): void {
public traceResponse(serverId: string, response: Proto.Response, startTime: number): void {
if (this.trace === Trace.Off) {
return;
}
......@@ -69,17 +69,17 @@ export default class Tracer {
if (this.trace === Trace.Verbose && response.body) {
data = `Result: ${JSON.stringify(response.body, null, 4)}`;
}
this.logTrace(`Response received: ${response.command} (${response.request_seq}). Request took ${Date.now() - startTime} ms. Success: ${response.success} ${!response.success ? '. Message: ' + response.message : ''}`, data);
this.logTrace(serverId, `Response received: ${response.command} (${response.request_seq}). Request took ${Date.now() - startTime} ms. Success: ${response.success} ${!response.success ? '. Message: ' + response.message : ''}`, data);
}
public traceRequestCompleted(command: string, request_seq: number, startTime: number): any {
public traceRequestCompleted(serverId: string, command: string, request_seq: number, startTime: number): any {
if (this.trace === Trace.Off) {
return;
}
this.logTrace(`Async response received: ${command} (${request_seq}). Request took ${Date.now() - startTime} ms.`);
this.logTrace(serverId, `Async response received: ${command} (${request_seq}). Request took ${Date.now() - startTime} ms.`);
}
public traceEvent(event: Proto.Event): void {
public traceEvent(serverId: string, event: Proto.Event): void {
if (this.trace === Trace.Off) {
return;
}
......@@ -87,12 +87,12 @@ export default class Tracer {
if (this.trace === Trace.Verbose && event.body) {
data = `Data: ${JSON.stringify(event.body, null, 4)}`;
}
this.logTrace(`Event received: ${event.event} (${event.seq}).`, data);
this.logTrace(serverId, `Event received: ${event.event} (${event.seq}).`, data);
}
public logTrace(message: string, data?: any): void {
public logTrace(serverId: string, message: string, data?: any): void {
if (this.trace !== Trace.Off) {
this.logger.logLevel('Trace', message, data);
this.logger.logLevel('Trace', `<${serverId}> ${message}`, data);
}
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册