refactor🎨: (阅读代码):logging.ts增加注释

master
yetao 1 week ago
parent f4a0fe5568
commit 2363cf52bc

@ -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:

Loading…
Cancel
Save