Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'

This commit is contained in:
Joe Previte
2020-12-15 15:52:33 -07:00
4649 changed files with 1311795 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILogService, DEFAULT_LOG_LEVEL, LogLevel, LogServiceAdapter } from 'vs/platform/log/common/log';
interface IAutomatedWindow {
codeAutomationLog(type: string, args: any[]): void;
}
/**
* A logger that is used when VSCode is running in the web with
* an automation such as playwright. We expect a global codeAutomationLog
* to be defined that we can use to log to.
*/
export class ConsoleLogInAutomationService extends LogServiceAdapter implements ILogService {
declare codeAutomationLog: any;
declare readonly _serviceBrand: undefined;
constructor(logLevel: LogLevel = DEFAULT_LOG_LEVEL) {
super({ consoleLog: (type, args) => this.consoleLog(type, args) }, logLevel);
}
private consoleLog(type: string, args: any[]): void {
const automatedWindow = window as unknown as IAutomatedWindow;
if (typeof automatedWindow.codeAutomationLog === 'function') {
automatedWindow.codeAutomationLog(type, args);
}
}
}

View File

@@ -0,0 +1,96 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILogService, LogLevel, AbstractLogService, DEFAULT_LOG_LEVEL } from 'vs/platform/log/common/log';
interface ILog {
level: LogLevel;
args: any[];
}
function getLogFunction(logger: ILogService, level: LogLevel): Function {
switch (level) {
case LogLevel.Trace: return logger.trace;
case LogLevel.Debug: return logger.debug;
case LogLevel.Info: return logger.info;
case LogLevel.Warning: return logger.warn;
case LogLevel.Error: return logger.error;
case LogLevel.Critical: return logger.critical;
default: throw new Error('Invalid log level');
}
}
export class BufferLogService extends AbstractLogService implements ILogService {
declare readonly _serviceBrand: undefined;
private buffer: ILog[] = [];
private _logger: ILogService | undefined = undefined;
constructor(logLevel: LogLevel = DEFAULT_LOG_LEVEL) {
super();
this.setLevel(logLevel);
this._register(this.onDidChangeLogLevel(level => {
if (this._logger) {
this._logger.setLevel(level);
}
}));
}
set logger(logger: ILogService) {
this._logger = logger;
for (const { level, args } of this.buffer) {
const fn = getLogFunction(logger, level);
fn.apply(logger, args);
}
this.buffer = [];
}
private _log(level: LogLevel, ...args: any[]): void {
if (this._logger) {
const fn = getLogFunction(this._logger, level);
fn.apply(this._logger, args);
} else if (this.getLevel() <= level) {
this.buffer.push({ level, args });
}
}
trace(message: string, ...args: any[]): void {
this._log(LogLevel.Trace, message, ...args);
}
debug(message: string, ...args: any[]): void {
this._log(LogLevel.Debug, message, ...args);
}
info(message: string, ...args: any[]): void {
this._log(LogLevel.Info, message, ...args);
}
warn(message: string, ...args: any[]): void {
this._log(LogLevel.Warning, message, ...args);
}
error(message: string | Error, ...args: any[]): void {
this._log(LogLevel.Error, message, ...args);
}
critical(message: string | Error, ...args: any[]): void {
this._log(LogLevel.Critical, message, ...args);
}
dispose(): void {
if (this._logger) {
this._logger.dispose();
}
}
flush(): void {
if (this._logger) {
this._logger.flush();
}
}
}

View File

