logger.tsโข3.08 kB
/**
* Logger Utility using Winston
*
* Provides structured logging with:
* - Multiple log levels (error, warn, info, debug)
* - File rotation (automatic cleanup of old logs)
* - Console and file output
* - Timestamp and context tracking
*
* ๐ TEACHING: Why Winston?
* - Industry standard for Node.js logging
* - Supports transports (console, file, remote services)
* - Automatic log rotation prevents disk space issues
* - Structured logging (JSON format) for easy parsing
*/
import winston from 'winston';
import { resolve } from 'path';
import { mkdirSync } from 'fs';
import type { LoggingConfig } from '../config/config.schema.js';
/**
* Create Winston logger instance
*/
export function createLogger(config: LoggingConfig): winston.Logger {
// Ensure logs directory exists
const logDir = resolve(config.file, '..');
try {
mkdirSync(logDir, { recursive: true });
} catch (error) {
// Directory might already exist, that's fine
}
const transports: winston.transport[] = [];
// Console transport
if (config.console) {
transports.push(
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
),
})
);
}
// File transport with rotation
transports.push(
new winston.transports.File({
filename: resolve(config.file),
maxsize: parseSizeToBytes(config.maxSize),
maxFiles: config.maxFiles,
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
})
);
return winston.createLogger({
level: config.level,
format: winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.errors({ stack: true }),
winston.format.splat(),
winston.format.json()
),
defaultMeta: { service: 'rs-waybill-mcp' },
transports,
});
}
/**
* Parse size string (e.g., '10m', '100k') to bytes
*/
function parseSizeToBytes(sizeStr: string): number {
const match = sizeStr.match(/^(\d+)([kmg]?)$/i);
if (!match) {
return 10 * 1024 * 1024; // Default 10MB
}
const value = parseInt(match[1], 10);
const unit = (match[2] || '').toLowerCase();
switch (unit) {
case 'k':
return value * 1024;
case 'm':
return value * 1024 * 1024;
case 'g':
return value * 1024 * 1024 * 1024;
default:
return value;
}
}
/**
* Global logger instance (initialized in main)
*/
let globalLogger: winston.Logger | null = null;
/**
* Initialize global logger
*/
export function initLogger(config: LoggingConfig): winston.Logger {
globalLogger = createLogger(config);
return globalLogger;
}
/**
* Get global logger instance
*/
export function getLogger(): winston.Logger {
if (!globalLogger) {
// Fallback logger if not initialized
globalLogger = winston.createLogger({
level: 'info',
format: winston.format.simple(),
transports: [new winston.transports.Console()],
});
}
return globalLogger;
}