const winston = require("winston"); const path = require("path"); const config = require("../config"); // Create logs directory if it doesn't exist const fs = require("fs"); const logsDir = path.join(__dirname, "..", "logs"); if (!fs.existsSync(logsDir)) { fs.mkdirSync(logsDir); } // Custom format for console output const consoleFormat = winston.format.combine( winston.format.timestamp({ format: "HH:mm:ss" }), winston.format.colorize(), winston.format.printf(({ timestamp, level, message, ...meta }) => { let metaStr = ""; if (Object.keys(meta).length > 0) { metaStr = " " + JSON.stringify(meta); } return `${timestamp} [${level}] ${message}${metaStr}`; }) ); // Custom format for file output const fileFormat = winston.format.combine( winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json() ); // Create the logger const logger = winston.createLogger({ level: config.logging.level, format: fileFormat, defaultMeta: { service: "outreach-engine", environment: config.env, }, transports: [ // Error log file new winston.transports.File({ filename: path.join(logsDir, "error.log"), level: "error", maxsize: 5242880, // 5MB maxFiles: 5, }), // Combined log file new winston.transports.File({ filename: path.join(logsDir, "combined.log"), maxsize: 5242880, // 5MB maxFiles: 5, }), // Email activity log new winston.transports.File({ filename: path.join(logsDir, "email-activity.log"), level: "info", maxsize: 10485760, // 10MB maxFiles: 10, }), ], }); // Add console transport for development if (config.isDevelopment) { logger.add( new winston.transports.Console({ format: consoleFormat, }) ); } // Custom logging methods for email activities logger.emailSent = (recipient, subject, firmName, testMode = false) => { logger.info("Email sent successfully", { event: "email_sent", recipient, subject, firmName, testMode, timestamp: new Date().toISOString(), }); }; logger.emailFailed = (recipient, error, errorType, firmName) => { logger.error("Email failed to send", { event: "email_failed", recipient, error: error.message, errorType, firmName, stack: error.stack, timestamp: new Date().toISOString(), }); }; logger.emailRetry = (recipient, attempt, maxRetries, retryDelay, errorType) => { logger.warn("Email retry scheduled", { event: "email_retry", recipient, attempt, maxRetries, retryDelay, errorType, timestamp: new Date().toISOString(), }); }; logger.emailPermanentFailure = (recipient, errorType, attempts) => { logger.error("Email permanent failure", { event: "email_permanent_failure", recipient, errorType, attempts, timestamp: new Date().toISOString(), }); }; logger.campaignStart = (totalEmails, testMode) => { logger.info("Email campaign started", { event: "campaign_start", totalEmails, testMode, timestamp: new Date().toISOString(), }); }; logger.campaignComplete = (stats) => { logger.info("Email campaign completed", { event: "campaign_complete", ...stats, timestamp: new Date().toISOString(), }); }; logger.rateLimitPause = (duration, reason) => { logger.warn("Rate limit pause activated", { event: "rate_limit_pause", duration, reason, timestamp: new Date().toISOString(), }); }; // Helper method to log with email context logger.withEmail = (recipient, firmName) => { return { info: (message, meta = {}) => logger.info(message, { ...meta, recipient, firmName }), warn: (message, meta = {}) => logger.warn(message, { ...meta, recipient, firmName }), error: (message, meta = {}) => logger.error(message, { ...meta, recipient, firmName }), }; }; module.exports = logger;