const winston = require('winston'); const path = require('path'); require('winston-daily-rotate-file'); const fs = require('fs'); // verifica se a pasta de logs existe, se não, cria const logsDir = path.join(__dirname, '../../logs'); if (!fs.existsSync(logsDir)) { fs.mkdirSync(logsDir, { recursive: true }); } const isProduction = process.env.NODE_ENV === 'production'; const logLevel = process.env.LOG_LEVEL || 'info'; const logToConsole = process.env.LOG_TO_CONSOLE ? process.env.LOG_TO_CONSOLE === 'true' : !isProduction; // Configuração do logger com winston const logger = winston.createLogger({ level: logLevel, format: winston.format.combine(winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.errors({ stack: true })), transports: [ // Log geral da aplicação new winston.transports.DailyRotateFile({ filename: path.join(logsDir, 'app-%DATE%.log'), datePattern: 'YYYY-MM-DD', zippedArchive: true, maxSize: '5m', maxFiles: '10d', format: winston.format.combine( winston.format.printf((info) => { const { timestamp, level, message, stack, ...meta } = info; let logMessage = `${timestamp} [${level}]: ${stack || message}`; if (Object.keys(meta).length) { logMessage += ` ${JSON.stringify(meta, null, 2)}`; } return logMessage; }) ), }), // Log de erros new winston.transports.DailyRotateFile({ filename: path.join(logsDir, 'error-%DATE%.log'), level: 'error', datePattern: 'YYYY-MM-DD', zippedArchive: true, maxSize: '5m', maxFiles: '10d', format: winston.format.combine( winston.format.printf((info) => { const { timestamp, level, message, stack, ...meta } = info; let logMessage = `${timestamp} [${level}]: ${stack || message}`; if (Object.keys(meta).length) { logMessage += ` ${JSON.stringify(meta, null, 2)}`; } return logMessage; }) ), }) ], }); // Log no console (controlado por variavel de ambiente) if (logToConsole) { logger.add(new winston.transports.Console({ format: winston.format.combine( winston.format.colorize(), winston.format.printf((info) => { const { timestamp, level, message, stack, ...meta } = info; let logMessage = `${timestamp} [${level}]: ${stack || message}`; if (Object.keys(meta).length) { logMessage += ` ${JSON.stringify(meta, null, 2)}`; } return logMessage; }) ) })); } // Funções utilitárias const logError = (error, context = '') => { if (error instanceof Error) { logger.error(`${context} - ${error.message}`, { stack: error.stack }); } else { logger.error(`${context} - ${error}`); } }; const logInfo = (message, meta = {}) => { logger.info(message, meta); }; const logWarning = (message, meta = {}) => { logger.warn(message, meta); }; const logDebug = (message, meta = {}) => { logger.debug(message, meta); }; // Log de sincronização específico. Fica em debug: por causa da margem de seguranca do // watermark, esse log dispara toda vez que ha qualquer ticket "quente" no lote, mesmo sem // mudanca real de campo - o sinal de mudanca de verdade e logado no chamador. const logSync = (service, count, type) => { logger.debug(`SYNC: ${service} - ${count} ${type} sincronizados`, { service, count, type }); }; module.exports = { logger, logError, logInfo, logWarning, logDebug, logSync, };