UNPKG

sfcc-dev-mcp

Version:

MCP server for Salesforce B2C Commerce Cloud development assistance including logs, debugging, and development tools

200 lines 7.7 kB
/** * Logger class for standardized logging across the SFCC MCP application. * Provides consistent logging with timestamps and log levels. * Always logs to files for consistent debugging and to avoid interfering with stdio. * * ## Log Directory Location * * The logger uses the operating system's temporary directory via Node.js `os.tmpdir()`: * - **macOS**: `/var/folders/{user-specific-path}/T/sfcc-mcp-logs/` * - **Linux**: `/tmp/sfcc-mcp-logs/` (typically) * - **Windows**: `%TEMP%\sfcc-mcp-logs\` (typically `C:\Users\{user}\AppData\Local\Temp\`) * * This approach provides: * - User-specific isolation (more secure than system-wide `/tmp`) * - Automatic cleanup by the OS * - Platform-appropriate temporary storage * - Proper permissions handling * * To find your log directory, use `Logger.getInstance().getLogDirectory()` or check * the debug logs which show the directory path during initialization. */ import { appendFileSync, existsSync, mkdirSync } from 'fs'; import { join } from 'path'; import { tmpdir } from 'os'; export class Logger { context; enableTimestamp; debugEnabled; logDir; static instance = null; /** * Create a new Logger instance * @param context The context/component name for this logger * @param enableTimestamp Whether to include timestamps in log messages (default: true) * @param debugEnabled Whether to enable debug logging (default: false) * @param customLogDir Custom log directory for testing purposes */ constructor(context = 'SFCC-MCP', enableTimestamp = true, debugEnabled = false, customLogDir) { this.context = context; this.enableTimestamp = enableTimestamp; this.debugEnabled = debugEnabled; // Set up log directory - use custom directory for testing or default for production this.logDir = customLogDir ?? join(tmpdir(), 'sfcc-mcp-logs'); if (!existsSync(this.logDir)) { mkdirSync(this.logDir, { recursive: true }); } } /** * Initialize the global logger instance with specific settings * This should be called once at application startup */ static initialize(context = 'SFCC-MCP', enableTimestamp = true, debugEnabled = false, customLogDir) { Logger.instance = new Logger(context, enableTimestamp, debugEnabled, customLogDir); } /** * Get the global logger instance * If not initialized, creates a default instance */ static getInstance() { Logger.instance ??= new Logger(); return Logger.instance; } /** * Create a child logger with a new context but inheriting other settings from the global instance * @param subContext The sub-context to append to the current context * @returns A new Logger instance with the combined context */ static getChildLogger(subContext) { const globalLogger = Logger.getInstance(); return new Logger(`${globalLogger.context}:${subContext}`, globalLogger.enableTimestamp, globalLogger.debugEnabled, globalLogger.logDir); } /** * Format a log message with optional timestamp and context * @param message The message to format * @returns Formatted message string */ formatMessage(message) { const timestamp = this.enableTimestamp ? `[${new Date().toISOString()}] ` : ''; return `${timestamp}[${this.context}] ${message}`; } /** * Write log message to appropriate log file */ writeLog(level, message, ...args) { const formattedMessage = this.formatMessage(message); const fullMessage = args.length > 0 ? `${formattedMessage} ${args.map(arg => typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)).join(' ')}` : formattedMessage; // Always write to log files const logFile = join(this.logDir, `sfcc-mcp-${level}.log`); const logEntry = `${fullMessage}\n`; try { appendFileSync(logFile, logEntry, 'utf8'); } catch (error) { // Fallback: if file logging fails, try stderr for critical errors only if (level === 'error') { process.stderr.write(`[LOGGER ERROR] Could not write to log file: ${error}\n`); process.stderr.write(`${logEntry}`); } } } /** * Log an informational message * @param message The message to log * @param args Optional arguments to include */ log(message, ...args) { this.writeLog('info', message, ...args); } /** * Log an informational message (alias for log) * @param message The message to log * @param args Optional arguments to include */ info(message, ...args) { this.writeLog('info', message, ...args); } /** * Log a warning message * @param message The warning message to log * @param args Optional arguments to include */ warn(message, ...args) { this.writeLog('warn', message, ...args); } /** * Log an error message * @param message The error message to log * @param args Optional arguments to include */ error(message, ...args) { this.writeLog('error', message, ...args); } /** * Log a debug message (only if debug is enabled) * @param message The debug message to log * @param args Optional arguments to include */ debug(message, ...args) { if (this.debugEnabled) { this.writeLog('debug', `[DEBUG] ${message}`, ...args); } } /** * Log method entry with parameters * @param methodName The name of the method being entered * @param params Optional parameters being passed to the method */ methodEntry(methodName, params) { if (this.debugEnabled) { const paramStr = params ? ` with params: ${JSON.stringify(params)}` : ''; this.debug(`Entering method: ${methodName}${paramStr}`); } } /** * Log method exit with optional result * @param methodName The name of the method being exited * @param result Optional result being returned from the method */ methodExit(methodName, result) { if (this.debugEnabled) { const resultStr = result !== undefined ? ` with result: ${typeof result === 'object' ? JSON.stringify(result) : result}` : ''; this.debug(`Exiting method: ${methodName}${resultStr}`); } } /** * Log performance timing information * @param operation The operation being timed * @param startTime The start time (from performance.now() or Date.now()) */ timing(operation, startTime) { if (this.debugEnabled) { const duration = Date.now() - startTime; this.debug(`Performance: ${operation} took ${duration}ms`); } } /** * Create a child logger with a new context but inheriting other settings * @param subContext The sub-context to append to the current context * @returns A new Logger instance with the combined context */ createChildLogger(subContext) { return new Logger(`${this.context}:${subContext}`, this.enableTimestamp, this.debugEnabled, this.logDir); } /** * Enable or disable debug logging * @param enabled Whether debug logging should be enabled */ setDebugEnabled(enabled) { this.debugEnabled = enabled; } /** * Get the current log directory */ getLogDirectory() { return this.logDir; } } // Export the singleton instance getter for convenience export const getLogger = Logger.getInstance; //# sourceMappingURL=logger.js.map