Skip to main content
Glama
apolosan

Design Patterns MCP Server

by apolosan
logger.ts9.02 kB
/** * Structured Logging Service for Design Patterns MCP Server * Provides consistent logging across all services with multiple output formats */ enum LogLevel { DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3, FATAL = 4 } interface LogEntry { timestamp: string; level: LogLevel; service: string; message: string; data?: any; error?: Error; correlationId?: string; duration?: number; } interface LoggerConfig { level: LogLevel; format: 'json' | 'text'; enableConsole: boolean; enableFile: boolean; logFile?: string; maxFileSize: number; maxFiles: number; } export class Logger { private config: LoggerConfig; private logBuffer: LogEntry[] = []; private bufferSize = 100; constructor(config: LoggerConfig) { this.config = config; } /** * Log debug message */ debug(service: string, message: string, data?: any, correlationId?: string): void { this.log(LogLevel.DEBUG, service, message, data, undefined, correlationId); } /** * Log info message */ info(service: string, message: string, data?: any, correlationId?: string): void { this.log(LogLevel.INFO, service, message, data, undefined, correlationId); } /** * Log warning message */ warn(service: string, message: string, data?: any, correlationId?: string): void { this.log(LogLevel.WARN, service, message, data, undefined, correlationId); } /** * Log error message */ error(service: string, message: string, error?: Error, data?: any, correlationId?: string): void { this.log(LogLevel.ERROR, service, message, data, error, correlationId); } /** * Log fatal error message */ fatal(service: string, message: string, error?: Error, data?: any, correlationId?: string): void { this.log(LogLevel.FATAL, service, message, data, error, correlationId); } /** * Log performance timing */ timing(service: string, operation: string, duration: number, data?: any, correlationId?: string): void { this.log(LogLevel.INFO, service, `Operation completed: ${operation}`, { ...data, operation, duration, durationUnit: 'ms' }, undefined, correlationId, duration); } /** * Create child logger with service context */ child(service: string): Logger { return new ChildLogger(this, service) as any as Logger; } /** * Internal logging method */ private log( level: LogLevel, service: string, message: string, data?: any, error?: Error, correlationId?: string, duration?: number ): void { // Check if we should log this level if (level < this.config.level) { return; } const entry: LogEntry = { timestamp: new Date().toISOString(), level, service, message, data, error, correlationId, duration }; // Add to buffer this.logBuffer.push(entry); // Flush if buffer is full if (this.logBuffer.length >= this.bufferSize) { this.flush(); } // Immediate logging for errors and above if (level >= LogLevel.ERROR) { this.writeEntry(entry); } } /** * Flush buffered log entries */ private flush(): void { const entries = [...this.logBuffer]; this.logBuffer = []; entries.forEach(entry => this.writeEntry(entry)); } /** * Write log entry to configured outputs */ private writeEntry(entry: LogEntry): void { const formattedEntry = this.formatEntry(entry); if (this.config.enableConsole) { this.writeToConsole(entry, formattedEntry); } if (this.config.enableFile && this.config.logFile) { this.writeToFile(formattedEntry); } } /** * Format log entry based on configuration */ private formatEntry(entry: LogEntry): string { if (this.config.format === 'json') { return JSON.stringify({ timestamp: entry.timestamp, level: LogLevel[entry.level], service: entry.service, message: entry.message, correlationId: entry.correlationId, duration: entry.duration, data: entry.data, error: entry.error ? { name: entry.error.name, message: entry.error.message, stack: entry.error.stack } : undefined }); } else { const levelStr = LogLevel[entry.level].padEnd(5); const serviceStr = entry.service.padEnd(20); const correlationStr = entry.correlationId ? `[${entry.correlationId}] ` : ''; const durationStr = entry.duration ? ` (${entry.duration}ms)` : ''; const dataStr = entry.data ? ` ${JSON.stringify(entry.data)}` : ''; const errorStr = entry.error ? ` Error: ${entry.error.message}` : ''; return `${entry.timestamp} ${levelStr} ${serviceStr} ${correlationStr}${entry.message}${durationStr}${dataStr}${errorStr}`; } } /** * Write to console with appropriate coloring */ private writeToConsole(entry: LogEntry, formattedEntry: string): void { const stream = entry.level >= LogLevel.ERROR ? process.stderr : process.stdout; if (this.config.format === 'text') { // Add ANSI color codes for text format const color = this.getColorForLevel(entry.level); stream.write(`${color}${formattedEntry}\x1b[0m\n`); } else { stream.write(`${formattedEntry}\n`); } } /** * Get ANSI color code for log level */ private getColorForLevel(level: LogLevel): string { switch (level) { case LogLevel.DEBUG: return '\x1b[36m'; // Cyan case LogLevel.INFO: return '\x1b[32m'; // Green case LogLevel.WARN: return '\x1b[33m'; // Yellow case LogLevel.ERROR: return '\x1b[31m'; // Red case LogLevel.FATAL: return '\x1b[35m'; // Magenta default: return '\x1b[0m'; // Reset } } /** * Write to log file */ private writeToFile(formattedEntry: string): void { try { if (!this.config.logFile) return; // Ensure log directory exists const fs = require('fs'); const path = require('path'); const logDir = path.dirname(this.config.logFile); if (!fs.existsSync(logDir)) { fs.mkdirSync(logDir, { recursive: true }); } fs.appendFileSync(this.config.logFile, `${formattedEntry}\n`); } catch (error) { // Fallback to console if file logging fails console.error('Failed to write to log file:', error); } } /** * Get current log statistics */ getStats(): { buffered: number; totalLogged: number } { return { buffered: this.logBuffer.length, totalLogged: this.logBuffer.length // This is a simple implementation }; } /** * Force flush all buffered entries */ flushAll(): void { this.flush(); } } /** * Child logger that inherits configuration but adds service context */ class ChildLogger extends Logger { private parentLogger: Logger; private serviceName: string; constructor(parent: Logger, service: string) { // Create a minimal config for the child - it will delegate to parent super({ level: LogLevel.DEBUG, format: 'text', enableConsole: false, enableFile: false, maxFileSize: 0, maxFiles: 0 }); this.parentLogger = parent; this.serviceName = service; } debug(service: string, message: string, data?: any, correlationId?: string): void { this.parentLogger.debug(service || this.serviceName, message, data, correlationId); } info(service: string, message: string, data?: any, correlationId?: string): void { this.parentLogger.info(service || this.serviceName, message, data, correlationId); } warn(service: string, message: string, data?: any, correlationId?: string): void { this.parentLogger.warn(service || this.serviceName, message, data, correlationId); } error(service: string, message: string, error?: Error, data?: any, correlationId?: string): void { this.parentLogger.error(service || this.serviceName, message, error, data, correlationId); } fatal(service: string, message: string, error?: Error, data?: any, correlationId?: string): void { this.parentLogger.fatal(service || this.serviceName, message, error, data, correlationId); } timing(service: string, operation: string, duration: number, data?: any, correlationId?: string): void { this.parentLogger.timing(service || this.serviceName, operation, duration, data, correlationId); } child(service: string): Logger { return new ChildLogger(this.parentLogger, service); } } // Default configuration const DEFAULT_LOGGER_CONFIG: LoggerConfig = { level: LogLevel.INFO, format: 'text', enableConsole: true, enableFile: false, logFile: './logs/design-patterns-mcp.log', maxFileSize: 10 * 1024 * 1024, // 10MB maxFiles: 5 }; // Factory function function createLogger(config?: Partial<LoggerConfig>): Logger { const finalConfig = { ...DEFAULT_LOGGER_CONFIG, ...config }; return new Logger(finalConfig); } // Global logger instance export const logger = createLogger();

Latest Blog Posts

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/apolosan/design_patterns_mcp'

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