feat: add logger module
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
import * as util from 'util';
|
||||
import * as path from 'path';
|
||||
|
||||
import 'colorts/lib/string';
|
||||
|
||||
interface CallSite {
|
||||
getThis(): unknown;
|
||||
getTypeName(): string | null;
|
||||
getFunction(): Function | undefined;
|
||||
getFunctionName(): string | null;
|
||||
getMethodName(): string | null;
|
||||
getFileName(): string | null;
|
||||
getLineNumber(): number | null;
|
||||
getColumnNumber(): number | null;
|
||||
getEvalOrigin(): string | undefined;
|
||||
isToplevel(): boolean;
|
||||
isEval(): boolean;
|
||||
isNative(): boolean;
|
||||
isConstructor(): boolean;
|
||||
}
|
||||
|
||||
/*
|
||||
helper fns
|
||||
*/
|
||||
function getCallerFrame(shift: number = 0): NodeJS.CallSite | undefined {
|
||||
const prepareStackTrace = Error.prepareStackTrace;
|
||||
let loggedStack: NodeJS.CallSite[] | undefined;
|
||||
|
||||
Error.prepareStackTrace = (_, stackTraces: NodeJS.CallSite[]) => {
|
||||
loggedStack = stackTraces;
|
||||
stackTraces.shift(); // discard curr frame
|
||||
return stackTraces;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
new Error().stack;
|
||||
|
||||
Error.prepareStackTrace = prepareStackTrace;
|
||||
|
||||
if (!loggedStack) return undefined;
|
||||
|
||||
if (shift > 0)
|
||||
for (let i = 0; i < shift; i++)
|
||||
loggedStack.shift();
|
||||
|
||||
return loggedStack[1];
|
||||
}
|
||||
|
||||
/*
|
||||
logger
|
||||
*/
|
||||
const LOG_TYPES = {
|
||||
Info: "[Info]".green,
|
||||
Verbose: "[Verbose]".blue,
|
||||
Warning: "[Warning]".yellow,
|
||||
Error: "[Error]".red,
|
||||
} as const;
|
||||
|
||||
export class Logger {
|
||||
private readonly name: string;
|
||||
|
||||
constructor(name: string) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public info(fmt: string, ...args: unknown[]): void {
|
||||
this.log(LOG_TYPES.Info, fmt, args);
|
||||
}
|
||||
|
||||
public verbose(fmt: string, ...args: unknown[]): void {
|
||||
this.log(LOG_TYPES.Verbose, fmt, args);
|
||||
}
|
||||
|
||||
public warning(fmt: string, ...args: unknown[]): void {
|
||||
this.log(LOG_TYPES.Warning, fmt, args);
|
||||
}
|
||||
|
||||
public error(fmt: string, ...args: unknown[]): void {
|
||||
this.log(LOG_TYPES.Error, fmt, args);
|
||||
}
|
||||
|
||||
private log(type: string, message: string, args: unknown[]): void {
|
||||
const caller = getCallerFrame(1);
|
||||
if (!caller) {
|
||||
console.error('Failed to determine caller information');
|
||||
return;
|
||||
}
|
||||
|
||||
const timestamp = this.getFormattedTime();
|
||||
const callerInfo = this.getCallerInfo(caller);
|
||||
const formattedMessage = util.format(message, ...args);
|
||||
|
||||
console.log(
|
||||
`${timestamp} - ${callerInfo} [${this.name.magenta}] ${type} ${formattedMessage}`
|
||||
);
|
||||
}
|
||||
|
||||
private getFormattedTime(): string {
|
||||
const now = new Date();
|
||||
return now.toLocaleTimeString('en-US', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false
|
||||
});
|
||||
}
|
||||
|
||||
private getCallerInfo(caller: NodeJS.CallSite): string {
|
||||
const functionName = caller.getFunctionName()?.replace(/\[.*\]/, '') || '<anonymous>';
|
||||
const fileName = caller.getFileName() || 'unknown';
|
||||
const relativePath = path.relative(process.cwd(), fileName);
|
||||
|
||||
return `${functionName.cyan} @ ${relativePath.dim}`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user