166 lines
4.0 KiB
JavaScript
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; |