const { format } = require('winston'); const winston = require('winston'); const DailyRotateFile = require('winston-daily-rotate-file'); const fs = require('fs'); const path = require('path'); // Configuration des préfixes de logs avec couleurs const logPrefix = (label) => { const prefixes = { 'server': '[🖥️ \x1b[32mServer\x1b[0m]', 'client': '[🌐 \x1b[34mClient\x1b[0m]', 'Internal-Server-Error': '[❗️ \x1b[38;5;9mInternal-Error\x1b[0m]', 'Authentification': '[🔒 \x1b[33mAuthentification\x1b[0m]', 'Suspicious Request': '[⚠️ \x1b[38;5;208mSuspicious Request\x1b[0m]', 'API Request': '[⚙️ \x1b[38;5;9mAPI Request\x1b[0m]', 'File System': '[📁 \x1b[36mFile System\x1b[0m]', 'Database': '[🗄️ \x1b[35mDatabase\x1b[0m]' }; return prefixes[label] || ''; }; // Configuration du transport de fichiers rotatifs const createDailyRotateFileTransport = () => { return new DailyRotateFile({ filename: 'logs/log-%DATE%.log', datePattern: 'YYYY-MM-DD', zippedArchive: true, maxSize: '20m', maxFiles: '14d', format: format.combine( format.timestamp(), format.json() ) }); }; // Configuration du format de base pour tous les loggers const createLoggerFormat = (label) => { return format.combine( format.label({ label }), format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), format.printf(info => { const { timestamp, label, message } = info; const prefix = logPrefix(label); return `${prefix} [\x1b[36m${timestamp}\x1b[0m] ${message}`; }) ); }; // Création des différents loggers const createLogger = (label) => { return winston.createLogger({ format: createLoggerFormat(label), transports: [ new winston.transports.Console(), createDailyRotateFileTransport() ] }); }; // Instanciation des loggers const logger = createLogger('server'); const clientLogger = createLogger('client'); const ErrorLogger = createLogger('Internal-Server-Error'); const authLogger = createLogger('Authentification'); const suspiciousLogger = createLogger('Suspicious Request'); const apiLogger = createLogger('API Request'); const fileSystemLogger = createLogger('File System'); const databaseLogger = createLogger('Database'); // Fonction utilitaire pour vérifier les chemins const checkPath = (url, paths) => { return paths && paths.length > 0 && paths.some(p => url.startsWith(p.trim())); }; // Middleware principal de logging const logRequestInfo = (req, res, next) => { try { // Lire la configuration const setup = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'data', 'setup.json'), 'utf-8'))[0]; const logsConfig = setup.logs || { enabled: 'off', level: 'info' }; // Vérifier si les logs sont activés if (logsConfig.enabled !== 'on') { return next(); } const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; const userAgent = req.headers['user-agent']; const url = req.originalUrl || req.url; // Vérifier d'abord includeOnly if (logsConfig.includeOnly && logsConfig.includeOnly.length > 0) { const isIncluded = logsConfig.includeOnly.some(p => url.startsWith(p.trim())); if (!isIncluded) { return next(); // Ne pas logger si le chemin n'est pas dans includeOnly } } // Ensuite vérifier excludePaths if (logsConfig.excludePaths && logsConfig.excludePaths.length > 0) { const isExcluded = logsConfig.excludePaths.some(p => url.startsWith(p.trim())); if (isExcluded) { return next(); // Ne pas logger si le chemin est dans excludePaths } } // Sélectionner le logger approprié et le niveau let selectedLogger = clientLogger; let logLevel = logsConfig.level || 'info'; if (url.startsWith('/api')) { selectedLogger = apiLogger; } else if (url.startsWith('/auth')) { selectedLogger = authLogger; } // Ne logger que si le niveau est activé if (!logsConfig.levels || logsConfig.levels.includes(logLevel)) { const logMessage = `[${ip}] - ${userAgent} - ${req.method} ${url}`; selectedLogger[logLevel](logMessage); } } catch (err) { console.error('Error in logRequestInfo:', err); } next(); } // Middleware spécifique pour les requêtes API const logApiRequest = (req, res, next) => { if (!req.originalUrl.startsWith('/api')) { return next(); } const start = Date.now(); res.on('finish', () => { try { const setup = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'data', 'setup.json'), 'utf-8'))[0]; const logsConfig = setup.logs || { enabled: 'off' }; if (logsConfig.enabled !== 'on' || (logsConfig.levels && !logsConfig.levels.includes('info'))) { return; } const duration = Date.now() - start; const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; apiLogger.info(`[${ip}] - ${req.method} ${req.originalUrl} - ${res.statusCode} - ${duration}ms`); } catch (err) { ErrorLogger.error(`Error in API logging: ${err.message}`); } }); next(); }; // Fonction utilitaire pour logger les erreurs système const logSystemError = (error, context = '') => { const errorMessage = `${context ? `[${context}] ` : ''}${error.message}`; ErrorLogger.error(errorMessage); if (error.stack) { ErrorLogger.error(error.stack); } }; // Middleware pour logger les erreurs 404 const log404Error = (req, res, next) => { suspiciousLogger.warn(`404 Not Found: ${req.method} ${req.originalUrl} from IP ${req.ip}`); next(); }; // Middleware pour logger les erreurs 500 const log500Error = (error, req, res, next) => { logSystemError(error, '500 Internal Server Error'); next(error); }; // Export des fonctionnalités module.exports = { logger, clientLogger, ErrorLogger, authLogger, suspiciousLogger, apiLogger, fileSystemLogger, databaseLogger, logRequestInfo, logApiRequest, logSystemError, log404Error, log500Error };