149 lines
3.6 KiB
JavaScript
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;
|