@@ -0,0 +1,194 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILogService, LogLevel, AbstractLogService, ILoggerService, ILogger } from 'vs/platform/log/common/log';
import { URI } from 'vs/base/common/uri';
import { FileOperationError, FileOperationResult, IFileService, whenProviderRegistered } from 'vs/platform/files/common/files';
import { Queue } from 'vs/base/common/async';
import { VSBuffer } from 'vs/base/common/buffer';
import { dirname, joinPath, basename } from 'vs/base/common/resources';
import { Disposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { BufferLogService } from 'vs/platform/log/common/bufferLog';
const MAX_FILE_SIZE = 1024 * 1024 * 5;
export class FileLogService extends AbstractLogService implements ILogService {
declare readonly _serviceBrand: undefined;
private readonly initializePromise: Promise<void>;
private readonly queue: Queue<void>;
private backupIndex: number = 1;
constructor(
private readonly name: string,
private readonly resource: URI,
level: LogLevel,
@IFileService private readonly fileService: IFileService
) {
super();
this.setLevel(level);
this.queue = this._register(new Queue<void>());
this.initializePromise = this.initialize();
}
trace(): void {
if (this.getLevel() <= LogLevel.Trace) {
this._log(LogLevel.Trace, this.format(arguments));
}
}
debug(): void {
if (this.getLevel() <= LogLevel.Debug) {
this._log(LogLevel.Debug, this.format(arguments));
}
}
info(): void {
if (this.getLevel() <= LogLevel.Info) {
this._log(LogLevel.Info, this.format(arguments));
}
}
warn(): void {
if (this.getLevel() <= LogLevel.Warning) {
this._log(LogLevel.Warning, this.format(arguments));
}
}
error(): void {
if (this.getLevel() <= LogLevel.Error) {
const arg = arguments[0];
if (arg instanceof Error) {
const array = Array.prototype.slice.call(arguments) as any[];
array[0] = arg.stack;
this._log(LogLevel.Error, this.format(array));
} else {
this._log(LogLevel.Error, this.format(arguments));
}
}
}
critical(): void {
if (this.getLevel() <= LogLevel.Critical) {
this._log(LogLevel.Critical, this.format(arguments));
}
}
flush(): void {
}
log(level: LogLevel, args: any[]): void {
this._log(level, this.format(args));
}
private async initialize(): Promise<void> {
try {
await this.fileService.createFile(this.resource);
} catch (error) {
if ((<FileOperationError>error).fileOperationResult !== FileOperationResult.FILE_MODIFIED_SINCE) {
throw error;
}
}
}
private _log(level: LogLevel, message: string): void {
this.queue.queue(async () => {
await this.initializePromise;
let content = await this.loadContent();
if (content.length > MAX_FILE_SIZE) {
await this.fileService.writeFile(this.getBackupResource(), VSBuffer.fromString(content));
content = '';
}
content += `[${this.getCurrentTimestamp()}] [${this.name}] [${this.stringifyLogLevel(level)}] ${message}\n`;
await this.fileService.writeFile(this.resource, VSBuffer.fromString(content));
});
}
private getCurrentTimestamp(): string {
const toTwoDigits = (v: number) => v < 10 ? `0${v}` : v;
const toThreeDigits = (v: number) => v < 10 ? `00${v}` : v < 100 ? `0${v}` : v;
const currentTime = new Date();
return `${currentTime.getFullYear()}-${toTwoDigits(currentTime.getMonth() + 1)}-${toTwoDigits(currentTime.getDate())} ${toTwoDigits(currentTime.getHours())}:${toTwoDigits(currentTime.getMinutes())}:${toTwoDigits(currentTime.getSeconds())}.${toThreeDigits(currentTime.getMilliseconds())}`;
}
private getBackupResource(): URI {
this.backupIndex = this.backupIndex > 5 ? 1 : this.backupIndex;
return joinPath(dirname(this.resource), `${basename(this.resource)}_${this.backupIndex++}`);
}
private async loadContent(): Promise<string> {
try {
const content = await this.fileService.readFile(this.resource);
return content.value.toString();
} catch (e) {
return '';
}
}
private stringifyLogLevel(level: LogLevel): string {
switch (level) {
case LogLevel.Critical: return 'critical';
case LogLevel.Debug: return 'debug';
case LogLevel.Error: return 'error';
case LogLevel.Info: return 'info';
case LogLevel.Trace: return 'trace';
case LogLevel.Warning: return 'warning';
}
return '';
}
private format(args: any): string {
let result = '';
for (let i = 0; i < args.length; i++) {
let a = args[i];
if (typeof a === 'object') {
try {
a = JSON.stringify(a);
} catch (e) { }
}
result += (i > 0 ? ' ' : '') + a;
}
return result;
}
}
export class FileLoggerService extends Disposable implements ILoggerService {
declare readonly _serviceBrand: undefined;
private readonly loggers = new Map<string, ILogger>();
constructor(
@ILogService private logService: ILogService,
@IInstantiationService private instantiationService: IInstantiationService,
@IFileService private fileService: IFileService,
) {
super();
this._register(logService.onDidChangeLogLevel(level => this.loggers.forEach(logger => logger.setLevel(level))));
}
getLogger(resource: URI): ILogger {
let logger = this.loggers.get(resource.toString());
if (!logger) {
logger = new BufferLogService(this.logService.getLevel());
this.loggers.set(resource.toString(), logger);
whenProviderRegistered(resource, this.fileService).then(() => (<BufferLogService>logger).logger = this.instantiationService.createInstance(FileLogService, basename(resource), resource, this.logService.getLevel()));
}
return logger;
}
dispose(): void {
this.loggers.forEach(logger => logger.dispose());
this.loggers.clear();
super.dispose();
}
}

View File

@@ -0,0 +1,441 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator as createServiceDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { isWindows } from 'vs/base/common/platform';
import { Event, Emitter } from 'vs/base/common/event';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { LoggerChannelClient } from 'vs/platform/log/common/logIpc';
import { URI } from 'vs/base/common/uri';
import { toErrorMessage } from 'vs/base/common/errorMessage';
export const ILogService = createServiceDecorator<ILogService>('logService');
export const ILoggerService = createServiceDecorator<ILoggerService>('loggerService');
function now(): string {
return new Date().toISOString();
}
export enum LogLevel {
Trace,
Debug,
Info,
Warning,
Error,
Critical,
Off
}
export const DEFAULT_LOG_LEVEL: LogLevel = LogLevel.Info;
export interface ILogger extends IDisposable {
onDidChangeLogLevel: Event<LogLevel>;
getLevel(): LogLevel;
setLevel(level: LogLevel): void;
trace(message: string, ...args: any[]): void;
debug(message: string, ...args: any[]): void;
info(message: string, ...args: any[]): void;
warn(message: string, ...args: any[]): void;
error(message: string | Error, ...args: any[]): void;
critical(message: string | Error, ...args: any[]): void;
/**
* An operation to flush the contents. Can be synchronous.
*/
flush(): void;
}
export interface ILogService extends ILogger {
readonly _serviceBrand: undefined;
}
export interface ILoggerService {
readonly _serviceBrand: undefined;
getLogger(file: URI): ILogger;
}
export abstract class AbstractLogService extends Disposable {
private level: LogLevel = DEFAULT_LOG_LEVEL;
private readonly _onDidChangeLogLevel: Emitter<LogLevel> = this._register(new Emitter<LogLevel>());
readonly onDidChangeLogLevel: Event<LogLevel> = this._onDidChangeLogLevel.event;
setLevel(level: LogLevel): void {
if (this.level !== level) {
this.level = level;
this._onDidChangeLogLevel.fire(this.level);
}
}
getLevel(): LogLevel {
return this.level;
}
}
export class ConsoleLogMainService extends AbstractLogService implements ILogService {
declare readonly _serviceBrand: undefined;
private useColors: boolean;
constructor(logLevel: LogLevel = DEFAULT_LOG_LEVEL) {
super();
this.setLevel(logLevel);
this.useColors = !isWindows;
}
trace(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Trace) {
if (this.useColors) {
console.log(`\x1b[90m[main ${now()}]\x1b[0m`, message, ...args);
} else {
console.log(`[main ${now()}]`, message, ...args);
}
}
}
debug(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Debug) {
if (this.useColors) {
console.log(`\x1b[90m[main ${now()}]\x1b[0m`, message, ...args);
} else {
console.log(`[main ${now()}]`, message, ...args);
}
}
}
info(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Info) {
if (this.useColors) {
console.log(`\x1b[90m[main ${now()}]\x1b[0m`, message, ...args);
} else {
console.log(`[main ${now()}]`, message, ...args);
}
}
}
warn(message: string | Error, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Warning) {
if (this.useColors) {
console.warn(`\x1b[93m[main ${now()}]\x1b[0m`, message, ...args);
} else {
console.warn(`[main ${now()}]`, message, ...args);
}
}
}
error(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Error) {
if (this.useColors) {
console.error(`\x1b[91m[main ${now()}]\x1b[0m`, message, ...args);
} else {
console.error(`[main ${now()}]`, message, ...args);
}
}
}
critical(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Critical) {
if (this.useColors) {
console.error(`\x1b[90m[main ${now()}]\x1b[0m`, message, ...args);
} else {
console.error(`[main ${now()}]`, message, ...args);
}
}
}
dispose(): void {
// noop
}
flush(): void {
// noop
}
}
export class ConsoleLogService extends AbstractLogService implements ILogService {
declare readonly _serviceBrand: undefined;
constructor(logLevel: LogLevel = DEFAULT_LOG_LEVEL) {
super();
this.setLevel(logLevel);
}
trace(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Trace) {
console.log('%cTRACE', 'color: #888', message, ...args);
}
}
debug(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Debug) {
console.log('%cDEBUG', 'background: #eee; color: #888', message, ...args);
}
}
info(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Info) {
console.log('%c INFO', 'color: #33f', message, ...args);
}
}
warn(message: string | Error, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Warning) {
console.log('%c WARN', 'color: #993', message, ...args);
}
}
error(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Error) {
console.log('%c ERR', 'color: #f33', message, ...args);
}
}
critical(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Critical) {
console.log('%cCRITI', 'background: #f33; color: white', message, ...args);
}
}
dispose(): void {
// noop
}
flush(): void {
// noop
}
}
export class LogServiceAdapter extends AbstractLogService implements ILogService {
declare readonly _serviceBrand: undefined;
constructor(private readonly adapter: { consoleLog: (type: string, args: any[]) => void }, logLevel: LogLevel = DEFAULT_LOG_LEVEL) {
super();
this.setLevel(logLevel);
}
trace(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Trace) {
this.adapter.consoleLog('trace', [this.extractMessage(message), ...args]);
}
}
debug(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Debug) {
this.adapter.consoleLog('debug', [this.extractMessage(message), ...args]);
}
}
info(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Info) {
this.adapter.consoleLog('info', [this.extractMessage(message), ...args]);
}
}
warn(message: string | Error, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Warning) {
this.adapter.consoleLog('warn', [this.extractMessage(message), ...args]);
}
}
error(message: string | Error, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Error) {
this.adapter.consoleLog('error', [this.extractMessage(message), ...args]);
}
}
critical(message: string | Error, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Critical) {
this.adapter.consoleLog('critical', [this.extractMessage(message), ...args]);
}
}
private extractMessage(msg: string | Error): string {
if (typeof msg === 'string') {
return msg;
}
return toErrorMessage(msg, this.getLevel() <= LogLevel.Trace);
}
dispose(): void {
// noop
}
flush(): void {
// noop
}
}
export class ConsoleLogInMainService extends LogServiceAdapter implements ILogService {
declare readonly _serviceBrand: undefined;
constructor(client: LoggerChannelClient, logLevel: LogLevel = DEFAULT_LOG_LEVEL) {
super({ consoleLog: (type, args) => client.consoleLog(type, args) }, logLevel);
}
}
export class MultiplexLogService extends AbstractLogService implements ILogService {
declare readonly _serviceBrand: undefined;
constructor(private readonly logServices: ReadonlyArray<ILogService>) {
super();
if (logServices.length) {
this.setLevel(logServices[0].getLevel());
}
}
setLevel(level: LogLevel): void {
for (const logService of this.logServices) {
logService.setLevel(level);
}
super.setLevel(level);
}
trace(message: string, ...args: any[]): void {
for (const logService of this.logServices) {
logService.trace(message, ...args);
}
}
debug(message: string, ...args: any[]): void {
for (const logService of this.logServices) {
logService.debug(message, ...args);
}
}
info(message: string, ...args: any[]): void {
for (const logService of this.logServices) {
logService.info(message, ...args);
}
}
warn(message: string, ...args: any[]): void {
for (const logService of this.logServices) {
logService.warn(message, ...args);
}
}
error(message: string | Error, ...args: any[]): void {
for (const logService of this.logServices) {
logService.error(message, ...args);
}
}
critical(message: string | Error, ...args: any[]): void {
for (const logService of this.logServices) {
logService.critical(message, ...args);
}
}
flush(): void {
for (const logService of this.logServices) {
logService.flush();
}
}
dispose(): void {
for (const logService of this.logServices) {
logService.dispose();
}
}
}
export class DelegatedLogService extends Disposable implements ILogService {
declare readonly _serviceBrand: undefined;
constructor(private logService: ILogService) {
super();
this._register(logService);
}
get onDidChangeLogLevel(): Event<LogLevel> {
return this.logService.onDidChangeLogLevel;
}
setLevel(level: LogLevel): void {
this.logService.setLevel(level);
}
getLevel(): LogLevel {
return this.logService.getLevel();
}
trace(message: string, ...args: any[]): void {
this.logService.trace(message, ...args);
}
debug(message: string, ...args: any[]): void {
this.logService.debug(message, ...args);
}
info(message: string, ...args: any[]): void {
this.logService.info(message, ...args);
}
warn(message: string, ...args: any[]): void {
this.logService.warn(message, ...args);
}
error(message: string | Error, ...args: any[]): void {
this.logService.error(message, ...args);
}
critical(message: string | Error, ...args: any[]): void {
this.logService.critical(message, ...args);
}
flush(): void {
this.logService.flush();
}
}
export class NullLogService implements ILogService {
declare readonly _serviceBrand: undefined;
readonly onDidChangeLogLevel: Event<LogLevel> = new Emitter<LogLevel>().event;
setLevel(level: LogLevel): void { }
getLevel(): LogLevel { return LogLevel.Info; }
trace(message: string, ...args: any[]): void { }
debug(message: string, ...args: any[]): void { }
info(message: string, ...args: any[]): void { }
warn(message: string, ...args: any[]): void { }
error(message: string | Error, ...args: any[]): void { }
critical(message: string | Error, ...args: any[]): void { }
dispose(): void { }
flush(): void { }
}
export function getLogLevel(environmentService: IEnvironmentService): LogLevel {
if (environmentService.verbose) {
return LogLevel.Trace;
}
if (typeof environmentService.logLevel === 'string') {
const logLevel = environmentService.logLevel.toLowerCase();
switch (logLevel) {
case 'trace':
return LogLevel.Trace;
case 'debug':
return LogLevel.Debug;
case 'info':
return LogLevel.Info;
case 'warn':
return LogLevel.Warning;
case 'error':
return LogLevel.Error;
case 'critical':
return LogLevel.Critical;
case 'off':
return LogLevel.Off;
}
}
return DEFAULT_LOG_LEVEL;
}

