Skip to main content
Glama
debug-logger.ts6.93 kB
/** * Debug Logger Utility * Provides detailed method entry/exit logging with timestamps and parameters * Only active when debug mode is enabled */ import { ConfigLoader } from '../config/config-loader.js'; import * as fs from 'fs'; import * as path from 'path'; export class DebugLogger { private static instance: DebugLogger; private isDebugMode: boolean; private config = ConfigLoader.getInstance(); private logStream?: fs.WriteStream; private logFilePath: string; private constructor() { this.isDebugMode = this.config.getConfig().server.debug || false; // Disable file logging - use stderr/stdout only this.logFilePath = ''; this.logStream = undefined; if (this.isDebugMode) { console.error(`[DEBUG] Logging to stderr (file logging disabled)`); } } /** * Write a log entry to stderr only */ private writeLog(message: string): void { console.error(message); } /** * Write to log file (disabled - no-op) */ private writeToFile(message: string): void { // File logging disabled - no-op } static getInstance(): DebugLogger { if (!DebugLogger.instance) { DebugLogger.instance = new DebugLogger(); } return DebugLogger.instance; } /** * Get formatted timestamp for log entries */ private getTimestamp(): string { const now = new Date(); return `[${now.toISOString()}]`; } /** * Safely stringify parameters, handling circular references and sensitive data */ private stringifyParams(params: any): string { try { const seen = new WeakSet(); return JSON.stringify(params, (key, value) => { // Hide sensitive data - but not in debug mode for Authorization if (key.toLowerCase().includes('password') || key.toLowerCase().includes('token') || key.toLowerCase().includes('secret')) { return '[REDACTED]'; } // Don't redact Authorization in debug mode - we need to see it if (key.toLowerCase() === 'authorization') { return value; // Show the actual value for debugging } // Handle circular references if (typeof value === 'object' && value !== null) { if (seen.has(value)) { return '[Circular]'; } seen.add(value); } // Limit string length to avoid log flooding if (typeof value === 'string' && value.length > 500) { return value.substring(0, 500) + '... [truncated]'; } return value; }, 2); } catch (error) { return '[Unable to stringify]'; } } /** * Log method entry with parameters */ methodEntry(className: string, methodName: string, params?: any): void { if (!this.isDebugMode) return; const timestamp = this.getTimestamp(); const paramStr = params ? this.stringifyParams(params) : 'no parameters'; this.writeLog(`${timestamp} [DEBUG] >>> ENTER ${className}.${methodName}()`); this.writeLog(`${timestamp} [DEBUG] Parameters: ${paramStr}`); } /** * Log method exit with optional return value */ methodExit(className: string, methodName: string, returnValue?: any): void { if (!this.isDebugMode) return; const timestamp = this.getTimestamp(); const returnStr = returnValue !== undefined ? this.stringifyParams(returnValue) : 'void'; this.writeLog(`${timestamp} [DEBUG] <<< EXIT ${className}.${methodName}()`); if (returnValue !== undefined) { this.writeLog(`${timestamp} [DEBUG] Return: ${returnStr}`); } } /** * Log important state changes or decisions */ logState(className: string, message: string, data?: any): void { if (!this.isDebugMode) return; const timestamp = this.getTimestamp(); const dataStr = data ? this.stringifyParams(data) : ''; this.writeLog(`${timestamp} [DEBUG] [${className}] ${message}`); if (data) { this.writeLog(`${timestamp} [DEBUG] Data: ${dataStr}`); } } /** * Log API calls with details */ logApiCall(method: string, url: string, headers?: any, body?: any): void { if (!this.isDebugMode) return; const timestamp = this.getTimestamp(); this.writeLog(`${timestamp} [DEBUG] === API CALL ===`); this.writeLog(`${timestamp} [DEBUG] Method: ${method}`); this.writeLog(`${timestamp} [DEBUG] URL: ${url}`); if (headers) { this.writeLog(`${timestamp} [DEBUG] Headers: ${this.stringifyParams(headers)}`); } if (body) { this.writeLog(`${timestamp} [DEBUG] Body: ${this.stringifyParams(body)}`); } } /** * Log API response */ logApiResponse(url: string, status: number, data?: any): void { if (!this.isDebugMode) return; const timestamp = this.getTimestamp(); this.writeLog(`${timestamp} [DEBUG] === API RESPONSE ===`); this.writeLog(`${timestamp} [DEBUG] URL: ${url}`); this.writeLog(`${timestamp} [DEBUG] Status: ${status}`); if (data) { this.writeLog(`${timestamp} [DEBUG] Data: ${this.stringifyParams(data)}`); } } /** * Log errors with context */ logError(className: string, methodName: string, error: any, context?: any): void { // Always log errors, even in release mode const timestamp = this.getTimestamp(); const errorMsg1 = `${timestamp} [ERROR] !!! ERROR in ${className}.${methodName}()`; const errorMsg2 = `${timestamp} [ERROR] Message: ${error.message || error}`; // Always write to stderr for errors console.error(errorMsg1); console.error(errorMsg2); // Also write to file if in debug mode if (this.isDebugMode) { this.writeToFile(errorMsg1); this.writeToFile(errorMsg2); if (error.stack) { const stackMsg = `${timestamp} [ERROR] Stack: ${error.stack}`; console.error(stackMsg); this.writeToFile(stackMsg); } if (context) { const contextMsg = `${timestamp} [ERROR] Context: ${this.stringifyParams(context)}`; console.error(contextMsg); this.writeToFile(contextMsg); } } } /** * Check if debug mode is enabled */ isDebugEnabled(): boolean { return this.isDebugMode; } /** * Get the current log file path */ getLogFilePath(): string | undefined { return this.isDebugMode ? this.logFilePath : undefined; } /** * Clean up and close the log file (disabled - no-op) */ close(): void { // File logging disabled - no-op } } // Export singleton instance for convenience export const debugLog = DebugLogger.getInstance(); // Ensure cleanup on process exit process.on('exit', () => { debugLog.close(); }); process.on('SIGINT', () => { debugLog.close(); process.exit(); }); process.on('SIGTERM', () => { debugLog.close(); process.exit(); });

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/daviddraiumbrella/invoice-monitoring'

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