Skip to main content
Glama

Bruno MCP Server

by jcr82
logger.ts•6.72 kB
import * as fs from 'fs/promises'; import * as path from 'path'; import { getConfigLoader } from './config.js'; export type LogLevel = 'debug' | 'info' | 'warning' | 'error'; export type LogFormat = 'json' | 'text'; interface LogEntry { timestamp: string; level: LogLevel; message: string; context?: Record<string, any>; error?: { message: string; stack?: string; }; } class Logger { private logFilePath?: string; private maxLogSize: number = 10 * 1024 * 1024; // 10MB default private maxLogFiles: number = 5; // Keep 5 rotated files private currentLogSize: number = 0; private directoryInitialized: boolean = false; constructor() { this.initializeLogger(); } private initializeLogger() { const config = getConfigLoader().getConfig(); if (config.logging?.logFile) { this.logFilePath = config.logging.logFile; } if (config.logging?.maxLogSize) { this.maxLogSize = config.logging.maxLogSize; } if (config.logging?.maxLogFiles) { this.maxLogFiles = config.logging.maxLogFiles; } } // Ensure log directory exists (called before first write) private async ensureLogDirectory() { if (!this.logFilePath) return; const logDir = path.dirname(this.logFilePath); try { await fs.mkdir(logDir, { recursive: true }); } catch (error) { console.error('Failed to create log directory:', error); } // Get current log file size if file exists try { const stats = await fs.stat(this.logFilePath); this.currentLogSize = stats.size; } catch { this.currentLogSize = 0; } } private shouldLog(level: LogLevel): boolean { const config = getConfigLoader().getConfig(); const configLevel = config.logging?.level || 'info'; const levels: LogLevel[] = ['debug', 'info', 'warning', 'error']; const configLevelIndex = levels.indexOf(configLevel); const messageLevelIndex = levels.indexOf(level); return messageLevelIndex >= configLevelIndex; } private formatLogEntry(entry: LogEntry): string { const config = getConfigLoader().getConfig(); const format = config.logging?.format || 'text'; if (format === 'json') { return JSON.stringify(entry); } // Text format let message = `[${entry.timestamp}] ${entry.level.toUpperCase()}: ${entry.message}`; if (entry.context && Object.keys(entry.context).length > 0) { message += ` | Context: ${JSON.stringify(entry.context)}`; } if (entry.error) { message += `\n Error: ${entry.error.message}`; if (entry.error.stack) { message += `\n Stack: ${entry.error.stack}`; } } return message; } private async writeToFile(message: string) { if (!this.logFilePath) return; try { // Ensure directory exists on first write if (!this.directoryInitialized) { await this.ensureLogDirectory(); this.directoryInitialized = true; } // Check if rotation is needed if (this.currentLogSize + message.length > this.maxLogSize) { await this.rotateLogFiles(); this.currentLogSize = 0; } await fs.appendFile(this.logFilePath, message + '\n'); this.currentLogSize += message.length + 1; } catch (error) { console.error('Failed to write to log file:', error); } } private async rotateLogFiles() { if (!this.logFilePath) return; try { // Rotate existing log files for (let i = this.maxLogFiles - 1; i >= 1; i--) { const oldFile = `${this.logFilePath}.${i}`; const newFile = `${this.logFilePath}.${i + 1}`; try { await fs.rename(oldFile, newFile); } catch { // File might not exist, ignore } } // Move current log to .1 try { await fs.rename(this.logFilePath, `${this.logFilePath}.1`); } catch { // Current file might not exist } // Delete oldest log file if it exists if (this.maxLogFiles > 0) { try { await fs.unlink(`${this.logFilePath}.${this.maxLogFiles + 1}`); } catch { // File might not exist } } } catch (error) { console.error('Failed to rotate log files:', error); } } private async log(level: LogLevel, message: string, context?: Record<string, any>, error?: Error) { if (!this.shouldLog(level)) return; const entry: LogEntry = { timestamp: new Date().toISOString(), level, message, context, error: error ? { message: error.message, stack: error.stack } : undefined }; const formattedMessage = this.formatLogEntry(entry); // Always write to stderr for console output console.error(formattedMessage); // Write to file if configured if (this.logFilePath) { await this.writeToFile(formattedMessage); } } debug(message: string, context?: Record<string, any>) { return this.log('debug', message, context); } info(message: string, context?: Record<string, any>) { return this.log('info', message, context); } warning(message: string, context?: Record<string, any>) { return this.log('warning', message, context); } error(message: string, error?: Error, context?: Record<string, any>) { return this.log('error', message, context, error); } // Log tool execution logToolExecution(toolName: string, params: any, duration: number, success: boolean) { const level: LogLevel = success ? 'info' : 'error'; this.log(level, `Tool execution: ${toolName}`, { tool: toolName, duration, success, params: this.sanitizeParams(params) }); } // Log security events logSecurityEvent(event: string, details: string, severity: 'info' | 'warning' | 'error') { const level: LogLevel = severity === 'info' ? 'info' : severity === 'warning' ? 'warning' : 'error'; this.log(level, `Security: ${event}`, { event, details, severity }); } // Sanitize sensitive parameters private sanitizeParams(params: any): any { if (!params) return params; const sanitized = { ...params }; const sensitiveKeys = ['password', 'token', 'apikey', 'secret', 'authorization']; for (const key of Object.keys(sanitized)) { if (sensitiveKeys.some(sk => key.toLowerCase().includes(sk))) { sanitized[key] = '***'; } } return sanitized; } } // Singleton instance let loggerInstance: Logger | null = null; export function getLogger(): Logger { if (!loggerInstance) { loggerInstance = new Logger(); } return loggerInstance; } export function resetLogger() { loggerInstance = null; }

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/jcr82/bruno-mcp-server'

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