UNPKG

@debugmcp/mcp-debugger

Version:

Step-through debugging MCP server for LLMs

105 lines 4.32 kB
/** * Logger utility for the Debug MCP Server. */ import * as winston from 'winston'; import path from 'path'; import fs from 'fs'; import { fileURLToPath } from 'url'; let defaultLogger = null; /** * Create a winston logger with the given namespace. * * @param namespace - The logger namespace * @param options - Logger configuration options * @returns A configured winston logger instance */ export function createLogger(namespace, options = {}) { // Check for global log level from environment or options const level = options.level || process.env.DEBUG_MCP_LOG_LEVEL || 'info'; const transports = []; // When console output is silenced we MUST NOT write to stdout as it corrupts transports const isConsoleSilenced = process.env.CONSOLE_OUTPUT_SILENCED === '1'; if (!isConsoleSilenced) { // Only add console transport when NOT silencing console output transports.push(new winston.transports.Console({ format: winston.format.combine(winston.format.colorize(), winston.format.timestamp(), winston.format.printf(({ timestamp, level, message, ...rest }) => { return `${timestamp} [${level}] [${namespace}]: ${message} ${Object.keys(rest).length ? JSON.stringify(rest, null, 2) : ''}`; })) })); } // Handle cases where import.meta.url might be undefined (e.g., in test environments) let projectRootDefaultLogPath; try { if (import.meta.url) { const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); projectRootDefaultLogPath = path.resolve(__dirname, '../../logs/debug-mcp-server.log'); } else { // Fallback for test environments projectRootDefaultLogPath = path.resolve(process.cwd(), 'logs/debug-mcp-server.log'); } } catch { // Fallback if import.meta.url fails projectRootDefaultLogPath = path.resolve(process.cwd(), 'logs/debug-mcp-server.log'); } // In container runtime, centralize logs under /app/logs for easier collection if (process.env.MCP_CONTAINER === 'true') { projectRootDefaultLogPath = '/app/logs/debug-mcp-server.log'; } const logFilePath = options.file || projectRootDefaultLogPath; try { const logDir = path.dirname(logFilePath); if (!fs.existsSync(logDir)) { fs.mkdirSync(logDir, { recursive: true }); } } catch (e) { // When console output is silenced we must not write to console as it corrupts transports if (!isConsoleSilenced) { console.error(`[Logger Init Error] Failed to ensure log directory for ${logFilePath}:`, e); } } try { transports.push(new winston.transports.File({ filename: logFilePath, format: winston.format.combine(winston.format.timestamp(), winston.format.json()) })); } catch (fileTransportError) { // When console output is silenced we must not write to console as it corrupts transports if (!isConsoleSilenced) { console.error(`[Logger Init Error] Failed to create file transport for ${logFilePath}:`, fileTransportError); } } const logger = winston.createLogger({ level, transports, defaultMeta: { namespace }, exitOnError: false }); logger.on('error', (error) => { // When console output is silenced we must not write to console as it corrupts transports if (!isConsoleSilenced) { console.error('[Winston Logger Internal Error] Failed to write to a transport:', error); } }); // If this is the root logger, set it as the default if (namespace === 'debug-mcp') { defaultLogger = logger; } return logger; } /** * Get the default logger instance. If no root logger has been created, a fallback logger is created. * @returns The default logger instance. */ export function getLogger() { if (!defaultLogger) { defaultLogger = createLogger('debug-mcp:default-fallback', { level: 'info' }); defaultLogger.warn('[Logger] getLogger() called before root logger was initialized. Using fallback logger.'); } return defaultLogger; } //# sourceMappingURL=logger.js.map