Azure MCP Server
Official
by Streen9
Verified
- src
import winston from 'winston';
import DailyRotateFile from 'winston-daily-rotate-file';
import path from 'path';
import fs from 'fs';
class LoggerService {
private logger!: winston.Logger;
private readonly logDir: string;
private isMCPMode: boolean;
constructor() {
this.logDir = process.env.LOG_DIR || path.join(process.cwd(), 'logs');
this.isMCPMode = process.env.MCP_MODE === 'true';
// Ensure log directory exists
if (!fs.existsSync(this.logDir)) {
fs.mkdirSync(this.logDir, { recursive: true });
}
this.initializeLogger();
}
private initializeLogger(): void {
const logFormat = winston.format.combine(
winston.format.timestamp(),
winston.format.json()
);
// File transports are always active
const fileTransports = [
new DailyRotateFile({
dirname: this.logDir,
filename: 'azure-mcp-%DATE%.log',
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '14d',
format: logFormat
}) as winston.transport,
new DailyRotateFile({
dirname: this.logDir,
filename: 'azure-mcp-error-%DATE%.log',
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '30d',
level: 'error',
format: logFormat
}) as winston.transport
];
// In MCP mode, we only log to files and stderr
// In non-MCP mode, we can use console transport
const transports = this.isMCPMode ? fileTransports : [
...fileTransports,
new winston.transports.Console({
format: winston.format.combine(
winston.format.uncolorize(),
// winston.format.printf(({ level, message }) => {
// return `[${level.toUpperCase()}]: ${message}`;
// })
),
stderrLevels: ['error', 'warn', 'info'] // Send ALL console output to stderr
})
];
this.logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: logFormat,
exitOnError: false,
handleExceptions: true,
handleRejections: true,
transports: transports.map(transport => {
if (this.isMCPMode && transport instanceof winston.transports.Console) {
// Configure console transport to use stderr
transport.stderrLevels = ['error', 'warn', 'info', 'debug'];
}
return transport;
})
});
}
private formatMessage(message: string, meta?: any): string {
try {
const metaStr = meta ? (typeof meta === 'object' ? JSON.stringify(meta) : String(meta)) : '';
return `${message}${metaStr ? ' ' + metaStr : ''}`.trim();
} catch (error) {
return message;
}
}
private logToStderr(level: string, message: string): void {
// In MCP mode, all console output MUST go to stderr
if (this.isMCPMode) {
const timestamp = new Date().toISOString();
process.stderr.write(`[${timestamp}] [${level.toUpperCase()}]: ${message}\n`);
}
}
public info(message: string, meta?: any): void {
const formattedMessage = this.formatMessage(message, meta);
this.logToStderr('info', formattedMessage);
this.logger.info(formattedMessage);
}
public error(message: string, meta?: any): void {
const formattedMessage = this.formatMessage(message, meta);
this.logToStderr('error', formattedMessage);
this.logger.error(formattedMessage);
}
public warning(message: string, meta?: any): void {
const formattedMessage = this.formatMessage(message, meta);
this.logToStderr('warn', formattedMessage);
this.logger.warn(formattedMessage);
}
}
// Export a singleton instance
export default new LoggerService();