Skip to main content
Glama

CodeCompass MCP

logger.ts8.69 kB
import { getConfig } from './config.js'; // Log levels export enum LogLevel { DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3, } // Log entry interface export interface LogEntry { timestamp: string; level: LogLevel; message: string; context?: Record<string, any>; error?: Error; duration?: number; requestId?: string; } // Logger configuration interface LoggerConfig { level: LogLevel; enableTimestamps: boolean; enableColors: boolean; enableJson: boolean; enableFile: boolean; logFile?: string; } // Color codes for console output const COLORS = { reset: '\x1b[0m', bright: '\x1b[1m', dim: '\x1b[2m', red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m', blue: '\x1b[34m', magenta: '\x1b[35m', cyan: '\x1b[36m', white: '\x1b[37m', gray: '\x1b[90m', }; // Log level colors const LEVEL_COLORS = { [LogLevel.DEBUG]: COLORS.gray, [LogLevel.INFO]: COLORS.blue, [LogLevel.WARN]: COLORS.yellow, [LogLevel.ERROR]: COLORS.red, }; // Log level names const LEVEL_NAMES = { [LogLevel.DEBUG]: 'DEBUG', [LogLevel.INFO]: 'INFO', [LogLevel.WARN]: 'WARN', [LogLevel.ERROR]: 'ERROR', }; class Logger { private config: LoggerConfig; private logBuffer: LogEntry[] = []; private requestCounter = 0; constructor() { const appConfig = getConfig(); this.config = { level: this.parseLogLevel(appConfig.logging.level), enableTimestamps: appConfig.logging.enableTimestamps, enableColors: appConfig.logging.enableColors, enableJson: process.env.NODE_ENV === 'production', enableFile: process.env.LOG_FILE !== undefined, logFile: process.env.LOG_FILE, }; } private parseLogLevel(level: string): LogLevel { switch (level.toLowerCase()) { case 'debug': return LogLevel.DEBUG; case 'info': return LogLevel.INFO; case 'warn': return LogLevel.WARN; case 'error': return LogLevel.ERROR; default: return LogLevel.INFO; } } private shouldLog(level: LogLevel): boolean { return level >= this.config.level; } private formatTimestamp(): string { return new Date().toISOString(); } private formatConsoleMessage(entry: LogEntry): string { let message = ''; if (this.config.enableColors) { const color = LEVEL_COLORS[entry.level]; const levelName = LEVEL_NAMES[entry.level].padEnd(5); if (this.config.enableTimestamps) { message += `${COLORS.gray}${entry.timestamp}${COLORS.reset} `; } message += `${color}${levelName}${COLORS.reset} `; if (entry.requestId) { message += `${COLORS.cyan}[${entry.requestId}]${COLORS.reset} `; } message += entry.message; if (entry.duration !== undefined) { message += ` ${COLORS.gray}(${entry.duration}ms)${COLORS.reset}`; } if (entry.context && Object.keys(entry.context).length > 0) { message += `\n${COLORS.dim}${JSON.stringify(entry.context, null, 2)}${COLORS.reset}`; } if (entry.error) { message += `\n${COLORS.red}${entry.error.stack || entry.error.message}${COLORS.reset}`; } } else { const levelName = LEVEL_NAMES[entry.level].padEnd(5); if (this.config.enableTimestamps) { message += `${entry.timestamp} `; } message += `${levelName} `; if (entry.requestId) { message += `[${entry.requestId}] `; } message += entry.message; if (entry.duration !== undefined) { message += ` (${entry.duration}ms)`; } if (entry.context && Object.keys(entry.context).length > 0) { message += `\n${JSON.stringify(entry.context, null, 2)}`; } if (entry.error) { message += `\n${entry.error.stack || entry.error.message}`; } } return message; } private formatJsonMessage(entry: LogEntry): string { return JSON.stringify({ timestamp: entry.timestamp, level: LEVEL_NAMES[entry.level], message: entry.message, context: entry.context, error: entry.error ? { message: entry.error.message, stack: entry.error.stack, name: entry.error.name, } : undefined, duration: entry.duration, requestId: entry.requestId, }); } private writeLog(entry: LogEntry): void { if (!this.shouldLog(entry.level)) { return; } // Add to buffer for monitoring this.logBuffer.push(entry); if (this.logBuffer.length > 1000) { this.logBuffer.shift(); } // Console output if (this.config.enableJson) { console.log(this.formatJsonMessage(entry)); } else { console.log(this.formatConsoleMessage(entry)); } // File output (if enabled) if (this.config.enableFile && this.config.logFile) { // In a real implementation, you'd write to file here // For now, we'll just use console } } public generateRequestId(): string { return `req-${Date.now()}-${++this.requestCounter}`; } public debug(message: string, context?: Record<string, any>, requestId?: string): void { this.writeLog({ timestamp: this.formatTimestamp(), level: LogLevel.DEBUG, message, context, requestId, }); } public info(message: string, context?: Record<string, any>, requestId?: string): void { this.writeLog({ timestamp: this.formatTimestamp(), level: LogLevel.INFO, message, context, requestId, }); } public warn(message: string, context?: Record<string, any>, requestId?: string): void { this.writeLog({ timestamp: this.formatTimestamp(), level: LogLevel.WARN, message, context, requestId, }); } public error(message: string, error?: Error, context?: Record<string, any>, requestId?: string): void { this.writeLog({ timestamp: this.formatTimestamp(), level: LogLevel.ERROR, message, error, context, requestId, }); } public timing(message: string, startTime: number, context?: Record<string, any>, requestId?: string): void { const duration = Date.now() - startTime; this.writeLog({ timestamp: this.formatTimestamp(), level: LogLevel.INFO, message, context, duration, requestId, }); } public getLogBuffer(): LogEntry[] { return [...this.logBuffer]; } public getStats(): { totalLogs: number; errorCount: number; warnCount: number; averageResponseTime: number; } { const totalLogs = this.logBuffer.length; const errorCount = this.logBuffer.filter(entry => entry.level === LogLevel.ERROR).length; const warnCount = this.logBuffer.filter(entry => entry.level === LogLevel.WARN).length; const timingEntries = this.logBuffer.filter(entry => entry.duration !== undefined); const averageResponseTime = timingEntries.length > 0 ? timingEntries.reduce((sum, entry) => sum + (entry.duration || 0), 0) / timingEntries.length : 0; return { totalLogs, errorCount, warnCount, averageResponseTime, }; } } // Global logger instance const logger = new Logger(); // Helper functions for structured logging export const log = { debug: (message: string, context?: Record<string, any>, requestId?: string) => logger.debug(message, context, requestId), info: (message: string, context?: Record<string, any>, requestId?: string) => logger.info(message, context, requestId), warn: (message: string, context?: Record<string, any>, requestId?: string) => logger.warn(message, context, requestId), error: (message: string, error?: Error, context?: Record<string, any>, requestId?: string) => logger.error(message, error, context, requestId), timing: (message: string, startTime: number, context?: Record<string, any>, requestId?: string) => logger.timing(message, startTime, context, requestId), generateRequestId: () => logger.generateRequestId(), getStats: () => logger.getStats(), getLogBuffer: () => logger.getLogBuffer(), }; // Performance monitoring helpers export function createPerformanceTimer(name: string, requestId?: string) { const startTime = Date.now(); return { end: (context?: Record<string, any>) => { log.timing(`${name} completed`, startTime, context, requestId); }, checkpoint: (checkpointName: string, context?: Record<string, any>) => { log.timing(`${name} - ${checkpointName}`, startTime, context, requestId); }, }; } // Export the logger instance export { logger }; export default log;

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/TheAlchemist6/codecompass-mcp'

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