nakama/backend/middleware/logger.js

106 lines
3.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
}
});
};
// Функция для логирования
const log = (level, message, data = {}) => {
const timestamp = new Date().toISOString();
const logMessage = `[${timestamp}] [${level.toUpperCase()}] ${message}`;
const serializedData = Object.keys(data).length ? ` ${JSON.stringify(data)}` : '';
const fullMessage = `${logMessage}${serializedData}`;
// Логирование в консоль
if (level === 'error') {
console.error(logMessage, data);
} else if (level === 'warn') {
console.warn(logMessage, data);
} else {
console.log(logMessage, data);
}
const fileName = `${level}-${getDatePrefix()}.log`;
appendLog(fileName, fullMessage);
};
// Middleware для логирования запросов
const requestLogger = (req, res, next) => {
const start = Date.now();
// Логировать после завершения запроса
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'),
userId: req.user?.id || 'anonymous'
};
// Пропустить логирование для публичных роутов (health, корневой роут)
if (req.path === '/health' || req.path === '/') {
// Логировать только ошибки для публичных роутов
if (res.statusCode >= 400) {
log('error', 'Request failed', logData);
}
return; // Не логировать успешные запросы к публичным роутам
}
if (res.statusCode >= 400) {
log('error', 'Request failed', logData);
} else if (res.statusCode >= 300 && res.statusCode !== 304) {
// 304 - это нормально (кеш), не логируем
log('warn', 'Request redirect', logData);
} 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);
};
module.exports = {
log,
requestLogger,
logSecurityEvent
};