const winston = require('winston'); const path = require('path'); // Define log levels const logLevels = { error: 0, warn: 1, info: 2, http: 3, debug: 4, }; // Define log colors const logColors = { error: 'red', warn: 'yellow', info: 'green', http: 'magenta', debug: 'white', }; winston.addColors(logColors); // Create logs directory if it doesn't exist const fs = require('fs'); const logDir = path.join(__dirname, '../logs'); if (!fs.existsSync(logDir)) { fs.mkdirSync(logDir, { recursive: true }); } // Define log format const logFormat = winston.format.combine( winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.errors({ stack: true }), winston.format.json(), winston.format.printf(({ timestamp, level, message, stack, ...meta }) => { let log = `${timestamp} [${level.toUpperCase()}]: ${message}`; if (Object.keys(meta).length > 0) { log += ` | ${JSON.stringify(meta)}`; } if (stack) { log += `\n${stack}`; } return log; }) ); // Create console format for development const consoleFormat = winston.format.combine( winston.format.colorize({ all: true }), winston.format.timestamp({ format: 'HH:mm:ss' }), winston.format.printf(({ timestamp, level, message, ...meta }) => { let log = `${timestamp} [${level}]: ${message}`; if (Object.keys(meta).length > 0) { log += ` | ${JSON.stringify(meta)}`; } return log; }) ); // Create the logger instance const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', levels: logLevels, format: logFormat, defaultMeta: { service: 'financial-data-server' }, transports: [ // Write all logs with importance level of `error` or less to `error.log` new winston.transports.File({ filename: path.join(logDir, 'error.log'), level: 'error', maxsize: 5242880, // 5MB maxFiles: 5, }), // Write all logs with importance level of `info` or less to `combined.log` new winston.transports.File({ filename: path.join(logDir, 'combined.log'), maxsize: 5242880, // 5MB maxFiles: 5, }), ], }); // If we're not in production, log to the console as well if (process.env.NODE_ENV !== 'production') { logger.add(new winston.transports.Console({ format: consoleFormat, level: process.env.LOG_LEVEL || 'debug', })); } // Create a stream object for Morgan HTTP logger const stream = { write: (message) => { logger.http(message.trim()); }, }; // Handle uncaught exceptions and unhandled rejections logger.exceptions.handle( new winston.transports.File({ filename: path.join(logDir, 'exceptions.log') }) ); logger.rejections.handle( new winston.transports.File({ filename: path.join(logDir, 'rejections.log') }) ); // Graceful shutdown process.on('SIGTERM', () => { logger.info('SIGTERM received, shutting down gracefully'); logger.end(() => { process.exit(0); }); }); process.on('SIGINT', () => { logger.info('SIGINT received, shutting down gracefully'); logger.end(() => { process.exit(0); }); }); module.exports = { logger, stream };