← back to Watches
monitoring/logger.js
144 lines
/**
* Winston Logger Configuration
* Provides structured logging with rotation and multiple transports
*/
import winston from 'winston';
import DailyRotateFile from 'winston-daily-rotate-file';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const logDir = process.env.LOG_FILE_PATH || path.join(__dirname, '../logs');
// Custom 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 with colors
const consoleFormat = winston.format.combine(
winston.format.colorize(),
winston.format.timestamp({ format: 'HH:mm:ss' }),
winston.format.printf(({ timestamp, level, message, ...meta }) => {
let msg = `${timestamp} [${level}]: ${message}`;
if (Object.keys(meta).length > 0) {
msg += ` ${JSON.stringify(meta)}`;
}
return msg;
})
);
// Create logger instance
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: logFormat,
defaultMeta: { service: 'omega-watches' },
transports: [
// Console output
new winston.transports.Console({
format: consoleFormat
}),
// Error logs - separate file
new DailyRotateFile({
filename: path.join(logDir, 'error-%DATE%.log'),
datePattern: 'YYYY-MM-DD',
level: 'error',
maxSize: '20m',
maxFiles: '30d',
zippedArchive: true
}),
// Combined logs
new DailyRotateFile({
filename: path.join(logDir, 'combined-%DATE%.log'),
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '30d',
zippedArchive: true
}),
// Access logs
new DailyRotateFile({
filename: path.join(logDir, 'access-%DATE%.log'),
datePattern: 'YYYY-MM-DD',
level: 'http',
maxSize: '20m',
maxFiles: '14d',
zippedArchive: true
})
]
});
// Production error handling
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.File({
filename: path.join(logDir, 'debug.log'),
level: 'debug'
}));
}
// Create request logger middleware
export const requestLogger = (req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
logger.http({
method: req.method,
url: req.url,
status: res.statusCode,
duration: `${duration}ms`,
ip: req.ip,
userAgent: req.get('user-agent')
});
});
next();
};
// Error logger middleware
export const errorLogger = (err, req, res, next) => {
logger.error({
message: err.message,
stack: err.stack,
url: req.url,
method: req.method,
ip: req.ip,
body: req.body
});
res.status(err.status || 500).json({
error: process.env.NODE_ENV === 'production'
? 'Internal server error'
: err.message
});
};
// Performance monitoring
export const logPerformance = (operation, duration) => {
if (duration > 1000) {
logger.warn(`Slow operation: ${operation} took ${duration}ms`);
} else {
logger.debug(`Operation: ${operation} completed in ${duration}ms`);
}
};
// Business metrics logging
export const logMetric = (metric, value, tags = {}) => {
logger.info({
metric,
value,
tags,
timestamp: new Date().toISOString()
});
};
export default logger;