Skip to main content
Glama

SFCC Development MCP Server

by taurgis
logger.ts7.8 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 { private context: string; private enableTimestamp: boolean; private debugEnabled: boolean; private logDir: string; private static instance: Logger | null = 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: string = 'SFCC-MCP', enableTimestamp: boolean = true, debugEnabled: boolean = false, customLogDir?: string) { 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 */ public static initialize(context: string = 'SFCC-MCP', enableTimestamp: boolean = true, debugEnabled: boolean = false, customLogDir?: string): void { Logger.instance = new Logger(context, enableTimestamp, debugEnabled, customLogDir); } /** * Get the global logger instance * If not initialized, creates a default instance */ public static getInstance(): Logger { 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 */ public static getChildLogger(subContext: string): Logger { 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 */ private formatMessage(message: string): string { const timestamp = this.enableTimestamp ? `[${new Date().toISOString()}] ` : ''; return `${timestamp}[${this.context}] ${message}`; } /** * Write log message to appropriate log file */ private writeLog(level: 'info' | 'warn' | 'error' | 'debug', message: string, ...args: any[]): void { 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 */ public log(message: string, ...args: any[]): void { this.writeLog('info', message, ...args); } /** * Log an informational message (alias for log) * @param message The message to log * @param args Optional arguments to include */ public info(message: string, ...args: any[]): void { this.writeLog('info', message, ...args); } /** * Log a warning message * @param message The warning message to log * @param args Optional arguments to include */ public warn(message: string, ...args: any[]): void { this.writeLog('warn', message, ...args); } /** * Log an error message * @param message The error message to log * @param args Optional arguments to include */ public error(message: string, ...args: any[]): void { 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 */ public debug(message: string, ...args: any[]): void { 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 */ public methodEntry(methodName: string, params?: any): void { 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 */ public methodExit(methodName: string, result?: any): void { 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()) */ public timing(operation: string, startTime: number): void { 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 */ public createChildLogger(subContext: string): Logger { 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 */ public setDebugEnabled(enabled: boolean): void { this.debugEnabled = enabled; } /** * Get the current log directory */ public getLogDirectory(): string { return this.logDir; } } // Export the singleton instance getter for convenience export const getLogger = Logger.getInstance;

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/taurgis/sfcc-dev-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server