This commit is contained in:
299
config/logs.js
299
config/logs.js
@@ -1,147 +1,194 @@
|
||||
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) => {
|
||||
if (label === 'server') {
|
||||
return '[🖥️ \x1b[32mServer\x1b[0m] ';
|
||||
} else if (label === 'client') {
|
||||
return '[🌐 \x1b[34mClient\x1b[0m] ';
|
||||
} else if (label === 'Internal-Server-Error') {
|
||||
return '[❗️ \x1b[38;5;9mInternal-Error\x1b[0m] ';
|
||||
} else if (label === 'Authentification') {
|
||||
return '[🔒 \x1b[33mAuthentification\x1b[0m] ';
|
||||
} else if (label === 'Suspicious Request') {
|
||||
return '[⚠️ \x1b[38;5;208mSuspicious Request\x1b[0m] ';
|
||||
}else if (label === 'API Request') {
|
||||
return '[⚙️ \x1b[38;5;9mAPI Request\x1b[0m] ';
|
||||
}
|
||||
return '';
|
||||
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: false,
|
||||
maxSize: '20m',
|
||||
maxFiles: '14d'
|
||||
});
|
||||
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()
|
||||
)
|
||||
});
|
||||
};
|
||||
|
||||
const logger = winston.createLogger({
|
||||
format: format.combine(
|
||||
format.label({ label: 'server' }),
|
||||
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
||||
format.printf(info => {
|
||||
const { timestamp, level, label, message } = info;
|
||||
const prefix = logPrefix(label);
|
||||
return `${prefix}[\x1b[36m${timestamp}\x1b[0m] ${message}`;
|
||||
})
|
||||
),
|
||||
transports: [
|
||||
new winston.transports.Console(),
|
||||
createDailyRotateFileTransport()
|
||||
]
|
||||
});
|
||||
// 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}`;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const ErrorLogger = winston.createLogger({
|
||||
format: format.combine(
|
||||
format.label({ label: 'Internal-Server-Error' }),
|
||||
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
||||
format.printf(info => {
|
||||
const { timestamp, level, label, message } = info;
|
||||
const prefix = logPrefix(label);
|
||||
return `${prefix}[\x1b[36m${timestamp}\x1b[0m] ${message}`;
|
||||
})
|
||||
),
|
||||
transports: [
|
||||
new winston.transports.Console(),
|
||||
createDailyRotateFileTransport()
|
||||
]
|
||||
});
|
||||
// Création des différents loggers
|
||||
const createLogger = (label) => {
|
||||
return winston.createLogger({
|
||||
format: createLoggerFormat(label),
|
||||
transports: [
|
||||
new winston.transports.Console(),
|
||||
createDailyRotateFileTransport()
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
const clientLogger = winston.createLogger({
|
||||
format: format.combine(
|
||||
format.label({ label: 'client' }),
|
||||
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
||||
format.printf(info => {
|
||||
const { timestamp, level, label, message } = info;
|
||||
const prefix = logPrefix(label);
|
||||
return `${prefix}[\x1b[36m${timestamp}\x1b[0m] ${message}`;
|
||||
})
|
||||
),
|
||||
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');
|
||||
|
||||
const authLogger = winston.createLogger({
|
||||
format: format.combine(
|
||||
format.label({ label: 'Authentification' }),
|
||||
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
||||
format.printf(info => {
|
||||
const { timestamp, level, label, message } = info;
|
||||
const prefix = logPrefix(label);
|
||||
return `${prefix}[\x1b[36m${timestamp}\x1b[0m] ${message}`;
|
||||
})
|
||||
),
|
||||
transports: [
|
||||
new winston.transports.Console(),
|
||||
createDailyRotateFileTransport()
|
||||
]
|
||||
});
|
||||
|
||||
const suspiciousLogger = winston.createLogger({
|
||||
format: format.combine(
|
||||
format.label({ label: 'Suspicious Request' }),
|
||||
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
||||
format.printf(info => {
|
||||
const { timestamp, level, label, message } = info;
|
||||
const prefix = logPrefix(label);
|
||||
return `${prefix}[\x1b[36m${timestamp}\x1b[0m] ${message}`;
|
||||
})
|
||||
),
|
||||
transports: [
|
||||
new winston.transports.Console(),
|
||||
createDailyRotateFileTransport()
|
||||
]
|
||||
});
|
||||
// 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) => {
|
||||
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
|
||||
const userAgent = req.headers['user-agent'];
|
||||
req.log = clientLogger;
|
||||
req.log.info(`[${ip}] - ${userAgent} - ${req.method} ${req.url}`);
|
||||
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' };
|
||||
|
||||
const apiLogger = winston.createLogger({
|
||||
format: format.combine(
|
||||
format.label({ label: 'API Request' }),
|
||||
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
||||
format.printf(info => {
|
||||
const { timestamp, level, label, message } = info;
|
||||
const prefix = logPrefix(label);
|
||||
return `${prefix}[\x1b[36m${timestamp}\x1b[0m] ${message}`;
|
||||
})
|
||||
),
|
||||
transports: [
|
||||
new winston.transports.Console(),
|
||||
createDailyRotateFileTransport()
|
||||
]
|
||||
});
|
||||
// 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) => {
|
||||
const start = Date.now();
|
||||
res.on('finish', () => {
|
||||
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`);
|
||||
});
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { logger, clientLogger, ErrorLogger, logRequestInfo, authLogger, suspiciousLogger, logApiRequest };
|
||||
// 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
|
||||
};
|
||||
Reference in New Issue
Block a user