/**
* Logger configuration for NIST CSF MCP Server
*/
import winston from 'winston';
import path from 'path';
// Define log levels
const levels = {
error: 0,
warn: 1,
info: 2,
debug: 3,
};
// Define colors for each level
const colors = {
error: 'red',
warn: 'yellow',
info: 'green',
debug: 'blue',
};
winston.addColors(colors);
// Create format for console output
const consoleFormat = winston.format.combine(
winston.format.colorize(),
winston.format.timestamp({ format: 'YYYY-MM-DD 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 format for file output
const fileFormat = winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.json()
);
// Determine if we're running as an MCP server (stdio transport)
const isMcpServer = process.argv.includes('--mcp') ||
process.env.MCP_SERVER === 'true' ||
process.stdin.isTTY === false;
// Create the logger
export const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
levels,
transports: [
// Console transport - only if NOT running as MCP server
// MCP servers must reserve stdout for JSON protocol
...(isMcpServer ? [] : [
new winston.transports.Console({
format: consoleFormat,
})
]),
// Stderr transport for MCP servers (optional, for debugging)
...(isMcpServer ? [
new winston.transports.Console({
stderrLevels: ['error', 'warn', 'info', 'debug'],
format: winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.printf(({ timestamp, level, message, ...meta }) => {
let msg = `${timestamp} [MCP] [${level}]: ${message}`;
if (Object.keys(meta).length > 0) {
msg += ` ${JSON.stringify(meta)}`;
}
return msg;
})
),
})
] : []),
// File transport for errors
new winston.transports.File({
filename: path.join(process.cwd(), 'logs/error.log'),
level: 'error',
format: fileFormat,
maxsize: 5242880, // 5MB
maxFiles: 5,
}),
// File transport for all logs
new winston.transports.File({
filename: path.join(process.cwd(), 'logs/combined.log'),
format: fileFormat,
maxsize: 10485760, // 10MB
maxFiles: 10,
}),
],
exitOnError: false,
});
// Handle unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
logger.error('Unhandled Rejection at:', { promise, reason });
});
// Handle uncaught exceptions
process.on('uncaughtException', (error) => {
logger.error('Uncaught Exception:', error);
process.exit(1);
});
export default logger;