|
|
@ -1,10 +1,22 @@
|
|
|
|
|
|
|
|
// 导入 FileSystemConfig 类型和 isFileSystemConfig 函数,用于处理文件系统配置
|
|
|
|
import { FileSystemConfig, isFileSystemConfig } from 'common/fileSystemConfig';
|
|
|
|
import { FileSystemConfig, isFileSystemConfig } from 'common/fileSystemConfig';
|
|
|
|
|
|
|
|
// 导入 vscode 模块,用于访问 VS Code 的 API
|
|
|
|
import * as vscode from 'vscode';
|
|
|
|
import * as vscode from 'vscode';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Since the Extension Development Host runs with debugger, we can use this to detect if we're debugging.
|
|
|
|
// Since the Extension Development Host runs with debugger, we can use this to detect if we're debugging.
|
|
|
|
// The only things it currently does is copying Logging messages to the console, while also enabling
|
|
|
|
// The only things it currently does is copying Logging messages to the console, while also enabling
|
|
|
|
// the webview (Settings UI) from trying a local dev server first instead of the pre-built version.
|
|
|
|
// the webview (Settings UI) from trying a local dev server first instead of the pre-built version.
|
|
|
|
|
|
|
|
// 定义一个名为 DEBUG 的布尔类型的变量,初始值为 false
|
|
|
|
export let DEBUG: boolean = false;
|
|
|
|
export let DEBUG: boolean = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 设置调试模式
|
|
|
|
|
|
|
|
* @param debug - 一个布尔值,指示是否启用调试模式
|
|
|
|
|
|
|
|
* @remarks
|
|
|
|
|
|
|
|
* 当设置为 true 时,会启用调试模式,并尝试安装一些调试工具。
|
|
|
|
|
|
|
|
* 如果设置为 false,则不会进行任何操作。
|
|
|
|
|
|
|
|
*/
|
|
|
|
export function setDebug(debug: boolean) {
|
|
|
|
export function setDebug(debug: boolean) {
|
|
|
|
console.warn(`[vscode-sshfs] Debug mode set to ${debug}`);
|
|
|
|
console.warn(`[vscode-sshfs] Debug mode set to ${debug}`);
|
|
|
|
DEBUG = debug;
|
|
|
|
DEBUG = debug;
|
|
|
@ -17,6 +29,7 @@ export function setDebug(debug: boolean) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建一个名为 'SSH FS' 的输出通道,用于在 VS Code 中显示日志信息
|
|
|
|
export const OUTPUT_CHANNEL = vscode.window.createOutputChannel('SSH FS');
|
|
|
|
export const OUTPUT_CHANNEL = vscode.window.createOutputChannel('SSH FS');
|
|
|
|
|
|
|
|
|
|
|
|
export interface LoggingOptions {
|
|
|
|
export interface LoggingOptions {
|
|
|
@ -26,6 +39,12 @@ export interface LoggingOptions {
|
|
|
|
* - `1`: Only report the name (or first line of stacktrace if missing)
|
|
|
|
* - `1`: Only report the name (or first line of stacktrace if missing)
|
|
|
|
* - `2`: Report name and stacktrace (if available) (default for `WARNING`/`ERROR`)
|
|
|
|
* - `2`: Report name and stacktrace (if available) (default for `WARNING`/`ERROR`)
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 报告日志记录器名称/堆栈跟踪的级别:
|
|
|
|
|
|
|
|
* - `0`:不报告任何内容(默认值为 `WARNING`/`ERROR`)
|
|
|
|
|
|
|
|
* - `1`:仅报告名称(如果缺少堆栈跟踪,则报告第一行)
|
|
|
|
|
|
|
|
* - `2`:报告名称和堆栈跟踪(如果可用)(默认值为 `WARNING`/`ERROR`)
|
|
|
|
|
|
|
|
*/
|
|
|
|
reportedFromLevel: number;
|
|
|
|
reportedFromLevel: number;
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Whether to output a stacktrace of the .info() call etc
|
|
|
|
* Whether to output a stacktrace of the .info() call etc
|
|
|
@ -35,12 +54,25 @@ export interface LoggingOptions {
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Defaults to `3` for `WARNING`, `5` for `ERROR` and `0` for everything else.
|
|
|
|
* Defaults to `3` for `WARNING`, `5` for `ERROR` and `0` for everything else.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 是否输出调用堆栈跟踪:
|
|
|
|
|
|
|
|
* - `0`:不输出堆栈跟踪
|
|
|
|
|
|
|
|
* - `-1`:输出整个堆栈跟踪
|
|
|
|
|
|
|
|
* - `N`:仅输出前 N 帧
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* 默认值为 `3`(`WARNING`),`5`(`ERROR`),其他级别为 `0`。
|
|
|
|
|
|
|
|
*/
|
|
|
|
callStacktrace: number;
|
|
|
|
callStacktrace: number;
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Used with `.callStacktrace` to skip the given amount of stacktraces in the beginning.
|
|
|
|
* Used with `.callStacktrace` to skip the given amount of stacktraces in the beginning.
|
|
|
|
* Useful when `.info()` etc is called from a helper function which itself isn't worth logging the stacktrace of.
|
|
|
|
* Useful when `.info()` etc is called from a helper function which itself isn't worth logging the stacktrace of.
|
|
|
|
* Defaults to `0` meaning no offset.
|
|
|
|
* Defaults to `0` meaning no offset.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 与 `.callStacktrace` 一起使用,用于在开始时跳过给定数量的堆栈跟踪。
|
|
|
|
|
|
|
|
* 当 `.info()` 等方法从一个辅助函数调用时,这个选项很有用,因为辅助函数本身不值得记录堆栈跟踪。
|
|
|
|
|
|
|
|
* 默认值为 `0`,表示没有偏移。
|
|
|
|
|
|
|
|
*/
|
|
|
|
callStacktraceOffset: number;
|
|
|
|
callStacktraceOffset: number;
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Used when the "message" to be logged is an Error object with a stack:
|
|
|
|
* Used when the "message" to be logged is an Error object with a stack:
|
|
|
@ -48,37 +80,93 @@ export interface LoggingOptions {
|
|
|
|
* - `-1`: Output the whole stack
|
|
|
|
* - `-1`: Output the whole stack
|
|
|
|
* - `N`: Only output the first N lines
|
|
|
|
* - `N`: Only output the first N lines
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 当要记录的“消息”是一个带有堆栈的 Error 对象时使用:
|
|
|
|
|
|
|
|
* - `0`:不输出堆栈(这是 `DEBUG` 和 `INFO` 的默认值)
|
|
|
|
|
|
|
|
* - `-1`:输出整个堆栈
|
|
|
|
|
|
|
|
* - `N`:仅输出前 N 行
|
|
|
|
|
|
|
|
*/
|
|
|
|
maxErrorStack: number;
|
|
|
|
maxErrorStack: number;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 定义了一个日志记录选项的常量,其中 callStacktrace 设置为 0,表示不输出堆栈跟踪信息。
|
|
|
|
export const LOGGING_NO_STACKTRACE: Partial<LoggingOptions> = { callStacktrace: 0 };
|
|
|
|
export const LOGGING_NO_STACKTRACE: Partial<LoggingOptions> = { callStacktrace: 0 };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 定义了一个日志记录选项的常量,其中 callStacktrace 设置为 1,表示只输出一行堆栈跟踪信息。
|
|
|
|
export const LOGGING_SINGLE_LINE_STACKTRACE: Partial<LoggingOptions> = { callStacktrace: 1 };
|
|
|
|
export const LOGGING_SINGLE_LINE_STACKTRACE: Partial<LoggingOptions> = { callStacktrace: 1 };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 检查错误对象是否包含 promiseCause 属性,该属性是一个错误对象,并且还有一个名为 promiseCauseName 的字符串属性
|
|
|
|
|
|
|
|
* @param error - 要检查的错误对象
|
|
|
|
|
|
|
|
* @returns 如果 error 对象包含 promiseCause 属性,则返回 true,否则返回 false
|
|
|
|
|
|
|
|
*/
|
|
|
|
function hasPromiseCause(error: Error): error is Error & { promiseCause: Error; promiseCauseName: string } {
|
|
|
|
function hasPromiseCause(error: Error): error is Error & { promiseCause: Error; promiseCauseName: string } {
|
|
|
|
return 'promiseCause' in error && (error as any).promiseCause instanceof Error;
|
|
|
|
return 'promiseCause' in error && (error as any).promiseCause instanceof Error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 定义了日志记录器的默认级别。
|
|
|
|
|
|
|
|
* 这些级别包括:
|
|
|
|
|
|
|
|
* - 'DEBUG':用于调试信息。
|
|
|
|
|
|
|
|
* - 'INFO':用于一般信息。
|
|
|
|
|
|
|
|
* - 'WARNING':用于警告信息。
|
|
|
|
|
|
|
|
* - 'ERROR':用于错误信息。
|
|
|
|
|
|
|
|
*/
|
|
|
|
export type LoggerDefaultLevels = 'DEBUG' | 'INFO' | 'WARNING' | 'ERROR';
|
|
|
|
export type LoggerDefaultLevels = 'DEBUG' | 'INFO' | 'WARNING' | 'ERROR';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 定义了一个特定类型的日志记录器的行为。
|
|
|
|
|
|
|
|
* 这个日志记录器具有以下属性和方法:
|
|
|
|
|
|
|
|
* - `logger`: 日志记录器的实例。
|
|
|
|
|
|
|
|
* - `type`: 日志记录的类型,是 `LoggerDefaultLevels` 联合类型的一个值。
|
|
|
|
|
|
|
|
* - `options`: 日志记录的选项,是 `LoggingOptions` 接口的一个部分。
|
|
|
|
|
|
|
|
* - `(error: Error, options?: Partial<LoggingOptions>): void`: 记录一个错误信息,可以指定额外的选项。
|
|
|
|
|
|
|
|
* - `(message: string, options?: Partial<LoggingOptions>): void`: 记录一个字符串信息,可以指定额外的选项。
|
|
|
|
|
|
|
|
* - `(template: TemplateStringsArray,...args: any[]): void`: 记录一个模板字符串信息,可以指定额外的选项。
|
|
|
|
|
|
|
|
* - `withOptions(options: Partial<LoggingOptions>): LoggerForType`: 返回一个新的 `LoggerForType` 实例,具有指定的选项。
|
|
|
|
|
|
|
|
*/
|
|
|
|
export interface LoggerForType {
|
|
|
|
export interface LoggerForType {
|
|
|
|
|
|
|
|
// 日志记录器的实例
|
|
|
|
logger: Logger;
|
|
|
|
logger: Logger;
|
|
|
|
|
|
|
|
// 日志记录的类型
|
|
|
|
type: LoggerDefaultLevels;
|
|
|
|
type: LoggerDefaultLevels;
|
|
|
|
|
|
|
|
// 日志记录的选项
|
|
|
|
options: Partial<LoggingOptions>;
|
|
|
|
options: Partial<LoggingOptions>;
|
|
|
|
|
|
|
|
// 记录一个错误信息,可以指定额外的选项
|
|
|
|
(error: Error, options?: Partial<LoggingOptions>): void;
|
|
|
|
(error: Error, options?: Partial<LoggingOptions>): void;
|
|
|
|
|
|
|
|
// 记录一个字符串信息,可以指定额外的选项
|
|
|
|
(message: string, options?: Partial<LoggingOptions>): void;
|
|
|
|
(message: string, options?: Partial<LoggingOptions>): void;
|
|
|
|
|
|
|
|
// 记录一个模板字符串信息,可以指定额外的选项
|
|
|
|
(template: TemplateStringsArray, ...args: any[]): void;
|
|
|
|
(template: TemplateStringsArray, ...args: any[]): void;
|
|
|
|
|
|
|
|
// 返回一个新的 LoggerForType 实例,具有指定的选项
|
|
|
|
withOptions(options: Partial<LoggingOptions>): LoggerForType;
|
|
|
|
withOptions(options: Partial<LoggingOptions>): LoggerForType;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class Logger {
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Logger 类用于记录日志信息
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
class Logger {
|
|
|
|
|
|
|
|
// 父 Logger 对象,用于在当前 Logger 无法处理日志时将其传递给父 Logger
|
|
|
|
protected parent?: Logger;
|
|
|
|
protected parent?: Logger;
|
|
|
|
|
|
|
|
// 堆栈跟踪信息,用于记录日志时提供更多的上下文信息
|
|
|
|
protected stack?: string;
|
|
|
|
protected stack?: string;
|
|
|
|
|
|
|
|
// 默认的日志记录选项
|
|
|
|
protected defaultLoggingOptions: LoggingOptions = {
|
|
|
|
protected defaultLoggingOptions: LoggingOptions = {
|
|
|
|
|
|
|
|
// 报告日志记录器名称/堆栈跟踪的级别
|
|
|
|
reportedFromLevel: 0,
|
|
|
|
reportedFromLevel: 0,
|
|
|
|
|
|
|
|
// 是否输出调用堆栈跟踪
|
|
|
|
callStacktrace: 0,
|
|
|
|
callStacktrace: 0,
|
|
|
|
|
|
|
|
// 在开始时跳过给定数量的堆栈跟踪
|
|
|
|
callStacktraceOffset: 0,
|
|
|
|
callStacktraceOffset: 0,
|
|
|
|
|
|
|
|
// 当要记录的“消息”是一个带有堆栈的 Error 对象时使用
|
|
|
|
maxErrorStack: 0,
|
|
|
|
maxErrorStack: 0,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 构造函数,用于初始化 Logger 对象
|
|
|
|
|
|
|
|
* @param name - 日志记录器的名称
|
|
|
|
|
|
|
|
* @param generateStack - 是否生成堆栈跟踪信息
|
|
|
|
|
|
|
|
*/
|
|
|
|
protected constructor(protected name?: string, generateStack: number | boolean = false) {
|
|
|
|
protected constructor(protected name?: string, generateStack: number | boolean = false) {
|
|
|
|
if (generateStack) {
|
|
|
|
if (generateStack) {
|
|
|
|
const len = typeof generateStack === 'number' ? generateStack : 1;
|
|
|
|
const len = typeof generateStack === 'number' ? generateStack : 1;
|
|
|
@ -86,11 +174,18 @@ class Logger {
|
|
|
|
this.stack = stack || '<stack unavailable>';
|
|
|
|
this.stack = stack || '<stack unavailable>';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 打印日志信息
|
|
|
|
|
|
|
|
* @param type - 日志类型
|
|
|
|
|
|
|
|
* @param message - 日志信息
|
|
|
|
|
|
|
|
* @param options - 日志记录选项
|
|
|
|
|
|
|
|
*/
|
|
|
|
protected doPrint(type: string, message: string, options: LoggingOptions) {
|
|
|
|
protected doPrint(type: string, message: string, options: LoggingOptions) {
|
|
|
|
const { reportedFromLevel } = options;
|
|
|
|
const { reportedFromLevel } = options;
|
|
|
|
// Calculate prefix
|
|
|
|
// 计算前缀
|
|
|
|
const prefix = this.name ? `[${this.name}] ` : '';
|
|
|
|
const prefix = this.name? `[${this.name}] ` : '';
|
|
|
|
// Calculate suffix
|
|
|
|
// 计算后缀
|
|
|
|
let suffix = '';
|
|
|
|
let suffix = '';
|
|
|
|
if (this.name && this.stack && reportedFromLevel >= 2) {
|
|
|
|
if (this.name && this.stack && reportedFromLevel >= 2) {
|
|
|
|
suffix = `\nReported by logger ${this.name}:\n${this.stack}`;
|
|
|
|
suffix = `\nReported by logger ${this.name}:\n${this.stack}`;
|
|
|
@ -101,24 +196,31 @@ class Logger {
|
|
|
|
} else if (this.stack && reportedFromLevel === 1) {
|
|
|
|
} else if (this.stack && reportedFromLevel === 1) {
|
|
|
|
suffix = `\nReported by logger:\n${this.stack.split('\n', 2)[0]}`;
|
|
|
|
suffix = `\nReported by logger:\n${this.stack.split('\n', 2)[0]}`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If there is a parent logger, pass the message with prefix/suffix on
|
|
|
|
// 如果有父 Logger,则将带有前缀/后缀的消息传递给父 Logger
|
|
|
|
if (this.parent) return this.parent.doPrint(type, `${prefix}${message}${suffix}`, options);
|
|
|
|
if (this.parent) return this.parent.doPrint(type, `${prefix}${message}${suffix}`, options);
|
|
|
|
// There is no parent, we're responsible for actually logging the message
|
|
|
|
// 没有父 Logger,我们负责实际记录消息
|
|
|
|
const space = ' '.repeat(Math.max(0, 8 - type.length));
|
|
|
|
const space = ' '.repeat(Math.max(0, 8 - type.length));
|
|
|
|
const msg = `[${type}]${space}${prefix}${message}${suffix}`
|
|
|
|
const msg = `[${type}]${space}${prefix}${message}${suffix}`
|
|
|
|
OUTPUT_CHANNEL.appendLine(msg);
|
|
|
|
OUTPUT_CHANNEL.appendLine(msg);
|
|
|
|
// VS Code issue where console.debug logs twice in the Debug Console
|
|
|
|
// VS Code 问题,console.debug 在调试控制台中记录两次
|
|
|
|
if (type.toLowerCase() === 'debug') type = 'log';
|
|
|
|
if (type.toLowerCase() === 'debug') type = 'log';
|
|
|
|
if (DEBUG) (console[type.toLowerCase()] || console.log).call(console, msg);
|
|
|
|
if (DEBUG) (console[type.toLowerCase()] || console.log).call(console, msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 格式化值
|
|
|
|
|
|
|
|
* @param value - 要格式化的值
|
|
|
|
|
|
|
|
* @param options - 日志记录选项
|
|
|
|
|
|
|
|
* @returns 格式化后的字符串
|
|
|
|
|
|
|
|
*/
|
|
|
|
protected formatValue(value: any, options: LoggingOptions): string {
|
|
|
|
protected formatValue(value: any, options: LoggingOptions): string {
|
|
|
|
if (typeof value === 'string') return value;
|
|
|
|
if (typeof value === 'string') return value;
|
|
|
|
if (value instanceof Error && value.stack) {
|
|
|
|
if (value instanceof Error && value.stack) {
|
|
|
|
// Format errors with stacktraces to display the JSON and the stacktrace if needed
|
|
|
|
// 格式化带有堆栈跟踪的错误,以显示 JSON 和堆栈跟踪(如果需要)
|
|
|
|
let result = `${value.name}: ${value.message}`;
|
|
|
|
let result = `${value.name}: ${value.message}`;
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
const json = JSON.stringify(value);
|
|
|
|
const json = JSON.stringify(value);
|
|
|
|
if (json !== '{}') result += `\nJSON: ${json}`;
|
|
|
|
if (json!== '{}') result += `\nJSON: ${json}`;
|
|
|
|
} finally { }
|
|
|
|
} finally { }
|
|
|
|
const { maxErrorStack } = options;
|
|
|
|
const { maxErrorStack } = options;
|
|
|
|
if (value.stack && maxErrorStack) {
|
|
|
|
if (value.stack && maxErrorStack) {
|
|
|
@ -128,7 +230,7 @@ class Logger {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
result += '\n' + stack;
|
|
|
|
result += '\n' + stack;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (maxErrorStack !== 0) for (let cause = value; hasPromiseCause(cause); cause = cause.promiseCause) {
|
|
|
|
if (maxErrorStack!== 0) for (let cause = value; hasPromiseCause(cause); cause = cause.promiseCause) {
|
|
|
|
let promiseStack = cause.promiseCause.stack?.split(/\n/g);
|
|
|
|
let promiseStack = cause.promiseCause.stack?.split(/\n/g);
|
|
|
|
if (!promiseStack) continue;
|
|
|
|
if (!promiseStack) continue;
|
|
|
|
if (maxErrorStack > 0) promiseStack = promiseStack.slice(1, maxErrorStack + 1);
|
|
|
|
if (maxErrorStack > 0) promiseStack = promiseStack.slice(1, maxErrorStack + 1);
|
|
|
@ -150,78 +252,182 @@ class Logger {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 打印日志信息
|
|
|
|
|
|
|
|
* @param type - 日志类型
|
|
|
|
|
|
|
|
* @param message - 日志信息
|
|
|
|
|
|
|
|
* @param partialOptions - 日志记录选项
|
|
|
|
|
|
|
|
*/
|
|
|
|
protected print(type: string, message: string | Error, partialOptions?: Partial<LoggingOptions>) {
|
|
|
|
protected print(type: string, message: string | Error, partialOptions?: Partial<LoggingOptions>) {
|
|
|
|
const options: LoggingOptions = { ...this.defaultLoggingOptions, ...partialOptions };
|
|
|
|
const options: LoggingOptions = {...this.defaultLoggingOptions,...partialOptions };
|
|
|
|
message = this.formatValue(message, options);
|
|
|
|
message = this.formatValue(message, options);
|
|
|
|
// Do we need to also output a stacktrace?
|
|
|
|
// 是否需要输出堆栈跟踪?
|
|
|
|
const { callStacktrace, callStacktraceOffset = 0 } = options;
|
|
|
|
const { callStacktrace, callStacktraceOffset = 0 } = options;
|
|
|
|
if (callStacktrace) {
|
|
|
|
if (callStacktrace) {
|
|
|
|
let stack = new Error().stack;
|
|
|
|
let stack = new Error().stack;
|
|
|
|
let split = stack && stack.split('\n');
|
|
|
|
let split = stack && stack.split('\n');
|
|
|
|
split = split && split.slice(callStacktraceOffset + 3, callStacktrace > 0 ? callStacktraceOffset + 3 + callStacktrace : undefined);
|
|
|
|
split = split && split.slice(callStacktraceOffset + 3, callStacktrace > 0? callStacktraceOffset + 3 + callStacktrace : undefined);
|
|
|
|
stack = split ? split.join('\n') : '<stack unavailable>';
|
|
|
|
stack = split? split.join('\n') : '<stack unavailable>';
|
|
|
|
message += `\nLogged at:\n${stack}`;
|
|
|
|
message += `\nLogged at:\n${stack}`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Start the (recursive parent-related) printing
|
|
|
|
// 开始(递归父相关)打印
|
|
|
|
this.doPrint(type.toUpperCase(), message, options as LoggingOptions)
|
|
|
|
this.doPrint(type.toUpperCase(), message, options as LoggingOptions)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 打印模板字符串日志信息
|
|
|
|
|
|
|
|
* @param type - 日志类型
|
|
|
|
|
|
|
|
* @param template - 模板字符串
|
|
|
|
|
|
|
|
* @param args - 模板字符串参数
|
|
|
|
|
|
|
|
* @param partialOptions - 日志记录选项
|
|
|
|
|
|
|
|
*/
|
|
|
|
protected printTemplate(type: string, template: TemplateStringsArray, args: any[], partialOptions?: Partial<LoggingOptions>) {
|
|
|
|
protected printTemplate(type: string, template: TemplateStringsArray, args: any[], partialOptions?: Partial<LoggingOptions>) {
|
|
|
|
const options: LoggingOptions = { ...this.defaultLoggingOptions, ...partialOptions };
|
|
|
|
const options: LoggingOptions = { ...this.defaultLoggingOptions, ...partialOptions };
|
|
|
|
options.callStacktraceOffset = (options.callStacktraceOffset || 0) + 1;
|
|
|
|
options.callStacktraceOffset = (options.callStacktraceOffset || 0) + 1;
|
|
|
|
this.print(type, template.reduce((acc, part, i) => acc + part + (i < args.length ? this.formatValue(args[i], options) : ''), ''), partialOptions);
|
|
|
|
this.print(type, template.reduce((acc, part, i) => acc + part + (i < args.length ? this.formatValue(args[i], options) : ''), ''), partialOptions);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 创建一个新的 Logger 对象,该对象是当前 Logger 的子 Logger
|
|
|
|
|
|
|
|
* @param name - 子 Logger 的名称
|
|
|
|
|
|
|
|
* @param generateStack - 是否生成堆栈跟踪信息
|
|
|
|
|
|
|
|
* @returns 新的 Logger 对象
|
|
|
|
|
|
|
|
*/
|
|
|
|
public scope(name?: string, generateStack: number | boolean = false) {
|
|
|
|
public scope(name?: string, generateStack: number | boolean = false) {
|
|
|
|
const logger = new Logger(name, generateStack);
|
|
|
|
const logger = new Logger(name, generateStack);
|
|
|
|
logger.parent = this;
|
|
|
|
logger.parent = this;
|
|
|
|
return logger;
|
|
|
|
return logger;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 包装日志类型
|
|
|
|
|
|
|
|
* @param type - 日志类型
|
|
|
|
|
|
|
|
* @param options - 日志记录选项
|
|
|
|
|
|
|
|
* @returns 包装后的日志记录器
|
|
|
|
|
|
|
|
*/
|
|
|
|
public wrapType(type: LoggerDefaultLevels, options: Partial<LoggingOptions> = {}): LoggerForType {
|
|
|
|
public wrapType(type: LoggerDefaultLevels, options: Partial<LoggingOptions> = {}): LoggerForType {
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 定义一个函数,它接受一个消息参数,可以是字符串、错误对象或模板字符串数组,并接受一个可选的部分日志记录选项参数
|
|
|
|
|
|
|
|
* 如果消息是字符串或错误对象,则调用 logger 的 print 方法记录日志
|
|
|
|
|
|
|
|
* 如果消息是模板字符串数组,则调用 logger 的 printTemplate 方法记录日志
|
|
|
|
|
|
|
|
* 如果消息既不是字符串也不是错误对象也不是模板字符串数组,则记录一个错误信息
|
|
|
|
|
|
|
|
* @param message - 要记录的消息,可以是字符串、错误对象或模板字符串数组
|
|
|
|
|
|
|
|
* @param args - 模板字符串数组的参数
|
|
|
|
|
|
|
|
* @param partialOptions - 部分日志记录选项
|
|
|
|
|
|
|
|
* @returns 返回记录的日志信息
|
|
|
|
|
|
|
|
*/
|
|
|
|
const result: LoggerForType = (message: string | Error | TemplateStringsArray, ...args: any[]) => {
|
|
|
|
const result: LoggerForType = (message: string | Error | TemplateStringsArray, ...args: any[]) => {
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 合并当前日志记录器的选项和传入的选项,生成一个新的选项对象
|
|
|
|
|
|
|
|
* 同时,将新的选项对象中的 callStacktraceOffset 属性值加 1
|
|
|
|
|
|
|
|
*/
|
|
|
|
const options = { ...result.options };
|
|
|
|
const options = { ...result.options };
|
|
|
|
options.callStacktraceOffset = (options.callStacktraceOffset || 0) + 1;
|
|
|
|
options.callStacktraceOffset = (options.callStacktraceOffset || 0) + 1;
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 根据传入的 message 参数类型,选择不同的日志记录方法
|
|
|
|
|
|
|
|
* 如果 message 是字符串或 Error 对象,则调用 print 方法记录日志
|
|
|
|
|
|
|
|
* 如果 message 是模板字符串数组,则调用 printTemplate 方法记录日志
|
|
|
|
|
|
|
|
* 如果 message 既不是字符串也不是 Error 对象也不是模板字符串数组,则记录一个错误信息
|
|
|
|
|
|
|
|
*/
|
|
|
|
if (typeof message === 'string' || message instanceof Error) {
|
|
|
|
if (typeof message === 'string' || message instanceof Error) {
|
|
|
|
return result.logger.print(result.type, message, options)
|
|
|
|
return result.logger.print(result.type, message, options)
|
|
|
|
} else if (Array.isArray(message)) {
|
|
|
|
} else if (Array.isArray(message)) {
|
|
|
|
return result.logger.printTemplate(result.type, message, args, options)
|
|
|
|
return result.logger.printTemplate(result.type, message, args, options)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 记录错误信息
|
|
|
|
result.logger.error`Trying to log type ${type} with message=${message} and args=${args}`;
|
|
|
|
result.logger.error`Trying to log type ${type} with message=${message} and args=${args}`;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 设置 result 对象的 logger 属性为当前 Logger 实例
|
|
|
|
result.logger = this;
|
|
|
|
result.logger = this;
|
|
|
|
|
|
|
|
// 设置 result 对象的 type 属性为传入的 type 参数
|
|
|
|
result.type = type;
|
|
|
|
result.type = type;
|
|
|
|
|
|
|
|
// 设置 result 对象的 options 属性为传入的 options 参数
|
|
|
|
result.options = options;
|
|
|
|
result.options = options;
|
|
|
|
|
|
|
|
// 定义一个名为 withOptions 的方法,该方法接受一个新的选项对象 newOptions
|
|
|
|
|
|
|
|
// 并返回一个新的 LoggerForType 实例,该实例的类型和当前实例相同,但选项是合并后的结果
|
|
|
|
result.withOptions = newOptions => this.wrapType(result.type, { ...result.options, ...newOptions });
|
|
|
|
result.withOptions = newOptions => this.wrapType(result.type, { ...result.options, ...newOptions });
|
|
|
|
|
|
|
|
// 返回 result 对象,该对象是一个新的 LoggerForType 实例
|
|
|
|
return result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建一个用于记录调试信息的日志记录器
|
|
|
|
public debug = this.wrapType('DEBUG');
|
|
|
|
public debug = this.wrapType('DEBUG');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建一个用于记录一般信息的日志记录器
|
|
|
|
public info = this.wrapType('INFO');
|
|
|
|
public info = this.wrapType('INFO');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 创建一个用于记录警告信息的日志记录器,并设置了一些额外的选项
|
|
|
|
|
|
|
|
* @param callStacktrace - 调用堆栈跟踪的深度
|
|
|
|
|
|
|
|
* @param reportedFromLevel - 报告的起始级别
|
|
|
|
|
|
|
|
*/
|
|
|
|
public warning = this.wrapType('WARNING', { callStacktrace: 3, reportedFromLevel: 2 });
|
|
|
|
public warning = this.wrapType('WARNING', { callStacktrace: 3, reportedFromLevel: 2 });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 创建一个用于记录错误信息的日志记录器,并设置了一些额外的选项
|
|
|
|
|
|
|
|
* @param callStacktrace - 调用堆栈跟踪的深度
|
|
|
|
|
|
|
|
* @param reportedFromLevel - 报告的起始级别
|
|
|
|
|
|
|
|
* @param maxErrorStack - 最大错误堆栈跟踪深度
|
|
|
|
|
|
|
|
*/
|
|
|
|
public error = this.wrapType('ERROR', { callStacktrace: 5, reportedFromLevel: 2, maxErrorStack: 10 });
|
|
|
|
public error = this.wrapType('ERROR', { callStacktrace: 5, reportedFromLevel: 2, maxErrorStack: 10 });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 导出 Logger 类型,以便在其他文件中使用
|
|
|
|
export type { Logger };
|
|
|
|
export type { Logger };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 增加日志记录选项中的堆栈跟踪偏移量
|
|
|
|
|
|
|
|
* @param amount - 要增加的堆栈跟踪偏移量,默认为 1
|
|
|
|
|
|
|
|
* @param options - 日志记录选项
|
|
|
|
|
|
|
|
* @returns 一个新的日志记录选项对象,其中堆栈跟踪偏移量增加了指定的 amount
|
|
|
|
|
|
|
|
*/
|
|
|
|
export function withStacktraceOffset(amount: number = 1, options: Partial<LoggingOptions> = {}): Partial<LoggingOptions> {
|
|
|
|
export function withStacktraceOffset(amount: number = 1, options: Partial<LoggingOptions> = {}): Partial<LoggingOptions> {
|
|
|
|
return { ...options, callStacktraceOffset: (options.callStacktraceOffset || 0) + amount };
|
|
|
|
return { ...options, callStacktraceOffset: (options.callStacktraceOffset || 0) + amount };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 定义一个接口,继承自 FileSystemConfig 并省略了 sock 和 _calculated 属性
|
|
|
|
|
|
|
|
* 同时重新定义了这两个属性,使其为可选的字符串类型
|
|
|
|
|
|
|
|
* 此外,_calculated 属性的类型是 CensoredFileSystemConfig 类型,允许嵌套
|
|
|
|
|
|
|
|
*/
|
|
|
|
export interface CensoredFileSystemConfig extends Omit<FileSystemConfig, 'sock' | '_calculated'> {
|
|
|
|
export interface CensoredFileSystemConfig extends Omit<FileSystemConfig, 'sock' | '_calculated'> {
|
|
|
|
|
|
|
|
// 可选的 sock 属性,用于存储套接字信息
|
|
|
|
sock?: string;
|
|
|
|
sock?: string;
|
|
|
|
|
|
|
|
// 可选的 _calculated 属性,用于存储计算后的值
|
|
|
|
_calculated?: CensoredFileSystemConfig;
|
|
|
|
_calculated?: CensoredFileSystemConfig;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 审查配置对象,将敏感信息替换为 `<censored>`
|
|
|
|
|
|
|
|
* @param config - 要审查的文件系统配置对象
|
|
|
|
|
|
|
|
* @returns 审查后的文件系统配置对象,其中敏感信息已被替换
|
|
|
|
|
|
|
|
*/
|
|
|
|
function censorConfig(config: FileSystemConfig): CensoredFileSystemConfig {
|
|
|
|
function censorConfig(config: FileSystemConfig): CensoredFileSystemConfig {
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
|
|
|
|
// 复制 config 对象的所有属性
|
|
|
|
...config,
|
|
|
|
...config,
|
|
|
|
|
|
|
|
// 如果 password 是字符串类型,则替换为 <censored>
|
|
|
|
password: typeof config.password === 'string' ? '<censored>' : config.password,
|
|
|
|
password: typeof config.password === 'string' ? '<censored>' : config.password,
|
|
|
|
|
|
|
|
// 如果 passphrase 是字符串类型,则替换为 <censored>
|
|
|
|
passphrase: typeof config.passphrase === 'string' ? '<censored>' : config.passphrase,
|
|
|
|
passphrase: typeof config.passphrase === 'string' ? '<censored>' : config.passphrase,
|
|
|
|
|
|
|
|
// 如果 privateKey 是 Buffer 类型,则替换为 Buffer(长度)
|
|
|
|
privateKey: config.privateKey instanceof Buffer ? `Buffer(${config.privateKey.length})` : config.privateKey,
|
|
|
|
privateKey: config.privateKey instanceof Buffer ? `Buffer(${config.privateKey.length})` : config.privateKey,
|
|
|
|
|
|
|
|
// 如果 sock 存在,则替换为 <socket>
|
|
|
|
sock: config.sock ? '<socket>' : config.sock,
|
|
|
|
sock: config.sock ? '<socket>' : config.sock,
|
|
|
|
|
|
|
|
// 如果 _calculated 存在,则递归调用 censorConfig 函数进行审查
|
|
|
|
_calculated: config._calculated ? censorConfig(config._calculated) : config._calculated,
|
|
|
|
_calculated: config._calculated ? censorConfig(config._calculated) : config._calculated,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 创建一个全局的日志记录器实例
|
|
|
|
|
|
|
|
* 这个实例是 Logger 类的一个实例,用于记录应用程序的日志信息
|
|
|
|
|
|
|
|
*/
|
|
|
|
export const Logging = new (Logger as any) as Logger;
|
|
|
|
export const Logging = new (Logger as any) as Logger;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 在全局日志记录器中记录一条信息
|
|
|
|
Logging.info`
|
|
|
|
Logging.info`
|
|
|
|
Created output channel for vscode-sshfs
|
|
|
|
Created output channel for vscode-sshfs
|
|
|
|
When posting your logs somewhere, keep the following in mind:
|
|
|
|
When posting your logs somewhere, keep the following in mind:
|
|
|
|