gui/mozregui/log_report.py (92 lines of code) (raw):

from datetime import datetime from mozlog import get_default_logger from mozlog.structuredlog import log_levels from PySide6.QtCore import QObject, Signal, Slot from PySide6.QtGui import ( QAction, QActionGroup, QColor, QTextBlockUserData, QTextCharFormat, QTextCursor, ) from PySide6.QtWidgets import QMenu, QPlainTextEdit COLORS = { "DEBUG": QColor(6, 146, 6), # green "INFO": QColor(250, 184, 4), # deep yellow "WARNING": QColor(255, 0, 0, 127), # red "CRITICAL": QColor(255, 0, 0, 127), "ERROR": QColor(255, 0, 0, 127), } class LogLevelData(QTextBlockUserData): def __init__(self, log_lvl): QTextBlockUserData.__init__(self) self.log_lvl = log_lvl class LogView(QPlainTextEdit): def __init__(self, parent=None): QPlainTextEdit.__init__(self, parent) self.setMaximumBlockCount(1000) self.group = QActionGroup(self) self.actions = [QAction(log_lvl, self.group) for log_lvl in ["Debug", "Info"]] for action in self.actions: action.setCheckable(True) action.triggered.connect(self.on_log_filter) self.actions[1].setChecked(True) self.customContextMenuRequested.connect(self.on_custom_context_menu_requested) self.log_lvl = log_levels["INFO"] def text_blocks(self): current_block = QTextCursor(self.document()).block() while current_block.isValid and current_block.text(): yield current_block current_block = current_block.next() @Slot(dict) def on_log_received(self, data): time_info = datetime.fromtimestamp((data["time"] / 1000)).isoformat() log_message = "%s: %s : %s" % (time_info, data["level"], data["message"]) message_document = self.document() cursor_to_add = QTextCursor(message_document) cursor_to_add.movePosition(QTextCursor.End) cursor_to_add.insertText(log_message + "\n") if data["level"] in COLORS: fmt = QTextCharFormat() fmt.setForeground(COLORS[data["level"]]) cursor_to_add.movePosition(QTextCursor.PreviousBlock) log_lvl_data = LogLevelData(log_levels[data["level"].upper()]) cursor_to_add.block().setUserData(log_lvl_data) cursor_to_add_fmt = message_document.find(data["level"], cursor_to_add.position()) cursor_to_add_fmt.mergeCharFormat(fmt) if log_levels[data["level"]] > self.log_lvl: cursor_to_add.block().setVisible(False) self.ensureCursorVisible() @Slot() def on_custom_context_menu_requested(self): menu = QMenu(self) for action in self.actions: menu.addAction(action) menu.popup(self.cursor().pos()) @Slot() def on_log_filter(self): log_lvl_name = str(self.sender().iconText()).upper() self.log_lvl = log_levels[log_lvl_name] it = self.document().begin() while it != self.document().end(): userdata = it.userData() if userdata: block_log_lvl = userdata.log_lvl it.setVisible(block_log_lvl <= self.log_lvl) it = it.next() self.viewport().update() class LogModel(QObject): log = Signal(dict) def __call__(self, data): self.log.emit(data) def log(text, log=True, status_bar=True, status_bar_timeout=2.0): if log: logger = get_default_logger("mozregui") if logger: logger.info(text) if status_bar: from mozregui.mainwindow import MainWindow mw = MainWindow.INSTANCE if mw: mw.ui.status_bar.showMessage(text, int(status_bar_timeout * 1000))