nakama/backend/middleware/logger.js

201 lines
6.2 KiB
JavaScript
Raw Permalink Normal View History

2025-11-04 21:51:05 +00:00
const fs = require('fs');
const path = require('path');
// Создать директорию для логов если её нет
const logsDir = path.join(__dirname, '../logs');
if (!fs.existsSync(logsDir)) {
fs.mkdirSync(logsDir, { recursive: true });
}
2025-11-10 20:13:22 +00:00
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);
}
});
};
2025-11-21 01:14:56 +00:00
// Цвета для консоли (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: '🔒'
};
2025-11-04 21:51:05 +00:00
// Функция для логирования
const log = (level, message, data = {}) => {
const timestamp = new Date().toISOString();
2025-11-21 01:14:56 +00:00
const emoji = levelEmojis[level] || '📋';
const logMessage = `[${timestamp}] ${emoji} [${level.toUpperCase()}] ${message}`;
const serializedData = Object.keys(data).length ? ` ${JSON.stringify(data, null, 2)}` : '';
2025-11-10 20:13:22 +00:00
const fullMessage = `${logMessage}${serializedData}`;
2025-11-04 21:51:05 +00:00
2025-11-21 01:14:56 +00:00
// Логирование в консоль с цветами
let colorCode = colors.reset;
2025-11-04 21:51:05 +00:00
if (level === 'error') {
2025-11-21 01:14:56 +00:00
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);
2025-11-04 21:51:05 +00:00
} else {
2025-11-21 01:14:56 +00:00
colorCode = colors.cyan;
console.log(`${colorCode}${logMessage}${colors.reset}`, data);
2025-11-04 21:51:05 +00:00
}
2025-11-10 20:13:22 +00:00
const fileName = `${level}-${getDatePrefix()}.log`;
appendLog(fileName, fullMessage);
2025-11-21 01:14:56 +00:00
// Также писать все логи в общий файл
appendLog(`all-${getDatePrefix()}.log`, fullMessage);
2025-11-04 21:51:05 +00:00
};
// Middleware для логирования запросов
const requestLogger = (req, res, next) => {
const start = Date.now();
2025-11-21 01:14:56 +00:00
// Логировать входящий запрос (только для важных роутов)
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'
});
}
2025-11-04 21:51:05 +00:00
// Логировать после завершения запроса
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,
2025-11-21 01:14:56 +00:00
userAgent: req.get('user-agent')?.substring(0, 100),
userId: req.user?.id || req.user?._id || 'anonymous'
2025-11-04 21:51:05 +00:00
};
2025-11-04 22:41:35 +00:00
// Пропустить логирование для публичных роутов (health, корневой роут)
2025-11-21 01:14:56 +00:00
if (req.path === '/health' || req.path === '/' || req.path === '/favicon.ico') {
2025-11-04 22:41:35 +00:00
// Логировать только ошибки для публичных роутов
if (res.statusCode >= 400) {
log('error', 'Request failed', logData);
}
return; // Не логировать успешные запросы к публичным роутам
}
2025-11-21 01:14:56 +00:00
if (res.statusCode >= 500) {
log('error', 'Server error', logData);
} else if (res.statusCode >= 400) {
log('warn', 'Client error', logData);
2025-11-04 22:41:35 +00:00
} else if (res.statusCode >= 300 && res.statusCode !== 304) {
// 304 - это нормально (кеш), не логируем
2025-11-21 01:14:56 +00:00
log('info', 'Redirect', logData);
2025-11-04 21:51:05 +00:00
} else {
2025-11-21 01:14:56 +00:00
// Успешный запрос
if (duration > 1000) {
// Медленный запрос
log('warn', 'Slow request', { ...logData, slow: true });
} else {
log('info', 'Request completed', logData);
}
2025-11-04 21:51:05 +00:00
}
});
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 можно отправить уведомление
2025-11-10 20:13:22 +00:00
const securityMessage = `[${new Date().toISOString()}] [SECURITY] ${type}: ${JSON.stringify(securityData)}`;
appendLog(`security-${getDatePrefix()}.log`, securityMessage);
2025-11-04 21:51:05 +00:00
};
2025-11-21 01:14:56 +00:00
// Дополнительные утилиты для логирования
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
});
};
2025-11-04 21:51:05 +00:00
module.exports = {
log,
2025-11-21 01:14:56 +00:00
logError,
logSuccess,
logDebug,
logPerformance,
2025-11-04 21:51:05 +00:00
requestLogger,
logSecurityEvent
};