261 lines
10 KiB
Python
261 lines
10 KiB
Python
from PySide6.QtWidgets import QWidget, QLabel, QMessageBox, QVBoxLayout, QTreeWidget, QHeaderView, QHBoxLayout, QPushButton, QFrame, QLineEdit, QCheckBox, QTreeWidgetItem, QDialog, QPlainTextEdit, QApplication
|
|
from PySide6.QtCore import Qt, Signal
|
|
from PySide6.QtGui import QColor, QIcon, QFont, QKeySequence, QIntValidator, QShortcut
|
|
|
|
from codes.common.signal_bus import signal_bus
|
|
from codes.common import db_operation
|
|
from codes.common import clibs
|
|
|
|
|
|
class LogDialog(QDialog):
|
|
def __init__(self, content, parent=None):
|
|
super().__init__(parent)
|
|
self.setWindowTitle("日志详情")
|
|
self.setGeometry(100, 100, 700, 300)
|
|
self.center_on_screen()
|
|
layout = QVBoxLayout(self)
|
|
self.plain_text_edit = QPlainTextEdit()
|
|
self.plain_text_edit.setReadOnly(True)
|
|
self.plain_text_edit.setPlainText(content)
|
|
layout.addWidget(self.plain_text_edit)
|
|
|
|
def center_on_screen(self):
|
|
screen_geometry = QApplication.primaryScreen().geometry()
|
|
screen_width = screen_geometry.width()
|
|
screen_height = screen_geometry.height()
|
|
dialog_width = self.width()
|
|
dialog_height = self.height()
|
|
x = (screen_width - dialog_width) // 2
|
|
y = (screen_height - dialog_height) // 2
|
|
self.move(x, y)
|
|
|
|
|
|
class PageNumberInput(QDialog):
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent)
|
|
self.init_ui()
|
|
self.setup_slot()
|
|
|
|
def setup_slot(self):
|
|
QShortcut(QKeySequence("Esc"), self).activated.connect(self.reject)
|
|
self.le_page_number.returnPressed.connect(self.accept)
|
|
self.le_page_number.returnPressed.connect(self.get_page_number)
|
|
|
|
def init_ui(self):
|
|
self.setMinimumSize(300,100)
|
|
self.setMaximumSize(400,120)
|
|
self.resize(300, 100)
|
|
self.setWindowIcon(QIcon(clibs.icon))
|
|
self.setWindowTitle("输入页码")
|
|
layout_h = QHBoxLayout()
|
|
layout_h.addStretch(1)
|
|
self.le_page_number = QLineEdit(self)
|
|
self.le_page_number.setText("1")
|
|
self.le_page_number.selectAll()
|
|
self.le_page_number.setFont(QFont('Consolas', 14))
|
|
self.le_page_number.setValidator(QIntValidator(0, 9999999, self))
|
|
layout_h.addWidget(self.le_page_number, stretch=4)
|
|
layout_h.addStretch(1)
|
|
self.setLayout(layout_h)
|
|
|
|
def get_page_number(self):
|
|
text = self.le_page_number.text()
|
|
return 1 if text == 0 else int(text)
|
|
|
|
|
|
class ClickableLabel(QLabel):
|
|
clicked = Signal()
|
|
|
|
def mousePressEvent(self, event):
|
|
if event.button() == Qt.MouseButton.LeftButton:
|
|
self.clicked.emit()
|
|
super().mousePressEvent(event)
|
|
|
|
|
|
class W08Log(QWidget):
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent)
|
|
self.predos()
|
|
self.ui_init()
|
|
self.setup_slot()
|
|
self.setup_sc()
|
|
|
|
def predos(self):
|
|
self.records, self.len_records = "", ""
|
|
self.is_searching = False
|
|
self.max_item_number = clibs.config["log_number_per_page"]
|
|
|
|
def ui_init(self):
|
|
layout_v = QVBoxLayout(self)
|
|
self.treeW = QTreeWidget()
|
|
self.treeW.setUniformRowHeights(True)
|
|
self.treeW.setHeaderLabels(["ID", "时间戳", "告警级别", "模块信息", "告警内容"])
|
|
header = self.treeW.header()
|
|
for i in range(self.treeW.columnCount()):
|
|
header.setSectionResizeMode(i, QHeaderView.ResizeMode.ResizeToContents)
|
|
layout_v.addWidget(self.treeW, stretch=9)
|
|
|
|
layout_h = QHBoxLayout()
|
|
self.pb_previous = QPushButton("上一页")
|
|
layout_h.addWidget(self.pb_previous, stretch=1)
|
|
|
|
self.lb_page = ClickableLabel("999999/999999")
|
|
self.lb_page.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
self.lb_page.setMinimumWidth(144)
|
|
layout_h.addWidget(self.lb_page, stretch=2)
|
|
|
|
self.pb_next = QPushButton("下一页")
|
|
layout_h.addWidget(self.pb_next, stretch=1)
|
|
|
|
layout_h.addStretch(9)
|
|
|
|
self.frame_checkbox = QFrame()
|
|
layout_h_checkbox = QHBoxLayout()
|
|
self.box_info = QCheckBox("通知", parent=self.frame_checkbox)
|
|
self.box_info.setChecked(True)
|
|
layout_h_checkbox.addWidget(self.box_info, stretch=1)
|
|
self.box_warning = QCheckBox("告警", parent=self.frame_checkbox)
|
|
self.box_warning.setChecked(True)
|
|
layout_h_checkbox.addWidget(self.box_warning, stretch=1)
|
|
self.box_error = QCheckBox("错误", parent=self.frame_checkbox)
|
|
self.box_error.setChecked(True)
|
|
layout_h_checkbox.addWidget(self.box_error, stretch=1)
|
|
self.box_exception = QCheckBox("异常", parent=self.frame_checkbox)
|
|
self.box_exception.setChecked(True)
|
|
layout_h_checkbox.addWidget(self.box_exception, stretch=1)
|
|
self.box_unknown = QCheckBox("未知", parent=self.frame_checkbox)
|
|
self.box_unknown.setChecked(True)
|
|
layout_h_checkbox.addWidget(self.box_unknown, stretch=1)
|
|
layout_h.addLayout(layout_h_checkbox, stretch=4)
|
|
|
|
self.le_search = QLineEdit()
|
|
self.le_search.setPlaceholderText("告警内容")
|
|
self.le_search.setMinimumWidth(300)
|
|
layout_h.addWidget(self.le_search, stretch=5)
|
|
|
|
self.pb_search = QPushButton("查找")
|
|
layout_h.addWidget(self.pb_search, stretch=1)
|
|
|
|
layout_v.addLayout(layout_h, stretch=1)
|
|
|
|
self.setLayout(layout_v)
|
|
|
|
def setup_slot(self):
|
|
self.treeW.itemDoubleClicked.connect(self.show_single_log)
|
|
self.pb_previous.clicked.connect(self.previous_page)
|
|
self.pb_next.clicked.connect(self.next_page)
|
|
self.pb_search.clicked.connect(self.search_page)
|
|
self.le_search.returnPressed.connect(self.search_page)
|
|
self.lb_page.clicked.connect(self.goto_page)
|
|
signal_bus.stacked_page_switch_log.connect(self.show_latest_page)
|
|
|
|
def setup_sc(self):
|
|
...
|
|
|
|
def previous_page(self):
|
|
if not self.is_searching:
|
|
self.records, self.len_records = db_operation.db_query_logs(True)
|
|
current, total = self.lb_page.text().split("/")
|
|
page_number = int(current) - 1
|
|
self.show_page(self.records, self.len_records, page_number=page_number)
|
|
|
|
def next_page(self):
|
|
if not self.is_searching:
|
|
self.records, self.len_records = db_operation.db_query_logs(True)
|
|
current, total = self.lb_page.text().split("/")
|
|
page_number = int(current) + 1
|
|
self.show_page(self.records, self.len_records, page_number=page_number)
|
|
|
|
def search_page(self):
|
|
filters = {"info": self.box_info.isChecked(), "warning": self.box_warning.isChecked(), "error": self.box_error.isChecked(), "exception": self.box_exception.isChecked(), "unknown": self.box_unknown.isChecked()}
|
|
search_text = self.le_search.text().strip()
|
|
flag, levels = False, []
|
|
for level, enable in filters.items():
|
|
if not enable:
|
|
continue
|
|
flag = True
|
|
levels.append(level)
|
|
|
|
if not flag:
|
|
QMessageBox().warning(None, "警告", "至少选择一个过滤器!")
|
|
return
|
|
|
|
self.records, self.len_records = db_operation.db_query_logs(levels=levels)
|
|
if search_text:
|
|
self.records, self.len_records = db_operation.db_query_logs(search_text, self.records)
|
|
|
|
self.is_searching = True
|
|
self.show_page(self.records, self.len_records, page_number=None)
|
|
|
|
def goto_page(self):
|
|
dlg = PageNumberInput()
|
|
if dlg.exec() != 1:
|
|
return
|
|
|
|
page_number = dlg.get_page_number()
|
|
if not self.is_searching:
|
|
self.records, self.len_records = db_operation.db_query_logs(True)
|
|
self.show_page(self.records, self.len_records, page_number=page_number)
|
|
|
|
def show_latest_page(self):
|
|
self.records, self.len_records = db_operation.db_query_logs(True)
|
|
self.is_searching = False
|
|
self.box_info.setChecked(True)
|
|
self.box_warning.setChecked(True)
|
|
self.box_error.setChecked(True)
|
|
self.box_exception.setChecked(True)
|
|
self.box_unknown.setChecked(True)
|
|
self.le_search.clear()
|
|
self.show_page(self.records, self.len_records, page_number=None)
|
|
|
|
def show_page(self, records, len_records, page_number: int | None):
|
|
if len_records == 0:
|
|
self.treeW.clear()
|
|
return
|
|
|
|
remainder = len_records % self.max_item_number
|
|
total = len_records // self.max_item_number + 1 if remainder else len_records // self.max_item_number
|
|
if page_number is None:
|
|
current = total
|
|
elif page_number <= 0:
|
|
current = 1
|
|
elif page_number < total:
|
|
current = page_number
|
|
else:
|
|
current = total
|
|
self.lb_page.setText(f"{current}/{total}")
|
|
|
|
if current == 1:
|
|
idx_start = 0
|
|
idx_end = self.max_item_number if len_records >= self.max_item_number else len_records
|
|
elif current == total:
|
|
remainder = len_records % self.max_item_number
|
|
idx_start = len_records - remainder if remainder else len_records - self.max_item_number
|
|
idx_end = len_records
|
|
else:
|
|
idx_start = self.max_item_number * (current-1)
|
|
idx_end = self.max_item_number * current
|
|
|
|
self.treeW.clear()
|
|
for record in records[idx_start:idx_end]:
|
|
record = [str(_) for _ in record]
|
|
item = QTreeWidgetItem(self.treeW, record)
|
|
self.treeW.addTopLevelItem(item)
|
|
colors = {"info": QColor(144, 238, 144), "warning": QColor(255, 240, 210), "error": QColor(255, 220, 220), "exception": QColor(255, 220, 220), "unknown": QColor(255, 220, 220)}
|
|
color = colors[record[2]]
|
|
for col in range(5):
|
|
item.setBackground(col, color)
|
|
item.setTextAlignment(0, Qt.AlignmentFlag.AlignRight)
|
|
self.treeW.scrollToBottom()
|
|
|
|
def show_single_log(self, item, column):
|
|
log_id = f"id = {item.text(0)}"
|
|
log_ts = f"ts = {item.text(1)}"
|
|
log_level = f"level = {item.text(2)}"
|
|
log_module = f"module = {item.text(3)}\n"
|
|
deco_line = "=" * 40
|
|
log_msg = item.text(4)
|
|
content = "\n".join([log_id, log_ts, log_level, log_module, deco_line, log_msg])
|
|
dialog = LogDialog(content, self)
|
|
dialog.exec()
|