mirror of
https://github.com/coder/code-server.git
synced 2026-05-27 06:37:27 +02:00
Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'
This commit is contained in:
33
lib/vscode/src/vs/platform/log/browser/log.ts
Normal file
33
lib/vscode/src/vs/platform/log/browser/log.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
96
lib/vscode/src/vs/platform/log/common/bufferLog.ts
Normal file
96
lib/vscode/src/vs/platform/log/common/bufferLog.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
194
lib/vscode/src/vs/platform/log/common/fileLogService.ts
Normal file
194
lib/vscode/src/vs/platform/log/common/fileLogService.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
441
lib/vscode/src/vs/platform/log/common/log.ts
Normal file
441
lib/vscode/src/vs/platform/log/common/log.ts
Normal 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;
|
||||
}
|
||||
88
lib/vscode/src/vs/platform/log/common/logIpc.ts
Normal file
88
lib/vscode/src/vs/platform/log/common/logIpc.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
50
lib/vscode/src/vs/platform/log/node/loggerService.ts
Normal file
50
lib/vscode/src/vs/platform/log/node/loggerService.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
173
lib/vscode/src/vs/platform/log/node/spdlogService.ts
Normal file
173
lib/vscode/src/vs/platform/log/node/spdlogService.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user