UNPKG

@re-shell/cli

Version:

Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja

506 lines (505 loc) 19.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DebugSystem = exports.TraceLevel = void 0; exports.createDebugSystem = createDebugSystem; exports.getGlobalDebugSystem = getGlobalDebugSystem; exports.setGlobalDebugSystem = setGlobalDebugSystem; const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const events_1 = require("events"); const chalk_1 = __importDefault(require("chalk")); var TraceLevel; (function (TraceLevel) { TraceLevel[TraceLevel["TRACE"] = 0] = "TRACE"; TraceLevel[TraceLevel["DEBUG"] = 1] = "DEBUG"; TraceLevel[TraceLevel["INFO"] = 2] = "INFO"; TraceLevel[TraceLevel["WARN"] = 3] = "WARN"; TraceLevel[TraceLevel["ERROR"] = 4] = "ERROR"; })(TraceLevel || (exports.TraceLevel = TraceLevel = {})); class DebugSystem extends events_1.EventEmitter { constructor(options = {}) { super(); this.sessions = []; this.activeOperations = new Map(); this.traceCounter = 0; this.sessionCounter = 0; this.options = { enabled: false, level: TraceLevel.DEBUG, includeStack: false, includeMemory: true, includePerformance: true, maxTraces: 10000, maxSessions: 100, categories: [], excludeCategories: [], realtime: false, format: 'structured', ...options }; if (this.options.outputFile) { this.initializeOutputStream(); } if (this.options.realtime) { this.setupRealtimeOutput(); } } initializeOutputStream() { if (!this.options.outputFile) return; try { const dir = path.dirname(this.options.outputFile); fs.ensureDirSync(dir); this.outputStream = fs.createWriteStream(this.options.outputFile, { flags: 'a' }); this.outputStream.on('error', (error) => { console.error('Debug output stream error:', error); }); } catch (error) { console.error('Failed to initialize debug output stream:', error); } } setupRealtimeOutput() { this.on('trace', (trace) => { if (this.shouldOutputTrace(trace)) { this.outputTrace(trace); } }); } shouldOutputTrace(trace) { if (!this.options.enabled) return false; if (trace.level < this.options.level) return false; if (this.options.categories.length > 0 && !this.options.categories.includes(trace.category)) { return false; } if (this.options.excludeCategories.includes(trace.category)) { return false; } return true; } outputTrace(trace) { const output = this.formatTrace(trace); if (this.outputStream) { this.outputStream.write(output + '\n'); } else if (this.options.realtime) { console.log(output); } } formatTrace(trace) { switch (this.options.format) { case 'json': return JSON.stringify(trace); case 'text': return this.formatTraceAsText(trace); case 'structured': default: return this.formatTraceStructured(trace); } } formatTraceAsText(trace) { const timestamp = trace.timestamp.toISOString(); const level = TraceLevel[trace.level].padEnd(5); const duration = trace.duration ? ` (${trace.duration}ms)` : ''; const memory = this.options.includeMemory ? ` [${Math.round(trace.metadata.memory.heapUsed / 1024 / 1024)}MB]` : ''; return `${timestamp} ${level} ${trace.category}:${trace.operation}${duration}${memory}`; } formatTraceStructured(trace) { const parts = [ chalk_1.default.gray(trace.timestamp.toLocaleTimeString()), this.getLevelColor(trace.level)(TraceLevel[trace.level].padEnd(5)), chalk_1.default.cyan(`${trace.category}:`), chalk_1.default.white(trace.operation) ]; if (trace.duration !== undefined) { const color = trace.duration > 1000 ? chalk_1.default.red : trace.duration > 100 ? chalk_1.default.yellow : chalk_1.default.green; parts.push(color(`(${trace.duration}ms)`)); } if (this.options.includeMemory) { const memMB = Math.round(trace.metadata.memory.heapUsed / 1024 / 1024); parts.push(chalk_1.default.gray(`[${memMB}MB]`)); } if (trace.error) { parts.push(chalk_1.default.red(`ERROR: ${trace.error.message}`)); } return parts.join(' '); } getLevelColor(level) { switch (level) { case TraceLevel.TRACE: return chalk_1.default.gray; case TraceLevel.DEBUG: return chalk_1.default.blue; case TraceLevel.INFO: return chalk_1.default.green; case TraceLevel.WARN: return chalk_1.default.yellow; case TraceLevel.ERROR: return chalk_1.default.red; default: return chalk_1.default.white; } } startSession(command, args, options) { const sessionId = `session_${++this.sessionCounter}_${Date.now()}`; this.currentSession = { id: sessionId, startTime: new Date(), command, args, options, traces: [], performance: { operations: new Map() } }; this.sessions.push(this.currentSession); // Trim old sessions if (this.sessions.length > this.options.maxSessions) { this.sessions = this.sessions.slice(-this.options.maxSessions); } this.trace(TraceLevel.INFO, 'session', 'start', { sessionId, command, args, options }); this.emit('session:start', this.currentSession); return sessionId; } endSession() { if (!this.currentSession) return; this.currentSession.endTime = new Date(); this.currentSession.performance.totalDuration = this.currentSession.endTime.getTime() - this.currentSession.startTime.getTime(); this.trace(TraceLevel.INFO, 'session', 'end', { sessionId: this.currentSession.id, duration: this.currentSession.performance.totalDuration, traceCount: this.currentSession.traces.length }); this.emit('session:end', this.currentSession); this.currentSession = undefined; } trace(level, category, operation, data, error) { if (!this.options.enabled || !this.currentSession) { return ''; } const traceId = `trace_${++this.traceCounter}`; const caller = this.options.includeStack ? this.getCaller() : undefined; const trace = { id: traceId, timestamp: new Date(), level, category, operation, data, error, stack: this.options.includeStack && error ? error.stack : undefined, metadata: { pid: process.pid, memory: process.memoryUsage(), thread: 'main', caller } }; this.currentSession.traces.push(trace); // Trim traces if needed if (this.currentSession.traces.length > this.options.maxTraces) { this.currentSession.traces = this.currentSession.traces.slice(-this.options.maxTraces); } this.emit('trace', trace); return traceId; } startOperation(category, operation, data) { const operationId = `op_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const trace = this.trace(TraceLevel.DEBUG, category, `${operation}:start`, { operationId, ...data }); this.activeOperations.set(operationId, { start: Date.now(), trace: this.currentSession?.traces.find(t => t.id === trace) }); return operationId; } endOperation(operationId, data, error) { const operation = this.activeOperations.get(operationId); if (!operation) return; const duration = Date.now() - operation.start; operation.trace.duration = duration; this.trace(error ? TraceLevel.ERROR : TraceLevel.DEBUG, operation.trace.category, `${operation.trace.operation.replace(':start', ':end')}`, { operationId, duration, ...data }, error); // Update performance stats if (this.currentSession && this.options.includePerformance) { this.updatePerformanceStats(operation.trace.category, operation.trace.operation, duration, !!error); } this.activeOperations.delete(operationId); } updatePerformanceStats(category, operation, duration, isError) { if (!this.currentSession) return; const key = `${category}:${operation}`; const stats = this.currentSession.performance.operations.get(key) || { count: 0, totalDuration: 0, averageDuration: 0, minDuration: Infinity, maxDuration: 0, errors: 0 }; stats.count++; stats.totalDuration += duration; stats.averageDuration = stats.totalDuration / stats.count; stats.minDuration = Math.min(stats.minDuration, duration); stats.maxDuration = Math.max(stats.maxDuration, duration); if (isError) { stats.errors++; } this.currentSession.performance.operations.set(key, stats); } getCaller() { const stack = new Error().stack; if (!stack) return undefined; const lines = stack.split('\n'); // Skip this function and the trace function const callerLine = lines[4]; if (callerLine) { const match = callerLine.match(/at\s+([^(]+)\s*\(([^)]+)\)/); if (match) { return `${match[1].trim()} (${path.basename(match[2])})`; } } return undefined; } // Convenience methods traceInfo(category, operation, data) { return this.trace(TraceLevel.INFO, category, operation, data); } traceDebug(category, operation, data) { return this.trace(TraceLevel.DEBUG, category, operation, data); } traceWarn(category, operation, data) { return this.trace(TraceLevel.WARN, category, operation, data); } traceError(category, operation, error, data) { return this.trace(TraceLevel.ERROR, category, operation, data, error); } // Timing helpers time(category, operation) { const operationId = this.startOperation(category, operation); return (data, error) => { this.endOperation(operationId, data, error); }; } async timeAsync(category, operation, fn, data) { const operationId = this.startOperation(category, operation, data); try { const result = await fn(); this.endOperation(operationId, { success: true }); return result; } catch (error) { this.endOperation(operationId, { success: false }, error); throw error; } } // Query and analysis getCurrentSession() { return this.currentSession; } getAllSessions() { return [...this.sessions]; } getSession(sessionId) { return this.sessions.find(s => s.id === sessionId); } searchTraces(query) { let sessions = this.sessions; if (query.sessionId) { const session = this.getSession(query.sessionId); sessions = session ? [session] : []; } const traces = []; for (const session of sessions) { for (const trace of session.traces) { if (query.category && trace.category !== query.category) continue; if (query.operation && !trace.operation.includes(query.operation)) continue; if (query.level !== undefined && trace.level !== query.level) continue; if (query.startTime && trace.timestamp < query.startTime) continue; if (query.endTime && trace.timestamp > query.endTime) continue; traces.push(trace); } } return traces; } generateReport(sessionId) { const session = sessionId ? this.getSession(sessionId) : this.currentSession; if (!session) return 'No session found'; const report = []; // Session info report.push(chalk_1.default.bold('Debug Session Report')); report.push(chalk_1.default.gray('='.repeat(50))); report.push(`Session ID: ${session.id}`); report.push(`Command: ${session.command} ${session.args.join(' ')}`); report.push(`Start Time: ${session.startTime.toISOString()}`); if (session.endTime) { report.push(`End Time: ${session.endTime.toISOString()}`); report.push(`Duration: ${session.performance.totalDuration}ms`); } report.push(`Traces: ${session.traces.length}`); report.push(''); // Performance stats if (session.performance.operations.size > 0) { report.push(chalk_1.default.bold('Performance Statistics')); report.push(chalk_1.default.gray('-'.repeat(30))); for (const [operation, stats] of session.performance.operations) { report.push(`${operation}:`); report.push(` Count: ${stats.count}`); report.push(` Total: ${stats.totalDuration}ms`); report.push(` Average: ${Math.round(stats.averageDuration)}ms`); report.push(` Min: ${stats.minDuration}ms`); report.push(` Max: ${stats.maxDuration}ms`); if (stats.errors > 0) { report.push(chalk_1.default.red(` Errors: ${stats.errors}`)); } report.push(''); } } // Error summary const errors = session.traces.filter(t => t.level === TraceLevel.ERROR); if (errors.length > 0) { report.push(chalk_1.default.bold.red('Errors')); report.push(chalk_1.default.gray('-'.repeat(20))); for (const error of errors) { report.push(`${error.timestamp.toLocaleTimeString()} - ${error.category}:${error.operation}`); if (error.error) { report.push(` ${error.error.message}`); } report.push(''); } } return report.join('\n'); } exportSession(sessionId, format = 'json') { const session = this.getSession(sessionId); if (!session) throw new Error('Session not found'); if (format === 'json') { return JSON.stringify(session, null, 2); } // CSV format const headers = ['timestamp', 'level', 'category', 'operation', 'duration', 'memory', 'error']; const rows = [headers.join(',')]; for (const trace of session.traces) { const row = [ trace.timestamp.toISOString(), TraceLevel[trace.level], trace.category, trace.operation, trace.duration || '', Math.round(trace.metadata.memory.heapUsed / 1024 / 1024), trace.error ? trace.error.message.replace(/,/g, ';') : '' ]; rows.push(row.join(',')); } return rows.join('\n'); } // Configuration setOptions(options) { this.options = { ...this.options, ...options }; if (options.outputFile && !this.outputStream) { this.initializeOutputStream(); } if (options.realtime && !this.listenerCount('trace')) { this.setupRealtimeOutput(); } } getOptions() { return { ...this.options }; } enable() { this.options.enabled = true; } disable() { this.options.enabled = false; } isEnabled() { return this.options.enabled; } close() { if (this.outputStream) { this.outputStream.end(); this.outputStream = undefined; } this.removeAllListeners(); } } exports.DebugSystem = DebugSystem; // Global debug system let globalDebugSystem = null; function createDebugSystem(options) { return new DebugSystem(options); } function getGlobalDebugSystem() { if (!globalDebugSystem) { const debugEnabled = process.env.DEBUG === 'true' || process.argv.includes('--debug') || process.argv.includes('--verbose'); globalDebugSystem = new DebugSystem({ enabled: debugEnabled, level: debugEnabled ? TraceLevel.DEBUG : TraceLevel.INFO, realtime: debugEnabled, outputFile: debugEnabled ? path.join(process.cwd(), '.re-shell', 'debug.log') : undefined }); } return globalDebugSystem; } function setGlobalDebugSystem(debug) { if (globalDebugSystem) { globalDebugSystem.close(); } globalDebugSystem = debug; }