Skip to main content
Glama
apolosan

Design Patterns MCP Server

by apolosan
logger.ts14.4 kB
/** * Enhanced Structured JSON Logger for Design Patterns MCP Server * Provides comprehensive logging with structured data, correlation IDs, * performance tracking, and multiple output formats */ import { writeFileSync, existsSync, mkdirSync } from 'fs'; import { dirname } from 'path'; enum LogLevel { DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3, FATAL = 4, } interface StructuredLogEntry { timestamp: string; level: string; service: string; message: string; correlationId?: string; requestId?: string; userId?: string; sessionId?: string; operation?: string; duration?: number; statusCode?: number; method?: string; url?: string; userAgent?: string; ip?: string; data?: Record<string, any>; error?: { name: string; message: string; stack?: string; code?: string; }; performance?: { memoryUsage?: NodeJS.MemoryUsage; cpuUsage?: NodeJS.CpuUsage; uptime?: number; }; context?: Record<string, any>; } interface LoggerConfig { level: LogLevel; format: 'json' | 'text' | 'pretty'; enableConsole: boolean; enableFile: boolean; logFile?: string; maxFileSize: number; maxFiles: number; enableRotation: boolean; enableCompression: boolean; structuredData: boolean; includeStackTrace: boolean; includePerformanceMetrics: boolean; } export class StructuredLogger { private config: LoggerConfig; private logBuffer: StructuredLogEntry[] = []; private bufferSize = 100; private currentFileSize = 0; private fileIndex = 0; constructor(config: LoggerConfig) { this.config = config; } /** * Log debug message with structured data */ debug( service: string, message: string, data?: Record<string, any>, context?: Record<string, any> ): void { this.log(LogLevel.DEBUG, service, message, data, undefined, context); } /** * Log info message with structured data */ info( service: string, message: string, data?: Record<string, any>, context?: Record<string, any> ): void { this.log(LogLevel.INFO, service, message, data, undefined, context); } /** * Log warning message with structured data */ warn( service: string, message: string, data?: Record<string, any>, context?: Record<string, any> ): void { this.log(LogLevel.WARN, service, message, data, undefined, context); } /** * Log error message with structured error data */ error( service: string, message: string, error?: Error, data?: Record<string, any>, context?: Record<string, any> ): void { this.log(LogLevel.ERROR, service, message, data, error, context); } /** * Log fatal error message */ fatal( service: string, message: string, error?: Error, data?: Record<string, any>, context?: Record<string, any> ): void { this.log(LogLevel.FATAL, service, message, data, error, context); } /** * Log HTTP request with structured data */ http( service: string, method: string, url: string, statusCode: number, duration: number, userAgent?: string, ip?: string, context?: Record<string, any> ): void { this.log( LogLevel.INFO, service, `HTTP ${method} ${url}`, { method, url, statusCode, duration, userAgent, ip, }, undefined, context ); } /** * Log database operation with performance data */ database( service: string, operation: string, table: string, duration: number, recordCount?: number, context?: Record<string, any> ): void { this.log( LogLevel.INFO, service, `Database ${operation} on ${table}`, { operation, table, duration, recordCount, }, undefined, context ); } /** * Log performance timing with detailed metrics */ timing( service: string, operation: string, duration: number, data?: Record<string, any>, context?: Record<string, any> ): void { const performanceData = this.config.includePerformanceMetrics ? { memoryUsage: process.memoryUsage(), cpuUsage: process.cpuUsage(), uptime: process.uptime(), } : undefined; this.log( LogLevel.INFO, service, `Operation completed: ${operation}`, { ...data, operation, duration, durationUnit: 'ms', }, undefined, { ...context, performance: performanceData, } ); } /** * Create child logger with service context */ child(service: string): StructuredLogger { return new ChildStructuredLogger(this, service); } /** * Internal structured logging method */ private log( level: LogLevel, service: string, message: string, data?: Record<string, any>, error?: Error, context?: Record<string, any> ): void { // Check if we should log this level if (level < this.config.level) { return; } const entry: StructuredLogEntry = { timestamp: new Date().toISOString(), level: LogLevel[level], service, message, ...context, data: this.config.structuredData ? data : undefined, error: error ? { name: error.name, message: error.message, code: (error as any).code, stack: this.config.includeStackTrace ? error.stack : undefined, } : undefined, }; // Add to buffer this.logBuffer.push(entry); // Flush if buffer is full or for high-priority logs if (this.logBuffer.length >= this.bufferSize || level >= LogLevel.ERROR) { this.flush(); } } /** * Flush buffered log entries */ private flush(): void { if (this.logBuffer.length === 0) return; const entries = [...this.logBuffer]; this.logBuffer = []; entries.forEach(entry => this.writeEntry(entry)); } /** * Write log entry to configured outputs */ private writeEntry(entry: StructuredLogEntry): 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: StructuredLogEntry): string { switch (this.config.format) { case 'json': return JSON.stringify(entry, null, this.config.structuredData ? 2 : 0); case 'pretty': return this.formatPretty(entry); case 'text': default: return this.formatText(entry); } } /** * Format entry as pretty-printed JSON */ private formatPretty(entry: StructuredLogEntry): string { const color = this.getColorForLevel(LogLevel[entry.level as keyof typeof LogLevel]); const reset = '\x1b[0m'; const jsonStr = JSON.stringify(entry, null, 2); return `${color}${jsonStr}${reset}`; } /** * Format entry as human-readable text */ private formatText(entry: StructuredLogEntry): string { const levelStr = 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 && Object.keys(entry.data).length > 0 ? ` ${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: StructuredLogEntry, formattedEntry: string): void { const stream = LogLevel[entry.level as keyof typeof LogLevel] >= LogLevel.ERROR ? process.stderr : process.stdout; if (this.config.format === 'text') { const color = this.getColorForLevel(LogLevel[entry.level as keyof typeof LogLevel]); 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 with rotation support */ private writeToFile(formattedEntry: string): void { try { if (!this.config.logFile) return; // Ensure log directory exists const logDir = dirname(this.config.logFile); if (!existsSync(logDir)) { mkdirSync(logDir, { recursive: true }); } // Check if rotation is needed if (this.config.enableRotation && this.currentFileSize >= this.config.maxFileSize) { this.rotateLogFile(); } // Write to file const entryWithNewline = `${formattedEntry}\n`; writeFileSync(this.config.logFile, entryWithNewline, { flag: 'a' }); this.currentFileSize += Buffer.byteLength(entryWithNewline, 'utf8'); } catch (error) { // Fallback to console if file logging fails console.error('Failed to write to log file:', error); } } /** * Rotate log file when size limit is reached */ private rotateLogFile(): void { if (!this.config.logFile) return; try { // Rename current file const rotatedFile = `${this.config.logFile}.${this.fileIndex}`; if (existsSync(this.config.logFile)) { writeFileSync(rotatedFile, ''); // In a real implementation, you'd copy the content here } // Reset current file writeFileSync(this.config.logFile, ''); this.currentFileSize = 0; this.fileIndex = (this.fileIndex + 1) % this.config.maxFiles; } catch (error) { console.error('Failed to rotate log file:', error); } } /** * Get current log statistics */ getStats(): { buffered: number; currentFileSize: number; fileIndex: number; } { return { buffered: this.logBuffer.length, currentFileSize: this.currentFileSize, fileIndex: this.fileIndex, }; } /** * Force flush all buffered entries */ flushAll(): void { this.flush(); } } /** * Child logger that inherits configuration but adds service context */ class ChildStructuredLogger extends StructuredLogger { private parentLogger: StructuredLogger; private serviceName: string; constructor(parent: StructuredLogger, service: string) { // Create a minimal config for the child - it will delegate to parent super({ level: LogLevel.DEBUG, format: 'json', enableConsole: false, enableFile: false, maxFileSize: 0, maxFiles: 0, enableRotation: false, enableCompression: false, structuredData: true, includeStackTrace: false, includePerformanceMetrics: false, }); this.parentLogger = parent; this.serviceName = service; } debug( service: string, message: string, data?: Record<string, any>, context?: Record<string, any> ): void { this.parentLogger.debug(service || this.serviceName, message, data, context); } info( service: string, message: string, data?: Record<string, any>, context?: Record<string, any> ): void { this.parentLogger.info(service || this.serviceName, message, data, context); } warn( service: string, message: string, data?: Record<string, any>, context?: Record<string, any> ): void { this.parentLogger.warn(service || this.serviceName, message, data, context); } error( service: string, message: string, error?: Error, data?: Record<string, any>, context?: Record<string, any> ): void { this.parentLogger.error(service || this.serviceName, message, error, data, context); } fatal( service: string, message: string, error?: Error, data?: Record<string, any>, context?: Record<string, any> ): void { this.parentLogger.fatal(service || this.serviceName, message, error, data, context); } http( service: string, method: string, url: string, statusCode: number, duration: number, userAgent?: string, ip?: string, context?: Record<string, any> ): void { this.parentLogger.http( service || this.serviceName, method, url, statusCode, duration, userAgent, ip, context ); } database( service: string, operation: string, table: string, duration: number, recordCount?: number, context?: Record<string, any> ): void { this.parentLogger.database( service || this.serviceName, operation, table, duration, recordCount, context ); } timing( service: string, operation: string, duration: number, data?: Record<string, any>, context?: Record<string, any> ): void { this.parentLogger.timing(service || this.serviceName, operation, duration, data, context); } child(service: string): StructuredLogger { return new ChildStructuredLogger(this.parentLogger, service); } } // Default configuration const DEFAULT_STRUCTURED_LOGGER_CONFIG: LoggerConfig = { level: LogLevel.INFO, format: 'json', enableConsole: true, enableFile: true, logFile: './logs/design-patterns-mcp.log', maxFileSize: 10 * 1024 * 1024, // 10MB maxFiles: 5, enableRotation: true, enableCompression: false, structuredData: true, includeStackTrace: true, includePerformanceMetrics: false, }; // Factory function function createStructuredLogger(config?: Partial<LoggerConfig>): StructuredLogger { const finalConfig = { ...DEFAULT_STRUCTURED_LOGGER_CONFIG, ...config }; return new StructuredLogger(finalConfig); } // Global logger instance export const structuredLogger = createStructuredLogger();

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