const { BrowserWindow } = require('electron'); const path = require('path'); const fs = require('fs'); /** * 日志窗口管理器 * 显示 MySQL 和 Spring Boot 的实时日志 */ class LogWindowManager { constructor() { this.logWindow = null; this.logs = []; this.maxLogs = 1000; // 最多保留1000条日志 // 初始化日志文件路径 this.initLogFile(); } /** * 初始化日志文件路径(按天滚动) */ initLogFile() { // 开发环境:项目根目录的 logs 文件夹 // 打包后:应用根目录的 logs 文件夹 const isDev = !process.resourcesPath; const baseDir = isDev ? path.join(__dirname, '..') : path.dirname(process.resourcesPath); this.logsDir = path.join(baseDir, 'logs'); // 确保 logs 目录存在 if (!fs.existsSync(this.logsDir)) { fs.mkdirSync(this.logsDir, { recursive: true }); } // 生成当天的日志文件名:startup-YYYYMMDD.log const today = new Date(); const dateStr = today.getFullYear() + String(today.getMonth() + 1).padStart(2, '0') + String(today.getDate()).padStart(2, '0'); this.logFilePath = path.join(this.logsDir, `startup-${dateStr}.log`); console.log('[LogWindowManager] Log file:', this.logFilePath); // 写入启动标记 this.writeToFile(new Date().toISOString().replace('T', ' ').substring(0, 19), 'SYSTEM', '=' .repeat(80)); this.writeToFile(new Date().toISOString().replace('T', ' ').substring(0, 19), 'SYSTEM', 'NPQS9100 应用启动'); this.writeToFile(new Date().toISOString().replace('T', ' ').substring(0, 19), 'SYSTEM', '=' .repeat(80)); // 清理超过30天的旧日志 this.cleanOldLogs(30); } /** * 清理旧日志文件 * @param {number} days - 保留天数 */ cleanOldLogs(days) { try { const now = Date.now(); const maxAge = days * 24 * 60 * 60 * 1000; // 转换为毫秒 const files = fs.readdirSync(this.logsDir); let deletedCount = 0; files.forEach(file => { if (file.startsWith('startup-') && file.endsWith('.log')) { const filePath = path.join(this.logsDir, file); const stats = fs.statSync(filePath); const age = now - stats.mtimeMs; if (age > maxAge) { fs.unlinkSync(filePath); deletedCount++; console.log(`[LogWindowManager] Deleted old log: ${file}`); } } }); if (deletedCount > 0) { console.log(`[LogWindowManager] Cleaned up ${deletedCount} old log file(s)`); } } catch (error) { console.error('[LogWindowManager] Failed to clean old logs:', error); } } /** * 写入日志到文件 */ writeToFile(timestamp, type, message) { try { const logLine = `[${timestamp}] [${type.toUpperCase()}] ${message}\n`; fs.appendFileSync(this.logFilePath, logLine, 'utf-8'); } catch (error) { console.error('[LogWindowManager] Failed to write log to file:', error); } } /** * 创建日志窗口 */ createLogWindow() { const isDev = !process.resourcesPath; const iconPath = isDev ? path.join(__dirname, '..', 'public', 'images', 'icon.png') : path.join(process.resourcesPath, 'app.asar.unpacked', 'public', 'images', 'icon.png'); this.logWindow = new BrowserWindow({ width: 900, height: 600, title: 'NPQS9100 - 服务日志', backgroundColor: '#1e1e1e', icon: iconPath, webPreferences: { nodeIntegration: true, contextIsolation: false } }); // 加载日志页面 const logHtml = this.generateLogHTML(); this.logWindow.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(logHtml)}`); // 窗口关闭事件 - 只清理引用,不退出应用 this.logWindow.on('closed', () => { console.log('[LogWindow] Log window closed by user'); this.logWindow = null; }); // 防止日志窗口关闭时退出应用(但允许隐藏) this.closeHandler = (event) => { // 只是隐藏窗口,不是真正关闭 // 这样可以随时再打开 event.preventDefault(); this.logWindow.hide(); console.log('[LogWindow] Log window hidden'); }; this.logWindow.on('close', this.closeHandler); return this.logWindow; } /** * 生成日志HTML页面 */ generateLogHTML() { return ` NPQS9100 服务日志
📝 NPQS9100 服务日志监控
`; } /** * 添加日志 */ addLog(type, message) { const now = new Date(); const timestamp = now.toLocaleTimeString(); const fullTimestamp = now.toISOString().replace('T', ' ').substring(0, 19); const logEntry = { timestamp, type, message }; this.logs.push(logEntry); // 限制日志数量 if (this.logs.length > this.maxLogs) { this.logs.shift(); } // 写入文件(使用完整时间戳) this.writeToFile(fullTimestamp, type, message); // 发送到窗口 if (this.logWindow && !this.logWindow.isDestroyed()) { this.logWindow.webContents.send('log-message', logEntry); } // 同时输出到控制台 console.log(`[${type.toUpperCase()}] ${message}`); } /** * 显示日志窗口 */ show() { if (!this.logWindow || this.logWindow.isDestroyed()) { // 窗口已被销毁,重新创建 console.log('[LogWindow] Recreating log window...'); this.createLogWindow(); // 重新发送历史日志 this.logs.forEach(log => { this.logWindow.webContents.send('log-message', log); }); } else { this.logWindow.show(); this.logWindow.focus(); console.log('[LogWindow] Log window shown'); } } /** * 隐藏日志窗口 */ hide() { if (this.logWindow && !this.logWindow.isDestroyed()) { this.logWindow.hide(); console.log('[LogWindow] Log window hidden'); } } /** * 检查日志窗口是否可见 */ isVisible() { return this.logWindow && !this.logWindow.isDestroyed() && this.logWindow.isVisible(); } /** * 切换日志窗口显示/隐藏 */ toggle() { if (this.isVisible()) { this.hide(); } else { this.show(); } } /** * 关闭日志窗口(真正销毁) */ close() { if (this.logWindow && !this.logWindow.isDestroyed()) { try { // 移除 close 事件监听,允许真正关闭 if (this.closeHandler) { this.logWindow.removeListener('close', this.closeHandler); } this.logWindow.removeAllListeners('close'); this.logWindow.removeAllListeners('closed'); // 强制销毁窗口 this.logWindow.destroy(); console.log('[LogWindow] Log window destroyed'); } catch (error) { console.error('[LogWindow] Error closing log window:', error); } finally { this.logWindow = null; this.closeHandler = null; } } } /** * 获取所有日志 */ getLogs() { return this.logs; } /** * 清空日志 */ clearLogs() { this.logs = []; if (this.logWindow && !this.logWindow.isDestroyed()) { this.logWindow.webContents.send('clear-logs'); } } } module.exports = LogWindowManager;