|
|
@ -20,28 +20,33 @@ export function setDebug(debug: boolean) {
|
|
|
|
const outputChannel = vscode.window.createOutputChannel('SSH FS');
|
|
|
|
const outputChannel = vscode.window.createOutputChannel('SSH FS');
|
|
|
|
|
|
|
|
|
|
|
|
export interface LoggingOptions {
|
|
|
|
export interface LoggingOptions {
|
|
|
|
/** The level of outputting the logger's name/stacktrace:
|
|
|
|
/**
|
|
|
|
* 0: Don't report anything
|
|
|
|
* The level of outputting the logger's name/stacktrace:
|
|
|
|
* 1: Only report the name (or first line of stacktrace if missing)
|
|
|
|
* - `0`: Don't report anything (default for `WARNING`/`ERROR`)
|
|
|
|
* 2: Report name and stacktrace (if available)
|
|
|
|
* - `1`: Only report the name (or first line of stacktrace if missing)
|
|
|
|
|
|
|
|
* - `2`: Report name and stacktrace (if available) (default for `WARNING`/`ERROR`)
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
reportedFromLevel: number;
|
|
|
|
reportedFromLevel: number;
|
|
|
|
/** Whether to output a stacktrace of the .info() call etc
|
|
|
|
/**
|
|
|
|
* 0: Don't output a stacktrace
|
|
|
|
* Whether to output a stacktrace of the .info() call etc
|
|
|
|
* -1: Output the whole stacktrace
|
|
|
|
* - `0`: Don't output a stacktrace
|
|
|
|
* N: Only output the first N frames
|
|
|
|
* - `-1`: Output the whole stacktrace
|
|
|
|
|
|
|
|
* - `N`: Only output the first N frames
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* Defaults to `3` for `WARNING`, `5` for `ERROR` and `0` for everything else.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
callStacktrace: number;
|
|
|
|
callStacktrace: number;
|
|
|
|
/** 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.
|
|
|
|
* Used with `.callStacktrace` to skip the given amount of stacktraces in the beginning.
|
|
|
|
* Defaults to 0 meaning no offset.
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
callStacktraceOffset: number;
|
|
|
|
callStacktraceOffset: number;
|
|
|
|
/** Used when the "message" to be logged is an Error object with a stack.
|
|
|
|
/**
|
|
|
|
* 0: Don't output the stack
|
|
|
|
* Used when the "message" to be logged is an Error object with a stack:
|
|
|
|
* -1: Output the whole stack
|
|
|
|
* - `0`: Don't output the stack (which is the default for `DEBUG` and `INFO`)
|
|
|
|
* N: Only output the first N lines
|
|
|
|
* - `-1`: Output the whole stack
|
|
|
|
* The stack gets stringified in the first logger, so child loggers don't inherit, it uses the default (which is 0)
|
|
|
|
* - `N`: Only output the first N lines
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
maxErrorStack: number;
|
|
|
|
maxErrorStack: number;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -49,8 +54,8 @@ export interface LoggingOptions {
|
|
|
|
export const LOGGING_NO_STACKTRACE: Partial<LoggingOptions> = { callStacktrace: 0 };
|
|
|
|
export const LOGGING_NO_STACKTRACE: Partial<LoggingOptions> = { callStacktrace: 0 };
|
|
|
|
export const LOGGING_SINGLE_LINE_STACKTRACE: Partial<LoggingOptions> = { callStacktrace: 1 };
|
|
|
|
export const LOGGING_SINGLE_LINE_STACKTRACE: Partial<LoggingOptions> = { callStacktrace: 1 };
|
|
|
|
|
|
|
|
|
|
|
|
function hasPromiseCause(error: Error): error is Error & { promiseCause: string } {
|
|
|
|
function hasPromiseCause(error: Error): error is Error & { promiseCause: Error; promiseCauseName: string } {
|
|
|
|
return typeof (error as any).promiseCause === 'string';
|
|
|
|
return 'promiseCause' in error && (error as any).promiseCause instanceof Error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export type LoggerDefaultLevels = 'DEBUG' | 'INFO' | 'WARNING' | 'ERROR';
|
|
|
|
export type LoggerDefaultLevels = 'DEBUG' | 'INFO' | 'WARNING' | 'ERROR';
|
|
|
@ -76,7 +81,7 @@ class Logger {
|
|
|
|
};
|
|
|
|
};
|
|
|
|
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 : 5;
|
|
|
|
const len = typeof generateStack === 'number' ? generateStack : 1;
|
|
|
|
const stack = new Error().stack?.split('\n').slice(3, 3 + len).join('\n');
|
|
|
|
const stack = new Error().stack?.split('\n').slice(3, 3 + len).join('\n');
|
|
|
|
this.stack = stack || '<stack unavailable>';
|
|
|
|
this.stack = stack || '<stack unavailable>';
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -88,11 +93,13 @@ class Logger {
|
|
|
|
// Calculate suffix
|
|
|
|
// Calculate suffix
|
|
|
|
let suffix = '';
|
|
|
|
let suffix = '';
|
|
|
|
if (this.name && this.stack && reportedFromLevel >= 2) {
|
|
|
|
if (this.name && this.stack && reportedFromLevel >= 2) {
|
|
|
|
suffix = `\nReported from ${this.name}:\n${this.stack}`;
|
|
|
|
suffix = `\nReported by logger ${this.name}:\n${this.stack}`;
|
|
|
|
} else if (this.name && reportedFromLevel >= 1) {
|
|
|
|
} else if (this.name && reportedFromLevel >= 1) {
|
|
|
|
suffix = `\nReported from ${this.name}`;
|
|
|
|
suffix = `\nReported by logger ${this.name}`;
|
|
|
|
} else if (this.stack && reportedFromLevel >= 2) {
|
|
|
|
} else if (this.stack && reportedFromLevel >= 2) {
|
|
|
|
suffix = `\nReported from:\n${this.stack}`;
|
|
|
|
suffix = `\nReported by logger:\n${this.stack}`;
|
|
|
|
|
|
|
|
} else if (this.stack && reportedFromLevel === 1) {
|
|
|
|
|
|
|
|
suffix = `\nReported by logger:\n${this.stack.split('\n', 2)[0]}`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If there is a parent logger, pass the message with prefix/suffix on
|
|
|
|
// If there is a parent logger, pass the message with prefix/suffix on
|
|
|
|
if (this.parent) return this.parent.doPrint(type, `${prefix}${message}${suffix}`, options);
|
|
|
|
if (this.parent) return this.parent.doPrint(type, `${prefix}${message}${suffix}`, options);
|
|
|
@ -119,14 +126,15 @@ class Logger {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
result += '\n' + stack;
|
|
|
|
result += '\n' + stack;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (hasPromiseCause(value) && maxErrorStack) {
|
|
|
|
if (maxErrorStack !== 0) for (let cause = value; hasPromiseCause(cause); cause = cause.promiseCause) {
|
|
|
|
let { promiseCause } = value;
|
|
|
|
let promiseStack = cause.promiseCause.stack?.split(/\n/g);
|
|
|
|
if (maxErrorStack > 0) {
|
|
|
|
if (!promiseStack) continue;
|
|
|
|
promiseCause = promiseCause.split(/\n/g).slice(1, maxErrorStack + 1).join('\n');
|
|
|
|
if (maxErrorStack > 0) promiseStack = promiseStack.slice(1, maxErrorStack + 1);
|
|
|
|
}
|
|
|
|
result += `\nCaused by ${cause.promiseCauseName || 'promise'}:\n${promiseStack.join('\n')}`;
|
|
|
|
result += '\nCaused by promise:\n' + promiseCause;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
return result;
|
|
|
|
|
|
|
|
} else if (value instanceof vscode.Uri) {
|
|
|
|
|
|
|
|
return value.toString();
|
|
|
|
} else if (isFileSystemConfig(value)) {
|
|
|
|
} else if (isFileSystemConfig(value)) {
|
|
|
|
return JSON.stringify(censorConfig(value), null, 4);
|
|
|
|
return JSON.stringify(censorConfig(value), null, 4);
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -157,6 +165,7 @@ class Logger {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
this.print(type, template.reduce((acc, part, i) => acc + part + this.formatValue(args[i] || '', options), ''), partialOptions);
|
|
|
|
this.print(type, template.reduce((acc, part, i) => acc + part + this.formatValue(args[i] || '', options), ''), partialOptions);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public scope(name?: string, generateStack: number | boolean = false) {
|
|
|
|
public scope(name?: string, generateStack: number | boolean = false) {
|
|
|
@ -166,10 +175,12 @@ class Logger {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public wrapType(type: LoggerDefaultLevels, options: Partial<LoggingOptions> = {}): LoggerForType {
|
|
|
|
public wrapType(type: LoggerDefaultLevels, options: Partial<LoggingOptions> = {}): LoggerForType {
|
|
|
|
const result: LoggerForType = (message: string | Error | TemplateStringsArray, ...args: any[]) => {
|
|
|
|
const result: LoggerForType = (message: string | Error | TemplateStringsArray, ...args: any[]) => {
|
|
|
|
|
|
|
|
const options = { ...result.options };
|
|
|
|
|
|
|
|
options.callStacktraceOffset = (options.callStacktraceOffset || 0) + 1;
|
|
|
|
if (typeof message === 'string' || message instanceof Error) {
|
|
|
|
if (typeof message === 'string' || message instanceof Error) {
|
|
|
|
return result.logger.print(result.type, message, result.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, result.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}`;
|
|
|
|
};
|
|
|
|
};
|
|
|
|