Files
Tasks/backend/config/logger.js
2025-11-12 23:06:27 +02:00

149 lines
3.6 KiB
JavaScript

const winston = require('winston');
const DailyRotateFile = require('winston-daily-rotate-file');
const path = require('path');
// Define log format
const logFormat = winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.errors({ stack: true }),
winston.format.splat(),
winston.format.json()
);
// Console format for development
const consoleFormat = winston.format.combine(
winston.format.colorize(),
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.printf(({ timestamp, level, message, stack }) => {
return stack
? `${timestamp} [${level}]: ${message}\n${stack}`
: `${timestamp} [${level}]: ${message}`;
})
);
// Create logs directory if it doesn't exist
const fs = require('fs');
const logsDir = path.join(__dirname, '../logs');
if (!fs.existsSync(logsDir)) {
fs.mkdirSync(logsDir);
}
// Daily rotate file transport for error logs
const errorRotateTransport = new DailyRotateFile({
filename: path.join(logsDir, 'error-%DATE%.log'),
datePattern: 'YYYY-MM-DD',
level: 'error',
maxSize: '20m',
maxFiles: '14d',
format: logFormat
});
// Daily rotate file transport for combined logs
const combinedRotateTransport = new DailyRotateFile({
filename: path.join(logsDir, 'combined-%DATE%.log'),
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '30d',
format: logFormat
});
// Daily rotate file transport for HTTP logs
const httpRotateTransport = new DailyRotateFile({
filename: path.join(logsDir, 'http-%DATE%.log'),
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '7d',
format: logFormat
});
// Create the Winston logger
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: logFormat,
defaultMeta: { service: 'interview-quiz-api' },
transports: [
errorRotateTransport,
combinedRotateTransport
],
exceptionHandlers: [
new winston.transports.File({
filename: path.join(logsDir, 'exceptions.log')
})
],
rejectionHandlers: [
new winston.transports.File({
filename: path.join(logsDir, 'rejections.log')
})
]
});
// Add console transport in development
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: consoleFormat
}));
}
// HTTP logger for request logging
const httpLogger = winston.createLogger({
level: 'http',
format: logFormat,
defaultMeta: { service: 'interview-quiz-api' },
transports: [httpRotateTransport]
});
// Stream for Morgan middleware
logger.stream = {
write: (message) => {
httpLogger.http(message.trim());
}
};
// Helper functions for structured logging
logger.logRequest = (req, message) => {
logger.info(message, {
method: req.method,
url: req.originalUrl,
ip: req.ip,
userId: req.user?.id,
userAgent: req.get('user-agent')
});
};
logger.logError = (error, req = null) => {
const errorLog = {
message: error.message,
stack: error.stack,
statusCode: error.statusCode || 500
};
if (req) {
errorLog.method = req.method;
errorLog.url = req.originalUrl;
errorLog.ip = req.ip;
errorLog.userId = req.user?.id;
errorLog.body = req.body;
}
logger.error('Application Error', errorLog);
};
logger.logDatabaseQuery = (query, duration) => {
logger.debug('Database Query', {
query,
duration: `${duration}ms`
});
};
logger.logSecurityEvent = (event, req) => {
logger.warn('Security Event', {
event,
method: req.method,
url: req.originalUrl,
ip: req.ip,
userAgent: req.get('user-agent')
});
};
module.exports = logger;