View File

@@ -0,0 +1,88 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { LogLevel, ILogService, DelegatedLogService } from 'vs/platform/log/common/log';
import { Event } from 'vs/base/common/event';
export class LoggerChannel implements IServerChannel {
onDidChangeLogLevel: Event<LogLevel>;
constructor(private service: ILogService) {
this.onDidChangeLogLevel = Event.buffer(service.onDidChangeLogLevel, true);
}
listen(_: unknown, event: string): Event<any> {
switch (event) {
case 'onDidChangeLogLevel': return this.onDidChangeLogLevel;
}
throw new Error(`Event not found: ${event}`);
}
call(_: unknown, command: string, arg?: any): Promise<any> {
switch (command) {
case 'setLevel': this.service.setLevel(arg); return Promise.resolve();
case 'consoleLog': this.consoleLog(arg[0], arg[1]); return Promise.resolve();
}
throw new Error(`Call not found: ${command}`);
}
private consoleLog(severity: string, args: string[]): void {
let consoleFn = console.log;
switch (severity) {
case 'error':
consoleFn = console.error;
break;
case 'warn':
consoleFn = console.warn;
break;
case 'info':
consoleFn = console.info;
break;
}
consoleFn.call(console, ...args);
}
}
export class LoggerChannelClient {
constructor(private channel: IChannel) { }
get onDidChangeLogLevel(): Event<LogLevel> {
return this.channel.listen('onDidChangeLogLevel');
}
setLevel(level: LogLevel): void {
LoggerChannelClient.setLevel(this.channel, level);
}
public static setLevel(channel: IChannel, level: LogLevel): Promise<void> {
return channel.call('setLevel', level);
}
consoleLog(severity: string, args: string[]): void {
this.channel.call('consoleLog', [severity, args]);
}
}
export class FollowerLogService extends DelegatedLogService implements ILogService {
declare readonly _serviceBrand: undefined;
constructor(private parent: LoggerChannelClient, logService: ILogService) {
super(logService);
this._register(parent.onDidChangeLogLevel(level => logService.setLevel(level)));
}
setLevel(level: LogLevel): void {
super.setLevel(level);
this.parent.setLevel(level);
}
}

