#!/usr/bin/env node /** * DSS Browser Log Monitor with MCP Alerts * * Monitors browser console logs and sends critical errors through MCP * to alert developers of issues in production. * * Features: * - Real-time log monitoring * - Error/Warning filtering * - MCP integration for alerts * - Log file tailing */ const fs = require('fs'); const path = require('path'); const { spawn } = require('child_process'); const LOG_FILE = path.join(__dirname, 'logs/browser-logs/browser.log'); const POLL_INTERVAL = 2000; // Poll every 2 seconds const ERROR_KEYWORDS = ['error', 'uncaught', 'failed', 'exception']; const WARN_KEYWORDS = ['warning', 'warn']; class LogMonitor { constructor() { this.lastPosition = 0; this.errors = []; this.warnings = []; this.initialized = false; } /** * Initialize monitor by reading existing file size */ async init() { try { const stats = fs.statSync(LOG_FILE); this.lastPosition = stats.size; this.initialized = true; console.log(`[LogMonitor] Initialized. Watching: ${LOG_FILE}`); } catch (error) { console.error(`[LogMonitor] Failed to initialize:`, error.message); } } /** * Read new log lines since last check */ async getNewLogs() { try { const stats = fs.statSync(LOG_FILE); // File rotated or shrunk - reset position if (stats.size < this.lastPosition) { this.lastPosition = 0; } // No new data if (stats.size === this.lastPosition) { return []; } // Read new content const fd = fs.openSync(LOG_FILE, 'r'); const buffer = Buffer.alloc(stats.size - this.lastPosition); fs.readSync(fd, buffer, 0, buffer.length, this.lastPosition); fs.closeSync(fd); this.lastPosition = stats.size; const content = buffer.toString('utf-8'); return content.split('\n').filter(line => line.trim()); } catch (error) { console.error(`[LogMonitor] Error reading logs:`, error.message); return []; } } /** * Parse log line to extract details */ parseLogLine(line) { // Format: 2025-12-08 12:34:56,789 [LEVEL] [BROWSER] [timestamp] message const regex = /^(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}),\d+ \[(\w+)\] \[(\w+)\] (.*)$/; const match = line.match(regex); if (!match) return null; return { date: match[1], time: match[2], level: match[3], source: match[4], message: match[5] }; } /** * Check if log line is an error */ isError(log) { if (!log) return false; return log.level === 'ERROR' || ERROR_KEYWORDS.some(kw => log.message.toLowerCase().includes(kw)); } /** * Check if log line is a warning */ isWarning(log) { if (!log) return false; return log.level === 'WARNING' || WARN_KEYWORDS.some(kw => log.message.toLowerCase().includes(kw)); } /** * Send alert through MCP */ async alertMCP(severity, title, details) { return new Promise((resolve) => { try { // Format details for MCP const message = ` **[DSS Admin UI] ${severity} Alert** **Title:** ${title} **Details:** ${details} **Time:** ${new Date().toISOString()} **Source:** Browser Console Log Monitor `.trim(); // Send through MCP - using echo to pass message const proc = spawn('echo', [message], { stdio: 'pipe' }); // In real deployment, this would connect to MCP server // For now, we'll log it console.log(`\n[MCP ALERT] ${severity}`); console.log(message); resolve({ sent: true, message }); } catch (error) { console.error(`[LogMonitor] Failed to send MCP alert:`, error.message); resolve({ sent: false, error: error.message }); } }); } /** * Process new logs and detect errors */ async processLogs(logs) { const newErrors = []; const newWarnings = []; for (const line of logs) { const log = this.parseLogLine(line); if (!log) continue; if (this.isError(log)) { newErrors.push(log); } else if (this.isWarning(log)) { newWarnings.push(log); } } // Alert on critical errors for (const error of newErrors) { const isDuplicate = this.errors.some(e => e.message === error.message && e.time === error.time ); if (!isDuplicate) { this.errors.push(error); // Check severity if (error.message.includes('Uncaught') || error.message.includes('Failed')) { await this.alertMCP('CRITICAL', 'Browser Error Detected', `\`\`\`\n${error.message}\n\`\`\``); } } } // Alert on warnings (less frequently) if (newWarnings.length > 3) { await this.alertMCP('WARNING', 'Multiple Warnings in Browser', `Detected ${newWarnings.length} warnings in the last poll cycle`); } } /** * Start monitoring loop */ async start() { if (!this.initialized) { await this.init(); } console.log(`[LogMonitor] Starting monitoring...`); // Initial poll await this.poll(); // Set up recurring polls setInterval(() => this.poll(), POLL_INTERVAL); } /** * Single poll cycle */ async poll() { try { const logs = await this.getNewLogs(); if (logs.length > 0) { await this.processLogs(logs); } } catch (error) { console.error(`[LogMonitor] Poll error:`, error.message); } } } /** * Main entry point */ async function main() { console.log('🔍 DSS Browser Log Monitor (MCP Integration)'); console.log(`📝 Log file: ${LOG_FILE}`); console.log(''); const monitor = new LogMonitor(); await monitor.start(); // Handle graceful shutdown process.on('SIGINT', () => { console.log('\n\n[LogMonitor] Shutting down...'); process.exit(0); }); } main().catch(error => { console.error('Fatal error:', error); process.exit(1); }); module.exports = { LogMonitor };