201 lines
6.2 KiB
JavaScript
201 lines
6.2 KiB
JavaScript
const fs = require('fs');
|
||
const path = require('path');
|
||
// Создать директорию для логов если её нет
|
||
const logsDir = path.join(__dirname, '../logs');
|
||
if (!fs.existsSync(logsDir)) {
|
||
fs.mkdirSync(logsDir, { recursive: true });
|
||
}
|
||
|
||
const getDatePrefix = () => {
|
||
return new Date().toISOString().slice(0, 10);
|
||
};
|
||
|
||
const appendLog = (fileName, message) => {
|
||
const filePath = path.join(logsDir, fileName);
|
||
fs.appendFile(filePath, `${message}\n`, (err) => {
|
||
if (err) {
|
||
console.error('Ошибка записи в лог файл:', err);
|
||
}
|
||
});
|
||
};
|
||
|
||
// Цвета для консоли (ANSI коды)
|
||
const colors = {
|
||
reset: '\x1b[0m',
|
||
bright: '\x1b[1m',
|
||
dim: '\x1b[2m',
|
||
red: '\x1b[31m',
|
||
green: '\x1b[32m',
|
||
yellow: '\x1b[33m',
|
||
blue: '\x1b[34m',
|
||
magenta: '\x1b[35m',
|
||
cyan: '\x1b[36m',
|
||
white: '\x1b[37m'
|
||
};
|
||
|
||
// Эмодзи для уровней
|
||
const levelEmojis = {
|
||
debug: '🔍',
|
||
info: '📝',
|
||
success: '✅',
|
||
warn: '⚠️',
|
||
error: '❌',
|
||
security: '🔒'
|
||
};
|
||
|
||
// Функция для логирования
|
||
const log = (level, message, data = {}) => {
|
||
const timestamp = new Date().toISOString();
|
||
const emoji = levelEmojis[level] || '📋';
|
||
const logMessage = `[${timestamp}] ${emoji} [${level.toUpperCase()}] ${message}`;
|
||
const serializedData = Object.keys(data).length ? ` ${JSON.stringify(data, null, 2)}` : '';
|
||
const fullMessage = `${logMessage}${serializedData}`;
|
||
|
||
// Логирование в консоль с цветами
|
||
let colorCode = colors.reset;
|
||
if (level === 'error') {
|
||
colorCode = colors.red;
|
||
console.error(`${colors.bright}${colorCode}${logMessage}${colors.reset}`, data);
|
||
} else if (level === 'warn' || level === 'security') {
|
||
colorCode = colors.yellow;
|
||
console.warn(`${colors.bright}${colorCode}${logMessage}${colors.reset}`, data);
|
||
} else if (level === 'success') {
|
||
colorCode = colors.green;
|
||
console.log(`${colors.bright}${colorCode}${logMessage}${colors.reset}`, data);
|
||
} else if (level === 'debug') {
|
||
colorCode = colors.dim;
|
||
console.log(`${colorCode}${logMessage}${colors.reset}`, data);
|
||
} else {
|
||
colorCode = colors.cyan;
|
||
console.log(`${colorCode}${logMessage}${colors.reset}`, data);
|
||
}
|
||
|
||
const fileName = `${level}-${getDatePrefix()}.log`;
|
||
appendLog(fileName, fullMessage);
|
||
|
||
// Также писать все логи в общий файл
|
||
appendLog(`all-${getDatePrefix()}.log`, fullMessage);
|
||
};
|
||
|
||
// Middleware для логирования запросов
|
||
const requestLogger = (req, res, next) => {
|
||
const start = Date.now();
|
||
|
||
// Логировать входящий запрос (только для важных роутов)
|
||
if (req.path.startsWith('/api/') && !req.path.includes('/health')) {
|
||
log('debug', 'Incoming request', {
|
||
method: req.method,
|
||
path: req.path,
|
||
query: req.query,
|
||
body: req.method === 'POST' || req.method === 'PUT' ?
|
||
(req.body && Object.keys(req.body).length > 0 ?
|
||
{ ...req.body, password: req.body.password ? '***' : undefined } :
|
||
'empty') : undefined,
|
||
ip: req.ip,
|
||
userId: req.user?.id || req.user?._id || 'anonymous'
|
||
});
|
||
}
|
||
|
||
// Логировать после завершения запроса
|
||
res.on('finish', () => {
|
||
const duration = Date.now() - start;
|
||
const logData = {
|
||
method: req.method,
|
||
path: req.path,
|
||
status: res.statusCode,
|
||
duration: `${duration}ms`,
|
||
ip: req.ip,
|
||
userAgent: req.get('user-agent')?.substring(0, 100),
|
||
userId: req.user?.id || req.user?._id || 'anonymous'
|
||
};
|
||
|
||
// Пропустить логирование для публичных роутов (health, корневой роут)
|
||
if (req.path === '/health' || req.path === '/' || req.path === '/favicon.ico') {
|
||
// Логировать только ошибки для публичных роутов
|
||
if (res.statusCode >= 400) {
|
||
log('error', 'Request failed', logData);
|
||
}
|
||
return; // Не логировать успешные запросы к публичным роутам
|
||
}
|
||
|
||
if (res.statusCode >= 500) {
|
||
log('error', 'Server error', logData);
|
||
} else if (res.statusCode >= 400) {
|
||
log('warn', 'Client error', logData);
|
||
} else if (res.statusCode >= 300 && res.statusCode !== 304) {
|
||
// 304 - это нормально (кеш), не логируем
|
||
log('info', 'Redirect', logData);
|
||
} else {
|
||
// Успешный запрос
|
||
if (duration > 1000) {
|
||
// Медленный запрос
|
||
log('warn', 'Slow request', { ...logData, slow: true });
|
||
} else {
|
||
log('info', 'Request completed', logData);
|
||
}
|
||
}
|
||
});
|
||
|
||
next();
|
||
};
|
||
|
||
// Логирование подозрительной активности
|
||
const logSecurityEvent = (type, req, details = {}) => {
|
||
const securityData = {
|
||
type,
|
||
ip: req.ip,
|
||
userAgent: req.get('user-agent'),
|
||
path: req.path,
|
||
method: req.method,
|
||
userId: req.user?.id || 'anonymous',
|
||
...details
|
||
};
|
||
|
||
log('warn', 'Security event', securityData);
|
||
|
||
// В production можно отправить уведомление
|
||
const securityMessage = `[${new Date().toISOString()}] [SECURITY] ${type}: ${JSON.stringify(securityData)}`;
|
||
appendLog(`security-${getDatePrefix()}.log`, securityMessage);
|
||
};
|
||
|
||
// Дополнительные утилиты для логирования
|
||
const logError = (context, error, additionalData = {}) => {
|
||
log('error', `Error in ${context}`, {
|
||
error: error.message,
|
||
stack: error.stack,
|
||
name: error.name,
|
||
...additionalData
|
||
});
|
||
};
|
||
|
||
const logSuccess = (message, data = {}) => {
|
||
log('success', message, data);
|
||
};
|
||
|
||
const logDebug = (message, data = {}) => {
|
||
// Логировать debug только в development
|
||
if (process.env.NODE_ENV === 'development') {
|
||
log('debug', message, data);
|
||
}
|
||
};
|
||
|
||
// Логирование производительности
|
||
const logPerformance = (operation, duration, data = {}) => {
|
||
const level = duration > 1000 ? 'warn' : 'info';
|
||
log(level, `Performance: ${operation}`, {
|
||
duration: `${duration}ms`,
|
||
...data
|
||
});
|
||
};
|
||
|
||
module.exports = {
|
||
log,
|
||
logError,
|
||
logSuccess,
|
||
logDebug,
|
||
logPerformance,
|
||
requestLogger,
|
||
logSecurityEvent
|
||
};
|
||
|