View File

@@ -0,0 +1,50 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILogService, ILoggerService, ILogger } from 'vs/platform/log/common/log';
import { Disposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { basename, extname, dirname } from 'vs/base/common/resources';
import { Schemas } from 'vs/base/common/network';
import { FileLogService } from 'vs/platform/log/common/fileLogService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { SpdLogService } from 'vs/platform/log/node/spdlogService';
export class LoggerService extends Disposable implements ILoggerService {
declare readonly _serviceBrand: undefined;
private readonly loggers = new Map<string, ILogger>();
constructor(
@ILogService private logService: ILogService,
@IInstantiationService private instantiationService: IInstantiationService,
) {
super();
this._register(logService.onDidChangeLogLevel(level => this.loggers.forEach(logger => logger.setLevel(level))));
}
getLogger(resource: URI): ILogger {
let logger = this.loggers.get(resource.toString());
if (!logger) {
if (resource.scheme === Schemas.file) {
const baseName = basename(resource);
const ext = extname(resource);
logger = new SpdLogService(baseName.substring(0, baseName.length - ext.length), dirname(resource).fsPath, this.logService.getLevel());
} else {
logger = this.instantiationService.createInstance(FileLogService, basename(resource), resource, this.logService.getLevel());
}
this.loggers.set(resource.toString(), logger);
}
return logger;
}
dispose(): void {
this.loggers.forEach(logger => logger.dispose());
this.loggers.clear();
super.dispose();
}
}

View File

@@ -0,0 +1,173 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'vs/base/common/path';
import { ILogService, LogLevel, AbstractLogService } from 'vs/platform/log/common/log';
import * as spdlog from 'spdlog';
async function createSpdLogLogger(processName: string, logsFolder: string): Promise<spdlog.RotatingLogger | null> {
// Do not crash if spdlog cannot be loaded
try {
const _spdlog = await import('spdlog');
_spdlog.setAsyncMode(8192, 500);
const logfilePath = path.join(logsFolder, `${processName}.log`);
return _spdlog.createRotatingLoggerAsync(processName, logfilePath, 1024 * 1024 * 5, 6);
} catch (e) {
console.error(e);
}
return null;
}
export function createRotatingLogger(name: string, filename: string, filesize: number, filecount: number): spdlog.RotatingLogger {
const _spdlog: typeof spdlog = require.__$__nodeRequire('spdlog');
return _spdlog.createRotatingLogger(name, filename, filesize, filecount);
}
interface ILog {
level: LogLevel;
message: string;
}
function log(logger: spdlog.RotatingLogger, level: LogLevel, message: string): void {
switch (level) {
case LogLevel.Trace: logger.trace(message); break;
case LogLevel.Debug: logger.debug(message); break;
case LogLevel.Info: logger.info(message); break;
case LogLevel.Warning: logger.warn(message); break;
case LogLevel.Error: logger.error(message); break;
case LogLevel.Critical: logger.critical(message); break;
default: throw new Error('Invalid log level');
}
}
export class SpdLogService extends AbstractLogService implements ILogService {
declare readonly _serviceBrand: undefined;
private buffer: ILog[] = [];
private _loggerCreationPromise: Promise<void> | undefined = undefined;
private _logger: spdlog.RotatingLogger | undefined;
constructor(private readonly name: string, private readonly logsFolder: string, level: LogLevel) {
super();
this.setLevel(level);
this._createSpdLogLogger();
this._register(this.onDidChangeLogLevel(level => {
if (this._logger) {
this._logger.setLevel(level);
}
}));
}
private _createSpdLogLogger(): Promise<void> {
if (!this._loggerCreationPromise) {
this._loggerCreationPromise = createSpdLogLogger(this.name, this.logsFolder)
.then(logger => {
if (logger) {
this._logger = logger;
this._logger.setLevel(this.getLevel());
for (const { level, message } of this.buffer) {
log(this._logger, level, message);
}
this.buffer = [];
}
});
}
return this._loggerCreationPromise;
}
private _log(level: LogLevel, message: string): void {
if (this._logger) {
log(this._logger, level, message);
} else if (this.getLevel() <= level) {
this.buffer.push({ level, message });
}
}
trace(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Trace) {
this._log(LogLevel.Trace, this.format([message, ...args]));
}
}
debug(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Debug) {
this._log(LogLevel.Debug, this.format([message, ...args]));
}
}
info(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Info) {
this._log(LogLevel.Info, this.format([message, ...args]));
}
}
warn(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Warning) {
this._log(LogLevel.Warning, this.format([message, ...args]));
}
}
error(message: string | Error, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Error) {
if (message instanceof Error) {
const array = Array.prototype.slice.call(arguments) as any[];
array[0] = message.stack;
this._log(LogLevel.Error, this.format(array));
} else {
this._log(LogLevel.Error, this.format([message, ...args]));
}
}
}
critical(message: string | Error, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Critical) {
this._log(LogLevel.Critical, this.format([message, ...args]));
}
}
flush(): void {
if (this._logger) {
this._logger.flush();
} else if (this._loggerCreationPromise) {
this._loggerCreationPromise.then(() => this.flush());
}
}
dispose(): void {
if (this._logger) {
this.disposeLogger();
} else if (this._loggerCreationPromise) {
this._loggerCreationPromise.then(() => this.disposeLogger());
}
this._loggerCreationPromise = undefined;
}
private disposeLogger(): void {
if (this._logger) {
this._logger.drop();
this._logger = undefined;
}
}
private format(args: any): string {
let result = '';
for (let i = 0; i < args.length; i++) {
let a = args[i];
if (typeof a === 'object') {
try {
a = JSON.stringify(a);
} catch (e) { }
}
result += (i > 0 ? ' ' : '') + a;
}
return result;
}
}