feat(lite): add detailed runtime logs and publish 9999091817
This commit is contained in:
+108
-3
@@ -2,12 +2,13 @@
|
||||
|
||||
import json
|
||||
import threading
|
||||
from datetime import datetime
|
||||
from dataclasses import dataclass
|
||||
|
||||
from PyQt5.QtCore import Qt, pyqtSignal
|
||||
from PyQt5.QtCore import Qt, QTimer, pyqtSignal
|
||||
from PyQt5.QtWidgets import (
|
||||
QComboBox, QDialog, QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem,
|
||||
QPushButton, QVBoxLayout, QWidget,
|
||||
QApplication, QComboBox, QDialog, QFileDialog, QHBoxLayout, QLabel, QLineEdit,
|
||||
QListWidget, QListWidgetItem, QPushButton, QVBoxLayout, QWidget, QTextEdit,
|
||||
)
|
||||
|
||||
from .theme import C_BG, C_BORDER, C_CARD, C_GREEN, C_RED, C_SUBTEXT, C_TEXT, C_TITLE
|
||||
@@ -834,6 +835,94 @@ class ThirdPartyDialog(QDialog):
|
||||
self.lb_status.setStyleSheet(f"color: {color}; font-size: 11px;")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class LogServices:
|
||||
"""运行日志框所需的显式业务依赖。"""
|
||||
|
||||
load_icon: object
|
||||
get_logs: object
|
||||
|
||||
|
||||
class LogDialog(QDialog):
|
||||
"""显示带时间戳的运行日志;窗口打开时会持续读取最新日志。"""
|
||||
|
||||
def __init__(self, services: LogServices, parent=None):
|
||||
super().__init__(parent)
|
||||
self._services = services
|
||||
self.setWindowTitle("运行日志")
|
||||
self.setWindowIcon(services.load_icon())
|
||||
self.resize(760, 500)
|
||||
self.setMinimumSize(560, 320)
|
||||
self.setStyleSheet(f"""
|
||||
QDialog {{ background-color: {C_BG}; }}
|
||||
QLabel {{ color: {C_SUBTEXT}; font-family: "Microsoft YaHei", "Segoe UI", sans-serif; font-size: 11px; }}
|
||||
QTextEdit {{ background-color: {C_CARD}; border: 1px solid {C_BORDER}; border-radius: 6px;
|
||||
padding: 6px 8px; color: {C_TEXT}; font-family: Consolas, "Microsoft YaHei", monospace; font-size: 11px; }}
|
||||
QPushButton {{ background-color: #d9dde3; color: #4f5965; border: none; border-radius: 6px;
|
||||
padding: 5px 14px; font-size: 12px; font-weight: bold; }}
|
||||
QPushButton:hover {{ background-color: #cdd3da; color: #3f4853; }}
|
||||
""")
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setSpacing(8)
|
||||
layout.setContentsMargins(14, 12, 14, 12)
|
||||
hint = QLabel("显示当前运行期间的最近日志,认证信息不会写入日志。")
|
||||
layout.addWidget(hint)
|
||||
|
||||
self.text = QTextEdit()
|
||||
self.text.setReadOnly(True)
|
||||
self.text.setLineWrapMode(QTextEdit.NoWrap)
|
||||
layout.addWidget(self.text, 1)
|
||||
|
||||
buttons = QHBoxLayout()
|
||||
buttons.setSpacing(8)
|
||||
buttons.addStretch()
|
||||
copy = QPushButton("复制")
|
||||
copy.clicked.connect(self._copy_logs)
|
||||
buttons.addWidget(copy)
|
||||
export = QPushButton("导出")
|
||||
export.clicked.connect(self._export_logs)
|
||||
buttons.addWidget(export)
|
||||
close = QPushButton("关闭")
|
||||
close.clicked.connect(self.accept)
|
||||
buttons.addWidget(close)
|
||||
layout.addLayout(buttons)
|
||||
|
||||
self._last_text = None
|
||||
self._refresh()
|
||||
self._timer = QTimer(self)
|
||||
self._timer.timeout.connect(self._refresh)
|
||||
self._timer.start(500)
|
||||
|
||||
def _log_text(self):
|
||||
text = self.text.toPlainText()
|
||||
return "" if text == "暂无日志" else text
|
||||
|
||||
def _copy_logs(self):
|
||||
QApplication.clipboard().setText(self._log_text())
|
||||
|
||||
def _export_logs(self):
|
||||
default_name = f"rrpp-log-{datetime.now().strftime('%Y%m%d-%H%M%S')}.txt"
|
||||
path, _ = QFileDialog.getSaveFileName(
|
||||
self, "导出运行日志", default_name, "文本文件 (*.txt);;所有文件 (*.*)"
|
||||
)
|
||||
if not path:
|
||||
return
|
||||
with open(path, "w", encoding="utf-8", newline="\n") as file:
|
||||
file.write(self._log_text())
|
||||
|
||||
def _refresh(self):
|
||||
text = "\n".join(self._services.get_logs()) or "暂无日志"
|
||||
if text == self._last_text:
|
||||
return
|
||||
scrollbar = self.text.verticalScrollBar()
|
||||
follow_tail = scrollbar.value() >= scrollbar.maximum() - 4
|
||||
self.text.setPlainText(text)
|
||||
self._last_text = text
|
||||
if follow_tail:
|
||||
scrollbar.setValue(scrollbar.maximum())
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SettingsServices:
|
||||
"""设置框所需的配置与业务回调。"""
|
||||
@@ -850,6 +939,7 @@ class SettingsServices:
|
||||
open_whitelist: object
|
||||
open_jump: object
|
||||
open_third_party: object
|
||||
open_logs: object
|
||||
has_login: object
|
||||
open_login: object
|
||||
logout: object
|
||||
@@ -944,6 +1034,17 @@ class SettingsDialog(QDialog):
|
||||
third_party_row.addWidget(self.btn_third_party)
|
||||
layout.addLayout(third_party_row)
|
||||
|
||||
log_row = QHBoxLayout()
|
||||
log_label = QLabel("日志")
|
||||
log_label.setStyleSheet(f"color: {C_SUBTEXT}; font-size: 12px;")
|
||||
log_label.setToolTip("查看连接域名、规则匹配、路由、错误与流量等运行信息")
|
||||
log_row.addWidget(log_label)
|
||||
log_row.addStretch()
|
||||
self.btn_logs = QPushButton("查看")
|
||||
self.btn_logs.setCursor(Qt.PointingHandCursor)
|
||||
self.btn_logs.setStyleSheet(self.btn_whitelist.styleSheet())
|
||||
self.btn_logs.clicked.connect(self._open_logs)
|
||||
log_row.addWidget(self.btn_logs)
|
||||
port_row = QHBoxLayout()
|
||||
port_label = QLabel("代理转发端口")
|
||||
port_label.setStyleSheet(f"color: {C_SUBTEXT}; font-size: 12px;")
|
||||
@@ -974,6 +1075,7 @@ class SettingsDialog(QDialog):
|
||||
self.cb_skip_update.setToolTip("跳过 PAC 数据与程序版本检查(节点更新与登录不受影响),被跳过的检查不输出日志")
|
||||
skip_row.addWidget(self.cb_skip_update)
|
||||
layout.addLayout(skip_row)
|
||||
layout.addLayout(log_row)
|
||||
layout.addStretch()
|
||||
|
||||
buttons = QHBoxLayout()
|
||||
@@ -1026,6 +1128,9 @@ class SettingsDialog(QDialog):
|
||||
def _open_third_party(self):
|
||||
self._services.open_third_party(self)
|
||||
|
||||
def _open_logs(self):
|
||||
self._services.open_logs(self)
|
||||
|
||||
def _save_settings(self):
|
||||
port = self._services.parse_port(self.ed_port.text())
|
||||
if port is None:
|
||||
|
||||
Reference in New Issue
Block a user