import winston from 'winston'; import path from 'path'; import { config } from './config'; const logDir = path.join(__dirname, '../logs'); // Create logs directory if it doesn't exist import fs from 'fs'; if (!fs.existsSync(logDir)) { fs.mkdirSync(logDir, { recursive: true }); } const logFormat = winston.format.combine( winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.errors({ stack: true }), winston.format.json() ); const consoleFormat = winston.format.combine( winston.format.colorize(), winston.format.timestamp({ format: 'HH:mm:ss' }), winston.format.printf(({ timestamp, level, message, ...meta }) => { const metaStr = Object.keys(meta).length ? ` ${JSON.stringify(meta)}` : ''; return `${timestamp} ${level}: ${message}${metaStr}`; }) ); // log auth attempts and failures export const logger = winston.createLogger({ level: config.logging.level, format: logFormat, transports: [ // logs auth and security events new winston.transports.File({ filename: path.join(logDir, 'security.log'), level: 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.json() ) }), // logs all errors new winston.transports.File({ filename: path.join(logDir, 'error.log'), level: 'error' }), // logs everything new winston.transports.File({ filename: path.join(logDir, 'combined.log') }) ] }); // console log in dev if (config.server.nodeEnv === 'development') { logger.add(new winston.transports.Console({ format: consoleFormat })); } // Security specific logging export const securityLogger = { rateLimitExceeded: (ip: string, endpoint: string) => { logger.warn('Rate limit exceeded', { event: 'rate_limit_exceeded', ip, endpoint, timestamp: new Date().toISOString() }); }, suspiciousActivity: (ip: string, activity: string, details?: any) => { logger.warn('Suspicious activity detected', { event: 'suspicious_activity', ip, activity, details, timestamp: new Date().toISOString() }); } }; export default logger;