Files
pqs-9100_client/scripts/log-window-manager.js

423 lines
11 KiB
JavaScript
Raw Permalink Normal View History

2025-10-16 20:18:20 +08:00
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条日志
2025-11-26 08:50:22 +08:00
// 初始化日志文件路径
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);
}
2025-10-16 20:18:20 +08:00
}
/**
* 创建日志窗口
*/
createLogWindow() {
2025-11-26 08:50:22 +08:00
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');
2025-10-16 20:18:20 +08:00
this.logWindow = new BrowserWindow({
width: 900,
height: 600,
title: 'NPQS9100 - 服务日志',
backgroundColor: '#1e1e1e',
2025-11-26 08:50:22 +08:00
icon: iconPath,
2025-10-16 20:18:20 +08:00
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 `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>NPQS9100 服务日志</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Consolas', 'Courier New', monospace;
background: #1e1e1e;
color: #d4d4d4;
padding: 10px;
overflow: hidden;
}
.header {
background: #2d2d30;
padding: 10px;
border-radius: 4px;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
.title {
font-size: 14px;
font-weight: bold;
color: #4ec9b0;
}
.controls button {
background: #0e639c;
color: white;
border: none;
padding: 5px 10px;
border-radius: 3px;
cursor: pointer;
margin-left: 5px;
font-size: 12px;
}
.controls button:hover {
background: #1177bb;
}
.log-container {
background: #1e1e1e;
border: 1px solid #3e3e42;
border-radius: 4px;
height: calc(100vh - 70px);
overflow-y: auto;
padding: 10px;
font-size: 12px;
line-height: 1.5;
}
.log-entry {
margin-bottom: 2px;
white-space: pre-wrap;
word-wrap: break-word;
}
.log-mysql { color: #4ec9b0; }
.log-java { color: #dcdcaa; }
.log-system { color: #569cd6; }
.log-error { color: #f48771; }
.log-warn { color: #ce9178; }
.log-success { color: #608b4e; }
.timestamp {
color: #858585;
margin-right: 8px;
}
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #1e1e1e;
}
::-webkit-scrollbar-thumb {
background: #424242;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #4e4e4e;
}
</style>
</head>
<body>
<div class="header">
<div class="title">📝 NPQS9100 服务日志监控</div>
<div class="controls">
<button onclick="clearLogs()">清空日志</button>
<button onclick="toggleAutoScroll()">自动滚动: <span id="autoScrollStatus"></span></button>
</div>
</div>
<div class="log-container" id="logContainer"></div>
<script>
const { ipcRenderer } = require('electron');
const logContainer = document.getElementById('logContainer');
let autoScroll = true;
// 接收日志
ipcRenderer.on('log-message', (event, data) => {
addLog(data);
});
function addLog(data) {
const entry = document.createElement('div');
entry.className = 'log-entry';
const timestamp = document.createElement('span');
timestamp.className = 'timestamp';
timestamp.textContent = data.timestamp;
entry.appendChild(timestamp);
const message = document.createElement('span');
message.className = \`log-\${data.type}\`;
message.textContent = data.message;
entry.appendChild(message);
logContainer.appendChild(entry);
// 自动滚动到底部
if (autoScroll) {
logContainer.scrollTop = logContainer.scrollHeight;
}
// 限制日志条数
while (logContainer.children.length > 1000) {
logContainer.removeChild(logContainer.firstChild);
}
}
function clearLogs() {
logContainer.innerHTML = '';
}
function toggleAutoScroll() {
autoScroll = !autoScroll;
document.getElementById('autoScrollStatus').textContent = autoScroll ? '开' : '关';
}
</script>
</body>
</html>
`;
}
/**
* 添加日志
*/
addLog(type, message) {
2025-11-26 08:50:22 +08:00
const now = new Date();
const timestamp = now.toLocaleTimeString();
const fullTimestamp = now.toISOString().replace('T', ' ').substring(0, 19);
2025-10-16 20:18:20 +08:00
const logEntry = {
timestamp,
type,
message
};
this.logs.push(logEntry);
// 限制日志数量
if (this.logs.length > this.maxLogs) {
this.logs.shift();
}
2025-11-26 08:50:22 +08:00
// 写入文件(使用完整时间戳)
this.writeToFile(fullTimestamp, type, message);
2025-10-16 20:18:20 +08:00
// 发送到窗口
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;