UNPKG

@syngrisi/syngrisi

Version:
148 lines (129 loc) 5.43 kB
import winston, { Logger as WinstonLogger } from 'winston'; import 'winston-mongodb'; import { blue, gray, magenta } from 'chalk'; import formatISOToDateTime from '@utils/formatISOToDateTime'; import { config } from '@config'; import path from 'path'; import { LogOpts } from '@types'; import { ApiError } from '../utils'; import { env } from "@env"; const logLevel: string = env.SYNGRISI_LOG_LEVEL; interface LoggerOptions { dbConnectionString: string; } function getScriptLine(): string { const stack = new Error().stack; if (stack) { const stackLines = stack.split('\n'); let loggerLineIndex = -1; // last string contains 'lib/logger' for (let i = 0; i < stackLines.length; i++) { if (stackLines[i].includes('lib/logger')) { loggerLineIndex = i; } } // check string after 'lib/logger' const targetLineIndex = loggerLineIndex + 1; if (targetLineIndex >= 0 && targetLineIndex < stackLines.length) { const targetLine = stackLines[targetLineIndex]; const match = targetLine.match(/at\s+(?:.+\s+\()?(.+):(\d+):(\d+)\)?/); if (match) { const scriptPath = match[1]; const relativePath = path.relative(process.cwd(), scriptPath); const lineNumber = match[2]; return `${relativePath}:${lineNumber}`; } } } return 'unknown'; } function createWinstonLogger(opts: LoggerOptions): WinstonLogger { return winston.createLogger({ transports: [ new winston.transports.Console({ level: logLevel || 'silly', format: winston.format.combine( winston.format.colorize(), winston.format.timestamp(), winston.format.ms(), winston.format.metadata(), winston.format.printf((info) => { const user = info.metadata.user ? blue(` <${info.metadata.user}>`) : ''; const ref = info.metadata.ref ? gray(` ${info.metadata.ref}`) : ''; const msgType = info.metadata.msgType ? ` ${info.metadata.msgType}` : ''; const itemType = info.metadata.itemType ? magenta(` ${info.metadata.itemType}`) : ''; const scope = info.metadata.scope ? magenta(` [${info.metadata.scope}] `) : magenta(` [${getScriptLine()}] `); const msg = typeof info.message === 'object' ? `\n${JSON.stringify(info.message, null, 2)}` : info.message; return `${info.level} ${scope}${formatISOToDateTime(info.metadata.timestamp)} ` + `${info.metadata.ms}${user}${ref}${msgType}${itemType} '${msg}'`; }), winston.format.padLevels(), ), }), new winston.transports.MongoDB({ level: logLevel || 'debug', format: winston.format.combine( winston.format.timestamp(), winston.format.json(), winston.format.metadata(), ), options: { useUnifiedTopology: true, }, db: opts.dbConnectionString, collection: 'vrslogs', }), ], }); } class Logger { private winstonLogger: WinstonLogger; constructor(opts: LoggerOptions = { dbConnectionString: config.connectionString }) { this.winstonLogger = createWinstonLogger(opts); } private static mergeMeta(objects: LogOpts[]): LogOpts { return objects.reduce((acc, obj) => { return { ...acc, ...obj }; }, {}); } private log(severity: string, msg: string | object, meta: LogOpts[]): void { const mergedMeta = Logger.mergeMeta(meta); if (!mergedMeta.scope) { mergedMeta.scope = getScriptLine(); } const formattedMsg = typeof msg === 'object' ? JSON.stringify(msg, null, 2) : msg; this.winstonLogger.log(severity, formattedMsg, mergedMeta); } public error(msg: string | object | unknown, ...meta: LogOpts[]): void { let message: unknown = String(msg); let code = 0; if ((msg instanceof Object)) { message = JSON.stringify(msg); } if ((msg instanceof Error)) { message = msg.stack; } if ((msg instanceof ApiError)) { code = msg.statusCode; } this.log('error', `${code !== 0 ? '[' + code + ']' : ''}${message}\n stacktrace: ${new Error().stack}`, meta); } public warn(msg: string | object, ...meta: LogOpts[]): void { this.log('warn', `${msg}\n stacktrace: ${new Error().stack}`, meta); } public info(msg: string | object, ...meta: LogOpts[]): void { this.log('info', msg, meta); } public verbose(msg: string | object, ...meta: LogOpts[]): void { this.log('verbose', msg, meta); } public debug(msg: string | object, ...meta: LogOpts[]): void { this.log('debug', msg, meta); } public silly(msg: string | object, ...meta: LogOpts[]): void { this.log('silly', msg, meta); } } export default new Logger();