Files

166 lines
4.0 KiB
JavaScript

const winston = require('winston');
const DailyRotateFile = require('winston-daily-rotate-file');
const path = require('path');
// Create logs directory if it doesn't exist
const logsDir = path.join(__dirname, '..', 'logs');
// Define log levels and colors
const logLevels = {
error: 0,
warn: 1,
info: 2,
http: 3,
debug: 4
};
const logColors = {
error: 'red',
warn: 'yellow',
info: 'green',
http: 'magenta',
debug: 'blue'
};
winston.addColors(logColors);
// Custom format for structured logging
const logFormat = winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.errors({ stack: true }),
winston.format.json(),
winston.format.printf(({ timestamp, level, message, stack, ...meta }) => {
let logMessage = `${timestamp} [${level.toUpperCase()}]: ${message}`;
// Add stack trace for errors
if (stack) {
logMessage += `\nStack: ${stack}`;
}
// Add metadata if present
if (Object.keys(meta).length > 0) {
logMessage += `\nMeta: ${JSON.stringify(meta, null, 2)}`;
}
return logMessage;
})
);
// Console format for development
const consoleFormat = winston.format.combine(
winston.format.colorize({ all: true }),
winston.format.timestamp({ format: 'HH:mm:ss' }),
winston.format.printf(({ timestamp, level, message, stack }) => {
let logMessage = `${timestamp} ${level}: ${message}`;
if (stack) {
logMessage += `\n${stack}`;
}
return logMessage;
})
);
// Create transports
const transports = [
// Console transport for development
new winston.transports.Console({
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
format: consoleFormat,
handleExceptions: true,
handleRejections: true
}),
// File transport for all logs
new DailyRotateFile({
filename: path.join(logsDir, 'application-%DATE%.log'),
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '14d',
level: 'debug',
format: logFormat,
handleExceptions: true,
handleRejections: true
}),
// Separate file for errors
new DailyRotateFile({
filename: path.join(logsDir, 'error-%DATE%.log'),
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '30d',
level: 'error',
format: logFormat,
handleExceptions: true,
handleRejections: true
}),
// HTTP requests log
new DailyRotateFile({
filename: path.join(logsDir, 'http-%DATE%.log'),
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '7d',
level: 'http',
format: logFormat
})
];
// Create logger instance
const logger = winston.createLogger({
levels: logLevels,
transports,
exitOnError: false
});
// Add request logging helper
logger.logRequest = (req, res, responseTime) => {
const logData = {
method: req.method,
url: req.originalUrl,
ip: req.ip || req.connection.remoteAddress,
userAgent: req.get('User-Agent'),
statusCode: res.statusCode,
responseTime: `${responseTime}ms`,
contentLength: res.get('Content-Length') || 0
};
// Log based on status code
if (res.statusCode >= 500) {
logger.error('HTTP Request Error', logData);
} else if (res.statusCode >= 400) {
logger.warn('HTTP Request Warning', logData);
} else {
logger.http('HTTP Request', logData);
}
};
// Add database operation logging helper
logger.logDbOperation = (operation, table, data = {}, duration = null) => {
const logData = {
operation,
table,
duration: duration ? `${duration}ms` : null,
...data
};
logger.debug('Database Operation', logData);
};
// Add error context helper
logger.logError = (error, context = {}) => {
const errorData = {
message: error.message,
stack: error.stack,
name: error.name,
code: error.code,
...context
};
logger.error('Application Error', errorData);
};
// Add business logic logging helper
logger.logBusinessEvent = (event, data = {}) => {
logger.info(`Business Event: ${event}`, data);
};
module.exports = logger;