import * as fs from 'fs';
import * as path from 'path';
import * as winston from 'winston';
import 'winston-daily-rotate-file';
import { TransformableInfo } from 'logform';
//================================================================================
// Logger Configuration
//================================================================================
// Define Winston logger types
export interface LoggerMeta {
[key: string]: any;
}
// Use Winston's TransformableInfo type from logform
// Ensure logs directory exists
const logsDir = path.join(process.cwd(), 'logs');
if (!fs.existsSync(logsDir)) {
fs.mkdirSync(logsDir, { recursive: true });
}
// 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()
);
// Create logger instance
export const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: logFormat,
defaultMeta: { service: 'grep-app-server' },
transports: [
// Console transport for development
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.printf((info: TransformableInfo) => {
const { timestamp, level, message, ...meta } = info;
return `${timestamp} ${level}: ${message} ${Object.keys(meta).length ? JSON.stringify(meta, null, 2) : ''}`;
})
),
}),
// File transport with daily rotation
new winston.transports.DailyRotateFile({
filename: path.join(logsDir, 'application-%DATE%.log'),
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '14d',
format: logFormat
}),
// Separate error log file
new winston.transports.DailyRotateFile({
filename: path.join(logsDir, 'error-%DATE%.log'),
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '14d',
level: 'error',
format: logFormat
})
],
});
// Log unhandled exceptions and rejections
process.on('uncaughtException', (error) => {
logger.error('Uncaught exception', { error });
process.exit(1);
});
process.on('unhandledRejection', (reason) => {
logger.error('Unhandled rejection', { reason });
});
logger.info('Logger initialized');