UNPKG

@seawingai/winglog

Version:

A powerful TypeScript/JavaScript logging library built on top of Pino for structured logging with enhanced features

200 lines (175 loc) 6.36 kB
import pino from 'pino'; import * as path from 'path'; import * as fs from 'fs'; export enum LogType { FAILED = 'FAILED', WARN = 'WARN', DEBUG = 'DEBUG', SUCCESS = 'SUCCESS', STARTED = 'STARTED', FINISHED = 'FINISHED', INFO = 'INFO', } export class WingLog { private name: string; private logger: pino.Logger; constructor(name: string) { this.name = name; // Ensure logs directory exists const logsDir = path.join(process.cwd(), 'logs'); if (!fs.existsSync(logsDir)) { fs.mkdirSync(logsDir, { recursive: true }); } const logFile = path.join(logsDir, `${name}.log`); const createConsoleTransport = () => { try { return pino.transport({ target: 'pino-pretty', options: { colorize: true, translateTime: 'SYS:standard', ignore: 'pid,hostname', messageFormat: '{msg}', }, }); } catch (error) { return pino.destination(1); // stdout } }; this.logger = pino({ level: 'debug', timestamp: () => `,"time":"${new Date().toISOString()}"`, }, pino.multistream([ { level: 'debug', stream: createConsoleTransport(), }, { level: 'debug', stream: pino.destination({ dest: logFile, sync: false, mkdir: true, }), }, ])); this.logger.debug(`Logger initialized: [${this.name}]`); } private formatDuration(seconds: number): string { const mins = Math.floor(seconds / 60); let secs = Math.round(seconds % 60); if (secs === 60) { secs = 0; } return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; } private log(message: string, type: LogType | '', icon: string, startTime?: number): number { let timeSpent = 0; let duration = ''; icon = icon || ''; if (startTime) { timeSpent = (Date.now() - startTime) / 1000; duration = `Duration:[${this.formatDuration(timeSpent)}]:`; } let logMessage = `[${this.name}]:${duration}${message}`; switch (type) { case LogType.FAILED: this.logger.error(logMessage); break; case LogType.WARN: this.logger.warn(logMessage); break; case LogType.DEBUG: this.logger.debug(logMessage); break; case LogType.SUCCESS: case LogType.STARTED: case LogType.FINISHED: case LogType.INFO: default: this.logger.info(logMessage); break; } return Number(timeSpent.toFixed(2)); } private logWithData(message: string, type: LogType, rec: Record<string, any>): void { this.log(`${message} ${JSON.stringify(rec)}`, type, '', 0); } // Method overloads for started started(message: string, startTime?: number): number; started(message: string, rec: Record<string, any>): void; started(message: string, startTimeOrRec?: number | Record<string, any>): number | void { if (startTimeOrRec === undefined || typeof startTimeOrRec === 'number') { return this.log(message, LogType.STARTED, '>', startTimeOrRec); } else { this.logWithData(message, LogType.STARTED, startTimeOrRec); } } // Method overloads for finished finished(message: string, startTime?: number): number; finished(message: string, rec: Record<string, any>): void; finished(message: string, startTimeOrRec?: number | Record<string, any>): number | void { if (startTimeOrRec === undefined || typeof startTimeOrRec === 'number') { return this.log(message, LogType.FINISHED, '✓', startTimeOrRec); } else { this.logWithData(message, LogType.FINISHED, startTimeOrRec); } } // Method overloads for success success(message: string, startTime?: number): number; success(message: string, rec: Record<string, any>): void; success(message: string, startTimeOrRec?: number | Record<string, any>): number | void { if (startTimeOrRec === undefined || typeof startTimeOrRec === 'number') { return this.log(message, LogType.SUCCESS, '+', startTimeOrRec); } else { this.logWithData(message, LogType.SUCCESS, startTimeOrRec); } } // Method overloads for failed failed(message: string, startTime?: number): number; failed(message: string, rec: Record<string, any>): void; failed(message: string, startTimeOrRec?: number | Record<string, any>): number | void { if (startTimeOrRec === undefined || typeof startTimeOrRec === 'number') { return this.log(message, LogType.FAILED, 'x', startTimeOrRec); } else { this.logWithData(message, LogType.FAILED, startTimeOrRec); } } // Method overloads for info info(message: string, startTime?: number): number; info(message: string, rec: Record<string, any>): void; info(message: string, startTimeOrRec?: number | Record<string, any>): number | void { if (startTimeOrRec === undefined || typeof startTimeOrRec === 'number') { return this.log(message, LogType.INFO, 'i', startTimeOrRec); } else { this.logWithData(message, LogType.INFO, startTimeOrRec); } } // Method overloads for warn warn(message: string, startTime?: number): number; warn(message: string, rec: Record<string, any>): void; warn(message: string, startTimeOrRec?: number | Record<string, any>): number | void { if (startTimeOrRec === undefined || typeof startTimeOrRec === 'number') { return this.log(message, LogType.WARN, '', startTimeOrRec); } else { this.logWithData(message, LogType.WARN, startTimeOrRec); } } // Method overloads for debug debug(message: string, startTime?: number): number; debug(message: string, rec: Record<string, any>): void; debug(message: string, startTimeOrRec?: number | Record<string, any>): number | void { if (startTimeOrRec === undefined || typeof startTimeOrRec === 'number') { return this.log(message, LogType.DEBUG, '', startTimeOrRec); } else { this.logWithData(message, LogType.DEBUG, startTimeOrRec); } } error(message: string, err: unknown): void { let error = err instanceof Error ? err : new Error(String(err)) let msg = `${message}: ${error.message}`; let rec = {"error": error}; this.logWithData(msg, LogType.FAILED, rec); } } export { WingLog as Logger };