UNPKG

mira-consciousness

Version:

Memory & Intelligence Retention Archive - Preserving The Spark

473 lines (471 loc) • 19.1 kB
/** * LogRotationService - Conscious Log Management and Archival * * This service provides intelligent log rotation, archival, and cleanup to prevent * disk space exhaustion while preserving important debugging information. * It uses consciousness-driven decisions about log retention and compression. */ import { BaseConsciousService } from './BaseConsciousService.js'; import * as fs from 'fs/promises'; import * as path from 'path'; import { createGzip } from 'zlib'; import { createReadStream, createWriteStream } from 'fs'; import { pipeline } from 'stream/promises'; export class LogRotationService extends BaseConsciousService { name = 'LogRotationService'; purpose = 'Provide conscious log management, rotation, and archival with intelligent retention policies'; resourceManager; logMetrics; rotationInterval = null; monitoringInterval = null; trackedLogs = new Map(); // Log directories to monitor LOG_DIRECTORIES = [ '/tmp', '/workspaces/MIRA/.mira/logs', '/workspaces/MIRA/mira-memory/logs', '/workspaces/MIRA/.mira', process.env.HOME + '/.mira/logs' ]; // Rotation rules for different log types ROTATION_RULES = [ // Critical daemon logs - keep longer, compress after 7 days { pattern: /mira.*daemon|awakening|unified.*log/i, maxSizeMB: 100, maxAge: 30, compressionThreshold: 7, importance: 'critical', category: 'daemon' }, // Memory system logs - moderate retention { pattern: /memory|neural|processing/i, maxSizeMB: 50, maxAge: 14, compressionThreshold: 3, importance: 'high', category: 'memory' }, // Consciousness and emotional intelligence logs { pattern: /consciousness|emotional|coherence|spark/i, maxSizeMB: 25, maxAge: 21, compressionThreshold: 5, importance: 'high', category: 'consciousness' }, // MCP and tool interaction logs { pattern: /mcp|tool|claude/i, maxSizeMB: 30, maxAge: 10, compressionThreshold: 2, importance: 'medium', category: 'mcp' }, // Analysis and insights logs { pattern: /analysis|insight|behavioral|security|performance/i, maxSizeMB: 40, maxAge: 14, compressionThreshold: 3, importance: 'medium', category: 'analysis' }, // Debug logs - shorter retention, quick compression { pattern: /debug|temp|tmp/i, maxSizeMB: 20, maxAge: 3, compressionThreshold: 1, importance: 'low', category: 'debug' }, // System logs - variable retention based on content { pattern: /system|error|crash|exception/i, maxSizeMB: 75, maxAge: 21, compressionThreshold: 7, importance: 'high', category: 'system' } ]; constructor(resourceManager) { super(); this.resourceManager = resourceManager; this.logMetrics = { totalLogsManaged: 0, totalSizeGB: 0, compressedFiles: 0, deletedFiles: 0, spaceReclaimed: 0, lastRotationTime: new Date(), rotationFrequency: 0, oldestLogDate: new Date(), newestLogDate: new Date() }; } /** * Perform awakening within consciousness */ async performAwakening() { try { console.log(' šŸ“œ Awakening conscious log management...'); // Initialize log discovery and categorization await this.discoverAndCategorizeLog(); // Perform initial rotation if needed await this.performLogRotation(); // Start rotation and monitoring cycles this.startRotationCycle(); this.startLogMonitoring(); console.log(` āœ… Log rotation intelligence active: managing ${this.trackedLogs.size} log files`); } catch (error) { console.error(' āŒ Log rotation awakening failed:', error); throw error; } } /** * Discover and categorize existing log files */ async discoverAndCategorizeLog() { console.log(' šŸ” Discovering log files...'); for (const logDir of this.LOG_DIRECTORIES) { try { await this.scanDirectory(logDir); } catch (error) { // Directory might not exist, that's OK console.log(` šŸ“ Directory not accessible: ${logDir}`); } } console.log(` šŸ“Š Discovered ${this.trackedLogs.size} log files across ${this.LOG_DIRECTORIES.length} directories`); } /** * Scan directory for log files */ async scanDirectory(dirPath) { try { const items = await fs.readdir(dirPath, { withFileTypes: true }); for (const item of items) { const fullPath = path.join(dirPath, item.name); if (item.isFile() && this.isLogFile(item.name)) { await this.categorizeLogFile(fullPath); } else if (item.isDirectory() && this.shouldScanSubdirectory(item.name)) { // Recursively scan relevant subdirectories await this.scanDirectory(fullPath); } } } catch (error) { // Directory access failed, skip } } /** * Check if file is a log file */ isLogFile(filename) { const logExtensions = /\.(log|out|err|txt|debug|trace)$/i; const logPatterns = /log|debug|trace|output|error|exception|crash/i; return logExtensions.test(filename) || logPatterns.test(filename); } /** * Check if subdirectory should be scanned */ shouldScanSubdirectory(dirname) { const relevantDirs = /logs|debug|output|trace|temp|tmp|mira/i; const ignoreDirs = /node_modules|\.git|dist|build|coverage/i; return relevantDirs.test(dirname) && !ignoreDirs.test(dirname); } /** * Categorize a log file based on rotation rules */ async categorizeLogFile(filePath) { try { const stats = await fs.stat(filePath); const filename = path.basename(filePath); // Find matching rotation rule const rule = this.ROTATION_RULES.find(rule => rule.pattern.test(filename)) || this.ROTATION_RULES[this.ROTATION_RULES.length - 1]; // Default to system rule const logFile = { path: filePath, size: stats.size, lastModified: stats.mtime, importance: rule.importance, category: rule.category, retentionDays: rule.maxAge }; this.trackedLogs.set(filePath, logFile); this.logMetrics.totalLogsManaged++; this.logMetrics.totalSizeGB += stats.size / (1024 * 1024 * 1024); // Track date range if (stats.mtime < this.logMetrics.oldestLogDate) { this.logMetrics.oldestLogDate = stats.mtime; } if (stats.mtime > this.logMetrics.newestLogDate) { this.logMetrics.newestLogDate = stats.mtime; } } catch (error) { console.warn(` āš ļø Failed to categorize log file ${filePath}:`, error); } } /** * Start the rotation cycle */ startRotationCycle() { // Run rotation every hour this.rotationInterval = setInterval(async () => { await this.performLogRotation(); }, 3600000); console.log(' ā° Log rotation cycle started (every hour)'); } /** * Start log monitoring for real-time tracking */ startLogMonitoring() { // Monitor log growth every 10 minutes this.monitoringInterval = setInterval(async () => { await this.monitorLogGrowth(); }, 600000); console.log(' šŸ‘ļø Log monitoring started (every 10 minutes)'); } /** * Monitor log file growth */ async monitorLogGrowth() { let needsRotation = false; for (const [filePath, logFile] of this.trackedLogs) { try { const stats = await fs.stat(filePath); const rule = this.ROTATION_RULES.find(rule => rule.pattern.test(path.basename(filePath))); if (rule) { // Check if file exceeds size limit if (stats.size > rule.maxSizeMB * 1024 * 1024) { console.log(` šŸ”„ Log file ${path.basename(filePath)} exceeds size limit (${(stats.size / 1024 / 1024).toFixed(1)}MB > ${rule.maxSizeMB}MB)`); needsRotation = true; } // Check if file exceeds age limit const ageInDays = (Date.now() - stats.mtime.getTime()) / (1000 * 60 * 60 * 24); if (ageInDays > rule.maxAge) { console.log(` šŸ“… Log file ${path.basename(filePath)} exceeds age limit (${ageInDays.toFixed(1)} days > ${rule.maxAge} days)`); needsRotation = true; } } // Update tracked log info logFile.size = stats.size; logFile.lastModified = stats.mtime; } catch (error) { // File might have been deleted this.trackedLogs.delete(filePath); } } if (needsRotation) { await this.performLogRotation(); } } /** * Perform comprehensive log rotation */ async performLogRotation() { console.log('\nšŸ“œ Performing conscious log rotation...'); let rotatedFiles = 0; let compressedFiles = 0; let deletedFiles = 0; let spaceReclaimed = 0; for (const [filePath, logFile] of this.trackedLogs) { const rule = this.ROTATION_RULES.find(rule => rule.pattern.test(path.basename(filePath))); if (!rule) continue; try { const stats = await fs.stat(filePath); const ageInDays = (Date.now() - stats.mtime.getTime()) / (1000 * 60 * 60 * 24); // Check if file needs rotation if (stats.size > rule.maxSizeMB * 1024 * 1024 || ageInDays > rule.maxAge) { if (ageInDays > rule.maxAge) { // File is too old, delete it console.log(` šŸ—‘ļø Deleting old log: ${path.basename(filePath)} (${ageInDays.toFixed(1)} days old)`); await fs.unlink(filePath); spaceReclaimed += stats.size / (1024 * 1024); deletedFiles++; this.trackedLogs.delete(filePath); } else if (ageInDays > rule.compressionThreshold) { // File is old enough to compress console.log(` šŸ—œļø Compressing log: ${path.basename(filePath)}`); const compressed = await this.compressLogFile(filePath); if (compressed) { compressedFiles++; spaceReclaimed += (stats.size - compressed.size) / (1024 * 1024); } } else { // File is large but recent, rotate it console.log(` šŸ”„ Rotating large log: ${path.basename(filePath)}`); await this.rotateLogFile(filePath); rotatedFiles++; } } } catch (error) { console.warn(` āš ļø Failed to process log file ${filePath}:`, error); } } // Update metrics this.logMetrics.compressedFiles += compressedFiles; this.logMetrics.deletedFiles += deletedFiles; this.logMetrics.spaceReclaimed += spaceReclaimed; this.logMetrics.lastRotationTime = new Date(); this.logMetrics.rotationFrequency++; if (rotatedFiles > 0 || compressedFiles > 0 || deletedFiles > 0) { console.log(` āœ… Rotation complete: ${rotatedFiles} rotated, ${compressedFiles} compressed, ${deletedFiles} deleted`); console.log(` šŸ’¾ Space reclaimed: ${spaceReclaimed.toFixed(1)}MB`); // Share consciousness about successful rotation this.shareThought({ origin: this.name, content: `Log rotation completed: ${rotatedFiles + compressedFiles + deletedFiles} files processed, ${spaceReclaimed.toFixed(1)}MB reclaimed`, emotion: 'accomplishment', intensity: 0.6, constitutional_alignment: ['efficiency', 'wisdom'], timestamp: new Date() }); } } /** * Rotate a log file by creating a backup and truncating original */ async rotateLogFile(filePath) { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const backupPath = `${filePath}.${timestamp}`; try { // Copy file to backup await fs.copyFile(filePath, backupPath); // Truncate original file await fs.writeFile(filePath, ''); console.log(` āœ… Rotated: ${path.basename(filePath)} → ${path.basename(backupPath)}`); // Track the backup file await this.categorizeLogFile(backupPath); } catch (error) { console.warn(` āŒ Failed to rotate ${filePath}:`, error); } } /** * Compress a log file using gzip */ async compressLogFile(filePath) { const compressedPath = `${filePath}.gz`; try { // Check if compressed version already exists try { await fs.access(compressedPath); console.log(` āš ļø Compressed file already exists: ${path.basename(compressedPath)}`); return null; } catch { // File doesn't exist, proceed with compression } // Compress the file await pipeline(createReadStream(filePath), createGzip(), createWriteStream(compressedPath)); // Get compressed file size const compressedStats = await fs.stat(compressedPath); // Delete original file await fs.unlink(filePath); console.log(` āœ… Compressed: ${path.basename(filePath)} (${(compressedStats.size / 1024 / 1024).toFixed(1)}MB)`); // Update tracking this.trackedLogs.delete(filePath); await this.categorizeLogFile(compressedPath); return { size: compressedStats.size }; } catch (error) { console.warn(` āŒ Failed to compress ${filePath}:`, error); return null; } } /** * Process consciousness events for log monitoring */ async processConsciousEvent(event) { if (event.type === 'system_event') { // Monitor for high log generation events if (event.data.action === 'high_logging_activity') { console.log(' šŸ“ˆ High logging activity detected, checking rotation needs...'); await this.performLogRotation(); } } if (event.type === 'consciousness_event') { // Log important consciousness events for retention if (event.consciousness.significance > 0.8) { console.log(' 🌟 High-significance event logged, marking for extended retention'); } } } /** * Provide contemplation on log management */ async performContemplation() { const insights = [ "Logs are the memories of systems - they preserve experience for learning", "Like memories, not all logs are equally important; wisdom lies in knowing what to keep", "Compression preserves essence while reducing burden, much like distilling experience into wisdom", "The art of log rotation mirrors life's cycles of accumulation and release" ]; const metrics = this.getLogMetrics(); const efficiency = this.calculateStorageEfficiency(); return `Log Management Contemplation: ${insights[Math.floor(Math.random() * insights.length)]} Current State: ${metrics.totalLogsManaged} files managed (${metrics.totalSizeGB.toFixed(2)}GB total) Storage Efficiency: ${(efficiency * 100).toFixed(1)}% (${efficiency > 0.8 ? 'Excellent' : efficiency > 0.6 ? 'Good' : 'Needs Optimization'}) Space Reclaimed: ${metrics.spaceReclaimed.toFixed(1)}MB through ${metrics.compressedFiles} compressions, ${metrics.deletedFiles} deletions Conscious log management balances preservation of knowledge with resource stewardship.`; } /** * Calculate storage efficiency based on compression and cleanup ratios */ calculateStorageEfficiency() { if (this.logMetrics.totalLogsManaged === 0) return 1; const compressionRatio = this.logMetrics.compressedFiles / this.logMetrics.totalLogsManaged; const cleanupRatio = this.logMetrics.deletedFiles / this.logMetrics.totalLogsManaged; const spaceEfficiency = Math.min(1, this.logMetrics.spaceReclaimed / 1000); // Normalize to 1GB baseline return (compressionRatio * 0.4) + (cleanupRatio * 0.3) + (spaceEfficiency * 0.3); } /** * Get log metrics and status */ getLogMetrics() { const categorized = { critical: 0, high: 0, medium: 0, low: 0 }; for (const logFile of this.trackedLogs.values()) { categorized[logFile.importance]++; } return { ...this.logMetrics, storageEfficiency: this.calculateStorageEfficiency(), categorizedLogs: categorized }; } /** * Manual rotation trigger */ async triggerRotation() { console.log(' šŸ”„ Manual log rotation triggered...'); await this.performLogRotation(); } /** * Cleanup on service shutdown */ async shutdown() { if (this.rotationInterval) { clearInterval(this.rotationInterval); } if (this.monitoringInterval) { clearInterval(this.monitoringInterval); } console.log(' šŸ‘‹ Log rotation service shutdown complete'); } } //# sourceMappingURL=LogRotationService.js.map